From 6a814b2152716d853fe53a451fb305e4d6bc1e50 Mon Sep 17 00:00:00 2001 From: Xiangling Liao Date: Thu, 17 Oct 2019 13:20:25 +0000 Subject: [PATCH] [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 llvm-svn: 375113 --- include/llvm/MC/MCExpr.h | 2 + lib/MC/MCExpr.cpp | 4 + .../PowerPC/MCTargetDesc/PPCInstPrinter.cpp | 25 +++++ lib/Target/PowerPC/PPCAsmPrinter.cpp | 92 ++++++++++++++++--- lib/Target/PowerPC/PPCInstrInfo.cpp | 1 + .../PowerPC/lower-globaladdr32-aix-asm.ll | 48 +++++++--- .../PowerPC/lower-globaladdr64-aix-asm.ll | 45 +++++++++ 7 files changed, 194 insertions(+), 23 deletions(-) create mode 100644 test/CodeGen/PowerPC/lower-globaladdr64-aix-asm.ll diff --git a/include/llvm/MC/MCExpr.h b/include/llvm/MC/MCExpr.h index 74c3392df75..eb2786501f8 100644 --- a/include/llvm/MC/MCExpr.h +++ b/include/llvm/MC/MCExpr.h @@ -235,6 +235,8 @@ public: 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 c4b84a10c0c..813c00f6f3b 100644 --- a/lib/MC/MCExpr.cpp +++ b/lib/MC/MCExpr.cpp @@ -259,6 +259,8 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) { 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 @@ MCSymbolRefExpr::getVariantKindForName(StringRef Name) { .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 0e64ae55ab1..7fc231618fa 100644 --- a/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.cpp +++ b/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.cpp @@ -66,6 +66,31 @@ void PPCInstPrinter::printRegName(raw_ostream &OS, unsigned RegNo) const { 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(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 a93c33bafd5..ea4e1791d43 100644 --- a/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -536,6 +536,8 @@ static MCSymbol *getMCSymbolForTOCPseudoMO(const MachineOperand &MO, 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 @@ void PPCAsmPrinter::EmitInstruction(const MachineInstr *MI) { 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 @@ void PPCAsmPrinter::EmitInstruction(const MachineInstr *MI) { 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 @@ void PPCAsmPrinter::EmitInstruction(const MachineInstr *MI) { 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 @@ void PPCAsmPrinter::EmitInstruction(const MachineInstr *MI) { 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 @@ void PPCAsmPrinter::EmitInstruction(const MachineInstr *MI) { (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 @@ void PPCAsmPrinter::EmitInstruction(const MachineInstr *MI) { 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 @@ void PPCAsmPrinter::EmitInstruction(const MachineInstr *MI) { 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 @@ void PPCAsmPrinter::EmitInstruction(const MachineInstr *MI) { 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 06533fe0de3..6b10672965c 100644 --- a/lib/Target/PowerPC/PPCInstrInfo.cpp +++ b/lib/Target/PowerPC/PPCInstrInfo.cpp @@ -328,6 +328,7 @@ bool PPCInstrInfo::isReallyTriviallyReMaterializable(const MachineInstr &MI, 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 b368cb43dbb..e48f43a2d4b 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 + +; RUN: llc -verify-machineinstrs -mcpu=pwr7 -mtriple powerpc-ibm-aix-xcoff \ +; RUN: -code-model=large < %s | FileCheck %s --check-prefixes=CHECK,LARGE -@b = common global i32 0 @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 00000000000..371fa0ec279 --- /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