From 551f0b3fcd6f797ed6dca90f39123b0359396dcb Mon Sep 17 00:00:00 2001 From: Richard-Qin-X Date: Tue, 11 Aug 2026 20:08:36 +0800 Subject: [RISCV] Match LLVM call-frame pseudo semantics --- backend/lib/Target/RISCV/RISCV.rccdesc | 4 ++ backend/lib/Target/RISCV/RISCVCallLowering.cpp | 25 ++++++------ backend/lib/Target/RISCV/RISCVFrameLowering.cpp | 46 ++++++++-------------- .../lib/Target/RISCV/RISCVRegisterAllocation.cpp | 32 --------------- backend/unittests/Target/RISCVCallLoweringTest.cpp | 35 ++++++++++++++++ .../unittests/Target/RISCVFrameLoweringTest.cpp | 23 +++++------ 6 files changed, 80 insertions(+), 85 deletions(-) (limited to 'backend') diff --git a/backend/lib/Target/RISCV/RISCV.rccdesc b/backend/lib/Target/RISCV/RISCV.rccdesc index f8e22d4..ea3a775 100644 --- a/backend/lib/Target/RISCV/RISCV.rccdesc +++ b/backend/lib/Target/RISCV/RISCV.rccdesc @@ -3351,6 +3351,8 @@ instructions: - { kinds: [immediate, integer_immediate] } - { kinds: [immediate, integer_immediate] } num_explicit_defs: 0 + implicit_defs: [{ register: X2 }] + implicit_uses: [{ register: X2 }] flags: [pseudo, allowed_pre_legalizer, has_side_effects] owner: prologue_epilogue_insertion deadline: frame_finalized @@ -3362,6 +3364,8 @@ instructions: - { kinds: [immediate, integer_immediate] } - { kinds: [immediate, integer_immediate] } num_explicit_defs: 0 + implicit_defs: [{ register: X2 }] + implicit_uses: [{ register: X2 }] flags: [pseudo, allowed_pre_legalizer, has_side_effects] owner: prologue_epilogue_insertion deadline: frame_finalized diff --git a/backend/lib/Target/RISCV/RISCVCallLowering.cpp b/backend/lib/Target/RISCV/RISCVCallLowering.cpp index fd01d52..3a5805a 100644 --- a/backend/lib/Target/RISCV/RISCVCallLowering.cpp +++ b/backend/lib/Target/RISCV/RISCVCallLowering.cpp @@ -1172,20 +1172,20 @@ createMemoryOperand(MachineFunction &Function, MemoryAccessFlags Accesses, std:: [[nodiscard]] Expected emitCallFrameAdjustment(MachineIRBuilder &Builder, InstructionOpcode OpcodeValue, - const CallFrameRequirements &Frame, - MachineInstrFlag Flag) { + const CallFrameRequirements &Frame) { + if (Frame.SizeBytes > static_cast(std::numeric_limits::max())) { + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V call-frame size does not fit a MIR immediate"); + } auto Instruction = Builder.buildInstr(targetOpcode(OpcodeValue)); if (!Instruction) return std::unexpected(Instruction.error()); - auto Size = addUnsignedImmediate(*Instruction, Frame.SizeBytes); + auto Size = Instruction->addImmediate(static_cast(Frame.SizeBytes)); if (!Size) return std::unexpected(Size.error()); - auto Align = addUnsignedImmediate(*Instruction, Frame.Align.bytes()); - if (!Align) - return std::unexpected(Align.error()); - auto Flags = Instruction->setFlags({Flag}); - if (!Flags) - return std::unexpected(Flags.error()); + auto PreparedBytes = Instruction->addImmediate(0); + if (!PreparedBytes) + return std::unexpected(PreparedBytes.error()); auto Built = Instruction->build(); if (!Built) return std::unexpected(Built.error()); @@ -1371,8 +1371,8 @@ emitOutgoingArguments(MachineIRBuilder &Builder, const CallLoweringPlan &Plan, [[nodiscard]] Expected emitCall(MachineIRBuilder &Builder, const CallLoweringPlan &Plan) { if (Plan.callFrame().EmitSetup) { - auto Down = emitCallFrameAdjustment(Builder, InstructionOpcode::ADJCALLSTACKDOWN, - Plan.callFrame(), MachineInstrFlag::FrameSetup); + auto Down = + emitCallFrameAdjustment(Builder, InstructionOpcode::ADJCALLSTACKDOWN, Plan.callFrame()); if (!Down) return std::unexpected(Down.error()); } @@ -1388,8 +1388,7 @@ emitOutgoingArguments(MachineIRBuilder &Builder, const CallLoweringPlan &Plan, return std::unexpected(Call.error()); if (Plan.callFrame().EmitDestroy) { - auto Up = emitCallFrameAdjustment(Builder, InstructionOpcode::ADJCALLSTACKUP, Plan.callFrame(), - MachineInstrFlag::FrameDestroy); + auto Up = emitCallFrameAdjustment(Builder, InstructionOpcode::ADJCALLSTACKUP, Plan.callFrame()); if (!Up) return std::unexpected(Up.error()); } diff --git a/backend/lib/Target/RISCV/RISCVFrameLowering.cpp b/backend/lib/Target/RISCV/RISCVFrameLowering.cpp index e47a9c8..4ba5540 100644 --- a/backend/lib/Target/RISCV/RISCVFrameLowering.cpp +++ b/backend/lib/Target/RISCV/RISCVFrameLowering.cpp @@ -102,29 +102,33 @@ constexpr std::uint8_t DwarfOpBaseRegisterExtended = 0x92U; struct CallFrameRequirement final { std::int64_t Amount = 0; - std::int64_t Alignment = 0; + std::int64_t PreparedBytes = 0; std::int64_t AlignedAmount = 0; }; [[nodiscard]] Expected callFrameRequirement(const MachineInstr &Instruction) { - if (Instruction.operandCount() != 2) { + if (Instruction.operandCount() < 2) { return makeUnexpected(ErrorCode::VerificationError, - "RISC-V call-frame pseudo requires exactly amount and alignment"); + "RISC-V call-frame pseudo requires amount and prepared-byte operands"); + } + for (std::size_t Index = 2; Index < Instruction.operandCount(); ++Index) { + const MachineOperand *Operand = Instruction.operandAt(Index); + if (Operand == nullptr || !Operand->isRegister() || !Operand->isImplicit()) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V call-frame pseudo has an unexpected explicit operand"); + } } auto Amount = immediateValue(*Instruction.operandAt(0)); - auto RequestedAlignment = immediateValue(*Instruction.operandAt(1)); - if (!Amount || !RequestedAlignment || *Amount < 0 || *RequestedAlignment <= 0 || - !std::has_single_bit(static_cast(*RequestedAlignment))) { + auto PreparedBytes = immediateValue(*Instruction.operandAt(1)); + if (!Amount || !PreparedBytes || *Amount < 0 || *PreparedBytes < 0) { return makeUnexpected(ErrorCode::VerificationError, - "RISC-V call-frame amount or alignment is invalid"); + "RISC-V call-frame amount or prepared-byte count is invalid"); } - const std::uint64_t Alignment = - std::max(StackAlignmentBytes, static_cast(*RequestedAlignment)); - auto Aligned = alignTo(static_cast(*Amount), Alignment); + auto Aligned = alignTo(static_cast(*Amount), StackAlignmentBytes); if (!Aligned || *Aligned > static_cast(std::numeric_limits::max())) { return makeUnexpected(ErrorCode::FrameLayoutFailed, "RISC-V call-frame amount is too large"); } - return CallFrameRequirement{*Amount, *RequestedAlignment, static_cast(*Aligned)}; + return CallFrameRequirement{*Amount, *PreparedBytes, static_cast(*Aligned)}; } [[nodiscard]] Expected callFrameAmount(const MachineInstr &Instruction) { @@ -148,23 +152,6 @@ struct CallFrameRequirement final { if (!Object.isTombstone() && !Object.description()->IsDead) Result = std::max(Result, Object.description()->Align.bytes()); } - for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr; - Block = Block->next()) { - for (const MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr; - Instruction = Instruction->next()) { - if (Instruction->opcode() != targetOpcode(InstructionOpcode::ADJCALLSTACKDOWN) && - Instruction->opcode() != targetOpcode(InstructionOpcode::ADJCALLSTACKUP)) { - continue; - } - if (Instruction->operandCount() < 2) - continue; - auto Requested = immediateValue(*Instruction->operandAt(1)); - if (Requested && *Requested > 0 && - std::has_single_bit(static_cast(*Requested))) { - Result = std::max(Result, static_cast(*Requested)); - } - } - } return Result; } @@ -314,7 +301,8 @@ struct CallFrameRequirement final { return makeUnexpected(ErrorCode::VerificationError, "RISC-V call-frame destroy has no matching setup"); } - if (Active->Amount != Requirement->Amount || Active->Alignment != Requirement->Alignment) { + if (Active->Amount != Requirement->Amount || + Active->PreparedBytes != Requirement->PreparedBytes) { return makeUnexpected(ErrorCode::VerificationError, "RISC-V call-frame setup and destroy requirements differ"); } diff --git a/backend/lib/Target/RISCV/RISCVRegisterAllocation.cpp b/backend/lib/Target/RISCV/RISCVRegisterAllocation.cpp index 81fc6dc..aee09a9 100644 --- a/backend/lib/Target/RISCV/RISCVRegisterAllocation.cpp +++ b/backend/lib/Target/RISCV/RISCVRegisterAllocation.cpp @@ -14,7 +14,6 @@ #include #include -#include #include #include #include @@ -37,20 +36,6 @@ struct SpillForm final { return RegisterClassID::fromValue(static_cast(Value)); } -[[nodiscard]] std::optional immediateValue(const MachineOperand &Operand) noexcept { - if (const auto *Immediate = Operand.getIf()) - return *Immediate; - const auto *Integer = Operand.getIf(); - if (Integer == nullptr || Integer->Value.bitWidth() == 0 || Integer->Value.bitWidth() > 64) { - return std::nullopt; - } - std::uint64_t Raw = Integer->Value.words().empty() ? 0 : Integer->Value.words().front(); - const std::uint32_t Width = Integer->Value.bitWidth(); - if (Width < 64 && (Raw & (std::uint64_t{1} << (Width - 1U))) != 0) - Raw |= ~std::uint64_t{0} << Width; - return std::bit_cast(Raw); -} - [[nodiscard]] constexpr std::optional spillForm(RegisterClassID Class) noexcept { if (Class == registerClassID(RegisterClass::GPR) || Class == registerClassID(RegisterClass::GPRC) || @@ -129,23 +114,6 @@ public: RequiredAlignment = std::max(RequiredAlignment, Object.description()->Align.bytes()); } } - for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr; - Block = Block->next()) { - for (const MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr; - Instruction = Instruction->next()) { - if (Instruction->opcode() != targetOpcode(InstructionOpcode::ADJCALLSTACKDOWN) && - Instruction->opcode() != targetOpcode(InstructionOpcode::ADJCALLSTACKUP)) { - continue; - } - if (Instruction->operandCount() < 2) - continue; - const auto Requested = immediateValue(*Instruction->operandAt(1)); - if (Requested && *Requested > 0 && - std::has_single_bit(static_cast(*Requested))) { - RequiredAlignment = std::max(RequiredAlignment, static_cast(*Requested)); - } - } - } const bool RequiresRealignment = RequiredAlignment > 16; if (Reg.number() == static_cast(PhysicalRegisterID::X9)) return RequiresRealignment; diff --git a/backend/unittests/Target/RISCVCallLoweringTest.cpp b/backend/unittests/Target/RISCVCallLoweringTest.cpp index d3685ae..1eaf146 100644 --- a/backend/unittests/Target/RISCVCallLoweringTest.cpp +++ b/backend/unittests/Target/RISCVCallLoweringTest.cpp @@ -483,6 +483,8 @@ void testOrdinaryAndIndirectEmission(TestContext &Context) { return; const auto Instructions = instructions(*Block); + rcc::MachineInstr *FrameDown = Instructions.empty() ? nullptr : Instructions.front(); + rcc::MachineInstr *FrameUp = Instructions.size() > 3 ? Instructions[3] : nullptr; rcc::MachineInstr *Call = findInstruction(*Block, rcc::riscv::InstructionOpcode::PseudoCALL); Context.expect(Instructions.size() == 5 && Instructions.front()->opcode() == @@ -497,6 +499,39 @@ void testOrdinaryAndIndirectEmission(TestContext &Context) { Call->operandAt(2)->kind() == rcc::MachineOperand::Kind::RegisterMask, "ordinary emission must materialize canonical full-value copies, call-frame, " "target, ID, and RegMask"); + const rcc::Register StackPointer = + rcc::Register::fromPhysical(physical(rcc::riscv::PhysicalRegisterID::X2)); + const auto HasStackPointerEffects = [StackPointer](const rcc::MachineInstr *Instruction) { + bool HasDef = false; + bool HasUse = false; + if (Instruction == nullptr) + return false; + for (const rcc::MachineOperand &Operand : Instruction->operands()) { + if (!Operand.isRegister() || !Operand.isImplicit() || + Operand.registerValue() != StackPointer) { + continue; + } + HasDef |= Operand.isDef(); + HasUse |= Operand.isUse(); + } + return HasDef && HasUse; + }; + const auto ImmediateAt = [](const rcc::MachineInstr *Instruction, std::size_t Index) { + const rcc::MachineOperand *Operand = + Instruction == nullptr ? nullptr : Instruction->operandAt(Index); + return Operand == nullptr ? nullptr : Operand->getIf(); + }; + const auto *DownAmount = ImmediateAt(FrameDown, 0); + const auto *DownPrepared = ImmediateAt(FrameDown, 1); + const auto *UpAmount = ImmediateAt(FrameUp, 0); + const auto *UpPrepared = ImmediateAt(FrameUp, 1); + Context.expect( + DownAmount != nullptr && *DownAmount == 0 && DownPrepared != nullptr && *DownPrepared == 0 && + UpAmount != nullptr && *UpAmount == 0 && UpPrepared != nullptr && *UpPrepared == 0 && + HasStackPointerEffects(FrameDown) && HasStackPointerEffects(FrameUp) && + !FrameDown->flags().has(rcc::MachineInstrFlag::FrameSetup) && + !FrameUp->flags().has(rcc::MachineInstrFlag::FrameDestroy), + "call-frame pseudos must use LLVM amount/prepared-byte operands and implicit SP effects"); bool HasArgumentUse = false; bool HasResultDef = false; for (const rcc::MachineOperand &Operand : Call->operands()) { diff --git a/backend/unittests/Target/RISCVFrameLoweringTest.cpp b/backend/unittests/Target/RISCVFrameLoweringTest.cpp index 7096840..95d6a18 100644 --- a/backend/unittests/Target/RISCVFrameLoweringTest.cpp +++ b/backend/unittests/Target/RISCVFrameLoweringTest.cpp @@ -347,7 +347,7 @@ buildMoveToDouble(rcc::MachineFunction &Function, const rcc::TargetDescription & [[nodiscard]] rcc::Expected buildCallFramePseudo(rcc::MachineFunction &Function, const rcc::TargetDescription &Target, rcc::MachineBasicBlock &Block, rcc::riscv::InstructionOpcode OpcodeValue, - std::int64_t Amount, std::int64_t Alignment = 16) { + std::int64_t Amount, std::int64_t PreparedBytes = 0) { rcc::MachineIRBuilder Builder(Function, Target); if (auto Result = Builder.setInsertPoint(Block); !Result) return std::unexpected(Result.error()); @@ -356,7 +356,7 @@ buildCallFramePseudo(rcc::MachineFunction &Function, const rcc::TargetDescriptio return std::unexpected(Pending.error()); if (auto Result = Pending->addImmediate(Amount); !Result) return std::unexpected(Result.error()); - if (auto Result = Pending->addImmediate(Alignment); !Result) + if (auto Result = Pending->addImmediate(PreparedBytes); !Result) return std::unexpected(Result.error()); return Pending->build(); } @@ -804,14 +804,14 @@ void testCallFrames(TestContext &Context, const rcc::TargetDescription &Target) auto Computed = createFunction("computed-call-frame", 17); if (!Computed) return; - auto ComputedOutgoingAlign = rcc::Alignment::create(16); + auto ComputedOutgoingAlign = rcc::Alignment::create(64); if (!ComputedOutgoingAlign) return; rcc::StackObjectDesc ComputedOutgoingDescription; ComputedOutgoingDescription.Kind = rcc::StackObjectKind::Target; ComputedOutgoingDescription.Size = 8; ComputedOutgoingDescription.Align = *ComputedOutgoingAlign; - ComputedOutgoingDescription.LocalOffset = 48; + ComputedOutgoingDescription.LocalOffset = 0; ComputedOutgoingDescription.TargetFlags = static_cast(rcc::riscv::CallStackObjectFlag::OutgoingArgument); auto ComputedOutgoing = @@ -825,9 +825,9 @@ void testCallFrames(TestContext &Context, const rcc::TargetDescription &Target) rcc::riscv::InstructionOpcode::SD, physical(rcc::riscv::PhysicalRegisterID::X11), *ComputedLocal, 0) || !buildCallFramePseudo(*Computed->Function, Target, *Computed->Block, - rcc::riscv::InstructionOpcode::ADJCALLSTACKDOWN, 48, 64) || + rcc::riscv::InstructionOpcode::ADJCALLSTACKDOWN, 64) || !buildCallFramePseudo(*Computed->Function, Target, *Computed->Block, - rcc::riscv::InstructionOpcode::ADJCALLSTACKUP, 48, 64) || + rcc::riscv::InstructionOpcode::ADJCALLSTACKUP, 64) || !buildReturn(*Computed->Function, Target, *Computed->Block)) { return; } @@ -846,7 +846,7 @@ void testCallFrames(TestContext &Context, const rcc::TargetDescription &Target) !buildCallFramePseudo(*Mismatched->Function, Target, *Mismatched->Block, rcc::riscv::InstructionOpcode::ADJCALLSTACKDOWN, 48, 16) || !buildCallFramePseudo(*Mismatched->Function, Target, *Mismatched->Block, - rcc::riscv::InstructionOpcode::ADJCALLSTACKUP, 48, 32) || + rcc::riscv::InstructionOpcode::ADJCALLSTACKUP, 48, 24) || !buildReturn(*Mismatched->Function, Target, *Mismatched->Block)) { return; } @@ -877,17 +877,18 @@ void testCallFrames(TestContext &Context, const rcc::TargetDescription &Target) return; auto Variable = createStackObject(*Dynamic->Function, rcc::StackObjectKind::DynamicAlloca, std::nullopt, 16); - if (!Variable || + auto OverAligned = createStackObject(*Dynamic->Function, rcc::StackObjectKind::Default, 8, 32); + if (!Variable || !OverAligned || !buildCallFramePseudo(*Dynamic->Function, Target, *Dynamic->Block, - rcc::riscv::InstructionOpcode::ADJCALLSTACKDOWN, 4096, 32) || + rcc::riscv::InstructionOpcode::ADJCALLSTACKDOWN, 4096) || !buildCallFramePseudo(*Dynamic->Function, Target, *Dynamic->Block, - rcc::riscv::InstructionOpcode::ADJCALLSTACKUP, 4096, 32) || + rcc::riscv::InstructionOpcode::ADJCALLSTACKUP, 4096) || !buildReturn(*Dynamic->Function, Target, *Dynamic->Block)) { return; } Context.expect(rcc::riscv::registerAllocationInfo().isReservedForFunction( *Dynamic->Function, physReg(rcc::riscv::PhysicalRegisterID::X9), Target), - "over-aligned call frames must reserve BP before register allocation"); + "over-aligned dynamic frames must reserve BP before register allocation"); auto DynamicResult = runFrameLowering(*Dynamic->Function, Target); Context.expect( DynamicResult && -- cgit v1.2.3