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,17 @@ 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 GlobalValue *GV = dyn_cast(GO)) + if (const GlobalVariable *GVar = dyn_cast(GV)) + 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 +2228,17 @@ MCSection *TargetLoweringObjectFileXCOFF::SelectSectionForGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { + // Handle XCOFF::TD case first, then deal with the rest + if (const GlobalValue *GV = dyn_cast(GO)) + if (const GlobalVariable *GVar = dyn_cast(GV)) + 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,16 @@ if (isSpecialLLVMGlobalArrayToSkip(GV) || isSpecialLLVMGlobalArrayForStaticInit(GV)) return; + // If the Global Variable has the toc-data attribute, return + 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."); @@ -2222,7 +2260,7 @@ 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 (M.empty() && TOCDataGlobalVars.empty()) return; // Switch to section to emit TOC base. @@ -2241,6 +2279,9 @@ if (TS != nullptr) TS->emitTCEntry(*I.first.first, I.first.second); } + + for (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 @@ -5539,6 +5539,22 @@ if (isAIXABI && CModel == CodeModel::Medium) report_fatal_error("Medium code model is not supported on AIX."); + // Transforms the ISD::TOC_ENTRY node to a PPCISD::ADDItoc + if (!isPPC64 && CModel == CodeModel::Small) { + SDValue Op = N->getOperand(0); + if (GlobalAddressSDNode *GA = dyn_cast(Op)) + if (const GlobalValue *GV = GA->getGlobal()) + if (const GlobalVariable *GVar = dyn_cast(GV)) + if (GVar->hasAttribute("toc-data")) { + SDValue TocBase = N->getOperand(1); + SDNode *MN = CurDAG->getMachineNode(PPC::ADDItoc, dl, MVT::i32, + Op, TocBase); + transferMemOperands(N, MN); + ReplaceNode(N, MN); + return; + } + } + // For 64-bit small code model, we allow SelectCodeCommon to handle this, // selecting one of LDtoc, LDtocJTI, LDtocCPT, and LDtocBA. if (isPPC64 && CModel == CodeModel::Small) 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" }