forked from OSchip/llvm-project
[Hexagon] Implement TLS support
Patch by Anand Kodnani. llvm-svn: 261218
This commit is contained in:
parent
72dae62b8a
commit
7a737d1abb
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue