diff options
| -rw-r--r-- | backend/include/rcc/Target/RISCV/RISCVTarget.h | 3 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCV.rccdesc | 19 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVCallLowering.cpp | 112 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVCodeGen.cpp | 9 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVTarget.cpp | 12 | ||||
| -rw-r--r-- | backend/unittests/Target/RISCVCallLoweringTest.cpp | 21 | ||||
| -rw-r--r-- | backend/unittests/Target/RISCVCodeGenTest.cpp | 7 | ||||
| -rw-r--r-- | backend/unittests/Target/RISCVLateCodeGenTest.cpp | 2 | ||||
| -rw-r--r-- | backend/unittests/Target/RISCVTargetTest.cpp | 30 |
9 files changed, 146 insertions, 69 deletions
diff --git a/backend/include/rcc/Target/RISCV/RISCVTarget.h b/backend/include/rcc/Target/RISCV/RISCVTarget.h index e005fb8..6d6b806 100644 --- a/backend/include/rcc/Target/RISCV/RISCVTarget.h +++ b/backend/include/rcc/Target/RISCV/RISCVTarget.h @@ -61,7 +61,8 @@ struct CallingConventionInfo final { [[nodiscard]] Expected<TargetRegisterInfo> createRegisterInfo(); [[nodiscard]] Expected<TargetMIRTextInfo> createMIRTextInfo(); [[nodiscard]] Expected<TargetDescription> createTargetDescription(); -[[nodiscard]] std::shared_ptr<const RegisterMask> createCallPreservedMask(); +[[nodiscard]] std::shared_ptr<const RegisterMask> +createCallPreservedMask(CallingConvention Convention); [[nodiscard]] std::span<const SchedulingClassInfo> schedulingClasses() noexcept; [[nodiscard]] std::span<const EncodingCategoryInfo> encodingCategories() noexcept; [[nodiscard]] std::span<const AssemblyFormatInfo> assemblyFormats() noexcept; diff --git a/backend/lib/Target/RISCV/RISCV.rccdesc b/backend/lib/Target/RISCV/RISCV.rccdesc index ad79073..93c94b4 100644 --- a/backend/lib/Target/RISCV/RISCV.rccdesc +++ b/backend/lib/Target/RISCV/RISCV.rccdesc @@ -238,6 +238,9 @@ register_classes: register_masks: - + name: CSR_ILP32_LP64 + preserved: [X1, X8, X9, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27] + - name: CSR_ILP32D_LP64D preserved: [X1, X8, X9, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, F8, F9, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27] @@ -253,6 +256,22 @@ mir_operand_flags: calling_conventions: - + id: 2 + symbol: LP64 + name: lp64 + integer_register_class: GPR + floating_register_class: FPR64 + integer_argument_registers: [X10, X11, X12, X13, X14, X15, X16, X17] + floating_argument_registers: [] + integer_return_registers: [X10, X11] + floating_return_registers: [] + preserved_mask: CSR_ILP32_LP64 + stack_alignment_bytes: 16 + stack_argument_alignment_bytes: 16 + shadow_space_bytes: 0 + supports_varargs: true + supports_tail_calls: true + - id: 1 symbol: LP64D name: lp64d diff --git a/backend/lib/Target/RISCV/RISCVCallLowering.cpp b/backend/lib/Target/RISCV/RISCVCallLowering.cpp index 308ba05..4780edb 100644 --- a/backend/lib/Target/RISCV/RISCVCallLowering.cpp +++ b/backend/lib/Target/RISCV/RISCVCallLowering.cpp @@ -29,9 +29,9 @@ namespace rcc::riscv { namespace { -constexpr std::string_view PlanTarget = "riscv64-unknown-linux-gnu:lp64d"; +constexpr std::string_view PlanTarget = "riscv64-unknown-linux-gnu"; constexpr std::uint64_t XLenBytes = 8; -constexpr std::uint64_t LP64DStackAlignmentBytes = 16; +constexpr std::uint64_t ABIStackAlignmentBytes = 16; constexpr std::uint32_t AllowedDirectCallFlags = static_cast<std::uint32_t>(CallTargetFlag::DirectCall); @@ -53,7 +53,7 @@ constexpr std::uint32_t AllowedDirectCallFlags = return Value; const std::uint64_t Increment = AlignmentBytes - Remainder; if (Value > std::numeric_limits<std::uint64_t>::max() - Increment) { - return makeUnexpected(ErrorCode::Unsupported, "LP64D stack-area size overflows uint64"); + return makeUnexpected(ErrorCode::Unsupported, "RISC-V ABI stack-area size overflows uint64"); } return Value + Increment; } @@ -78,24 +78,24 @@ constexpr std::uint32_t AllowedDirectCallFlags = [[nodiscard]] Expected<void> verifyABIValue(const ABIValue &Value, bool RequireRegister) { if (RequireRegister && Value.Value.isNoRegister()) { return makeUnexpected(ErrorCode::InvalidArgument, - "LP64D ABI value requires a source or destination register"); + "RISC-V ABI value requires a source or destination register"); } if (!Value.Type.isValid() || !Value.Type.isScalarValue() || Value.Type.scalarBitWidth() > 64) { return makeUnexpected( ErrorCode::Unsupported, - "LP64D accepts explicitly classified integer, pointer, f32, and f64 scalar fragments"); + "RISC-V ABI accepts explicitly classified integer, pointer, f32, and f64 scalar fragments"); } if (Value.Type.isFloat() && !isSupportedFloat(Value.Type)) { return makeUnexpected(ErrorCode::Unsupported, - "RV64GC LP64D only supports f32 and f64 ABI fragments"); + "RISC-V LP64 ABIs only support f32 and f64 floating fragments"); } if (Value.Type.isPointer() && Value.Type.scalarBitWidth() != 64) { - return makeUnexpected(ErrorCode::Unsupported, "RV64GC LP64D requires 64-bit machine pointers"); + return makeUnexpected(ErrorCode::Unsupported, "RISC-V LP64 ABIs require 64-bit pointers"); } if ((Value.Type.isInteger() || Value.Type.isAnyScalar()) && Value.Type.scalarBitWidth() < 64) { if (Value.Extension == ABIExtensionKind::None) { return makeUnexpected(ErrorCode::InvalidArgument, - "narrow LP64D integer fragments require an explicit extension kind"); + "narrow RISC-V ABI integers require an explicit extension kind"); } } else if (Value.Extension != ABIExtensionKind::None) { return makeUnexpected(ErrorCode::InvalidArgument, @@ -104,7 +104,7 @@ constexpr std::uint32_t AllowedDirectCallFlags = if (Value.Extension >= ABIExtensionKind::Count || Value.FragmentCount == 0 || Value.FragmentIndex >= Value.FragmentCount || Value.FragmentCount > 2) { return makeUnexpected(ErrorCode::InvalidArgument, - "LP64D ABI fragment coordinates or extension are invalid"); + "RISC-V ABI fragment coordinates or extension are invalid"); } const bool IsByVal = Value.Flags.has(ABIValueFlag::ByVal); @@ -147,7 +147,7 @@ constexpr std::uint32_t AllowedDirectCallFlags = Index + First.FragmentCount > Values.size()) { return makeUnexpected( ErrorCode::InvalidArgument, - "LP64D ABI values must form contiguous, zero-based original-value groups"); + "RISC-V ABI values must form contiguous, zero-based original-value groups"); } std::uint64_t PreviousEnd = 0; @@ -162,7 +162,7 @@ constexpr std::uint32_t AllowedDirectCallFlags = Value.ByValSizeBytes != First.ByValSizeBytes || Value.ByValAlignment != First.ByValAlignment || Value.FragmentOffsetBytes < PreviousEnd) { return makeUnexpected(ErrorCode::InvalidArgument, - "LP64D fragments of one value have inconsistent metadata"); + "RISC-V ABI fragments of one value have inconsistent metadata"); } PreviousEnd = static_cast<std::uint64_t>(Value.FragmentOffsetBytes) + valueSizeBytes(Value.Type); @@ -215,7 +215,7 @@ struct AssignmentState final { PhysicalRegister Location, LLT LocationType, bool IsVariadic, bool IsBitcast) { auto Align = - alignment(std::min<std::uint64_t>(Value.OriginalAlignment.bytes(), LP64DStackAlignmentBytes)); + alignment(std::min<std::uint64_t>(Value.OriginalAlignment.bytes(), ABIStackAlignmentBytes)); if (!Align) return std::unexpected(Align.error()); return CCAssignment{ @@ -269,7 +269,7 @@ assignArguments(std::span<const ABIValue> Values, std::size_t FixedArgumentCount auto I64 = LLT::makeInteger(64); auto StackAlign = alignment(Convention.StackArgumentAlignmentBytes); if (!I64 || !StackAlign) - return makeUnexpected(ErrorCode::InternalError, "LP64D primitive type facts are invalid"); + return makeUnexpected(ErrorCode::InternalError, "RISC-V ABI primitive type facts are invalid"); ArgumentAssignmentResult Result; AssignmentState State; @@ -347,13 +347,13 @@ assignArguments(std::span<const ABIValue> Values, std::size_t FixedArgumentCount if (Value.Flags.has(ABIValueFlag::InRegister)) { return makeUnexpected(ErrorCode::Unsupported, - "LP64D inreg argument cannot be assigned to the stack"); + "RISC-V inreg argument cannot be assigned to the stack"); } std::uint64_t RequiredAlignment = XLenBytes; if (FirstStackFragment && ForceEntireGroupToStack) { RequiredAlignment = std::min<std::uint64_t>( std::max<std::uint64_t>(XLenBytes, First.OriginalAlignment.bytes()), - LP64DStackAlignmentBytes); + ABIStackAlignmentBytes); } auto AlignedOffset = alignTo(State.StackOffset, RequiredAlignment); if (!AlignedOffset) @@ -366,7 +366,7 @@ assignArguments(std::span<const ABIValue> Values, std::size_t FixedArgumentCount return std::unexpected(Assignment.error()); Result.Assignments.push_back(*Assignment); if (State.StackOffset > std::numeric_limits<std::uint64_t>::max() - XLenBytes) { - return makeUnexpected(ErrorCode::Unsupported, "LP64D argument area overflows uint64"); + return makeUnexpected(ErrorCode::Unsupported, "RISC-V argument area overflows uint64"); } State.StackOffset += XLenBytes; FirstStackFragment = false; @@ -392,7 +392,7 @@ assignReturns(std::span<const ABIValue> Values, bool RequireRegisters, return std::unexpected(Sequence.error()); if (Values.size() > 2) { return makeUnexpected(ErrorCode::Unsupported, - "LP64D direct returns are limited to two explicit fragments"); + "RISC-V direct returns are limited to two explicit fragments"); } auto I64 = LLT::makeInteger(64); if (!I64) @@ -408,12 +408,17 @@ assignReturns(std::span<const ABIValue> Values, bool RequireRegisters, return makeUnexpected(ErrorCode::InvalidArgument, "return fragments cannot carry argument-only ABI flags"); } - Expected<CCAssignment> Assignment = - isSupportedFloat(Value.Type) - ? registerAssignment(Value, Index, Convention.FloatingReturnRegisters[FPR++], - Value.Type, false, false) - : registerAssignment(Value, Index, Convention.IntegerReturnRegisters[GPR++], *I64, - false, false); + const bool UseFloatingRegister = + isSupportedFloat(Value.Type) && FPR < Convention.FloatingReturnRegisters.size(); + Expected<CCAssignment> Assignment = makeUnexpected( + ErrorCode::Unsupported, "RISC-V calling convention has no return register available"); + if (UseFloatingRegister) { + Assignment = registerAssignment(Value, Index, Convention.FloatingReturnRegisters[FPR++], + Value.Type, false, false); + } else if (GPR < Convention.IntegerReturnRegisters.size()) { + Assignment = registerAssignment(Value, Index, Convention.IntegerReturnRegisters[GPR++], *I64, + false, isSupportedFloat(Value.Type)); + } if (!Assignment) return std::unexpected(Assignment.error()); Result.push_back(*Assignment); @@ -480,14 +485,18 @@ tailCallRejection(const CallLoweringRequest &Request, const ArgumentAssignmentRe } [[nodiscard]] Expected<const CallingConventionInfo *> -lp64dConvention(CallingConventionID Convention) { - if (Convention != callingConventionID(CallingConvention::LP64D)) { - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V backend only supports the LP64D calling convention"); - } - const CallingConventionInfo *Info = callingConvention(CallingConvention::LP64D); +riscvConvention(CallingConventionID Convention) { + CallingConvention TargetConvention = CallingConvention::None; + if (Convention == callingConventionID(CallingConvention::LP64D)) + TargetConvention = CallingConvention::LP64D; + else if (Convention == callingConventionID(CallingConvention::LP64)) + TargetConvention = CallingConvention::LP64; + else + return makeUnexpected(ErrorCode::Unsupported, "RISC-V backend only supports LP64 and LP64D"); + const CallingConventionInfo *Info = callingConvention(TargetConvention); if (Info == nullptr) - return makeUnexpected(ErrorCode::InternalError, "LP64D target facts are unavailable"); + return makeUnexpected(ErrorCode::InternalError, + "RISC-V calling-convention target facts are unavailable"); return Info; } @@ -505,7 +514,7 @@ public: Expected<CallLoweringPlan> RISCVCallLowering::planFormalArguments(const FormalArgumentRequest &Request) const { - auto Convention = lp64dConvention(Request.Convention); + auto Convention = riscvConvention(Request.Convention); if (!Convention) return std::unexpected(Convention.error()); auto OriginalCount = verifyValueSequence(Request.Arguments, true); @@ -523,7 +532,7 @@ RISCVCallLowering::planFormalArguments(const FormalArgumentRequest &Request) con (Request.Arguments.empty() || Request.Arguments.front().OriginalValue != 0 || !Request.Arguments.front().Flags.has(ABIValueFlag::StructReturn))) { return makeUnexpected(ErrorCode::InvalidArgument, - "LP64D sret must be the first formal argument"); + "RISC-V sret must be the first formal argument"); } auto Assignments = assignArguments(Request.Arguments, Request.FixedArgumentCount, @@ -563,7 +572,7 @@ RISCVCallLowering::planFormalArguments(const FormalArgumentRequest &Request) con } Expected<CallLoweringPlan> RISCVCallLowering::planCall(const CallLoweringRequest &Request) const { - auto Convention = lp64dConvention(Request.Convention); + auto Convention = riscvConvention(Request.Convention); if (!Convention) return std::unexpected(Convention.error()); auto OriginalCount = verifyValueSequence(Request.Arguments, true); @@ -579,11 +588,12 @@ Expected<CallLoweringPlan> RISCVCallLowering::planCall(const CallLoweringRequest if (hasStructReturn(Request.Arguments) && (Request.Arguments.empty() || !Request.Arguments.front().Flags.has(ABIValueFlag::StructReturn))) { - return makeUnexpected(ErrorCode::InvalidArgument, "LP64D sret must be the first call argument"); + return makeUnexpected(ErrorCode::InvalidArgument, + "RISC-V sret must be the first call argument"); } if (hasStructReturn(Request.Arguments) && !Request.Results.empty()) { return makeUnexpected(ErrorCode::InvalidArgument, - "LP64D sret calls cannot also request direct return values"); + "RISC-V sret calls cannot also request direct return values"); } MachineCallTarget EffectiveCallee = Request.Callee; @@ -636,7 +646,7 @@ Expected<CallLoweringPlan> RISCVCallLowering::planCall(const CallLoweringRequest if (!Results) return std::unexpected(Results.error()); - auto Align16 = alignment(LP64DStackAlignmentBytes); + auto Align16 = alignment(ABIStackAlignmentBytes); if (!Align16) return std::unexpected(Align16.error()); std::uint64_t CallFrameCursor = Arguments->StackArea.SizeBytes; @@ -647,12 +657,12 @@ Expected<CallLoweringPlan> RISCVCallLowering::planCall(const CallLoweringRequest continue; const ABIValue &Value = Request.Arguments[Assignment.ValueIndex]; const std::uint64_t CopyAlignment = - std::min<std::uint64_t>(Value.ByValAlignment.bytes(), LP64DStackAlignmentBytes); + std::min<std::uint64_t>(Value.ByValAlignment.bytes(), ABIStackAlignmentBytes); auto CopyOffset = alignTo(CallFrameCursor, CopyAlignment); if (!CopyOffset) return std::unexpected(CopyOffset.error()); if (*CopyOffset > std::numeric_limits<std::uint64_t>::max() - Value.ByValSizeBytes) { - return makeUnexpected(ErrorCode::Unsupported, "LP64D byval area overflows uint64"); + return makeUnexpected(ErrorCode::Unsupported, "RISC-V byval area overflows uint64"); } auto CopyAlign = alignment(CopyAlignment); if (!CopyAlign) @@ -661,7 +671,7 @@ Expected<CallLoweringPlan> RISCVCallLowering::planCall(const CallLoweringRequest {Index, Value.Value, Value.Type, *CopyOffset, Value.ByValSizeBytes, *CopyAlign}); CallFrameCursor = *CopyOffset + Value.ByValSizeBytes; } - auto CallFrameSize = alignTo(CallFrameCursor, LP64DStackAlignmentBytes); + auto CallFrameSize = alignTo(CallFrameCursor, ABIStackAlignmentBytes); if (!CallFrameSize) return std::unexpected(CallFrameSize.error()); @@ -700,7 +710,9 @@ Expected<CallLoweringPlan> RISCVCallLowering::planCall(const CallLoweringRequest Plan.VarArgs.FixedArgumentCount = Request.FixedArgumentCount; } Plan.ByValCopies = std::move(ByValCopies); - Plan.PreservedMask = createCallPreservedMask(); + Plan.PreservedMask = createCallPreservedMask((*Convention)->ID); + if (!Plan.PreservedMask) + return makeUnexpected(ErrorCode::InternalError, "RISC-V call-preserved mask is unavailable"); Plan.Callee = std::move(EffectiveCallee); Plan.TailDecision = Decision; Plan.TailRejectionReason = std::move(RejectionReason); @@ -709,7 +721,7 @@ Expected<CallLoweringPlan> RISCVCallLowering::planCall(const CallLoweringRequest Expected<CallLoweringPlan> RISCVCallLowering::planReturn(const ReturnLoweringRequest &Request) const { - auto Convention = lp64dConvention(Request.Convention); + auto Convention = riscvConvention(Request.Convention); if (!Convention) return std::unexpected(Convention.error()); auto Values = assignReturns(Request.Values, true, **Convention); @@ -920,7 +932,7 @@ createMemoryOperand(MachineFunction &Function, MemoryAccessFlags Accesses, std:: const std::optional<PhysicalRegister> Physical = CalleeReg.physicalRegister(); if (!Physical || !Target.registers().isKnownPhysicalRegister(*Physical)) { return makeUnexpected(ErrorCode::VerificationError, - "indirect LP64D callee names an unknown physical register"); + "indirect RISC-V callee names an unknown physical register"); } } else { auto Info = Registers.virtualRegisterInfo(CalleeReg); @@ -929,7 +941,7 @@ createMemoryOperand(MachineFunction &Function, MemoryAccessFlags Accesses, std:: const std::optional<LLT> Type = Info->get().Type; if (!Type || ((!Type->isPointer() && !Type->isInteger()) || Type->scalarBitWidth() != 64)) { return makeUnexpected(ErrorCode::VerificationError, - "indirect LP64D callee requires a 64-bit pointer or integer LLT"); + "indirect RISC-V callee requires a 64-bit pointer or integer LLT"); } } } @@ -979,8 +991,10 @@ createMemoryOperand(MachineFunction &Function, MemoryAccessFlags Accesses, std:: [[nodiscard]] Expected<void> preflightPlan(MachineIRBuilder &Builder, const CallLoweringPlan &Plan) { - if (Plan.target() != PlanTarget || - Plan.convention() != callingConventionID(CallingConvention::LP64D)) { + auto Convention = riscvConvention(Plan.convention()); + if (!Convention) + return std::unexpected(Convention.error()); + if (Plan.target() != PlanTarget) { return makeUnexpected(ErrorCode::InvalidArgument, "call-lowering plan belongs to another target or ABI"); } @@ -1001,14 +1015,14 @@ createMemoryOperand(MachineFunction &Function, MemoryAccessFlags Accesses, std:: *Assignment.StackOffsetBytes > static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max())) { return makeUnexpected(ErrorCode::Unsupported, - "LP64D stack assignment offset exceeds the frame-index range"); + "RISC-V stack assignment offset exceeds the frame-index range"); } } for (const ByValCopyAssignment &Copy : Plan.byValCopies()) { if (Copy.StackOffsetBytes > static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max())) { return makeUnexpected(ErrorCode::Unsupported, - "LP64D byval offset exceeds the frame-index range"); + "RISC-V byval offset exceeds the frame-index range"); } } if (Plan.kind() != CallLoweringPlanKind::Return && Builder.function().frameInfo().isFrozen()) { @@ -1096,9 +1110,9 @@ createMemoryOperand(MachineFunction &Function, MemoryAccessFlags Accesses, std:: } auto Align8 = alignment(XLenBytes); - auto Align16 = alignment(LP64DStackAlignmentBytes); + auto Align16 = alignment(ABIStackAlignmentBytes); if (!Align8 || !Align16) - return makeUnexpected(ErrorCode::InternalError, "LP64D primitive type facts are invalid"); + return makeUnexpected(ErrorCode::InternalError, "RISC-V ABI primitive type facts are invalid"); FrameIndex SaveObject; if (VarArgs.RegisterSaveAreaSizeBytes == 0) { diff --git a/backend/lib/Target/RISCV/RISCVCodeGen.cpp b/backend/lib/Target/RISCV/RISCVCodeGen.cpp index bd4e20c..076e030 100644 --- a/backend/lib/Target/RISCV/RISCVCodeGen.cpp +++ b/backend/lib/Target/RISCV/RISCVCodeGen.cpp @@ -1470,7 +1470,8 @@ void eraseDeadArtifacts(std::span<MachineInstr *const> Candidates, [[nodiscard]] Expected<void> lowerMultiplyLibcall(MachineInstr &Instruction, MachineFunction &Function, - const TargetDescription &Target) { + const TargetDescription &Target, + CallingConvention Convention) { const std::optional<LLT> Type = resultType(Function, Instruction); const MachineOperand *DestinationOperand = Instruction.operandAt(0); const MachineOperand *LeftOperand = Instruction.operandAt(1); @@ -1506,7 +1507,7 @@ void eraseDeadArtifacts(std::span<MachineInstr *const> Candidates, }; CallLoweringRequest Request; - Request.Convention = callingConventionID(CallingConvention::LP64D); + Request.Convention = callingConventionID(Convention); Request.Callee = std::move(*Callee); Request.Arguments = {Value(*Left, 0), Value(*Right, 1)}; Request.Results = {Value(*Destination, 0)}; @@ -3620,7 +3621,9 @@ public: if (Action == LegalizationAction::Custom) return widenNarrowMultiply(Instruction, Function, Target); if (Action == LegalizationAction::Libcall) - return lowerMultiplyLibcall(Instruction, Function, Target); + return lowerMultiplyLibcall(Instruction, Function, Target, + Features.has(RISCVExtension::D) ? CallingConvention::LP64D + : CallingConvention::LP64); } if (Action == LegalizationAction::Custom && resultType(Function, Instruction) diff --git a/backend/lib/Target/RISCV/RISCVTarget.cpp b/backend/lib/Target/RISCV/RISCVTarget.cpp index aabce94..299b5ab 100644 --- a/backend/lib/Target/RISCV/RISCVTarget.cpp +++ b/backend/lib/Target/RISCV/RISCVTarget.cpp @@ -11,6 +11,7 @@ #include <cstdint> #include <initializer_list> #include <memory> +#include <ranges> #include <span> #include <utility> #include <vector> @@ -121,12 +122,17 @@ Expected<TargetDescription> createTargetDescription() { std::move(MIRText)); } -std::shared_ptr<const RegisterMask> createCallPreservedMask() { +std::shared_ptr<const RegisterMask> createCallPreservedMask(CallingConvention Convention) { + const CallingConventionInfo *Info = callingConvention(Convention); + if (Info == nullptr) + return {}; TargetMIRTextInfoDesc Description; appendGeneratedMIRTextInfo(Description); configureFunctionInfoMIRSchema(Description); - assert(!Description.RegisterMasks.empty()); - return Description.RegisterMasks.front().Mask; + const auto Mask = std::ranges::find(Description.RegisterMasks, Info->PreservedMaskName, + &MIRRegisterMaskDesc::Name); + return Mask == Description.RegisterMasks.end() ? std::shared_ptr<const RegisterMask>{} + : Mask->Mask; } std::span<const SchedulingClassInfo> schedulingClasses() noexcept { diff --git a/backend/unittests/Target/RISCVCallLoweringTest.cpp b/backend/unittests/Target/RISCVCallLoweringTest.cpp index 7284887..08049f6 100644 --- a/backend/unittests/Target/RISCVCallLoweringTest.cpp +++ b/backend/unittests/Target/RISCVCallLoweringTest.cpp @@ -191,6 +191,7 @@ void testIntegerAndFloatingAssignments(TestContext &Context) { if (!IntegerPlan) return; const auto IntegerAssignments = IntegerPlan->argumentAssignments(); + const auto LP64DMask = rcc::riscv::createCallPreservedMask(rcc::riscv::CallingConvention::LP64D); bool IntegerRegistersMatch = IntegerAssignments.size() == 9; for (std::size_t Index = 0; IntegerRegistersMatch && Index < 8; ++Index) IntegerRegistersMatch = isLocation(IntegerAssignments[Index], argumentGPR(Index)); @@ -200,7 +201,9 @@ void testIntegerAndFloatingAssignments(TestContext &Context) { IntegerPlan->stackArguments().SizeBytes == 16 && IntegerPlan->callFrame().SizeBytes == 16 && IntegerPlan->callee()->targetFlags() == - static_cast<std::uint32_t>(rcc::riscv::CallTargetFlag::DirectCall), + static_cast<std::uint32_t>(rcc::riscv::CallTargetFlag::DirectCall) && + LP64DMask != nullptr && IntegerPlan->preservedMask() != nullptr && + *IntegerPlan->preservedMask() == *LP64DMask, "LP64D integer arguments must use a0-a7 before a 16-byte stack area"); auto Floats = callRequest(floatingValues(17), 17); @@ -247,6 +250,22 @@ void testIntegerAndFloatingAssignments(TestContext &Context) { GenericScalarPlan->returnAssignments()[0].ValueType == anyScalar(64), "LLVM generic scalar LLTs must use integer ABI transport without type loss"); + auto LP64Float = callRequest({abiValue(floating(rcc::FloatSemantics::IEEEDouble), 0)}, 1); + LP64Float.Convention = rcc::riscv::callingConventionID(rcc::riscv::CallingConvention::LP64); + LP64Float.Results = {abiValue(floating(rcc::FloatSemantics::IEEEDouble), 0)}; + auto LP64Plan = rcc::riscv::callLowering().planCall(LP64Float); + const auto LP64Mask = rcc::riscv::createCallPreservedMask(rcc::riscv::CallingConvention::LP64); + Context.expect( + LP64Plan && LP64Plan->argumentAssignments().size() == 1 && + LP64Plan->returnAssignments().size() == 1 && + isLocation(LP64Plan->argumentAssignments()[0], argumentGPR(0)) && + isLocation(LP64Plan->returnAssignments()[0], returnGPR(0)) && + LP64Plan->argumentAssignments()[0].CustomFlags.has(rcc::CCAssignmentFlag::Bitcast) && + LP64Plan->returnAssignments()[0].CustomFlags.has(rcc::CCAssignmentFlag::Bitcast) && + LP64Mask != nullptr && LP64Plan->preservedMask() != nullptr && + *LP64Plan->preservedMask() == *LP64Mask, + "LP64 must transport floating values through GPRs and use the integer preserved mask"); + auto Narrow = abiValue(integer(32), 0); auto InvalidNarrow = callRequest({Narrow}, 1); Context.expect(!rcc::riscv::callLowering().planCall(InvalidNarrow), diff --git a/backend/unittests/Target/RISCVCodeGenTest.cpp b/backend/unittests/Target/RISCVCodeGenTest.cpp index aa58989..fcf0e57 100644 --- a/backend/unittests/Target/RISCVCodeGenTest.cpp +++ b/backend/unittests/Target/RISCVCodeGenTest.cpp @@ -1219,10 +1219,15 @@ void testMultiplyLibcallWithoutM(TestContext &Context) { } } const rcc::MachineOperand *Callee = Call == nullptr ? nullptr : Call->operandAt(0); + const rcc::MachineOperand *MaskOperand = Call == nullptr ? nullptr : Call->operandAt(1); const auto *External = Callee == nullptr ? nullptr : Callee->getIf<rcc::ExternalSymbolOperand>(); + const auto *CallMask = + MaskOperand == nullptr ? nullptr : MaskOperand->getIf<rcc::RegisterMaskOperand>(); + const auto LP64Mask = rcc::riscv::createCallPreservedMask(rcc::riscv::CallingConvention::LP64); Context.expect(Action && *Action == rcc::LegalizationAction::Libcall && Lowered && Multiply64.Instruction->parent() == nullptr && External != nullptr && - External->Name == "__muldi3" && + External->Name == "__muldi3" && CallMask != nullptr && CallMask->Mask && + LP64Mask != nullptr && *CallMask->Mask == *LP64Mask && Multiply64.Function->frameInfo().state().HasCalls, "s64 G_MUL without M must lower through the LP64 __muldi3 call path"); diff --git a/backend/unittests/Target/RISCVLateCodeGenTest.cpp b/backend/unittests/Target/RISCVLateCodeGenTest.cpp index 84ed796..5069137 100644 --- a/backend/unittests/Target/RISCVLateCodeGenTest.cpp +++ b/backend/unittests/Target/RISCVLateCodeGenTest.cpp @@ -1129,7 +1129,7 @@ void testPostRAPseudoExpansionAndFinalization(TestContext &Context, return; } auto Call = Builder.buildInstr(opcode(rcc::riscv::InstructionOpcode::PseudoCALL)); - auto Preserved = rcc::riscv::createCallPreservedMask(); + auto Preserved = rcc::riscv::createCallPreservedMask(rcc::riscv::CallingConvention::LP64D); if (!Call || !Call->addExternalSymbol("callee", 0, 1) || !Call->addRegisterMask(Preserved) || !Call->addImplicitUse(physical(rcc::riscv::PhysicalRegisterID::X10)) || !Call->build()) { Context.expect(false, "post-RA call pseudo construction must succeed"); diff --git a/backend/unittests/Target/RISCVTargetTest.cpp b/backend/unittests/Target/RISCVTargetTest.cpp index e0ef604..1aa2d72 100644 --- a/backend/unittests/Target/RISCVTargetTest.cpp +++ b/backend/unittests/Target/RISCVTargetTest.cpp @@ -87,19 +87,25 @@ void testABIAndDebugFacts(TestContext &Context) { const rcc::PhysicalRegister F10 = registerID(rcc::riscv::PhysicalRegisterID::F10); const rcc::PhysicalRegister F31 = registerID(rcc::riscv::PhysicalRegisterID::F31); - const auto Mask = rcc::riscv::createCallPreservedMask(); + const auto LP64Mask = rcc::riscv::createCallPreservedMask(rcc::riscv::CallingConvention::LP64); + const auto LP64DMask = rcc::riscv::createCallPreservedMask(rcc::riscv::CallingConvention::LP64D); Context.expect( - Mask != nullptr && Mask->preserves(X1) && Mask->preserves(F8) && Mask->clobbers(X10) && - Mask->clobbers(F10), - "LP64D preserved-mask facts must distinguish saved and caller-clobbered registers"); + LP64DMask != nullptr && LP64DMask->preserves(X1) && LP64DMask->preserves(F8) && + LP64DMask->clobbers(X10) && LP64DMask->clobbers(F10) && LP64Mask != nullptr && + LP64Mask->preserves(X1) && LP64Mask->clobbers(F8) && LP64Mask->clobbers(X10), + "LP64 and LP64D masks must distinguish integer and floating callee-saved registers"); auto MIRText = rcc::riscv::createMIRTextInfo(); Context.expect(MIRText.has_value(), "RV64GC MIR text facts must be valid"); if (!MIRText) return; - Context.expect(MIRText->registerMasks().size() == 1 && - MIRText->registerMasks().front().Name == "csr_ilp32d_lp64d", - "RV64GC must expose its named LP64D call-preserved mask"); + const auto TextLP64Mask = MIRText->registerMask("csr_ilp32_lp64"); + const auto TextLP64DMask = MIRText->registerMask("csr_ilp32d_lp64d"); + Context.expect(MIRText->registerMasks().size() == 2 && TextLP64Mask != nullptr && + LP64Mask != nullptr && *TextLP64Mask == *LP64Mask && + TextLP64DMask != nullptr && LP64DMask != nullptr && + *TextLP64DMask == *LP64DMask, + "RV64GC must expose named LP64 and LP64D call-preserved masks"); Context.expect(MIRText->directOperandFlagMask() == 31 && MIRText->directOperandFlag("riscv-call") == 1, "RV64GC MIR target flags must expose the canonical direct-call spelling"); @@ -109,16 +115,20 @@ void testABIAndDebugFacts(TestContext &Context) { "the final floating register DWARF number must map to f31"); const auto Conventions = rcc::riscv::callingConventions(); + const auto *LP64 = rcc::riscv::callingConvention(rcc::riscv::CallingConvention::LP64); const auto *LP64D = rcc::riscv::callingConvention(rcc::riscv::CallingConvention::LP64D); Context.expect( - Conventions.size() == 1 && LP64D != nullptr && LP64D->Name == "lp64d" && - LP64D->IntegerArgumentRegisters.size() == 8 && + Conventions.size() == 2 && LP64 != nullptr && LP64->Name == "lp64" && + LP64->IntegerArgumentRegisters.size() == 8 && LP64->FloatingArgumentRegisters.empty() && + LP64->IntegerReturnRegisters.size() == 2 && LP64->FloatingReturnRegisters.empty() && + LP64->PreservedMaskName == "CSR_ILP32_LP64" && LP64D != nullptr && + LP64D->Name == "lp64d" && LP64D->IntegerArgumentRegisters.size() == 8 && LP64D->FloatingArgumentRegisters.size() == 8 && LP64D->IntegerReturnRegisters.size() == 2 && LP64D->FloatingReturnRegisters.size() == 2 && LP64D->StackAlignmentBytes == 16 && LP64D->StackArgumentAlignmentBytes == 16 && LP64D->SupportsVarArgs && LP64D->SupportsTailCalls && LP64D->PreservedMaskName == "CSR_ILP32D_LP64D", - "LP64D calling-convention tables must expose register, stack, and tail-call facts"); + "LP64 and LP64D calling-convention tables must expose complete ABI facts"); } void testInstructionFacts(TestContext &Context) { |
