Skip to content

[IR] llvm.reloc.none intrinsic for no-op symbol references #147427

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

mysterymath
Copy link
Contributor

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.

@mysterymath mysterymath requested a review from nikic July 7, 2025 23:54
@llvmbot
Copy link
Member

llvmbot commented Jul 7, 2025

@llvm/pr-subscribers-llvm-globalisel
@llvm/pr-subscribers-llvm-selectiondag
@llvm/pr-subscribers-llvm-support
@llvm/pr-subscribers-backend-x86

@llvm/pr-subscribers-llvm-ir

Author: Daniel Thornburgh (mysterymath)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/147427.diff

10 Files Affected:

  • (modified) llvm/docs/LangRef.rst (+32)
  • (modified) llvm/include/llvm/CodeGen/ISDOpcodes.h (+3)
  • (modified) llvm/include/llvm/CodeGen/SelectionDAGISel.h (+1)
  • (modified) llvm/include/llvm/IR/Intrinsics.td (+3)
  • (modified) llvm/include/llvm/Support/TargetOpcodes.def (+3)
  • (modified) llvm/include/llvm/Target/Target.td (+5)
  • (modified) llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp (+14)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp (+13)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp (+2)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp (+8)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index cc72a37f68599..187182e5357e7 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -30439,6 +30439,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 465e4a0a9d0d8..9c36f9c4fe525 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 375d2f31e2835..3a399c39c496f 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAGISel.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAGISel.h
@@ -473,6 +473,7 @@ class SelectionDAGISel {
   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 7add4a27ce9e9..131bc56c32362 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1917,6 +1917,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 92fd60e03112a..5fd0b2a06188e 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 ce9a2b2751968..fca69107c8b55 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 a2c3b50b24670..e6206e6cbb6ae 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -1930,6 +1930,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 c01f1e7928477..bcfbca582d699 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -7757,6 +7757,19 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
     return;
   }
 
+  case Intrinsic::reloc_none: {
+    SDValue V = getValue(I.getArgOperand(0));
+    auto *GA = dyn_cast<GlobalAddressSDNode>(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 7fc15581c17e4..5296913e718c2 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 4b98d87fcc63b..ccc583c6a2b8d 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
@@ -2498,6 +2498,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
@@ -3273,6 +3278,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;

@mysterymath
Copy link
Contributor Author

mysterymath commented Jul 7, 2025

Sending this out as a draft to obtain some early feedback about the direction of the implemenation of the Modular Printf RFC.

Next PR in chain: #147429

@nikic nikic requested a review from MaskRay July 9, 2025 11:03
Comment on lines +7763 to +7766
auto *M = const_cast<Module *>(I.getModule());
auto *RelocSymbol = cast<GlobalVariable>(
M->getOrInsertGlobal(SymbolName, StructType::create(M->getContext())));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You cannot modify the IR here

Copy link
Contributor Author

@mysterymath mysterymath Jul 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because this occurs in a MachineFunctionPass right? In that case, I got bamboozled! I had unceremoniously copy-pasted this from the lowering of Intrinsic::amdgcn_reloc_constant in SIISelLowering.cpp, which also runs in a MachineFunctionPass. I'll admit that this completely slipped past me, although I was curious about the const_cast.

AFAIK, converting an arbitrary symbol name metadata string to a GlobalValue reference instrinsically mutates the module. So, if we wanted to fix this, we could either go back to having this be a symbol reference to a pre-existing global value, or we could forward the metadata string to RELOC_NONE as a string (odd in the backend; unsure if this is even possible?) to be lowered to a symbol reference at assembly time. My preference would definitely be the former, since it has fewer unknowns.

Whichever we choose, also I'd think either way we should give amdgcn_reloc_constant the same treatment, unless it's not possible to do so for backwards compatibility reasons.

@arsenm Do you have preferences here?

EDIT: There is Metadata and MCSymbol support in MachineOperand, but it's tagged in the enum as "for debug info". We could probably use this for RELOC_NONE's argument, but maybe that would cause problems? (Or be "weird"?) Unsure.

@mysterymath mysterymath marked this pull request as draft July 29, 2025 21:48
@mysterymath mysterymath force-pushed the users/mysterymath/modular-printf/reloc.none branch from c89242c to 657f659 Compare July 29, 2025 22:11
@mysterymath mysterymath marked this pull request as ready for review July 29, 2025 22:11
@mysterymath
Copy link
Contributor Author

Alright, I've filled out tests and added lowering to GlobalISel also. This should be formally ready for review (although much has already occurred).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants