Index: llvm/lib/Target/M68k/M68kAsmPrinter.h =================================================================== --- llvm/lib/Target/M68k/M68kAsmPrinter.h +++ llvm/lib/Target/M68k/M68kAsmPrinter.h @@ -55,6 +55,10 @@ virtual bool runOnMachineFunction(MachineFunction &MF) override; + void LowerTLSDynamic(const MachineInstr *MI, bool isGeneralDynamic); + void LowerTLSExec(const MachineInstr *MI, bool isInitialExec); + void LowerTLS(const MachineInstr *MI); + bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, const char *ExtraCode, raw_ostream &OS) override; Index: llvm/lib/Target/M68k/M68kAsmPrinter.cpp =================================================================== --- llvm/lib/Target/M68k/M68kAsmPrinter.cpp +++ llvm/lib/Target/M68k/M68kAsmPrinter.cpp @@ -21,6 +21,7 @@ #include "MCTargetDesc/M68kInstPrinter.h" #include "TargetInfo/M68kTargetInfo.h" +#include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/TargetRegistry.h" using namespace llvm; @@ -76,6 +77,137 @@ return AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS); } +static void EmitLoadGOTInstr(MCStreamer *Streamer, MCContext &Ctx, + const MCSubtargetInfo &SI) { + const auto *GOTRefExpr = MCSymbolRefExpr::create( + "_GLOBAL_OFFSET_TABLE_", MCSymbolRefExpr::VK_GOTPCREL, Ctx); + Streamer->emitInstruction(MCInstBuilder(M68k::LEA32q) + .addReg(M68k::A5) + .addExpr(GOTRefExpr) + .addReg(M68k::A5), + SI); +} + +void M68kAsmPrinter::LowerTLSDynamic(const MachineInstr *MI, + bool isGeneralDynamic) { + // General Dynamic: + // lea (%pc, _GLOBAL_OFFSET_TABLE_@GOTPC),%a5 + // adda.l #myvar@TLSGD,%a5 + // move.l %a5,-(%sp) + // bsr.l __tls_get_addr@PLTPC + // add.l #4, %sp + + // Local Dynamic: + // lea (%pc, _GLOBAL_OFFSET_TABLE_@GOTPC), %a5 + // adda.l #myvar@TLSLDM,%a5 + // move.l %a5,-(%sp) + // bsr.l __tls_get_addr@PLTPC + // add.l #4,%sp + // adda.l #myvar@TLSLDO,%a0 + + MCContext &Ctx = OutContext; + const MCSubtargetInfo &SI = getSubtargetInfo(); + const MachineOperand &MO = MI->getOperand(0); + assert(MO.isGlobal()); + + const auto *TLSRefExpr = MCSymbolRefExpr::create( + MO.getGlobal()->getName(), + isGeneralDynamic ? MCSymbolRefExpr::VK_TLSGD : MCSymbolRefExpr::VK_TLSLDM, + Ctx); + const auto *TlsGetAddr = + MCSymbolRefExpr::create("__tls_get_addr", MCSymbolRefExpr::VK_PLT, Ctx); + + EmitLoadGOTInstr(OutStreamer.get(), Ctx, SI); + + OutStreamer->emitInstruction(MCInstBuilder(M68k::ADD32ab) + .addReg(M68k::A5) + .addReg(M68k::A5) + .addExpr(TLSRefExpr), + SI); + OutStreamer->emitInstruction( + MCInstBuilder(M68k::MOV32ea).addReg(M68k::SP).addReg(M68k::A5), SI); + OutStreamer->emitInstruction(MCInstBuilder(M68k::BSR32).addExpr(TlsGetAddr), + SI); + OutStreamer->emitInstruction( + MCInstBuilder(M68k::ADD32ai).addReg(M68k::SP).addReg(M68k::SP).addImm(4), + SI); + + if (isGeneralDynamic) + return; + + TLSRefExpr = MCSymbolRefExpr::create(MO.getGlobal()->getName(), + MCSymbolRefExpr::VK_TLSLD, Ctx); + OutStreamer->emitInstruction(MCInstBuilder(M68k::ADD32ab) + .addReg(M68k::A0) + .addReg(M68k::A0) + .addExpr(TLSRefExpr), + SI); +} + +void M68kAsmPrinter::LowerTLSExec(const MachineInstr *MI, bool isInitialExec) { + // Initial Exec: + // jsr __m68k_read_tp + // lea (%pc, _GLOBAL_OFFSET_TABLE_@GOTPC),%a5 + // adda.l myvar@TLSIE,%a5 + // adda.l (%a5),%a0 + + // Local Exec: + // jsr __m68k_read_tp + // adda.l #myvar@TLSLE,%a0 + + MCContext &Ctx = OutContext; + const MCSubtargetInfo &SI = getSubtargetInfo(); + const MachineOperand &MO = MI->getOperand(0); + assert(MO.isGlobal()); + + const auto *ReadTpExpr = + MCSymbolRefExpr::create("__m68k_read_tp", MCSymbolRefExpr::VK_None, Ctx); + OutStreamer->emitInstruction(MCInstBuilder(M68k::CALLb).addExpr(ReadTpExpr), + SI); + + const auto *TLSRefExpr = MCSymbolRefExpr::create( + MO.getGlobal()->getName(), + isInitialExec ? MCSymbolRefExpr::VK_GOTTPOFF : MCSymbolRefExpr::VK_TPOFF, + Ctx); + + if (isInitialExec) { + EmitLoadGOTInstr(OutStreamer.get(), Ctx, SI); + OutStreamer->emitInstruction(MCInstBuilder(M68k::ADD32ab) + .addReg(M68k::A5) + .addReg(M68k::A5) + .addExpr(TLSRefExpr), + SI); + OutStreamer->emitInstruction(MCInstBuilder(M68k::ADD32aj) + .addReg(M68k::A0) + .addReg(M68k::A0) + .addReg(M68k::A5), + SI); + return; + } + + OutStreamer->emitInstruction(MCInstBuilder(M68k::ADD32ab) + .addReg(M68k::A0) + .addReg(M68k::A0) + .addExpr(TLSRefExpr), + SI); +} + +void M68kAsmPrinter::LowerTLS(const MachineInstr *MI) { + switch (MI->getOpcode()) { + case M68k::TLS_GD: + return LowerTLSDynamic(MI, /*isGeneralDynamic*/ true); + case M68k::TLS_LD: + return LowerTLSDynamic(MI, /*isGeneralDynamic*/ false); + case M68k::TLS_IE: + return LowerTLSExec(MI, /*isInitialExec*/ true); + case M68k::TLS_LE: + return LowerTLSExec(MI, /*isInitalExec*/ false); + + default: + llvm_unreachable("unexpected flag"); + } +} + void M68kAsmPrinter::emitInstruction(const MachineInstr *MI) { M68k_MC::verifyInstructionPredicates(MI->getOpcode(), getSubtargetInfo().getFeatureBits()); @@ -94,6 +226,12 @@ // Lower these as normal, but add some comments. OutStreamer->AddComment("TAILCALL"); break; + case M68k::TLS_GD: + case M68k::TLS_LD: + case M68k::TLS_IE: + case M68k::TLS_LE: + LowerTLS(MI); + return; } MCInst TmpInst0; Index: llvm/lib/Target/M68k/M68kISelDAGToDAG.cpp =================================================================== --- llvm/lib/Target/M68k/M68kISelDAGToDAG.cpp +++ llvm/lib/Target/M68k/M68kISelDAGToDAG.cpp @@ -226,7 +226,7 @@ bool SelectAL(SDNode *Parent, SDValue N, SDValue &Sym); bool SelectPCD(SDNode *Parent, SDValue N, SDValue &Imm); bool SelectPCI(SDNode *Parent, SDValue N, SDValue &Imm, SDValue &Index); - + void SelectTLS(SDNode *Node, SDLoc DL, unsigned Opcode); // If Address Mode represents Frame Index store FI in Disp and // Displacement bit size in Base. These values are read symmetrically by // M68kRegisterInfo::eliminateFrameIndex method @@ -666,9 +666,26 @@ case M68kISD::GLOBAL_BASE_REG: ReplaceNode(Node, getGlobalBaseReg()); return; + + case M68kISD::TLS_GD: + return SelectTLS(Node, DL, M68k::TLS_GD); + case M68kISD::TLS_LD: + return SelectTLS(Node, DL, M68k::TLS_LD); + case M68kISD::TLS_IE: + return SelectTLS(Node, DL, M68k::TLS_IE); + case M68kISD::TLS_LE: + return SelectTLS(Node, DL, M68k::TLS_LE); } SelectCode(Node); + LLVM_DEBUG(CurDAG->dump()); +} + +void M68kDAGToDAGISel::SelectTLS(SDNode *Node, SDLoc DL, unsigned Opcode) { + auto *NewNode = CurDAG->getMachineNode(Opcode, DL, Node->getValueType(0), + Node->getOperand(0)); + ReplaceNode(Node, NewNode); + return; } bool M68kDAGToDAGISel::SelectARIPI(SDNode *Parent, SDValue N, SDValue &Base) { Index: llvm/lib/Target/M68k/M68kISelLowering.h =================================================================== --- llvm/lib/Target/M68k/M68kISelLowering.h +++ llvm/lib/Target/M68k/M68kISelLowering.h @@ -94,6 +94,12 @@ // segmented stacks. Check if the current stacklet has enough space, and // falls back to heap allocation if not. SEG_ALLOCA, + + /// Thread local storage support. + TLS_GD, + TLS_LD, + TLS_IE, + TLS_LE, }; } // namespace M68kISD @@ -231,6 +237,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. Index: llvm/lib/Target/M68k/M68kISelLowering.cpp =================================================================== --- llvm/lib/Target/M68k/M68kISelLowering.cpp +++ llvm/lib/Target/M68k/M68kISelLowering.cpp @@ -1389,9 +1389,58 @@ return LowerShiftRightParts(Op, DAG, true); case ISD::SRL_PARTS: return LowerShiftRightParts(Op, DAG, false); + case ISD::GlobalTLSAddress: + return LowerGlobalTLSAddress(Op, DAG); } } +static SDValue LowerTLS(GlobalAddressSDNode *GA, SelectionDAG &DAG, + unsigned Opcode) { + SDLoc DL(GA); + SDValue TGA = DAG.getTargetGlobalAddress( + GA->getGlobal(), GA, GA->getValueType(0), GA->getOffset()); + return DAG.getNode(Opcode, DL, GA->getValueType(0), TGA); +} + +static SDValue LowerTLSGeneralDynamic(GlobalAddressSDNode *GA, + SelectionDAG &DAG) { + return LowerTLS(GA, DAG, M68kISD::TLS_GD); +} + +static SDValue LowerTLSLocalDynamic(GlobalAddressSDNode *GA, + SelectionDAG &DAG) { + return LowerTLS(GA, DAG, M68kISD::TLS_LD); +} + +static SDValue LowerTLSInitialExec(GlobalAddressSDNode *GA, SelectionDAG &DAG) { + return LowerTLS(GA, DAG, M68kISD::TLS_IE); +} + +static SDValue LowerTLSLocalExec(GlobalAddressSDNode *GA, SelectionDAG &DAG) { + return LowerTLS(GA, DAG, M68kISD::TLS_LE); +} + +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("should not be here"); +} + bool M68kTargetLowering::decomposeMulByConstant(LLVMContext &Context, EVT VT, SDValue C) const { // Shifts and add instructions in M68000 and M68010 support @@ -3541,6 +3590,14 @@ return "M68kISD::WrapperPC"; case M68kISD::SEG_ALLOCA: return "M68kISD::SEG_ALLOCA"; + case M68kISD::TLS_GD: + return "M68kISD::TLS_GD"; + case M68kISD::TLS_LD: + return "M68kISD::TLS_LD"; + case M68kISD::TLS_IE: + return "M68kISD::TLS_IE"; + case M68kISD::TLS_LE: + return "M68kISD::TLS_LE"; default: return NULL; } Index: llvm/lib/Target/M68k/M68kInstrCompiler.td =================================================================== --- llvm/lib/Target/M68k/M68kInstrCompiler.td +++ llvm/lib/Target/M68k/M68kInstrCompiler.td @@ -124,3 +124,16 @@ let usesCustomInserter = 1 in def SALLOCA : MxPseudo<(outs MxARD32:$dst), (ins MxARD32:$size), [(set iPTR:$dst, (MxSegAlloca iPTR:$size))]>; + +//===----------------------------------------------------------------------===// +// Thread local storage support +//===----------------------------------------------------------------------===// + +let isCall = 1, Defs = [A0, A5], mayLoad = 1 in { + +def TLS_GD : MxPseudo<(outs), (ins MxAL32:$sym)>; +def TLS_LD : MxPseudo<(outs), (ins MxAL32:$sym)>; +def TLS_IE : MxPseudo<(outs), (ins MxAL32:$sym)>; +def TLS_LE : MxPseudo<(outs), (ins MxAL32:$sym)>; + +} Index: llvm/lib/Target/M68k/M68kInstrInfo.cpp =================================================================== --- llvm/lib/Target/M68k/M68kInstrInfo.cpp +++ llvm/lib/Target/M68k/M68kInstrInfo.cpp @@ -809,7 +809,11 @@ {MO_GOT, "m68k-got"}, {MO_GOTOFF, "m68k-gotoff"}, {MO_GOTPCREL, "m68k-gotpcrel"}, - {MO_PLT, "m68k-plt"}}; + {MO_PLT, "m68k-plt"}, + {MO_TLS_GD, "m68k-tlsgd"}, + {MO_TLS_LD, "m68k-tlsld"}, + {MO_TLS_IE, "m68k-tlsie"}, + {MO_TLS_LE, "m68k-tlsle"}}; return ArrayRef(TargetFlags); } Index: llvm/lib/Target/M68k/MCTargetDesc/M68kBaseInfo.h =================================================================== --- llvm/lib/Target/M68k/MCTargetDesc/M68kBaseInfo.h +++ llvm/lib/Target/M68k/MCTargetDesc/M68kBaseInfo.h @@ -131,6 +131,11 @@ /// /// name@PLT MO_PLT, + + MO_TLS_GD, + MO_TLS_LD, + MO_TLS_IE, + MO_TLS_LE, }; // 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 @@ -67,9 +67,29 @@ MCSymbolRefExpr::VariantKind Modifier = Target.getAccessVariant(); unsigned Kind = Fixup.getKind(); M68kRelType Type = getType(Kind, Modifier, IsPCRel); + +#define MAP_RELOC_TYPE(KIND, TYPE) \ + case MCSymbolRefExpr::KIND: \ + switch (Type) { \ + case RT_32: \ + return ELF::TYPE##32; \ + case RT_16: \ + return ELF::TYPE##16; \ + case RT_8: \ + return ELF::TYPE##8; \ + } \ + llvm_unreachable("Unrecognized size"); + switch (Modifier) { default: llvm_unreachable("Unimplemented"); + + MAP_RELOC_TYPE(VK_TLSGD, R_68K_TLS_GD) + MAP_RELOC_TYPE(VK_TLSLDM, R_68K_TLS_LDM) + MAP_RELOC_TYPE(VK_TLSLD, R_68K_TLS_LDO) + MAP_RELOC_TYPE(VK_GOTTPOFF, R_68K_TLS_IE) + MAP_RELOC_TYPE(VK_TPOFF, R_68K_TLS_LE) + 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,34 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=m68k --relocation-model=pic -o - %s | FileCheck --check-prefix=CODEGEN %s +; RUN: llc -mtriple=m68k --relocation-model=pic -o - %s --filetype=obj \ +; RUN: | llvm-readobj -r - | FileCheck --check-prefix=RELOC %s + +@myvar = external thread_local global i32, align 4 + +define ptr @get_addr() { +; CODEGEN-LABEL: get_addr: +; CODEGEN: .cfi_startproc +; CODEGEN-NEXT: ; %bb.0: ; %entry +; CODEGEN-NEXT: suba.l #4, %sp +; CODEGEN-NEXT: .cfi_def_cfa_offset -8 +; CODEGEN-NEXT: movem.l %a5, (0,%sp) ; 8-byte Folded Spill +; CODEGEN-NEXT: lea (_GLOBAL_OFFSET_TABLE_@GOTPCREL,%pc), %a5 +; CODEGEN-NEXT: adda.l myvar@TLSGD, %a5 +; CODEGEN-NEXT: move.l %a5, -(%sp) +; CODEGEN-NEXT: bsr.l __tls_get_addr@PLT +; CODEGEN-NEXT: adda.l #4, %sp +; CODEGEN-NEXT: move.l %a0, %d0 +; CODEGEN-NEXT: movem.l (0,%sp), %a5 ; 8-byte Folded Reload +; CODEGEN-NEXT: adda.l #4, %sp +; CODEGEN-NEXT: rts + +; RELOC-LABEL: Section (3) .rela.text +; RELOC-NEXT: 0xE R_68K_GOTPCREL16 _GLOBAL_OFFSET_TABLE_ 0x0 +; RELOC-NEXT: 0x12 R_68K_TLS_GD32 myvar 0x0 +; RELOC-NEXT: 0x1A R_68K_PLT32 __tls_get_addr 0x0 +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,33 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=m68k -o - %s | FileCheck --check-prefix=CODEGEN %s +; RUN: llc -mtriple=m68k -o - %s --filetype=obj \ +; RUN: | llvm-readobj -r - | FileCheck --check-prefix=RELOC %s + +@myvar = external thread_local global i32, align 4 + +define dso_local ptr @get_addr() { +; CODEGEN-LABEL: get_addr: +; CODEGEN: .cfi_startproc +; CODEGEN-NEXT: ; %bb.0: ; %entry +; CODEGEN-NEXT: suba.l #4, %sp +; CODEGEN-NEXT: .cfi_def_cfa_offset -8 +; CODEGEN-NEXT: movem.l %a5, (0,%sp) ; 8-byte Folded Spill +; CODEGEN-NEXT: jsr __m68k_read_tp +; CODEGEN-NEXT: lea (_GLOBAL_OFFSET_TABLE_@GOTPCREL,%pc), %a5 +; CODEGEN-NEXT: adda.l myvar@GOTTPOFF, %a5 +; CODEGEN-NEXT: adda.l (%a5), %a0 +; CODEGEN-NEXT: move.l %a0, %d0 +; CODEGEN-NEXT: movem.l (0,%sp), %a5 ; 8-byte Folded Reload +; CODEGEN-NEXT: adda.l #4, %sp +; CODEGEN-NEXT: rts + +; RELOC-LABEL: Section (3) .rela.text +; RELOC-NEXT: 0xE R_68K_32 __m68k_read_tp 0x0 +; RELOC-NEXT: 0x14 R_68K_GOTPCREL16 _GLOBAL_OFFSET_TABLE_ 0x0 +; RELOC-NEXT: 0x18 R_68K_TLS_IE32 myvar 0x0 +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,38 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=m68k --relocation-model=pic -o - %s | FileCheck --check-prefix=CODEGEN %s +; RUN: llc -mtriple=m68k --relocation-model=pic -o - %s --filetype=obj \ +; RUN: | llvm-readobj -r - | FileCheck --check-prefix=RELOC %s + +@myvar = internal thread_local global i32 2, align 4 + +define dso_local ptr @get_addr() { +; CODEGEN-LABEL: get_addr: +; CODEGEN: .Lget_addr$local: +; CODEGEN-NEXT: .type .Lget_addr$local,@function +; CODEGEN-NEXT: .cfi_startproc +; CODEGEN-NEXT: ; %bb.0: ; %entry +; CODEGEN-NEXT: suba.l #4, %sp +; CODEGEN-NEXT: .cfi_def_cfa_offset -8 +; CODEGEN-NEXT: movem.l %a5, (0,%sp) ; 8-byte Folded Spill +; CODEGEN-NEXT: lea (_GLOBAL_OFFSET_TABLE_@GOTPCREL,%pc), %a5 +; CODEGEN-NEXT: adda.l myvar@TLSLDM, %a5 +; CODEGEN-NEXT: move.l %a5, -(%sp) +; CODEGEN-NEXT: bsr.l __tls_get_addr@PLT +; CODEGEN-NEXT: adda.l #4, %sp +; CODEGEN-NEXT: adda.l myvar@TLSLD, %a0 +; CODEGEN-NEXT: move.l %a0, %d0 +; CODEGEN-NEXT: movem.l (0,%sp), %a5 ; 8-byte Folded Reload +; CODEGEN-NEXT: adda.l #4, %sp +; CODEGEN-NEXT: rts + +; RELOC-LABEL: Section (3) .rela.text { +; RELOC-NEXT: 0xE R_68K_GOTPCREL16 _GLOBAL_OFFSET_TABLE_ 0x0 +; RELOC-NEXT: 0x12 R_68K_TLS_LDM32 myvar 0x0 +; RELOC-NEXT: 0x1A R_68K_PLT32 __tls_get_addr 0x0 +; RELOC-NEXT: 0x26 R_68K_TLS_LDO32 myvar 0x0 +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,31 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=m68k -o - %s | FileCheck --check-prefix=CODEGEN %s +; RUN: llc -mtriple=m68k -o - %s --filetype=obj \ +; RUN: | llvm-readobj -r - | FileCheck --check-prefix=RELOC %s + +@myvar = internal thread_local global i32 2, align 4 + +define dso_local ptr @get_addr() { +; CODEGEN-LABEL: get_addr: +; CODEGEN: .cfi_startproc +; CODEGEN-NEXT: ; %bb.0: ; %entry +; CODEGEN-NEXT: suba.l #4, %sp +; CODEGEN-NEXT: .cfi_def_cfa_offset -8 +; CODEGEN-NEXT: movem.l %a5, (0,%sp) ; 8-byte Folded Spill +; CODEGEN-NEXT: jsr __m68k_read_tp +; CODEGEN-NEXT: adda.l myvar@TPOFF, %a0 +; CODEGEN-NEXT: move.l %a0, %d0 +; CODEGEN-NEXT: movem.l (0,%sp), %a5 ; 8-byte Folded Reload +; CODEGEN-NEXT: adda.l #4, %sp +; CODEGEN-NEXT: rts + +; RELOC-LABEL: Section (3) .rela.text +; RELOC-NEXT: 0xE R_68K_32 __m68k_read_tp 0x0 +; RELOC-NEXT: 0x14 R_68K_TLS_LE32 myvar 0x0 + +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)