This patch introduces TLS (Thread-Local Storage) support to the LLVM m68k backend.
Reviewed By: glaubitz
Differential Revision: https://reviews.llvm.org/D144941
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;
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
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.
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;
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 {
{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);
}
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) {
///
/// 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
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:
--- /dev/null
+; 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)
--- /dev/null
+; 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)
--- /dev/null
+; 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)
--- /dev/null
+; 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)