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
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30483,6 +30483,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.reloc.none`` intrinsic takes the symbol as a metadata string
argument.

Semantics:
""""""""""

This intrinsic emits a no-op relocation for the symbol the location of the
intrinsic call.


Stack Map Intrinsics
--------------------
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/CodeGen/ISDOpcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/CodeGen/SelectionDAGISel.h
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,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);
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/IR/Intrinsics.td
Original file line number Diff line number Diff line change
Expand Up @@ -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_metadata_ty],
[IntrHasSideEffects, IntrInaccessibleMemOnly, IntrWillReturn]>;

//===---------------- Vector Predication Intrinsics --------------===//
// Memory Intrinsics
def int_vp_store : DefaultAttrsIntrinsic<[],
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/Support/TargetOpcodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/Target/Target.td
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
10 changes: 10 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1938,6 +1938,16 @@ 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(
getSymbol(MI.getOperand(0).getGlobal()), OutContext);
OutStreamer->emitRelocDirective(*Dot, "BFD_RELOC_NONE", Value, SMLoc());
break;
}
default:
emitInstruction(&MI);

Expand Down
10 changes: 10 additions & 0 deletions llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2645,6 +2645,16 @@ 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();
auto *M = const_cast<Module *>(CI.getModule());
auto *RelocSymbol = cast<GlobalVariable>(
M->getOrInsertGlobal(SymbolName, StructType::create(M->getContext())));
MIRBuilder.buildInstr(TargetOpcode::RELOC_NONE)
.addGlobalAddress(RelocSymbol);
return true;
}
}
return false;
}
Expand Down
14 changes: 14 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7758,6 +7758,20 @@ 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();
auto *M = const_cast<Module *>(I.getModule());
auto *RelocSymbol = cast<GlobalVariable>(
M->getOrInsertGlobal(SymbolName, StructType::create(M->getContext())));
Comment on lines +7764 to +7766
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.

SDValue Ops[2];
Ops[0] = getRoot();
Ops[1] = DAG.getTargetGlobalAddress(
RelocSymbol, sdl, TLI.getPointerTy(DAG.getDataLayout()), 0);
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.
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
8 changes: 8 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2519,6 +2519,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
Expand Down Expand Up @@ -3294,6 +3299,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;
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5894,6 +5894,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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@
# DEBUG-NEXT: .. the first uncovered type index: 1, OK
# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
#
# DEBUG-NEXT: G_ABDS (opcode 65): 1 type index, 0 imm indices
# DEBUG-NEXT: G_ABDS (opcode 66): 1 type index, 0 imm indices
# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
#
# DEBUG-NEXT: G_ABDU (opcode 66): 1 type index, 0 imm indices
# DEBUG-NEXT: G_ABDU (opcode 67): 1 type index, 0 imm indices
# DEBUG-NEXT: .. opcode {{[0-9]+}} is aliased to {{[0-9]+}}
# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
Expand Down
10 changes: 10 additions & 0 deletions llvm/test/CodeGen/Generic/reloc-none.ll
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@
# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
#
# DEBUG-NEXT: G_ABDS (opcode 65): 1 type index, 0 imm indices
# DEBUG-NEXT: G_ABDS (opcode 66): 1 type index, 0 imm indices
# DEBUG-NEXT:.. type index coverage check SKIPPED: no rules defined
# DEBUG-NEXT:.. imm index coverage check SKIPPED: no rules defined
#
# DEBUG-NEXT:G_ABDU (opcode 66): 1 type index, 0 imm indices
# DEBUG-NEXT:G_ABDU (opcode 67): 1 type index, 0 imm indices
# DEBUG-NEXT:.. type index coverage check SKIPPED: no rules defined
# DEBUG-NEXT:.. imm index coverage check SKIPPED: no rules defined
#
Expand Down
14 changes: 14 additions & 0 deletions llvm/test/CodeGen/X86/GlobalISel/reloc-none.ll
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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(210), /*)*//*default:*//*Label 5*/ GIMT_Encode4(520),
// CHECK-NEXT: /* 0 */ GIM_SwitchOpcode, /*MI*/0, /*[*/GIMT_Encode2(100), GIMT_Encode2(211), /*)*//*default:*//*Label 5*/ GIMT_Encode4(520),
// CHECK-NEXT: /* 10 */ /*TargetOpcode::G_STORE*//*Label 0*/ GIMT_Encode4(454), 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(472), GIMT_Encode4(0),
// CHECK-NEXT: /* 190 */ /*TargetOpcode::G_ZEXT*//*Label 2*/ GIMT_Encode4(484), 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),
Expand Down
13 changes: 13 additions & 0 deletions llvm/test/Verifier/reloc_none.ll
Original file line number Diff line number Diff line change
@@ -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 = !{}