[X86] Support -fno-plt __tls_get_addr calls

In general dynamic/local dynamic TLS models, with -fno-plt,

* x86: emit `calll *___tls_get_addr@GOT(%ebx)` instead of `calll ___tls_get_addr@PLT`
  Note, on x86, if we can get rid of %ebx as the PIC register,
  it may be better to use a register not preserved across function calls.
* x86_64: emit `callq *__tls_get_addr@GOTPCREL(%rip)` instead of `callq __tls_get_addr@PLT`

Reorganize the code by separating 32-bit and 64-bit.

Reviewed By: rnk

Differential Revision: https://reviews.llvm.org/D62106

llvm-svn: 361453
This commit is contained in:
Fangrui Song 2019-05-23 01:05:13 +00:00
parent eac9a7830b
commit 86c9ca48c3
2 changed files with 102 additions and 51 deletions

View File

@ -683,16 +683,9 @@ void X86MCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) const {
void X86AsmPrinter::LowerTlsAddr(X86MCInstLower &MCInstLowering, void X86AsmPrinter::LowerTlsAddr(X86MCInstLower &MCInstLowering,
const MachineInstr &MI) { const MachineInstr &MI) {
bool Is64Bits = MI.getOpcode() == X86::TLS_addr64 ||
bool is64Bits = MI.getOpcode() == X86::TLS_addr64 ||
MI.getOpcode() == X86::TLS_base_addr64; MI.getOpcode() == X86::TLS_base_addr64;
MCContext &Ctx = OutStreamer->getContext();
bool needsPadding = MI.getOpcode() == X86::TLS_addr64;
MCContext &context = OutStreamer->getContext();
if (needsPadding)
EmitAndCountInstruction(MCInstBuilder(X86::DATA16_PREFIX));
MCSymbolRefExpr::VariantKind SRVK; MCSymbolRefExpr::VariantKind SRVK;
switch (MI.getOpcode()) { switch (MI.getOpcode()) {
@ -710,51 +703,79 @@ void X86AsmPrinter::LowerTlsAddr(X86MCInstLower &MCInstLowering,
llvm_unreachable("unexpected opcode"); llvm_unreachable("unexpected opcode");
} }
MCSymbol *sym = MCInstLowering.GetSymbolFromOperand(MI.getOperand(3)); const MCSymbolRefExpr *Sym = MCSymbolRefExpr::create(
const MCSymbolRefExpr *symRef = MCSymbolRefExpr::create(sym, SRVK, context); MCInstLowering.GetSymbolFromOperand(MI.getOperand(3)), SRVK, Ctx);
bool UseGot = MMI->getModule()->getRtLibUseGOT();
MCInst LEA; if (Is64Bits) {
if (is64Bits) { bool NeedsPadding = SRVK == MCSymbolRefExpr::VK_TLSGD;
LEA.setOpcode(X86::LEA64r); if (NeedsPadding)
LEA.addOperand(MCOperand::createReg(X86::RDI)); // dest EmitAndCountInstruction(MCInstBuilder(X86::DATA16_PREFIX));
LEA.addOperand(MCOperand::createReg(X86::RIP)); // base EmitAndCountInstruction(MCInstBuilder(X86::LEA64r)
LEA.addOperand(MCOperand::createImm(1)); // scale .addReg(X86::RDI)
LEA.addOperand(MCOperand::createReg(0)); // index .addReg(X86::RIP)
LEA.addOperand(MCOperand::createExpr(symRef)); // disp .addImm(1)
LEA.addOperand(MCOperand::createReg(0)); // seg .addReg(0)
} else if (SRVK == MCSymbolRefExpr::VK_TLSLDM) { .addExpr(Sym)
LEA.setOpcode(X86::LEA32r); .addReg(0));
LEA.addOperand(MCOperand::createReg(X86::EAX)); // dest const MCSymbol *TlsGetAddr = Ctx.getOrCreateSymbol("__tls_get_addr");
LEA.addOperand(MCOperand::createReg(X86::EBX)); // base if (NeedsPadding) {
LEA.addOperand(MCOperand::createImm(1)); // scale if (!UseGot)
LEA.addOperand(MCOperand::createReg(0)); // index EmitAndCountInstruction(MCInstBuilder(X86::DATA16_PREFIX));
LEA.addOperand(MCOperand::createExpr(symRef)); // disp EmitAndCountInstruction(MCInstBuilder(X86::DATA16_PREFIX));
LEA.addOperand(MCOperand::createReg(0)); // seg EmitAndCountInstruction(MCInstBuilder(X86::REX64_PREFIX));
}
if (UseGot) {
const MCExpr *Expr = MCSymbolRefExpr::create(
TlsGetAddr, MCSymbolRefExpr::VK_GOTPCREL, Ctx);
EmitAndCountInstruction(MCInstBuilder(X86::CALL64m)
.addReg(X86::RIP)
.addImm(1)
.addReg(0)
.addExpr(Expr)
.addReg(0));
} else {
EmitAndCountInstruction(
MCInstBuilder(X86::CALL64pcrel32)
.addExpr(MCSymbolRefExpr::create(TlsGetAddr,
MCSymbolRefExpr::VK_PLT, Ctx)));
}
} else { } else {
LEA.setOpcode(X86::LEA32r); if (SRVK == MCSymbolRefExpr::VK_TLSGD && !UseGot) {
LEA.addOperand(MCOperand::createReg(X86::EAX)); // dest EmitAndCountInstruction(MCInstBuilder(X86::LEA32r)
LEA.addOperand(MCOperand::createReg(0)); // base .addReg(X86::EAX)
LEA.addOperand(MCOperand::createImm(1)); // scale .addReg(0)
LEA.addOperand(MCOperand::createReg(X86::EBX)); // index .addImm(1)
LEA.addOperand(MCOperand::createExpr(symRef)); // disp .addReg(X86::EBX)
LEA.addOperand(MCOperand::createReg(0)); // seg .addExpr(Sym)
.addReg(0));
} else {
EmitAndCountInstruction(MCInstBuilder(X86::LEA32r)
.addReg(X86::EAX)
.addReg(X86::EBX)
.addImm(1)
.addReg(0)
.addExpr(Sym)
.addReg(0));
}
const MCSymbol *TlsGetAddr = Ctx.getOrCreateSymbol("___tls_get_addr");
if (UseGot) {
const MCExpr *Expr =
MCSymbolRefExpr::create(TlsGetAddr, MCSymbolRefExpr::VK_GOT, Ctx);
EmitAndCountInstruction(MCInstBuilder(X86::CALL32m)
.addReg(X86::EBX)
.addImm(1)
.addReg(0)
.addExpr(Expr)
.addReg(0));
} else {
EmitAndCountInstruction(
MCInstBuilder(X86::CALLpcrel32)
.addExpr(MCSymbolRefExpr::create(TlsGetAddr,
MCSymbolRefExpr::VK_PLT, Ctx)));
}
} }
EmitAndCountInstruction(LEA);
if (needsPadding) {
EmitAndCountInstruction(MCInstBuilder(X86::DATA16_PREFIX));
EmitAndCountInstruction(MCInstBuilder(X86::DATA16_PREFIX));
EmitAndCountInstruction(MCInstBuilder(X86::REX64_PREFIX));
}
StringRef name = is64Bits ? "__tls_get_addr" : "___tls_get_addr";
MCSymbol *tlsGetAddr = context.getOrCreateSymbol(name);
const MCSymbolRefExpr *tlsRef =
MCSymbolRefExpr::create(tlsGetAddr, MCSymbolRefExpr::VK_PLT, context);
EmitAndCountInstruction(
MCInstBuilder(is64Bits ? X86::CALL64pcrel32 : X86::CALLpcrel32)
.addExpr(tlsRef));
} }
/// Emit the largest nop instruction smaller than or equal to \p NumBytes /// Emit the largest nop instruction smaller than or equal to \p NumBytes

