mirror of
https://github.com/intel/llvm.git
synced 2026-01-20 01:58:44 +08:00
[m68k] Add TLS Support
This patch introduces TLS (Thread-Local Storage) support to the LLVM m68k backend. Reviewed By: glaubitz Differential Revision: https://reviews.llvm.org/D144941
This commit is contained in:
@@ -666,6 +666,15 @@ void M68kDAGToDAGISel::Select(SDNode *Node) {
|
||||
default:
|
||||
break;
|
||||
|
||||
case ISD::GLOBAL_OFFSET_TABLE: {
|
||||
SDValue GOT = CurDAG->getTargetExternalSymbol(
|
||||
"_GLOBAL_OFFSET_TABLE_", MVT::i32, M68kII::MO_GOTPCREL);
|
||||
MachineSDNode *Res =
|
||||
CurDAG->getMachineNode(M68k::LEA32q, DL, MVT::i32, GOT);
|
||||
ReplaceNode(Node, Res);
|
||||
return;
|
||||
}
|
||||
|
||||
case M68kISD::GLOBAL_BASE_REG:
|
||||
ReplaceNode(Node, getGlobalBaseReg());
|
||||
return;
|
||||
|
||||
@@ -1420,9 +1420,108 @@ SDValue M68kTargetLowering::LowerOperation(SDValue Op,
|
||||
return LowerShiftRightParts(Op, DAG, false);
|
||||
case ISD::ATOMIC_FENCE:
|
||||
return LowerATOMICFENCE(Op, DAG);
|
||||
case ISD::GlobalTLSAddress:
|
||||
return LowerGlobalTLSAddress(Op, DAG);
|
||||
}
|
||||
}
|
||||
|
||||
SDValue M68kTargetLowering::LowerExternalSymbolCall(SelectionDAG &DAG,
|
||||
SDLoc Loc,
|
||||
llvm::StringRef SymbolName,
|
||||
ArgListTy &&ArgList) const {
|
||||
PointerType *PtrTy = PointerType::get(*DAG.getContext(), 0);
|
||||
CallLoweringInfo CLI(DAG);
|
||||
CLI.setDebugLoc(Loc)
|
||||
.setChain(DAG.getEntryNode())
|
||||
.setLibCallee(CallingConv::C, PtrTy,
|
||||
DAG.getExternalSymbol(SymbolName.data(),
|
||||
getPointerMemTy(DAG.getDataLayout())),
|
||||
std::move(ArgList));
|
||||
return LowerCallTo(CLI).first;
|
||||
}
|
||||
|
||||
SDValue M68kTargetLowering::getTLSGetAddr(GlobalAddressSDNode *GA,
|
||||
SelectionDAG &DAG,
|
||||
unsigned TargetFlags) const {
|
||||
SDValue GOT = DAG.getGLOBAL_OFFSET_TABLE(MVT::i32);
|
||||
SDValue TGA = DAG.getTargetGlobalAddress(
|
||||
GA->getGlobal(), GA, GA->getValueType(0), GA->getOffset(), TargetFlags);
|
||||
SDValue Arg = DAG.getNode(ISD::ADD, SDLoc(GA), MVT::i32, GOT, TGA);
|
||||
|
||||
PointerType *PtrTy = PointerType::get(*DAG.getContext(), 0);
|
||||
|
||||
ArgListTy Args;
|
||||
ArgListEntry Entry;
|
||||
Entry.Node = Arg;
|
||||
Entry.Ty = PtrTy;
|
||||
Args.push_back(Entry);
|
||||
return LowerExternalSymbolCall(DAG, SDLoc(GA), "__tls_get_addr",
|
||||
std::move(Args));
|
||||
}
|
||||
|
||||
SDValue M68kTargetLowering::getM68kReadTp(SDLoc Loc, SelectionDAG &DAG) const {
|
||||
return LowerExternalSymbolCall(DAG, Loc, "__m68k_read_tp", ArgListTy());
|
||||
}
|
||||
|
||||
SDValue M68kTargetLowering::LowerTLSGeneralDynamic(GlobalAddressSDNode *GA,
|
||||
SelectionDAG &DAG) const {
|
||||
return getTLSGetAddr(GA, DAG, M68kII::MO_TLSGD);
|
||||
}
|
||||
|
||||
SDValue M68kTargetLowering::LowerTLSLocalDynamic(GlobalAddressSDNode *GA,
|
||||
SelectionDAG &DAG) const {
|
||||
SDValue Addr = getTLSGetAddr(GA, DAG, M68kII::MO_TLSLDM);
|
||||
SDValue TGA =
|
||||
DAG.getTargetGlobalAddress(GA->getGlobal(), GA, GA->getValueType(0),
|
||||
GA->getOffset(), M68kII::MO_TLSLD);
|
||||
return DAG.getNode(ISD::ADD, SDLoc(GA), MVT::i32, TGA, Addr);
|
||||
}
|
||||
|
||||
SDValue M68kTargetLowering::LowerTLSInitialExec(GlobalAddressSDNode *GA,
|
||||
SelectionDAG &DAG) const {
|
||||
SDValue GOT = DAG.getGLOBAL_OFFSET_TABLE(MVT::i32);
|
||||
SDValue Tp = getM68kReadTp(SDLoc(GA), DAG);
|
||||
SDValue TGA =
|
||||
DAG.getTargetGlobalAddress(GA->getGlobal(), GA, GA->getValueType(0),
|
||||
GA->getOffset(), M68kII::MO_TLSIE);
|
||||
SDValue Addr = DAG.getNode(ISD::ADD, SDLoc(GA), MVT::i32, TGA, GOT);
|
||||
SDValue Offset =
|
||||
DAG.getLoad(MVT::i32, SDLoc(GA), DAG.getEntryNode(), Addr,
|
||||
MachinePointerInfo::getGOT(DAG.getMachineFunction()));
|
||||
|
||||
return DAG.getNode(ISD::ADD, SDLoc(GA), MVT::i32, Offset, Tp);
|
||||
}
|
||||
|
||||
SDValue M68kTargetLowering::LowerTLSLocalExec(GlobalAddressSDNode *GA,
|
||||
SelectionDAG &DAG) const {
|
||||
SDValue Tp = getM68kReadTp(SDLoc(GA), DAG);
|
||||
SDValue TGA =
|
||||
DAG.getTargetGlobalAddress(GA->getGlobal(), GA, GA->getValueType(0),
|
||||
GA->getOffset(), M68kII::MO_TLSLE);
|
||||
return DAG.getNode(ISD::ADD, SDLoc(GA), MVT::i32, TGA, Tp);
|
||||
}
|
||||
|
||||
SDValue M68kTargetLowering::LowerGlobalTLSAddress(SDValue Op,
|
||||
SelectionDAG &DAG) const {
|
||||
assert(Subtarget.isTargetELF());
|
||||
|
||||
auto *GA = cast<GlobalAddressSDNode>(Op);
|
||||
TLSModel::Model AccessModel = DAG.getTarget().getTLSModel(GA->getGlobal());
|
||||
|
||||
switch (AccessModel) {
|
||||
case TLSModel::GeneralDynamic:
|
||||
return LowerTLSGeneralDynamic(GA, DAG);
|
||||
case TLSModel::LocalDynamic:
|
||||
return LowerTLSLocalDynamic(GA, DAG);
|
||||
case TLSModel::InitialExec:
|
||||
return LowerTLSInitialExec(GA, DAG);
|
||||
case TLSModel::LocalExec:
|
||||
return LowerTLSLocalExec(GA, DAG);
|
||||
}
|
||||
|
||||
llvm_unreachable("Unexpected TLS access model type");
|
||||
}
|
||||
|
||||
bool M68kTargetLowering::decomposeMulByConstant(LLVMContext &Context, EVT VT,
|
||||
SDValue C) const {
|
||||
// Shifts and add instructions in M68000 and M68010 support
|
||||
|
||||
@@ -245,6 +245,7 @@ private:
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
const SDLoc &DL, SelectionDAG &DAG,
|
||||
SmallVectorImpl<SDValue> &InVals) const;
|
||||
SDValue LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const;
|
||||
|
||||
/// LowerFormalArguments - transform physical registers into virtual
|
||||
/// registers and generate load operations for arguments places on the stack.
|
||||
@@ -269,6 +270,20 @@ private:
|
||||
const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
|
||||
SelectionDAG &DAG) const override;
|
||||
|
||||
SDValue LowerExternalSymbolCall(SelectionDAG &DAG, SDLoc loc,
|
||||
llvm::StringRef SymbolName,
|
||||
ArgListTy &&ArgList) const;
|
||||
SDValue getTLSGetAddr(GlobalAddressSDNode *GA, SelectionDAG &DAG,
|
||||
unsigned TargetFlags) const;
|
||||
SDValue getM68kReadTp(SDLoc Loc, SelectionDAG &DAG) const;
|
||||
|
||||
SDValue LowerTLSGeneralDynamic(GlobalAddressSDNode *GA,
|
||||
SelectionDAG &DAG) const;
|
||||
SDValue LowerTLSLocalDynamic(GlobalAddressSDNode *GA,
|
||||
SelectionDAG &DAG) const;
|
||||
SDValue LowerTLSInitialExec(GlobalAddressSDNode *GA, SelectionDAG &DAG) const;
|
||||
SDValue LowerTLSLocalExec(GlobalAddressSDNode *GA, SelectionDAG &DAG) const;
|
||||
|
||||
bool decomposeMulByConstant(LLVMContext &Context, EVT VT,
|
||||
SDValue C) const override;
|
||||
|
||||
|
||||
@@ -312,6 +312,12 @@ defm ADD : MxBiArOp_AF<"adda", MxAdd, 0xD>;
|
||||
defm SUB : MxBiArOp_DF<"sub", MxSub, 0, 0x9, 0x4>;
|
||||
defm SUB : MxBiArOp_AF<"suba", MxSub, 0x9>;
|
||||
|
||||
// This pattern is used to enable the instruction selector to select ADD32ab
|
||||
// for global values that are allocated in thread-local storage, i.e.:
|
||||
// t8: i32 = ISD::ADD GLOBAL_OFFSET_TABLE, TargetGlobalTLSAddress:i32<ptr @myvar>
|
||||
// ====>
|
||||
// t8: i32,i8 = ADD32ab GLOBAL_OFFSET_TABLE, TargetGlobalTLSAddress:i32<ptr @myvar>
|
||||
def : Pat<(add MxARD32:$src, tglobaltlsaddr:$opd), (ADD32ab MxARD32:$src, MxAL32:$opd)>;
|
||||
|
||||
let Uses = [CCR], Defs = [CCR] in {
|
||||
let Constraints = "$src = $dst" in {
|
||||
|
||||
@@ -809,7 +809,12 @@ M68kInstrInfo::getSerializableDirectMachineOperandTargetFlags() const {
|
||||
{MO_GOT, "m68k-got"},
|
||||
{MO_GOTOFF, "m68k-gotoff"},
|
||||
{MO_GOTPCREL, "m68k-gotpcrel"},
|
||||
{MO_PLT, "m68k-plt"}};
|
||||
{MO_PLT, "m68k-plt"},
|
||||
{MO_TLSGD, "m68k-tlsgd"},
|
||||
{MO_TLSLD, "m68k-tlsld"},
|
||||
{MO_TLSLDM, "m68k-tlsldm"},
|
||||
{MO_TLSIE, "m68k-tlsie"},
|
||||
{MO_TLSLE, "m68k-tlsle"}};
|
||||
return ArrayRef(TargetFlags);
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,21 @@ MCOperand M68kMCInstLower::LowerSymbolOperand(const MachineOperand &MO,
|
||||
case M68kII::MO_PLT:
|
||||
RefKind = MCSymbolRefExpr::VK_PLT;
|
||||
break;
|
||||
case M68kII::MO_TLSGD:
|
||||
RefKind = MCSymbolRefExpr::VK_TLSGD;
|
||||
break;
|
||||
case M68kII::MO_TLSLD:
|
||||
RefKind = MCSymbolRefExpr::VK_TLSLD;
|
||||
break;
|
||||
case M68kII::MO_TLSLDM:
|
||||
RefKind = MCSymbolRefExpr::VK_TLSLDM;
|
||||
break;
|
||||
case M68kII::MO_TLSIE:
|
||||
RefKind = MCSymbolRefExpr::VK_GOTTPOFF;
|
||||
break;
|
||||
case M68kII::MO_TLSLE:
|
||||
RefKind = MCSymbolRefExpr::VK_TPOFF;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!Expr) {
|
||||
|
||||
@@ -157,6 +157,37 @@ enum TOF {
|
||||
///
|
||||
/// name@PLT
|
||||
MO_PLT,
|
||||
|
||||
/// On a symbol operand, this indicates that the immediate is the offset to
|
||||
/// the slot in GOT which stores the information for accessing the TLS
|
||||
/// variable. This is used when operating in Global Dynamic mode.
|
||||
/// name@TLSGD
|
||||
MO_TLSGD,
|
||||
|
||||
/// On a symbol operand, this indicates that the immediate is the offset to
|
||||
/// variable within the thread local storage when operating in Local Dynamic
|
||||
/// mode.
|
||||
/// name@TLSLD
|
||||
MO_TLSLD,
|
||||
|
||||
/// On a symbol operand, this indicates that the immediate is the offset to
|
||||
/// the slot in GOT which stores the information for accessing the TLS
|
||||
/// variable. This is used when operating in Local Dynamic mode.
|
||||
/// name@TLSLDM
|
||||
MO_TLSLDM,
|
||||
|
||||
/// On a symbol operand, this indicates that the immediate is the offset to
|
||||
/// the variable within the thread local storage when operating in Initial
|
||||
/// Exec mode.
|
||||
/// name@TLSIE
|
||||
MO_TLSIE,
|
||||
|
||||
/// On a symbol operand, this indicates that the immediate is the offset to
|
||||
/// the variable within in the thread local storage when operating in Local
|
||||
/// Exec mode.
|
||||
/// name@TLSLE
|
||||
MO_TLSLE,
|
||||
|
||||
}; // enum TOF
|
||||
|
||||
/// Return true if the specified TargetFlag operand is a reference to a stub
|
||||
|
||||
@@ -70,6 +70,57 @@ unsigned M68kELFObjectWriter::getRelocType(MCContext &Ctx,
|
||||
switch (Modifier) {
|
||||
default:
|
||||
llvm_unreachable("Unimplemented");
|
||||
|
||||
case MCSymbolRefExpr::VK_TLSGD:
|
||||
switch (Type) {
|
||||
case RT_32:
|
||||
return ELF::R_68K_TLS_GD32;
|
||||
case RT_16:
|
||||
return ELF::R_68K_TLS_GD16;
|
||||
case RT_8:
|
||||
return ELF::R_68K_TLS_GD8;
|
||||
}
|
||||
llvm_unreachable("Unrecognized size");
|
||||
case MCSymbolRefExpr::VK_TLSLDM:
|
||||
switch (Type) {
|
||||
case RT_32:
|
||||
return ELF::R_68K_TLS_LDM32;
|
||||
case RT_16:
|
||||
return ELF::R_68K_TLS_LDM16;
|
||||
case RT_8:
|
||||
return ELF::R_68K_TLS_LDM8;
|
||||
}
|
||||
llvm_unreachable("Unrecognized size");
|
||||
case MCSymbolRefExpr::VK_TLSLD:
|
||||
switch (Type) {
|
||||
case RT_32:
|
||||
return ELF::R_68K_TLS_LDO32;
|
||||
case RT_16:
|
||||
return ELF::R_68K_TLS_LDO16;
|
||||
case RT_8:
|
||||
return ELF::R_68K_TLS_LDO8;
|
||||
}
|
||||
llvm_unreachable("Unrecognized size");
|
||||
case MCSymbolRefExpr::VK_GOTTPOFF:
|
||||
switch (Type) {
|
||||
case RT_32:
|
||||
return ELF::R_68K_TLS_IE32;
|
||||
case RT_16:
|
||||
return ELF::R_68K_TLS_IE16;
|
||||
case RT_8:
|
||||
return ELF::R_68K_TLS_IE8;
|
||||
}
|
||||
llvm_unreachable("Unrecognized size");
|
||||
case MCSymbolRefExpr::VK_TPOFF:
|
||||
switch (Type) {
|
||||
case RT_32:
|
||||
return ELF::R_68K_TLS_LE32;
|
||||
case RT_16:
|
||||
return ELF::R_68K_TLS_LE16;
|
||||
case RT_8:
|
||||
return ELF::R_68K_TLS_LE8;
|
||||
}
|
||||
llvm_unreachable("Unrecognized size");
|
||||
case MCSymbolRefExpr::VK_None:
|
||||
switch (Type) {
|
||||
case RT_32:
|
||||
|
||||
21
llvm/test/CodeGen/M68k/TLS/tlsgd.ll
Normal file
21
llvm/test/CodeGen/M68k/TLS/tlsgd.ll
Normal file
@@ -0,0 +1,21 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
||||
; RUN: llc -mtriple=m68k --relocation-model=pic -o - %s | FileCheck %s
|
||||
|
||||
@myvar = external thread_local global i32, align 4
|
||||
|
||||
define ptr @get_addr() nounwind {
|
||||
; CHECK-LABEL: get_addr:
|
||||
; CHECK: ; %bb.0: ; %entry
|
||||
; CHECK-NEXT: suba.l #4, %sp
|
||||
; CHECK-NEXT: lea (_GLOBAL_OFFSET_TABLE_@GOTPCREL,%pc), %a0
|
||||
; CHECK-NEXT: adda.l myvar@TLSGD, %a0
|
||||
; CHECK-NEXT: move.l %a0, (%sp)
|
||||
; CHECK-NEXT: jsr (__tls_get_addr@PLT,%pc)
|
||||
; CHECK-NEXT: adda.l #4, %sp
|
||||
; CHECK-NEXT: rts
|
||||
entry:
|
||||
%0 = call align 4 ptr @llvm.threadlocal.address.p0(ptr align 4 @myvar)
|
||||
ret ptr %0
|
||||
}
|
||||
|
||||
declare nonnull ptr @llvm.threadlocal.address.p0(ptr nonnull)
|
||||
23
llvm/test/CodeGen/M68k/TLS/tlsie.ll
Normal file
23
llvm/test/CodeGen/M68k/TLS/tlsie.ll
Normal file
@@ -0,0 +1,23 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
||||
; RUN: llc -mtriple=m68k -o - %s | FileCheck %s
|
||||
|
||||
@myvar = external thread_local global i32, align 4
|
||||
|
||||
define dso_local ptr @get_addr() nounwind {
|
||||
; CHECK-LABEL: get_addr:
|
||||
; CHECK: ; %bb.0: ; %entry
|
||||
; CHECK-NEXT: suba.l #4, %sp
|
||||
; CHECK-NEXT: jsr __m68k_read_tp@PLT
|
||||
; CHECK-NEXT: move.l %a0, %d0
|
||||
; CHECK-NEXT: lea (_GLOBAL_OFFSET_TABLE_@GOTPCREL,%pc), %a0
|
||||
; CHECK-NEXT: add.l (0,myvar@GOTTPOFF,%a0), %d0
|
||||
; CHECK-NEXT: move.l %d0, %a0
|
||||
; CHECK-NEXT: adda.l #4, %sp
|
||||
; CHECK-NEXT: rts
|
||||
|
||||
entry:
|
||||
%0 = call align 4 ptr @llvm.threadlocal.address.p0(ptr align 4 @myvar)
|
||||
ret ptr %0
|
||||
}
|
||||
|
||||
declare nonnull ptr @llvm.threadlocal.address.p0(ptr nonnull)
|
||||
22
llvm/test/CodeGen/M68k/TLS/tlsld.ll
Normal file
22
llvm/test/CodeGen/M68k/TLS/tlsld.ll
Normal file
@@ -0,0 +1,22 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
||||
; RUN: llc -mtriple=m68k --relocation-model=pic -o - %s | FileCheck %s
|
||||
|
||||
@myvar = internal thread_local global i32 2, align 4
|
||||
|
||||
define dso_local ptr @get_addr() nounwind {
|
||||
; CHECK-LABEL: get_addr:
|
||||
; CHECK: ; %bb.0: ; %entry
|
||||
; CHECK-NEXT: suba.l #4, %sp
|
||||
; CHECK-NEXT: lea (_GLOBAL_OFFSET_TABLE_@GOTPCREL,%pc), %a0
|
||||
; CHECK-NEXT: adda.l myvar@TLSLDM, %a0
|
||||
; CHECK-NEXT: move.l %a0, (%sp)
|
||||
; CHECK-NEXT: jsr (__tls_get_addr@PLT,%pc)
|
||||
; CHECK-NEXT: adda.l myvar@TLSLD, %a0
|
||||
; CHECK-NEXT: adda.l #4, %sp
|
||||
; CHECK-NEXT: rts
|
||||
entry:
|
||||
%0 = call align 4 ptr @llvm.threadlocal.address.p0(ptr align 4 @myvar)
|
||||
ret ptr %0
|
||||
}
|
||||
|
||||
declare nonnull ptr @llvm.threadlocal.address.p0(ptr nonnull)
|
||||
19
llvm/test/CodeGen/M68k/TLS/tlsle.ll
Normal file
19
llvm/test/CodeGen/M68k/TLS/tlsle.ll
Normal file
@@ -0,0 +1,19 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
||||
; RUN: llc -mtriple=m68k -o - %s | FileCheck %s
|
||||
|
||||
@myvar = internal thread_local global i32 2, align 4
|
||||
|
||||
define dso_local ptr @get_addr() nounwind {
|
||||
; CHECK-LABEL: get_addr:
|
||||
; CHECK: ; %bb.0: ; %entry
|
||||
; CHECK-NEXT: suba.l #4, %sp
|
||||
; CHECK-NEXT: jsr __m68k_read_tp@PLT
|
||||
; CHECK-NEXT: adda.l myvar@TPOFF, %a0
|
||||
; CHECK-NEXT: adda.l #4, %sp
|
||||
; CHECK-NEXT: rts
|
||||
entry:
|
||||
%0 = call align 4 ptr @llvm.threadlocal.address.p0(ptr align 4 @myvar)
|
||||
ret ptr %0
|
||||
}
|
||||
|
||||
declare nonnull ptr @llvm.threadlocal.address.p0(ptr nonnull)
|
||||
Reference in New Issue
Block a user