From f8daf08aba1f09c3024a70d3426891112d1662a2 Mon Sep 17 00:00:00 2001 From: Richard-Qin-X Date: Wed, 12 Aug 2026 21:35:21 +0800 Subject: [RISCV] Consolidate instruction behavior in RISCVInstrInfo --- .../include/rcc/CodeGen/MachineCopyPropagation.h | 6 +- .../rcc/CodeGen/MachineOptimizationPipeline.h | 4 +- backend/include/rcc/CodeGen/MachineSSAExit.h | 10 +- backend/include/rcc/CodeGen/TargetCodeGen.h | 38 - backend/include/rcc/CodeGen/TargetInstrInfo.h | 24 +- backend/include/rcc/Target/RISCV/RISCVCodeGen.h | 3 - backend/lib/CodeGen/MachineBranchOptimization.cpp | 5 +- backend/lib/CodeGen/MachineCopyPropagation.cpp | 6 +- backend/lib/CodeGen/MachineFallthrough.cpp | 5 +- .../lib/CodeGen/MachineOptimizationPipeline.cpp | 18 +- backend/lib/CodeGen/MachineSSAExit.cpp | 16 +- backend/lib/CodeGen/MachineTailDuplication.cpp | 3 +- backend/lib/CodeGen/MachineTailMerge.cpp | 2 +- backend/lib/Target/RISCV/CMakeLists.txt | 11 +- backend/lib/Target/RISCV/RISCVBranchInfo.cpp | 738 ------------------ backend/lib/Target/RISCV/RISCVCodeGen.cpp | 197 ----- backend/lib/Target/RISCV/RISCVInstrInfo.cpp | 843 +++++++++++++++++++++ backend/lib/Target/RISCV/RISCVInstrInfo.h | 14 + .../lib/Target/RISCV/RISCVOptimizationPipeline.cpp | 4 +- backend/lib/Target/RISCV/RISCVPipeline.cpp | 9 +- backend/unittests/CodeGen/MachineSSAExitTest.cpp | 71 +- backend/unittests/Target/CMakeLists.txt | 8 + backend/unittests/Target/RISCVLateCodeGenTest.cpp | 55 +- backend/unittests/Target/RISCVSSAExitTest.cpp | 85 ++- 24 files changed, 1097 insertions(+), 1078 deletions(-) delete mode 100644 backend/lib/Target/RISCV/RISCVBranchInfo.cpp create mode 100644 backend/lib/Target/RISCV/RISCVInstrInfo.cpp create mode 100644 backend/lib/Target/RISCV/RISCVInstrInfo.h (limited to 'backend') diff --git a/backend/include/rcc/CodeGen/MachineCopyPropagation.h b/backend/include/rcc/CodeGen/MachineCopyPropagation.h index e8ee788..43baa7e 100644 --- a/backend/include/rcc/CodeGen/MachineCopyPropagation.h +++ b/backend/include/rcc/CodeGen/MachineCopyPropagation.h @@ -10,7 +10,7 @@ namespace rcc { -class TargetSSAInfo; +class TargetInstrInfo; enum class MachineCopyPropagationStage : std::uint8_t { PreRegisterAllocation, @@ -23,7 +23,7 @@ struct MachineCopyPropagationOptions final { class MachineCopyPropagationPass final : public MachineFunctionPass { public: - MachineCopyPropagationPass(const TargetSSAInfo &Info, MachineCopyPropagationStage Stage, + MachineCopyPropagationPass(const TargetInstrInfo &Info, MachineCopyPropagationStage Stage, MachineCopyPropagationOptions Options = {}) noexcept : Info(&Info), Stage(Stage), Options(Options) {} @@ -37,7 +37,7 @@ private: const TargetDescription &Target, MachineActionReplay &Actions) const; - const TargetSSAInfo *Info; + const TargetInstrInfo *Info; MachineCopyPropagationStage Stage; MachineCopyPropagationOptions Options; }; diff --git a/backend/include/rcc/CodeGen/MachineOptimizationPipeline.h b/backend/include/rcc/CodeGen/MachineOptimizationPipeline.h index 2cf1bcd..10bcbc6 100644 --- a/backend/include/rcc/CodeGen/MachineOptimizationPipeline.h +++ b/backend/include/rcc/CodeGen/MachineOptimizationPipeline.h @@ -13,16 +13,14 @@ class TargetMachineCombinerInfo; class TargetMemoryFoldingInfo; class TargetPseudoExpansionInfo; class TargetSchedulingModel; -class TargetSSAInfo; struct MachineOptimizationPipelineTargetInfo final { - const TargetSSAInfo &SSA; + const TargetInstrInfo &InstrInfo; const TargetMachineCombinerInfo &Combiner; const TargetMemoryFoldingInfo &MemoryFolding; const TargetFrameLowering &FrameLowering; const TargetPseudoExpansionInfo &PseudoExpansion; const TargetSchedulingModel &SchedulingModel; - const TargetInstrInfo &BranchInfo; }; [[nodiscard]] Expected registerMachineOptimizationAnalyses(MachineAnalysisManager &Analyses); diff --git a/backend/include/rcc/CodeGen/MachineSSAExit.h b/backend/include/rcc/CodeGen/MachineSSAExit.h index fdf74f0..4b6966d 100644 --- a/backend/include/rcc/CodeGen/MachineSSAExit.h +++ b/backend/include/rcc/CodeGen/MachineSSAExit.h @@ -4,14 +4,14 @@ #define RCC_CODEGEN_MACHINESSAEXIT_H #include "rcc/CodeGen/MachinePass.h" -#include "rcc/CodeGen/TargetCodeGen.h" +#include "rcc/CodeGen/TargetInstrInfo.h" namespace rcc { class PHIEliminationPass final : public MachineFunctionPass { public: PHIEliminationPass() noexcept = default; - explicit PHIEliminationPass(const TargetSSAInfo &Info) noexcept : Info(&Info) {} + explicit PHIEliminationPass(const TargetInstrInfo &Info) noexcept : Info(&Info) {} [[nodiscard]] const MachinePassContract &contract() const noexcept override; [[nodiscard]] Expected run(MachineFunction &Function, @@ -23,13 +23,13 @@ private: const TargetDescription &Target, MachineActionReplay &Actions); - const TargetSSAInfo *Info = nullptr; + const TargetInstrInfo *Info = nullptr; }; class TwoAddressRewritePass final : public MachineFunctionPass { public: TwoAddressRewritePass() noexcept = default; - explicit TwoAddressRewritePass(const TargetSSAInfo &Info) noexcept : Info(&Info) {} + explicit TwoAddressRewritePass(const TargetInstrInfo &Info) noexcept : Info(&Info) {} [[nodiscard]] const MachinePassContract &contract() const noexcept override; [[nodiscard]] Expected run(MachineFunction &Function, @@ -41,7 +41,7 @@ private: const TargetDescription &Target, MachineActionReplay &Actions); - const TargetSSAInfo *Info = nullptr; + const TargetInstrInfo *Info = nullptr; }; } // namespace rcc diff --git a/backend/include/rcc/CodeGen/TargetCodeGen.h b/backend/include/rcc/CodeGen/TargetCodeGen.h index 34a6de4..65f65a4 100644 --- a/backend/include/rcc/CodeGen/TargetCodeGen.h +++ b/backend/include/rcc/CodeGen/TargetCodeGen.h @@ -3,20 +3,14 @@ #ifndef RCC_CODEGEN_TARGETCODEGEN_H #define RCC_CODEGEN_TARGETCODEGEN_H -#include "rcc/CodeGen/MachineIRTypes.h" -#include "rcc/CodeGen/Register.h" #include "rcc/Support/Error.h" -#include #include -#include namespace rcc { class MachineFunction; -class MachineBasicBlock; class MachineInstr; -class MachineRegisterInfo; class TargetDescription; struct VirtualRegisterInfo; @@ -96,38 +90,6 @@ public: } }; -class TargetSSAInfo { -public: - virtual ~TargetSSAInfo() = default; - - [[nodiscard]] virtual Expected isFallthroughEdge(const MachineBasicBlock &From, - const MachineBasicBlock &To, - const MachineFunction &Function, - const TargetDescription &Target) const = 0; - - [[nodiscard]] virtual Expected - rewriteBranchTarget(MachineBasicBlock &Block, MachineBasicBlock &OldTarget, - MachineBasicBlock &NewTarget, MachineFunction &Function, - const TargetDescription &Target) const = 0; - - [[nodiscard]] virtual Expected - insertUnconditionalBranch(MachineBasicBlock &Block, MachineBasicBlock &Target, - MachineFunction &Function, - const TargetDescription &TargetDescription) const = 0; - - [[nodiscard]] virtual Expected validateCopy(Register Destination, - SubRegIndex DestinationSubReg, Register Source, - SubRegIndex SourceSubReg, - const MachineFunction &Function, - const TargetDescription &Target) const = 0; - - [[nodiscard]] virtual Expected tryConvertToThreeAddress(MachineInstr &, std::size_t, - std::size_t, MachineFunction &, - const TargetDescription &) const { - return false; - } -}; - } // namespace rcc #endif // RCC_CODEGEN_TARGETCODEGEN_H diff --git a/backend/include/rcc/CodeGen/TargetInstrInfo.h b/backend/include/rcc/CodeGen/TargetInstrInfo.h index d9f0906..34752c5 100644 --- a/backend/include/rcc/CodeGen/TargetInstrInfo.h +++ b/backend/include/rcc/CodeGen/TargetInstrInfo.h @@ -3,8 +3,11 @@ #ifndef RCC_CODEGEN_TARGETINSTRINFO_H #define RCC_CODEGEN_TARGETINSTRINFO_H +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/Register.h" #include "rcc/Support/Error.h" +#include #include #include @@ -40,8 +43,25 @@ public: MachineBasicBlock &NewTarget, MachineFunction &Function, const TargetDescription &Target) const = 0; [[nodiscard]] virtual Expected - insertUnconditionalBranch(MachineBasicBlock &Block, MachineBasicBlock &TargetBlock, - MachineFunction &Function, const TargetDescription &Target) const = 0; + insertBranch(MachineBasicBlock &Block, MachineBasicBlock &TargetBlock, MachineFunction &Function, + const TargetDescription &Target) const = 0; + + [[nodiscard]] virtual Expected isFallthroughEdge(const MachineBasicBlock &From, + const MachineBasicBlock &To, + const MachineFunction &Function, + const TargetDescription &Target) const = 0; + + [[nodiscard]] virtual Expected validateCopy(Register Destination, + SubRegIndex DestinationSubReg, Register Source, + SubRegIndex SourceSubReg, + const MachineFunction &Function, + const TargetDescription &Target) const = 0; + + [[nodiscard]] virtual Expected tryConvertToThreeAddress(MachineInstr &, std::size_t, + std::size_t, MachineFunction &, + const TargetDescription &) const { + return false; + } [[nodiscard]] virtual Expected instructionSize(const MachineInstr &Instruction, const MachineFunction &Function, diff --git a/backend/include/rcc/Target/RISCV/RISCVCodeGen.h b/backend/include/rcc/Target/RISCV/RISCVCodeGen.h index d68157a..6cda815 100644 --- a/backend/include/rcc/Target/RISCV/RISCVCodeGen.h +++ b/backend/include/rcc/Target/RISCV/RISCVCodeGen.h @@ -7,7 +7,6 @@ #include "rcc/CodeGen/MachineOptimizationPipeline.h" #include "rcc/CodeGen/TargetCodeGen.h" #include "rcc/CodeGen/TargetFrameLowering.h" -#include "rcc/CodeGen/TargetInstrInfo.h" #include "rcc/CodeGen/TargetMemoryFolding.h" #include "rcc/CodeGen/TargetPseudoExpansion.h" #include "rcc/CodeGen/TargetRegisterAllocationInfo.h" @@ -23,13 +22,11 @@ namespace rcc::riscv { createLegalizerInfo(RISCVSubtargetFeatures Features); [[nodiscard]] const TargetRegisterBankInfo ®isterBankInfo() noexcept; [[nodiscard]] const TargetInstructionSelector &instructionSelector() noexcept; -[[nodiscard]] const TargetSSAInfo &ssaInfo() noexcept; [[nodiscard]] const TargetRegisterAllocationInfo ®isterAllocationInfo() noexcept; [[nodiscard]] const TargetFrameLowering &frameLowering() noexcept; [[nodiscard]] const TargetSchedulingModel &schedulingModel() noexcept; [[nodiscard]] const TargetMachineCombinerInfo &machineCombinerInfo() noexcept; [[nodiscard]] const TargetMemoryFoldingInfo &memoryFoldingInfo() noexcept; -[[nodiscard]] const TargetInstrInfo &branchInfo() noexcept; [[nodiscard]] const TargetPseudoExpansionInfo &pseudoExpansionInfo() noexcept; [[nodiscard]] MachineOptimizationPipelineTargetInfo optimizationPipelineInfo() noexcept; diff --git a/backend/lib/CodeGen/MachineBranchOptimization.cpp b/backend/lib/CodeGen/MachineBranchOptimization.cpp index 19127ea..df25a4a 100644 --- a/backend/lib/CodeGen/MachineBranchOptimization.cpp +++ b/backend/lib/CodeGen/MachineBranchOptimization.cpp @@ -142,7 +142,7 @@ namespace { if (!RemovedUnconditional) return std::unexpected(RemovedUnconditional.error()); } - auto Inserted = Info.insertUnconditionalBranch(*Block, *Selected, Function, Target); + auto Inserted = Info.insertBranch(*Block, *Selected, Function, Target); if (!Inserted) return std::unexpected(Inserted.error()); @@ -269,8 +269,7 @@ namespace { } else { if (Predecessor->next() != Block) continue; - auto Inserted = - Info.insertUnconditionalBranch(*Predecessor, *Successor, Function, Target); + auto Inserted = Info.insertBranch(*Predecessor, *Successor, Function, Target); if (!Inserted) return std::unexpected(Inserted.error()); } diff --git a/backend/lib/CodeGen/MachineCopyPropagation.cpp b/backend/lib/CodeGen/MachineCopyPropagation.cpp index f9211f9..60992a5 100644 --- a/backend/lib/CodeGen/MachineCopyPropagation.cpp +++ b/backend/lib/CodeGen/MachineCopyPropagation.cpp @@ -12,8 +12,8 @@ #include "rcc/CodeGen/MachineOperand.h" #include "rcc/CodeGen/MachineRegisterInfo.h" #include "rcc/CodeGen/RegisterMask.h" -#include "rcc/CodeGen/TargetCodeGen.h" #include "rcc/CodeGen/TargetDescription.h" +#include "rcc/CodeGen/TargetInstrInfo.h" #include "rcc/CodeGen/TargetRegisterInfo.h" #include @@ -127,7 +127,7 @@ struct CopyOperands final { [[nodiscard]] Expected propagateVirtualCopy(MachineInstr &Copy, const CopyOperands &Operands, MachineFunction &Function, const TargetDescription &Target, - const TargetSSAInfo &Info) { + const TargetInstrInfo &Info) { if (!Operands.Destination.isVirtual() || !Operands.Source.isVirtual()) return false; if (Function.registerInfo().getUniqueVRegDef(Operands.Destination) != @@ -231,7 +231,7 @@ struct CopyOperands final { [[nodiscard]] Expected propagatePhysicalCopy(MachineInstr &Copy, const CopyOperands &Operands, MachineFunction &Function, const TargetDescription &Target, - const TargetSSAInfo &Info) { + const TargetInstrInfo &Info) { const std::optional Destination = Operands.Destination.physicalRegister(); const std::optional Source = Operands.Source.physicalRegister(); if (!Destination || !Source || Operands.DestinationOperand->subReg().value() != 0 || diff --git a/backend/lib/CodeGen/MachineFallthrough.cpp b/backend/lib/CodeGen/MachineFallthrough.cpp index a853f96..e912f0b 100644 --- a/backend/lib/CodeGen/MachineFallthrough.cpp +++ b/backend/lib/CodeGen/MachineFallthrough.cpp @@ -83,8 +83,7 @@ normalizeFallthroughs(MachineFunction &Function, const TargetDescription &Target Changed = true; } else if (Analysis->Conditional != nullptr && Analysis->Unconditional == nullptr && Analysis->FalseTarget != nullptr) { - auto Inserted = - Info.insertUnconditionalBranch(*Block, *Analysis->FalseTarget, Function, Target); + auto Inserted = Info.insertBranch(*Block, *Analysis->FalseTarget, Function, Target); if (!Inserted) return std::unexpected(Inserted.error()); Changed = true; @@ -102,7 +101,7 @@ normalizeFallthroughs(MachineFunction &Function, const TargetDescription &Target return makeUnexpected(ErrorCode::VerificationError, "fallthrough normalization encountered a null successor"); } - auto Inserted = Info.insertUnconditionalBranch(*Block, *Successor, Function, Target); + auto Inserted = Info.insertBranch(*Block, *Successor, Function, Target); if (!Inserted) return std::unexpected(Inserted.error()); Changed = true; diff --git a/backend/lib/CodeGen/MachineOptimizationPipeline.cpp b/backend/lib/CodeGen/MachineOptimizationPipeline.cpp index 76dbd44..6d24034 100644 --- a/backend/lib/CodeGen/MachineOptimizationPipeline.cpp +++ b/backend/lib/CodeGen/MachineOptimizationPipeline.cpp @@ -69,10 +69,10 @@ addSelectedSSAOptimizationPipeline(MachinePassManager &Pipeline, Expected addPreRegisterAllocationOptimizationPipeline( MachinePassManager &Pipeline, const MachineOptimizationPipelineTargetInfo &TargetInfo) { - auto Added = addPass(Pipeline, TargetInfo.BranchInfo); + auto Added = addPass(Pipeline, TargetInfo.InstrInfo); if (!Added) return std::unexpected(Added.error()); - Added = addPass(Pipeline, TargetInfo.SSA, + Added = addPass(Pipeline, TargetInfo.InstrInfo, MachineCopyPropagationStage::PreRegisterAllocation); if (!Added) return std::unexpected(Added.error()); @@ -92,7 +92,7 @@ addPostRegisterAllocationPipeline(MachinePassManager &Pipeline, auto Added = addPass(Pipeline); if (!Added) return std::unexpected(Added.error()); - Added = addPass(Pipeline, TargetInfo.SSA, + Added = addPass(Pipeline, TargetInfo.InstrInfo, MachineCopyPropagationStage::PostRegisterAllocation); if (!Added) return std::unexpected(Added.error()); @@ -128,29 +128,29 @@ addPostRegisterAllocationPipeline(MachinePassManager &Pipeline, Added = addPass(Pipeline, TargetInfo.SchedulingModel); if (!Added) return std::unexpected(Added.error()); - Added = addPass(Pipeline, TargetInfo.BranchInfo); + Added = addPass(Pipeline, TargetInfo.InstrInfo); if (!Added) return std::unexpected(Added.error()); - Added = addPass(Pipeline, TargetInfo.BranchInfo); + Added = addPass(Pipeline, TargetInfo.InstrInfo); if (!Added) return std::unexpected(Added.error()); Added = addPass(Pipeline); if (!Added) return std::unexpected(Added.error()); - Added = addPass(Pipeline, TargetInfo.BranchInfo); + Added = addPass(Pipeline, TargetInfo.InstrInfo); if (!Added) return std::unexpected(Added.error()); - Added = addPass(Pipeline, TargetInfo.BranchInfo); + Added = addPass(Pipeline, TargetInfo.InstrInfo); if (!Added) return std::unexpected(Added.error()); Added = addPass(Pipeline, TargetInfo.PseudoExpansion, - TargetInfo.BranchInfo); + TargetInfo.InstrInfo); if (!Added) return std::unexpected(Added.error()); Added = addPass(Pipeline); if (!Added) return std::unexpected(Added.error()); - return addPass(Pipeline, TargetInfo.BranchInfo); + return addPass(Pipeline, TargetInfo.InstrInfo); } } // namespace rcc diff --git a/backend/lib/CodeGen/MachineSSAExit.cpp b/backend/lib/CodeGen/MachineSSAExit.cpp index 76963ea..ec10b3b 100644 --- a/backend/lib/CodeGen/MachineSSAExit.cpp +++ b/backend/lib/CodeGen/MachineSSAExit.cpp @@ -167,7 +167,7 @@ struct PhiPlan final { [[nodiscard]] Expected buildCopy(MachineFunction &Function, const TargetDescription &Target, - const TargetSSAInfo &TargetInfo, MachineBasicBlock &Block, MachineInstr *Before, + const TargetInstrInfo &TargetInfo, MachineBasicBlock &Block, MachineInstr *Before, RegisterValue Destination, RegisterValue Source, const MachineInstr *DebugSource) { auto Legality = TargetInfo.validateCopy(Destination.Reg, Destination.SubReg, Source.Reg, Source.SubReg, Function, Target); @@ -388,7 +388,7 @@ buildImplicitDef(MachineFunction &Function, const TargetDescription &Target, [[nodiscard]] Expected placePhiCopiesOnEdge(MachineFunction &Function, const TargetDescription &Target, - const TargetSSAInfo &TargetInfo, MachineBasicBlock &From, + const TargetInstrInfo &TargetInfo, MachineBasicBlock &From, MachineBasicBlock &To, std::unordered_map &Cache, MachinePassResult &PassResult) { @@ -415,7 +415,7 @@ placePhiCopiesOnEdge(MachineFunction &Function, const TargetDescription &Target, if (!OtherFallthrough) return std::unexpected(OtherFallthrough.error()); if (*OtherFallthrough) { - auto Branch = TargetInfo.insertUnconditionalBranch(*LayoutPredecessor, To, Function, Target); + auto Branch = TargetInfo.insertBranch(*LayoutPredecessor, To, Function, Target); if (!Branch) return std::unexpected(Branch.error()); PassResult.Changed = true; @@ -445,7 +445,7 @@ placePhiCopiesOnEdge(MachineFunction &Function, const TargetDescription &Target, auto LiveIns = copyLiveIns(To, *EdgeBlock); if (!LiveIns) return std::unexpected(LiveIns.error()); - auto Branch = TargetInfo.insertUnconditionalBranch(*EdgeBlock, To, Function, Target); + auto Branch = TargetInfo.insertBranch(*EdgeBlock, To, Function, Target); if (!Branch) return std::unexpected(Branch.error()); @@ -526,7 +526,7 @@ placePhiCopiesOnEdge(MachineFunction &Function, const TargetDescription &Target, [[nodiscard]] Expected expandRegSequence(MachineInstr &Instruction, MachineFunction &Function, const TargetDescription &Target, - const TargetSSAInfo &TargetInfo) { + const TargetInstrInfo &TargetInfo) { if (Instruction.operandCount() < 3 || (Instruction.operandCount() - 1U) % 2U != 0) { return makeUnexpected(ErrorCode::VerificationError, "REG_SEQUENCE requires a destination and source/subregister pairs"); @@ -588,7 +588,7 @@ placePhiCopiesOnEdge(MachineFunction &Function, const TargetDescription &Target, [[nodiscard]] Expected expandInsertSubreg(MachineInstr &Instruction, MachineFunction &Function, const TargetDescription &Target, - const TargetSSAInfo &TargetInfo) { + const TargetInstrInfo &TargetInfo) { if (Instruction.operandCount() != 4) { return makeUnexpected(ErrorCode::VerificationError, "INSERT_SUBREG requires destination, base, inserted value, and index"); @@ -792,7 +792,7 @@ Expected PHIEliminationPass::run(MachineFunction &Function, MachineAnalysisManager &Analyses) { if (Info == nullptr) { return makeUnexpected(ErrorCode::InvalidArgument, - "PHI elimination has no target SSA behavior interface"); + "PHI elimination has no target instruction information"); } auto Required = requireProperties(Function, contract()); if (!Required) @@ -927,7 +927,7 @@ Expected TwoAddressRewritePass::run(MachineFunction &Function MachineAnalysisManager &Analyses) { if (Info == nullptr) { return makeUnexpected(ErrorCode::InvalidArgument, - "two-address rewrite has no target SSA behavior interface"); + "two-address rewrite has no target instruction information"); } auto Required = requireProperties(Function, contract()); if (!Required) diff --git a/backend/lib/CodeGen/MachineTailDuplication.cpp b/backend/lib/CodeGen/MachineTailDuplication.cpp index a4b31b2..80c58c9 100644 --- a/backend/lib/CodeGen/MachineTailDuplication.cpp +++ b/backend/lib/CodeGen/MachineTailDuplication.cpp @@ -230,8 +230,7 @@ duplicateTailIntoPredecessor(const TailCandidate &Candidate, MachineBasicBlock & if (!Removed) return std::unexpected(Removed.error()); } - auto NewBranch = - Info.insertUnconditionalBranch(Predecessor, *Candidate.Successor, Function, Target); + auto NewBranch = Info.insertBranch(Predecessor, *Candidate.Successor, Function, Target); if (!NewBranch) return std::unexpected(NewBranch.error()); MachineCFGUpdater CFG = Function.cfgUpdater(); diff --git a/backend/lib/CodeGen/MachineTailMerge.cpp b/backend/lib/CodeGen/MachineTailMerge.cpp index c8aad80..07608cc 100644 --- a/backend/lib/CodeGen/MachineTailMerge.cpp +++ b/backend/lib/CodeGen/MachineTailMerge.cpp @@ -279,7 +279,7 @@ redirectPredecessor(MachineBasicBlock &Predecessor, MachineBasicBlock &Duplicate Analysis->FalseTarget == &Duplicate); if (!IsImplicitEdge) return false; - auto Inserted = Info.insertUnconditionalBranch(Predecessor, Canonical, Function, Target); + auto Inserted = Info.insertBranch(Predecessor, Canonical, Function, Target); if (!Inserted) return std::unexpected(Inserted.error()); } diff --git a/backend/lib/Target/RISCV/CMakeLists.txt b/backend/lib/Target/RISCV/CMakeLists.txt index 68bd444..d28cdf3 100644 --- a/backend/lib/Target/RISCV/CMakeLists.txt +++ b/backend/lib/Target/RISCV/CMakeLists.txt @@ -47,7 +47,7 @@ set_source_files_properties( add_library( RCCBackendRISCV - RISCVBranchInfo.cpp + RISCVInstrInfo.cpp RISCVCodeGen.cpp RISCVCallLowering.cpp RISCVAssemblyPrinter.cpp @@ -84,7 +84,9 @@ target_include_directories( "$" "$" "$" - PRIVATE "${RCC_BACKEND_BINARY_DIR}/generated/lib/Target/RISCV" + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}" + "${RCC_BACKEND_BINARY_DIR}/generated/lib/Target/RISCV" ) target_link_libraries( RCCBackendRISCV @@ -116,7 +118,7 @@ install( ) rcc_backend_register_format_files( - "${CMAKE_CURRENT_SOURCE_DIR}/RISCVBranchInfo.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVInstrInfo.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVAssemblyPrinter.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVCodeGen.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVCallLowering.cpp" @@ -140,6 +142,7 @@ rcc_backend_register_format_files( "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTarget.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTargetConstant.cpp" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVCodeGen.h" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVInstrInfo.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVAssemblyPrinter.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVCallLowering.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVInstPrinter.h" @@ -156,7 +159,7 @@ rcc_backend_register_format_files( "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVTargetRelocationKinds.h" ) rcc_backend_register_tidy_files( - "${CMAKE_CURRENT_SOURCE_DIR}/RISCVBranchInfo.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVInstrInfo.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVAssemblyPrinter.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVCodeGen.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVCallLowering.cpp" diff --git a/backend/lib/Target/RISCV/RISCVBranchInfo.cpp b/backend/lib/Target/RISCV/RISCVBranchInfo.cpp deleted file mode 100644 index 059bbef..0000000 --- a/backend/lib/Target/RISCV/RISCVBranchInfo.cpp +++ /dev/null @@ -1,738 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rcc/CodeGen/InstructionDescriptor.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/MachineRegisterInfo.h" -#include "rcc/CodeGen/TargetDescription.h" -#include "rcc/Target/RISCV/RISCVCodeGen.h" -#include "rcc/Target/RISCV/RISCVInstructions.h" -#include "rcc/Target/RISCV/RISCVRegisters.h" -#include "rcc/Target/RISCV/RISCVTarget.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace rcc::riscv { -namespace { - -constexpr std::uint32_t PCRelativeHighFlag = 2; -constexpr std::uint32_t PCRelativeLowFlag = 4; - -[[nodiscard]] constexpr Opcode targetOpcode(InstructionOpcode Value) noexcept { - return Opcode::fromValue(static_cast(Value)); -} - -[[nodiscard]] bool isConditionalBranch(Opcode Value) noexcept { - return Value == targetOpcode(InstructionOpcode::BEQ) || - Value == targetOpcode(InstructionOpcode::BNE) || - Value == targetOpcode(InstructionOpcode::BLT) || - Value == targetOpcode(InstructionOpcode::BGE) || - Value == targetOpcode(InstructionOpcode::BLTU) || - Value == targetOpcode(InstructionOpcode::BGEU) || - Value == targetOpcode(InstructionOpcode::C_BEQZ) || - Value == targetOpcode(InstructionOpcode::C_BNEZ); -} - -[[nodiscard]] std::optional reversedOpcode(Opcode Value) noexcept { - constexpr std::array, 4> Pairs = {{ - {InstructionOpcode::BEQ, InstructionOpcode::BNE}, - {InstructionOpcode::BLT, InstructionOpcode::BGE}, - {InstructionOpcode::BLTU, InstructionOpcode::BGEU}, - {InstructionOpcode::C_BEQZ, InstructionOpcode::C_BNEZ}, - }}; - for (const auto &[Left, Right] : Pairs) { - if (Value == targetOpcode(Left)) - return targetOpcode(Right); - if (Value == targetOpcode(Right)) - return targetOpcode(Left); - } - return std::nullopt; -} - -[[nodiscard]] bool isZeroRegister(const MachineOperand *Operand) noexcept { - const Register *Reg = Operand == nullptr ? nullptr : Operand->getIf(); - const std::optional Physical = - Reg == nullptr ? std::nullopt : Reg->physicalRegister(); - return Physical && Physical->number() == static_cast(PhysicalRegisterID::X0); -} - -[[nodiscard]] bool sameRegister(const MachineOperand *Left, const MachineOperand *Right) noexcept { - const Register *LeftReg = Left == nullptr ? nullptr : Left->getIf(); - const Register *RightReg = Right == nullptr ? nullptr : Right->getIf(); - return LeftReg != nullptr && RightReg != nullptr && *LeftReg == *RightReg && - Left->subReg() == Right->subReg(); -} - -[[nodiscard]] bool isUnconditionalBranch(const MachineInstr &Instruction) noexcept { - if (Instruction.opcode() == targetOpcode(InstructionOpcode::C_J) || - Instruction.opcode() == targetOpcode(InstructionOpcode::PseudoBR)) { - return true; - } - return Instruction.opcode() == targetOpcode(InstructionOpcode::JAL) && - isZeroRegister(Instruction.operandAt(0)); -} - -[[nodiscard]] std::size_t branchTargetIndex(Opcode Value) noexcept { - if (Value == targetOpcode(InstructionOpcode::C_J) || - Value == targetOpcode(InstructionOpcode::PseudoBR)) { - return 0; - } - if (Value == targetOpcode(InstructionOpcode::C_BEQZ) || - Value == targetOpcode(InstructionOpcode::C_BNEZ)) { - return 1; - } - if (Value == targetOpcode(InstructionOpcode::JAL)) - return 1; - return 2; -} - -[[nodiscard]] Expected directTarget(MachineInstr &Instruction) { - if (!isConditionalBranch(Instruction.opcode()) && - Instruction.opcode() != targetOpcode(InstructionOpcode::JAL) && - Instruction.opcode() != targetOpcode(InstructionOpcode::C_J) && - Instruction.opcode() != targetOpcode(InstructionOpcode::PseudoBR)) { - return static_cast(nullptr); - } - MachineOperand *Operand = Instruction.operandAt(branchTargetIndex(Instruction.opcode())); - const BasicBlockOperand *Block = - Operand == nullptr ? nullptr : Operand->getIf(); - if (Block == nullptr || Block->Block == nullptr) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V direct branch has no basic-block target"); - } - return Block->Block; -} - -[[nodiscard]] Expected setDirectTarget(MachineInstr &Instruction, - MachineBasicBlock &TargetBlock) { - MachineOperand *Operand = Instruction.operandAt(branchTargetIndex(Instruction.opcode())); - if (Operand == nullptr || Operand->kind() != MachineOperand::Kind::BasicBlock) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V direct branch has no mutable basic-block target"); - } - return Operand->setBasicBlock(TargetBlock); -} - -[[nodiscard]] Expected removeInstruction(MachineInstr &Instruction) { - MachineBasicBlock *Block = Instruction.parent(); - if (Block == nullptr) { - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V branch instruction is not linked to a block"); - } - return Block->removeInstruction(Instruction); -} - -[[nodiscard]] Expected physical(PhysicalRegisterID ID) { - auto Reg = physicalRegister(ID); - if (!Reg) - return std::unexpected(Reg.error()); - return *Reg; -} - -[[nodiscard]] Expected -buildJAL(MachineFunction &Function, const TargetDescription &Target, MachineBasicBlock &Block, - MachineInstr *Before, PhysicalRegister Destination, MachineBasicBlock &TargetBlock) { - MachineIRBuilder Builder(Function, Target); - Expected Point = - Before == nullptr ? Builder.setInsertPoint(Block) : Builder.setInsertPointBefore(*Before); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(targetOpcode(InstructionOpcode::JAL)); - if (!Pending) - return std::unexpected(Pending.error()); - auto Def = Pending->addDef(Register::fromPhysical(Destination)); - if (!Def) - return std::unexpected(Def.error()); - auto TargetOperand = Pending->addBasicBlock(TargetBlock); - if (!TargetOperand) - return std::unexpected(TargetOperand.error()); - return Pending->build(); -} - -[[nodiscard]] Expected copySourceIdentity(const MachineInstr &Source, - MachineInstr &Destination) { - if (Source.sourceLocation()) - Destination.setSourceLocation(*Source.sourceLocation()); - if (Source.debugLocation()) { - auto Debug = Destination.setDebugLocation(*Source.debugLocation()); - if (!Debug) - return std::unexpected(Debug.error()); - } - if (Source.origin()) - Destination.setOrigin(*Source.origin()); - return {}; -} - -[[nodiscard]] Expected copyTrailingAnnotations(const MachineInstr &Source, - MachineInstr &Destination) { - if (Source.debugInstrNumber() != 0) { - auto DebugNumber = Destination.setDebugInstrNumber(Source.debugInstrNumber()); - if (!DebugNumber) - return std::unexpected(DebugNumber.error()); - } - if (Source.postInstrSymbol()) { - auto Symbol = Destination.setPostInstrSymbol(std::string(*Source.postInstrSymbol())); - if (!Symbol) - return std::unexpected(Symbol.error()); - } - if (Source.heapAllocMarker()) { - auto Marker = Destination.setHeapAllocMarker(*Source.heapAllocMarker()); - if (!Marker) - return std::unexpected(Marker.error()); - } - if (Source.pcSections()) { - auto Sections = Destination.setPCSections(*Source.pcSections()); - if (!Sections) - return std::unexpected(Sections.error()); - } - if (Source.mmra()) { - auto Metadata = Destination.setMMRA(*Source.mmra()); - if (!Metadata) - return std::unexpected(Metadata.error()); - } - if (Source.cfiType()) { - auto Type = Destination.setCFIType(*Source.cfiType()); - if (!Type) - return std::unexpected(Type.error()); - } - if (Source.deactivationSymbol()) { - auto Symbol = Destination.setDeactivationSymbol(std::string(*Source.deactivationSymbol())); - if (!Symbol) - return std::unexpected(Symbol.error()); - } - return {}; -} - -[[nodiscard]] Expected copySingleInstructionAnnotations(const MachineInstr &Source, - MachineInstr &Destination) { - Destination.setFlags(Source.flags()); - auto Identity = copySourceIdentity(Source, Destination); - if (!Identity) - return std::unexpected(Identity.error()); - if (Source.preInstrSymbol()) { - auto Symbol = Destination.setPreInstrSymbol(std::string(*Source.preInstrSymbol())); - if (!Symbol) - return std::unexpected(Symbol.error()); - } - return copyTrailingAnnotations(Source, Destination); -} - -[[nodiscard]] bool isZeroPhysicalRegister(PhysicalRegister Reg) noexcept { - return Reg.number() == static_cast(PhysicalRegisterID::X0); -} - -[[nodiscard]] Expected -selectLongBranchScratch(const MachineInstr &Instruction, const MachineBasicBlock &TargetBlock, - const MachineFunction &Function, const TargetDescription &Target) { - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf(); - const std::optional DestinationPhysical = - Destination == nullptr ? std::nullopt : Destination->physicalRegister(); - if (!DestinationPhysical) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V long JAL has no physical destination"); - } - if (!isZeroPhysicalRegister(*DestinationPhysical)) - return *DestinationPhysical; - - const auto GPRClass = - RegisterClassID::fromValue(static_cast(RegisterClass::GPR_NX0)); - const RegisterClassDesc *Candidates = Target.registers().registerClass(GPRClass); - if (Candidates == nullptr) { - return makeUnexpected(ErrorCode::InternalError, "RISC-V target has no nonzero GPR class"); - } - for (const PhysicalRegister Candidate : Candidates->AllocationOrder) { - if (Target.registers().isReservedRegister(Candidate) || - Function.registerInfo().isReserved(Candidate)) { - continue; - } - const bool LiveAtTarget = - std::ranges::any_of(TargetBlock.liveIns(), [&](const MachineBasicBlockLiveIn &LiveIn) { - return Target.registers().regsOverlap(Candidate, LiveIn.PhysReg); - }); - if (!LiveAtTarget) - return Candidate; - } - return makeUnexpected(ErrorCode::BranchRelaxationFailed, - "RISC-V long branch has no dead physical scratch register"); -} - -[[nodiscard]] Expected expandCompressedBranch(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) { - MachineBasicBlock *Block = Instruction.parent(); - if (Block == nullptr) - return makeUnexpected(ErrorCode::InvalidArgument, "compressed branch is not linked"); - auto TargetBlock = directTarget(Instruction); - if (!TargetBlock) - return std::unexpected(TargetBlock.error()); - MachineIRBuilder Builder(Function, Target); - auto Point = Builder.setInsertPointBefore(Instruction); - if (!Point) - return std::unexpected(Point.error()); - - if (Instruction.opcode() == targetOpcode(InstructionOpcode::C_J)) { - auto Zero = physical(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - auto Replacement = buildJAL(Function, Target, *Block, &Instruction, *Zero, **TargetBlock); - if (!Replacement) - return std::unexpected(Replacement.error()); - auto Annotations = copySingleInstructionAnnotations(Instruction, **Replacement); - if (!Annotations) - return std::unexpected(Annotations.error()); - auto Removed = removeInstruction(Instruction); - if (!Removed) - return std::unexpected(Removed.error()); - return true; - } - - const MachineOperand *SourceOperand = Instruction.operandAt(0); - const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf(); - if (Source == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "compressed conditional branch has no source register"); - auto Zero = physical(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - const InstructionOpcode Expanded = Instruction.opcode() == targetOpcode(InstructionOpcode::C_BEQZ) - ? InstructionOpcode::BEQ - : InstructionOpcode::BNE; - auto Pending = Builder.buildInstr(targetOpcode(Expanded)); - if (!Pending) - return std::unexpected(Pending.error()); - auto First = Pending->addUse(*Source); - if (!First) - return std::unexpected(First.error()); - auto Second = Pending->addUse(Register::fromPhysical(*Zero)); - if (!Second) - return std::unexpected(Second.error()); - auto Destination = Pending->addBasicBlock(**TargetBlock); - if (!Destination) - return std::unexpected(Destination.error()); - auto Replacement = Pending->build(); - if (!Replacement) - return std::unexpected(Replacement.error()); - auto Annotations = copySingleInstructionAnnotations(Instruction, **Replacement); - if (!Annotations) - return std::unexpected(Annotations.error()); - auto Removed = removeInstruction(Instruction); - if (!Removed) - return std::unexpected(Removed.error()); - return true; -} - -[[nodiscard]] Expected relaxConditionalBranch(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) { - MachineBasicBlock *Block = Instruction.parent(); - if (Block == nullptr) - return makeUnexpected(ErrorCode::InvalidArgument, "conditional branch is not linked"); - auto TrueTarget = directTarget(Instruction); - if (!TrueTarget) - return std::unexpected(TrueTarget.error()); - MachineBasicBlock *FalseTarget = Block->next(); - if (FalseTarget == nullptr || FalseTarget == *TrueTarget || - std::ranges::none_of(Block->successors(), [FalseTarget](const MachineSuccessor &Successor) { - return Successor.Block == FalseTarget; - })) { - return makeUnexpected(ErrorCode::BranchRelaxationFailed, - "RISC-V conditional relaxation requires its false edge as fallthrough"); - } - - auto Reversed = reversedOpcode(Instruction.opcode()); - if (!Reversed) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V conditional branch cannot be inverted"); - auto SetOpcode = Instruction.setOpcode(*Reversed); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - auto SetTarget = setDirectTarget(Instruction, *FalseTarget); - if (!SetTarget) - return std::unexpected(SetTarget.error()); - - MachineInstr *Following = Instruction.next(); - if (Following != nullptr && isUnconditionalBranch(*Following)) { - auto Retargeted = setDirectTarget(*Following, **TrueTarget); - if (!Retargeted) - return std::unexpected(Retargeted.error()); - return true; - } - auto Zero = physical(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - MachineIRBuilder Builder(Function, Target); - auto Point = Builder.setInsertPointAfter(Instruction); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(targetOpcode(InstructionOpcode::JAL)); - if (!Pending) - return std::unexpected(Pending.error()); - auto Def = Pending->addDef(Register::fromPhysical(*Zero)); - if (!Def) - return std::unexpected(Def.error()); - auto Destination = Pending->addBasicBlock(**TrueTarget); - if (!Destination) - return std::unexpected(Destination.error()); - auto LongJump = Pending->build(); - if (!LongJump) - return std::unexpected(LongJump.error()); - auto Identity = copySourceIdentity(Instruction, **LongJump); - if (!Identity) - return std::unexpected(Identity.error()); - MachineInstrFlags OriginalFlags = Instruction.flags(); - const bool HadBundledSuccessor = OriginalFlags.has(MachineInstrFlag::BundledWithSucc); - OriginalFlags.set(MachineInstrFlag::BundledWithSucc); - Instruction.setFlags(OriginalFlags); - MachineInstrFlags LongFlags; - LongFlags.set(MachineInstrFlag::BundledWithPred); - if (HadBundledSuccessor) - LongFlags.set(MachineInstrFlag::BundledWithSucc); - if (OriginalFlags.has(MachineInstrFlag::NoMerge)) - LongFlags.set(MachineInstrFlag::NoMerge); - (*LongJump)->setFlags(LongFlags); - return true; -} - -[[nodiscard]] Expected relaxJAL(MachineInstr &Instruction, std::int64_t Offset, - MachineFunction &Function, const TargetDescription &Target) { - MachineBasicBlock *Block = Instruction.parent(); - if (Block == nullptr) - return makeUnexpected(ErrorCode::InvalidArgument, "RISC-V JAL is not linked"); - if (Offset < std::numeric_limits::min() || - Offset > std::numeric_limits::max()) { - return makeUnexpected(ErrorCode::BranchRelaxationFailed, - "RISC-V PC-relative long branch exceeds the signed 32-bit range"); - } - auto DestinationBlock = directTarget(Instruction); - if (!DestinationBlock) - return std::unexpected(DestinationBlock.error()); - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf(); - if (Destination == nullptr || !Destination->isPhysical()) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V long JAL requires a physical destination register"); - } - auto Scratch = selectLongBranchScratch(Instruction, **DestinationBlock, Function, Target); - if (!Scratch) - return std::unexpected(Scratch.error()); - const std::string Anchor = Instruction.preInstrSymbol() - ? std::string(*Instruction.preInstrSymbol()) - : ".Lrcc_long_" + std::to_string(Function.stableID().value()) + - "_" + std::to_string(Instruction.stableID().value()); - const MachineInstrFlags OriginalFlags = Instruction.flags(); - - MachineIRBuilder Builder(Function, Target); - auto Point = Builder.setInsertPointBefore(Instruction); - if (!Point) - return std::unexpected(Point.error()); - auto HighPending = Builder.buildInstr(targetOpcode(InstructionOpcode::AUIPC)); - if (!HighPending) - return std::unexpected(HighPending.error()); - auto HighDef = HighPending->addDef(Register::fromPhysical(*Scratch)); - if (!HighDef) - return std::unexpected(HighDef.error()); - auto HighTarget = HighPending->addBasicBlock(**DestinationBlock, PCRelativeHighFlag); - if (!HighTarget) - return std::unexpected(HighTarget.error()); - auto High = HighPending->build(); - if (!High) - return std::unexpected(High.error()); - auto HighSymbol = (*High)->setPreInstrSymbol(Anchor); - if (!HighSymbol) - return std::unexpected(HighSymbol.error()); - auto HighIdentity = copySourceIdentity(Instruction, **High); - if (!HighIdentity) - return std::unexpected(HighIdentity.error()); - - auto LowPending = Builder.buildInstr(targetOpcode(InstructionOpcode::JALR)); - if (!LowPending) - return std::unexpected(LowPending.error()); - auto LowDef = LowPending->addDef(*Destination); - if (!LowDef) - return std::unexpected(LowDef.error()); - auto LowBase = LowPending->addUse(Register::fromPhysical(*Scratch)); - if (!LowBase) - return std::unexpected(LowBase.error()); - auto LowSymbol = LowPending->addMCSymbol(Anchor, 0, PCRelativeLowFlag); - if (!LowSymbol) - return std::unexpected(LowSymbol.error()); - auto Low = LowPending->build(); - if (!Low) - return std::unexpected(Low.error()); - auto LowIdentity = copySourceIdentity(Instruction, **Low); - if (!LowIdentity) - return std::unexpected(LowIdentity.error()); - auto LowAnnotations = copyTrailingAnnotations(Instruction, **Low); - if (!LowAnnotations) - return std::unexpected(LowAnnotations.error()); - - MachineInstrFlags HighFlags; - if (OriginalFlags.has(MachineInstrFlag::BundledWithPred)) - HighFlags.set(MachineInstrFlag::BundledWithPred); - HighFlags.set(MachineInstrFlag::BundledWithSucc); - if (OriginalFlags.has(MachineInstrFlag::NoMerge)) - HighFlags.set(MachineInstrFlag::NoMerge); - (*High)->setFlags(HighFlags); - MachineInstrFlags LowFlags = OriginalFlags; - LowFlags.clear(MachineInstrFlag::BundledWithPred); - LowFlags.set(MachineInstrFlag::BundledWithPred); - (*Low)->setFlags(LowFlags); - auto Removed = removeInstruction(Instruction); - if (!Removed) - return std::unexpected(Removed.error()); - return true; -} - -class RISCVBranchInfo final : public TargetInstrInfo { -public: - [[nodiscard]] Expected - analyzeBranch(MachineBasicBlock &Block, MachineFunction &Function, - const TargetDescription &Target) const override { - if (&Block.function() != &Function) - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V branch analysis references another function"); - std::vector Terminators; - for (MachineInstr *Instruction = Block.firstInstruction(); Instruction != nullptr; - Instruction = Instruction->next()) { - const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction->opcode()); - if (Descriptor == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V branch analysis encountered an unknown opcode"); - if (Descriptor->Flags.has(InstructionFlag::Terminator)) - Terminators.push_back(Instruction); - } - - MachineBranchAnalysis Result; - if (Terminators.empty()) - return Result; - if (Terminators.size() > 2) { - Result.HasUnanalyzableTerminator = true; - return Result; - } - MachineInstr *First = Terminators.front(); - if (isConditionalBranch(First->opcode())) { - auto TrueTarget = directTarget(*First); - if (!TrueTarget) - return std::unexpected(TrueTarget.error()); - Result.Conditional = First; - Result.TrueTarget = *TrueTarget; - if (Terminators.size() == 2) { - MachineInstr *Second = Terminators.back(); - if (!isUnconditionalBranch(*Second)) { - Result.HasUnanalyzableTerminator = true; - return Result; - } - auto FalseTarget = directTarget(*Second); - if (!FalseTarget) - return std::unexpected(FalseTarget.error()); - Result.Unconditional = Second; - Result.FalseTarget = *FalseTarget; - } else { - for (const MachineSuccessor &Successor : Block.successors()) { - if (Successor.Block == nullptr || Successor.Block == Result.TrueTarget) - continue; - if (Result.FalseTarget != nullptr) { - Result.HasUnanalyzableTerminator = true; - return Result; - } - Result.FalseTarget = Successor.Block; - } - if (Result.FalseTarget == nullptr) - Result.FalseTarget = Block.next(); - } - return Result; - } - if (Terminators.size() == 1 && isUnconditionalBranch(*First)) { - auto TargetBlock = directTarget(*First); - if (!TargetBlock) - return std::unexpected(TargetBlock.error()); - Result.Unconditional = First; - Result.TrueTarget = *TargetBlock; - return Result; - } - Result.HasUnanalyzableTerminator = true; - return Result; - } - - [[nodiscard]] Expected reverseBranchCondition(MachineInstr &Branch, - MachineBasicBlock &NewTarget, - MachineFunction &Function, - const TargetDescription &) const override { - if (&Branch.function() != &Function || &NewTarget.function() != &Function) - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V branch reversal references another function"); - const std::optional Reversed = reversedOpcode(Branch.opcode()); - if (!Reversed) - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V branch opcode has no inverse condition"); - auto OpcodeResult = Branch.setOpcode(*Reversed); - if (!OpcodeResult) - return std::unexpected(OpcodeResult.error()); - return setDirectTarget(Branch, NewTarget); - } - - [[nodiscard]] Expected removeBranch(MachineInstr &Branch, MachineFunction &Function, - const TargetDescription &) const override { - if (&Branch.function() != &Function) - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V branch removal references another function"); - return removeInstruction(Branch); - } - - [[nodiscard]] Expected rewriteBranchTarget(MachineBasicBlock &Block, - MachineBasicBlock &OldTarget, - MachineBasicBlock &NewTarget, - MachineFunction &Function, - const TargetDescription &) const override { - if (&Block.function() != &Function || &OldTarget.function() != &Function || - &NewTarget.function() != &Function) { - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V branch rewrite references another function"); - } - bool Changed = false; - for (MachineInstr *Instruction = Block.firstInstruction(); Instruction != nullptr; - Instruction = Instruction->next()) { - if (!isConditionalBranch(Instruction->opcode()) && !isUnconditionalBranch(*Instruction)) { - continue; - } - auto CurrentTarget = directTarget(*Instruction); - if (!CurrentTarget) - return std::unexpected(CurrentTarget.error()); - if (*CurrentTarget != &OldTarget) - continue; - auto Rewritten = setDirectTarget(*Instruction, NewTarget); - if (!Rewritten) - return std::unexpected(Rewritten.error()); - Changed = true; - } - return Changed; - } - - [[nodiscard]] Expected - insertUnconditionalBranch(MachineBasicBlock &Block, MachineBasicBlock &TargetBlock, - MachineFunction &Function, - const TargetDescription &Target) const override { - if (&Block.function() != &Function || &TargetBlock.function() != &Function) - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V branch insertion references another function"); - auto Zero = physical(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - return buildJAL(Function, Target, Block, nullptr, *Zero, TargetBlock); - } - - [[nodiscard]] Expected - instructionSize(const MachineInstr &Instruction, const MachineFunction &, - const TargetDescription &Target) const override { - const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction.opcode()); - if (Descriptor == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V instruction-size query encountered an unknown opcode"); - if (!Descriptor->Flags.has(InstructionFlag::ProducesCode)) - return 0; - const auto Category = - std::ranges::find_if(encodingCategories(), [Descriptor](const EncodingCategoryInfo &Info) { - return Info.ID == Descriptor->EncodingCategory; - }); - if (Category == encodingCategories().end() || Category->SizeInBytes == 0) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V producing instruction has no exact encoding size"); - return Category->SizeInBytes; - } - - [[nodiscard]] Expected - branchTarget(MachineInstr &Instruction, MachineFunction &Function, - const TargetDescription &) const override { - if (&Instruction.function() != &Function) - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V branch target query references another function"); - return directTarget(Instruction); - } - - [[nodiscard]] Expected> - evaluateBranchCondition(const MachineInstr &Instruction, const MachineFunction &Function, - const TargetDescription &) const override { - if (&Instruction.function() != &Function) { - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V branch evaluation references another function"); - } - const Opcode Value = Instruction.opcode(); - if (!isConditionalBranch(Value)) - return std::optional{}; - if (Value == targetOpcode(InstructionOpcode::C_BEQZ) || - Value == targetOpcode(InstructionOpcode::C_BNEZ)) { - if (!isZeroRegister(Instruction.operandAt(0))) - return std::optional{}; - return std::optional{Value == targetOpcode(InstructionOpcode::C_BEQZ)}; - } - if (!sameRegister(Instruction.operandAt(0), Instruction.operandAt(1))) - return std::optional{}; - if (Value == targetOpcode(InstructionOpcode::BEQ) || - Value == targetOpcode(InstructionOpcode::BGE) || - Value == targetOpcode(InstructionOpcode::BGEU)) { - return std::optional{true}; - } - return std::optional{false}; - } - - [[nodiscard]] bool isBranchOffsetInRange(const MachineInstr &Instruction, - std::int64_t Offset) const noexcept override { - if ((Offset & 1) != 0) - return false; - const Opcode Value = Instruction.opcode(); - if (Value == targetOpcode(InstructionOpcode::C_BEQZ) || - Value == targetOpcode(InstructionOpcode::C_BNEZ)) { - return Offset >= -256 && Offset <= 254; - } - if (Value == targetOpcode(InstructionOpcode::C_J)) - return Offset >= -2048 && Offset <= 2046; - if (isConditionalBranch(Value)) - return Offset >= -4096 && Offset <= 4094; - if (Value == targetOpcode(InstructionOpcode::JAL) || - Value == targetOpcode(InstructionOpcode::PseudoBR)) { - return Offset >= -1048576 && Offset <= 1048574; - } - return true; - } - - [[nodiscard]] Expected relaxBranch(MachineInstr &Instruction, std::int64_t Offset, - MachineFunction &Function, - const TargetDescription &Target) const override { - if (&Instruction.function() != &Function) - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V branch relaxation references another function"); - if (Instruction.opcode() == targetOpcode(InstructionOpcode::C_J) || - Instruction.opcode() == targetOpcode(InstructionOpcode::C_BEQZ) || - Instruction.opcode() == targetOpcode(InstructionOpcode::C_BNEZ)) { - return expandCompressedBranch(Instruction, Function, Target); - } - if (isConditionalBranch(Instruction.opcode())) - return relaxConditionalBranch(Instruction, Function, Target); - if (Instruction.opcode() == targetOpcode(InstructionOpcode::JAL)) - return relaxJAL(Instruction, Offset, Function, Target); - return false; - } -}; - -} // namespace - -const TargetInstrInfo &branchInfo() noexcept { - static const RISCVBranchInfo Value; - return Value; -} - -} // namespace rcc::riscv diff --git a/backend/lib/Target/RISCV/RISCVCodeGen.cpp b/backend/lib/Target/RISCV/RISCVCodeGen.cpp index d03653f..00dba94 100644 --- a/backend/lib/Target/RISCV/RISCVCodeGen.cpp +++ b/backend/lib/Target/RISCV/RISCVCodeGen.cpp @@ -6512,198 +6512,6 @@ private: } }; -class RISCVSSAInfo final : public TargetSSAInfo { -public: - [[nodiscard]] Expected isFallthroughEdge(const MachineBasicBlock &From, - const MachineBasicBlock &To, - const MachineFunction &, - const TargetDescription &Target) const override { - if (From.next() != &To || - std::ranges::none_of(From.successors(), [&To](const MachineSuccessor &Successor) { - return Successor.Block == &To; - })) { - return false; - } - - bool CanFallThrough = true; - for (const MachineInstr *Instruction = From.firstInstruction(); Instruction != nullptr; - Instruction = Instruction->next()) { - const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction->opcode()); - if (Descriptor == nullptr) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V fallthrough analysis encountered an unknown opcode"); - } - if (!Descriptor->Flags.has(InstructionFlag::Terminator)) - continue; - CanFallThrough = isConditionalBranch(Instruction->opcode()); - } - return CanFallThrough; - } - - [[nodiscard]] Expected rewriteBranchTarget(MachineBasicBlock &Block, - MachineBasicBlock &OldTarget, - MachineBasicBlock &NewTarget, - MachineFunction &Function, - const TargetDescription &Target) const override { - if (&Block.function() != &Function || &OldTarget.function() != &Function || - &NewTarget.function() != &Function) { - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V branch rewrite references another machine function"); - } - - bool Changed = false; - for (MachineInstr *Instruction = Block.firstInstruction(); Instruction != nullptr; - Instruction = Instruction->next()) { - const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction->opcode()); - if (Descriptor == nullptr) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V branch rewrite encountered an unknown opcode"); - } - if (!Descriptor->Flags.has(InstructionFlag::Branch) || - Descriptor->Flags.has(InstructionFlag::IndirectBranch)) { - continue; - } - for (std::size_t Index = 0; Index < Instruction->operandCount(); ++Index) { - MachineOperand *Operand = Instruction->operandAt(Index); - const BasicBlockOperand *TargetOperand = - Operand == nullptr ? nullptr : Operand->getIf(); - if (TargetOperand == nullptr || TargetOperand->Block != &OldTarget) - continue; - auto Rewritten = Operand->setBasicBlock(NewTarget); - if (!Rewritten) - return std::unexpected(Rewritten.error()); - Changed = true; - } - } - return Changed; - } - - [[nodiscard]] Expected - insertUnconditionalBranch(MachineBasicBlock &Block, MachineBasicBlock &TargetBlock, - MachineFunction &Function, - const TargetDescription &Target) const override { - if (&Block.function() != &Function || &TargetBlock.function() != &Function) { - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V branch insertion references another machine function"); - } - auto Zero = physicalRegister(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - MachineIRBuilder Builder(Function, Target); - auto Point = Builder.setInsertPoint(Block); - if (!Point) - return std::unexpected(Point.error()); - auto Branch = Builder.buildInstr(targetOpcode(InstructionOpcode::JAL)); - if (!Branch) - return std::unexpected(Branch.error()); - auto Link = Branch->addDef(Register::fromPhysical(*Zero)); - if (!Link) - return std::unexpected(Link.error()); - auto Destination = Branch->addBasicBlock(TargetBlock); - if (!Destination) - return std::unexpected(Destination.error()); - auto Built = Branch->build(); - if (!Built) - return std::unexpected(Built.error()); - return {}; - } - - [[nodiscard]] Expected validateCopy(Register Destination, SubRegIndex DestinationSubReg, - Register Source, SubRegIndex SourceSubReg, - const MachineFunction &Function, - const TargetDescription &Target) const override { - if (Destination.isNoRegister() || Source.isNoRegister()) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V COPY requires two nonzero registers"); - } - if (DestinationSubReg.value() != 0 || SourceSubReg.value() != 0) { - return makeUnexpected(ErrorCode::Unsupported, "RV64GC does not model register subindices"); - } - - auto DestinationShape = copyShape(Destination, Function, Target); - if (!DestinationShape) - return std::unexpected(DestinationShape.error()); - auto SourceShape = copyShape(Source, Function, Target); - if (!SourceShape) - return std::unexpected(SourceShape.error()); - if (DestinationShape->Bank == SourceShape->Bank) { - if (DestinationShape->Width && SourceShape->Width && - DestinationShape->Width != SourceShape->Width) { - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V same-bank COPY has incompatible register widths"); - } - return {}; - } - - const RegisterBankID GPR = - RegisterBankID::fromValue(static_cast(RegisterBank::GPR)); - const RegisterBankID FPR = - RegisterBankID::fromValue(static_cast(RegisterBank::FPR)); - const bool IsCrossBankMove = (DestinationShape->Bank == GPR && SourceShape->Bank == FPR) || - (DestinationShape->Bank == FPR && SourceShape->Bank == GPR); - if (!IsCrossBankMove) { - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V COPY crosses unsupported register banks"); - } - auto SupportedWidth = [](std::optional Width) { - return !Width || *Width == 32 || *Width == 64; - }; - if (!SupportedWidth(DestinationShape->Width) || !SupportedWidth(SourceShape->Width)) { - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V GPR/FPR COPY requires 32-bit or 64-bit values"); - } - return {}; - } - -private: - struct CopyShape final { - RegisterBankID Bank; - std::optional Width; - }; - - [[nodiscard]] static bool isConditionalBranch(Opcode Value) noexcept { - return Value == targetOpcode(InstructionOpcode::BEQ) || - Value == targetOpcode(InstructionOpcode::BNE) || - Value == targetOpcode(InstructionOpcode::BLT) || - Value == targetOpcode(InstructionOpcode::BGE) || - Value == targetOpcode(InstructionOpcode::BLTU) || - Value == targetOpcode(InstructionOpcode::BGEU); - } - - [[nodiscard]] static Expected copyShape(Register Reg, const MachineFunction &Function, - const TargetDescription &Target) { - if (Reg.isVirtual()) { - auto Info = Function.registerInfo().virtualRegisterInfo(Reg); - if (!Info) - return std::unexpected(Info.error()); - const std::optional &Bank = Info->get().Bank; - const std::optional &RegClass = Info->get().RegClass; - if (!Bank || !RegClass) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V COPY virtual register is not fully constrained"); - } - const RegisterClassDesc *Class = Target.registers().registerClass(RegClass.value()); - if (Class == nullptr) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V COPY references an unknown register class"); - } - return CopyShape{Bank.value(), Class->SizeInBits}; - } - - const std::optional Physical = Reg.physicalRegister(); - if (!Physical || !Target.registers().isKnownPhysicalRegister(*Physical)) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V COPY references an unknown physical register"); - } - for (const RegisterBankDesc &Bank : Target.registers().registerBanks()) { - if (Target.registers().bankContainsRegister(Bank.ID, *Physical)) - return CopyShape{Bank.ID, std::nullopt}; - } - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V physical register is not assigned to a register bank"); - } -}; - } // namespace const TargetLegalizerInfo &legalizerInfo() noexcept { @@ -6725,9 +6533,4 @@ const TargetInstructionSelector &instructionSelector() noexcept { return Value; } -const TargetSSAInfo &ssaInfo() noexcept { - static const RISCVSSAInfo Value; - return Value; -} - } // namespace rcc::riscv diff --git a/backend/lib/Target/RISCV/RISCVInstrInfo.cpp b/backend/lib/Target/RISCV/RISCVInstrInfo.cpp new file mode 100644 index 0000000..6e26e2c --- /dev/null +++ b/backend/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -0,0 +1,843 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "RISCVInstrInfo.h" + +#include "rcc/CodeGen/InstructionDescriptor.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/MachineRegisterInfo.h" +#include "rcc/CodeGen/TargetDescription.h" +#include "rcc/Target/RISCV/RISCVInstructions.h" +#include "rcc/Target/RISCV/RISCVRegisters.h" +#include "rcc/Target/RISCV/RISCVTarget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rcc::riscv { +namespace { + +constexpr std::uint32_t PCRelativeHighFlag = 2; +constexpr std::uint32_t PCRelativeLowFlag = 4; + +[[nodiscard]] constexpr Opcode targetOpcode(InstructionOpcode Value) noexcept { + return Opcode::fromValue(static_cast(Value)); +} + +[[nodiscard]] bool isConditionalBranch(Opcode Value) noexcept { + return Value == targetOpcode(InstructionOpcode::BEQ) || + Value == targetOpcode(InstructionOpcode::BNE) || + Value == targetOpcode(InstructionOpcode::BLT) || + Value == targetOpcode(InstructionOpcode::BGE) || + Value == targetOpcode(InstructionOpcode::BLTU) || + Value == targetOpcode(InstructionOpcode::BGEU) || + Value == targetOpcode(InstructionOpcode::C_BEQZ) || + Value == targetOpcode(InstructionOpcode::C_BNEZ); +} + +[[nodiscard]] std::optional reversedOpcode(Opcode Value) noexcept { + constexpr std::array, 4> Pairs = {{ + {InstructionOpcode::BEQ, InstructionOpcode::BNE}, + {InstructionOpcode::BLT, InstructionOpcode::BGE}, + {InstructionOpcode::BLTU, InstructionOpcode::BGEU}, + {InstructionOpcode::C_BEQZ, InstructionOpcode::C_BNEZ}, + }}; + for (const auto &[Left, Right] : Pairs) { + if (Value == targetOpcode(Left)) + return targetOpcode(Right); + if (Value == targetOpcode(Right)) + return targetOpcode(Left); + } + return std::nullopt; +} + +[[nodiscard]] bool isZeroRegister(const MachineOperand *Operand) noexcept { + const Register *Reg = Operand == nullptr ? nullptr : Operand->getIf(); + const std::optional Physical = + Reg == nullptr ? std::nullopt : Reg->physicalRegister(); + return Physical && Physical->number() == static_cast(PhysicalRegisterID::X0); +} + +[[nodiscard]] bool sameRegister(const MachineOperand *Left, const MachineOperand *Right) noexcept { + const Register *LeftReg = Left == nullptr ? nullptr : Left->getIf(); + const Register *RightReg = Right == nullptr ? nullptr : Right->getIf(); + return LeftReg != nullptr && RightReg != nullptr && *LeftReg == *RightReg && + Left->subReg() == Right->subReg(); +} + +[[nodiscard]] bool isUnconditionalBranch(const MachineInstr &Instruction) noexcept { + if (Instruction.opcode() == targetOpcode(InstructionOpcode::C_J) || + Instruction.opcode() == targetOpcode(InstructionOpcode::PseudoBR)) { + return true; + } + return Instruction.opcode() == targetOpcode(InstructionOpcode::JAL) && + isZeroRegister(Instruction.operandAt(0)); +} + +[[nodiscard]] std::size_t branchTargetIndex(Opcode Value) noexcept { + if (Value == targetOpcode(InstructionOpcode::C_J) || + Value == targetOpcode(InstructionOpcode::PseudoBR)) { + return 0; + } + if (Value == targetOpcode(InstructionOpcode::C_BEQZ) || + Value == targetOpcode(InstructionOpcode::C_BNEZ)) { + return 1; + } + if (Value == targetOpcode(InstructionOpcode::JAL)) + return 1; + return 2; +} + +[[nodiscard]] Expected directTarget(MachineInstr &Instruction) { + if (!isConditionalBranch(Instruction.opcode()) && + Instruction.opcode() != targetOpcode(InstructionOpcode::JAL) && + Instruction.opcode() != targetOpcode(InstructionOpcode::C_J) && + Instruction.opcode() != targetOpcode(InstructionOpcode::PseudoBR)) { + return static_cast(nullptr); + } + MachineOperand *Operand = Instruction.operandAt(branchTargetIndex(Instruction.opcode())); + const BasicBlockOperand *Block = + Operand == nullptr ? nullptr : Operand->getIf(); + if (Block == nullptr || Block->Block == nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V direct branch has no basic-block target"); + } + return Block->Block; +} + +[[nodiscard]] Expected setDirectTarget(MachineInstr &Instruction, + MachineBasicBlock &TargetBlock) { + MachineOperand *Operand = Instruction.operandAt(branchTargetIndex(Instruction.opcode())); + if (Operand == nullptr || Operand->kind() != MachineOperand::Kind::BasicBlock) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V direct branch has no mutable basic-block target"); + } + return Operand->setBasicBlock(TargetBlock); +} + +[[nodiscard]] Expected removeInstruction(MachineInstr &Instruction) { + MachineBasicBlock *Block = Instruction.parent(); + if (Block == nullptr) { + return makeUnexpected(ErrorCode::InvalidArgument, + "RISC-V branch instruction is not linked to a block"); + } + return Block->removeInstruction(Instruction); +} + +[[nodiscard]] Expected physical(PhysicalRegisterID ID) { + auto Reg = physicalRegister(ID); + if (!Reg) + return std::unexpected(Reg.error()); + return *Reg; +} + +[[nodiscard]] Expected +buildJAL(MachineFunction &Function, const TargetDescription &Target, MachineBasicBlock &Block, + MachineInstr *Before, PhysicalRegister Destination, MachineBasicBlock &TargetBlock) { + MachineIRBuilder Builder(Function, Target); + Expected Point = + Before == nullptr ? Builder.setInsertPoint(Block) : Builder.setInsertPointBefore(*Before); + if (!Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(targetOpcode(InstructionOpcode::JAL)); + if (!Pending) + return std::unexpected(Pending.error()); + auto Def = Pending->addDef(Register::fromPhysical(Destination)); + if (!Def) + return std::unexpected(Def.error()); + auto TargetOperand = Pending->addBasicBlock(TargetBlock); + if (!TargetOperand) + return std::unexpected(TargetOperand.error()); + return Pending->build(); +} + +[[nodiscard]] Expected copySourceIdentity(const MachineInstr &Source, + MachineInstr &Destination) { + if (Source.sourceLocation()) + Destination.setSourceLocation(*Source.sourceLocation()); + if (Source.debugLocation()) { + auto Debug = Destination.setDebugLocation(*Source.debugLocation()); + if (!Debug) + return std::unexpected(Debug.error()); + } + if (Source.origin()) + Destination.setOrigin(*Source.origin()); + return {}; +} + +[[nodiscard]] Expected copyTrailingAnnotations(const MachineInstr &Source, + MachineInstr &Destination) { + if (Source.debugInstrNumber() != 0) { + auto DebugNumber = Destination.setDebugInstrNumber(Source.debugInstrNumber()); + if (!DebugNumber) + return std::unexpected(DebugNumber.error()); + } + if (Source.postInstrSymbol()) { + auto Symbol = Destination.setPostInstrSymbol(std::string(*Source.postInstrSymbol())); + if (!Symbol) + return std::unexpected(Symbol.error()); + } + if (Source.heapAllocMarker()) { + auto Marker = Destination.setHeapAllocMarker(*Source.heapAllocMarker()); + if (!Marker) + return std::unexpected(Marker.error()); + } + if (Source.pcSections()) { + auto Sections = Destination.setPCSections(*Source.pcSections()); + if (!Sections) + return std::unexpected(Sections.error()); + } + if (Source.mmra()) { + auto Metadata = Destination.setMMRA(*Source.mmra()); + if (!Metadata) + return std::unexpected(Metadata.error()); + } + if (Source.cfiType()) { + auto Type = Destination.setCFIType(*Source.cfiType()); + if (!Type) + return std::unexpected(Type.error()); + } + if (Source.deactivationSymbol()) { + auto Symbol = Destination.setDeactivationSymbol(std::string(*Source.deactivationSymbol())); + if (!Symbol) + return std::unexpected(Symbol.error()); + } + return {}; +} + +[[nodiscard]] Expected copySingleInstructionAnnotations(const MachineInstr &Source, + MachineInstr &Destination) { + Destination.setFlags(Source.flags()); + auto Identity = copySourceIdentity(Source, Destination); + if (!Identity) + return std::unexpected(Identity.error()); + if (Source.preInstrSymbol()) { + auto Symbol = Destination.setPreInstrSymbol(std::string(*Source.preInstrSymbol())); + if (!Symbol) + return std::unexpected(Symbol.error()); + } + return copyTrailingAnnotations(Source, Destination); +} + +[[nodiscard]] bool isZeroPhysicalRegister(PhysicalRegister Reg) noexcept { + return Reg.number() == static_cast(PhysicalRegisterID::X0); +} + +[[nodiscard]] Expected +selectLongBranchScratch(const MachineInstr &Instruction, const MachineBasicBlock &TargetBlock, + const MachineFunction &Function, const TargetDescription &Target) { + const MachineOperand *DestinationOperand = Instruction.operandAt(0); + const Register *Destination = + DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf(); + const std::optional DestinationPhysical = + Destination == nullptr ? std::nullopt : Destination->physicalRegister(); + if (!DestinationPhysical) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V long JAL has no physical destination"); + } + if (!isZeroPhysicalRegister(*DestinationPhysical)) + return *DestinationPhysical; + + const auto GPRClass = + RegisterClassID::fromValue(static_cast(RegisterClass::GPR_NX0)); + const RegisterClassDesc *Candidates = Target.registers().registerClass(GPRClass); + if (Candidates == nullptr) { + return makeUnexpected(ErrorCode::InternalError, "RISC-V target has no nonzero GPR class"); + } + for (const PhysicalRegister Candidate : Candidates->AllocationOrder) { + if (Target.registers().isReservedRegister(Candidate) || + Function.registerInfo().isReserved(Candidate)) { + continue; + } + const bool LiveAtTarget = + std::ranges::any_of(TargetBlock.liveIns(), [&](const MachineBasicBlockLiveIn &LiveIn) { + return Target.registers().regsOverlap(Candidate, LiveIn.PhysReg); + }); + if (!LiveAtTarget) + return Candidate; + } + return makeUnexpected(ErrorCode::BranchRelaxationFailed, + "RISC-V long branch has no dead physical scratch register"); +} + +[[nodiscard]] Expected expandCompressedBranch(MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target) { + MachineBasicBlock *Block = Instruction.parent(); + if (Block == nullptr) + return makeUnexpected(ErrorCode::InvalidArgument, "compressed branch is not linked"); + auto TargetBlock = directTarget(Instruction); + if (!TargetBlock) + return std::unexpected(TargetBlock.error()); + MachineIRBuilder Builder(Function, Target); + auto Point = Builder.setInsertPointBefore(Instruction); + if (!Point) + return std::unexpected(Point.error()); + + if (Instruction.opcode() == targetOpcode(InstructionOpcode::C_J)) { + auto Zero = physical(PhysicalRegisterID::X0); + if (!Zero) + return std::unexpected(Zero.error()); + auto Replacement = buildJAL(Function, Target, *Block, &Instruction, *Zero, **TargetBlock); + if (!Replacement) + return std::unexpected(Replacement.error()); + auto Annotations = copySingleInstructionAnnotations(Instruction, **Replacement); + if (!Annotations) + return std::unexpected(Annotations.error()); + auto Removed = removeInstruction(Instruction); + if (!Removed) + return std::unexpected(Removed.error()); + return true; + } + + const MachineOperand *SourceOperand = Instruction.operandAt(0); + const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf(); + if (Source == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "compressed conditional branch has no source register"); + auto Zero = physical(PhysicalRegisterID::X0); + if (!Zero) + return std::unexpected(Zero.error()); + const InstructionOpcode Expanded = Instruction.opcode() == targetOpcode(InstructionOpcode::C_BEQZ) + ? InstructionOpcode::BEQ + : InstructionOpcode::BNE; + auto Pending = Builder.buildInstr(targetOpcode(Expanded)); + if (!Pending) + return std::unexpected(Pending.error()); + auto First = Pending->addUse(*Source); + if (!First) + return std::unexpected(First.error()); + auto Second = Pending->addUse(Register::fromPhysical(*Zero)); + if (!Second) + return std::unexpected(Second.error()); + auto Destination = Pending->addBasicBlock(**TargetBlock); + if (!Destination) + return std::unexpected(Destination.error()); + auto Replacement = Pending->build(); + if (!Replacement) + return std::unexpected(Replacement.error()); + auto Annotations = copySingleInstructionAnnotations(Instruction, **Replacement); + if (!Annotations) + return std::unexpected(Annotations.error()); + auto Removed = removeInstruction(Instruction); + if (!Removed) + return std::unexpected(Removed.error()); + return true; +} + +[[nodiscard]] Expected relaxConditionalBranch(MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target) { + MachineBasicBlock *Block = Instruction.parent(); + if (Block == nullptr) + return makeUnexpected(ErrorCode::InvalidArgument, "conditional branch is not linked"); + auto TrueTarget = directTarget(Instruction); + if (!TrueTarget) + return std::unexpected(TrueTarget.error()); + MachineBasicBlock *FalseTarget = Block->next(); + if (FalseTarget == nullptr || FalseTarget == *TrueTarget || + std::ranges::none_of(Block->successors(), [FalseTarget](const MachineSuccessor &Successor) { + return Successor.Block == FalseTarget; + })) { + return makeUnexpected(ErrorCode::BranchRelaxationFailed, + "RISC-V conditional relaxation requires its false edge as fallthrough"); + } + + auto Reversed = reversedOpcode(Instruction.opcode()); + if (!Reversed) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V conditional branch cannot be inverted"); + auto SetOpcode = Instruction.setOpcode(*Reversed); + if (!SetOpcode) + return std::unexpected(SetOpcode.error()); + auto SetTarget = setDirectTarget(Instruction, *FalseTarget); + if (!SetTarget) + return std::unexpected(SetTarget.error()); + + MachineInstr *Following = Instruction.next(); + if (Following != nullptr && isUnconditionalBranch(*Following)) { + auto Retargeted = setDirectTarget(*Following, **TrueTarget); + if (!Retargeted) + return std::unexpected(Retargeted.error()); + return true; + } + auto Zero = physical(PhysicalRegisterID::X0); + if (!Zero) + return std::unexpected(Zero.error()); + MachineIRBuilder Builder(Function, Target); + auto Point = Builder.setInsertPointAfter(Instruction); + if (!Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(targetOpcode(InstructionOpcode::JAL)); + if (!Pending) + return std::unexpected(Pending.error()); + auto Def = Pending->addDef(Register::fromPhysical(*Zero)); + if (!Def) + return std::unexpected(Def.error()); + auto Destination = Pending->addBasicBlock(**TrueTarget); + if (!Destination) + return std::unexpected(Destination.error()); + auto LongJump = Pending->build(); + if (!LongJump) + return std::unexpected(LongJump.error()); + auto Identity = copySourceIdentity(Instruction, **LongJump); + if (!Identity) + return std::unexpected(Identity.error()); + MachineInstrFlags OriginalFlags = Instruction.flags(); + const bool HadBundledSuccessor = OriginalFlags.has(MachineInstrFlag::BundledWithSucc); + OriginalFlags.set(MachineInstrFlag::BundledWithSucc); + Instruction.setFlags(OriginalFlags); + MachineInstrFlags LongFlags; + LongFlags.set(MachineInstrFlag::BundledWithPred); + if (HadBundledSuccessor) + LongFlags.set(MachineInstrFlag::BundledWithSucc); + if (OriginalFlags.has(MachineInstrFlag::NoMerge)) + LongFlags.set(MachineInstrFlag::NoMerge); + (*LongJump)->setFlags(LongFlags); + return true; +} + +[[nodiscard]] Expected relaxJAL(MachineInstr &Instruction, std::int64_t Offset, + MachineFunction &Function, const TargetDescription &Target) { + MachineBasicBlock *Block = Instruction.parent(); + if (Block == nullptr) + return makeUnexpected(ErrorCode::InvalidArgument, "RISC-V JAL is not linked"); + if (Offset < std::numeric_limits::min() || + Offset > std::numeric_limits::max()) { + return makeUnexpected(ErrorCode::BranchRelaxationFailed, + "RISC-V PC-relative long branch exceeds the signed 32-bit range"); + } + auto DestinationBlock = directTarget(Instruction); + if (!DestinationBlock) + return std::unexpected(DestinationBlock.error()); + const MachineOperand *DestinationOperand = Instruction.operandAt(0); + const Register *Destination = + DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf(); + if (Destination == nullptr || !Destination->isPhysical()) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V long JAL requires a physical destination register"); + } + auto Scratch = selectLongBranchScratch(Instruction, **DestinationBlock, Function, Target); + if (!Scratch) + return std::unexpected(Scratch.error()); + const std::string Anchor = Instruction.preInstrSymbol() + ? std::string(*Instruction.preInstrSymbol()) + : ".Lrcc_long_" + std::to_string(Function.stableID().value()) + + "_" + std::to_string(Instruction.stableID().value()); + const MachineInstrFlags OriginalFlags = Instruction.flags(); + + MachineIRBuilder Builder(Function, Target); + auto Point = Builder.setInsertPointBefore(Instruction); + if (!Point) + return std::unexpected(Point.error()); + auto HighPending = Builder.buildInstr(targetOpcode(InstructionOpcode::AUIPC)); + if (!HighPending) + return std::unexpected(HighPending.error()); + auto HighDef = HighPending->addDef(Register::fromPhysical(*Scratch)); + if (!HighDef) + return std::unexpected(HighDef.error()); + auto HighTarget = HighPending->addBasicBlock(**DestinationBlock, PCRelativeHighFlag); + if (!HighTarget) + return std::unexpected(HighTarget.error()); + auto High = HighPending->build(); + if (!High) + return std::unexpected(High.error()); + auto HighSymbol = (*High)->setPreInstrSymbol(Anchor); + if (!HighSymbol) + return std::unexpected(HighSymbol.error()); + auto HighIdentity = copySourceIdentity(Instruction, **High); + if (!HighIdentity) + return std::unexpected(HighIdentity.error()); + + auto LowPending = Builder.buildInstr(targetOpcode(InstructionOpcode::JALR)); + if (!LowPending) + return std::unexpected(LowPending.error()); + auto LowDef = LowPending->addDef(*Destination); + if (!LowDef) + return std::unexpected(LowDef.error()); + auto LowBase = LowPending->addUse(Register::fromPhysical(*Scratch)); + if (!LowBase) + return std::unexpected(LowBase.error()); + auto LowSymbol = LowPending->addMCSymbol(Anchor, 0, PCRelativeLowFlag); + if (!LowSymbol) + return std::unexpected(LowSymbol.error()); + auto Low = LowPending->build(); + if (!Low) + return std::unexpected(Low.error()); + auto LowIdentity = copySourceIdentity(Instruction, **Low); + if (!LowIdentity) + return std::unexpected(LowIdentity.error()); + auto LowAnnotations = copyTrailingAnnotations(Instruction, **Low); + if (!LowAnnotations) + return std::unexpected(LowAnnotations.error()); + + MachineInstrFlags HighFlags; + if (OriginalFlags.has(MachineInstrFlag::BundledWithPred)) + HighFlags.set(MachineInstrFlag::BundledWithPred); + HighFlags.set(MachineInstrFlag::BundledWithSucc); + if (OriginalFlags.has(MachineInstrFlag::NoMerge)) + HighFlags.set(MachineInstrFlag::NoMerge); + (*High)->setFlags(HighFlags); + MachineInstrFlags LowFlags = OriginalFlags; + LowFlags.clear(MachineInstrFlag::BundledWithPred); + LowFlags.set(MachineInstrFlag::BundledWithPred); + (*Low)->setFlags(LowFlags); + auto Removed = removeInstruction(Instruction); + if (!Removed) + return std::unexpected(Removed.error()); + return true; +} + +class RISCVInstrInfo final : public TargetInstrInfo { +public: + [[nodiscard]] Expected + analyzeBranch(MachineBasicBlock &Block, MachineFunction &Function, + const TargetDescription &Target) const override { + if (&Block.function() != &Function) + return makeUnexpected(ErrorCode::InvalidArgument, + "RISC-V branch analysis references another function"); + std::vector Terminators; + for (MachineInstr *Instruction = Block.firstInstruction(); Instruction != nullptr; + Instruction = Instruction->next()) { + const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction->opcode()); + if (Descriptor == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V branch analysis encountered an unknown opcode"); + if (Descriptor->Flags.has(InstructionFlag::Terminator)) + Terminators.push_back(Instruction); + } + + MachineBranchAnalysis Result; + if (Terminators.empty()) + return Result; + if (Terminators.size() > 2) { + Result.HasUnanalyzableTerminator = true; + return Result; + } + MachineInstr *First = Terminators.front(); + if (isConditionalBranch(First->opcode())) { + auto TrueTarget = directTarget(*First); + if (!TrueTarget) + return std::unexpected(TrueTarget.error()); + Result.Conditional = First; + Result.TrueTarget = *TrueTarget; + if (Terminators.size() == 2) { + MachineInstr *Second = Terminators.back(); + if (!isUnconditionalBranch(*Second)) { + Result.HasUnanalyzableTerminator = true; + return Result; + } + auto FalseTarget = directTarget(*Second); + if (!FalseTarget) + return std::unexpected(FalseTarget.error()); + Result.Unconditional = Second; + Result.FalseTarget = *FalseTarget; + } else { + for (const MachineSuccessor &Successor : Block.successors()) { + if (Successor.Block == nullptr || Successor.Block == Result.TrueTarget) + continue; + if (Result.FalseTarget != nullptr) { + Result.HasUnanalyzableTerminator = true; + return Result; + } + Result.FalseTarget = Successor.Block; + } + if (Result.FalseTarget == nullptr) + Result.FalseTarget = Block.next(); + } + return Result; + } + if (Terminators.size() == 1 && isUnconditionalBranch(*First)) { + auto TargetBlock = directTarget(*First); + if (!TargetBlock) + return std::unexpected(TargetBlock.error()); + Result.Unconditional = First; + Result.TrueTarget = *TargetBlock; + return Result; + } + Result.HasUnanalyzableTerminator = true; + return Result; + } + + [[nodiscard]] Expected reverseBranchCondition(MachineInstr &Branch, + MachineBasicBlock &NewTarget, + MachineFunction &Function, + const TargetDescription &) const override { + if (&Branch.function() != &Function || &NewTarget.function() != &Function) + return makeUnexpected(ErrorCode::InvalidArgument, + "RISC-V branch reversal references another function"); + const std::optional Reversed = reversedOpcode(Branch.opcode()); + if (!Reversed) + return makeUnexpected(ErrorCode::InvalidArgument, + "RISC-V branch opcode has no inverse condition"); + auto OpcodeResult = Branch.setOpcode(*Reversed); + if (!OpcodeResult) + return std::unexpected(OpcodeResult.error()); + return setDirectTarget(Branch, NewTarget); + } + + [[nodiscard]] Expected removeBranch(MachineInstr &Branch, MachineFunction &Function, + const TargetDescription &) const override { + if (&Branch.function() != &Function) + return makeUnexpected(ErrorCode::InvalidArgument, + "RISC-V branch removal references another function"); + return removeInstruction(Branch); + } + + [[nodiscard]] Expected rewriteBranchTarget(MachineBasicBlock &Block, + MachineBasicBlock &OldTarget, + MachineBasicBlock &NewTarget, + MachineFunction &Function, + const TargetDescription &) const override { + if (&Block.function() != &Function || &OldTarget.function() != &Function || + &NewTarget.function() != &Function) { + return makeUnexpected(ErrorCode::InvalidArgument, + "RISC-V branch rewrite references another function"); + } + bool Changed = false; + for (MachineInstr *Instruction = Block.firstInstruction(); Instruction != nullptr; + Instruction = Instruction->next()) { + if (!isConditionalBranch(Instruction->opcode()) && !isUnconditionalBranch(*Instruction)) { + continue; + } + auto CurrentTarget = directTarget(*Instruction); + if (!CurrentTarget) + return std::unexpected(CurrentTarget.error()); + if (*CurrentTarget != &OldTarget) + continue; + auto Rewritten = setDirectTarget(*Instruction, NewTarget); + if (!Rewritten) + return std::unexpected(Rewritten.error()); + Changed = true; + } + return Changed; + } + + [[nodiscard]] Expected + insertBranch(MachineBasicBlock &Block, MachineBasicBlock &TargetBlock, MachineFunction &Function, + const TargetDescription &Target) const override { + if (&Block.function() != &Function || &TargetBlock.function() != &Function) + return makeUnexpected(ErrorCode::InvalidArgument, + "RISC-V branch insertion references another function"); + auto Zero = physical(PhysicalRegisterID::X0); + if (!Zero) + return std::unexpected(Zero.error()); + return buildJAL(Function, Target, Block, nullptr, *Zero, TargetBlock); + } + + [[nodiscard]] Expected isFallthroughEdge(const MachineBasicBlock &From, + const MachineBasicBlock &To, + const MachineFunction &Function, + const TargetDescription &Target) const override { + if (&From.function() != &Function || &To.function() != &Function) + return makeUnexpected(ErrorCode::InvalidArgument, + "RISC-V fallthrough query references another machine function"); + if (From.next() != &To || + std::ranges::none_of(From.successors(), [&To](const MachineSuccessor &Successor) { + return Successor.Block == &To; + })) { + return false; + } + + bool CanFallThrough = true; + for (const MachineInstr *Instruction = From.firstInstruction(); Instruction != nullptr; + Instruction = Instruction->next()) { + const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction->opcode()); + if (Descriptor == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V fallthrough analysis encountered an unknown opcode"); + if (Descriptor->Flags.has(InstructionFlag::Terminator)) + CanFallThrough = isConditionalBranch(Instruction->opcode()); + } + return CanFallThrough; + } + + [[nodiscard]] Expected validateCopy(Register Destination, SubRegIndex DestinationSubReg, + Register Source, SubRegIndex SourceSubReg, + const MachineFunction &Function, + const TargetDescription &Target) const override { + if (Destination.isNoRegister() || Source.isNoRegister()) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V COPY requires two nonzero registers"); + if (DestinationSubReg.value() != 0 || SourceSubReg.value() != 0) + return makeUnexpected(ErrorCode::Unsupported, "RV64GC does not model register subindices"); + + auto DestinationShape = copyShape(Destination, Function, Target); + if (!DestinationShape) + return std::unexpected(DestinationShape.error()); + auto SourceShape = copyShape(Source, Function, Target); + if (!SourceShape) + return std::unexpected(SourceShape.error()); + if (DestinationShape->Bank == SourceShape->Bank) { + if (DestinationShape->Width && SourceShape->Width && + DestinationShape->Width != SourceShape->Width) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V same-bank COPY has incompatible register widths"); + return {}; + } + + const RegisterBankID GPR = + RegisterBankID::fromValue(static_cast(RegisterBank::GPR)); + const RegisterBankID FPR = + RegisterBankID::fromValue(static_cast(RegisterBank::FPR)); + const bool IsCrossBankMove = (DestinationShape->Bank == GPR && SourceShape->Bank == FPR) || + (DestinationShape->Bank == FPR && SourceShape->Bank == GPR); + if (!IsCrossBankMove) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V COPY crosses unsupported register banks"); + const auto SupportedWidth = [](std::optional Width) { + return !Width || *Width == 32 || *Width == 64; + }; + if (!SupportedWidth(DestinationShape->Width) || !SupportedWidth(SourceShape->Width)) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V GPR/FPR COPY requires 32-bit or 64-bit values"); + return {}; + } + + [[nodiscard]] Expected + instructionSize(const MachineInstr &Instruction, const MachineFunction &, + const TargetDescription &Target) const override { + const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction.opcode()); + if (Descriptor == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V instruction-size query encountered an unknown opcode"); + if (!Descriptor->Flags.has(InstructionFlag::ProducesCode)) + return 0; + const auto Category = + std::ranges::find_if(encodingCategories(), [Descriptor](const EncodingCategoryInfo &Info) { + return Info.ID == Descriptor->EncodingCategory; + }); + if (Category == encodingCategories().end() || Category->SizeInBytes == 0) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V producing instruction has no exact encoding size"); + return Category->SizeInBytes; + } + + [[nodiscard]] Expected + branchTarget(MachineInstr &Instruction, MachineFunction &Function, + const TargetDescription &) const override { + if (&Instruction.function() != &Function) + return makeUnexpected(ErrorCode::InvalidArgument, + "RISC-V branch target query references another function"); + return directTarget(Instruction); + } + + [[nodiscard]] Expected> + evaluateBranchCondition(const MachineInstr &Instruction, const MachineFunction &Function, + const TargetDescription &) const override { + if (&Instruction.function() != &Function) { + return makeUnexpected(ErrorCode::InvalidArgument, + "RISC-V branch evaluation references another function"); + } + const Opcode Value = Instruction.opcode(); + if (!isConditionalBranch(Value)) + return std::optional{}; + if (Value == targetOpcode(InstructionOpcode::C_BEQZ) || + Value == targetOpcode(InstructionOpcode::C_BNEZ)) { + if (!isZeroRegister(Instruction.operandAt(0))) + return std::optional{}; + return std::optional{Value == targetOpcode(InstructionOpcode::C_BEQZ)}; + } + if (!sameRegister(Instruction.operandAt(0), Instruction.operandAt(1))) + return std::optional{}; + if (Value == targetOpcode(InstructionOpcode::BEQ) || + Value == targetOpcode(InstructionOpcode::BGE) || + Value == targetOpcode(InstructionOpcode::BGEU)) { + return std::optional{true}; + } + return std::optional{false}; + } + + [[nodiscard]] bool isBranchOffsetInRange(const MachineInstr &Instruction, + std::int64_t Offset) const noexcept override { + if ((Offset & 1) != 0) + return false; + const Opcode Value = Instruction.opcode(); + if (Value == targetOpcode(InstructionOpcode::C_BEQZ) || + Value == targetOpcode(InstructionOpcode::C_BNEZ)) { + return Offset >= -256 && Offset <= 254; + } + if (Value == targetOpcode(InstructionOpcode::C_J)) + return Offset >= -2048 && Offset <= 2046; + if (isConditionalBranch(Value)) + return Offset >= -4096 && Offset <= 4094; + if (Value == targetOpcode(InstructionOpcode::JAL) || + Value == targetOpcode(InstructionOpcode::PseudoBR)) { + return Offset >= -1048576 && Offset <= 1048574; + } + return true; + } + + [[nodiscard]] Expected relaxBranch(MachineInstr &Instruction, std::int64_t Offset, + MachineFunction &Function, + const TargetDescription &Target) const override { + if (&Instruction.function() != &Function) + return makeUnexpected(ErrorCode::InvalidArgument, + "RISC-V branch relaxation references another function"); + if (Instruction.opcode() == targetOpcode(InstructionOpcode::C_J) || + Instruction.opcode() == targetOpcode(InstructionOpcode::C_BEQZ) || + Instruction.opcode() == targetOpcode(InstructionOpcode::C_BNEZ)) { + return expandCompressedBranch(Instruction, Function, Target); + } + if (isConditionalBranch(Instruction.opcode())) + return relaxConditionalBranch(Instruction, Function, Target); + if (Instruction.opcode() == targetOpcode(InstructionOpcode::JAL)) + return relaxJAL(Instruction, Offset, Function, Target); + return false; + } + +private: + struct CopyShape final { + RegisterBankID Bank; + std::optional Width; + }; + + [[nodiscard]] static Expected copyShape(Register Reg, const MachineFunction &Function, + const TargetDescription &Target) { + if (Reg.isVirtual()) { + auto Info = Function.registerInfo().virtualRegisterInfo(Reg); + if (!Info) + return std::unexpected(Info.error()); + const std::optional &Bank = Info->get().Bank; + const std::optional &RegClass = Info->get().RegClass; + if (!Bank || !RegClass) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V COPY virtual register is not fully constrained"); + const RegisterClassDesc *Class = Target.registers().registerClass(*RegClass); + if (Class == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V COPY references an unknown register class"); + return CopyShape{*Bank, Class->SizeInBits}; + } + + const std::optional Physical = Reg.physicalRegister(); + if (!Physical || !Target.registers().isKnownPhysicalRegister(*Physical)) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V COPY references an unknown physical register"); + for (const RegisterBankDesc &Bank : Target.registers().registerBanks()) { + if (Target.registers().bankContainsRegister(Bank.ID, *Physical)) + return CopyShape{Bank.ID, std::nullopt}; + } + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V physical register is not assigned to a register bank"); + } +}; + +} // namespace + +const TargetInstrInfo &instrInfo() noexcept { + static const RISCVInstrInfo Value; + return Value; +} + +} // namespace rcc::riscv diff --git a/backend/lib/Target/RISCV/RISCVInstrInfo.h b/backend/lib/Target/RISCV/RISCVInstrInfo.h new file mode 100644 index 0000000..62c7723 --- /dev/null +++ b/backend/lib/Target/RISCV/RISCVInstrInfo.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_LIB_TARGET_RISCV_RISCVINSTRINFO_H +#define RCC_LIB_TARGET_RISCV_RISCVINSTRINFO_H + +#include "rcc/CodeGen/TargetInstrInfo.h" + +namespace rcc::riscv { + +[[nodiscard]] const TargetInstrInfo &instrInfo() noexcept; + +} // namespace rcc::riscv + +#endif // RCC_LIB_TARGET_RISCV_RISCVINSTRINFO_H diff --git a/backend/lib/Target/RISCV/RISCVOptimizationPipeline.cpp b/backend/lib/Target/RISCV/RISCVOptimizationPipeline.cpp index 2f8c5d1..9301757 100644 --- a/backend/lib/Target/RISCV/RISCVOptimizationPipeline.cpp +++ b/backend/lib/Target/RISCV/RISCVOptimizationPipeline.cpp @@ -1,18 +1,18 @@ // SPDX-License-Identifier: GPL-3.0-or-later +#include "RISCVInstrInfo.h" #include "rcc/Target/RISCV/RISCVCodeGen.h" namespace rcc::riscv { MachineOptimizationPipelineTargetInfo optimizationPipelineInfo() noexcept { return { - .SSA = ssaInfo(), + .InstrInfo = instrInfo(), .Combiner = machineCombinerInfo(), .MemoryFolding = memoryFoldingInfo(), .FrameLowering = frameLowering(), .PseudoExpansion = pseudoExpansionInfo(), .SchedulingModel = schedulingModel(), - .BranchInfo = branchInfo(), }; } diff --git a/backend/lib/Target/RISCV/RISCVPipeline.cpp b/backend/lib/Target/RISCV/RISCVPipeline.cpp index 3665db5..08a4653 100644 --- a/backend/lib/Target/RISCV/RISCVPipeline.cpp +++ b/backend/lib/Target/RISCV/RISCVPipeline.cpp @@ -2,6 +2,7 @@ #include "rcc/Target/RISCV/RISCVPipeline.h" +#include "RISCVInstrInfo.h" #include "rcc/CodeGen/MachineCombiner.h" #include "rcc/CodeGen/MachineFunction.h" #include "rcc/CodeGen/MachineOptimizationPipeline.h" @@ -45,10 +46,10 @@ template auto Added = addSelectedSSAOptimizationPipeline(Pipeline, optimizationPipelineInfo()); if (!Added) return std::unexpected(Added.error()); - Added = addPass(Pipeline, ssaInfo()); + Added = addPass(Pipeline, instrInfo()); if (!Added) return std::unexpected(Added.error()); - return addPass(Pipeline, ssaInfo()); + return addPass(Pipeline, instrInfo()); } [[nodiscard]] Expected addRegisterAllocationPipeline(MachinePassManager &Pipeline) { @@ -104,13 +105,13 @@ Expected addCanonicalPipeline(MachinePassManager &Pipeline, const MachineF if (!Added) return std::unexpected(Added.error()); if (!Function.properties().has(MachineFunctionProperty::NoPHIs)) { - Added = addPass(Pipeline, ssaInfo()); + Added = addPass(Pipeline, instrInfo()); if (!Added) return std::unexpected(Added.error()); } } if (!Function.properties().has(MachineFunctionProperty::TiedOpsRewritten)) { - auto Added = addPass(Pipeline, ssaInfo()); + auto Added = addPass(Pipeline, instrInfo()); if (!Added) return std::unexpected(Added.error()); } diff --git a/backend/unittests/CodeGen/MachineSSAExitTest.cpp b/backend/unittests/CodeGen/MachineSSAExitTest.cpp index a3940c2..a5cc1c7 100644 --- a/backend/unittests/CodeGen/MachineSSAExitTest.cpp +++ b/backend/unittests/CodeGen/MachineSSAExitTest.cpp @@ -15,6 +15,7 @@ #include "rcc/CodeGen/MachineRegisterInfo.h" #include "rcc/CodeGen/MachineVerifier.h" #include "rcc/CodeGen/TargetDescription.h" +#include "rcc/CodeGen/TargetInstrInfo.h" #include #include @@ -158,9 +159,26 @@ private: return rcc::test::createTestTargetDescription(std::move(*Instructions)); } -class TestSSAInfo final : public rcc::TargetSSAInfo { +class TestInstrInfo final : public rcc::TargetInstrInfo { public: - explicit TestSSAInfo(bool RejectCopies = false) : RejectCopies(RejectCopies) {} + explicit TestInstrInfo(bool RejectCopies = false) : RejectCopies(RejectCopies) {} + + [[nodiscard]] rcc::Expected + analyzeBranch(rcc::MachineBasicBlock &, rcc::MachineFunction &, + const rcc::TargetDescription &) const override { + return rcc::MachineBranchAnalysis{}; + } + + [[nodiscard]] rcc::Expected + reverseBranchCondition(rcc::MachineInstr &, rcc::MachineBasicBlock &, rcc::MachineFunction &, + const rcc::TargetDescription &) const override { + return rcc::makeUnexpected(rcc::ErrorCode::Unsupported, "test target has no branch reversal"); + } + + [[nodiscard]] rcc::Expected removeBranch(rcc::MachineInstr &, rcc::MachineFunction &, + const rcc::TargetDescription &) const override { + return rcc::makeUnexpected(rcc::ErrorCode::Unsupported, "test target has no branch removal"); + } [[nodiscard]] rcc::Expected isFallthroughEdge(const rcc::MachineBasicBlock &From, const rcc::MachineBasicBlock &To, @@ -225,10 +243,10 @@ public: return Changed; } - [[nodiscard]] rcc::Expected - insertUnconditionalBranch(rcc::MachineBasicBlock &Block, rcc::MachineBasicBlock &TargetBlock, - rcc::MachineFunction &Function, - const rcc::TargetDescription &Target) const override { + [[nodiscard]] rcc::Expected + insertBranch(rcc::MachineBasicBlock &Block, rcc::MachineBasicBlock &TargetBlock, + rcc::MachineFunction &Function, + const rcc::TargetDescription &Target) const override { rcc::MachineIRBuilder Builder(Function, Target); auto Point = Builder.setInsertPoint(Block); if (!Point) @@ -242,7 +260,7 @@ public: auto Built = Pending->build(); if (!Built) return std::unexpected(Built.error()); - return {}; + return *Built; } [[nodiscard]] rcc::Expected @@ -289,6 +307,29 @@ public: return true; } + [[nodiscard]] rcc::Expected + instructionSize(const rcc::MachineInstr &, const rcc::MachineFunction &, + const rcc::TargetDescription &) const override { + return 4; + } + + [[nodiscard]] rcc::Expected + branchTarget(rcc::MachineInstr &, rcc::MachineFunction &, + const rcc::TargetDescription &) const override { + return static_cast(nullptr); + } + + [[nodiscard]] bool isBranchOffsetInRange(const rcc::MachineInstr &, + std::int64_t) const noexcept override { + return true; + } + + [[nodiscard]] rcc::Expected relaxBranch(rcc::MachineInstr &, std::int64_t, + rcc::MachineFunction &, + const rcc::TargetDescription &) const override { + return false; + } + private: bool RejectCopies = false; }; @@ -430,7 +471,7 @@ buildPhi(rcc::MachineFunction &Function, const rcc::TargetDescription &Target, [[nodiscard]] rcc::Expected runPHIElimination(rcc::MachineFunction &Function, const rcc::TargetDescription &Target, - const rcc::TargetSSAInfo &Info) { + const rcc::TargetInstrInfo &Info) { rcc::MachinePassManager Pipeline; auto Added = Pipeline.addPass(std::make_unique(Info)); if (!Added) @@ -469,7 +510,7 @@ instructionsWithOpcode(rcc::MachineFunction &Function, rcc::Opcode Opcode) { } void testParallelPHIsAndPlacement(TestContext &Context, const rcc::TargetDescription &Target, - const TestSSAInfo &Info) { + const TestInstrInfo &Info) { auto Fixture = createFunction(Context, "parallel_phis", 1, 3); if (!Fixture.Function || Fixture.Blocks.size() != 3) return; @@ -620,7 +661,7 @@ void testParallelPHIsAndPlacement(TestContext &Context, const rcc::TargetDescrip } void testCriticalEdgeAndPhysicalLiveIn(TestContext &Context, const rcc::TargetDescription &Target, - const TestSSAInfo &Info) { + const TestInstrInfo &Info) { auto Fixture = createFunction(Context, "critical_edge", 2, 4); if (!Fixture.Function || Fixture.Blocks.size() != 4) return; @@ -721,7 +762,7 @@ void testCriticalEdgeAndPhysicalLiveIn(TestContext &Context, const rcc::TargetDe } void testUndefAndFailureAtomicity(TestContext &Context, const rcc::TargetDescription &Target, - const TestSSAInfo &Info) { + const TestInstrInfo &Info) { auto BuildFixture = [&](std::uint64_t ID) { auto Fixture = createFunction(Context, "undef_phi", ID, 2); if (!Fixture.Function || Fixture.Blocks.size() != 2) @@ -773,7 +814,7 @@ void testUndefAndFailureAtomicity(TestContext &Context, const rcc::TargetDescrip instructionsWithOpcode(*Atomic.Function, rcc::opcode(rcc::GenericOpcode::PHI)).size(); const std::size_t RegistersBefore = Atomic.Function->registerInfo().virtualRegisters().size(); const std::uint64_t PropertiesBefore = Atomic.Function->properties().rawValue(); - TestSSAInfo RejectingInfo(true); + TestInstrInfo RejectingInfo(true); rcc::MachineAnalysisManager Analyses; rcc::PHIEliminationPass FailingPass(RejectingInfo); auto Failed = FailingPass.run(*Atomic.Function, Target, Analyses); @@ -794,7 +835,7 @@ void testUndefAndFailureAtomicity(TestContext &Context, const rcc::TargetDescrip } void testTwoAddressAndSubregisterPseudos(TestContext &Context, const rcc::TargetDescription &Target, - const TestSSAInfo &Info) { + const TestInstrInfo &Info) { auto Fixture = createFunction(Context, "two_address", 5, 1); if (!Fixture.Function || Fixture.Blocks.empty()) return; @@ -1015,7 +1056,7 @@ void testTwoAddressAndSubregisterPseudos(TestContext &Context, const rcc::Target AtomicFixture.Function->registerInfo().virtualRegisters().size(); const std::uint64_t AtomicPropertiesBefore = AtomicFixture.Function->properties().rawValue(); - TestSSAInfo RejectingInfo(true); + TestInstrInfo RejectingInfo(true); rcc::MachineAnalysisManager AtomicAnalyses; rcc::TwoAddressRewritePass FailingPass(RejectingInfo); auto Failed = FailingPass.run(*AtomicFixture.Function, Target, AtomicAnalyses); @@ -1128,7 +1169,7 @@ int main() { Context.expect(Target.has_value(), "SSA-exit TestTarget must be created"); if (!Target) return Context.result(); - TestSSAInfo Info; + TestInstrInfo Info; testParallelPHIsAndPlacement(Context, *Target, Info); testCriticalEdgeAndPhysicalLiveIn(Context, *Target, Info); testUndefAndFailureAtomicity(Context, *Target, Info); diff --git a/backend/unittests/Target/CMakeLists.txt b/backend/unittests/Target/CMakeLists.txt index e68c525..0b4ed74 100644 --- a/backend/unittests/Target/CMakeLists.txt +++ b/backend/unittests/Target/CMakeLists.txt @@ -34,6 +34,10 @@ target_link_libraries(RCCBackendRISCVCodeGenTests PRIVATE RCCBackend::RISCV RCCB add_test(NAME RCCBackendRISCVCodeGenTests COMMAND RCCBackendRISCVCodeGenTests) add_executable(RCCBackendRISCVSSAExitTests RISCVSSAExitTest.cpp) +target_include_directories( + RCCBackendRISCVSSAExitTests + PRIVATE "${RCC_BACKEND_SOURCE_DIR}/lib/Target/RISCV" +) rcc_backend_configure_target(RCCBackendRISCVSSAExitTests) target_link_libraries(RCCBackendRISCVSSAExitTests PRIVATE RCCBackend::RISCV RCCBackend::CodeGen) add_test(NAME RCCBackendRISCVSSAExitTests COMMAND RCCBackendRISCVSSAExitTests) @@ -61,6 +65,10 @@ add_test( ) add_executable(RCCBackendRISCVLateCodeGenTests RISCVLateCodeGenTest.cpp) +target_include_directories( + RCCBackendRISCVLateCodeGenTests + PRIVATE "${RCC_BACKEND_SOURCE_DIR}/lib/Target/RISCV" +) rcc_backend_configure_target(RCCBackendRISCVLateCodeGenTests) target_link_libraries( RCCBackendRISCVLateCodeGenTests diff --git a/backend/unittests/Target/RISCVLateCodeGenTest.cpp b/backend/unittests/Target/RISCVLateCodeGenTest.cpp index 97590cd..d0bd952 100644 --- a/backend/unittests/Target/RISCVLateCodeGenTest.cpp +++ b/backend/unittests/Target/RISCVLateCodeGenTest.cpp @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later +#include "RISCVInstrInfo.h" #include "rcc/CodeGen/GenericOpcodes.h" #include "rcc/CodeGen/MachineBasicBlock.h" #include "rcc/CodeGen/MachineBlockPlacement.h" @@ -102,7 +103,7 @@ public: } }; -class FixedSizeBranchInfo final : public rcc::TargetInstrInfo { +class FixedSizeInstrInfo final : public rcc::TargetInstrInfo { public: [[nodiscard]] rcc::Expected analyzeBranch(rcc::MachineBasicBlock &, rcc::MachineFunction &, @@ -128,11 +129,23 @@ public: } [[nodiscard]] rcc::Expected - insertUnconditionalBranch(rcc::MachineBasicBlock &, rcc::MachineBasicBlock &, - rcc::MachineFunction &, const rcc::TargetDescription &) const override { + insertBranch(rcc::MachineBasicBlock &, rcc::MachineBasicBlock &, rcc::MachineFunction &, + const rcc::TargetDescription &) const override { return rcc::makeUnexpected(rcc::ErrorCode::Unsupported, "test target has no branches"); } + [[nodiscard]] rcc::Expected + isFallthroughEdge(const rcc::MachineBasicBlock &, const rcc::MachineBasicBlock &, + const rcc::MachineFunction &, const rcc::TargetDescription &) const override { + return false; + } + + [[nodiscard]] rcc::Expected validateCopy(rcc::Register, rcc::SubRegIndex, rcc::Register, + rcc::SubRegIndex, const rcc::MachineFunction &, + const rcc::TargetDescription &) const override { + return rcc::makeUnexpected(rcc::ErrorCode::Unsupported, "test target has no COPY support"); + } + [[nodiscard]] rcc::Expected instructionSize(const rcc::MachineInstr &, const rcc::MachineFunction &, const rcc::TargetDescription &) const override { @@ -398,7 +411,7 @@ void testFallthroughNormalization(TestContext &Context, const rcc::TargetDescrip auto Liveness = rcc::rebuildMachineLiveness(*Function, Target); Context.expect(Liveness.has_value(), "fallthrough fixture liveness must be rebuilt"); rcc::MachineAnalysisManager Analyses; - rcc::MachineFallthroughPass Pass(rcc::riscv::branchInfo()); + rcc::MachineFallthroughPass Pass(rcc::riscv::instrInfo()); auto Result = Pass.run(*Function, Target, Analyses); if (!Result) std::cerr << "fallthrough normalization failure: " << Result.error().message() << '\n'; @@ -433,7 +446,7 @@ void testFallthroughMaterialization(TestContext &Context, const rcc::TargetDescr Context.expect(rcc::rebuildMachineLiveness(*Function, Target).has_value(), "materialized fallthrough liveness must be rebuilt"); rcc::MachineAnalysisManager Analyses; - rcc::MachineFallthroughPass Pass(rcc::riscv::branchInfo()); + rcc::MachineFallthroughPass Pass(rcc::riscv::instrInfo()); auto Result = Pass.run(*Function, Target, Analyses); if (!Result) std::cerr << "fallthrough materialization failure: " << Result.error().message() << '\n'; @@ -474,7 +487,7 @@ void testBranchOptimization(TestContext &Context, const rcc::TargetDescription & } setSelectedProperties(*Function); rcc::MachineAnalysisManager Analyses; - rcc::MachineBranchOptimizationPass Pass(rcc::riscv::branchInfo()); + rcc::MachineBranchOptimizationPass Pass(rcc::riscv::instrInfo()); auto Result = Pass.run(*Function, Target, Analyses); if (!Result) std::cerr << "branch optimization failure: " << Result.error().message() << '\n'; @@ -554,7 +567,7 @@ void testTailDuplication(TestContext &Context, const rcc::TargetDescription &Tar } setRelaxationProperties(Context, *Function, Target); rcc::MachineAnalysisManager Analyses; - rcc::MachineTailDuplicationPass Pass(rcc::riscv::branchInfo()); + rcc::MachineTailDuplicationPass Pass(rcc::riscv::instrInfo()); Tail->attributes().set(rcc::MachineBasicBlockAttribute::MachineBlockAddressTaken); auto Protected = Pass.run(*Function, Target, Analyses); Context.expect(Protected && !Protected->Changed && Tail->isLinked(), @@ -605,7 +618,7 @@ void testTailMerge(TestContext &Context, const rcc::TargetDescription &Target) { } setRelaxationProperties(Context, *Function, Target); rcc::MachineAnalysisManager Analyses; - rcc::MachineTailMergePass Pass(rcc::riscv::branchInfo()); + rcc::MachineTailMergePass Pass(rcc::riscv::instrInfo()); Second->attributes().set(rcc::MachineBasicBlockAttribute::MachineBlockAddressTaken); auto Protected = Pass.run(*Function, Target, Analyses); Context.expect(Protected && !Protected->Changed && Second->isLinked(), @@ -650,7 +663,7 @@ void testCopyPropagation(TestContext &Context, const rcc::TargetDescription &Tar return; setSelectedProperties(*Function); rcc::MachineAnalysisManager Analyses; - rcc::MachineCopyPropagationPass PreRA(rcc::riscv::ssaInfo(), + rcc::MachineCopyPropagationPass PreRA(rcc::riscv::instrInfo(), rcc::MachineCopyPropagationStage::PreRegisterAllocation); auto PreResult = PreRA.run(*Function, Target, Analyses); if (!PreResult) @@ -690,7 +703,7 @@ void testCopyPropagation(TestContext &Context, const rcc::TargetDescription &Tar Context.expect(rcc::rebuildMachineLiveness(*PostFunction, Target).has_value(), "post-RA COPY propagation liveness must be rebuilt"); rcc::MachineAnalysisManager PostAnalyses; - rcc::MachineCopyPropagationPass PostRA(rcc::riscv::ssaInfo(), + rcc::MachineCopyPropagationPass PostRA(rcc::riscv::instrInfo(), rcc::MachineCopyPropagationStage::PostRegisterAllocation); auto PostResult = PostRA.run(*PostFunction, Target, PostAnalyses); if (!PostResult) @@ -963,7 +976,7 @@ void testCompressedAndConditionalRelaxation(TestContext &Context, return; setRelaxationProperties(Context, *Function, Target); rcc::MachineAnalysisManager Analyses; - rcc::MachineBranchRelaxationPass Pass(rcc::riscv::branchInfo()); + rcc::MachineBranchRelaxationPass Pass(rcc::riscv::instrInfo()); auto Result = Pass.run(*Function, Target, Analyses); if (!Result) std::cerr << "conditional relaxation failure: " << Result.error().message() << '\n'; @@ -998,7 +1011,7 @@ void testCompressedAndConditionalRelaxation(TestContext &Context, if (!Compressed) return; auto Expanded = - rcc::riscv::branchInfo().relaxBranch(**Compressed, 4096, *CompressedFunction, Target); + rcc::riscv::instrInfo().relaxBranch(**Compressed, 4096, *CompressedFunction, Target); Context.expect(Expanded && *Expanded && CompressedEntry->firstInstruction() != nullptr && CompressedEntry->firstInstruction()->opcode() == opcode(rcc::riscv::InstructionOpcode::JAL) && @@ -1029,13 +1042,13 @@ void testLongJALRelaxation(TestContext &Context, const rcc::TargetDescription &T "long JAL live-in and annotation fixture must be valid"); if (!X5 || !X6) return; - auto TooFar = rcc::riscv::branchInfo().relaxBranch(*JAL, std::numeric_limits::max(), - *Function, Target); + auto TooFar = rcc::riscv::instrInfo().relaxBranch(*JAL, std::numeric_limits::max(), + *Function, Target); Context.expect(!TooFar && TooFar.error().code() == rcc::ErrorCode::BranchRelaxationFailed && JAL->parent() == Entry, "long JAL relaxation must reject an unencodable PC-relative distance atomically"); auto Relaxed = - rcc::riscv::branchInfo().relaxBranch(*JAL, std::int64_t{2} * 1024 * 1024, *Function, Target); + rcc::riscv::instrInfo().relaxBranch(*JAL, std::int64_t{2} * 1024 * 1024, *Function, Target); if (!Relaxed) std::cerr << "long JAL relaxation failure: " << Relaxed.error().message() << '\n'; rcc::MachineInstr *High = Entry->firstInstruction(); @@ -1186,7 +1199,7 @@ void testPostRAPseudoExpansionAndFinalization(TestContext &Context, rcc::MachinePassManager Pipeline; Context.expect(Pipeline.addPass(std::make_unique()) && Pipeline.addPass(std::make_unique( - rcc::riscv::branchInfo())), + rcc::riscv::instrInfo())), "finalization passes must register"); auto Finalized = Pipeline.run(*Function, Target, Analyses); if (!Finalized) @@ -1310,8 +1323,8 @@ void testTargetLateExpansionRejectsSizeGrowth(TestContext &Context) { return; GrowingLatePseudoInfo ExpansionInfo; - FixedSizeBranchInfo BranchInfo; - rcc::TargetLatePseudoExpansionPass Pass(ExpansionInfo, BranchInfo); + FixedSizeInstrInfo InstrInfo; + rcc::TargetLatePseudoExpansionPass Pass(ExpansionInfo, InstrInfo); rcc::MachineAnalysisManager Analyses; auto Result = Pass.run(*Function, *Target, Analyses); Context.expect(!Result && Result.error().code() == rcc::ErrorCode::PseudoExpansionFailed && @@ -1358,8 +1371,8 @@ void testTargetLateExpansionRejectsCompensatedGrowth(TestContext &Context) { return; GrowingLatePseudoInfo ExpansionInfo; - FixedSizeBranchInfo BranchInfo; - rcc::TargetLatePseudoExpansionPass Pass(ExpansionInfo, BranchInfo); + FixedSizeInstrInfo InstrInfo; + rcc::TargetLatePseudoExpansionPass Pass(ExpansionInfo, InstrInfo); rcc::MachineAnalysisManager Analyses; auto Result = Pass.run(*Function, *Target, Analyses); Context.expect( @@ -1391,7 +1404,7 @@ void testFinalEncodabilityRejectsInvalidInstruction(TestContext &Context, } setRelaxationProperties(Context, *ImmediateFunction, Target); rcc::MachineAnalysisManager Analyses; - rcc::FinalEncodabilityVerifierPass Verifier(rcc::riscv::branchInfo()); + rcc::FinalEncodabilityVerifierPass Verifier(rcc::riscv::instrInfo()); auto ImmediateResult = Verifier.run(*ImmediateFunction, Target, Analyses); Context.expect(!ImmediateResult && ImmediateResult.error().code() == rcc::ErrorCode::FinalEncodabilityFailed, diff --git a/backend/unittests/Target/RISCVSSAExitTest.cpp b/backend/unittests/Target/RISCVSSAExitTest.cpp index 8e1f5a2..18cbea9 100644 --- a/backend/unittests/Target/RISCVSSAExitTest.cpp +++ b/backend/unittests/Target/RISCVSSAExitTest.cpp @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later +#include "RISCVInstrInfo.h" #include "rcc/CodeGen/GenericOpcodes.h" #include "rcc/CodeGen/MachineBasicBlock.h" #include "rcc/CodeGen/MachineCFGUpdater.h" @@ -196,11 +197,11 @@ void testBehaviorAndCopyLegality(TestContext &Context, const rcc::TargetDescript if (!CFG.addSuccessor(**First, **Second, rcc::BranchProbability::one())) return; - auto Fallthrough = rcc::riscv::ssaInfo().isFallthroughEdge(**First, **Second, **Function, Target); + auto Fallthrough = + rcc::riscv::instrInfo().isFallthroughEdge(**First, **Second, **Function, Target); Context.expect(Fallthrough && *Fallthrough, "RV64GC SSA behavior must recognize an empty layout fallthrough edge"); - auto Inserted = - rcc::riscv::ssaInfo().insertUnconditionalBranch(**First, **Second, **Function, Target); + auto Inserted = rcc::riscv::instrInfo().insertBranch(**First, **Second, **Function, Target); Context.expect(Inserted.has_value(), "RV64GC SSA behavior must insert an unconditional branch"); if (!Inserted) return; @@ -212,10 +213,10 @@ void testBehaviorAndCopyLegality(TestContext &Context, const rcc::TargetDescript blockOperand(*Jump, 1) != nullptr && blockOperand(*Jump, 1)->Block == *Second, "RV64GC unconditional branches must use JAL with x0 as the link destination"); auto NoFallthrough = - rcc::riscv::ssaInfo().isFallthroughEdge(**First, **Second, **Function, Target); + rcc::riscv::instrInfo().isFallthroughEdge(**First, **Second, **Function, Target); Context.expect(NoFallthrough && !*NoFallthrough, "JAL must terminate RV64GC layout fallthrough"); auto Rewritten = - rcc::riscv::ssaInfo().rewriteBranchTarget(**First, **Second, **Third, **Function, Target); + rcc::riscv::instrInfo().rewriteBranchTarget(**First, **Second, **Third, **Function, Target); Context.expect(Rewritten && *Rewritten && blockOperand(*Jump, 1)->Block == *Third, "RV64GC branch target rewriting must update direct JAL operands"); @@ -237,21 +238,75 @@ void testBehaviorAndCopyLegality(TestContext &Context, const rcc::TargetDescript return; Context.expect( - rcc::riscv::ssaInfo().validateCopy(*GPR, {}, *OtherGPR, {}, **Function, Target).has_value(), + rcc::riscv::instrInfo().validateCopy(*GPR, {}, *OtherGPR, {}, **Function, Target).has_value(), "RV64GC must allow a compatible GPR COPY"); Context.expect( - rcc::riscv::ssaInfo().validateCopy(*GPR, {}, *FPR64, {}, **Function, Target).has_value(), + rcc::riscv::instrInfo().validateCopy(*GPR, {}, *FPR64, {}, **Function, Target).has_value(), "RV64GC must allow supported 64-bit GPR/FPR bit moves"); - Context.expect(!rcc::riscv::ssaInfo().validateCopy(*FPR64, {}, *FPR32, {}, **Function, Target), + Context.expect(!rcc::riscv::instrInfo().validateCopy(*FPR64, {}, *FPR32, {}, **Function, Target), "RV64GC must reject incompatible same-bank COPY widths"); - Context.expect(!rcc::riscv::ssaInfo().validateCopy(*GPR, rcc::SubRegIndex::fromValue(1), - *OtherGPR, {}, **Function, Target), + Context.expect(!rcc::riscv::instrInfo().validateCopy(*GPR, rcc::SubRegIndex::fromValue(1), + *OtherGPR, {}, **Function, Target), "RV64GC must reject unmodeled subregister COPYs"); Context.expect( - !rcc::riscv::ssaInfo().validateCopy(*GPR, {}, *Unclassified, {}, **Function, Target), + !rcc::riscv::instrInfo().validateCopy(*GPR, {}, *Unclassified, {}, **Function, Target), "RV64GC must reject COPYs involving an unconstrained virtual register"); } +void testCompressedConditionalBranchBehavior(TestContext &Context, + const rcc::TargetDescription &Target) { + auto Function = rcc::MachineFunction::create("riscv_compressed_branch", + rcc::MachineFunctionStableID::fromValue(3)); + if (!Function) + return; + auto Source = appendBlock(**Function); + auto LayoutSuccessor = appendBlock(**Function); + auto BranchTarget = appendBlock(**Function); + auto ReplacementTarget = appendBlock(**Function); + if (!Source || !LayoutSuccessor || !BranchTarget || !ReplacementTarget) + return; + auto Half = rcc::BranchProbability::fromFraction(1, 2); + auto CFG = (*Function)->cfgUpdater(); + if (!Half || !CFG.addSuccessor(**Source, **BranchTarget, *Half) || + !CFG.addSuccessor(**Source, **LayoutSuccessor, *Half)) { + Context.expect(false, "compressed-branch CFG construction must succeed"); + return; + } + + auto X8 = rcc::riscv::physicalRegister(rcc::riscv::PhysicalRegisterID::X8); + rcc::MachineIRBuilder Builder(**Function, Target); + auto Point = Builder.setInsertPoint(**Source); + if (!Point) { + Context.expect(false, "compressed conditional branch insertion point must be established"); + return; + } + auto Pending = Builder.buildInstr(targetOpcode(rcc::riscv::InstructionOpcode::C_BEQZ)); + if (!X8 || !Pending || !Pending->addUse(rcc::Register::fromPhysical(*X8)) || + !Pending->addBasicBlock(**BranchTarget)) { + Context.expect(false, "compressed conditional branch construction must succeed"); + return; + } + auto Branch = Pending->build(); + if (!Branch) { + Context.expect(false, "compressed conditional branch insertion must succeed"); + return; + } + + auto Fallthrough = + rcc::riscv::instrInfo().isFallthroughEdge(**Source, **LayoutSuccessor, **Function, Target); + auto Analysis = rcc::riscv::instrInfo().analyzeBranch(**Source, **Function, Target); + Context.expect(Fallthrough && *Fallthrough && Analysis && Analysis->Conditional == *Branch && + Analysis->TrueTarget == *BranchTarget && + Analysis->FalseTarget == *LayoutSuccessor, + "RV64GC instruction information must recognize compressed conditional branches"); + + auto Rewritten = rcc::riscv::instrInfo().rewriteBranchTarget( + **Source, **BranchTarget, **ReplacementTarget, **Function, Target); + Context.expect(Rewritten && *Rewritten && blockOperand(**Branch, 1) != nullptr && + blockOperand(**Branch, 1)->Block == *ReplacementTarget, + "RV64GC instruction information must rewrite compressed branch targets"); +} + void testRV64GCCriticalEdge(TestContext &Context, const rcc::TargetDescription &Target) { auto Function = rcc::MachineFunction::create("riscv_ssa_critical_edge", rcc::MachineFunctionStableID::fromValue(2)); @@ -298,9 +353,10 @@ void testRV64GCCriticalEdge(TestContext &Context, const rcc::TargetDescription & return; rcc::MachinePassManager Pipeline; - Context.expect(Pipeline.addPass(std::make_unique(rcc::riscv::ssaInfo())) - .has_value(), - "RV64GC PHI-elimination pass must be registered"); + Context.expect( + Pipeline.addPass(std::make_unique(rcc::riscv::instrInfo())) + .has_value(), + "RV64GC PHI-elimination pass must be registered"); rcc::MachineAnalysisManager Analyses; auto Run = Pipeline.run(**Function, Target, Analyses); Context.expect(Run.has_value(), "RV64GC critical-edge PHI elimination must succeed"); @@ -350,6 +406,7 @@ int main() { if (!Target) return Context.result(); testBehaviorAndCopyLegality(Context, *Target); + testCompressedConditionalBranchBehavior(Context, *Target); testRV64GCCriticalEdge(Context, *Target); return Context.result(); } -- cgit v1.2.3