[AIX] TOC pseudo expansion for 64bit large + 64bit small + 32bit large models

This patch provides support for peudo ops including ADDIStocHA8, ADDIStocHA, LWZtocL,
LDtoc, LDtocL for AIX, lowering them from MIR to assembly.

Differential Revision: https://reviews.llvm.org/D68341

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@375113 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/llvm/MC/MCExpr.h b/include/llvm/MC/MCExpr.h
index 74c3392..eb27865 100644
--- a/include/llvm/MC/MCExpr.h
+++ b/include/llvm/MC/MCExpr.h
@@ -235,6 +235,8 @@
     VK_PPC_TOC_LO,         // symbol@toc@l
     VK_PPC_TOC_HI,         // symbol@toc@h
     VK_PPC_TOC_HA,         // symbol@toc@ha
+    VK_PPC_U,              // symbol@u
+    VK_PPC_L,              // symbol@l
     VK_PPC_DTPMOD,         // symbol@dtpmod
     VK_PPC_TPREL_LO,       // symbol@tprel@l
     VK_PPC_TPREL_HI,       // symbol@tprel@h
diff --git a/lib/MC/MCExpr.cpp b/lib/MC/MCExpr.cpp
index c4b84a1..813c00f 100644
--- a/lib/MC/MCExpr.cpp
+++ b/lib/MC/MCExpr.cpp
@@ -259,6 +259,8 @@
   case VK_PPC_TOC_LO: return "toc@l";
   case VK_PPC_TOC_HI: return "toc@h";
   case VK_PPC_TOC_HA: return "toc@ha";
+  case VK_PPC_U: return "u";
+  case VK_PPC_L: return "l";
   case VK_PPC_DTPMOD: return "dtpmod";
   case VK_PPC_TPREL_LO: return "tprel@l";
   case VK_PPC_TPREL_HI: return "tprel@h";
@@ -373,6 +375,8 @@
     .Case("toc@l", VK_PPC_TOC_LO)
     .Case("toc@h", VK_PPC_TOC_HI)
     .Case("toc@ha", VK_PPC_TOC_HA)
+    .Case("u", VK_PPC_U)
+    .Case("l", VK_PPC_L)
     .Case("tls", VK_PPC_TLS)
     .Case("dtpmod", VK_PPC_DTPMOD)
     .Case("tprel@l", VK_PPC_TPREL_LO)
diff --git a/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.cpp b/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.cpp
index 0e64ae5..7fc2316 100644
--- a/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.cpp
+++ b/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.cpp
@@ -66,6 +66,31 @@
 
 void PPCInstPrinter::printInst(const MCInst *MI, raw_ostream &O,
                                StringRef Annot, const MCSubtargetInfo &STI) {
+  // Customize printing of the addis instruction on AIX. When an operand is a
+  // symbol reference, the instruction syntax is changed to look like a load
+  // operation, i.e:
+  //     Transform:  addis $rD, $rA, $src --> addis $rD, $src($rA).
+  if (TT.isOSAIX() &&
+      (MI->getOpcode() == PPC::ADDIS8 || MI->getOpcode() == PPC::ADDIS) &&
+      MI->getOperand(2).isExpr()) {
+    assert((MI->getOperand(0).isReg() && MI->getOperand(1).isReg()) &&
+           "The first and the second operand of an addis instruction"
+           " should be registers.");
+
+    assert(isa<MCSymbolRefExpr>(MI->getOperand(2).getExpr()) &&
+           "The third operand of an addis instruction should be a symbol "
+           "reference expression if it is an expression at all.");
+
+    O << "\taddis ";
+    printOperand(MI, 0, O);
+    O << ", ";
+    printOperand(MI, 2, O);
+    O << "(";
+    printOperand(MI, 1, O);
+    O << ")";
+    return;
+  }
+
   // Check for slwi/srwi mnemonics.
   if (MI->getOpcode() == PPC::RLWINM) {
     unsigned char SH = MI->getOperand(2).getImm();
diff --git a/lib/Target/PowerPC/PPCAsmPrinter.cpp b/lib/Target/PowerPC/PPCAsmPrinter.cpp
index a93c33b..ea4e179 100644
--- a/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -536,6 +536,8 @@
 void PPCAsmPrinter::EmitInstruction(const MachineInstr *MI) {
   MCInst TmpInst;
   const bool IsDarwin = TM.getTargetTriple().isOSDarwin();
+  const bool IsPPC64 = Subtarget->isPPC64();
+  const bool IsAIX = Subtarget->isAIXABI();
   const Module *M = MF->getFunction().getParent();
   PICLevel::Level PL = M->getPICLevel();
 
@@ -683,11 +685,10 @@
 
     const MachineOperand &MO = MI->getOperand(1);
     assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) &&
-           "Unexpected operand type for LWZtoc pseudo.");
+           "Invalid operand for LWZtoc.");
 
     // Map the operand to its corresponding MCSymbol.
     const MCSymbol *const MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this);
