Index: llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp =================================================================== --- llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -2150,6 +2150,12 @@ // function entry point. We choose to always return a function descriptor // here. if (const GlobalObject *GO = dyn_cast(GV)) { + if (const GlobalVariable *GVar = dyn_cast(GV)) + if (GVar->hasAttribute("toc-data")) + return cast( + SectionForGlobal(GVar, SectionKind::getData(), TM)) + ->getQualNameSymbol(); + if (GO->isDeclarationForLinker()) return cast(getSectionForExternalReference(GO, TM)) ->getQualNameSymbol(); @@ -2175,6 +2181,15 @@ report_fatal_error("#pragma clang section is not yet supported"); StringRef SectionName = GO->getSection(); + + // Handle the XCOFF::TD case first, then deal with the rest. + if (const GlobalVariable *GVar = dyn_cast(GO)) + if (GVar->hasAttribute("toc-data")) + return getContext().getXCOFFSection( + SectionName, Kind, + XCOFF::CsectProperties(/*MappingClass*/ XCOFF::XMC_TD, XCOFF::XTY_SD), + /* MultiSymbolsAllowed*/ true); + XCOFF::StorageMappingClass MappingClass; if (Kind.isText()) MappingClass = XCOFF::XMC_PR; @@ -2211,6 +2226,16 @@ MCSection *TargetLoweringObjectFileXCOFF::SelectSectionForGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { + // Handle the XCOFF::TD case first, then deal with the rest. + if (const GlobalVariable *GVar = dyn_cast(GO)) + if (GVar->hasAttribute("toc-data")) { + SmallString<128> Name; + getNameWithPrefix(Name, GO, TM); + return getContext().getXCOFFSection( + Name, Kind, XCOFF::CsectProperties(XCOFF::XMC_TD, XCOFF::XTY_SD), + /* MultiSymbolsAllowed*/ true); + } + // Common symbols go into a csect with matching name which will get mapped // into the .bss section. // Zero-initialized local TLS symbols go into a csect with matching name which Index: llvm/lib/MC/MCSectionXCOFF.cpp =================================================================== --- llvm/lib/MC/MCSectionXCOFF.cpp +++ llvm/lib/MC/MCSectionXCOFF.cpp @@ -53,6 +53,7 @@ switch (getMappingClass()) { case XCOFF::XMC_RW: case XCOFF::XMC_DS: + case XCOFF::XMC_TD: printCsectDirective(OS); break; case XCOFF::XMC_TC: @@ -68,6 +69,12 @@ return; } + if (isCsect() && getMappingClass() == XCOFF::XMC_TD) { + assert((getKind().isBSSExtern() || getKind().isBSSLocal()) && + "Unexepected section kind for toc-data"); + printCsectDirective(OS); + return; + } // Common csect type (uninitialized storage) does not have to print csect // directive for section switching. if (isCsect() && getCSectType() == XCOFF::XTY_CM) { Index: llvm/lib/MC/XCOFFObjectWriter.cpp =================================================================== --- llvm/lib/MC/XCOFFObjectWriter.cpp +++ llvm/lib/MC/XCOFFObjectWriter.cpp @@ -329,6 +329,8 @@ assert(!TOCCsects.empty() && "We should at least have a TOC-base in this CsectGroup."); return TOCCsects; + case XCOFF::XMC_TD: + report_fatal_error("toc-data not yet supported when writing object files."); default: report_fatal_error("Unhandled mapping of csect to section."); } @@ -439,6 +441,10 @@ TargetObjectWriter->getRelocTypeAndSignSize(Target, Fixup, IsPCRel); const MCSectionXCOFF *SymASec = getContainingCsect(cast(SymA)); + + if (SymASec->isCsect() && SymASec->getMappingClass() == XCOFF::XMC_TD) + report_fatal_error("toc-data not yet supported when writing object files."); + assert(SectionMap.find(SymASec) != SectionMap.end() && "Expected containing csect to exist in map."); Index: llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp =================================================================== --- llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -200,6 +200,10 @@ void emitTracebackTable(); + SmallVector TOCDataGlobalVars; + + void emitGlobalVariableHelper(const GlobalVariable *); + public: PPCAIXAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) : PPCAsmPrinter(TM, std::move(Streamer)) { @@ -843,6 +847,30 @@ EmitToStreamer(*OutStreamer, TmpInst); return; } + case PPC::ADDItoc: { + assert(IsAIX && TM.getCodeModel() == CodeModel::Small && + "Operand only valid in AIX 32 bit mode"); + + // Transform %rN = ADDItoc @op1, %r2. + LowerPPCMachineInstrToMCInst(MI, TmpInst, *this); + + // Change the opcode to load address. + TmpInst.setOpcode(PPC::LA); + + const MachineOperand &MO = MI->getOperand(1); + assert(MO.isGlobal() && "Invalid operand for ADDItoc."); + + // Map the operand to its corresponding MCSymbol. + const MCSymbol *const MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this); + + const MCExpr *Exp = + MCSymbolRefExpr::create(MOSymbol, MCSymbolRefExpr::VK_None, OutContext); + + TmpInst.getOperand(1) = TmpInst.getOperand(2); + TmpInst.getOperand(2) = MCOperand::createExpr(Exp); + EmitToStreamer(*OutStreamer, TmpInst); + return; + } case PPC::LDtocJTI: case PPC::LDtocCPT: case PPC::LDtocBA: @@ -2113,6 +2141,17 @@ if (isSpecialLLVMGlobalArrayToSkip(GV) || isSpecialLLVMGlobalArrayForStaticInit(GV)) return; + // If the Global Variable has the toc-data attribute, it needs to be emitted + // when we emit the .toc section. + if (GV->hasAttribute("toc-data")) { + TOCDataGlobalVars.push_back(GV); + return; + } + + emitGlobalVariableHelper(GV); +} + +void PPCAIXAsmPrinter::emitGlobalVariableHelper(const GlobalVariable *GV) { assert(!GV->getName().startswith("llvm.") && "Unhandled intrinsic global variable."); @@ -2220,9 +2259,9 @@ } void PPCAIXAsmPrinter::emitEndOfAsmFile(Module &M) { - // If there are no functions in this module, we will never need to reference - // the TOC base. - if (M.empty()) + // If there are no functions and there are no toc-data definitions in this + // module, we will never need to reference the TOC base. + if (M.empty() && TOCDataGlobalVars.empty()) return; // Switch to section to emit TOC base. @@ -2241,6 +2280,9 @@ if (TS != nullptr) TS->emitTCEntry(*I.first.first, I.first.second); } + + for (const auto *GV : TOCDataGlobalVars) + emitGlobalVariableHelper(GV); } bool PPCAIXAsmPrinter::doInitialization(Module &M) { Index: llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp =================================================================== --- llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp +++ llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp @@ -433,6 +433,65 @@ .getNode(); } +// Check if a SDValue has the toc-data attribute. +static bool hasTocDataAttr(SDValue Val, unsigned PointerSize) { + GlobalAddressSDNode *GA = dyn_cast(Val); + if (!GA) + return false; + + const GlobalVariable *GV = dyn_cast_or_null(GA->getGlobal()); + if (!GV) + return false; + + if (!GV->hasAttribute("toc-data")) + return false; + + // TODO: These asserts should be updated as more support for the toc data + // transformation is added (64 bit, struct support, etc.). + + assert(PointerSize == 4 && "Only 32 Bit Codegen is currently supported by " + "the toc data transformation."); + + assert(PointerSize >= GV->getAlign().valueOrOne().value() && + "GlobalVariables which must be alligned to be supported by the " + "toc data transformation."); + + Type *PtrType = GV->getType(); + assert(PtrType->isPointerTy() && + "GlobalVariables always have pointer type!."); + + Type *GVType = dyn_cast(PtrType)->getElementType(); + + assert(GVType->isSized() && "A GlobalVariable's size must be know to be " + "supported by the toc data transformation."); + + if (GVType->isVectorTy()) + report_fatal_error("A GlobalVariable of Vector type is not currently " + "supported by the toc data transformation."); + + if (GVType->isArrayTy()) + report_fatal_error("A GlobalVariable of Array type is not currently " + "supported by the toc data transformation."); + + if (GVType->isStructTy()) + report_fatal_error("A GlobalVariable of Struct type is not currently " + "supported by the toc data transformation."); + + assert(GVType->getPrimitiveSizeInBits() <= PointerSize * 8 && + "A GlobalVariable with size larger than 32 bits is not currently " + "supported by the toc data transformation."); + + if (GV->hasLocalLinkage() || GV->hasPrivateLinkage()) + report_fatal_error("A GlobalVariable with private or local linkage is not " + "currently supported by the toc data transformation."); + + assert(!GV->hasCommonLinkage() && + "A GlobalVariable with tentative definitions cannot have the mapping " + "class XMC_TD."); + + return true; +} + /// isInt32Immediate - This method tests to see if the node is a 32-bit constant /// operand. If so Imm will receive the 32-bit value. static bool isInt32Immediate(SDNode *N, unsigned &Imm) { @@ -5546,12 +5605,12 @@ // Handle 32-bit small code model. if (!isPPC64) { - // Transforms the ISD::TOC_ENTRY node to a PPCISD::LWZtoc. - auto replaceWithLWZtoc = [this, &dl](SDNode *TocEntry) { + // Transforms the ISD::TOC_ENTRY node to passed in Opcode, either + // PPC::ADDItoc, or PPC::LWZtoc + auto replaceWith = [this, &dl](unsigned OpCode, SDNode *TocEntry) { SDValue GA = TocEntry->getOperand(0); SDValue TocBase = TocEntry->getOperand(1); - SDNode *MN = CurDAG->getMachineNode(PPC::LWZtoc, dl, MVT::i32, GA, - TocBase); + SDNode *MN = CurDAG->getMachineNode(OpCode, dl, MVT::i32, GA, TocBase); transferMemOperands(TocEntry, MN); ReplaceNode(TocEntry, MN); }; @@ -5561,12 +5620,17 @@ "32-bit ELF can only have TOC entries in position independent" " code."); // 32-bit ELF always uses a small code model toc access. - replaceWithLWZtoc(N); + replaceWith(PPC::LWZtoc, N); return; } if (isAIXABI && CModel == CodeModel::Small) { - replaceWithLWZtoc(N); + if (hasTocDataAttr(N->getOperand(0), + CurDAG->getDataLayout().getPointerSize())) + replaceWith(PPC::ADDItoc, N); + else + replaceWith(PPC::LWZtoc, N); + return; } } Index: llvm/lib/Target/PowerPC/PPCInstrInfo.td =================================================================== --- llvm/lib/Target/PowerPC/PPCInstrInfo.td +++ llvm/lib/Target/PowerPC/PPCInstrInfo.td @@ -3532,6 +3532,11 @@ "#ADDIStocHA", [(set i32:$rD, (PPCtoc_entry i32:$reg, tglobaladdr:$disp))]>; +// Local Data Transform +def ADDItoc : PPCEmitTimePseudo<(outs gprc:$rD), (ins tocentry32:$disp, gprc:$reg), + "#ADDItoc", + [(set i32:$rD, + (PPCtoc_entry tglobaladdr:$disp, i32:$reg))]>; // Get Global (GOT) Base Register offset, from the word immediately preceding // the function label. Index: llvm/test/CodeGen/PowerPC/basic-toc-data-def.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/PowerPC/basic-toc-data-def.ll @@ -0,0 +1,15 @@ +; RUN: llc -mtriple powerpc-ibm-aix-xcoff -verify-machineinstrs < %s | FileCheck %s +; RUN: not --crash llc -filetype=obj -mtriple powerpc-ibm-aix-xcoff \ +; RUN: -verify-machineinstrs < %s 2>&1 | \ +; RUN: FileCheck %s --check-prefix=OBJ + +@i = global i32 55, align 4 #0 + +attributes #0 = { "toc-data" } +; CHECK: .toc +; CHECK-NEXT: .csect i[TD],2 +; CHECK-NEXT: .globl i[TD] +; CHECK-NEXT: .align 2 +; CHECK-NEXT: .vbyte 4, 55 + +; OBJ: LLVM ERROR: toc-data not yet supported when writing object files. Index: llvm/test/CodeGen/PowerPC/basic-toc-data-extern.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/PowerPC/basic-toc-data-extern.ll @@ -0,0 +1,20 @@ +; RUN: llc -mtriple powerpc-ibm-aix-xcoff -verify-machineinstrs < %s | FileCheck %s +; RUN: not --crash llc -filetype=obj -mtriple powerpc-ibm-aix-xcoff \ +; RUN: -verify-machineinstrs < %s 2>&1 | \ +; RUN: FileCheck %s --check-prefix=OBJ + +@i = external global i32, align 4 #0 + +; Function Attrs: noinline nounwind optnone +define i32* @get() { + entry: + ret i32* @i +} + +; CHECK: la 3, i[TD](2) +; CHECK: .toc +; CHECK-NEXT: .extern i[TD] + +; OBJ: LLVM ERROR: toc-data not yet supported when writing object files. + +attributes #0 = { "toc-data" } Index: llvm/test/CodeGen/PowerPC/basic-toc-data-local-linkage.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/PowerPC/basic-toc-data-local-linkage.ll @@ -0,0 +1,14 @@ +; RUN: not --crash llc -mtriple powerpc-ibm-aix-xcoff -verify-machineinstrs \ +; RUN: < %s 2>&1 | FileCheck %s + +@ilocal = internal global i32 0, align 4 #0 + +define dso_local i32 @read_i32_local_linkage() { + entry: + %0 = load i32, i32* @ilocal, align 4 + ret i32 %0 +} + +; CHECK: LLVM ERROR: A GlobalVariable with private or local linkage is not currently supported by the toc data transformation. + +attributes #0 = { "toc-data" } Index: llvm/test/CodeGen/PowerPC/toc-data.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/PowerPC/toc-data.ll @@ -0,0 +1,74 @@ +; RUN: llc -mtriple powerpc-ibm-aix-xcoff -verify-machineinstrs < %s \ +; RUN: -stop-before=ppc-ctr-loops-verify | FileCheck %s +; RUN: llc -mtriple powerpc-ibm-aix-xcoff -verify-machineinstrs < %s | FileCheck %s --check-prefix TEST + +@i = dso_local global i32 0, align 4 #0 +@d = dso_local local_unnamed_addr global double 3.141590e+00, align 8 +@f = dso_local local_unnamed_addr global float 0x4005BE76C0000000, align 4 #0 +@ll = dso_local local_unnamed_addr global i64 55, align 8 +@ilocal = internal global i32 0, align 4 + +define dso_local void @write_int(i32 signext %in) { + entry: + store i32 %in, i32* @i, align 4 + ret void +} +; CHECK: name: write_int +; CHECK: %[[SCRATCH:[0-9]+]]:gprc_and_gprc_nor0 = ADDItoc @i, $r2 +; CHECK-NEXT: STW %{{[0-9]+}}, 0, killed %[[SCRATCH]] :: (store 4 into @i) + +; TEST: .write_int: +; TEST: la 4, i[TD](2) +; TEST-NEXT: stw 3, 0(4) + +define dso_local i64 @read_ll() { + entry: + %0 = load i64, i64* @ll, align 8 + ret i64 %0 +} +; CHECK: name: read_ll +; CHECK: LWZtoc @ll, $r2 :: (load 4 from got) + +; TEST: .read_ll: +; TEST: lwz 4, L..C0(2) +; TEST-NEXT: lwz 3, 0(4) +; TEST-NEXT: lwz 4, 4(4) + +define dso_local float @read_float() { + entry: + %0 = load float, float* @f, align 4 + ret float %0 +} +; CHECK: name: read_float +; CHECK: %[[SCRATCH:[0-9]+]]:gprc_and_gprc_nor0 = ADDItoc @f, $r2 +; CHECK: %{{[0-9]+}}:f4rc = LFS 0, killed %[[SCRATCH]] :: (dereferenceable load 4 from @f) + +; TEST: .read_float: +; TEST: la 3, f[TD](2) +; TEST-NEXT: lfs 1, 0(3) + +define dso_local void @write_double(double %in) { + entry: + store double %in, double* @d, align 8 + ret void +} +; CHECK: name: write_double +; CHECK: LWZtoc @d, $r2 :: (load 4 from got) + +; TEST: .write_double +; TEST: lwz 3, L..C1(2) +; TEST-NEXT: stfd 1, 0(3) + +define dso_local nonnull i32* @addr() { + entry: + ret i32* @i +} +; CHECK: name: addr +; CHECK: %[[SCRATCH:[0-9]+]]:gprc = ADDItoc @i, $r2 +; CHECK-NEXT: $r3 = COPY %[[SCRATCH]] + +; TEST: .addr +; TEST: la 3, i[TD](2) + + +attributes #0 = { "toc-data" }