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 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 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/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 la + 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,25 @@ .getNode(); } +// Check if a SDValue has the toc-data attribute. +static bool hasTocDataAttr(SDValue Val) { + 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: assert on alignment and size, etc. Check that the GlobalVariable + // meets the criteria to be added to the TOC (TBD) + + 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 +5565,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 +5580,16 @@ "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))) + 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-2.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/PowerPC/basic-toc-data-2.ll @@ -0,0 +1,15 @@ +; RUN: llc -mtriple powerpc-ibm-aix-xcoff -verify-machineinstrs < %s | FileCheck %s + +@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] + +attributes #0 = { "toc-data" } Index: llvm/test/CodeGen/PowerPC/basic-toc-data.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/PowerPC/basic-toc-data.ll @@ -0,0 +1,10 @@ +; RUN: llc -mtriple powerpc-ibm-aix-xcoff -verify-machineinstrs < %s | FileCheck %s + +@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 Index: llvm/test/CodeGen/PowerPC/toc-data.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/PowerPC/toc-data.ll @@ -0,0 +1,61 @@ +; RUN: llc -mtriple powerpc-ibm-aix-xcoff -verify-machineinstrs < %s \ +; RUN: -stop-before=ppc-ctr-loops-verify | FileCheck %s + +@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 #0 + +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) + +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) + +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) + +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) + +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]] + +define dso_local i32 @read_i32() { + entry: + %0 = load i32, i32* @ilocal, align 4 + ret i32 %0 +} +; CHECK: name: read_i32 +; CHECK: %[[SCRATCH:[0-9]+]]:gprc_and_gprc_nor0 = ADDItoc @ilocal, $r2 +; CHECK: %{{[0-9]+}}:gprc = LWZ 0, killed %[[SCRATCH]] :: (dereferenceable load 4 from @ilocal) + +attributes #0 = { "toc-data" }