[llvm] 70c433a - [XCOFF][AIX] Add Global Variables Directly to TOC for 32 bit AIX

Sidharth Baveja via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 30 07:49:12 PDT 2021


Author: Sidharth Baveja
Date: 2021-04-30T14:48:02Z
New Revision: 70c433a184a54819835e54c62c3e6891e7069861

URL: https://github.com/llvm/llvm-project/commit/70c433a184a54819835e54c62c3e6891e7069861
DIFF: https://github.com/llvm/llvm-project/commit/70c433a184a54819835e54c62c3e6891e7069861.diff

LOG: [XCOFF][AIX] Add Global Variables Directly to TOC for 32 bit AIX

Summary:
This patch implements the backend implementation of adding global variables
directly to the table of contents (TOC), rather than adding the address of the
variable to the TOC.
Currently, this patch will look for the "toc-data" attribute on symbols in the
IR, and then add those symbols to the TOC.
ATM, this is implemented for 32 bit AIX.

Reviewers: sfertile
Differential Revision: https://reviews.llvm.org/D101178

Added: 
    llvm/test/CodeGen/PowerPC/basic-toc-data-def.ll
    llvm/test/CodeGen/PowerPC/basic-toc-data-extern.ll
    llvm/test/CodeGen/PowerPC/basic-toc-data-local-linkage.ll
    llvm/test/CodeGen/PowerPC/toc-data.ll

Modified: 
    llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
    llvm/lib/MC/MCSectionXCOFF.cpp
    llvm/lib/MC/XCOFFObjectWriter.cpp
    llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
    llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
    llvm/lib/Target/PowerPC/PPCInstrInfo.td

Removed: 
    


