diff --git a/llvm/lib/Target/PowerPC/P10InstrResources.td b/llvm/lib/Target/PowerPC/P10InstrResources.td --- a/llvm/lib/Target/PowerPC/P10InstrResources.td +++ b/llvm/lib/Target/PowerPC/P10InstrResources.td @@ -1976,7 +1976,7 @@ ICBLQ, ICBTLS, ICCCI, - LA, + LA, LA8, LDMX, MFDCR, MFPMR, @@ -2075,3 +2075,4 @@ VMSUMUHM, VMSUMUHS )>; + diff --git a/llvm/lib/Target/PowerPC/P9InstrResources.td b/llvm/lib/Target/PowerPC/P9InstrResources.td --- a/llvm/lib/Target/PowerPC/P9InstrResources.td +++ b/llvm/lib/Target/PowerPC/P9InstrResources.td @@ -151,6 +151,7 @@ (instregex "ADD(4|8)(TLS)?(_)?$"), (instregex "NEG(8)?(O)?$"), (instregex "ADDI(S)?toc(HA|L)(8)?$"), + (instregex "LA(8)?$"), COPY, MCRF, MCRXRX, @@ -165,7 +166,6 @@ SRADI_32, RLDIC, RFEBB, - LA, TBEGIN, TRECHKPT, NOP, diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp --- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -875,18 +875,19 @@ EmitToStreamer(*OutStreamer, TmpInst); return; } - case PPC::ADDItoc: { + case PPC::ADDItoc: + case PPC::ADDItoc8: { assert(IsAIX && TM.getCodeModel() == CodeModel::Small && - "Operand only valid in AIX 32 bit mode"); + "PseudoOp only valid for small code model AIX"); - // Transform %rN = ADDItoc @op1, %r2. + // Transform %rN = ADDItoc/8 @op1, %r2. LowerPPCMachineInstrToMCInst(MI, TmpInst, *this); // Change the opcode to load address. - TmpInst.setOpcode(PPC::LA); + TmpInst.setOpcode((!IsPPC64) ? (PPC::LA) : (PPC::LA8)); const MachineOperand &MO = MI->getOperand(1); - assert(MO.isGlobal() && "Invalid operand for ADDItoc."); + assert(MO.isGlobal() && "Invalid operand for ADDItoc[8]."); // Map the operand to its corresponding MCSymbol. const MCSymbol *const MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this); diff --git a/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp b/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp --- a/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp +++ b/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp @@ -510,14 +510,12 @@ return false; // TODO: These asserts should be updated as more support for the toc data - // transformation is added (64 bit, struct support, etc.). + // transformation is added (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."); + assert( + PointerSize >= GV->getAlign().valueOrOne().value() && + "GlobalVariables with an alignment requirement stricter than TOC entry " + "size not supported by the toc data transformation."); Type *GVType = GV->getValueType(); @@ -537,7 +535,7 @@ "supported by the toc data transformation."); assert(GVType->getPrimitiveSizeInBits() <= PointerSize * 8 && - "A GlobalVariable with size larger than 32 bits is not currently " + "A GlobalVariable with size larger than a TOC entry is not currently " "supported by the toc data transformation."); if (GV->hasLocalLinkage() || GV->hasPrivateLinkage()) @@ -5791,41 +5789,57 @@ if (isAIXABI && CModel == CodeModel::Medium) report_fatal_error("Medium code model is not supported on AIX."); - // 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) + // For 64-bit ELF small code model, we allow SelectCodeCommon to handle + // this, selecting one of LDtoc, LDtocJTI, LDtocCPT, and LDtocBA. For AIX + // small code model, we need to check for a toc-data attribute. + if (isPPC64 && !isAIXABI && CModel == CodeModel::Small) break; + auto replaceWith = [this, &dl](unsigned OpCode, SDNode *TocEntry, + EVT OperandTy) { + SDValue GA = TocEntry->getOperand(0); + SDValue TocBase = TocEntry->getOperand(1); + SDNode *MN = CurDAG->getMachineNode(OpCode, dl, OperandTy, GA, TocBase); + transferMemOperands(TocEntry, MN); + ReplaceNode(TocEntry, MN); + }; + // Handle 32-bit small code model. - if (!isPPC64) { + if (!isPPC64 && CModel == CodeModel::Small) { // 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(OpCode, dl, MVT::i32, GA, TocBase); - transferMemOperands(TocEntry, MN); - ReplaceNode(TocEntry, MN); - }; - if (isELFABI) { assert(TM.isPositionIndependent() && "32-bit ELF can only have TOC entries in position independent" " code."); // 32-bit ELF always uses a small code model toc access. - replaceWith(PPC::LWZtoc, N); + replaceWith(PPC::LWZtoc, N, MVT::i32); return; } - if (isAIXABI && CModel == CodeModel::Small) { - if (hasTocDataAttr(N->getOperand(0), - CurDAG->getDataLayout().getPointerSize())) - replaceWith(PPC::ADDItoc, N); - else - replaceWith(PPC::LWZtoc, N); + assert(isAIXABI && "ELF ABI already handled"); + + if (hasTocDataAttr(N->getOperand(0), + CurDAG->getDataLayout().getPointerSize())) { + replaceWith(PPC::ADDItoc, N, MVT::i32); + return; + } + replaceWith(PPC::LWZtoc, N, MVT::i32); + return; + } + + if (isPPC64 && CModel == CodeModel::Small) { + assert(isAIXABI && "ELF ABI handled in common SelectCode"); + + if (hasTocDataAttr(N->getOperand(0), + CurDAG->getDataLayout().getPointerSize())) { + replaceWith(PPC::ADDItoc8, N, MVT::i64); return; } + // Break if it doesn't have toc data attribute. Proceed with common + // SelectCode. + break; } assert(CModel != CodeModel::Small && "All small code models handled."); diff --git a/llvm/lib/Target/PowerPC/PPCInstr64Bit.td b/llvm/lib/Target/PowerPC/PPCInstr64Bit.td --- a/llvm/lib/Target/PowerPC/PPCInstr64Bit.td +++ b/llvm/lib/Target/PowerPC/PPCInstr64Bit.td @@ -773,6 +773,11 @@ "addis $rD, $rA, $imm", IIC_IntSimple, [(set i64:$rD, (add i64:$rA, imm16ShiftedSExt:$imm))]>; +def LA8 : DForm_2<14, (outs g8rc:$rD), (ins g8rc_nox0:$rA, s16imm64:$sym), + "la $rD, $sym($rA)", IIC_IntGeneral, + [(set i64:$rD, (add i64:$rA, + (PPClo tglobaladdr:$sym, 0)))]>; + let Defs = [CARRY] in { def SUBFIC8: DForm_2< 8, (outs g8rc:$rD), (ins g8rc:$rA, s16imm64:$imm), "subfic $rD, $rA, $imm", IIC_IntGeneral, @@ -1435,6 +1440,13 @@ def ADDItocL: PPCEmitTimePseudo<(outs g8rc:$rD), (ins g8rc_nox0:$reg, tocentry:$disp), "#ADDItocL", []>, isPPC64; } + +// Local Data Transform +def ADDItoc8 : PPCEmitTimePseudo<(outs g8rc:$rD), (ins tocentry:$disp, g8rc_nox0:$reg), + "#ADDItoc8", + [(set i64:$rD, + (PPCtoc_entry tglobaladdr:$disp, i64:$reg))]>, isPPC64; + let mayLoad = 1 in def LDtocL: PPCEmitTimePseudo<(outs g8rc:$rD), (ins tocentry:$disp, g8rc_nox0:$reg), "#LDtocL", []>, isPPC64; diff --git a/llvm/test/CodeGen/PowerPC/basic-toc-data-extern.ll b/llvm/test/CodeGen/PowerPC/basic-toc-data-extern.ll --- a/llvm/test/CodeGen/PowerPC/basic-toc-data-extern.ll +++ b/llvm/test/CodeGen/PowerPC/basic-toc-data-extern.ll @@ -1,7 +1,11 @@ ; 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 +; RUN: FileCheck %s --check-prefix=OBJ32 +; RUN: llc -mtriple powerpc64-ibm-aix-xcoff -verify-machineinstrs < %s | FileCheck %s +; RUN: not --crash llc -filetype=obj -mtriple powerpc64-ibm-aix-xcoff \ +; RUN: -verify-machineinstrs < %s 2>&1 | \ +; RUN: FileCheck %s --check-prefix=OBJ64 @i = external global i32, align 4 #0 @@ -15,6 +19,7 @@ ; CHECK: .toc ; CHECK-NEXT: .extern i[TD] -; OBJ: LLVM ERROR: toc-data not yet supported when writing object files. +; OBJ32: LLVM ERROR: toc-data not yet supported when writing object files. +; OBJ64: LLVM ERROR: 64-bit XCOFF object files are not supported yet. 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 --- a/llvm/test/CodeGen/PowerPC/basic-toc-data-local-linkage.ll +++ b/llvm/test/CodeGen/PowerPC/basic-toc-data-local-linkage.ll @@ -1,5 +1,7 @@ ; RUN: not --crash llc -mtriple powerpc-ibm-aix-xcoff -verify-machineinstrs \ ; RUN: < %s 2>&1 | FileCheck %s +; RUN: not --crash llc -mtriple powerpc64-ibm-aix-xcoff -verify-machineinstrs \ +; RUN: < %s 2>&1 | FileCheck %s @ilocal = internal global i32 0, align 4 #0 diff --git a/llvm/test/CodeGen/PowerPC/toc-data.ll b/llvm/test/CodeGen/PowerPC/toc-data.ll --- a/llvm/test/CodeGen/PowerPC/toc-data.ll +++ b/llvm/test/CodeGen/PowerPC/toc-data.ll @@ -1,7 +1,10 @@ ; REQUIRES: asserts ; 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 +; RUN: -stop-before=ppc-ctr-loops-verify | FileCheck %s --check-prefix CHECK32 +; RUN: llc -mtriple powerpc64-ibm-aix-xcoff -verify-machineinstrs < %s \ +; RUN: -stop-before=ppc-ctr-loops-verify | FileCheck %s --check-prefix CHECK64 +; RUN: llc -mtriple powerpc-ibm-aix-xcoff -verify-machineinstrs < %s | FileCheck %s --check-prefix TEST32 +; RUN: llc -mtriple powerpc64-ibm-aix-xcoff -verify-machineinstrs < %s | FileCheck %s --check-prefix TEST64 @i = dso_local global i32 0, align 4 #0 @d = dso_local local_unnamed_addr global double 3.141590e+00, align 8 @@ -14,62 +17,128 @@ 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 (s32) into @i) +; CHECK32: name: write_int +; CHECK32: %[[SCRATCH:[0-9]+]]:gprc_and_gprc_nor0 = ADDItoc @i, $r2 +; CHECK32-NEXT: STW %{{[0-9]+}}, 0, killed %[[SCRATCH]] :: (store (s32) into @i) + +; TEST32: .write_int: +; TEST32: la 4, i[TD](2) +; TEST32-NEXT: stw 3, 0(4) + +; CHECK64: name: write_int +; CHECK64: %[[SCRATCH:[0-9]+]]:g8rc_and_g8rc_nox0 = ADDItoc8 @i, $x2 +; CHECK64-NEXT: STW8 %{{[0-9]+}}, 0, killed %[[SCRATCH]] :: (store (s32) into @i) + +; TEST64: .write_int: +; TEST64: la 4, i[TD](2) +; TEST64-NEXT: stw 3, 0(4) -; 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 (s32) from got) +; CHECK32: name: read_ll +; CHECK32: LWZtoc @ll, $r2 :: (load (s32) from got) + +; TEST32: .read_ll: +; TEST32: lwz 4, L..C0(2) +; TEST32-NEXT: lwz 3, 0(4) +; TEST32-NEXT: lwz 4, 4(4) + +; CHECK64: name: read_ll +; CHECK64: LDtoc @ll, $x2 :: (load (s64) from got) + +; TEST64: .read_ll: +; TEST64: ld 3, L..C0(2) +; TEST64-NEXT: ld 3, 0(3) -; 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 (s32) from @f) +; CHECK32: name: read_float +; CHECK32: %[[SCRATCH:[0-9]+]]:gprc_and_gprc_nor0 = ADDItoc @f, $r2 +; CHECK32: %{{[0-9]+}}:f4rc = LFS 0, killed %[[SCRATCH]] :: (dereferenceable load (s32) from @f) + +; TEST32: .read_float: +; TEST32: la 3, f[TD](2) +; TEST32-NEXT: lfs 1, 0(3) + +; CHECK64: name: read_float +; CHECK64: %[[SCRATCH:[0-9]+]]:g8rc_and_g8rc_nox0 = ADDItoc8 @f, $x2 +; CHECK64: %{{[0-9]+}}:f4rc = LFS 0, killed %[[SCRATCH]] :: (dereferenceable load (s32) from @f) + +; TEST64: .read_float: +; TEST64: la 3, f[TD](2) +; TEST64-NEXT: lfs 1, 0(3) -; 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 (s32) from got) +; CHECK32: name: write_double +; CHECK32: LWZtoc @d, $r2 :: (load (s32) from got) + +; TEST32: .write_double +; TEST32: lwz 3, L..C1(2) +; TEST32-NEXT: stfd 1, 0(3) + +; CHECK64: name: write_double +; CHECK64: LDtoc @d, $x2 :: (load (s64) from got) + +; TEST64: .write_double +; TEST64: ld 3, L..C1(2) +; TEST64-NEXT: stfd 1, 0(3) -; 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]] +; CHECK32: name: addr +; CHECK32: %[[SCRATCH:[0-9]+]]:gprc = ADDItoc @i, $r2 +; CHECK32-NEXT: $r3 = COPY %[[SCRATCH]] + +; TEST32: .addr +; TEST32: la 3, i[TD](2) + +; CHECK64: name: addr +; CHECK64: %[[SCRATCH:[0-9]+]]:g8rc = ADDItoc8 @i, $x2 +; CHECK64-NEXT: $x3 = COPY %[[SCRATCH]] + +; TEST64: .addr +; TEST64: la 3, i[TD](2) -; TEST: .addr -; TEST: la 3, i[TD](2) +; TEST32: .toc +; TEST32: .tc ll[TC],ll[RW] +; TEST32-NOT: .csect ll[TD] +; TEST32: .tc d[TC],d[RW] +; TEST32-NOT: .csect d[TD],2 +; TEST32: .csect i[TD],2 +; TEST32-NEXT: .globl i[TD] +; TEST32-NEXT: .align 2 +; TEST32-NOT: .tc i[TC],i[RW] +; TEST32: .csect f[TD],2 +; TEST32-NEXT: .globl f[TD] +; TEST32-NOT: .tc f[TD],f[RW] +; TEST64: .toc +; TEST64: .tc ll[TC],ll[RW] +; TEST64-NOT: .csect ll[TD] +; TEST64: .tc d[TC],d[RW] +; TEST64-NOT: .csect d[TD],2 +; TEST64: .csect i[TD],2 +; TEST64-NEXT: .globl i[TD] +; TEST64-NEXT: .align 2 +; TEST64-NOT: .tc i[TC],i[RW] +; TEST64: .csect f[TD],2 +; TEST64-NEXT: .globl f[TD] +; TEST64-NOT: .tc f[TD],f[RW] attributes #0 = { "toc-data" }