Skip to content

Commit

Permalink
[m68k] Add TLS Support
Browse files Browse the repository at this point in the history
This patch introduces TLS (Thread-Local Storage) support to the LLVM m68k backend.

Reviewed By: glaubitz

Differential Revision: https://reviews.llvm.org/D144941
  • Loading branch information
0x59616e committed Jun 3, 2023
1 parent 40d89de commit 4c2ec08
Show file tree
Hide file tree
Showing 12 changed files with 317 additions and 1 deletion.
9 changes: 9 additions & 0 deletions llvm/lib/Target/M68k/M68kISelDAGToDAG.cpp
Expand Up @@ -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;
Expand Down
99 changes: 99 additions & 0 deletions llvm/lib/Target/M68k/M68kISelLowering.cpp
Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions llvm/lib/Target/M68k/M68kISelLowering.h
Expand Up @@ -245,6 +245,7 @@ class M68kTargetLowering : public TargetLowering {
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.
Expand All @@ -269,6 +270,20 @@ class M68kTargetLowering : public TargetLowering {
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;

Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Target/M68k/M68kInstrArithmetic.td
Expand Up @@ -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 {
Expand Down
7 changes: 6 additions & 1 deletion llvm/lib/Target/M68k/M68kInstrInfo.cpp
Expand Up @@ -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);
}

Expand Down
15 changes: 15 additions & 0 deletions llvm/lib/Target/M68k/M68kMCInstLower.cpp
Expand Up @@ -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) {
Expand Down
31 changes: 31 additions & 0 deletions llvm/lib/Target/M68k/MCTargetDesc/M68kBaseInfo.h
Expand Up @@ -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
Expand Down
51 changes: 51 additions & 0 deletions llvm/lib/Target/M68k/MCTargetDesc/M68kELFObjectWriter.cpp
Expand Up @@ -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:
Expand Down
21 changes: 21 additions & 0 deletions llvm/test/CodeGen/M68k/TLS/tlsgd.ll
@@ -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 changes: 23 additions & 0 deletions llvm/test/CodeGen/M68k/TLS/tlsie.ll
@@ -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 changes: 22 additions & 0 deletions llvm/test/CodeGen/M68k/TLS/tlsld.ll
@@ -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 changes: 19 additions & 0 deletions llvm/test/CodeGen/M68k/TLS/tlsle.ll
@@ -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)

0 comments on commit 4c2ec08

Please sign in to comment.