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 @@ -2111,7 +2111,7 @@ getSectionForFunctionDescriptor(cast(GO), TM)) ->getQualNameSymbol(); if ((TM.getDataSections() && !GO->hasSection()) || GOKind.isCommon() || - GOKind.isBSSLocal()) + GOKind.isBSSLocal() || GOKind.isThreadBSS()) return cast(SectionForGlobal(GO, GOKind, TM)) ->getQualNameSymbol(); } @@ -2158,12 +2158,15 @@ 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 TLS symbols go into a csect with matching name which will get + // mapped into the .tbss section. + if (Kind.isBSSLocal() || Kind.isCommon() || Kind.isThreadBSS()) { 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()) { @@ -2218,6 +2221,18 @@ return ReadOnlySection; } + if (Kind.isThreadData()) { + // 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 @@ -880,6 +880,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 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()) { 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 @@ -158,7 +158,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> @@ -2014,15 +2013,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()) @@ -2048,7 +2038,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)); @@ -2058,7 +2050,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."); @@ -2070,14 +2063,15 @@ const DataLayout &DL = GV->getParent()->getDataLayout(); - // Handle common symbols. - if (GVKind.isCommon() || GVKind.isBSSLocal()) { + // Handle common and uninitialized TLS symbols. + if (GVKind.isCommon() || GVKind.isBSSLocal() || GVKind.isThreadBSS()) { 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,63 @@ +; 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 tls1[TL],2 +; CHECK-NEXT: .globl tls1[TL] +; CHECK-NEXT: .align 2 +; CHECK-NEXT: .vbyte 4, 2 +; CHECK-NEXT: .comm tls2[UL],4,2 +; CHECK-NEXT: .csect tls3[TL],2 +; CHECK-NEXT: .lglobl tls3[TL] +; CHECK-NEXT: .align 2 +; CHECK-NEXT: .vbyte 4, 3 +; CHECK-NEXT: .lcomm tls4,4,tls4[UL],2 + +; 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: .csect .tdata[TL],2 +; NODATASEC-NEXT: .globl tls1 +; NODATASEC-NEXT: .align 2 +; NODATASEC-NEXT:tls1: +; NODATASEC-NEXT: .vbyte 4, 2 +; NODATASEC-NEXT: .comm tls2[UL],4,2 +; NODATASEC-NEXT: .csect .tdata[TL],2 +; NODATASEC-NEXT: .lglobl tls3 +; NODATASEC-NEXT: .align 2 +; NODATASEC-NEXT:tls3: +; NODATASEC-NEXT: .vbyte 4, 3 +; NODATASEC-NEXT: .lcomm tls4,4,tls4[UL],2 + +@glo1 = global i32 1, align 4 +@glo2 = global i32 0, align 4 +@tls1 = thread_local global i32 2, align 4 +@tls2 = thread_local global i32 0, align 4 +@tls3 = internal thread_local global i32 3, align 4 +@tls4 = internal thread_local global i32 0, align 4