Index: llvm/lib/Target/M68k/M68kISelDAGToDAG.cpp =================================================================== --- llvm/lib/Target/M68k/M68kISelDAGToDAG.cpp +++ llvm/lib/Target/M68k/M68kISelDAGToDAG.cpp @@ -666,6 +666,14 @@ default: break; + case ISD::GLOBAL_OFFSET_TABLE: { + auto GOT = CurDAG->getTargetExternalSymbol("_GLOBAL_OFFSET_TABLE_", + MVT::i32, M68kII::MO_GOTPCREL); + auto *Res = CurDAG->getMachineNode(M68k::LEA32q, DL, MVT::i32, GOT); + ReplaceNode(Node, Res); + return; + } + case M68kISD::GLOBAL_BASE_REG: ReplaceNode(Node, getGlobalBaseReg()); return; Index: llvm/lib/Target/M68k/M68kISelLowering.h =================================================================== --- llvm/lib/Target/M68k/M68kISelLowering.h +++ llvm/lib/Target/M68k/M68kISelLowering.h @@ -245,6 +245,7 @@ const SmallVectorImpl &Ins, const SDLoc &DL, SelectionDAG &DAG, SmallVectorImpl &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. @@ -264,6 +265,20 @@ const SmallVectorImpl &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; Index: llvm/lib/Target/M68k/M68kISelLowering.cpp =================================================================== --- llvm/lib/Target/M68k/M68kISelLowering.cpp +++ llvm/lib/Target/M68k/M68kISelLowering.cpp @@ -1412,9 +1412,108 @@ 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(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 Index: llvm/lib/Target/M68k/M68kInstrArithmetic.td =================================================================== --- llvm/lib/Target/M68k/M68kInstrArithmetic.td +++ llvm/lib/Target/M68k/M68kInstrArithmetic.td @@ -312,6 +312,12 @@ 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 +// ====> +// t8: i32,i8 = ADD32ab GLOBAL_OFFSET_TABLE, TargetGlobalTLSAddress:i32 +def : Pat<(add MxARD32:$src, tglobaltlsaddr:$opd), (ADD32ab MxARD32:$src, MxAL32:$opd)>; let Uses = [CCR], Defs = [CCR] in { let Constraints = "$src = $dst" in { Index: llvm/lib/Target/M68k/M68kInstrInfo.cpp =================================================================== --- llvm/lib/Target/M68k/M68kInstrInfo.cpp +++ llvm/lib/Target/M68k/M68kInstrInfo.cpp @@ -809,7 +809,12 @@ {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); } Index: llvm/lib/Target/M68k/M68kMCInstLower.cpp =================================================================== --- llvm/lib/Target/M68k/M68kMCInstLower.cpp +++ llvm/lib/Target/M68k/M68kMCInstLower.cpp @@ -96,6 +96,21 @@ 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) { Index: llvm/lib/Target/M68k/MCTargetDesc/M68kBaseInfo.h =================================================================== --- llvm/lib/Target/M68k/MCTargetDesc/M68kBaseInfo.h +++ llvm/lib/Target/M68k/MCTargetDesc/M68kBaseInfo.h @@ -157,6 +157,37 @@ /// /// 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 Index: llvm/lib/Target/M68k/MCTargetDesc/M68kELFObjectWriter.cpp =================================================================== --- llvm/lib/Target/M68k/MCTargetDesc/M68kELFObjectWriter.cpp +++ llvm/lib/Target/M68k/MCTargetDesc/M68kELFObjectWriter.cpp @@ -70,6 +70,57 @@ 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: Index: llvm/test/CodeGen/M68k/TLS/tlsgd.ll =================================================================== --- /dev/null +++ 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) Index: llvm/test/CodeGen/M68k/TLS/tlsie.ll =================================================================== --- /dev/null +++ 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) Index: llvm/test/CodeGen/M68k/TLS/tlsld.ll =================================================================== --- /dev/null +++ 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) Index: llvm/test/CodeGen/M68k/TLS/tlsle.ll =================================================================== --- /dev/null +++ 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) Index: llvm/utils/UpdateTestChecks/asm.py =================================================================== --- llvm/utils/UpdateTestChecks/asm.py +++ llvm/utils/UpdateTestChecks/asm.py @@ -63,6 +63,8 @@ ASM_FUNCTION_M68K_RE = re.compile( r'^_?(?P[^:]+):[ \t]*;[ \t]*@"?(?P=func)"?\n' + r'(?:\.L(?P=func)\$local:\n)?' # drop .L$local: + r'(?:[ \t]+\.type[ \t]+\.L(?P=func)\$local,@function\n)?' # drop .type .L$local,@funciton r'(?P.*?)\s*' # (body of the function) r'.Lfunc_end[0-9]+:\n', flags=(re.M | re.S))