Skip to content

Commit e7eb43f

Browse files
committed
[X86][ELF][LLD] Add R_X86_64_PCNEXT32 relocation and MO_PCNEXT32 flag for code prefetching
This adds support for the proposed R_X86_64_PCNEXT32 relocation and the @PCNEXT32 (MO_PCNEXT32) target operand flag in LLVM and LLD, including MC and LLD test coverage. x86-64 psABI proposal discussion: https://groups.google.com/g/x86-64-abi/c/v_alXxMCTgI ## Relocation Behavior & Calculation | Symbol State | Calculation | Notes | | ------------ | ----------- | ----- | | Defined | S + A - P | Identical to R_X86_64_PC32 | | Undefined | 0 | No diagnostic error reported | A 0 PC-relative displacement targets the instruction immediately following the relocated field. Consequently, an undefined reference degrades cleanly to a harmless, position-independent reference to the next instruction (a valid instruction boundary). ## Motivation LLVM's Code-prefetch insertion (#166324) inserts prefetchit1 target(%rip) ahead of an anticipated instruction cache or front-end miss: prefetchit1 __llvm_prefetch_target_<fn>_<bbid>_<cs>(%rip) The target is an address inside a callee (often in another translation unit), so the compiler synthesizes a target symbol at that address and uses a PC-relative reference resolved by the linker. However, the referenced target symbol may be undefined in the final build if the profile's <function, bb_id, callsite_index> triple fails to match the final image (due to function renaming, block structure changes, or altered callsite counts), or if the target is simply not linked in. Previously, avoiding an undefined-symbol error required emitting a weak fallback definition immediately after the prefetch: prefetchit1 __llvm_prefetch_target_foo_x_y(%rip) .weak __llvm_prefetch_target_foo_x_y __llvm_prefetch_target_foo_x_y: While this allows standard ELF resolution without linker modifications (a real STB_GLOBAL overrides the weak fallback, and an absent target degrades to the fallback immediately following the instruction), it prevents the prefetch target itself from being a weak definition—the linker may mistakenly resolve against an undesired weak fallback rather than the desired weak target in another translation unit. With R_X86_64_PCNEXT32, undefined targets resolve directly to displacement 0 (pointing to the next instruction without linker errors), removing the need for fallback definitions and allowing weak target definitions to function correctly.
1 parent 9ec7974 commit e7eb43f

15 files changed

Lines changed: 89 additions & 3 deletions

File tree