-    const bool IsAIX = TM.getTargetTriple().isOSAIX();
 
     // Create a reference to the GOT entry for the symbol. The GOT entry will be
     // synthesized later.
@@ -700,7 +701,7 @@
       return;
     }
 
-    // Otherwise use the TOC. 'TOCEntry' is a label used to reference the
+    // Otherwise, use the TOC. 'TOCEntry' is a label used to reference the
     // storage allocated in the TOC which contains the address of
     // 'MOSymbol'. Said TOC entry will be synthesized later.
     MCSymbol *TOCEntry = lookUpOrCreateTOCEntry(MOSymbol);
@@ -732,6 +733,8 @@
   case PPC::LDtocCPT:
   case PPC::LDtocBA:
   case PPC::LDtoc: {
+    assert(!IsDarwin && "TOC is an ELF/XCOFF construct");
+
     // Transform %x3 = LDtoc @min1, %x2
     LowerPPCMachineInstrToMCInst(MI, TmpInst, *this, IsDarwin);
 
@@ -748,15 +751,77 @@
     MCSymbol *TOCEntry =
         lookUpOrCreateTOCEntry(getMCSymbolForTOCPseudoMO(MO, *this));
 
+    const MCSymbolRefExpr::VariantKind VK =
+        IsAIX ? MCSymbolRefExpr::VK_None : MCSymbolRefExpr::VK_PPC_TOC;
     const MCExpr *Exp =
-      MCSymbolRefExpr::create(TOCEntry, MCSymbolRefExpr::VK_PPC_TOC,
-                              OutContext);
+        MCSymbolRefExpr::create(TOCEntry, VK, OutContext);
     TmpInst.getOperand(1) = MCOperand::createExpr(Exp);
     EmitToStreamer(*OutStreamer, TmpInst);
     return;
   }
+  case PPC::ADDIStocHA: {
+    assert((IsAIX && !IsPPC64 && TM.getCodeModel() == CodeModel::Large) &&
+           "This pseudo should only be selected for 32-bit large code model on"
+           " AIX.");
 
+    // Transform %rd = ADDIStocHA %rA, @sym(%r2)
+    LowerPPCMachineInstrToMCInst(MI, TmpInst, *this, IsDarwin);
+
+    // Change the opcode to ADDIS.
+    TmpInst.setOpcode(PPC::ADDIS);
+
+    const MachineOperand &MO = MI->getOperand(2);
+    assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) &&
+           "Invalid operand for ADDIStocHA.");
+
+    // Map the machine operand to its corresponding MCSymbol.
+    MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this);
+
+    // Always use TOC on AIX. Map the global address operand to be a reference
+    // to the TOC entry we will synthesize later. 'TOCEntry' is a label used to
+    // reference the storage allocated in the TOC which contains the address of
+    // 'MOSymbol'.
+    MCSymbol *TOCEntry = lookUpOrCreateTOCEntry(MOSymbol);
+    const MCExpr *Exp = MCSymbolRefExpr::create(TOCEntry,
+                                                MCSymbolRefExpr::VK_PPC_U,
+                                                OutContext);
+    TmpInst.getOperand(2) = MCOperand::createExpr(Exp);
+    EmitToStreamer(*OutStreamer, TmpInst);
+    return;
+  }
+  case PPC::LWZtocL: {
+    assert(IsAIX && !IsPPC64 && TM.getCodeModel() == CodeModel::Large &&
+           "This pseudo should only be selected for 32-bit large code model on"
+           " AIX.");
+
+    // Transform %rd = LWZtocL @sym, %rs.
+    LowerPPCMachineInstrToMCInst(MI, TmpInst, *this, IsDarwin);
+
+    // Change the opcode to lwz.
+    TmpInst.setOpcode(PPC::LWZ);
+
+    const MachineOperand &MO = MI->getOperand(1);
+    assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) &&
+           "Invalid operand for LWZtocL.");
+
+    // Map the machine operand to its corresponding MCSymbol.
+    MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this);
+
+    // Always use TOC on AIX. Map the global address operand to be a reference
+    // to the TOC entry we will synthesize later. 'TOCEntry' is a label used to
+    // reference the storage allocated in the TOC which contains the address of
+    // 'MOSymbol'.
+    MCSymbol *TOCEntry = lookUpOrCreateTOCEntry(MOSymbol);
+    const MCExpr *Exp = MCSymbolRefExpr::create(TOCEntry,
+                                                MCSymbolRefExpr::VK_PPC_L,
+                                                OutContext);
+    TmpInst.getOperand(1) = MCOperand::createExpr(Exp);
+    EmitToStreamer(*OutStreamer, TmpInst);
+    return;
+  }
   case PPC::ADDIStocHA8: {
+    assert(!IsDarwin && "TOC is an ELF/XCOFF construct");
+
     // Transform %xd = ADDIStocHA8 %x2, @sym
     LowerPPCMachineInstrToMCInst(MI, TmpInst, *this, IsDarwin);
 
@@ -778,9 +843,11 @@
         (MO.isCPI() && TM.getCodeModel() == CodeModel::Large))
       MOSymbol = lookUpOrCreateTOCEntry(MOSymbol);
 
