[Sparc] Add support for TLS in sparc.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@191164 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Venkatraman Govindaraju 2013-09-22 06:48:52 +00:00
parent 7d7db75a55
commit 7d052f272d
8 changed files with 311 additions and 10 deletions

View File

@ -53,7 +53,27 @@ enum TOF {
// Extract bits 41-32 of an address.
// Assembler: %hm(addr)
MO_HM
MO_HM,
// TargetFlags for Thread Local Storage.
MO_TLS_GD_HI22,
MO_TLS_GD_LO10,
MO_TLS_GD_ADD,
MO_TLS_GD_CALL,
MO_TLS_LDM_HI22,
MO_TLS_LDM_LO10,
MO_TLS_LDM_ADD,
MO_TLS_LDM_CALL,
MO_TLS_LDO_HIX22,
MO_TLS_LDO_LOX10,
MO_TLS_LDO_ADD,
MO_TLS_IE_HI22,
MO_TLS_IE_LO10,
MO_TLS_IE_LD,
MO_TLS_IE_LDX,
MO_TLS_IE_ADD,
MO_TLS_LE_HIX22,
MO_TLS_LE_LOX10
};
} // end namespace SPII

View File

@ -105,11 +105,37 @@ void SparcAsmPrinter::printOperand(const MachineInstr *MI, int opNum,
assert(TF == SPII::MO_NO_FLAG &&
"Cannot handle target flags on call address");
else if (MI->getOpcode() == SP::SETHIi)
assert((TF == SPII::MO_HI || TF == SPII::MO_H44 || TF == SPII::MO_HH) &&
assert((TF == SPII::MO_HI || TF == SPII::MO_H44 || TF == SPII::MO_HH
|| TF == SPII::MO_TLS_GD_HI22
|| TF == SPII::MO_TLS_LDM_HI22
|| TF == SPII::MO_TLS_LDO_HIX22
|| TF == SPII::MO_TLS_IE_HI22
|| TF == SPII::MO_TLS_LE_HIX22) &&
"Invalid target flags for address operand on sethi");
else if (MI->getOpcode() == SP::TLS_CALL)
assert((TF == SPII::MO_NO_FLAG
|| TF == SPII::MO_TLS_GD_CALL
|| TF == SPII::MO_TLS_LDM_CALL) &&
"Cannot handle target flags on tls call address");
else if (MI->getOpcode() == SP::TLS_ADDrr)
assert((TF == SPII::MO_TLS_GD_ADD || TF == SPII::MO_TLS_LDM_ADD
|| TF == SPII::MO_TLS_LDO_ADD || TF == SPII::MO_TLS_IE_ADD) &&
"Cannot handle target flags on add for TLS");
else if (MI->getOpcode() == SP::TLS_LDrr)
assert(TF == SPII::MO_TLS_IE_LD &&
"Cannot handle target flags on ld for TLS");
else if (MI->getOpcode() == SP::TLS_LDXrr)
assert(TF == SPII::MO_TLS_IE_LDX &&
"Cannot handle target flags on ldx for TLS");
else if (MI->getOpcode() == SP::XORri)
assert((TF == SPII::MO_TLS_LDO_LOX10 || TF == SPII::MO_TLS_LE_LOX10) &&
"Cannot handle target flags on xor for TLS");
else
assert((TF == SPII::MO_LO || TF == SPII::MO_M44 || TF == SPII::MO_L44 ||
TF == SPII::MO_HM) &&
assert((TF == SPII::MO_LO || TF == SPII::MO_M44 || TF == SPII::MO_L44
|| TF == SPII::MO_HM
|| TF == SPII::MO_TLS_GD_LO10
|| TF == SPII::MO_TLS_LDM_LO10
|| TF == SPII::MO_TLS_IE_LO10 ) &&
"Invalid target flags for small address operand");
}
#endif
@ -128,6 +154,24 @@ void SparcAsmPrinter::printOperand(const MachineInstr *MI, int opNum,
case SPII::MO_L44: O << "%l44("; break;
case SPII::MO_HH: O << "%hh("; break;
case SPII::MO_HM: O << "%hm("; break;
case SPII::MO_TLS_GD_HI22: O << "%tgd_hi22("; break;
case SPII::MO_TLS_GD_LO10: O << "%tgd_lo10("; break;
case SPII::MO_TLS_GD_ADD: O << "%tgd_add("; break;
case SPII::MO_TLS_GD_CALL: O << "%tgd_call("; break;
case SPII::MO_TLS_LDM_HI22: O << "%tldm_hi22("; break;
case SPII::MO_TLS_LDM_LO10: O << "%tldm_lo10("; break;
case SPII::MO_TLS_LDM_ADD: O << "%tldm_add("; break;
case SPII::MO_TLS_LDM_CALL: O << "%tldm_call("; break;
case SPII::MO_TLS_LDO_HIX22: O << "%tldo_hix22("; break;
case SPII::MO_TLS_LDO_LOX10: O << "%tldo_lox10("; break;
case SPII::MO_TLS_LDO_ADD: O << "%tldo_add("; break;
case SPII::MO_TLS_IE_HI22: O << "%tie_hi22("; break;
case SPII::MO_TLS_IE_LO10: O << "%tie_lo10("; break;
case SPII::MO_TLS_IE_LD: O << "%tie_ld("; break;
case SPII::MO_TLS_IE_LDX: O << "%tie_ldx("; break;
case SPII::MO_TLS_IE_ADD: O << "%tie_add("; break;
case SPII::MO_TLS_LE_HIX22: O << "%tle_hix22("; break;
case SPII::MO_TLS_LE_LOX10: O << "%tle_lox10("; break;
}
switch (MO.getType()) {

View File

@ -80,7 +80,8 @@ bool SparcDAGToDAGISel::SelectADDRri(SDValue Addr,
return true;
}
if (Addr.getOpcode() == ISD::TargetExternalSymbol ||
Addr.getOpcode() == ISD::TargetGlobalAddress)
Addr.getOpcode() == ISD::TargetGlobalAddress ||
Addr.getOpcode() == ISD::TargetGlobalTLSAddress)
return false; // direct calls.
if (Addr.getOpcode() == ISD::ADD) {
@ -117,7 +118,8 @@ bool SparcDAGToDAGISel::SelectADDRri(SDValue Addr,
bool SparcDAGToDAGISel::SelectADDRrr(SDValue Addr, SDValue &R1, SDValue &R2) {
if (Addr.getOpcode() == ISD::FrameIndex) return false;
if (Addr.getOpcode() == ISD::TargetExternalSymbol ||
Addr.getOpcode() == ISD::TargetGlobalAddress)
Addr.getOpcode() == ISD::TargetGlobalAddress ||
Addr.getOpcode() == ISD::TargetGlobalTLSAddress)
return false; // direct calls.
if (Addr.getOpcode() == ISD::ADD) {

View File

@ -1544,6 +1544,9 @@ const char *SparcTargetLowering::getTargetNodeName(unsigned Opcode) const {
case SPISD::RET_FLAG: return "SPISD::RET_FLAG";
case SPISD::GLOBAL_BASE_REG: return "SPISD::GLOBAL_BASE_REG";
case SPISD::FLUSHW: return "SPISD::FLUSHW";
case SPISD::TLS_ADD: return "SPISD::TLS_ADD";
case SPISD::TLS_LD: return "SPISD::TLS_LD";
case SPISD::TLS_CALL: return "SPISD::TLS_CALL";
}
}
@ -1699,6 +1702,103 @@ SDValue SparcTargetLowering::LowerBlockAddress(SDValue Op,
return makeAddress(Op, DAG);
}
SDValue SparcTargetLowering::LowerGlobalTLSAddress(SDValue Op,
SelectionDAG &DAG) const {
GlobalAddressSDNode *GA = cast<GlobalAddressSDNode>(Op);
SDLoc DL(GA);
const GlobalValue *GV = GA->getGlobal();
EVT PtrVT = getPointerTy();
TLSModel::Model model = getTargetMachine().getTLSModel(GV);
if (model == TLSModel::GeneralDynamic || model == TLSModel::LocalDynamic) {
unsigned HiTF = ((model == TLSModel::GeneralDynamic)? SPII::MO_TLS_GD_HI22
: SPII::MO_TLS_LDM_HI22);
unsigned LoTF = ((model == TLSModel::GeneralDynamic)? SPII::MO_TLS_GD_LO10
: SPII::MO_TLS_LDM_LO10);
unsigned addTF = ((model == TLSModel::GeneralDynamic)? SPII::MO_TLS_GD_ADD
: SPII::MO_TLS_LDM_ADD);
unsigned callTF = ((model == TLSModel::GeneralDynamic)? SPII::MO_TLS_GD_CALL
: SPII::MO_TLS_LDM_CALL);
SDValue HiLo = makeHiLoPair(Op, HiTF, LoTF, DAG);
SDValue Base = DAG.getNode(SPISD::GLOBAL_BASE_REG, DL, PtrVT);
SDValue Argument = DAG.getNode(SPISD::TLS_ADD, DL, PtrVT, Base, HiLo,
withTargetFlags(Op, addTF, DAG));
SDValue Chain = DAG.getEntryNode();
SDValue InFlag;
Chain = DAG.getCALLSEQ_START(Chain, DAG.getIntPtrConstant(1, true), DL);
Chain = DAG.getCopyToReg(Chain, DL, SP::O0, Argument, InFlag);
InFlag = Chain.getValue(1);
SDValue Callee = DAG.getTargetExternalSymbol("__tls_get_addr", PtrVT);
SDValue Symbol = withTargetFlags(Op, callTF, DAG);
SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue);
SmallVector<SDValue, 4> Ops;
Ops.push_back(Chain);
Ops.push_back(Callee);
Ops.push_back(Symbol);
Ops.push_back(DAG.getRegister(SP::O0, PtrVT));
const uint32_t *Mask = getTargetMachine()
.getRegisterInfo()->getCallPreservedMask(CallingConv::C);
assert(Mask && "Missing call preserved mask for calling convention");
Ops.push_back(DAG.getRegisterMask(Mask));
Ops.push_back(InFlag);
Chain = DAG.getNode(SPISD::TLS_CALL, DL, NodeTys, &Ops[0], Ops.size());
InFlag = Chain.getValue(1);
Chain = DAG.getCALLSEQ_END(Chain, DAG.getIntPtrConstant(1, true),
DAG.getIntPtrConstant(0, true), InFlag, DL);
InFlag = Chain.getValue(1);
SDValue Ret = DAG.getCopyFromReg(Chain, DL, SP::O0, PtrVT, InFlag);
if (model != TLSModel::LocalDynamic)
return Ret;
SDValue Hi = DAG.getNode(SPISD::Hi, DL, PtrVT,
withTargetFlags(Op, SPII::MO_TLS_LDO_HIX22, DAG));
SDValue Lo = DAG.getNode(SPISD::Lo, DL, PtrVT,
withTargetFlags(Op, SPII::MO_TLS_LDO_LOX10, DAG));
HiLo = DAG.getNode(ISD::XOR, DL, PtrVT, Hi, Lo);
return DAG.getNode(SPISD::TLS_ADD, DL, PtrVT, Ret, HiLo,
withTargetFlags(Op, SPII::MO_TLS_LDO_ADD, DAG));
}
if (model == TLSModel::InitialExec) {
unsigned ldTF = ((PtrVT == MVT::i64)? SPII::MO_TLS_IE_LDX
: SPII::MO_TLS_IE_LD);
SDValue Base = DAG.getNode(SPISD::GLOBAL_BASE_REG, DL, PtrVT);
// GLOBAL_BASE_REG codegen'ed with call. Inform MFI that this
// function has calls.
MachineFrameInfo *MFI = DAG.getMachineFunction().getFrameInfo();
MFI->setHasCalls(true);
SDValue TGA = makeHiLoPair(Op,
SPII::MO_TLS_IE_HI22, SPII::MO_TLS_IE_LO10, DAG);
SDValue Ptr = DAG.getNode(ISD::ADD, DL, PtrVT, Base, TGA);
SDValue Offset = DAG.getNode(SPISD::TLS_LD,
DL, PtrVT, Ptr,
withTargetFlags(Op, ldTF, DAG));
return DAG.getNode(SPISD::TLS_ADD, DL, PtrVT,
DAG.getRegister(SP::G7, PtrVT), Offset,
withTargetFlags(Op, SPII::MO_TLS_IE_ADD, DAG));
}
assert(model == TLSModel::LocalExec);
SDValue Hi = DAG.getNode(SPISD::Hi, DL, PtrVT,
withTargetFlags(Op, SPII::MO_TLS_LE_HIX22, DAG));
SDValue Lo = DAG.getNode(SPISD::Lo, DL, PtrVT,
withTargetFlags(Op, SPII::MO_TLS_LE_LOX10, DAG));
SDValue Offset = DAG.getNode(ISD::XOR, DL, PtrVT, Hi, Lo);
return DAG.getNode(ISD::ADD, DL, PtrVT,
DAG.getRegister(SP::G7, PtrVT), Offset);
}
SDValue
SparcTargetLowering::LowerF128_LibCallArg(SDValue Chain, ArgListTy &Args,
SDValue Arg, SDLoc DL,
@ -2333,8 +2433,7 @@ LowerOperation(SDValue Op, SelectionDAG &DAG) const {
case ISD::RETURNADDR: return LowerRETURNADDR(Op, DAG, *this);
case ISD::FRAMEADDR: return LowerFRAMEADDR(Op, DAG);
case ISD::GlobalTLSAddress:
llvm_unreachable("TLS not implemented for Sparc.");
case ISD::GlobalTLSAddress: return LowerGlobalTLSAddress(Op, DAG);
case ISD::GlobalAddress: return LowerGlobalAddress(Op, DAG);
case ISD::BlockAddress: return LowerBlockAddress(Op, DAG);
case ISD::ConstantPool: return LowerConstantPool(Op, DAG);

View File

@ -40,8 +40,12 @@ namespace llvm {
CALL, // A call instruction.
RET_FLAG, // Return with a flag operand.
GLOBAL_BASE_REG, // Global base reg for PIC
FLUSHW // FLUSH register windows to stack
GLOBAL_BASE_REG, // Global base reg for PIC.
FLUSHW, // FLUSH register windows to stack.
TLS_ADD, // For Thread Local Storage (TLS).
TLS_LD,
TLS_CALL
};
}
@ -119,6 +123,7 @@ namespace llvm {
SDLoc DL, SelectionDAG &DAG) const;
SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerBlockAddress(SDValue Op, SelectionDAG &DAG) const;

View File

@ -162,6 +162,9 @@ def : Pat<(subc i64:$a, i64:$b), (SUBCCrr $a, $b)>;
def : Pat<(SPcmpicc i64:$a, i64:$b), (CMPrr $a, $b)>;
def : Pat<(tlsadd i64:$a, i64:$b, tglobaltlsaddr:$sym),
(TLS_ADDrr $a, $b, $sym)>;
// Register-immediate instructions.
def : Pat<(and i64:$a, (i64 simm13:$b)), (ANDri $a, (as_i32imm $b))>;
@ -237,6 +240,12 @@ def LDXri : F3_2<3, 0b001011,
(outs I64Regs:$dst), (ins MEMri:$addr),
"ldx [$addr], $dst",
[(set i64:$dst, (load ADDRri:$addr))]>;
let mayLoad = 1 in
def TLS_LDXrr : F3_1<3, 0b001011,
(outs IntRegs:$dst), (ins MEMrr:$addr, TLSSym:$sym),
"ldx [$addr], $dst, $sym",
[(set i64:$dst,
(tlsld ADDRrr:$addr, tglobaltlsaddr:$sym))]>;
// Extending loads to i64.
def : Pat<(i64 (zextloadi1 ADDRrr:$addr)), (LDUBrr ADDRrr:$addr)>;

View File

@ -85,6 +85,8 @@ def MEMri : Operand<iPTR> {
let MIOperandInfo = (ops ptr_rc, i32imm);
}
def TLSSym : Operand<iPTR>;
// Branch targets have OtherVT type.
def brtarget : Operand<OtherVT>;
def calltarget : Operand<i32>;
@ -106,6 +108,11 @@ SDTypeProfile<1, 1, [SDTCisVT<0, f32>, SDTCisFP<1>]>;
def SDTSPITOF :
SDTypeProfile<1, 1, [SDTCisFP<0>, SDTCisVT<1, f32>]>;
def SDTSPtlsadd :
SDTypeProfile<1, 3, [SDTCisInt<0>, SDTCisSameAs<0, 1>, SDTCisPtrTy<2>]>;
def SDTSPtlsld :
SDTypeProfile<1, 2, [SDTCisPtrTy<0>, SDTCisPtrTy<1>]>;
def SPcmpicc : SDNode<"SPISD::CMPICC", SDTSPcmpicc, [SDNPOutGlue]>;
def SPcmpfcc : SDNode<"SPISD::CMPFCC", SDTSPcmpfcc, [SDNPOutGlue]>;
def SPbricc : SDNode<"SPISD::BRICC", SDTSPbrcc, [SDNPHasChain, SDNPInGlue]>;
@ -144,6 +151,12 @@ def retflag : SDNode<"SPISD::RET_FLAG", SDT_SPRet,
def flushw : SDNode<"SPISD::FLUSHW", SDTNone,
[SDNPHasChain, SDNPSideEffect, SDNPMayStore]>;
def tlsadd : SDNode<"SPISD::TLS_ADD", SDTSPtlsadd>;
def tlsld : SDNode<"SPISD::TLS_LD", SDTSPtlsld>;
def tlscall : SDNode<"SPISD::TLS_CALL", SDT_SPCall,
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
SDNPVariadic]>;
def getPCX : Operand<i32> {
let PrintMethod = "printGetPCX";
}
@ -806,6 +819,34 @@ let Defs = [FCC] in {
Requires<[HasHardQuad]>;
}
//===----------------------------------------------------------------------===//
// Instructions for Thread Local Storage(TLS).
//===----------------------------------------------------------------------===//
def TLS_ADDrr : F3_1<2, 0b000000,
(outs IntRegs:$rd),
(ins IntRegs:$rs1, IntRegs:$rs2, TLSSym:$sym),
"add $rs1, $rs2, $rd, $sym",
[(set i32:$rd,
(tlsadd i32:$rs1, i32:$rs2, tglobaltlsaddr:$sym))]>;
let mayLoad = 1 in
def TLS_LDrr : F3_1<3, 0b000000,
(outs IntRegs:$dst), (ins MEMrr:$addr, TLSSym:$sym),
"ld [$addr], $dst, $sym",
[(set i32:$dst,
(tlsld ADDRrr:$addr, tglobaltlsaddr:$sym))]>;
let Uses = [O6], isCall = 1 in
def TLS_CALL : InstSP<(outs),
(ins calltarget:$disp, TLSSym:$sym, variable_ops),
"call $disp, $sym\n\tnop",
[(tlscall texternalsym:$disp, tglobaltlsaddr:$sym)]> {
bits<30> disp;
let op = 1;
let Inst{29-0} = disp;
}
//===----------------------------------------------------------------------===//
// V9 Instructions
//===----------------------------------------------------------------------===//
@ -915,6 +956,14 @@ def : Pat<(SPlo tglobaladdr:$in), (ORri (i32 G0), tglobaladdr:$in)>;
def : Pat<(SPhi tconstpool:$in), (SETHIi tconstpool:$in)>;
def : Pat<(SPlo tconstpool:$in), (ORri (i32 G0), tconstpool:$in)>;
// GlobalTLS addresses
def : Pat<(SPhi tglobaltlsaddr:$in), (SETHIi tglobaltlsaddr:$in)>;
def : Pat<(SPlo tglobaltlsaddr:$in), (ORri (i32 G0), tglobaltlsaddr:$in)>;
def : Pat<(add (SPhi tglobaltlsaddr:$in1), (SPlo tglobaltlsaddr:$in2)),
(ADDri (SETHIi tglobaltlsaddr:$in1), (tglobaltlsaddr:$in2))>;
def : Pat<(xor (SPhi tglobaltlsaddr:$in1), (SPlo tglobaltlsaddr:$in2)),
(XORri (SETHIi tglobaltlsaddr:$in1), (tglobaltlsaddr:$in2))>;
// Blockaddress
def : Pat<(SPhi tblockaddress:$in), (SETHIi tblockaddress:$in)>;
def : Pat<(SPlo tblockaddress:$in), (ORri (i32 G0), tblockaddress:$in)>;

73
test/CodeGen/SPARC/tls.ll Normal file
View File

@ -0,0 +1,73 @@
; RUN: llc <%s -march=sparc -relocation-model=static | FileCheck %s --check-prefix=v8abs
; RUN: llc <%s -march=sparcv9 -relocation-model=static | FileCheck %s --check-prefix=v9abs
; RUN: llc <%s -march=sparc -relocation-model=pic | FileCheck %s --check-prefix=pic
; RUN: llc <%s -march=sparcv9 -relocation-model=pic | FileCheck %s --check-prefix=pic
@local_symbol = internal thread_local global i32 0
@extern_symbol = external thread_local global i32
; v8abs-LABEL: test_tls_local
; v8abs: sethi %tle_hix22(local_symbol), [[R0:%[goli][0-7]]]
; v8abs: xor [[R0]], %tle_lox10(local_symbol), [[R1:%[goli][0-7]]]
; v8abs: ld [%g7+[[R1]]]
; v9abs-LABEL: test_tls_local
; v9abs: sethi %tle_hix22(local_symbol), [[R0:%[goli][0-7]]]
; v9abs: xor [[R0]], %tle_lox10(local_symbol), [[R1:%[goli][0-7]]]
; v9abs: ld [%g7+[[R1]]]
; pic-LABEL: test_tls_local
; pic: or {{%[goli][0-7]}}, %lo(_GLOBAL_OFFSET_TABLE_+{{.+}}), [[PC:%[goli][0-7]]]
; pic: add [[PC]], %o7, [[GOTBASE:%[goli][0-7]]]
; pic-DAG: sethi %tldm_hi22(local_symbol), [[R0:%[goli][0-7]]]
; pic-DAG: add [[R0]], %tldm_lo10(local_symbol), [[R1:%[goli][0-7]]]
; pic-DAG: add [[GOTBASE]], [[R1]], %o0, %tldm_add(local_symbol)
; pic-DAG: call __tls_get_addr, %tldm_call(local_symbol)
; pic-DAG: sethi %tldo_hix22(local_symbol), [[R2:%[goli][0-7]]]
; pic-DAG: xor [[R2]], %tldo_lox10(local_symbol), [[R3:%[goli][0-7]]]
; pic: add %o0, [[R3]], {{.+}}, %tldo_add(local_symbol)
define i32 @test_tls_local() {
entry:
%0 = load i32* @local_symbol, align 4
%1 = add i32 %0, 1
store i32 %1, i32* @local_symbol, align 4
ret i32 %1
}
; v8abs-LABEL: test_tls_extern
; v8abs: or {{%[goli][0-7]}}, %lo(_GLOBAL_OFFSET_TABLE_+{{.+}}), [[PC:%[goli][0-7]]]
; v8abs: add [[PC]], %o7, %[[GOTBASE:[goli][0-7]]]
; v8abs: sethi %tie_hi22(extern_symbol), [[R1:%[goli][0-7]]]
; v8abs: add [[R1]], %tie_lo10(extern_symbol), %[[R2:[goli][0-7]]]
; v8abs: ld [%[[GOTBASE]]+%[[R2]]], [[R3:%[goli][0-7]]], %tie_ld(extern_symbol)
; v8abs: add %g7, [[R3]], %[[R4:[goli][0-7]]], %tie_add(extern_symbol)
; v8abs: ld [%[[R4]]]
; v9abs-LABEL: test_tls_extern
; v9abs: or {{%[goli][0-7]}}, %lo(_GLOBAL_OFFSET_TABLE_+{{.+}}), [[PC:%[goli][0-7]]]
; v9abs: add [[PC]], %o7, %[[GOTBASE:[goli][0-7]]]
; v9abs: sethi %tie_hi22(extern_symbol), [[R1:%[goli][0-7]]]
; v9abs: add [[R1]], %tie_lo10(extern_symbol), %[[R2:[goli][0-7]]]
; v9abs: ldx [%[[GOTBASE]]+%[[R2]]], [[R3:%[goli][0-7]]], %tie_ldx(extern_symbol)
; v9abs: add %g7, [[R3]], %[[R4:[goli][0-7]]], %tie_add(extern_symbol)
; v9abs: ld [%[[R4]]]
; pic-LABEL: test_tls_extern
; pic: or {{%[goli][0-7]}}, %lo(_GLOBAL_OFFSET_TABLE_+{{.+}}), [[PC:%[goli][0-7]]]
; pic: add [[PC]], %o7, [[GOTBASE:%[goli][0-7]]]
; pic: sethi %tgd_hi22(extern_symbol), [[R0:%[goli][0-7]]]
; pic: add [[R0]], %tgd_lo10(extern_symbol), [[R1:%[goli][0-7]]]
; pic: add [[GOTBASE]], [[R1]], %o0, %tgd_add(extern_symbol)
; pic: call __tls_get_addr, %tgd_call(extern_symbol)
; pic-NEXT: nop
define i32 @test_tls_extern() {
entry:
%0 = load i32* @extern_symbol, align 4
%1 = add i32 %0, 1
store i32 %1, i32* @extern_symbol, align 4
ret i32 %1
}