From f3fdeff3e33e4f34d5fad47517db03bb6616cd69 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh Date: Mon, 7 Jul 2025 16:19:59 -0700 Subject: [PATCH] [IR] llvm.reloc.none intrinsic for no-op symbol references This intrinsic emits a BFD_RELOC_NONE relocation at the point of call, which allows optimizations and languages to explicitly pull in symbols from static libraries without there being any code or data that has an effectual relocation against such a symbol. See issue #146159 for context. --- llvm/docs/LangRef.rst | 32 +++++++++++++++++++ llvm/include/llvm/CodeGen/ISDOpcodes.h | 3 ++ llvm/include/llvm/CodeGen/SelectionDAGISel.h | 1 + llvm/include/llvm/IR/Intrinsics.td | 3 ++ llvm/include/llvm/Support/TargetOpcodes.def | 3 ++ llvm/include/llvm/Target/Target.td | 5 +++ llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 14 ++++++++ .../SelectionDAG/SelectionDAGBuilder.cpp | 13 ++++++++ .../SelectionDAG/SelectionDAGDumper.cpp | 2 ++ .../CodeGen/SelectionDAG/SelectionDAGISel.cpp | 8 +++++ 10 files changed, 84 insertions(+) diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index a71eefd1eb68..59d2b351a2b7 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -30630,6 +30630,38 @@ This intrinsic does nothing, but optimizers must consider it a use of its single operand and should try to preserve the intrinsic and its position in the function. +.. _llvm_reloc_none: + +'``llvm.reloc.none``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare void @llvm.reloc.none(ptrty %ptr) + +Overview: +""""""""" + +The ``llvm.reloc.none`` intrinsic emits a no-op relocation against a given +operand symbol. This can bring the symbol +definition into the link without emitting any code or data to the binary for +that purpose. + +Arguments: +"""""""""" + +The ``llvm.fake.use`` intrinsic takes one argument, which may be any global +value. + +Semantics: +"""""""""" + +This intrinsic emits a no-op relocation at the location of the intrinsic call +for the symbol that corresponds to the global value argument. + Stack Map Intrinsics -------------------- diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h index 465e4a0a9d0d..9c36f9c4fe52 100644 --- a/llvm/include/llvm/CodeGen/ISDOpcodes.h +++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h @@ -1531,6 +1531,9 @@ enum NodeType { #define BEGIN_REGISTER_VP_SDNODE(VPSDID, ...) VPSDID, #include "llvm/IR/VPIntrinsics.def" + // Issue a no-op relocation against a given symbol at the current location. + RELOC_NONE, + // The `llvm.experimental.convergence.*` intrinsics. CONVERGENCECTRL_ANCHOR, CONVERGENCECTRL_ENTRY, diff --git a/llvm/include/llvm/CodeGen/SelectionDAGISel.h b/llvm/include/llvm/CodeGen/SelectionDAGISel.h index 5241a51dd8cd..c29a902be1be 100644 --- a/llvm/include/llvm/CodeGen/SelectionDAGISel.h +++ b/llvm/include/llvm/CodeGen/SelectionDAGISel.h @@ -473,6 +473,7 @@ private: void Select_WRITE_REGISTER(SDNode *Op); void Select_UNDEF(SDNode *N); void Select_FAKE_USE(SDNode *N); + void Select_RELOC_NONE(SDNode *N); void CannotYetSelect(SDNode *N); void Select_FREEZE(SDNode *N); diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index e0ee12391b31..88b1918c022c 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -1913,6 +1913,9 @@ def int_threadlocal_address : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [LLVMMatch def int_stepvector : DefaultAttrsIntrinsic<[llvm_anyvector_ty], [], [IntrNoMem]>; +def int_reloc_none : DefaultAttrsIntrinsic<[], [llvm_ptr_ty], + [IntrHasSideEffects, IntrInaccessibleMemOnly, IntrWillReturn]>; + //===---------------- Vector Predication Intrinsics --------------===// // Memory Intrinsics def int_vp_store : DefaultAttrsIntrinsic<[], diff --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def index b905576b6179..aca7e6232df6 100644 --- a/llvm/include/llvm/Support/TargetOpcodes.def +++ b/llvm/include/llvm/Support/TargetOpcodes.def @@ -233,6 +233,9 @@ HANDLE_TARGET_OPCODE(MEMBARRIER) // using. HANDLE_TARGET_OPCODE(JUMP_TABLE_DEBUG_INFO) +// Issue a no-op relocation against a given symbol at the current location. +HANDLE_TARGET_OPCODE(RELOC_NONE) + HANDLE_TARGET_OPCODE(CONVERGENCECTRL_ENTRY) HANDLE_TARGET_OPCODE(CONVERGENCECTRL_ANCHOR) HANDLE_TARGET_OPCODE(CONVERGENCECTRL_LOOP) diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td index 495b59ee916c..bdd5d6a2a477 100644 --- a/llvm/include/llvm/Target/Target.td +++ b/llvm/include/llvm/Target/Target.td @@ -1532,6 +1532,11 @@ def JUMP_TABLE_DEBUG_INFO : StandardPseudoInstruction { let Size = 0; let isMeta = true; } +def RELOC_NONE : StandardPseudoInstruction { + let OutOperandList = (outs); + let InOperandList = (ins unknown:$symbol); + let hasSideEffects = true; +} let hasSideEffects = false, isMeta = true, isConvergent = true in { def CONVERGENCECTRL_ANCHOR : StandardPseudoInstruction { diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 23a3543e9ebe..79b98388c691 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -2039,6 +2039,20 @@ void AsmPrinter::emitFunctionBody() { // This is only used to influence register allocation behavior, no // actual initialization is needed. break; + case TargetOpcode::RELOC_NONE: { + // Generate a temporary label for the current PC. + MCSymbol *Sym = OutContext.createTempSymbol("reloc_none"); + OutStreamer->emitLabel(Sym); + const MCExpr *Dot = MCSymbolRefExpr::create(Sym, OutContext); + + assert(MI.getNumOperands() == 1 && + "RELOC_NONE can only have one operand"); + const MCExpr *Value = MCSymbolRefExpr::create( + getSymbol(MI.getOperand(0).getGlobal()), OutContext); + OutStreamer->emitRelocDirective(*Dot, "BFD_RELOC_NONE", Value, SMLoc(), + *STI); + break; + } default: emitInstruction(&MI); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 901f10d1256d..72a1e95b50d7 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -7751,6 +7751,19 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, return; } + case Intrinsic::reloc_none: { + SDValue V = getValue(I.getArgOperand(0)); + auto *GA = dyn_cast(V); + if (!GA) + report_fatal_error("llvm.reloc.none operand must be a GlobalValue"); + SDValue Ops[2]; + Ops[0] = getRoot(); + Ops[1] = DAG.getTargetGlobalAddress(GA->getGlobal(), sdl, V.getValueType(), + GA->getOffset()); + DAG.setRoot(DAG.getNode(ISD::RELOC_NONE, sdl, MVT::Other, Ops)); + return; + } + case Intrinsic::eh_exceptionpointer: case Intrinsic::eh_exceptioncode: { // Get the exception pointer vreg, copy from it, and resize it to fit. diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp index 900da7645504..a79d46e89004 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp @@ -471,6 +471,8 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const { case ISD::LIFETIME_END: return "lifetime.end"; case ISD::FAKE_USE: return "fake_use"; + case ISD::RELOC_NONE: + return "reloc_none"; case ISD::PSEUDO_PROBE: return "pseudoprobe"; case ISD::GC_TRANSITION_START: return "gc_transition.start"; diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp index ece50ed95fc4..f92961aa82cc 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -2521,6 +2521,11 @@ void SelectionDAGISel::Select_FAKE_USE(SDNode *N) { N->getOperand(1), N->getOperand(0)); } +void SelectionDAGISel::Select_RELOC_NONE(SDNode *N) { + CurDAG->SelectNodeTo(N, TargetOpcode::RELOC_NONE, N->getValueType(0), + N->getOperand(1), N->getOperand(0)); +} + void SelectionDAGISel::Select_FREEZE(SDNode *N) { // TODO: We don't have FREEZE pseudo-instruction in MachineInstr-level now. // If FREEZE instruction is added later, the code below must be changed as @@ -3296,6 +3301,9 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch, case ISD::FAKE_USE: Select_FAKE_USE(NodeToMatch); return; + case ISD::RELOC_NONE: + Select_RELOC_NONE(NodeToMatch); + return; case ISD::FREEZE: Select_FREEZE(NodeToMatch); return;