diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -2132,7 +2132,8 @@ getSectionForFunctionDescriptor(cast(GO), TM)) ->getQualNameSymbol(); if ((TM.getDataSections() && !GO->hasSection()) || GOKind.isCommon() || - GOKind.isBSSLocal()) + GOKind.isBSSLocal() || + (GOKind.isThreadBSS() && !GO->hasExternalLinkage())) return cast(SectionForGlobal(GO, GOKind, TM)) ->getQualNameSymbol(); } @@ -2179,12 +2180,17 @@ const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { // Common symbols go into a csect with matching name which will get mapped // into the .bss section. - if (Kind.isBSSLocal() || Kind.isCommon()) { + // Uninitialized local TLS symbols go into a csect with matching name which + // will get mapped into the .tbss section. Uninitialized external symbols are + // generated into the .tdata section. + if (Kind.isBSSLocal() || Kind.isCommon() || + (Kind.isThreadBSS() && !GO->hasExternalLinkage())) { SmallString<128> Name; getNameWithPrefix(Name, GO, TM); - return getContext().getXCOFFSection( - Name, Kind.isBSSLocal() ? XCOFF::XMC_BS : XCOFF::XMC_RW, XCOFF::XTY_CM, - Kind); + XCOFF::StorageMappingClass SMC = Kind.isBSSLocal() ? XCOFF::XMC_BS + : Kind.isCommon() ? XCOFF::XMC_RW + : XCOFF::XMC_UL; + return getContext().getXCOFFSection(Name, SMC, XCOFF::XTY_CM, Kind); } if (Kind.isMergeableCString()) { @@ -2239,6 +2245,18 @@ return ReadOnlySection; } + if (Kind.isThreadLocal()) { + // If data sections are enabled, we emit thread data into separate sections. + // Otherwise, we emit thread data into the .tdata section. + if (TM.getDataSections()) { + SmallString<128> Name; + getNameWithPrefix(Name, GO, TM); + return getContext().getXCOFFSection(Name, XCOFF::XMC_TL, XCOFF::XTY_SD, + SectionKind::getThreadData()); + } + return TLSDataSection; + } + report_fatal_error("XCOFF other section types not yet implemented."); } diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -885,6 +885,10 @@ ".rodata", XCOFF::StorageMappingClass::XMC_RO, XCOFF::XTY_SD, SectionKind::getReadOnly(), /* MultiSymbolsAllowed*/ true); + TLSDataSection = Ctx->getXCOFFSection( + ".tdata", XCOFF::StorageMappingClass::XMC_TL, XCOFF::XTY_SD, + SectionKind::getThreadData(), /* MultiSymbolsAllowed*/ true); + TOCBaseSection = Ctx->getXCOFFSection("TOC", XCOFF::StorageMappingClass::XMC_TC0, XCOFF::XTY_SD, SectionKind::getData()); diff --git a/llvm/lib/MC/MCSectionXCOFF.cpp b/llvm/lib/MC/MCSectionXCOFF.cpp --- a/llvm/lib/MC/MCSectionXCOFF.cpp +++ b/llvm/lib/MC/MCSectionXCOFF.cpp @@ -38,6 +38,16 @@ return; } + // Initialized and external uninitialized TLS data. + if (getKind().isThreadData()) { + // We only expect XMC_TL here because XMC_UL is for uninitialized TLS + // data only. + if (getMappingClass() != XCOFF::XMC_TL) + report_fatal_error("Unhandled storage-mapping class for .tdata csect."); + printCsectDirective(OS); + return; + } + if (getKind().isData()) { switch (getMappingClass()) { case XCOFF::XMC_RW: @@ -57,16 +67,18 @@ return; } - if (getKind().isBSSLocal() || getKind().isCommon()) { + if (getKind().isBSSLocal() || getKind().isCommon() || + getKind().isThreadBSS()) { // Local uninitialized TLS data. assert((getMappingClass() == XCOFF::XMC_RW || - getMappingClass() == XCOFF::XMC_BS) && + getMappingClass() == XCOFF::XMC_BS || + getMappingClass() == XCOFF::XMC_UL) && "Generated a storage-mapping class for a common/bss csect we don't " "understand how to switch to."); assert(getCSectType() == XCOFF::XTY_CM && "wrong csect type for .bss csect"); - // Don't have to print a directive for switching to section for commons. - // '.comm' and '.lcomm' directives for the variable will create the needed - // csect. + // Don't have to print a directive for switching to section for commons and + // uninitialized TLS data. '.comm' and '.lcomm' directives for the variable + // will create the needed csect. return; } diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp --- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -191,7 +191,6 @@ /// sinit/sterm function names. std::string FormatIndicatorAndUniqueModId; - static void ValidateGV(const GlobalVariable *GV); // Record a list of GlobalAlias associated with a GlobalObject. // This is used for AIX's extra-label-at-definition aliasing strategy. DenseMap> @@ -2050,15 +2049,6 @@ #undef GENVALUECOMMENT } -void PPCAIXAsmPrinter::ValidateGV(const GlobalVariable *GV) { - // Early error checking limiting what is supported. - if (GV->isThreadLocal()) - report_fatal_error("Thread local not yet supported on AIX."); - - if (GV->hasComdat()) - report_fatal_error("COMDAT not yet supported by AIX."); -} - static bool isSpecialLLVMGlobalArrayToSkip(const GlobalVariable *GV) { return GV->hasAppendingLinkage() && StringSwitch(GV->getName()) @@ -2084,7 +2074,9 @@ assert(!GV->getName().startswith("llvm.") && "Unhandled intrinsic global variable."); - ValidateGV(GV); + + if (GV->hasComdat()) + report_fatal_error("COMDAT not yet supported by AIX."); MCSymbolXCOFF *GVSym = cast(getSymbol(GV)); @@ -2094,7 +2086,8 @@ } SectionKind GVKind = getObjFileLowering().getKindForGlobal(GV, TM); - if (!GVKind.isGlobalWriteableData() && !GVKind.isReadOnly()) + if (!GVKind.isGlobalWriteableData() && !GVKind.isReadOnly() && + !GVKind.isThreadLocal()) // Checks for both ThreadData and ThreadBSS. report_fatal_error("Encountered a global variable kind that is " "not supported yet."); @@ -2106,14 +2099,16 @@ const DataLayout &DL = GV->getParent()->getDataLayout(); - // Handle common symbols. - if (GVKind.isCommon() || GVKind.isBSSLocal()) { + // Handle common and uninitialized local TLS symbols. + if (GV->hasCommonLinkage() || GVKind.isBSSLocal() || + (GVKind.isThreadBSS() && GlobalValue::isLocalLinkage(GV->getLinkage()))) { Align Alignment = GV->getAlign().getValueOr(DL.getPreferredAlign(GV)); uint64_t Size = DL.getTypeAllocSize(GV->getType()->getElementType()); GVSym->setStorageClass( TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(GV)); - if (GVKind.isBSSLocal()) + if (GVKind.isBSSLocal() || + (GVKind.isThreadBSS() && GV->hasInternalLinkage())) OutStreamer->emitXCOFFLocalCommonSymbol( OutContext.getOrCreateSymbol(GVSym->getSymbolTableName()), Size, GVSym, Alignment.value()); diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp --- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp +++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp @@ -3112,6 +3112,8 @@ SDValue PPCTargetLowering::LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const { + if (Subtarget.isAIXABI()) + report_fatal_error("TLS is not yet supported on AIX."); // FIXME: TLS addresses currently use medium model code sequences, // which is the most useful form. Eventually support for small and // large models could be added if users need it, at the cost of diff --git a/llvm/test/CodeGen/PowerPC/aix-tls-checks.ll b/llvm/test/CodeGen/PowerPC/aix-tls-checks.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-tls-checks.ll @@ -0,0 +1,13 @@ +; RUN: not --crash llc -verify-machineinstrs -mcpu=pwr7 -mattr=-altivec \ +; RUN: -mtriple powerpc-ibm-aix-xcoff < %s - 2>&1 | FileCheck %s +; RUN: not --crash llc -verify-machineinstrs -mcpu=pwr7 -mattr=-altivec \ +; RUN: -mtriple powerpc64-ibm-aix-xcoff < %s - 2>&1 | FileCheck %s + +; CHECK: TLS is not yet supported on AIX + +@tls1 = thread_local global i32 0, align 4 + +define i32* @getTls1Addr() { +entry: + ret i32* @tls1 +} diff --git a/llvm/test/CodeGen/PowerPC/aix-tls-variables.ll b/llvm/test/CodeGen/PowerPC/aix-tls-variables.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-tls-variables.ll @@ -0,0 +1,122 @@ +; This file tests TLS variable generation. + +; RUN: llc -verify-machineinstrs -mcpu=pwr7 -mattr=-altivec -mtriple \ +; RUN: powerpc-ibm-aix-xcoff < %s | FileCheck %s +; RUN: llc -verify-machineinstrs -mcpu=pwr7 -mattr=-altivec -mtriple \ +; RUN: powerpc64-ibm-aix-xcoff < %s | FileCheck %s +; RUN: llc -verify-machineinstrs -mcpu=pwr7 -mattr=-altivec -mtriple \ +; RUN: powerpc-ibm-aix-xcoff -data-sections=false < %s | FileCheck %s \ +; RUN: --check-prefix=NODATASEC +; RUN: llc -verify-machineinstrs -mcpu=pwr7 -mattr=-altivec -mtriple \ +; RUN: powerpc64-ibm-aix-xcoff -data-sections=false < %s | FileCheck %s \ +; RUN: --check-prefix=NODATASEC + +; When data-sections is true (default), we emit data into separate sections. +; When data-sections is false, we emit data into the .data / .tdata sections. + +; CHECK: .csect glo1[RW],2 +; CHECK-NEXT: .globl glo1[RW] +; CHECK-NEXT: .align 2 +; CHECK-NEXT: .vbyte 4, 1 +; CHECK-NEXT: .csect glo2[RW],2 +; CHECK-NEXT: .globl glo2[RW] +; CHECK-NEXT: .align 2 +; CHECK-NEXT: .vbyte 4, 0 +; CHECK-NEXT: .csect glo3[RW],2 +; CHECK-NEXT: .lglobl glo3[RW] +; CHECK-NEXT: .align 2 +; CHECK-NEXT: .vbyte 4, 2 +; CHECK-NEXT: .lcomm glo4,4,glo4[BS],2 +; CHECK-NEXT: .comm glo5[RW],4,2 +; CHECK-NEXT: .csect glo6[RW],2 +; CHECK-NEXT: .weak glo6[RW] +; CHECK-NEXT: .align 2 +; CHECK-NEXT: .vbyte 4, 0 +; CHECK-NEXT: .csect glo7[RW],2 +; CHECK-NEXT: .weak glo7[RW] +; CHECK-NEXT: .align 2 +; CHECK-NEXT: .vbyte 4, 1 +; CHECK-NEXT: .csect tls1[TL],2 +; CHECK-NEXT: .globl tls1[TL] +; CHECK-NEXT: .align 2 +; CHECK-NEXT: .vbyte 4, 1 +; CHECK-NEXT: .csect tls2[TL],2 +; CHECK-NEXT: .globl tls2[TL] +; CHECK-NEXT: .align 2 +; CHECK-NEXT: .vbyte 4, 0 +; CHECK-NEXT: .csect tls3[TL],2 +; CHECK-NEXT: .lglobl tls3[TL] +; CHECK-NEXT: .align 2 +; CHECK-NEXT: .vbyte 4, 2 +; CHECK-NEXT: .lcomm tls4,4,tls4[UL],2 +; CHECK-NEXT: .comm tls5[UL],4,2 +; CHECK-NEXT: .weak tls6[UL] +; CHECK-NEXT: .align 2 +; CHECK-NEXT: .vbyte 4, 0 +; CHECK-NEXT: .weak tls7[UL] +; CHECK-NEXT: .align 2 +; CHECK-NEXT: .vbyte 4, 0 + +; NODATASEC: .csect .data[RW],2 +; NODATASEC-NEXT: .globl glo1 +; NODATASEC-NEXT: .align 2 +; NODATASEC-NEXT:glo1: +; NODATASEC-NEXT: .vbyte 4, 1 +; NODATASEC-NEXT: .globl glo2 +; NODATASEC-NEXT: .align 2 +; NODATASEC-NEXT:glo2: +; NODATASEC-NEXT: .vbyte 4, 0 +; NODATASEC-NEXT: .lglobl glo3 +; NODATASEC-NEXT: .align 2 +; NODATASEC-NEXT:glo3: +; NODATASEC-NEXT: .vbyte 4, 2 +; NODATASEC-NEXT: .lcomm glo4,4,glo4[BS],2 +; NODATASEC-NEXT: .comm glo5[RW],4,2 +; NODATASEC-NEXT: .csect .data[RW],2 +; NODATASEC-NEXT: .weak glo6 +; NODATASEC-NEXT: .align 2 +; NODATASEC-NEXT:glo6: +; NODATASEC-NEXT: .vbyte 4, 0 +; NODATASEC-NEXT: .weak glo7 +; NODATASEC-NEXT: .align 2 +; NODATASEC-NEXT:glo7: +; NODATASEC-NEXT: .vbyte 4, 1 +; NODATASEC-NEXT: .csect .tdata[TL],2 +; NODATASEC-NEXT: .globl tls1 +; NODATASEC-NEXT: .align 2 +; NODATASEC-NEXT:tls1: +; NODATASEC-NEXT: .vbyte 4, 1 +; NODATASEC-NEXT: .globl tls2 +; NODATASEC-NEXT: .align 2 +; NODATASEC-NEXT:tls2: +; NODATASEC-NEXT: .vbyte 4, 0 +; NODATASEC-NEXT: .lglobl tls3 +; NODATASEC-NEXT: .align 2 +; NODATASEC-NEXT:tls3: +; NODATASEC-NEXT: .vbyte 4, 2 +; NODATASEC-NEXT: .lcomm tls4,4,tls4[UL],2 +; NODATASEC-NEXT: .comm tls5[UL],4,2 +; NODATASEC-NEXT: .weak tls6[UL] +; NODATASEC-NEXT: .align 2 +; NODATASEC-NEXT:tls6[UL]: +; NODATASEC-NEXT: .vbyte 4, 0 +; NODATASEC-NEXT: .weak tls7[UL] +; NODATASEC-NEXT: .align 2 +; NODATASEC-NEXT:tls7[UL]: +; NODATASEC-NEXT: .vbyte 4, 0 + +@glo1 = global i32 1, align 4 +@glo2 = global i32 0, align 4 +@glo3 = internal global i32 2, align 4 +@glo4 = internal global i32 0, align 4 +@glo5 = common global i32 0, align 4 +@glo6 = weak global i32 0, align 4 +@glo7 = weak global i32 1, align 4 + +@tls1 = thread_local global i32 1, align 4 +@tls2 = thread_local global i32 0, align 4 +@tls3 = internal thread_local global i32 2, align 4 +@tls4 = internal thread_local global i32 0, align 4 +@tls5 = common thread_local global i32 0, align 4 +@tls6 = weak thread_local global i32 0, align 4 +@tls7 = weak thread_local global i32 0, align 4