This patch introduces initial-exec model support for thread-local storage
authorBill Schmidt <wschmidt@linux.vnet.ibm.com>
Tue, 4 Dec 2012 16:18:08 +0000 (16:18 +0000)
committerBill Schmidt <wschmidt@linux.vnet.ibm.com>
Tue, 4 Dec 2012 16:18:08 +0000 (16:18 +0000)
on 64-bit PowerPC ELF.

The patch includes code to handle external assembly and MC output with the
integrated assembler.  It intentionally does not support the "old" JIT.

For the initial-exec TLS model, the ABI requires the following to calculate
the address of external thread-local variable x:

 Code sequence            Relocation                  Symbol
  ld 9,x@got@tprel(2)      R_PPC64_GOT_TPREL16_DS      x
  add 9,9,x@tls            R_PPC64_TLS                 x

The register 9 is arbitrary here.  The linker will replace x@got@tprel
with the offset relative to the thread pointer to the generated GOT
entry for symbol x.  It will replace x@tls with the thread-pointer
register (13).

The two test cases verify correct assembly output and relocation output
as just described.

PowerPC-specific selection node variants are added for the two
instructions above:  LD_GOT_TPREL and ADD_TLS.  These are inserted
when an initial-exec global variable is encountered by
PPCTargetLowering::LowerGlobalTLSAddress(), and later lowered to
machine instructions LDgotTPREL and ADD8TLS.  LDgotTPREL is a pseudo
that uses the same LDrs support added for medium code model's LDtocL,
with a different relocation type.

The rest of the processing is straightforward.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@169281 91177308-0d34-0410-b5e6-96231b3b80d8

18 files changed:
include/llvm/MC/MCExpr.h
include/llvm/Support/ELF.h
lib/MC/MCExpr.cpp
lib/Target/PowerPC/MCTargetDesc/PPCAsmBackend.cpp
lib/Target/PowerPC/MCTargetDesc/PPCELFObjectWriter.cpp
lib/Target/PowerPC/MCTargetDesc/PPCFixupKinds.h
lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.cpp
lib/Target/PowerPC/PPC.h
lib/Target/PowerPC/PPCAsmPrinter.cpp
lib/Target/PowerPC/PPCCodeEmitter.cpp
lib/Target/PowerPC/PPCISelDAGToDAG.cpp
lib/Target/PowerPC/PPCISelLowering.cpp
lib/Target/PowerPC/PPCISelLowering.h
lib/Target/PowerPC/PPCInstr64Bit.td
lib/Target/PowerPC/PPCInstrInfo.td
lib/Target/PowerPC/PPCMCInstLower.cpp
test/CodeGen/PowerPC/tls-ie-obj.ll [new file with mode: 0644]
test/CodeGen/PowerPC/tls-ie.ll [new file with mode: 0644]

index fbaa740f82e0341c3a1c80354330d9f9ecc0d4c3..93872edf2ff4224619e12510eb35ba13cb7ca84b 100644 (file)
@@ -179,6 +179,8 @@ public:
     VK_PPC_TPREL16_LO,   // symbol@tprel@l
     VK_PPC_TOC16_HA,     // symbol@toc@ha
     VK_PPC_TOC16_LO,     // symbol@toc@l
+    VK_PPC_GOT_TPREL16_DS, // symbol@got@tprel
+    VK_PPC_TLS,            // symbol@tls
 
     VK_Mips_GPREL,
     VK_Mips_GOT_CALL,
index 18f8d6dfb100e53ca91a2603173d32c9474b5bba..0614a14cc85ac4edb7fa0d257646fdb89a4c009c 100644 (file)
@@ -476,7 +476,9 @@ enum {
   R_PPC64_TOC16_HA            = 50,
   R_PPC64_TOC                 = 51,
   R_PPC64_TOC16_DS            = 63,
-  R_PPC64_TOC16_LO_DS         = 64
+  R_PPC64_TOC16_LO_DS         = 64,
+  R_PPC64_TLS                 = 67,
+  R_PPC64_GOT_TPREL16_DS      = 87
 };
 
 // ARM Specific e_flags