+    const MCSymbolRefExpr::VariantKind VK =
+        IsAIX ? MCSymbolRefExpr::VK_PPC_U : MCSymbolRefExpr::VK_PPC_TOC_HA;
+
     const MCExpr *Exp =
-      MCSymbolRefExpr::create(MOSymbol, MCSymbolRefExpr::VK_PPC_TOC_HA,
-                              OutContext);
+        MCSymbolRefExpr::create(MOSymbol, VK, OutContext);
 
     if (!MO.isJTI() && MO.getOffset())
       Exp = MCBinaryExpr::createAdd(Exp,
@@ -793,6 +860,8 @@
     return;
   }
   case PPC::LDtocL: {
+    assert(!IsDarwin && "TOC is an ELF/XCOFF construct");
+
     // Transform %xd = LDtocL @sym, %xs
     LowerPPCMachineInstrToMCInst(MI, TmpInst, *this, IsDarwin);
 
@@ -817,9 +886,10 @@
     if (!MO.isCPI() || TM.getCodeModel() == CodeModel::Large)
       MOSymbol = lookUpOrCreateTOCEntry(MOSymbol);
 
+    const MCSymbolRefExpr::VariantKind VK =
+        IsAIX ? MCSymbolRefExpr::VK_PPC_L : MCSymbolRefExpr::VK_PPC_TOC_LO;
     const MCExpr *Exp =
-      MCSymbolRefExpr::create(MOSymbol, MCSymbolRefExpr::VK_PPC_TOC_LO,
-                              OutContext);
+        MCSymbolRefExpr::create(MOSymbol, VK, OutContext);
     TmpInst.getOperand(1) = MCOperand::createExpr(Exp);
     EmitToStreamer(*OutStreamer, TmpInst);
     return;
@@ -855,8 +925,8 @@
     const GlobalValue *GValue = MO.getGlobal();
     MCSymbol *MOSymbol = getSymbol(GValue);
     const MCExpr *SymGotTprel =
-      MCSymbolRefExpr::create(MOSymbol, MCSymbolRefExpr::VK_PPC_GOT_TPREL_HA,
-                              OutContext);
+        MCSymbolRefExpr::create(MOSymbol, MCSymbolRefExpr::VK_PPC_GOT_TPREL_HA,
+                                OutContext);
     EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::ADDIS8)
                                  .addReg(MI->getOperand(0).getReg())
                                  .addReg(MI->getOperand(1).getReg())
diff --git a/lib/Target/PowerPC/PPCInstrInfo.cpp b/lib/Target/PowerPC/PPCInstrInfo.cpp
index 06533fe..6b10672 100644
--- a/lib/Target/PowerPC/PPCInstrInfo.cpp
+++ b/lib/Target/PowerPC/PPCInstrInfo.cpp
@@ -328,6 +328,7 @@
   case PPC::LIS:
   case PPC::LIS8:
   case PPC::QVGPCI:
+  case PPC::ADDIStocHA:
   case PPC::ADDIStocHA8:
   case PPC::ADDItocL:
   case PPC::LOAD_STACK_GUARD:
diff --git a/test/CodeGen/PowerPC/lower-globaladdr32-aix-asm.ll b/test/CodeGen/PowerPC/lower-globaladdr32-aix-asm.ll
index b368cb4..e48f43a 100644
--- a/test/CodeGen/PowerPC/lower-globaladdr32-aix-asm.ll
+++ b/test/CodeGen/PowerPC/lower-globaladdr32-aix-asm.ll
@@ -1,21 +1,45 @@
-; RUN: llc -mtriple powerpc-ibm-aix-xcoff \
-; RUN: -code-model=small < %s | FileCheck %s
+; RUN: llc -verify-machineinstrs -mcpu=pwr7 -mtriple powerpc-ibm-aix-xcoff \
+; RUN: -code-model=small < %s | FileCheck %s --check-prefixes=CHECK,SMALL
 
