[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:
Sheng
2023-06-03 18:05:42 +08:00
parent 40d89de4c5
commit 4c2ec08ebc
12 changed files with 317 additions and 1 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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:

View 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)

View 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)

View 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)

View 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)