From 307fc0961972ac5264d6f3459978fbec95cad3a9 Mon Sep 17 00:00:00 2001 From: Richard-Qin-X Date: Wed, 19 Aug 2026 04:27:41 +0800 Subject: [RISCV][GlobalISel] Select and expand global addresses --- backend/lib/CodeGen/MIRParser/MIParser.cpp | 5 +- backend/lib/CodeGen/MIRParser/MIRParser.cpp | 6 + backend/lib/CodeGen/MachineOperand.cpp | 7 +- backend/lib/CodeGen/MachinePseudoExpansion.cpp | 37 ++++- backend/lib/MIR/EmbeddedIR.cpp | 27 ++++ .../RISCV/GISel/RISCVInstructionSelector.cpp | 178 +++++++++++++++++++++ backend/lib/Target/RISCV/RISCVAsmBackend.cpp | 9 ++ backend/lib/Target/RISCV/RISCVInstPrinter.cpp | 2 + backend/lib/Target/RISCV/RISCVInstrInfoI.rccdesc | 2 +- .../lib/Target/RISCV/RISCVInstrInfoPseudo.rccdesc | 25 +++ backend/lib/Target/RISCV/RISCVMCCodeEmitter.cpp | 3 + backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp | 11 +- backend/lib/Target/RISCV/RISCVPipeline.cpp | 6 +- backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp | 123 ++++++++++++++ backend/lib/Target/RISCV/RISCVRegisterInfo.rccdesc | 3 +- backend/lib/Target/RISCV/RISCVSubtarget.cpp | 4 +- backend/lib/Tooling/MIRFuzzer.cpp | 2 +- backend/lib/Tooling/PipelineDriver.cpp | 2 +- 18 files changed, 438 insertions(+), 14 deletions(-) (limited to 'backend/lib') diff --git a/backend/lib/CodeGen/MIRParser/MIParser.cpp b/backend/lib/CodeGen/MIRParser/MIParser.cpp index 62f1f14..b44701b 100644 --- a/backend/lib/CodeGen/MIRParser/MIParser.cpp +++ b/backend/lib/CodeGen/MIRParser/MIParser.cpp @@ -2290,7 +2290,10 @@ bool MIParser::Impl::parseOperand(bool IsDef, ParsedOperand &Result) { std::int64_t Offset = 0; if (parseOptionalOffset(Offset)) return true; - auto Created = MachineOperand::globalSymbol(Name, Offset, TargetFlags); + const GlobalValueProperties Properties = Context.GlobalValuePropertiesForName + ? Context.GlobalValuePropertiesForName(Name) + : GlobalValueProperties{}; + auto Created = MachineOperand::globalSymbol(Name, Offset, TargetFlags, Properties); if (!Created) return importError(OperandToken, Created.error()); Operand = std::move(*Created); diff --git a/backend/lib/CodeGen/MIRParser/MIRParser.cpp b/backend/lib/CodeGen/MIRParser/MIRParser.cpp index 3b89491..c7e92a0 100644 --- a/backend/lib/CodeGen/MIRParser/MIRParser.cpp +++ b/backend/lib/CodeGen/MIRParser/MIRParser.cpp @@ -290,6 +290,12 @@ bool MIRDocumentParser::parseFunction(const yaml::Node &Root, MIRDocument &Docum return IRFunction->block(BlockName) != nullptr; }; } + if (Document.EmbeddedIR) { + const EmbeddedIRReferenceIndex *Embedded = &*Document.EmbeddedIR; + Context.GlobalValuePropertiesForName = [Embedded](std::string_view GlobalName) { + return Embedded->globalObjectProperties(GlobalName).value_or(GlobalValueProperties{}); + }; + } std::vector PendingJumpTables; if (parseFunctionScalars(Root, Function, DocumentInfo) || diff --git a/backend/lib/CodeGen/MachineOperand.cpp b/backend/lib/CodeGen/MachineOperand.cpp index 92b09dc..f265c3f 100644 --- a/backend/lib/CodeGen/MachineOperand.cpp +++ b/backend/lib/CodeGen/MachineOperand.cpp @@ -116,12 +116,13 @@ MachineOperand MachineOperand::jumpTable(JumpTableIndex Index, std::uint32_t Tar } Expected MachineOperand::globalSymbol(std::string Name, std::int64_t Offset, - std::uint32_t TargetFlags) { + std::uint32_t TargetFlags, + GlobalValueProperties Properties) { if (Name.empty()) { return makeUnexpected(ErrorCode::InvalidArgument, "global symbol operand name cannot be empty"); } - return MachineOperand(Kind::GlobalSymbol, GlobalSymbolOperand{std::move(Name), Offset}, - TargetFlags); + return MachineOperand(Kind::GlobalSymbol, + GlobalSymbolOperand{std::move(Name), Offset, Properties}, TargetFlags); } Expected MachineOperand::externalSymbol(std::string Name, std::int64_t Offset, diff --git a/backend/lib/CodeGen/MachinePseudoExpansion.cpp b/backend/lib/CodeGen/MachinePseudoExpansion.cpp index e3109ed..8388079 100644 --- a/backend/lib/CodeGen/MachinePseudoExpansion.cpp +++ b/backend/lib/CodeGen/MachinePseudoExpansion.cpp @@ -94,7 +94,8 @@ using BlockSizeSnapshot = std::vector +PreRATargetPseudoExpansionPass::run(MachineFunction &Function, const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) { + if (!Function.properties().containsAll(contract().Requires)) { + return makeUnexpected(ErrorCode::VerificationError, + "pre-RA pseudo expansion requires selected SSA-exited MIR"); + } + return runExpansionTransaction(Function, Target, *Info, MachinePseudoExpansionPhase::PreRA, + nullptr, Analyses); +} + const MachinePassContract &PreparePostRAPseudosPass::contract() const noexcept { return preparationContract(); } diff --git a/backend/lib/MIR/EmbeddedIR.cpp b/backend/lib/MIR/EmbeddedIR.cpp index 25aea05..c727539 100644 --- a/backend/lib/MIR/EmbeddedIR.cpp +++ b/backend/lib/MIR/EmbeddedIR.cpp @@ -117,6 +117,14 @@ EmbeddedIRReferenceIndex::globalObject(std::string_view Name) const noexcept { return &GlobalObjects[Position->second]; } +std::optional +EmbeddedIRReferenceIndex::globalObjectProperties(std::string_view Name) const noexcept { + const auto Position = GlobalProperties.find(Name); + if (Position == GlobalProperties.end()) + return std::nullopt; + return Position->second; +} + const EmbeddedIRMetadataDefinition * EmbeddedIRReferenceIndex::metadata(std::string_view Name) const noexcept { const auto Position = MetadataNameToIndex.find(Name); @@ -794,12 +802,31 @@ private: "duplicate embedded LLVM IR global object"); return; } + ++Position; + GlobalValueProperties Properties; + while (Position < End) { + while (Position < End && !isIdentifierStart(Line[Position])) + ++Position; + const std::size_t WordBegin = Position; + while (Position < End && isIdentifierBody(Line[Position])) + ++Position; + if (WordBegin == Position) + continue; + const std::string_view Word = Line.substr(WordBegin, Position - WordBegin); + if (Word == "dso_local" || Word == "internal" || Word == "private") + Properties.DSOLocal = true; + else if (Word == "extern_weak") + Properties.ExternalWeak = true; + else if (Word == "thread_local") + Properties.ThreadLocal = true; + } const std::size_t GlobalIndex = Index.GlobalObjects.size(); Index.GlobalNameToIndex.emplace(Name->Name, GlobalIndex); Index.GlobalObjects.push_back(EmbeddedIRReference{ .Name = std::move(Name->Name), .Range = Name->Range, }); + Index.GlobalProperties.emplace(Index.GlobalObjects.back().Name, Properties); } void parseMetadataDefinition(std::string_view Line, std::size_t LineOffset, std::size_t Position, 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 + 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 + 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(); + const std::optional 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(); + 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 { + auto SetOpcode = Instruction.setOpcode(instructionOpcode(OpcodeValue)); + if (!SetOpcode) + return std::unexpected(SetOpcode.error()); + return constrainSelectedInstruction(Instruction, Function, Target); + }; + const auto BuildGOTLoad = [&]() -> Expected { + 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 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 RISCVAsmBackend::applyFixup(mc::MCFixupKind Kind, std::span(Value) + 0x800U; const std::uint32_t High = static_cast(Adjusted >> 12U); @@ -244,6 +246,13 @@ Expected 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(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 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 @@ -408,6 +408,31 @@ instructions: size: 4 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 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(RISCVMCExprKind::PCRelativeHigh)) return fixupKind(RISCVFixupKind::PCRelativeHigh20); + if (*Kind == static_cast(RISCVMCExprKind::GOTPCRelativeHigh)) + return fixupKind(RISCVFixupKind::GOTPCRelativeHigh20); if (*Kind == static_cast(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 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 } [[nodiscard]] Expected addRegisterAllocationPipeline(MachineFunctionPassManager &Pipeline) { - auto Added = addPreRegisterAllocationOptimizationPipeline(Pipeline, optimizationPipelineInfo()); + auto Added = addPass(Pipeline, pseudoExpansionInfo()); + if (!Added) + return std::unexpected(Added.error()); + Added = addPreRegisterAllocationOptimizationPipeline(Pipeline, optimizationPipelineInfo()); if (!Added) return std::unexpected(Added.error()); return addPass(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 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 createGPRScratchRegister(MachineFunction &Function, + const TargetDescription &Target) { + const RegisterBankID GPRBank = + RegisterBankID::fromValue(static_cast(RegisterBank::GPR)); + const RegisterClassID GPRClass = + RegisterClassID::fromValue(static_cast(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 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(); + 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(); 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; diff --git a/backend/lib/Tooling/MIRFuzzer.cpp b/backend/lib/Tooling/MIRFuzzer.cpp index dc6b8d1..7f1cc66 100644 --- a/backend/lib/Tooling/MIRFuzzer.cpp +++ b/backend/lib/Tooling/MIRFuzzer.cpp @@ -298,7 +298,7 @@ private: auto Features = riscv::RISCVSubtargetFeatures::fromFeatureList(Invocation.Features); if (!Features) return std::unexpected(Features.error()); - const riscv::RISCVSubtarget Subtarget(*Features); + const riscv::RISCVSubtarget Subtarget(*Features, Invocation.Relocation, Invocation.Code); auto Names = riscv::canonicalPipelinePassNames(*Function, Subtarget); if (!Names) return std::unexpected(Names.error()); diff --git a/backend/lib/Tooling/PipelineDriver.cpp b/backend/lib/Tooling/PipelineDriver.cpp index 42c33e7..76951b6 100644 --- a/backend/lib/Tooling/PipelineDriver.cpp +++ b/backend/lib/Tooling/PipelineDriver.cpp @@ -240,7 +240,7 @@ runFunction(MachineFunction &Function, mir::MIRDocument &Document, const TargetD riscv::RISCVSubtargetFeatures Features, bool AllowSnapshots, MachinePassInstrumentation *Instrumentation) { FunctionRunResult Result; - const riscv::RISCVSubtarget Subtarget(Features); + const riscv::RISCVSubtarget Subtarget(Features, Invocation.Relocation, Invocation.Code); MachinePassManagerOptions ManagerOptions{ .VerifyBefore = true, .VerifyEach = Controls.VerifyEach, -- cgit v1.2.3