index d601d4e94abb0dcb8c5e2753fc338c82db98370e..7a63bace6c8eae1014aea52f895b8144c315f5e9 100644 (file)
@@ -211,6 +211,8 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) {
   case VK_PPC_TPREL16_LO: return "tprel@l";
   case VK_PPC_TOC16_HA: return "toc@ha";
   case VK_PPC_TOC16_LO: return "toc@l";
+  case VK_PPC_GOT_TPREL16_DS: return "got@tprel";
+  case VK_PPC_TLS: return "tls";
   case VK_Mips_GPREL: return "GPREL";
   case VK_Mips_GOT_CALL: return "GOT_CALL";
   case VK_Mips_GOT16: return "GOT16";
index d9dfe26ffa7d69b630bf2f17fa6f791e92b631e1..631f74958181e2612c600605859a2b08fad7e426 100644 (file)
@@ -31,6 +31,7 @@ static unsigned adjustFixupValue(unsigned Kind, uint64_t Value) {
   case FK_Data_4:
   case FK_Data_8:
   case PPC::fixup_ppc_toc:
+  case PPC::fixup_ppc_tlsreg:
     return Value;
   case PPC::fixup_ppc_lo14:
   case PPC::fixup_ppc_toc16_ds:
@@ -83,7 +84,8 @@ public:
       { "fixup_ppc_lo14",        16,     14,   0 },
       { "fixup_ppc_toc",          0,     64,   0 },
       { "fixup_ppc_toc16",       16,     16,   0 },
-      { "fixup_ppc_toc16_ds",    16,     14,   0 }
+      { "fixup_ppc_toc16_ds",    16,     14,   0 },
+      { "fixup_ppc_tlsreg",       0,      0,   0 }
     };
 
     if (Kind < FirstTargetFixupKind)
index 997465e97f6da366cae2fc7c797f73cef2295b6f..462d7072b56ae25015da2de136d83acfc9fc276d 100644 (file)
@@ -119,8 +119,14 @@ unsigned PPCELFObjectWriter::getRelocTypeInner(const MCValue &Target,
       case MCSymbolRefExpr::VK_PPC_TOC16_LO:
         Type = ELF::R_PPC64_TOC16_LO_DS;
         break;
+      case MCSymbolRefExpr::VK_PPC_GOT_TPREL16_DS:
+        Type = ELF::R_PPC64_GOT_TPREL16_DS;
+        break;
       }
       break;
+    case PPC::fixup_ppc_tlsreg:
+      Type = ELF::R_PPC64_TLS;
+      break;
     case FK_Data_8:
       switch (Modifier) {
       default: llvm_unreachable("Unsupported Modifier");
index 37b265e7fd38d16e368182ea65b903a9dc153338..75bb851630f1d93ff5d3c08146aa9d0383ac464b 100644 (file)
@@ -44,6 +44,9 @@ enum Fixups {
   /// fixup_ppc_toc16_ds - A 14-bit signed fixup relative to the TOC base with
   /// implied 2 zero bits
   fixup_ppc_toc16_ds,
+
+  /// fixup_ppc_tlsreg - Insert thread-pointer register number.
+  fixup_ppc_tlsreg,
   
   // Marker
   LastTargetFixupKind,
index 00bef5bb576bcf6a025e37f0884a867a571666d8..5b208d41f4f64bd854140ee0248f6ee27f7d5ab4 100644 (file)
@@ -62,6 +62,10 @@ public:
                             SmallVectorImpl<MCFixup> &Fixups) const;
   unsigned getMemRIXEncoding(const MCInst &MI, unsigned OpNo,
                              SmallVectorImpl<MCFixup> &Fixups) const;
+  unsigned getTLSOffsetEncoding(const MCInst &MI, unsigned OpNo,
+                                SmallVectorImpl<MCFixup> &Fixups) const;
+  unsigned getTLSRegEncoding(const MCInst &MI, unsigned OpNo,
+                             SmallVectorImpl<MCFixup> &Fixups) const;
   unsigned get_crbitm_encoding(const MCInst &MI, unsigned OpNo,
                                SmallVectorImpl<MCFixup> &Fixups) const;
 
@@ -195,6 +199,31 @@ unsigned PPCMCCodeEmitter::getMemRIXEncoding(const MCInst &MI, unsigned OpNo,
 }
 
 
+unsigned PPCMCCodeEmitter::getTLSOffsetEncoding(const MCInst &MI, unsigned OpNo,
+                                       SmallVectorImpl<MCFixup> &Fixups) const {
+  const MCOperand &MO = MI.getOperand(OpNo);
+  
+  // Add a fixup for the GOT displacement to the TLS block offset.
+  Fixups.push_back(MCFixup::Create(0, MO.getExpr(),
+                                   (MCFixupKind)PPC::fixup_ppc_toc16_ds));
+  return 0;
+}
+
+
+unsigned PPCMCCodeEmitter::getTLSRegEncoding(const MCInst &MI, unsigned OpNo,
+                                       SmallVectorImpl<MCFixup> &Fixups) const {
+  const MCOperand &MO = MI.getOperand(OpNo);
+  if (MO.isReg()) return getMachineOpValue(MI, MO, Fixups);
+  
+  // Add a fixup for the TLS register, which simply provides a relocation
+  // hint to the linker that this statement is part of a relocation sequence.
+  // Return the thread-pointer register's encoding.
+  Fixups.push_back(MCFixup::Create(0, MO.getExpr(),
+                                   (MCFixupKind)PPC::fixup_ppc_tlsreg));
+  return getPPCRegisterNumbering(PPC::X13);
+}
+
+
 unsigned PPCMCCodeEmitter::
 get_crbitm_encoding(const MCInst &MI, unsigned OpNo,
                     SmallVectorImpl<MCFixup> &Fixups) const {
index 9103e12325056540007878438d6d38ba830dd3f6..bbd247f20fbce7be15d755e7a8231738e01a78a3 100644 (file)
@@ -65,13 +65,16 @@ namespace llvm {
     MO_NLP_HIDDEN_FLAG = 16,
 
     /// The next are not flags but distinct values.
-    MO_ACCESS_MASK = 224,
+    MO_ACCESS_MASK = 0xe0,
 
     /// MO_LO16, MO_HA16 - lo16(symbol) and ha16(symbol)
-    MO_LO16 = 32, MO_HA16 = 64,
+    MO_LO16 = 1 << 5,
+    MO_HA16 = 2 << 5,
 
-    MO_TPREL16_HA = 96,
-    MO_TPREL16_LO = 128
+    MO_TPREL16_HA = 3 << 5,
+    MO_TPREL16_LO = 4 << 5,
+    MO_GOT_TPREL16_DS = 5 << 5,
+    MO_TLS = 6 << 5
   };
   } // end namespace PPCII
   
index b33b04e260494430b43a4f73ba1d1ee9c30df804..4315bc1a072abaf95c219025f4e54c933105f6ec 100644 (file)
@@ -513,6 +513,23 @@ void PPCAsmPrinter::EmitInstruction(const MachineInstr *MI) {
     OutStreamer.EmitInstruction(TmpInst);
     return;
   }
+  case PPC::LDgotTPREL: {
+    // Transform %Xd = LDgotTPREL <ga:@sym>, %Xs
+    LowerPPCMachineInstrToMCInst(MI, TmpInst, *this, Subtarget.isDarwin());
+
+    // Change the opcode to LDrs, which is a form of LD with the offset
+    // specified by a SymbolLo.
+    TmpInst.setOpcode(PPC::LDrs);
+    const MachineOperand &MO = MI->getOperand(1);
+    const GlobalValue *GValue = MO.getGlobal();
+    MCSymbol *MOSymbol = Mang->getSymbol(GValue);
+    const MCExpr *Exp =
+      MCSymbolRefExpr::Create(MOSymbol, MCSymbolRefExpr::VK_PPC_GOT_TPREL16_DS,
+                              OutContext);
+    TmpInst.getOperand(1) = MCOperand::CreateExpr(Exp);
+    OutStreamer.EmitInstruction(TmpInst);
+    return;
+  }
   case PPC::MFCRpseud:
   case PPC::MFCR8pseud:
     // Transform: %R3 = MFCRpseud %CR7
index 7b37dff2c902b537bb37b901ab704a744e947370..0dcb5deb5d4ab3c89b43a9a0e4dd330d1cff912d 100644 (file)
@@ -68,6 +68,8 @@ namespace {
     unsigned getLO16Encoding(const MachineInstr &MI, unsigned OpNo) const;
     unsigned getMemRIEncoding(const MachineInstr &MI, unsigned OpNo) const;
     unsigned getMemRIXEncoding(const MachineInstr &MI, unsigned OpNo) const;
+    unsigned getTLSOffsetEncoding(const MachineInstr &MI, unsigned OpNo) const;
+    unsigned getTLSRegEncoding(const MachineInstr &MI, unsigned OpNo) const;
 
     const char *getPassName() const { return "PowerPC Machine Code Emitter"; }
 
@@ -243,6 +245,20 @@ unsigned PPCCodeEmitter::getMemRIXEncoding(const MachineInstr &MI,
 }
 
 
+unsigned PPCCodeEmitter::getTLSOffsetEncoding(const MachineInstr &MI,
+                                           unsigned OpNo) const {
+  llvm_unreachable("TLS not supported on the old JIT.");
+  return 0;
+}
+
+
+unsigned PPCCodeEmitter::getTLSRegEncoding(const MachineInstr &MI,
+                                           unsigned OpNo) const {
+  llvm_unreachable("TLS not supported on the old JIT.");
+  return 0;
+}
+
+
 unsigned PPCCodeEmitter::getMachineOpValue(const MachineInstr &MI,
                                            const MachineOperand &MO) const {
 
index c7c265ce783917dcd11309af04d0e79ad120454a..abcaa9cab0eb237eccc8b1cd23df758d40ab27f4 100644 (file)
@@ -1311,6 +1311,11 @@ SDNode *PPCDAGToDAGISel::Select(SDNode *N) {
     return CurDAG->getMachineNode(PPC::ADDItocL, dl, MVT::i64,
                                   SDValue(Tmp, 0), GA);
   }
+  case PPCISD::LD_GOT_TPREL: {
+    assert (PPCSubTarget.isPPC64() && "Only supported for 64-bit ABI");
+    return CurDAG->getMachineNode(PPC::LDgotTPREL, dl, MVT::i64, 
+                                  N->getOperand(0), N->getOperand(1));
+  }
   }
 
   return SelectCode(N);
index 0d026c80a3978ac2c7a1407d9148994974d04be7..1168171b232be1d4bf8446028406b99f01370556 100644 (file)
@@ -578,6 +578,8 @@ const char *PPCTargetLowering::getTargetNodeName(unsigned Opcode) const {
   case PPCISD::ADDIS_TOC_HA:    return "PPCISD::ADDIS_TOC_HA";
   case PPCISD::LD_TOC_L:        return "PPCISD::LD_TOC_L";
   case PPCISD::ADDI_TOC_L:      return "PPCISD::ADDI_TOC_L";
+  case PPCISD::LD_GOT_TPREL:    return "PPCISD::LD_GOT_TPREL";
+  case PPCISD::ADD_TLS:         return "PPCISD::ADD_TLS";
   }
 }
 
@@ -1324,19 +1326,34 @@ SDValue PPCTargetLowering::LowerGlobalTLSAddress(SDValue Op,
   EVT PtrVT = getPointerTy();
   bool is64bit = PPCSubTarget.isPPC64();
 
-  TLSModel::Model model = getTargetMachine().getTLSModel(GV);
+  TLSModel::Model Model = getTargetMachine().getTLSModel(GV);
+
+  if (Model == TLSModel::LocalExec) {
+    SDValue TGAHi = DAG.getTargetGlobalAddress(GV, dl, PtrVT, 0,
+                                               PPCII::MO_TPREL16_HA);
+    SDValue TGALo = DAG.getTargetGlobalAddress(GV, dl, PtrVT, 0,
+                                               PPCII::MO_TPREL16_LO);
+    SDValue TLSReg = DAG.getRegister(is64bit ? PPC::X13 : PPC::R2,
+                                     is64bit ? MVT::i64 : MVT::i32);
+    SDValue Hi = DAG.getNode(PPCISD::Hi, dl, PtrVT, TGAHi, TLSReg);
+    return DAG.getNode(PPCISD::Lo, dl, PtrVT, TGALo, Hi);
+  }
+
+  if (!is64bit)
+    llvm_unreachable("only local-exec is currently supported for ppc32");
 
-  SDValue TGAHi = DAG.getTargetGlobalAddress(GV, dl, PtrVT, 0,
-                                             PPCII::MO_TPREL16_HA);
-  SDValue TGALo = DAG.getTargetGlobalAddress(GV, dl, PtrVT, 0,
-                                             PPCII::MO_TPREL16_LO);
+  if (Model != TLSModel::InitialExec)
+    llvm_unreachable("only local-exec and initial-exec TLS modes supported");
 
-  if (model != TLSModel::LocalExec)
-    llvm_unreachable("only local-exec TLS mode supported");
-  SDValue TLSReg = DAG.getRegister(is64bit ? PPC::X13 : PPC::R2,
+  SDValue GOTOffset = DAG.getTargetGlobalAddress(GV, dl, PtrVT, 0,
+                                                 PPCII::MO_GOT_TPREL16_DS);
+  SDValue TPReg = DAG.getTargetGlobalAddress(GV, dl, PtrVT, 0,
+                                             PPCII::MO_TLS);
+  SDValue GOTReg = DAG.getRegister(is64bit ? PPC::X2  : PPC::R2,
                                    is64bit ? MVT::i64 : MVT::i32);
-  SDValue Hi = DAG.getNode(PPCISD::Hi, dl, PtrVT, TGAHi, TLSReg);
-  return DAG.getNode(PPCISD::Lo, dl, PtrVT, TGALo, Hi);
+  SDValue TPOffset = DAG.getNode(PPCISD::LD_GOT_TPREL, dl, PtrVT,
+                                 GOTOffset, GOTReg);
+  return DAG.getNode(PPCISD::ADD_TLS, dl, PtrVT, TPOffset, TPReg);
 }
 
 SDValue PPCTargetLowering::LowerGlobalAddress(SDValue Op,
index c20356c0411d439fd427a7cb4a2aee4c0a41bb6c..f54a8b77a45af6acb358e9195a1a0a9114c22dde 100644 (file)
@@ -178,6 +178,20 @@ namespace llvm {
       CR6SET,
       CR6UNSET,
 
+      /// G8RC = LD_GOT_TPREL Symbol, G8RReg - Used by the initial-exec
+      /// TLS model, produces a LD instruction with base register G8RReg
+      /// and offset sym@got@tprel.  The latter identifies the GOT entry
+      /// containing the offset of "sym" relative to the thread pointer.
+      LD_GOT_TPREL,
+
+      /// G8RC = ADD_TLS G8RReg, Symbol - Used by the initial-exec TLS
+      /// model, produces an ADD instruction that adds the contents of
+      /// G8RReg to the thread pointer.  Symbol contains a relocation
+      /// sym@tls which is to be replaced by the thread pointer and
+      /// identifies to the linker that the instruction is part of a
+      /// TLS sequence.
+      ADD_TLS,
+
       /// STD_32 - This is the STD instruction for use with "32-bit" registers.
       STD_32 = ISD::FIRST_TARGET_MEMORY_OPCODE,
 
index 78844f5eddb22038a38499f71ad1b828fd7edcf0..dff15664a34778d71c989323c45869bb594662d7 100644 (file)
@@ -37,6 +37,12 @@ def memrs : Operand<iPTR> {   // memri where the immediate is a symbolLo64
   let EncoderMethod = "getMemRIXEncoding";
   let MIOperandInfo = (ops symbolLo64:$off, ptr_rc:$reg);
 }
+def tlsaddr : Operand<i64> {
+  let EncoderMethod = "getTLSOffsetEncoding";
+}
+def tlsreg : Operand<i64> {
+  let EncoderMethod = "getTLSRegEncoding";
+}
 
 //===----------------------------------------------------------------------===//
 // 64-bit transformation functions.
@@ -364,6 +370,11 @@ def XORIS8  : DForm_4<27, (outs G8RC:$dst), (ins G8RC:$src1, u16imm:$src2),
 def ADD8  : XOForm_1<31, 266, 0, (outs G8RC:$rT), (ins G8RC:$rA, G8RC:$rB),
                      "add $rT, $rA, $rB", IntSimple,
                      [(set G8RC:$rT, (add G8RC:$rA, G8RC:$rB))]>;
+// ADD8 has a special form: reg = ADD8(reg, sym@tls) for use by the
+// initial-exec thread-local storage model.
+def ADD8TLS  : XOForm_1<31, 266, 0, (outs G8RC:$rT), (ins G8RC:$rA, tlsreg:$rB),
+                        "add $rT, $rA, $rB", IntSimple,
+                        [(set G8RC:$rT, (add G8RC:$rA, tglobaltlsaddr:$rB))]>;
                      
 let Defs = [CARRY] in {
 def ADDC8 : XOForm_1<31, 10, 0, (outs G8RC:$rT), (ins G8RC:$rA, G8RC:$rB),
@@ -697,6 +708,15 @@ def ADDItocL: Pseudo<(outs G8RC:$rD), (ins G8RC:$reg, tocentry:$disp),
                      [(set G8RC:$rD,
                        (PPCaddiTocL G8RC:$reg, tglobaladdr:$disp))]>, isPPC64;
 
+// Support for thread-local storage.
+def LDgotTPREL: Pseudo<(outs G8RC:$rD), (ins tlsaddr:$disp, G8RC:$reg),
+                       "#LDgotTPREL",
+                       [(set G8RC:$rD,
+                         (PPCldGotTprel G8RC:$reg, tglobaltlsaddr:$disp))]>,
+                      isPPC64;
+def : Pat<(PPCaddTls G8RC:$in, tglobaltlsaddr:$g),
+          (ADD8TLS G8RC:$in, tglobaltlsaddr:$g)>;
+
 let PPC970_Unit = 2 in {
 // Truncating stores.                       
 def STB8 : DForm_1<38, (outs), (ins G8RC:$rS, memri:$src),
index 937ed0d90bd93f1094e6f18c19cdcac60b66ded1..a29f40ad531231186792553a22bbc6d0053c12e8 100644 (file)
@@ -91,6 +91,9 @@ def PPCtoc_entry: SDNode<"PPCISD::TOC_ENTRY", SDTIntBinOp, [SDNPMayLoad]>;
 def PPCvmaddfp  : SDNode<"PPCISD::VMADDFP", SDTFPTernaryOp, []>;
 def PPCvnmsubfp : SDNode<"PPCISD::VNMSUBFP", SDTFPTernaryOp, []>;
 
+def PPCldGotTprel : SDNode<"PPCISD::LD_GOT_TPREL", SDTIntBinOp, [SDNPMayLoad]>;
+def PPCaddTls     : SDNode<"PPCISD::ADD_TLS", SDTIntBinOp, []>;
+
 def PPCvperm    : SDNode<"PPCISD::VPERM", SDT_PPCvperm, []>;
 
 // These nodes represent the 32-bit PPC shifts that operate on 6-bit shift
index 73f7a2cfd570387aa371441fd54383c3a6d434c7..1c4af901f72bad202f165a7e1c53711c95fad034 100644 (file)
@@ -114,6 +114,12 @@ static MCOperand GetSymbolRef(const MachineOperand &MO, const MCSymbol *Symbol,
                                break;
     case PPCII::MO_TPREL16_LO: RefKind = MCSymbolRefExpr::VK_PPC_TPREL16_LO;
                                break;
+    case PPCII::MO_GOT_TPREL16_DS:
+      RefKind = MCSymbolRefExpr::VK_PPC_GOT_TPREL16_DS;
+      break;
+    case PPCII::MO_TLS:
+      RefKind = MCSymbolRefExpr::VK_PPC_TLS;
+      break;
    }
 
   // FIXME: This isn't right, but we don't have a good way to express this in
diff --git a/test/CodeGen/PowerPC/tls-ie-obj.ll b/test/CodeGen/PowerPC/tls-ie-obj.ll
new file mode 100644 (file)
index 0000000..5cc0b18
--- /dev/null
@@ -0,0 +1,32 @@
+; RUN: llc -mcpu=pwr7 -O0 -filetype=obj %s -o - | \
+; RUN: elf-dump --dump-section-data | FileCheck %s
+
+; Test correct relocation generation for thread-local storage
+; using the initial-exec model and integrated assembly.
+
+target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-f128:128:128-v128:128:128-n32:64"
+target triple = "powerpc64-unknown-linux-gnu"
+
+@a = external thread_local global i32
+
+define signext i32 @main() nounwind {
+entry:
+  %retval = alloca i32, align 4
+  store i32 0, i32* %retval
+  %0 = load i32* @a, align 4
+  ret i32 %0
+}
+
+; Verify generation of R_PPC64_GOT_TPREL16_DS and R_PPC64_TLS for
+; accessing external variable a.
+;
+; CHECK:       '.rela.text'
+; CHECK:       Relocation 0
+; CHECK-NEXT:  'r_offset'
+; CHECK-NEXT:  'r_sym', 0x[[SYM1:[0-9a-f]+]]
+; CHECK-NEXT:  'r_type', 0x00000057
+; CHECK:       Relocation 1
+; CHECK-NEXT:  'r_offset'
+; CHECK-NEXT:  'r_sym', 0x[[SYM1]]
+; CHECK-NEXT:  'r_type', 0x00000043
+
diff --git a/test/CodeGen/PowerPC/tls-ie.ll b/test/CodeGen/PowerPC/tls-ie.ll
new file mode 100644 (file)
index 0000000..cc6f084
--- /dev/null
@@ -0,0 +1,21 @@
+; RUN: llc -mcpu=pwr7 -O0 <%s | FileCheck %s
+
+; Test correct assembly code generation for thread-local storage
+; using the initial-exec model.
+
+target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-f128:128:128-v128:128:128-n32:64"
+target triple = "powerpc64-unknown-linux-gnu"
+
+@a = external thread_local global i32
+
+define signext i32 @main() nounwind {
+entry:
+  %retval = alloca i32, align 4
+  store i32 0, i32* %retval
+  %0 = load i32* @a, align 4
+  ret i32 %0
+}
+
+; CHECK: ld [[REG:[0-9]+]], a@got@tprel(2)
+; CHECK: add {{[0-9]+}}, [[REG]], a@tls
+