-@b = common global i32 0
+; RUN: llc -verify-machineinstrs -mcpu=pwr7 -mtriple powerpc-ibm-aix-xcoff \
+; RUN: -code-model=large < %s | FileCheck %s --check-prefixes=CHECK,LARGE
+
 @a = common global i32 0
 
-define void @test() {
-  %1 = load i32, i32* @b
-  store i32 %1, i32* @a
+define i32 @test_load() {
+entry:
+  %0 = load i32, i32* @a
+  ret i32 %0
+}
+
+; SMALL-LABEL: .test_load:{{$}}
+; SMALL: lwz [[REG1:[0-9]+]], LC0(2)
+; SMALL: lwz [[REG2:[0-9]+]], 0([[REG1]])
+; SMALL: blr
+
+; LARGE-LABEL: .test_load:{{$}}
+; LARGE: addis [[REG1:[0-9]+]], LC0@u(2)
+; LARGE: lwz [[REG2:[0-9]+]], LC0@l([[REG1]])
+; LARGE: lwz [[REG3:[0-9]+]], 0([[REG2]])
+; LARGE: blr
+
+@b = common global i32 0
+
+define void @test_store(i32 %0) {
+  store i32 %0, i32* @b
   ret void
 }
 
-; CHECK-LABEL:   test
-; CHECK-DAG:       lwz [[REG1:[0-9]+]], LC0(2)
-; CHECK-DAG:       lwz [[REG2:[0-9]+]], LC1(2)
-; CHECK-DAG:       lwz [[REG3:[0-9]+]], 0([[REG1]])
-; CHECK:           stw [[REG3]], 0([[REG2]])
-; CHECK:           blr
+; SMALL-LABEL: .test_store:{{$}}
+; SMALL: lwz [[REG1:[0-9]+]], LC1(2)
+; SMALL: stw [[REG2:[0-9]+]], 0([[REG1]])
+; SMALL: blr
+
+; LARGE-LABEL: .test_store:{{$}}
+; LARGE: addis [[REG1:[0-9]+]], LC1@u(2)
+; LARGE: lwz [[REG2:[0-9]+]], LC1@l([[REG1]])
+; LARGE: stw [[REG3:[0-9]+]], 0([[REG2]])
+; LARGE: blr
 
 ; TODO Update test when TOC-entry emission lands.
 ; CHECK-NOT: .tc
diff --git a/test/CodeGen/PowerPC/lower-globaladdr64-aix-asm.ll b/test/CodeGen/PowerPC/lower-globaladdr64-aix-asm.ll
new file mode 100644
index 0000000..371fa0e
--- /dev/null
+++ b/test/CodeGen/PowerPC/lower-globaladdr64-aix-asm.ll
@@ -0,0 +1,45 @@
+; RUN: llc -verify-machineinstrs -mcpu=pwr7 -mtriple powerpc64-ibm-aix-xcoff \
+; RUN: -code-model=small < %s | FileCheck %s --check-prefixes=CHECK,SMALL
+
+; RUN: llc -verify-machineinstrs -mcpu=pwr7 -mtriple powerpc64-ibm-aix-xcoff \
+; RUN: -code-model=large < %s | FileCheck %s --check-prefixes=CHECK,LARGE
+
+@a = common global i32 0
+
+define zeroext i32 @test_load() {
+entry:
+  %0 = load i32, i32* @a
+  ret i32 %0
+}
+
+; SMALL-LABEL: .test_load:{{$}}
+; SMALL: ld [[REG1:[0-9]+]], LC0(2)
+; SMALL: lwz [[REG2:[0-9]+]], 0([[REG1]])
+; SMALL: blr
+
+; LARGE-LABEL: .test_load:{{$}}
+; LARGE: addis [[REG1:[0-9]+]], LC0@u(2)
+; LARGE: ld [[REG2:[0-9]+]], LC0@l([[REG1]])
+; LARGE: lwz [[REG3:[0-9]+]], 0([[REG2]])
+; LARGE: blr
+
+@b = common global i32 0
+
+define void @test_store(i32 zeroext %0) {
+  store i32 %0, i32* @b
+  ret void
+}
+
+; SMALL-LABEL: .test_store:{{$}}
+; SMALL: ld [[REG1:[0-9]+]], LC1(2)
+; SMALL: stw [[REG2:[0-9]+]], 0([[REG1]])
+; SMALL: blr
+
+; LARGE-LABEL: .test_store:{{$}}
+; LARGE: addis [[REG1:[0-9]+]], LC1@u(2)
+; LARGE: ld [[REG2:[0-9]+]], LC1@l([[REG1]])
+; LARGE: stw [[REG3:[0-9]+]], 0([[REG2]])
+; LARGE: blr
+
+; TODO Update test when TOC-entry emission lands.
+; CHECK-NOT: .tc