Index: llvm/lib/Target/PowerPC/P10InstrResources.td =================================================================== --- llvm/lib/Target/PowerPC/P10InstrResources.td +++ 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 )>; + Index: llvm/lib/Target/PowerPC/P9InstrResources.td =================================================================== --- llvm/lib/Target/PowerPC/P9InstrResources.td +++ 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, Index: llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp =================================================================== --- llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -877,7 +877,7 @@ } case PPC::ADDItoc: { 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. LowerPPCMachineInstrToMCInst(MI, TmpInst, *this); @@ -899,6 +899,30 @@ EmitToStreamer(*OutStreamer, TmpInst); return; } + case PPC::ADDItoc8: { + assert(IsAIX && TM.getCodeModel() == CodeModel::Small && + "PseudoOp only valid for small code model AIX"); + + // Transform %rN = ADDItoc8 @op1, %r2. + LowerPPCMachineInstrToMCInst(MI, TmpInst, *this); + + // Change the opcode to load address. + TmpInst.setOpcode(PPC::LA8); + + const MachineOperand &MO = MI->getOperand(1); + assert(MO.isGlobal() && "Invalid operand for ADDItoc8."); + + // 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: Index: llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp =================================================================== --- llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp +++ llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp @@ -510,14 +510,11 @@ 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."); + // transformation is added (struct support, etc.). assert(PointerSize >= GV->getAlign().valueOrOne().value() && - "GlobalVariables with an alignment requirement stricter then 4-bytes " - "not supported by the toc data transformation."); + "GlobalVariables with an alignment requirement stricter than TOC entry " + "size not supported by the toc data transformation."); Type *GVType = GV->getValueType(); @@ -537,7 +534,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 +5788,54 @@ 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; - // Handle 32-bit small code model. - if (!isPPC64) { - // Transforms the ISD::TOC_ENTRY node to passed in Opcode, either - // PPC::ADDItoc, or PPC::LWZtoc - auto replaceWith = [this, &dl](unsigned OpCode, SDNode *TocEntry) { + 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, MVT::i32, GA, TocBase); + SDNode *MN = CurDAG->getMachineNode(OpCode, dl, OperandTy, GA, TocBase); transferMemOperands(TocEntry, MN); ReplaceNode(TocEntry, MN); }; + // Handle 32-bit small code model. + if (!isPPC64 && CModel == CodeModel::Small) { + // Transforms the ISD::TOC_ENTRY node to passed in Opcode, either + // PPC::ADDItoc, or PPC::LWZtoc 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; } + + assert(isAIXABI && "ELF ABI already handled"); - if (isAIXABI && CModel == CodeModel::Small) { - if (hasTocDataAttr(N->getOperand(0), - CurDAG->getDataLayout().getPointerSize())) - replaceWith(PPC::ADDItoc, N); - else - replaceWith(PPC::LWZtoc, N); - + if (hasTocDataAttr(N->getOperand(0), + CurDAG->getDataLayout().getPointerSize())) { + replaceWith(PPC::ADDItoc, N, MVT::i32); + return; + } + + replaceWith(PPC::LWZtoc, N, MVT::i32); + return; + } + + if (isPPC64 && isAIXABI && CModel == CodeModel::Small) { + 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."); Index: llvm/lib/Target/PowerPC/PPCInstr64Bit.td =================================================================== --- llvm/lib/Target/PowerPC/PPCInstr64Bit.td +++ llvm/lib/Target/PowerPC/PPCInstr64Bit.td @@ -773,6 +773,12 @@ "addis $rD, $rA, $imm", IIC_IntSimple, [(set i64:$rD, (add i64:$rA, imm16ShiftedSExt:$imm))]>; +let isCodeGenOnly = 1 in +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 +1441,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; Index: llvm/test/CodeGen/PowerPC/basic-toc-data-extern.ll =================================================================== --- llvm/test/CodeGen/PowerPC/basic-toc-data-extern.ll +++ 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" } Index: llvm/test/CodeGen/PowerPC/basic-toc-data-local-linkage.ll =================================================================== --- llvm/test/CodeGen/PowerPC/basic-toc-data-local-linkage.ll +++ 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 Index: llvm/test/CodeGen/PowerPC/toc-data.ll =================================================================== --- llvm/test/CodeGen/PowerPC/toc-data.ll +++ 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" }