From cc24096d4df72caab78514e10b3f4fa4d75cf16a Mon Sep 17 00:00:00 2001 From: Martin Storsjo Date: Sat, 10 Mar 2018 19:05:21 +0000 Subject: [PATCH] [AArch64] Implement native TLS for Windows Differential Revision: https://reviews.llvm.org/D43971 llvm-svn: 327220 --- llvm/lib/Target/AArch64/AArch64ISelLowering.cpp | 63 ++++++++++++++++++++++ llvm/lib/Target/AArch64/AArch64ISelLowering.h | 1 + llvm/lib/Target/AArch64/AArch64InstrInfo.td | 1 + llvm/lib/Target/AArch64/AArch64MCInstLower.cpp | 13 ++++- llvm/test/CodeGen/AArch64/win-tls.ll | 72 +++++++++++++++++++++++++ 5 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/win-tls.ll diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index f50ff5b..4f0a7a2 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -3976,6 +3976,67 @@ AArch64TargetLowering::LowerELFGlobalTLSAddress(SDValue Op, return DAG.getNode(ISD::ADD, DL, PtrVT, ThreadBase, TPOff); } +SDValue +AArch64TargetLowering::LowerWindowsGlobalTLSAddress(SDValue Op, + SelectionDAG &DAG) const { + assert(Subtarget->isTargetWindows() && "Windows specific TLS lowering"); + + SDValue Chain = DAG.getEntryNode(); + EVT PtrVT = getPointerTy(DAG.getDataLayout()); + SDLoc DL(Op); + + SDValue TEB = DAG.getRegister(AArch64::X18, MVT::i64); + + // Load the ThreadLocalStoragePointer from the TEB + // A pointer to the TLS array is located at offset 0x58 from the TEB. + SDValue TLSArray = + DAG.getNode(ISD::ADD, DL, PtrVT, TEB, DAG.getIntPtrConstant(0x58, DL)); + TLSArray = DAG.getLoad(PtrVT, DL, Chain, TLSArray, MachinePointerInfo()); + Chain = TLSArray.getValue(1); + + // Load the TLS index from the C runtime; + // This does the same as getAddr(), but without having a GlobalAddressSDNode. + // This also does the same as LOADgot, but using a generic i32 load, + // while LOADgot only loads i64. + SDValue TLSIndexHi = + DAG.getTargetExternalSymbol("_tls_index", PtrVT, AArch64II::MO_PAGE); + SDValue TLSIndexLo = DAG.getTargetExternalSymbol( + "_tls_index", PtrVT, AArch64II::MO_PAGEOFF | AArch64II::MO_NC); + SDValue ADRP = DAG.getNode(AArch64ISD::ADRP, DL, PtrVT, TLSIndexHi); + SDValue TLSIndex = + DAG.getNode(AArch64ISD::ADDlow, DL, PtrVT, ADRP, TLSIndexLo); + TLSIndex = DAG.getLoad(MVT::i32, DL, Chain, TLSIndex, MachinePointerInfo()); + Chain = TLSIndex.getValue(1); + + // The pointer to the thread's TLS data area is at the TLS Index scaled by 8 + // offset into the TLSArray. + TLSIndex = DAG.getNode(ISD::ZERO_EXTEND, DL, PtrVT, TLSIndex); + SDValue Slot = DAG.getNode(ISD::SHL, DL, PtrVT, TLSIndex, + DAG.getConstant(3, DL, PtrVT)); + SDValue TLS = DAG.getLoad(PtrVT, DL, Chain, + DAG.getNode(ISD::ADD, DL, PtrVT, TLSArray, Slot), + MachinePointerInfo()); + Chain = TLS.getValue(1); + + const GlobalAddressSDNode *GA = cast(Op); + const GlobalValue *GV = GA->getGlobal(); + SDValue TGAHi = DAG.getTargetGlobalAddress( + GV, DL, PtrVT, 0, AArch64II::MO_TLS | AArch64II::MO_HI12); + SDValue TGALo = DAG.getTargetGlobalAddress( + GV, DL, PtrVT, 0, + AArch64II::MO_TLS | AArch64II::MO_PAGEOFF | AArch64II::MO_NC); + + // Add the offset from the start of the .tls section (section base). + SDValue Addr = + SDValue(DAG.getMachineNode(AArch64::ADDXri, DL, PtrVT, TLS, TGAHi, + DAG.getTargetConstant(0, DL, MVT::i32)), + 0); + Addr = SDValue(DAG.getMachineNode(AArch64::ADDXri, DL, PtrVT, Addr, TGALo, + DAG.getTargetConstant(0, DL, MVT::i32)), + 0); + return Addr; +} + SDValue AArch64TargetLowering::LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const { const GlobalAddressSDNode *GA = cast(Op); @@ -3986,6 +4047,8 @@ SDValue AArch64TargetLowering::LowerGlobalTLSAddress(SDValue Op, return LowerDarwinGlobalTLSAddress(Op, DAG); if (Subtarget->isTargetELF()) return LowerELFGlobalTLSAddress(Op, DAG); + if (Subtarget->isTargetWindows()) + return LowerWindowsGlobalTLSAddress(Op, DAG); llvm_unreachable("Unexpected platform trying to use TLS"); } diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h index 7778497..297c9bd 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h @@ -560,6 +560,7 @@ private: SDValue LowerELFGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const; SDValue LowerELFTLSDescCallSeq(SDValue SymAddr, const SDLoc &DL, SelectionDAG &DAG) const; + SDValue LowerWindowsGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSETCC(SDValue Op, SelectionDAG &DAG) const; SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSELECT(SDValue Op, SelectionDAG &DAG) const; diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index c418e17..db2e906 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -1331,6 +1331,7 @@ def ADRP : ADRI<1, "adrp", adrplabel, // page address of a constant pool entry, block address def : Pat<(AArch64adrp tconstpool:$cp), (ADRP tconstpool:$cp)>; def : Pat<(AArch64adrp tblockaddress:$cp), (ADRP tblockaddress:$cp)>; +def : Pat<(AArch64adrp texternalsym:$sym), (ADRP texternalsym:$sym)>; //===----------------------------------------------------------------------===// // Unconditional branch (register) instructions. diff --git a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp index 65dae03..25bcaea 100644 --- a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp +++ b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp @@ -173,11 +173,20 @@ MCOperand AArch64MCInstLower::lowerSymbolOperandELF(const MachineOperand &MO, MCOperand AArch64MCInstLower::lowerSymbolOperandCOFF(const MachineOperand &MO, MCSymbol *Sym) const { - MCSymbolRefExpr::VariantKind RefKind = MCSymbolRefExpr::VK_None; - const MCExpr *Expr = MCSymbolRefExpr::create(Sym, RefKind, Ctx); + AArch64MCExpr::VariantKind RefKind = AArch64MCExpr::VK_NONE; + if (MO.getTargetFlags() & AArch64II::MO_TLS) { + if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_PAGEOFF) + RefKind = AArch64MCExpr::VK_SECREL_LO12; + else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == + AArch64II::MO_HI12) + RefKind = AArch64MCExpr::VK_SECREL_HI12; + } + const MCExpr *Expr = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, Ctx); if (!MO.isJTI() && MO.getOffset()) Expr = MCBinaryExpr::createAdd( Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); + Expr = AArch64MCExpr::create(Expr, RefKind, Ctx); return MCOperand::createExpr(Expr); } diff --git a/llvm/test/CodeGen/AArch64/win-tls.ll b/llvm/test/CodeGen/AArch64/win-tls.ll new file mode 100644 index 0000000..f85c413 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/win-tls.ll @@ -0,0 +1,72 @@ +; RUN: llc -mtriple aarch64-windows %s -o - | FileCheck %s + +@tlsVar = thread_local global i32 0 +@tlsVar8 = thread_local global i8 0 +@tlsVar64 = thread_local global i64 0 + +define i32 @getVar() { + %1 = load i32, i32* @tlsVar + ret i32 %1 +} + +define i32* @getPtr() { + ret i32* @tlsVar +} + +define void @setVar(i32 %val) { + store i32 %val, i32* @tlsVar + ret void +} + +define i8 @getVar8() { + %1 = load i8, i8* @tlsVar8 + ret i8 %1 +} + +define i64 @getVar64() { + %1 = load i64, i64* @tlsVar64 + ret i64 %1 +} + +; CHECK-LABEL: getVar +; CHECK: adrp [[TLS_INDEX_ADDR:x[0-9]+]], _tls_index +; CHECK: ldr w[[TLS_INDEX:[0-9]+]], {{\[}}[[TLS_INDEX_ADDR]], _tls_index] +; CHECK: ldr [[TLS_POINTER:x[0-9]+]], [x18, #88] + +; CHECK: ldr [[TLS:x[0-9]+]], {{\[}}[[TLS_POINTER]], x[[TLS_INDEX]], lsl #3] +; CHECK: add [[TLS]], [[TLS]], :secrel_hi12:tlsVar +; This add+ldr could also be folded into a single ldr with a :secrel_lo12: +; offset. +; CHECK: add [[TLS]], [[TLS]], :secrel_lo12:tlsVar +; CHECK: ldr w0, {{\[}}[[TLS]]{{\]}} + +; CHECK-LABEL: getPtr +; CHECK: adrp [[TLS_INDEX_ADDR:x[0-9]+]], _tls_index +; CHECK: ldr w[[TLS_INDEX:[0-9]+]], {{\[}}[[TLS_INDEX_ADDR]], _tls_index] +; CHECK: ldr [[TLS_POINTER:x[0-9]+]], [x18, #88] + +; CHECK: ldr [[TLS:x[0-9]+]], {{\[}}[[TLS_POINTER]], x[[TLS_INDEX]], lsl #3] +; CHECK: add [[TLS]], [[TLS]], :secrel_hi12:tlsVar +; CHECK: add x0, [[TLS]], :secrel_lo12:tlsVar + +; CHECK-LABEL: setVar +; CHECK: adrp [[TLS_INDEX_ADDR:x[0-9]+]], _tls_index +; CHECK: ldr w[[TLS_INDEX:[0-9]+]], {{\[}}[[TLS_INDEX_ADDR]], _tls_index] +; CHECK: ldr [[TLS_POINTER:x[0-9]+]], [x18, #88] + +; CHECK: ldr [[TLS:x[0-9]+]], {{\[}}[[TLS_POINTER]], x[[TLS_INDEX]], lsl #3] +; CHECK: add [[TLS]], [[TLS]], :secrel_hi12:tlsVar +; This add+ldr could also be folded into a single ldr with a :secrel_lo12: +; offset. +; CHECK: add [[TLS]], [[TLS]], :secrel_lo12:tlsVar +; CHECK: str w0, {{\[}}[[TLS]]{{\]}} + +; CHECK-LABEL: getVar8 +; CHECK: add [[TLS:x[0-9]+]], [[TLS]], :secrel_hi12:tlsVar8 +; CHECK: add [[TLS]], [[TLS]], :secrel_lo12:tlsVar8 +; CHECK: ldrb w0, {{\[}}[[TLS]]{{\]}} + +; CHECK-LABEL: getVar64 +; CHECK: add [[TLS:x[0-9]+]], [[TLS]], :secrel_hi12:tlsVar64 +; CHECK: add [[TLS]], [[TLS]], :secrel_lo12:tlsVar64 +; CHECK: ldr x0, {{\[}}[[TLS]]{{\]}} -- 2.7.4