[llvm] [X86] Use a large-model call for __tls_get_addr (PR #216195)
Farid Zakaria via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 14 10:52:12 PDT 2026
https://github.com/fzakaria updated https://github.com/llvm/llvm-project/pull/216195
>From 870f6eed0149eb0a15c662b4829d8b996415cb36 Mon Sep 17 00:00:00 2001
From: Farid Zakaria <fmzakari at fb.com>
Date: Fri, 14 Aug 2026 10:50:55 -0700
Subject: [PATCH] [X86] Use a large-model call for __tls_get_addr
The x86-64 ELF TLSGD and TLSLD sequences currently use a PLT32 call
to __tls_get_addr for every code model. The call can overflow in the
large code model when the PLT is more than 2 GiB away.
Use the same relocation strategy as GCC for large-model PIC: materialize
the GOT base with GOTPC64, materialize __tls_get_addr with PLTOFF64, and
call it indirectly. This changes only x86-64 LP64 ELF PIC code using
the large code model.
Assisted-by: Codex
---
llvm/lib/Target/X86/X86MCInstLower.cpp | 42 ++++++++++++++++++++-
llvm/test/CodeGen/X86/code-model-elf.ll | 23 ------------
llvm/test/CodeGen/X86/tls-large-model.ll | 47 ++++++++++++++++++++++++
3 files changed, 88 insertions(+), 24 deletions(-)
create mode 100644 llvm/test/CodeGen/X86/tls-large-model.ll
diff --git a/llvm/lib/Target/X86/X86MCInstLower.cpp b/llvm/lib/Target/X86/X86MCInstLower.cpp
index ad0946b4c3310..87031104294f7 100644
--- a/llvm/lib/Target/X86/X86MCInstLower.cpp
+++ b/llvm/lib/Target/X86/X86MCInstLower.cpp
@@ -595,6 +595,47 @@ void X86AsmPrinter::LowerTlsAddr(X86MCInstLower &MCInstLowering,
.addReg(0));
} else if (Is64Bits) {
bool NeedsPadding = Specifier == X86::S_TLSGD;
+ const MCSymbol *TlsGetAddr = Ctx.getOrCreateSymbol("__tls_get_addr");
+ if (Is64BitsLP64 && getSubtarget().isTargetELF() &&
+ TM.getCodeModel() == CodeModel::Large && TM.isPositionIndependent()) {
+ MCSymbol *PICBase = Ctx.createTempSymbol();
+ OutStreamer->emitLabel(PICBase);
+ EmitAndCountInstruction(
+ MCInstBuilder(X86::LEA64r)
+ .addReg(X86::R11)
+ .addReg(X86::RIP)
+ .addImm(1)
+ .addReg(0)
+ .addExpr(MCSymbolRefExpr::create(PICBase, Ctx))
+ .addReg(0));
+ const MCExpr *GOT = MCSymbolRefExpr::create(
+ Ctx.getOrCreateSymbol("_GLOBAL_OFFSET_TABLE_"), Ctx);
+ const MCExpr *GOTOffset = MCBinaryExpr::createSub(
+ GOT, MCSymbolRefExpr::create(PICBase, Ctx), Ctx);
+ EmitAndCountInstruction(
+ MCInstBuilder(X86::MOV64ri).addReg(X86::RAX).addExpr(GOTOffset));
+ EmitAndCountInstruction(MCInstBuilder(X86::ADD64rr)
+ .addReg(X86::R11)
+ .addReg(X86::R11)
+ .addReg(X86::RAX));
+ EmitAndCountInstruction(MCInstBuilder(X86::LEA64r)
+ .addReg(X86::RDI)
+ .addReg(X86::RIP)
+ .addImm(1)
+ .addReg(0)
+ .addExpr(Sym)
+ .addReg(0));
+ const MCExpr *Callee =
+ MCSymbolRefExpr::create(TlsGetAddr, X86::S_PLTOFF, Ctx);
+ EmitAndCountInstruction(
+ MCInstBuilder(X86::MOV64ri).addReg(X86::RAX).addExpr(Callee));
+ EmitAndCountInstruction(MCInstBuilder(X86::ADD64rr)
+ .addReg(X86::RAX)
+ .addReg(X86::RAX)
+ .addReg(X86::R11));
+ EmitAndCountInstruction(MCInstBuilder(X86::CALL64r).addReg(X86::RAX));
+ return;
+ }
if (NeedsPadding && Is64BitsLP64)
EmitAndCountInstruction(MCInstBuilder(X86::DATA16_PREFIX));
EmitAndCountInstruction(MCInstBuilder(X86::LEA64r)
@@ -604,7 +645,6 @@ void X86AsmPrinter::LowerTlsAddr(X86MCInstLower &MCInstLowering,
.addReg(0)
.addExpr(Sym)
.addReg(0));
- const MCSymbol *TlsGetAddr = Ctx.getOrCreateSymbol("__tls_get_addr");
if (NeedsPadding) {
if (!UseGot)
EmitAndCountInstruction(MCInstBuilder(X86::DATA16_PREFIX));
diff --git a/llvm/test/CodeGen/X86/code-model-elf.ll b/llvm/test/CodeGen/X86/code-model-elf.ll
index aa2cc3a4981a2..574fd7188bba7 100644
--- a/llvm/test/CodeGen/X86/code-model-elf.ll
+++ b/llvm/test/CodeGen/X86/code-model-elf.ll
@@ -1324,29 +1324,6 @@ define dso_local ptr @lea_dso_local_ifunc() #0 {
ret ptr @dso_local_ifunc_func
}
-; FIXME: The result is same for small, medium and large model, because we
-; specify pie option in the test case. And the type of tls is initial exec tls.
-; For pic code. The large model code for pic tls should be emitted as below.
-
-; .L3:
-; leaq .L3(%rip), %rbx
-; movabsq $_GLOBAL_OFFSET_TABLE_-.L3, %r11
-; addq %r11, %rbx
-; leaq thread_data at TLSGD(%rip), %rdi
-; movabsq $__tls_get_addr at PLTOFF, %rax
-; addq %rbx, %rax
-; call *%rax
-; movl (%rax), %eax
-
-; The medium and small model code for pic tls should be emitted as below.
-; data16
-; leaq thread_data at TLSGD(%rip), %rdi
-; data16
-; data16
-; rex64
-; callq __tls_get_addr at PLT
-; movl (%rax), %eax
-
define dso_local i32 @load_thread_data() #0 {
; CHECK-LABEL: load_thread_data:
; CHECK: # %bb.0:
diff --git a/llvm/test/CodeGen/X86/tls-large-model.ll b/llvm/test/CodeGen/X86/tls-large-model.ll
new file mode 100644
index 0000000000000..e32c395c6e6fa
--- /dev/null
+++ b/llvm/test/CodeGen/X86/tls-large-model.ll
@@ -0,0 +1,47 @@
+; RUN: llc < %s -mtriple=x86_64-linux-gnu -relocation-model=pic \
+; RUN: -code-model=large | FileCheck %s
+; RUN: llc < %s -mtriple=x86_64-linux-gnu -relocation-model=pic \
+; RUN: -code-model=large -filetype=obj -o - | llvm-readobj -r - | \
+; RUN: FileCheck %s --check-prefix=RELOC
+
+; RELOC-DAG: R_X86_64_GOTPC64 _GLOBAL_OFFSET_TABLE_
+; RELOC-DAG: R_X86_64_GOTPC64 _GLOBAL_OFFSET_TABLE_
+; RELOC-DAG: R_X86_64_TLSGD gd
+; RELOC-DAG: R_X86_64_PLTOFF64 __tls_get_addr
+; RELOC-DAG: R_X86_64_PLTOFF64 __tls_get_addr
+; RELOC-DAG: R_X86_64_TLSLD ld
+
+ at gd = external thread_local global i32
+ at ld = external thread_local(localdynamic) global i32
+
+define i32 @load_gd() {
+; CHECK-LABEL: load_gd:
+; CHECK: [[PB:\.L[^:]+]]:
+; CHECK-NEXT: leaq [[PB]](%rip), %r11
+; CHECK-NEXT: movabsq $_GLOBAL_OFFSET_TABLE_-[[PB]], %rax
+; CHECK-NEXT: addq %rax, %r11
+; CHECK-NEXT: leaq gd at TLSGD(%rip), %rdi
+; CHECK-NEXT: movabsq $__tls_get_addr at PLTOFF, %rax
+; CHECK-NEXT: addq %r11, %rax
+; CHECK-NEXT: callq *%rax
+; CHECK-NEXT: movl (%rax), %eax
+; CHECK: retq
+ %v = load i32, ptr @gd
+ ret i32 %v
+}
+
+define i32 @load_ld() {
+; CHECK-LABEL: load_ld:
+; CHECK: [[PB:\.L[^:]+]]:
+; CHECK-NEXT: leaq [[PB]](%rip), %r11
+; CHECK-NEXT: movabsq $_GLOBAL_OFFSET_TABLE_-[[PB]], %rax
+; CHECK-NEXT: addq %rax, %r11
+; CHECK-NEXT: leaq ld at TLSLD(%rip), %rdi
+; CHECK-NEXT: movabsq $__tls_get_addr at PLTOFF, %rax
+; CHECK-NEXT: addq %r11, %rax
+; CHECK-NEXT: callq *%rax
+; CHECK-NEXT: movl (%rbx,%rax), %eax
+; CHECK: retq
+ %v = load i32, ptr @ld
+ ret i32 %v
+}
More information about the llvm-commits
mailing list