[Hexagon] Implement TLS support

Patch by Anand Kodnani.

llvm-svn: 261218
This commit is contained in:
Krzysztof Parzyszek 2016-02-18 15:42:57 +00:00
parent 72dae62b8a
commit 7a737d1abb
7 changed files with 267 additions and 2 deletions

View File

@ -1518,7 +1518,7 @@ HexagonTargetLowering::LowerBlockAddress(SDValue Op, SelectionDAG &DAG) const {
Reloc::Model RM = HTM.getRelocationModel();
if (RM == Reloc::Static) {
SDValue A = DAG.getTargetBlockAddress(BA, PtrVT);
SDValue A = DAG.getTargetBlockAddress(BA, PtrVT);
return DAG.getNode(HexagonISD::CONST32_GP, dl, PtrVT, A);
}
@ -1535,6 +1535,158 @@ HexagonTargetLowering::LowerGLOBAL_OFFSET_TABLE(SDValue Op, SelectionDAG &DAG)
return DAG.getNode(HexagonISD::AT_PCREL, SDLoc(Op), PtrVT, GOTSym);
}
SDValue
HexagonTargetLowering::GetDynamicTLSAddr(SelectionDAG &DAG, SDValue Chain,
GlobalAddressSDNode *GA, SDValue *InFlag, EVT PtrVT, unsigned ReturnReg,
unsigned char OperandFlags) const {
MachineFrameInfo *MFI = DAG.getMachineFunction().getFrameInfo();
SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue);
SDLoc dl(GA);
SDValue TGA = DAG.getTargetGlobalAddress(GA->getGlobal(), dl,
GA->getValueType(0),
GA->getOffset(),
OperandFlags);
// Create Operands for the call.The Operands should have the following:
// 1. Chain SDValue
// 2. Callee which in this case is the Global address value.
// 3. Registers live into the call.In this case its R0, as we
// have just one argument to be passed.
// 4. InFlag if there is any.
// Note: The order is important.
if (InFlag) {
SDValue Ops[] = { Chain, TGA,
DAG.getRegister(Hexagon::R0, PtrVT), *InFlag };
Chain = DAG.getNode(HexagonISD::CALLv3, dl, NodeTys, Ops);
} else {
SDValue Ops[] = { Chain, TGA, DAG.getRegister(Hexagon::R0, PtrVT)};
Chain = DAG.getNode(HexagonISD::CALLv3, dl, NodeTys, Ops);
}
// Inform MFI that function has calls.
MFI->setAdjustsStack(true);
SDValue Flag = Chain.getValue(1);
return DAG.getCopyFromReg(Chain, dl, ReturnReg, PtrVT, Flag);
}
//
// Lower using the intial executable model for TLS addresses
//
SDValue
HexagonTargetLowering::LowerToTLSInitialExecModel(GlobalAddressSDNode *GA,
SelectionDAG &DAG) const {
SDLoc dl(GA);
int64_t Offset = GA->getOffset();
auto PtrVT = getPointerTy(DAG.getDataLayout());
// Get the thread pointer.
SDValue TP = DAG.getCopyFromReg(DAG.getEntryNode(), dl, Hexagon::UGP, PtrVT);
Reloc::Model RM = HTM.getRelocationModel();
unsigned char TF = (RM == Reloc::PIC_) ? HexagonII::MO_IEGOT
: HexagonII::MO_IE;
// First generate the TLS symbol address
SDValue TGA = DAG.getTargetGlobalAddress(GA->getGlobal(), dl, PtrVT,
Offset, TF);
SDValue Sym = DAG.getNode(HexagonISD::CONST32, dl, PtrVT, TGA);
if (RM == Reloc::PIC_) {
// Generate the GOT pointer in case of position independent code
SDValue GOT = LowerGLOBAL_OFFSET_TABLE(Sym, DAG);
// Add the TLS Symbol address to GOT pointer.This gives
// GOT relative relocation for the symbol.
Sym = DAG.getNode(ISD::ADD, dl, PtrVT, GOT, Sym);
}
// Load the offset value for TLS symbol.This offset is relative to
// thread pointer.
SDValue LoadOffset = DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), Sym,
MachinePointerInfo(),
false, false, false, 0);
// Address of the thread local variable is the add of thread
// pointer and the offset of the variable.
return DAG.getNode(ISD::ADD, dl, PtrVT, TP, LoadOffset);
}
//
// Lower using the local executable model for TLS addresses
//
SDValue
HexagonTargetLowering::LowerToTLSLocalExecModel(GlobalAddressSDNode *GA,
SelectionDAG &DAG) const {
SDLoc dl(GA);
int64_t Offset = GA->getOffset();
auto PtrVT = getPointerTy(DAG.getDataLayout());
// Get the thread pointer.
SDValue TP = DAG.getCopyFromReg(DAG.getEntryNode(), dl, Hexagon::UGP, PtrVT);
// Generate the TLS symbol address
SDValue TGA = DAG.getTargetGlobalAddress(GA->getGlobal(), dl, PtrVT, Offset,
HexagonII::MO_TPREL);
SDValue Sym = DAG.getNode(HexagonISD::CONST32, dl, PtrVT, TGA);
// Address of the thread local variable is the add of thread
// pointer and the offset of the variable.
return DAG.getNode(ISD::ADD, dl, PtrVT, TP, Sym);
}
//
// Lower using the general dynamic model for TLS addresses
//
SDValue
HexagonTargetLowering::LowerToTLSGeneralDynamicModel(GlobalAddressSDNode *GA,
SelectionDAG &DAG) const {
SDLoc dl(GA);
int64_t Offset = GA->getOffset();
auto PtrVT = getPointerTy(DAG.getDataLayout());
// First generate the TLS symbol address
SDValue TGA = DAG.getTargetGlobalAddress(GA->getGlobal(), dl, PtrVT, Offset,
HexagonII::MO_GDGOT);
// Then, generate the GOT pointer
SDValue GOT = LowerGLOBAL_OFFSET_TABLE(TGA, DAG);
// Add the TLS symbol and the GOT pointer
SDValue Sym = DAG.getNode(HexagonISD::CONST32, dl, PtrVT, TGA);
SDValue Chain = DAG.getNode(ISD::ADD, dl, PtrVT, GOT, Sym);
// Copy over the argument to R0
SDValue InFlag;
Chain = DAG.getCopyToReg(DAG.getEntryNode(), dl, Hexagon::R0, Chain, InFlag);
InFlag = Chain.getValue(1);
return GetDynamicTLSAddr(DAG, Chain, GA, &InFlag, PtrVT,
Hexagon::R0, HexagonII::MO_GDPLT);
}
//
// Lower TLS addresses.
//
// For now for dynamic models, we only support the general dynamic model.
//
SDValue
HexagonTargetLowering::LowerGlobalTLSAddress(SDValue Op,
SelectionDAG &DAG) const {
GlobalAddressSDNode *GA = cast<GlobalAddressSDNode>(Op);
switch (HTM.getTLSModel(GA->getGlobal())) {
case TLSModel::GeneralDynamic:
case TLSModel::LocalDynamic:
return LowerToTLSGeneralDynamicModel(GA, DAG);
case TLSModel::InitialExec:
return LowerToTLSInitialExecModel(GA, DAG);
case TLSModel::LocalExec:
return LowerToTLSLocalExecModel(GA, DAG);
}
llvm_unreachable("Bogus TLS model");
}
//===----------------------------------------------------------------------===//
// TargetLowering Implementation
//===----------------------------------------------------------------------===//
@ -1638,6 +1790,7 @@ HexagonTargetLowering::HexagonTargetLowering(const TargetMachine &TM,
setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom);
setOperationAction(ISD::EH_RETURN, MVT::Other, Custom);
setOperationAction(ISD::GLOBAL_OFFSET_TABLE, MVT::i32, Custom);
setOperationAction(ISD::GlobalTLSAddress, MVT::i32, Custom);
setOperationAction(ISD::ATOMIC_FENCE, MVT::Other, Custom);
// Custom legalize GlobalAddress nodes into CONST32.
@ -2626,6 +2779,7 @@ HexagonTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
// Frame & Return address. Currently unimplemented.
case ISD::RETURNADDR: return LowerRETURNADDR(Op, DAG);
case ISD::FRAMEADDR: return LowerFRAMEADDR(Op, DAG);
case ISD::GlobalTLSAddress: return LowerGlobalTLSAddress(Op, DAG);
case ISD::ATOMIC_FENCE: return LowerATOMIC_FENCE(Op, DAG);
case ISD::GlobalAddress: return LowerGLOBALADDRESS(Op, DAG);
case ISD::BlockAddress: return LowerBlockAddress(Op, DAG);