View File

@ -0,0 +1,30 @@
; RUN: llc < %s -mtriple=i386-linux-musl -relocation-model=pic | FileCheck --check-prefixes=CHECK,X86 %s
; RUN: llc < %s -mtriple=x86_64-linux-musl -relocation-model=pic | FileCheck --check-prefixes=CHECK,X64 %s
@gd = thread_local global i32 0
@ld = internal thread_local global i32 0
define i32* @get_gd() {
entry:
; CHECK-LABEL: get_gd:
; X86: leal gd@TLSGD(%ebx), %eax
; X86: calll *___tls_get_addr@GOT(%ebx)
; X64: leaq gd@TLSGD(%rip), %rdi
; X64: callq *__tls_get_addr@GOTPCREL(%rip)
ret i32* @gd
}
define i32* @get_ld() {
; FIXME: This function uses a single thread-local variable, we might want to fall back to general-dynamic.
; CHECK-LABEL: get_ld:
; X86: leal ld@TLSLDM(%ebx), %eax
; X86: calll *___tls_get_addr@GOT(%ebx)
; X64: leaq ld@TLSLD(%rip), %rdi
; X64: callq *__tls_get_addr@GOTPCREL(%rip)
ret i32* @ld
}
!llvm.module.flags = !{!1}
!1 = !{i32 7, !"RtLibUseGOT", i32 1}