lld/ELF/Arch/X86_64.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,8 @@ RelExpr X86_64::getRelExpr(RelType type, const Symbol &s,
576576
case R_X86_64_PC32:
577577
case R_X86_64_PC64:
578578
return R_PC;
579+
case R_X86_64_PCNEXT32:
580+
return RE_X86_64_PCNEXT32;
579581
case R_X86_64_GOTOFF64:
580582
return R_GOTPLTREL;
581583
case R_X86_64_GOTPC32:
@@ -654,7 +656,7 @@ void X86_64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
654656
Symbol &sym = sec.getFile<ELFT>()->getSymbol(symIdx);
655657
uint64_t offset = rel.r_offset;
656658
RelType type = rel.getType(false);
657-
if (sym.isUndefined() && symIdx != 0 &&
659+
if (sym.isUndefined() && symIdx != 0 && type != R_X86_64_PCNEXT32 &&
658660
rs.maybeReportUndefined(cast<Undefined>(sym), offset))
659661
continue;
660662
int64_t addend = rs.getAddend<ELFT>(rel, type);
@@ -682,6 +684,9 @@ void X86_64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
682684
case R_X86_64_PC64:
683685
rs.processR_PC(type, offset, addend, sym);
684686
continue;
687+
case R_X86_64_PCNEXT32:
688+
rs.process(RE_X86_64_PCNEXT32, type, offset, sym, addend);
689+
continue;
685690

686691
// GOT-generating relocations:
687692
case R_X86_64_GOTPC32:
@@ -1182,6 +1187,7 @@ void X86_64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
11821187
case R_X86_64_GOTPC32:
11831188
case R_X86_64_GOTPCREL:
11841189
case R_X86_64_PC32:
1190+
case R_X86_64_PCNEXT32:
11851191
case R_X86_64_PLT32:
11861192
case R_X86_64_DTPOFF32:
11871193
case R_X86_64_SIZE32:

lld/ELF/InputSection.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,10 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
921921
}
922922
case RE_LOONGARCH_PAGE_PC:
923923
return getLoongArchPageDelta(r.sym->getVA(ctx, a), p, r.type);
924+
case RE_X86_64_PCNEXT32:
925+
if (r.sym->isUndefined() || r.sym->isShared())
926+
return 0;
927+
return r.sym->getVA(ctx, a) - p;
924928
case R_PC:
925929
case RE_ARM_PCA: {
926930
uint64_t dest;

lld/ELF/Relocations.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,8 @@ bool RelocScan::isStaticLinkTimeConstant(RelExpr e, RelType type,
846846
R_GOTPLTONLY_PC, R_PLT_PC, R_PLT_GOTREL, R_PLT_GOTPLT,
847847
R_GOTPLT_GOTREL, R_GOTPLT_PC, RE_PPC32_PLTREL, RE_PPC64_CALL_PLT,
848848
RE_RISCV_ADD, RE_AARCH64_GOT_PAGE, RE_LOONGARCH_PLT_PAGE_PC,
849-
RE_LOONGARCH_GOT, RE_LOONGARCH_GOT_PAGE_PC>(e))
849+
RE_LOONGARCH_GOT, RE_LOONGARCH_GOT_PAGE_PC,
850+
RE_X86_64_PCNEXT32>(e))
850851
return true;
851852

852853
// These never do, except if the entire file is position dependent or if

lld/ELF/Relocations.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ enum RelExpr {
126126
RE_LOONGARCH_PC_INDIRECT,
127127
RE_LOONGARCH_TLSGD_PAGE_PC,
128128
RE_LOONGARCH_TLSDESC_PAGE_PC,
129+
RE_X86_64_PCNEXT32,
129130
};
130131

131132
// Architecture-neutral representation of relocation.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# REQUIRES: x86
2+
# RUN: rm -rf %t && split-file %s %t
3+
4+
# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/so.s -o %t/so.o
5+
# RUN: ld.lld -shared %t/so.o -o %t/libso.so
6+
7+
# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/main.s -o %t/main.o
8+
# RUN: ld.lld --image-base=0x1000 --section-start=.text=0x1000 %t/main.o %t/libso.so -o %t/out
9+
# RUN: llvm-readelf -x .text %t/out | FileCheck %s
10+
11+
# CHECK: Hex dump of section '.text':
12+
# CHECK-NEXT: 0x00001000 0c000000 00000000 00000000 c3
13+
14+
#--- so.s
15+
.global shared_sym
16+
shared_sym:
17+
ret
18+
19+
#--- main.s
20+
.globl _start
21+
_start:
22+
# R_X86_64_PCNEXT32 to defined local symbol at 0x100c (place is 0x1000, 0x100c - 0x1000 = 0x0c)
23+
.reloc ., R_X86_64_PCNEXT32, local_sym
24+
.byte 0x00, 0x00, 0x00, 0x00
25+
26+
# R_X86_64_PCNEXT32 to shared symbol (resolves to 0)
27+
.reloc ., R_X86_64_PCNEXT32, shared_sym
28+
.byte 0x00, 0x00, 0x00, 0x00
29+
30+
# R_X86_64_PCNEXT32 to undefined weak symbol (resolves to 0)
31+
.reloc ., R_X86_64_PCNEXT32, undef_sym
32+
.byte 0x00, 0x00, 0x00, 0x00
33+
34+
local_sym:
35+
ret
36+
37+
.weak undef_sym

llvm/docs/Extensions.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,17 @@ is also not the same as `cmpb $foo, %dil`, which is an 8-bit comparison.
764764
the assembler emits an `R_X86_64_GOTPCREL` relocation instead of a relaxable
765765
`R_X86_64[_REX]_GOTPCRELX` relocation.
766766

767+
768+
**@PCNEXT32** can be used on symbol references to indicate that the immediate
769+
operand is an offset to the target symbol from the next code location, causing
770+
the assembler to emit an `R_X86_64_PCNEXT32` relocation. If the target symbol
771+
is defined, the relocation resolves identically to `R_X86_64_PC32` (`S + A - P`).
772+
If the target symbol is undefined or in a shared library, it resolves to zero
773+
displacement (`0`) without generating a linker diagnostic. This allows
774+
undefined code prefetch targets (e.g. `prefetchit1 target@PCNEXT32(%rip)`) to
775+
gracefully degrade to a harmless prefetch of the immediately following
776+
instruction.
777+
767778
### Windows on ARM
768779

769780
#### Stack Probe Emission

llvm/include/llvm/BinaryFormat/ELFRelocs/x86_64.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,5 @@ ELF_RELOC(R_X86_64_REX_GOTPCRELX, 42)
4646
ELF_RELOC(R_X86_64_CODE_4_GOTPCRELX, 43)
4747
ELF_RELOC(R_X86_64_CODE_4_GOTTPOFF, 44)
4848
ELF_RELOC(R_X86_64_CODE_4_GOTPC32_TLSDESC, 45)
49+
ELF_RELOC(R_X86_64_PCNEXT32, 46)
4950
ELF_RELOC(R_X86_64_CODE_6_GOTTPOFF, 50)

llvm/lib/Target/X86/MCTargetDesc/X86BaseInfo.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,11 @@ enum TOF {
394394
/// See the X86-64 ELF ABI supplement for more details.
395395
/// SYMBOL_LABEL @PLT
396396
MO_PLT,
397+
/// MO_PCNEXT32 - On a symbol operand this indicates that the immediate is
398+
/// offset to the prefetch target symbol name from the next code location.
399+
/// If the target symbol is undefined, it will resolve to zero displacement.
400+
/// SYMBOL_LABEL @PCNEXT32
401+
MO_PCNEXT32,
397402
/// MO_TLSGD - On a symbol operand this indicates that the immediate is
398403
/// the offset of the GOT entry with the TLS index structure that contains
399404
/// the module number and variable offset for the symbol. Used in the

llvm/lib/Target/X86/MCTargetDesc/X86ELFObjectWriter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ unsigned X86ELFObjectWriter::getRelocType64(SMLoc Loc, X86::Specifier Specifier,
212212
case X86::S_PLT:
213213
checkIs32(Loc, Type);
214214
return ELF::R_X86_64_PLT32;
215+
case X86::S_PCNEXT32:
216+
checkIs32(Loc, Type);
217+
return ELF::R_X86_64_PCNEXT32;
215218
case X86::S_GOTPCREL:
216219
checkIs32(Loc, Type);
217220
// Older versions of ld.bfd/ld.gold/lld

llvm/lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const MCAsmInfo::AtSpecifier atSpecifiers[] = {
4949
{X86::S_INDNTPOFF, "INDNTPOFF"},
5050
{MCSymbolRefExpr::VK_COFF_IMGREL32, "IMGREL"},
5151
{X86::S_NTPOFF, "NTPOFF"},
52+
{X86::S_PCNEXT32, "PCNEXT32"},
5253
{X86::S_PCREL, "PCREL"},
5354
{X86::S_PLT, "PLT"},
5455
{X86::S_PLTOFF, "PLTOFF"},

0 commit comments

Comments
 (0)