View File

@ -136,6 +136,16 @@ bool isPositiveHalfWord(SDNode *N);
SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const override;
SDValue LowerGLOBALADDRESS(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerBlockAddress(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerToTLSGeneralDynamicModel(GlobalAddressSDNode *GA,
SelectionDAG &DAG) const;
SDValue LowerToTLSInitialExecModel(GlobalAddressSDNode *GA,
SelectionDAG &DAG) const;
SDValue LowerToTLSLocalExecModel(GlobalAddressSDNode *GA,
SelectionDAG &DAG) const;
SDValue GetDynamicTLSAddr(SelectionDAG &DAG, SDValue Chain,
GlobalAddressSDNode *GA, SDValue *InFlag, EVT PtrVT,
unsigned ReturnReg, unsigned char OperandFlags) const;
SDValue LowerGLOBAL_OFFSET_TABLE(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerCall(TargetLowering::CallLoweringInfo &CLI,

View File

@ -58,6 +58,21 @@ static MCOperand GetSymbolRef(const MachineOperand &MO, const MCSymbol *Symbol,
case HexagonII::MO_GPREL:
RelocationType = MCSymbolRefExpr::VK_Hexagon_GPREL;
break;
case HexagonII::MO_GDGOT:
RelocationType = MCSymbolRefExpr::VK_Hexagon_GD_GOT;
break;
case HexagonII::MO_GDPLT:
RelocationType = MCSymbolRefExpr::VK_Hexagon_GD_PLT;
break;
case HexagonII::MO_IE:
RelocationType = MCSymbolRefExpr::VK_Hexagon_IE;
break;
case HexagonII::MO_IEGOT:
RelocationType = MCSymbolRefExpr::VK_Hexagon_IE_GOT;
break;
case HexagonII::MO_TPREL:
RelocationType = MCSymbolRefExpr::VK_TPREL;
break;
}
ME = MCSymbolRefExpr::create(Symbol, RelocationType, MC);

View File

@ -121,6 +121,7 @@ BitVector HexagonRegisterInfo::getReservedRegs(const MachineFunction &MF)
Reserved.set(Hexagon::LC1);
Reserved.set(Hexagon::SA0);
Reserved.set(Hexagon::SA1);
Reserved.set(Hexagon::UGP);
Reserved.set(Hexagon::GP);
Reserved.set(Hexagon::CS0);
Reserved.set(Hexagon::CS1);

View File

@ -227,7 +227,27 @@ namespace HexagonII {
MO_LO16, MO_HI16,
// Offset from the base of the SDA.
MO_GPREL
MO_GPREL,
// MO_GDGOT - indicates GOT relative relocation for TLS
// GeneralDynamic method
MO_GDGOT,
// MO_GDPLT - indicates PLT relative relocation for TLS
// GeneralDynamic method
MO_GDPLT,
// MO_IE - indicates non PIC relocation for TLS
// Initial Executable method
MO_IE,
// MO_IEGOT - indicates PIC relocation for TLS
// Initial Executable method
MO_IEGOT,
// MO_TPREL - indicates relocation for TLS
// local Executable method
MO_TPREL
};
// Hexagon Sub-instruction classes.

View File

@ -0,0 +1,37 @@
; RUN: llc -O2 -march=hexagon -relocation-model=pic < %s | FileCheck %s
@dst_ie = thread_local(initialexec) global i32 0, align 4
@src_ie = thread_local(initialexec) global i32 0, align 4
; CHECK-LABEL: test_initial_exec
; CHECK: = add(pc, ##_GLOBAL_OFFSET_TABLE_@PCREL)
; CHECK-DAG: = ##src_ie@IEGOT
; CHECK-DAG: = ##dst_ie@IEGOT
; CHECK-DAG-NOT: call
define i32 @test_initial_exec() nounwind {
entry:
%0 = load i32, i32* @src_ie, align 4
store i32 %0, i32* @dst_ie, align 4
ret i32 0
}
@dst_gd = external thread_local global i32
@src_gd = external thread_local global i32
; At the moment, the local-dynamic model uses the same code as the
; general-dynamic model.
; CHECK-LABEL: test_dynamic
; CHECK: = add(pc, ##_GLOBAL_OFFSET_TABLE_@PCREL)
; CHECK-DAG: = ##src_gd@GDGOT
; CHECK-DAG: = ##dst_gd@GDGOT
; CHECK-DAG: call src_gd@GDPLT
; CHECK-DAG: call dst_gd@GDPLT
define i32 @test_dynamic() nounwind {
entry:
%0 = load i32, i32* @src_gd, align 4
store i32 %0, i32* @dst_gd, align 4
ret i32 0
}

View File

@ -0,0 +1,28 @@
; RUN: llc -O2 -march=hexagon -relocation-model=static < %s | FileCheck %s
@dst_le = thread_local global i32 0, align 4
@src_le = thread_local global i32 0, align 4
; CHECK-LABEL: test_local_exec
; CHECK-DAG: = ##src_le@TPREL
; CHECK-DAG: = ##dst_le@TPREL
define i32 @test_local_exec() nounwind {
entry:
%0 = load i32, i32* @src_le, align 4
store i32 %0, i32* @dst_le, align 4
ret i32 0
}
@dst_ie = external thread_local global i32
@src_ie = external thread_local global i32
; CHECK-LABEL: test_initial_exec:
; CHECK-DAG: = memw(##src_ie@IE)
; CHECK-DAG: = memw(##dst_ie@IE)
define i32 @test_initial_exec() nounwind {
entry:
%0 = load i32, i32* @src_ie, align 4
store i32 %0, i32* @dst_ie, align 4
ret i32 0
}