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

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.
This commit is contained in:
Daniel Thornburgh 2025-11-06 08:52:46 -08:00 committed by GitHub
parent 71927ddb63
commit 5f08fb4d72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 5779 additions and 5647 deletions

View File

@ -30968,6 +30968,37 @@ 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(metadata !<name_str>)
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.reloc.none`` intrinsic takes the symbol as a metadata string
argument.
Semantics:
""""""""""
This intrinsic emits a no-op relocation for the symbol at the location of the
intrinsic call.
Stack Map Intrinsics
--------------------

View File

@ -1537,6 +1537,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,

View File

@ -474,6 +474,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);

View File

@ -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_metadata_ty],
[IntrNoMem, IntrHasSideEffects]>;
//===---------------- Vector Predication Intrinsics --------------===//
// Memory Intrinsics
def int_vp_store : DefaultAttrsIntrinsic<[],

View File

@ -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)

View File

@ -1554,6 +1554,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 {

View File

@ -2087,6 +2087,17 @@ 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);
const MCExpr *Value = MCSymbolRefExpr::create(
OutContext.getOrCreateSymbol(MI.getOperand(0).getSymbolName()),
OutContext);
OutStreamer->emitRelocDirective(*Dot, "BFD_RELOC_NONE", Value, SMLoc());
break;
}
default:
emitInstruction(&MI);

View File

@ -2686,6 +2686,13 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
case Intrinsic::experimental_convergence_entry:
case Intrinsic::experimental_convergence_loop:
return translateConvergenceControlIntrinsic(CI, ID, MIRBuilder);
case Intrinsic::reloc_none: {
Metadata *MD = cast<MetadataAsValue>(CI.getArgOperand(0))->getMetadata();
StringRef SymbolName = cast<MDString>(MD)->getString();
MIRBuilder.buildInstr(TargetOpcode::RELOC_NONE)
.addExternalSymbol(SymbolName.data());
return true;
}
}
return false;
}

View File

@ -7811,6 +7811,17 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
return;
}
case Intrinsic::reloc_none: {
Metadata *MD = cast<MetadataAsValue>(I.getArgOperand(0))->getMetadata();
StringRef SymbolName = cast<MDString>(MD)->getString();
SDValue Ops[2] = {
getRoot(),
DAG.getTargetExternalSymbol(
SymbolName.data(), TLI.getProgramPointerTy(DAG.getDataLayout()))};
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.

View File

@ -472,6 +472,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";

View File

@ -2550,6 +2550,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
@ -3325,6 +3330,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;

View File

@ -6013,6 +6013,12 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
Check(cast<ConstantInt>(Call.getArgOperand(3))->getZExtValue() < 2,
"cache type argument to llvm.prefetch must be 0-1", Call);
break;
case Intrinsic::reloc_none: {
Check(isa<MDString>(
cast<MetadataAsValue>(Call.getArgOperand(0))->getMetadata()),
"llvm.reloc.none argument must be a metadata string", &Call);
break;
}
case Intrinsic::stackprotector:
Check(isa<AllocaInst>(Call.getArgOperand(1)->stripPointerCasts()),
"llvm.stackprotector parameter #2 must resolve to an alloca.", Call);

View File

@ -0,0 +1,10 @@
; RUN: llc < %s | FileCheck %s
; CHECK: .reloc {{.*}}, BFD_RELOC_NONE, foo
define void @test_reloc_none() {
call void @llvm.reloc.none(metadata !"foo")
ret void
}
declare void @llvm.reloc.none(metadata)

View File

@ -1531,6 +1531,7 @@ Key: RDSSPQ: [ 0.00 0.00 ]
Key: RDTSC: [ 0.00 0.00 ]
Key: RDTSCP: [ 0.00 0.00 ]
Key: REG_SEQUENCE: [ 0.00 0.00 ]
Key: RELOC_NONE: [ 0.00 0.00 ]
Key: REPNE_PREFIX: [ 0.00 0.00 ]
Key: REP_MOVSB: [ 0.00 0.00 ]
Key: REP_MOVSD: [ 0.00 0.00 ]

View File

@ -1531,6 +1531,7 @@ Key: RDSSPQ: [ 0.00 0.00 ]
Key: RDTSC: [ 0.00 0.00 ]
Key: RDTSCP: [ 0.00 0.00 ]
Key: REG_SEQUENCE: [ 0.00 0.00 ]
Key: RELOC_NONE: [ 0.00 0.00 ]
Key: REPNE_PREFIX: [ 0.00 0.00 ]
Key: REP_MOVSB: [ 0.00 0.00 ]
Key: REP_MOVSD: [ 0.00 0.00 ]

View File

@ -0,0 +1,14 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=x86_64-linux-gnu -global-isel -verify-machineinstrs < %s -o - | FileCheck %s --check-prefix=CHECK
define void @test_reloc_none() {
; CHECK-LABEL: test_reloc_none:
; CHECK: # %bb.0:
; CHECK-NEXT: .Lreloc_none0:
; CHECK-NEXT: .reloc .Lreloc_none0, BFD_RELOC_NONE, foo
; CHECK-NEXT: retq
call void @llvm.reloc.none(metadata !"foo")
ret void
}
declare void @llvm.reloc.none(metadata)

View File

@ -96,7 +96,7 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
// CHECK: const uint8_t *GenMyCombiner::getMatchTable() const {
// CHECK-NEXT: constexpr static uint8_t MatchTable0[] = {
// CHECK-NEXT: /* 0 */ GIM_SwitchOpcode, /*MI*/0, /*[*/GIMT_Encode2(99), GIMT_Encode2(211), /*)*//*default:*//*Label 5*/ GIMT_Encode4(524),
// CHECK-NEXT: /* 0 */ GIM_SwitchOpcode, /*MI*/0, /*[*/GIMT_Encode2(100), GIMT_Encode2(212), /*)*//*default:*//*Label 5*/ GIMT_Encode4(524),
// CHECK-NEXT: /* 10 */ /*TargetOpcode::G_STORE*//*Label 0*/ GIMT_Encode4(458), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0),
// CHECK-NEXT: /* 182 */ /*TargetOpcode::G_SEXT*//*Label 1*/ GIMT_Encode4(476), GIMT_Encode4(0),
// CHECK-NEXT: /* 190 */ /*TargetOpcode::G_ZEXT*//*Label 2*/ GIMT_Encode4(488), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0),

View File

@ -95,7 +95,8 @@ def InstD : InstBase {
// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 0,
// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2,
// CHECK-NEXT: 0,
// CHECK-NEXT: };
// CHECK-NEXT: return InstructionIndex[Opcode];
// CHECK-NEXT: }

View File

@ -0,0 +1,13 @@
; RUN: not llvm-as -disable-output 2>&1 %s | FileCheck %s
; CHECK: llvm.reloc.none argument must be a metadata string
; CHECK-NEXT: call void @llvm.reloc.none(metadata !0)
define void @test_reloc_none_bad_arg() {
call void @llvm.reloc.none(metadata !0)
ret void
}
declare void @llvm.reloc.none(metadata)
!0 = !{}

View File

@ -1,33 +1,33 @@
MAX_RELATION=4
187 7051 1
187 6948 2
187 187 0
187 7051 1
187 7052 1
187 6949 2
187 187 0
187 7052 1
187 6950 2
187 10 0
10 7051 1
10 7051 2
10 7051 3
10 6941 4
10 7052 1
10 7052 2
10 7052 3
10 6942 4
10 187 0
187 6932 1
187 7051 2
187 1543 0
1543 6862 1
1543 6932 2
187 7051 1
187 6948 2
187 187 0
187 7051 1
187 6933 1
187 7052 2
187 1544 0
1544 6863 1
1544 6933 2
187 7052 1
187 6949 2
187 187 0
187 7052 1
187 6950 2
187 601 0
601 7051 1
601 7051 2
601 7051 3
601 6941 4
601 7052 1
601 7052 2
601 7052 3
601 6942 4
601 187 0
187 6932 1
187 7051 2
187 1543 0
1543 6862 1
1543 6932 2
187 6933 1
187 7052 2
187 1544 0
1544 6863 1
1544 6933 2

File diff suppressed because it is too large Load Diff