################################################################################
diff  --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index c298172fe600b..7388ffb7d3def 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -2171,6 +2171,12 @@ TargetLoweringObjectFileXCOFF::getTargetSymbol(const GlobalValue *GV,
   // function entry point. We choose to always return a function descriptor
   // here.
   if (const GlobalObject *GO = dyn_cast<GlobalObject>(GV)) {
+    if (const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GV))
+      if (GVar->hasAttribute("toc-data"))
+        return cast<MCSectionXCOFF>(
+                   SectionForGlobal(GVar, SectionKind::getData(), TM))
+            ->getQualNameSymbol();
+
     if (GO->isDeclarationForLinker())
       return cast<MCSectionXCOFF>(getSectionForExternalReference(GO, TM))
           ->getQualNameSymbol();
@@ -2196,6 +2202,15 @@ MCSection *TargetLoweringObjectFileXCOFF::getExplicitSectionGlobal(
     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<GlobalVariable>(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;
@@ -2232,6 +2247,16 @@ MCSection *TargetLoweringObjectFileXCOFF::getSectionForExternalReference(
 
 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<GlobalVariable>(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

diff  --git a/llvm/lib/MC/MCSectionXCOFF.cpp b/llvm/lib/MC/MCSectionXCOFF.cpp
index 2adddd3169732..648efc14da06c 100644
--- a/llvm/lib/MC/MCSectionXCOFF.cpp
+++ b/llvm/lib/MC/MCSectionXCOFF.cpp
@@ -53,6 +53,7 @@ void MCSectionXCOFF::PrintSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
     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 @@ void MCSectionXCOFF::PrintSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
     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) {

diff  --git a/llvm/lib/MC/XCOFFObjectWriter.cpp b/llvm/lib/MC/XCOFFObjectWriter.cpp
index f41f47a718cbb..355655f4333af 100644
--- a/llvm/lib/MC/XCOFFObjectWriter.cpp
+++ b/llvm/lib/MC/XCOFFObjectWriter.cpp
@@ -329,6 +329,8 @@ CsectGroup &XCOFFObjectWriter::getCsectGroup(const MCSectionXCOFF *MCSec) {
     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 @@ void XCOFFObjectWriter::recordRelocation(MCAssembler &Asm,
       TargetObjectWriter->getRelocTypeAndSignSize(Target, Fixup, IsPCRel);
 
   const MCSectionXCOFF *SymASec = getContainingCsect(cast<MCSymbolXCOFF>(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.");
 

diff  --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 289a0f3704b4e..9ddc60b5152c3 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -200,6 +200,10 @@ class PPCAIXAsmPrinter : public PPCAsmPrinter {
 
   void emitTracebackTable();
 
+  SmallVector<const GlobalVariable *, 8> TOCDataGlobalVars;
+
+  void emitGlobalVariableHelper(const GlobalVariable *);
+
 public:
   PPCAIXAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
       : PPCAsmPrinter(TM, std::move(Streamer)) {
@@ -855,6 +859,30 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) {
     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:
@@ -2125,6 +2153,17 @@ void PPCAIXAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
   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.");
 
@@ -2232,9 +2271,9 @@ void PPCAIXAsmPrinter::emitFunctionEntryLabel() {
 }
 
 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.
@@ -2266,6 +2305,9 @@ void PPCAIXAsmPrinter::emitEndOfAsmFile(Module &M) {
     if (TS != nullptr)
       TS->emitTCEntry(*I.first.first, I.first.second);
   }
+
+  for (const auto *GV : TOCDataGlobalVars)
+    emitGlobalVariableHelper(GV);
 }
 
 bool PPCAIXAsmPrinter::doInitialization(Module &M) {

diff  --git a/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp b/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
index fa95dc2e3fccd..568c9d64774e1 100644
--- a/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
@@ -433,6 +433,64 @@ SDNode *PPCDAGToDAGISel::getGlobalBaseReg() {
       .getNode();
 }
 
+// Check if a SDValue has the toc-data attribute.
+static bool hasTocDataAttr(SDValue Val, unsigned PointerSize) {
+  GlobalAddressSDNode *GA = dyn_cast<GlobalAddressSDNode>(Val);
+  if (!GA)
+    return false;
+
+  const GlobalVariable *GV = dyn_cast_or_null<GlobalVariable>(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 with an alignment requirement stricter then 4-bytes "
+         "not supported by the toc data transformation.");
+
+  Type *PtrType = GV->getType();
+  assert(PtrType->isPointerTy() &&
+         "GlobalVariables always have pointer type!.");
+
+  Type *GVType = dyn_cast<PointerType>(PtrType)->getElementType();
+
+  assert(GVType->isSized() && "A GlobalVariable's size must be known 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() &&
+         "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 +5604,12 @@ void PPCDAGToDAGISel::Select(SDNode *N) {
 
     // 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 +5619,17 @@ void PPCDAGToDAGISel::Select(SDNode *N) {
                "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;
       }
     }

diff  --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.td b/llvm/lib/Target/PowerPC/PPCInstrInfo.td
index a76a0e942cc30..401634523c74f 100644
--- a/llvm/lib/Target/PowerPC/PPCInstrInfo.td
+++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.td
@@ -3532,6 +3532,11 @@ def ADDIStocHA : PPCEmitTimePseudo<(outs gprc:$rD), (ins gprc_nor0:$reg, tocentr
                        "#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.

diff  --git a/llvm/test/CodeGen/PowerPC/basic-toc-data-def.ll b/llvm/test/CodeGen/PowerPC/basic-toc-data-def.ll
new file mode 100644
index 0000000000000..235b1c28aa5bb
--- /dev/null
+++ b/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
+
+ at 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.

diff  --git a/llvm/test/CodeGen/PowerPC/basic-toc-data-extern.ll b/llvm/test/CodeGen/PowerPC/basic-toc-data-extern.ll
new file mode 100644
index 0000000000000..adfdfc900ff6b
--- /dev/null
+++ b/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
+
+ at 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" }

diff  --git a/llvm/test/CodeGen/PowerPC/basic-toc-data-local-linkage.ll b/llvm/test/CodeGen/PowerPC/basic-toc-data-local-linkage.ll
new file mode 100644
index 0000000000000..9f28b51496777
--- /dev/null
+++ b/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
+
+ at 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" }

diff  --git a/llvm/test/CodeGen/PowerPC/toc-data.ll b/llvm/test/CodeGen/PowerPC/toc-data.ll
new file mode 100644
index 0000000000000..500f742d854b6
--- /dev/null
+++ b/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
+
+ at i = dso_local global i32 0, align 4 #0
+ at d = dso_local local_unnamed_addr global double 3.141590e+00, align 8
+ at f = dso_local local_unnamed_addr global float 0x4005BE76C0000000, align 4 #0
+ at ll = dso_local local_unnamed_addr global i64 55, align 8
+ at 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" }


        


More information about the llvm-commits mailing list