From 7b3dcced70be227bb43e71d84d11a2f15fb25072 Mon Sep 17 00:00:00 2001 From: Richard-Qin-X Date: Wed, 26 Aug 2026 17:55:16 +0800 Subject: [RISCV] Implement push-pop optimization --- backend/lib/Target/RISCV/RISCVPushPopOptimizer.cpp | 270 +++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 backend/lib/Target/RISCV/RISCVPushPopOptimizer.cpp (limited to 'backend/lib/Target/RISCV/RISCVPushPopOptimizer.cpp') diff --git a/backend/lib/Target/RISCV/RISCVPushPopOptimizer.cpp b/backend/lib/Target/RISCV/RISCVPushPopOptimizer.cpp new file mode 100644 index 0000000..16928fb --- /dev/null +++ b/backend/lib/Target/RISCV/RISCVPushPopOptimizer.cpp @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "RISCV.h" +#include "RISCVInstrInfo.h" +#include "rcc/CodeGen/InstructionDescriptor.h" +#include "rcc/CodeGen/LiveRegUnits.h" +#include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineFunction.h" +#include "rcc/CodeGen/MachineIRBuilder.h" +#include "rcc/CodeGen/MachineInstr.h" +#include "rcc/CodeGen/MachineOperand.h" +#include "rcc/CodeGen/RegisterMask.h" +#include "rcc/CodeGen/TargetDescription.h" +#include "rcc/Target/RISCV/RISCVRegisters.h" +#include "rcc/Target/RISCV/RISCVTarget.h" + +#include +#include +#include +#include + +namespace rcc::riscv { +namespace { + +struct PushPopOpcodes final { + Opcode Pop; + Opcode PopRet; + Opcode PopRetZero; +}; + +[[nodiscard]] const MachinePassContract &pushPopContract() { + static const MachinePassContract Contract = [] { + MachinePassContract Value{ + .Name = "riscv-push-pop-opt", + .Requires = {MachineFunctionProperty::NoPHIs, MachineFunctionProperty::NoVRegs, + MachineFunctionProperty::FrameFinalized, + MachineFunctionProperty::NoFrameIndices}, + .Sets = {}, + .Clears = {}, + .RequiredAnalyses = {}, + .VerificationStage = PipelineStage::PostRAFinalized, + }; + Value.ThreadSafety = PassThreadSafety::ThreadSafe; + Value.DeclaredChanges = {MachinePassChange::Instructions, MachinePassChange::Operands}; + return Value; + }(); + return Contract; +} + +[[nodiscard]] bool intersects(const LiveRegUnits &Left, const LiveRegUnits &Right) noexcept { + for (const auto &[Unit, Lanes] : Left.entries()) + if (Right.intersects(Unit, Lanes)) + return true; + return false; +} + +[[nodiscard]] Expected accumulateRegisterEffects(const MachineInstr &Instruction, + const TargetDescription &Target, + LiveRegUnits &Modified, LiveRegUnits &Used) { + for (const MachineOperand &Operand : Instruction.operands()) { + if (Operand.isDebugUse()) + continue; + if (Operand.isRegister()) { + const std::optional Reg = Operand.registerValue(); + if (!Reg || !Reg->isPhysical() || Reg->isNoRegister()) + continue; + auto Units = physicalOperandUnits(Operand, Target); + if (!Units) + return std::unexpected(Units.error()); + for (const PhysicalUnitAccess &Access : *Units) { + if (Operand.isDef()) + Modified.add(Access.Unit, Access.Lanes); + if (Operand.isUse()) + Used.add(Access.Unit, Access.Lanes); + } + continue; + } + if (const auto *Mask = Operand.getIf(); + Mask != nullptr && Mask->Mask != nullptr) { + if (auto Added = addRegisterMaskDefs(Modified, *Mask->Mask, Target); !Added) + return std::unexpected(Added.error()); + continue; + } + if (const auto *Mask = Operand.getIf(); + Mask != nullptr && Mask->Mask != nullptr) { + if (auto Added = addRegisterMaskUses(Used, *Mask->Mask, Target); !Added) + return std::unexpected(Added.error()); + } + } + return {}; +} + +[[nodiscard]] MachineInstr *previousNonDebug(MachineInstr &Instruction) noexcept { + MachineInstr *Previous = Instruction.previous(); + while (Previous != nullptr && Previous->isDebugInstr()) + Previous = Previous->previous(); + return Previous; +} + +[[nodiscard]] MachineInstr *firstTerminator(MachineBasicBlock &Block, + const TargetDescription &Target) noexcept { + for (MachineInstr *Instruction = Block.firstInstruction(); Instruction != nullptr; + Instruction = Instruction->next()) { + const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction->opcode()); + if (Descriptor != nullptr && Descriptor->Flags.has(InstructionFlag::Terminator)) + return Instruction; + } + return nullptr; +} + +[[nodiscard]] Expected removeZeroReturnAssignment(MachineInstr &Pop, + const TargetDescription &Target, + PhysicalRegister ReturnRegister, + PhysicalRegister ZeroRegister) { + const PhysicalRegisterDesc *ReturnDescription = + Target.registers().physicalRegister(ReturnRegister); + if (ReturnDescription == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V return register has no register-unit description"); + const LiveRegUnits ReturnUnits = physicalRegisterCoverage(*ReturnDescription); + LiveRegUnits Modified; + LiveRegUnits Used; + for (MachineInstr *Instruction = previousNonDebug(Pop); Instruction != nullptr; + Instruction = previousNonDebug(*Instruction)) { + if (const std::optional Copy = instrInfo().getCopyOperands(*Instruction)) { + const std::optional Destination = Copy->Destination->registerValue(); + const std::optional Source = Copy->Source->registerValue(); + if (Destination && Source && Destination->physicalRegister() == ReturnRegister && + Source->physicalRegister() == ZeroRegister) { + MachineBasicBlock *Parent = Instruction->parent(); + if (Parent == nullptr) + return makeUnexpected(ErrorCode::InternalError, + "RISC-V return-value assignment became detached"); + if (auto Removed = Parent->removeInstruction(*Instruction); !Removed) + return std::unexpected(Removed.error()); + return true; + } + } + if (auto Accumulated = accumulateRegisterEffects(*Instruction, Target, Modified, Used); + !Accumulated) { + return std::unexpected(Accumulated.error()); + } + if (intersects(Modified, ReturnUnits) || intersects(Used, ReturnUnits)) + return false; + } + return false; +} + +[[nodiscard]] Expected buildPopReturn(MachineInstr &Pop, MachineInstr &Return, + bool ReturnZero, + const PushPopOpcodes &Opcodes, + MachineFunction &Function, + const TargetDescription &Target) { + const InstructionDescriptor *PopDescriptor = Target.instructionDescriptor(Pop.opcode()); + if (PopDescriptor == nullptr || PopDescriptor->Operands.size() != 2U) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V CM_POP has an invalid instruction descriptor"); + MachineIRBuilder Builder(Function, Target); + if (auto Point = Builder.setInsertPointBefore(Return); !Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(ReturnZero ? Opcodes.PopRetZero : Opcodes.PopRet); + if (!Pending) + return std::unexpected(Pending.error()); + for (std::size_t Index = 0; Index < PopDescriptor->Operands.size(); ++Index) { + const MachineOperand *Operand = Pop.operandAt(Index); + if (Operand == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V CM_POP is missing an explicit operand"); + if (auto Added = Pending->addOperand(Operand->cloneDetached()); !Added) + return std::unexpected(Added.error()); + } + const std::size_t FirstDynamicImplicit = PopDescriptor->Operands.size() + + PopDescriptor->ImplicitDefs.size() + + PopDescriptor->ImplicitUses.size(); + for (std::size_t Index = FirstDynamicImplicit; Index < Pop.operandCount(); ++Index) { + const MachineOperand *Operand = Pop.operandAt(Index); + if (Operand == nullptr || !Operand->isImplicit()) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V CM_POP has a malformed dynamic implicit operand"); + } + if (auto Added = Pending->addImplicitOperand(Operand->cloneDetached()); !Added) + return std::unexpected(Added.error()); + } + if (auto Flags = Pending->setFlags({MachineInstrFlag::FrameDestroy}); !Flags) + return std::unexpected(Flags.error()); + if (Return.debugLocation()) { + if (auto Location = Pending->setDebugLocation(*Return.debugLocation()); !Location) + return std::unexpected(Location.error()); + } + return Pending->build(); +} + +class RISCVPushPopOptimizationPass final : public MachineFunctionPass { +public: + explicit RISCVPushPopOptimizationPass(const RISCVSubtarget &Subtarget) noexcept + : Subtarget(&Subtarget) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override { + return pushPopContract(); + } + + [[nodiscard]] Expected run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &) override { + MachinePassResult Result; + if (!Subtarget->features().has(RISCVExtension::Zcmp)) { + Result.Preserved = PreservedAnalyses::all(); + return Result; + } + + const InstructionDescriptor *Pop = Target.instructions().descriptor("CM_POP"); + const InstructionDescriptor *PopRet = Target.instructions().descriptor("CM_POPRET"); + const InstructionDescriptor *PopRetZero = Target.instructions().descriptor("CM_POPRETZ"); + if (Pop == nullptr || PopRet == nullptr || PopRetZero == nullptr) { + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V Zcmp push/pop instructions are not registered"); + } + auto ReturnRegister = physicalRegister(PhysicalRegisterID::X10); + if (!ReturnRegister) + return std::unexpected(ReturnRegister.error()); + auto ZeroRegister = physicalRegister(PhysicalRegisterID::X0); + if (!ZeroRegister) + return std::unexpected(ZeroRegister.error()); + const PushPopOpcodes Opcodes{Pop->InstructionOpcode, PopRet->InstructionOpcode, + PopRetZero->InstructionOpcode}; + + for (MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr; + Block = Block->next()) { + MachineInstr *Return = firstTerminator(*Block, Target); + if (Return == nullptr || + Return->opcode() != instructionOpcode(InstructionOpcode::PseudoRET)) { + continue; + } + MachineInstr *PopInstruction = previousNonDebug(*Return); + if (PopInstruction == nullptr || PopInstruction->opcode() != Opcodes.Pop || + !PopInstruction->flags().has(MachineInstrFlag::FrameDestroy)) { + continue; + } + auto ReturnZero = + removeZeroReturnAssignment(*PopInstruction, Target, *ReturnRegister, *ZeroRegister); + if (!ReturnZero) + return std::unexpected(ReturnZero.error()); + auto NewReturn = + buildPopReturn(*PopInstruction, *Return, *ReturnZero, Opcodes, Function, Target); + if (!NewReturn) + return std::unexpected(NewReturn.error()); + if (auto Removed = Block->removeInstruction(*PopInstruction); !Removed) + return std::unexpected(Removed.error()); + if (auto Removed = Block->removeInstruction(*Return); !Removed) + return std::unexpected(Removed.error()); + Result.Changes.add(MachinePassChange::Instructions); + Result.Changes.add(MachinePassChange::Operands); + } + Result.Changed = !Result.Changes.empty(); + Result.Preserved = Result.Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); + return Result; + } + +private: + const RISCVSubtarget *Subtarget; +}; + +} // namespace + +std::unique_ptr +createPushPopOptimizationPass(const RISCVSubtarget &Subtarget) { + return std::make_unique(Subtarget); +} + +} // namespace rcc::riscv -- cgit v1.2.3