diff --git a/llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp b/llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp --- a/llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp +++ b/llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp @@ -93,7 +93,8 @@ bool emitOpcodePrefix(int MemOperand, const MCInst &MI, const MCSubtargetInfo &STI, raw_ostream &OS) const; - bool emitREXPrefix(int MemOperand, const MCInst &MI, raw_ostream &OS) const; + bool emitREXPrefix(int MemOperand, const MCInst &MI, + const MCSubtargetInfo &STI, raw_ostream &OS) const; }; } // end anonymous namespace @@ -1201,6 +1202,7 @@ /// /// \returns true if REX prefix is used, otherwise returns false. bool X86MCCodeEmitter::emitREXPrefix(int MemOperand, const MCInst &MI, + const MCSubtargetInfo &STI, raw_ostream &OS) const { uint8_t REX = [&, MemOperand]() { uint8_t REX = 0; @@ -1221,15 +1223,27 @@ // If it accesses SPL, BPL, SIL, or DIL, then it requires a 0x40 REX prefix. for (unsigned i = CurOp; i != NumOps; ++i) { const MCOperand &MO = MI.getOperand(i); - if (!MO.isReg()) - continue; - unsigned Reg = MO.getReg(); - if (Reg == X86::AH || Reg == X86::BH || Reg == X86::CH || Reg == X86::DH) - UsesHighByteReg = true; - if (X86II::isX86_64NonExtLowByteReg(Reg)) - // FIXME: The caller of determineREXPrefix slaps this prefix onto - // anything that returns non-zero. - REX |= 0x40; // REX fixed encoding prefix + if (MO.isReg()) { + unsigned Reg = MO.getReg(); + if (Reg == X86::AH || Reg == X86::BH || Reg == X86::CH || Reg == X86::DH) + UsesHighByteReg = true; + if (X86II::isX86_64NonExtLowByteReg(Reg)) + // FIXME: The caller of determineREXPrefix slaps this prefix onto + // anything that returns non-zero. + REX |= 0x40; // REX fixed encoding prefix + } else if (MO.isExpr() && + STI.getTargetTriple().getEnvironment() == Triple::GNUX32) { + // GOTTPOFF and TLSDESC relocations require a REX prefix to allow + // linker optimizations: even if the instructions we see may not require + // any prefix, they may be replaced by instructions that do. This is + // handled as a special case here so that it also works for hand-written + // assembly without the user needing to write REX, as with GNU as. + const MCSymbolRefExpr *Ref = dyn_cast(MO.getExpr()); + if (Ref && (Ref->getKind() == MCSymbolRefExpr::VK_GOTTPOFF || + Ref->getKind() == MCSymbolRefExpr::VK_TLSDESC)) { + REX |= 0x40; // REX fixed encoding prefix + } + } } switch (TSFlags & X86II::FormMask) { @@ -1352,7 +1366,7 @@ assert((STI.hasFeature(X86::Mode64Bit) || !(TSFlags & X86II::REX_W)) && "REX.W requires 64bit mode."); bool HasREX = STI.hasFeature(X86::Mode64Bit) - ? emitREXPrefix(MemOperand, MI, OS) + ? emitREXPrefix(MemOperand, MI, STI, OS) : false; // 0x0F escape code must be emitted just before the opcode. diff --git a/llvm/test/MC/X86/tlsdesc-x32.s b/llvm/test/MC/X86/tlsdesc-x32.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/X86/tlsdesc-x32.s @@ -0,0 +1,20 @@ +# RUN: llvm-mc -triple x86_64-pc-linux-gnux32 %s | FileCheck --check-prefix=PRINT %s + +# RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnux32 %s -o %t +# RUN: llvm-readelf -s %t | FileCheck --check-prefix=SYM %s +# RUN: llvm-objdump -d -r %t | FileCheck --match-full-lines %s + +# PRINT: leal a@tlsdesc(%rip), %eax +# PRINT-NEXT: callq *a@tlscall(%eax) + +# SYM: TLS GLOBAL DEFAULT UND a + +# CHECK: 0: 40 8d 05 00 00 00 00 leal (%rip), %eax # 7 <{{.*}}> +# CHECK-NEXT: 00000003: R_X86_64_GOTPC32_TLSDESC a-0x4 +# CHECK-NEXT: 7: 67 ff 10 callq *(%eax) +# CHECK-NEXT: 00000007: R_X86_64_TLSDESC_CALL a +# CHECK-NEXT: a: 8d 05 34 12 00 00 leal 4660(%rip), %eax # {{.*}} + +lea a@tlsdesc(%rip), %eax +call *a@tlscall(%eax) +lea 0x1234(%rip), %eax