diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-19 04:27:41 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-19 04:27:41 +0800 |
| commit | 307fc0961972ac5264d6f3459978fbec95cad3a9 (patch) | |
| tree | 80eff53db8f62a736a46221d258dcadf7543fa88 /backend/lib/Target | |
| parent | 6a91664223881226e2b6186561c05ff378a19355 (diff) | |
| download | rcc-project-307fc0961972ac5264d6f3459978fbec95cad3a9.tar.gz rcc-project-307fc0961972ac5264d6f3459978fbec95cad3a9.tar.xz | |
[RISCV][GlobalISel] Select and expand global addresses
Diffstat (limited to 'backend/lib/Target')
| -rw-r--r-- | backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp | 178 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVAsmBackend.cpp | 9 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVInstPrinter.cpp | 2 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVInstrInfoI.rccdesc | 2 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVInstrInfoPseudo.rccdesc | 25 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVMCCodeEmitter.cpp | 3 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp | 11 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVPipeline.cpp | 6 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp | 123 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVRegisterInfo.rccdesc | 3 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVSubtarget.cpp | 4 |
11 files changed, 359 insertions, 7 deletions
diff --git a/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp index a4a2113..094f9cb 100644 --- a/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp +++ b/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp @@ -5,6 +5,7 @@ #include "RISCVInstrInfo.h" #include "RISCVRegisterBankInfo.h" #include "rcc/CodeGen/GenericOpcodes.h" +#include "rcc/CodeGen/GlobalObject.h" #include "rcc/CodeGen/GlobalISel/GISelValueTracking.h" #include "rcc/CodeGen/GlobalISel/InstructionSelector.h" #include "rcc/CodeGen/LowLevelType.h" @@ -12,6 +13,7 @@ #include "rcc/CodeGen/MachineFunction.h" #include "rcc/CodeGen/MachineIRBuilder.h" #include "rcc/CodeGen/MachineInstr.h" +#include "rcc/CodeGen/MachineMemOperand.h" #include "rcc/CodeGen/MachineOperand.h" #include "rcc/CodeGen/MachineRegisterInfo.h" #include "rcc/CodeGen/TargetDescription.h" @@ -33,6 +35,9 @@ namespace rcc::riscv { namespace { +constexpr std::uint32_t AbsoluteHighFlag = 32; +constexpr std::uint32_t AbsoluteLowFlag = 64; + #define GET_GLOBALISEL_MATCH_TABLE #include "RISCVGenGlobalISel.inc" #undef GET_GLOBALISEL_MATCH_TABLE @@ -591,6 +596,9 @@ public: return {}; case GenericOpcode::G_FPEXTLOAD: case GenericOpcode::G_FPTRUNCSTORE: + case GenericOpcode::G_GLOBAL_VALUE: + case GenericOpcode::G_JUMP_TABLE: + case GenericOpcode::G_CONSTANT_POOL: return {}; case GenericOpcode::G_FCONSTANT: { const MachineOperand *Immediate = Instruction.operandAt(1); @@ -692,6 +700,10 @@ public: return selectAtomicRMW(Instruction, Function, Target, Type, Generic); case GenericOpcode::G_FENCE: return selectFence(Instruction); + case GenericOpcode::G_GLOBAL_VALUE: + case GenericOpcode::G_JUMP_TABLE: + case GenericOpcode::G_CONSTANT_POOL: + return selectGlobalAddress(Instruction, Function, Target); case GenericOpcode::G_PHI: return Instruction.setOpcode(opcode(GenericOpcode::PHI)); case GenericOpcode::COPY: @@ -715,6 +727,172 @@ public: } private: + [[nodiscard]] Expected<const MachineMemOperand *> + createGOTLoad(MachineFunction &Function, LowLevelType Type) const { + auto Size = MemorySize::fixed(Type.minimumBitWidth() / 8U); + if (!Size) + return std::unexpected(Size.error()); + auto Alignment = rcc::Alignment::create(Type.minimumBitWidth() / 8U); + if (!Alignment) + return std::unexpected(Alignment.error()); + MachineMemOperandDesc Description; + Description.Accesses = {MemoryAccessFlag::Load, MemoryAccessFlag::Dereferenceable, + MemoryAccessFlag::Invariant}; + Description.Size = *Size; + Description.MemoryType = Type; + Description.Align = *Alignment; + Description.Base = MemoryBase::got(); + return Function.createMemOperand(std::move(Description)); + } + + [[nodiscard]] Expected<void> + selectGlobalAddress(MachineInstr &Instruction, MachineFunction &Function, + const TargetDescription &Target) const { + const MachineOperand *DestinationOperand = Instruction.operandAt(0); + const MachineOperand *SymbolOperand = Instruction.operandAt(1); + const Register *Destination = + DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); + const std::optional<LowLevelType> Type = resultType(Function, Instruction); + if (Destination == nullptr || SymbolOperand == nullptr || !Type || !Type->isPointer()) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V address selection has malformed generic operands"); + } + auto DestinationConstraint = constrainBuilderRegisters(Function, Target, {*Destination}); + if (!DestinationConstraint) + return std::unexpected(DestinationConstraint.error()); + + GlobalValueProperties Properties; + if (Instruction.opcode() == opcode(GenericOpcode::G_GLOBAL_VALUE)) { + const auto *Global = SymbolOperand->getIf<GlobalSymbolOperand>(); + if (Global == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V global address has no global symbol operand"); + Properties = Global->Properties; + if (Properties.ThreadLocal) { + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V GlobalISel does not support thread-local global addresses"); + } + } + + const bool TaggedGlobals = Subtarget.features().has(RISCVSubtargetOption::TaggedGlobals); + const bool UseGOT = Subtarget.relocationModel() == RelocationModel::PositionIndependent || + TaggedGlobals; + const auto ReplaceWithPseudo = [&](InstructionOpcode OpcodeValue) -> Expected<void> { + auto SetOpcode = Instruction.setOpcode(instructionOpcode(OpcodeValue)); + if (!SetOpcode) + return std::unexpected(SetOpcode.error()); + return constrainSelectedInstruction(Instruction, Function, Target); + }; + const auto BuildGOTLoad = [&]() -> Expected<void> { + auto Memory = createGOTLoad(Function, *Type); + if (!Memory) + return std::unexpected(Memory.error()); + MachineIRBuilder Builder(Function, Target); + auto Point = Builder.setInsertPointBefore(Instruction); + if (!Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(instructionOpcode(InstructionOpcode::PseudoLGA)); + if (!Pending) + return std::unexpected(Pending.error()); + auto Def = Pending->addDef(*Destination); + if (!Def) + return std::unexpected(Def.error()); + auto Symbol = Pending->addOperand(SymbolOperand->cloneDetached()); + if (!Symbol) + return std::unexpected(Symbol.error()); + auto MemoryAdded = Pending->addMemOperand(**Memory); + if (!MemoryAdded) + return std::unexpected(MemoryAdded.error()); + auto Result = Pending->build(); + if (!Result) + return std::unexpected(Result.error()); + auto Flags = propagateSelectionFlags(**Result, Instruction, Target); + if (!Flags) + return std::unexpected(Flags.error()); + auto Constraint = constrainSelectedInstruction(**Result, Function, Target); + if (!Constraint) + return std::unexpected(Constraint.error()); + MachineBasicBlock *Block = Instruction.parent(); + if (Block == nullptr) + return makeUnexpected(ErrorCode::InternalError, + "RISC-V global address is detached from its block"); + return Block->removeInstruction(Instruction); + }; + + if (UseGOT) { + if (Properties.DSOLocal && !TaggedGlobals) + return ReplaceWithPseudo(InstructionOpcode::PseudoLLA); + return BuildGOTLoad(); + } + + if (Subtarget.codeModel() == CodeModel::Medium) { + if (Properties.ExternalWeak) + return BuildGOTLoad(); + return ReplaceWithPseudo(InstructionOpcode::PseudoLLA); + } + if (Subtarget.codeModel() != CodeModel::Small) { + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V GlobalISel does not support this global address code model"); + } + + auto Temporary = createTemporary(Function, *Type, Target); + if (!Temporary) + return std::unexpected(Temporary.error()); + MachineIRBuilder Builder(Function, Target); + auto Point = Builder.setInsertPointBefore(Instruction); + if (!Point) + return std::unexpected(Point.error()); + auto HighPending = Builder.buildInstr(instructionOpcode(InstructionOpcode::LUI)); + if (!HighPending) + return std::unexpected(HighPending.error()); + auto HighDef = HighPending->addDef(*Temporary); + if (!HighDef) + return std::unexpected(HighDef.error()); + MachineOperand HighSymbol = SymbolOperand->cloneDetached(); + HighSymbol.setTargetFlags(AbsoluteHighFlag); + auto HighOperand = HighPending->addOperand(std::move(HighSymbol)); + if (!HighOperand) + return std::unexpected(HighOperand.error()); + auto High = HighPending->build(); + if (!High) + return std::unexpected(High.error()); + auto HighFlags = propagateSelectionFlags(**High, Instruction, Target); + if (!HighFlags) + return std::unexpected(HighFlags.error()); + auto HighConstraint = constrainSelectedInstruction(**High, Function, Target); + if (!HighConstraint) + return std::unexpected(HighConstraint.error()); + + auto LowPending = Builder.buildInstr(instructionOpcode(InstructionOpcode::ADDI)); + if (!LowPending) + return std::unexpected(LowPending.error()); + auto LowDef = LowPending->addDef(*Destination); + if (!LowDef) + return std::unexpected(LowDef.error()); + auto LowSource = LowPending->addUse(*Temporary); + if (!LowSource) + return std::unexpected(LowSource.error()); + MachineOperand LowSymbol = SymbolOperand->cloneDetached(); + LowSymbol.setTargetFlags(AbsoluteLowFlag); + auto LowOperand = LowPending->addOperand(std::move(LowSymbol)); + if (!LowOperand) + return std::unexpected(LowOperand.error()); + auto Low = LowPending->build(); + if (!Low) + return std::unexpected(Low.error()); + auto LowFlags = propagateSelectionFlags(**Low, Instruction, Target); + if (!LowFlags) + return std::unexpected(LowFlags.error()); + auto LowConstraint = constrainSelectedInstruction(**Low, Function, Target); + if (!LowConstraint) + return std::unexpected(LowConstraint.error()); + MachineBasicBlock *Block = Instruction.parent(); + if (Block == nullptr) + return makeUnexpected(ErrorCode::InternalError, + "RISC-V global address is detached from its block"); + return Block->removeInstruction(Instruction); + } + [[nodiscard]] Expected<void> selectCopy(MachineInstr &Instruction, MachineFunction &Function, const TargetDescription &Target) const { const MachineOperand *DestinationOperand = Instruction.operandAt(0); diff --git a/backend/lib/Target/RISCV/RISCVAsmBackend.cpp b/backend/lib/Target/RISCV/RISCVAsmBackend.cpp index ac17403..e118d79 100644 --- a/backend/lib/Target/RISCV/RISCVAsmBackend.cpp +++ b/backend/lib/Target/RISCV/RISCVAsmBackend.cpp @@ -21,6 +21,7 @@ enum class RISCVRelocation : std::uint32_t { Branch = 16, JAL = 17, CallPLT = 19, + GOTHigh20 = 20, PCRelativeHigh20 = 23, PCRelativeLow12I = 24, PCRelativeLow12S = 25, @@ -193,6 +194,7 @@ Expected<void> RISCVAsmBackend::applyFixup(mc::MCFixupKind Kind, std::span<std:: std::uint32_t Instruction = readWord(Data, Offset); if (isTargetKind(Kind, RISCVFixupKind::High20) || isTargetKind(Kind, RISCVFixupKind::PCRelativeHigh20) || + isTargetKind(Kind, RISCVFixupKind::GOTPCRelativeHigh20) || isTargetKind(Kind, RISCVFixupKind::CallPLT)) { const std::uint64_t Adjusted = static_cast<std::uint64_t>(Value) + 0x800U; const std::uint32_t High = static_cast<std::uint32_t>(Adjusted >> 12U); @@ -244,6 +246,13 @@ Expected<std::uint32_t> RISCVAsmBackend::relocationType(const mc::MCFixup &Fixup return std::unexpected(Kind.error()); return Relocation(RISCVRelocation::PCRelativeHigh20); } + if (isTargetKind(Fixup.Kind, RISCVFixupKind::GOTPCRelativeHigh20)) { + auto Kind = requireRelocationTargetKind( + Value, static_cast<std::uint32_t>(RISCVMCExprKind::GOTPCRelativeHigh), "R_RISCV_GOT_HI20"); + if (!Kind) + return std::unexpected(Kind.error()); + return Relocation(RISCVRelocation::GOTHigh20); + } if (isTargetKind(Fixup.Kind, RISCVFixupKind::PCRelativeLow12I) || isTargetKind(Fixup.Kind, RISCVFixupKind::PCRelativeLow12S)) { auto Kind = requireRelocationTargetKind( diff --git a/backend/lib/Target/RISCV/RISCVInstPrinter.cpp b/backend/lib/Target/RISCV/RISCVInstPrinter.cpp index e72681a..723628f 100644 --- a/backend/lib/Target/RISCV/RISCVInstPrinter.cpp +++ b/backend/lib/Target/RISCV/RISCVInstPrinter.cpp @@ -279,6 +279,8 @@ Expected<std::string> RISCVInstPrinter::printTargetExpression(std::uint32_t Kind return "%hi(" + std::string(Operand) + ")"; case RISCVMCExprKind::AbsoluteLow: return "%lo(" + std::string(Operand) + ")"; + case RISCVMCExprKind::GOTPCRelativeHigh: + return "%got_pcrel_hi(" + std::string(Operand) + ")"; } return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "RISC-V MC expression has an unknown target kind"); diff --git a/backend/lib/Target/RISCV/RISCVInstrInfoI.rccdesc b/backend/lib/Target/RISCV/RISCVInstrInfoI.rccdesc index 33f13a9..7bb30e5 100644 --- a/backend/lib/Target/RISCV/RISCVInstrInfoI.rccdesc +++ b/backend/lib/Target/RISCV/RISCVInstrInfoI.rccdesc @@ -21,7 +21,7 @@ instructions: encoding: 5143 operands: - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD } - - { kinds: [immediate, basic_block, global_symbol, external_symbol, mc_symbol], immediate_predicate: UIMM20, allowed_target_flags: 3, name: IMM } + - { kinds: [immediate, basic_block, global_symbol, external_symbol, mc_symbol], immediate_predicate: UIMM20, allowed_target_flags: 130, name: IMM } num_explicit_defs: 1 flags: [produces_code] scheduling_class: ALU diff --git a/backend/lib/Target/RISCV/RISCVInstrInfoPseudo.rccdesc b/backend/lib/Target/RISCV/RISCVInstrInfoPseudo.rccdesc index 4523641..14bcf26 100644 --- a/backend/lib/Target/RISCV/RISCVInstrInfoPseudo.rccdesc +++ b/backend/lib/Target/RISCV/RISCVInstrInfoPseudo.rccdesc @@ -409,6 +409,31 @@ instructions: owner: post_ra_expansion deadline: post_ra_finalized - + id: 1267 + symbol: PseudoLLA + name: PseudoLLA + operands: + - { kinds: [register], role: def, register_class: GPR, register_bank: GPR } + - { kinds: [constant_pool_index, jump_table_index, global_symbol, external_symbol, block_address, mc_symbol] } + num_explicit_defs: 1 + flags: [pseudo, produces_code, allowed_pre_legalizer] + size: 8 + owner: pre_ra_expansion + deadline: tied_ops_rewritten + - + id: 1268 + symbol: PseudoLGA + name: PseudoLGA + operands: + - { kinds: [register], role: def, register_class: GPR, register_bank: GPR } + - { kinds: [constant_pool_index, jump_table_index, global_symbol, external_symbol, block_address, mc_symbol] } + num_explicit_defs: 1 + flags: [pseudo, produces_code, allowed_pre_legalizer, may_load] + size: 8 + requires_memory_operands: true + owner: pre_ra_expansion + deadline: tied_ops_rewritten + - id: 1263 symbol: PseudoBRIND name: PseudoBRIND diff --git a/backend/lib/Target/RISCV/RISCVMCCodeEmitter.cpp b/backend/lib/Target/RISCV/RISCVMCCodeEmitter.cpp index 9ba3021..15a0c2d 100644 --- a/backend/lib/Target/RISCV/RISCVMCCodeEmitter.cpp +++ b/backend/lib/Target/RISCV/RISCVMCCodeEmitter.cpp @@ -186,6 +186,8 @@ namespace { return fixupKind(RISCVFixupKind::High20); if (*Kind == static_cast<std::uint32_t>(RISCVMCExprKind::PCRelativeHigh)) return fixupKind(RISCVFixupKind::PCRelativeHigh20); + if (*Kind == static_cast<std::uint32_t>(RISCVMCExprKind::GOTPCRelativeHigh)) + return fixupKind(RISCVFixupKind::GOTPCRelativeHigh20); if (*Kind == static_cast<std::uint32_t>(RISCVMCExprKind::CallPLT)) return fixupKind(RISCVFixupKind::CallPLT); return makeUnexpected(ErrorCode::InstructionEncodingFailed, @@ -493,6 +495,7 @@ namespace { if (!Kind) return std::unexpected(Kind.error()); const bool PCRelative = *Kind == fixupKind(RISCVFixupKind::PCRelativeHigh20) || + *Kind == fixupKind(RISCVFixupKind::GOTPCRelativeHigh20) || *Kind == fixupKind(RISCVFixupKind::CallPLT); auto Immediate = immediateOrFixup(Instruction, 1, *Kind, PCRelative, Result); if (!Immediate) diff --git a/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp b/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp index 78c56a3..61cb587 100644 --- a/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp +++ b/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp @@ -48,9 +48,11 @@ constexpr std::uint32_t CopyF32TargetFlag = 8; constexpr std::uint32_t CopyF64TargetFlag = 16; constexpr std::uint32_t AbsoluteHighTargetFlag = 32; constexpr std::uint32_t AbsoluteLowTargetFlag = 64; -constexpr std::uint32_t KnownTargetFlags = - CallTargetFlag | PCRelativeHighTargetFlag | PCRelativeLowTargetFlag | CopyF32TargetFlag | - CopyF64TargetFlag | AbsoluteHighTargetFlag | AbsoluteLowTargetFlag; +constexpr std::uint32_t GOTPCRelativeHighTargetFlag = 128; +constexpr std::uint32_t KnownTargetFlags = CallTargetFlag | PCRelativeHighTargetFlag | + PCRelativeLowTargetFlag | CopyF32TargetFlag | + CopyF64TargetFlag | AbsoluteHighTargetFlag | + AbsoluteLowTargetFlag | GOTPCRelativeHighTargetFlag; [[nodiscard]] Expected<std::uint8_t> targetRelocationSize(TargetRelocationKindID Kind) { if (Kind == targetRelocationKind(RISCVTargetRelocationKind::Absolute32)) @@ -97,6 +99,9 @@ constexpr std::uint32_t KnownTargetFlags = case AbsoluteLowTargetFlag: Kind = RISCVMCExprKind::AbsoluteLow; break; + case GOTPCRelativeHighTargetFlag: + Kind = RISCVMCExprKind::GOTPCRelativeHigh; + break; default: return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "RISC-V operand combines incompatible direct target flags"); diff --git a/backend/lib/Target/RISCV/RISCVPipeline.cpp b/backend/lib/Target/RISCV/RISCVPipeline.cpp index f1de092..00a4398 100644 --- a/backend/lib/Target/RISCV/RISCVPipeline.cpp +++ b/backend/lib/Target/RISCV/RISCVPipeline.cpp @@ -12,6 +12,7 @@ #include "rcc/CodeGen/MachineCombiner.h" #include "rcc/CodeGen/MachineFunction.h" #include "rcc/CodeGen/MachineOptimizationPipeline.h" +#include "rcc/CodeGen/MachinePseudoExpansion.h" #include "rcc/CodeGen/PHIElimination.h" #include "rcc/CodeGen/RegAllocPBQP.h" #include "rcc/CodeGen/TargetDescription.h" @@ -65,7 +66,10 @@ template <typename PassT, typename... ArgumentT> } [[nodiscard]] Expected<void> addRegisterAllocationPipeline(MachineFunctionPassManager &Pipeline) { - auto Added = addPreRegisterAllocationOptimizationPipeline(Pipeline, optimizationPipelineInfo()); + auto Added = addPass<PreRATargetPseudoExpansionPass>(Pipeline, pseudoExpansionInfo()); + if (!Added) + return std::unexpected(Added.error()); + Added = addPreRegisterAllocationOptimizationPipeline(Pipeline, optimizationPipelineInfo()); if (!Added) return std::unexpected(Added.error()); return addPass<PBQPAllocatorPass>(Pipeline, registerAllocationInfo()); diff --git a/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp b/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp index df3e5ad..257fa3a 100644 --- a/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp +++ b/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp @@ -28,6 +28,7 @@ constexpr std::uint32_t PCRelativeHighFlag = 2; constexpr std::uint32_t PCRelativeLowFlag = 4; constexpr std::uint32_t CopyF32Flag = 8; constexpr std::uint32_t CopyF64Flag = 16; +constexpr std::uint32_t GOTPCRelativeHighFlag = 128; [[nodiscard]] std::optional<RegisterBankID> registerBank(Register Reg, const MachineFunction &Function, const TargetDescription &Target) { @@ -300,6 +301,121 @@ copyRegisters(const MachineInstr &Instruction) { "RISC-V direct call pseudo has no symbol target"); } +[[nodiscard]] Expected<Register> createGPRScratchRegister(MachineFunction &Function, + const TargetDescription &Target) { + const RegisterBankID GPRBank = + RegisterBankID::fromValue(static_cast<std::uint32_t>(RegisterBank::GPR)); + const RegisterClassID GPRClass = + RegisterClassID::fromValue(static_cast<std::uint32_t>(RegisterClass::GPR)); + if (Target.registers().registerBank(GPRBank) == nullptr || + Target.registers().registerClass(GPRClass) == nullptr) { + return makeUnexpected(ErrorCode::InternalError, + "RISC-V pre-RA address expansion requires the GPR register class"); + } + auto Scratch = Function.registerInfo().createVirtualRegister(); + if (!Scratch) + return std::unexpected(Scratch.error()); + auto SetBank = Function.registerInfo().setRegisterBank(*Scratch, GPRBank); + if (!SetBank) + return std::unexpected(SetBank.error()); + auto SetClass = Function.registerInfo().setRegisterClass(*Scratch, GPRClass); + if (!SetClass) + return std::unexpected(SetClass.error()); + return *Scratch; +} + +[[nodiscard]] Expected<bool> expandAddressPseudo(MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target, + bool LoadFromGOT) { + const MachineOperand *Destination = Instruction.operandAt(0); + const MachineOperand *Symbol = Instruction.operandAt(1); + const Register *DestinationRegister = + Destination == nullptr ? nullptr : Destination->getIf<Register>(); + if (DestinationRegister == nullptr || Symbol == nullptr || Instruction.operandCount() != 2) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V address pseudo has malformed explicit operands"); + } + if (LoadFromGOT && Instruction.memOperands().size() != 1) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V global-address pseudo requires one GOT load memory operand"); + } + if (!LoadFromGOT && !Instruction.memOperands().empty()) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V local-address pseudo cannot carry memory operands"); + } + + auto Scratch = createGPRScratchRegister(Function, Target); + if (!Scratch) + return std::unexpected(Scratch.error()); + MachineIRBuilder Builder(Function, Target); + auto Point = Builder.setInsertPointBefore(Instruction); + if (!Point) + return std::unexpected(Point.error()); + + auto HighPending = Builder.buildInstr(instructionOpcode(InstructionOpcode::AUIPC)); + if (!HighPending) + return std::unexpected(HighPending.error()); + auto HighDef = HighPending->addDef(*Scratch); + if (!HighDef) + return std::unexpected(HighDef.error()); + MachineOperand HighSymbol = Symbol->cloneDetached(); + HighSymbol.setTargetFlags(LoadFromGOT ? GOTPCRelativeHighFlag : PCRelativeHighFlag); + auto HighTarget = HighPending->addOperand(std::move(HighSymbol)); + if (!HighTarget) + return std::unexpected(HighTarget.error()); + auto High = HighPending->build(); + if (!High) + return std::unexpected(High.error()); + const std::string Anchor = Instruction.preInstrSymbol() + ? std::string(*Instruction.preInstrSymbol()) + : ".Lrcc_pcrel_hi_" + std::to_string(Function.stableID().value()) + + "_" + std::to_string((*High)->stableID().value()); + auto HighAnchor = (*High)->setPreInstrSymbol(Anchor); + if (!HighAnchor) + return std::unexpected(HighAnchor.error()); + auto HighDecoration = copyCommonDecoration(Instruction, **High, false, false, false); + if (!HighDecoration) + return std::unexpected(HighDecoration.error()); + + const InstructionOpcode LowOpcode = LoadFromGOT ? InstructionOpcode::LD : InstructionOpcode::ADDI; + auto LowPending = Builder.buildInstr(instructionOpcode(LowOpcode)); + if (!LowPending) + return std::unexpected(LowPending.error()); + auto LowDef = LowPending->addOperand(Destination->cloneDetached()); + if (!LowDef) + return std::unexpected(LowDef.error()); + auto LowBase = LowPending->addUse(*Scratch); + if (!LowBase) + return std::unexpected(LowBase.error()); + auto LowOffset = LowPending->addMCSymbol(Anchor, 0, PCRelativeLowFlag); + if (!LowOffset) + return std::unexpected(LowOffset.error()); + if (LoadFromGOT) { + const MachineMemOperand *Memory = Instruction.memOperands().front(); + if (Memory == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V global-address pseudo has a null memory operand"); + auto AddedMemory = LowPending->addMemOperand(*Memory); + if (!AddedMemory) + return std::unexpected(AddedMemory.error()); + } + auto Low = LowPending->build(); + if (!Low) + return std::unexpected(Low.error()); + auto LowDecoration = copyCommonDecoration(Instruction, **Low, false, true, true); + if (!LowDecoration) + return std::unexpected(LowDecoration.error()); + auto Records = + Function.records().replaceInstructionReference(Instruction.stableID(), (*Low)->stableID()); + if (!Records) + return std::unexpected(Records.error()); + auto Removed = removeOldInstruction(Instruction); + if (!Removed) + return std::unexpected(Removed.error()); + return true; +} + [[nodiscard]] bool isRegister(const MachineOperand &Operand, PhysicalRegister Reg) noexcept { const Register *Value = Operand.getIf<Register>(); return Value != nullptr && Value->physicalRegister() == Reg; @@ -601,6 +717,13 @@ public: MachinePseudoExpansionPhase Phase, MachineFunction &Function, const TargetDescription &Target) const override { + if (Phase == MachinePseudoExpansionPhase::PreRA) { + if (Instruction.opcode() == instructionOpcode(InstructionOpcode::PseudoLLA)) + return expandAddressPseudo(Instruction, Function, Target, false); + if (Instruction.opcode() == instructionOpcode(InstructionOpcode::PseudoLGA)) + return expandAddressPseudo(Instruction, Function, Target, true); + return false; + } if (Phase != MachinePseudoExpansionPhase::PostRA) return false; if (Instruction.opcode() == opcode(GenericOpcode::COPY)) diff --git a/backend/lib/Target/RISCV/RISCVRegisterInfo.rccdesc b/backend/lib/Target/RISCV/RISCVRegisterInfo.rccdesc index 0a95adb..a2f26dc 100644 --- a/backend/lib/Target/RISCV/RISCVRegisterInfo.rccdesc +++ b/backend/lib/Target/RISCV/RISCVRegisterInfo.rccdesc @@ -388,7 +388,7 @@ register_masks: preserved: [X1, X8, X9, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, F8_D, F9_D, F18_D, F19_D, F20_D, F21_D, F22_D, F23_D, F24_D, F25_D, F26_D, F27_D, F8_F, F9_F, F18_F, F19_F, F20_F, F21_F, F22_F, F23_F, F24_F, F25_F, F26_F, F27_F] mir_operand_flags: - direct_mask: 127 + direct_mask: 255 direct: - { name: riscv-call, value: 1 } - { name: riscv-pcrel-hi, value: 2 } @@ -397,4 +397,5 @@ mir_operand_flags: - { name: riscv-copy-f64, value: 16 } - { name: riscv-hi, value: 32 } - { name: riscv-lo, value: 64 } + - { name: riscv-got-pcrel-hi, value: 128 } bitmask: [] diff --git a/backend/lib/Target/RISCV/RISCVSubtarget.cpp b/backend/lib/Target/RISCV/RISCVSubtarget.cpp index b6370fd..4b3064d 100644 --- a/backend/lib/Target/RISCV/RISCVSubtarget.cpp +++ b/backend/lib/Target/RISCV/RISCVSubtarget.cpp @@ -171,7 +171,9 @@ bool RISCVSubtargetFeatures::has(RISCVSubtargetOption Option) const noexcept { return (Options & optionBit(Option)) != 0; } -RISCVSubtarget::RISCVSubtarget(RISCVSubtargetFeatures Features) : Features(Features) {} +RISCVSubtarget::RISCVSubtarget(RISCVSubtargetFeatures Features, RelocationModel Relocation, + CodeModel Code) + : Features(Features), Relocation(Relocation), Code(Code) {} RISCVSubtarget::~RISCVSubtarget() = default; |
