diff --git a/llvm/docs/GlobalISel/GenericOpcode.rst b/llvm/docs/GlobalISel/GenericOpcode.rst --- a/llvm/docs/GlobalISel/GenericOpcode.rst +++ b/llvm/docs/GlobalISel/GenericOpcode.rst @@ -69,6 +69,15 @@ %0:_(p0) = G_BLOCK_ADDR blockaddress(@test_blockaddress, %ir-block.block) +G_CONSTANT_POOL +^^^^^^^^^^^^^^^ + +The address of an object in the constant pool. + +.. code-block:: none + + %0:_(p0) = G_CONSTANT_POOL %const.0 + Integer Extension and Truncation -------------------------------- diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h @@ -359,6 +359,7 @@ LegalizeResult bitcastInsertVectorElt(MachineInstr &MI, unsigned TypeIdx, LLT CastTy); + LegalizeResult lowerFConstant(MachineInstr &MI); LegalizeResult lowerBitcast(MachineInstr &MI); LegalizeResult lowerLoad(GAnyLoad &MI); LegalizeResult lowerStore(GStore &MI); diff --git a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h --- a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h @@ -458,6 +458,17 @@ /// \return a MachineInstrBuilder for the newly created instruction. MachineInstrBuilder buildGlobalValue(const DstOp &Res, const GlobalValue *GV); + /// Build and insert \p Res = G_CONSTANT_POOL \p Idx + /// + /// G_CONSTANT_POOL materializes the address of an object in the constant + /// pool. + /// + /// \pre setBasicBlock or setMI must have been called. + /// \pre \p Res must be a generic virtual register with pointer type. + /// + /// \return a MachineInstrBuilder for the newly created instruction. + MachineInstrBuilder buildConstantPool(const DstOp &Res, unsigned Idx); + /// Build and insert \p Res = G_PTR_ADD \p Op0, \p Op1 /// /// G_PTR_ADD adds \p Op1 addressible units to the pointer specified by \p Op0, diff --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def --- a/llvm/include/llvm/Support/TargetOpcodes.def +++ b/llvm/include/llvm/Support/TargetOpcodes.def @@ -290,6 +290,10 @@ /// Generic reference to global value. HANDLE_TARGET_OPCODE(G_GLOBAL_VALUE) +/// Generic instruction to materialize the address of an object in the constant +/// pool. +HANDLE_TARGET_OPCODE(G_CONSTANT_POOL) + /// Generic instruction to extract blocks of bits from the register given /// (typically a sub-register COPY after instruction selection). HANDLE_TARGET_OPCODE(G_EXTRACT) diff --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td --- a/llvm/include/llvm/Target/GenericOpcodes.td +++ b/llvm/include/llvm/Target/GenericOpcodes.td @@ -106,6 +106,12 @@ let hasSideEffects = false; } +def G_CONSTANT_POOL : GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins unknown:$src); + let hasSideEffects = false; +} + def G_INTTOPTR : GenericInstruction { let OutOperandList = (outs type0:$dst); let InOperandList = (ins type1:$src); diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp --- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp +++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp @@ -21,6 +21,7 @@ #include "llvm/CodeGen/GlobalISel/MIPatternMatch.h" #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" #include "llvm/CodeGen/GlobalISel/Utils.h" +#include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/TargetFrameLowering.h" @@ -2630,6 +2631,32 @@ Pieces.push_back(Unmerge.getReg(I)); } +LegalizerHelper::LegalizeResult +LegalizerHelper::lowerFConstant(MachineInstr &MI) { + Register Dst = MI.getOperand(0).getReg(); + + MachineFunction &MF = MIRBuilder.getMF(); + const DataLayout &DL = MIRBuilder.getDataLayout(); + + unsigned AddrSpace = DL.getDefaultGlobalsAddressSpace(); + LLT AddrPtrTy = LLT::pointer(AddrSpace, DL.getPointerSizeInBits(AddrSpace)); + Align Alignment = Align(DL.getABITypeAlign( + getFloatTypeForLLT(MF.getFunction().getContext(), MRI.getType(Dst)))); + + auto Addr = MIRBuilder.buildConstantPool( + AddrPtrTy, MF.getConstantPool()->getConstantPoolIndex( + MI.getOperand(1).getFPImm(), Alignment)); + + MachineMemOperand *MMO = MF.getMachineMemOperand( + MachinePointerInfo::getConstantPool(MF), MachineMemOperand::MOLoad, + MRI.getType(Dst), Alignment); + + MIRBuilder.buildLoadInstr(TargetOpcode::G_LOAD, Dst, Addr, *MMO); + MI.eraseFromParent(); + + return Legalized; +} + LegalizerHelper::LegalizeResult LegalizerHelper::lowerBitcast(MachineInstr &MI) { Register Dst = MI.getOperand(0).getReg(); @@ -3250,6 +3277,8 @@ switch(MI.getOpcode()) { default: return UnableToLegalize; + case TargetOpcode::G_FCONSTANT: + return lowerFConstant(MI); case TargetOpcode::G_BITCAST: return lowerBitcast(MI); case TargetOpcode::G_SREM: diff --git a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp --- a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp +++ b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp @@ -164,6 +164,15 @@ return MIB; } +MachineInstrBuilder MachineIRBuilder::buildConstantPool(const DstOp &Res, + unsigned Idx) { + assert(Res.getLLTTy(*getMRI()).isPointer() && "invalid operand type"); + auto MIB = buildInstr(TargetOpcode::G_CONSTANT_POOL); + Res.addDefToMIB(*getMRI(), MIB); + MIB.addConstantPoolIndex(Idx); + return MIB; +} + MachineInstrBuilder MachineIRBuilder::buildJumpTable(const LLT PtrTy, unsigned JTI) { return buildInstr(TargetOpcode::G_JUMP_TABLE, {PtrTy}, {}) diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp --- a/llvm/lib/CodeGen/MachineVerifier.cpp +++ b/llvm/lib/CodeGen/MachineVerifier.cpp @@ -1746,6 +1746,13 @@ report("alignment immediate must be >= 1", MI); break; } + case TargetOpcode::G_CONSTANT_POOL: { + if (!MI->getOperand(1).isCPI()) + report("Src operand 1 must be a constant pool index", MI); + if (!MRI->getType(MI->getOperand(0).getReg()).isPointer()) + report("Dst operand 0 must be a pointer", MI); + break; + } default: break; } diff --git a/llvm/lib/Target/PowerPC/GISel/PPCInstructionSelector.cpp b/llvm/lib/Target/PowerPC/GISel/PPCInstructionSelector.cpp --- a/llvm/lib/Target/PowerPC/GISel/PPCInstructionSelector.cpp +++ b/llvm/lib/Target/PowerPC/GISel/PPCInstructionSelector.cpp @@ -12,6 +12,7 @@ #include "PPC.h" #include "PPCInstrInfo.h" +#include "PPCMachineFunctionInfo.h" #include "PPCRegisterBankInfo.h" #include "PPCSubtarget.h" #include "PPCTargetMachine.h" @@ -54,6 +55,8 @@ bool selectZExt(MachineInstr &I, MachineBasicBlock &MBB, MachineRegisterInfo &MRI) const; + bool selectConstantPool(MachineInstr &I, MachineBasicBlock &MBB, + MachineRegisterInfo &MRI) const; std::optional selectI64ImmDirect(MachineInstr &I, MachineBasicBlock &MBB, @@ -62,6 +65,7 @@ bool selectI64Imm(MachineInstr &I, MachineBasicBlock &MBB, MachineRegisterInfo &MRI) const; + const PPCTargetMachine &TM; const PPCSubtarget &STI; const PPCInstrInfo &TII; const PPCRegisterInfo &TRI; @@ -85,7 +89,8 @@ PPCInstructionSelector::PPCInstructionSelector(const PPCTargetMachine &TM, const PPCSubtarget &STI, const PPCRegisterBankInfo &RBI) - : STI(STI), TII(*STI.getInstrInfo()), TRI(*STI.getRegisterInfo()), RBI(RBI), + : TM(TM), STI(STI), TII(*STI.getInstrInfo()), TRI(*STI.getRegisterInfo()), + RBI(RBI), #define GET_GLOBALISEL_PREDICATES_INIT #include "PPCGenGlobalISel.inc" #undef GET_GLOBALISEL_PREDICATES_INIT @@ -636,6 +641,66 @@ return true; } +bool PPCInstructionSelector::selectConstantPool( + MachineInstr &I, MachineBasicBlock &MBB, MachineRegisterInfo &MRI) const { + const DebugLoc &DbgLoc = I.getDebugLoc(); + MachineFunction *MF = MBB.getParent(); + + // TODO: handle 32-bit. + // TODO: Enabling floating point constant pool selection on AIX requires + // global isel on big endian target enabled first. + // See CallLowering::enableBigEndian(). + if (!STI.isPPC64() || !STI.isLittleEndian()) + return false; + + MF->getInfo()->setUsesTOCBasePtr(); + + const Register DstReg = I.getOperand(0).getReg(); + unsigned CPI = I.getOperand(1).getIndex(); + + // Address stored in the TOC entry. This is related to code model and the ABI + // we are currently using. For now we only handle 64-bit Linux LE. PowerPC + // only supports small, medium and large code model. + const CodeModel::Model CModel = TM.getCodeModel(); + assert(!(CModel == CodeModel::Tiny || CModel == CodeModel::Kernel) && + "PowerPC doesn't support tiny or kernel code models."); + + const MCRegister TOCReg = STI.getTOCPointerRegister(); + MachineMemOperand *MMO = MF->getMachineMemOperand( + MachinePointerInfo::getGOT(*MF), MachineMemOperand::MOLoad, + MRI.getType(DstReg), MF->getDataLayout().getPointerABIAlignment(0)); + + MachineInstr *MI = nullptr; + // For now we only handle 64-bit Linux. + if (CModel == CodeModel::Small) { + // For small code model, generate LDtocCPT(CPI, X2). + MI = BuildMI(MBB, I, DbgLoc, TII.get(PPC::LDtocCPT), DstReg) + .addConstantPoolIndex(CPI) + .addReg(TOCReg) + .addMemOperand(MMO); + } else { + Register HaAddrReg = MRI.createVirtualRegister(&PPC::G8RCRegClass); + BuildMI(MBB, I, DbgLoc, TII.get(PPC::ADDIStocHA8), HaAddrReg) + .addReg(TOCReg) + .addConstantPoolIndex(CPI); + + if (CModel == CodeModel::Large) + // For large code model, generate LDtocL(CPI, ADDIStocHA8(X2, CPI)) + MI = BuildMI(MBB, I, DbgLoc, TII.get(PPC::LDtocL), DstReg) + .addConstantPoolIndex(CPI) + .addReg(HaAddrReg) + .addMemOperand(MMO); + else + // For medium code model, generate ADDItocL(CPI, ADDIStocHA8(X2, CPI)) + MI = BuildMI(MBB, I, DbgLoc, TII.get(PPC::ADDItocL), DstReg) + .addReg(HaAddrReg) + .addConstantPoolIndex(CPI); + } + + I.eraseFromParent(); + return constrainSelectedInstRegOperands(*MI, TII, TRI, RBI); +} + bool PPCInstructionSelector::select(MachineInstr &I) { auto &MBB = *I.getParent(); auto &MF = *MBB.getParent(); @@ -704,6 +769,8 @@ return selectZExt(I, MBB, MRI); case TargetOpcode::G_CONSTANT: return selectI64Imm(I, MBB, MRI); + case TargetOpcode::G_CONSTANT_POOL: + return selectConstantPool(I, MBB, MRI); } return false; } diff --git a/llvm/lib/Target/PowerPC/GISel/PPCLegalizerInfo.cpp b/llvm/lib/Target/PowerPC/GISel/PPCLegalizerInfo.cpp --- a/llvm/lib/Target/PowerPC/GISel/PPCLegalizerInfo.cpp +++ b/llvm/lib/Target/PowerPC/GISel/PPCLegalizerInfo.cpp @@ -54,5 +54,8 @@ getActionDefinitionsBuilder({G_LOAD, G_STORE}) .legalForTypesWithMemDesc({{S64, P0, S64, 8}, {S32, P0, S32, 4}}); + getActionDefinitionsBuilder(G_FCONSTANT).lowerFor({S32, S64}); + getActionDefinitionsBuilder(G_CONSTANT_POOL).legalFor({P0}); + getLegacyLegalizerInfo().computeTables(); } diff --git a/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.cpp b/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.cpp --- a/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.cpp +++ b/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.cpp @@ -118,6 +118,9 @@ case TargetOpcode::G_CONSTANT: OperandsMapping = getOperandsMapping({getValueMapping(PMI_GPR64), nullptr}); break; + case TargetOpcode::G_CONSTANT_POOL: + OperandsMapping = getOperandsMapping({getValueMapping(PMI_GPR64), nullptr}); + break; case TargetOpcode::G_FPTOUI: case TargetOpcode::G_FPTOSI: { Register SrcReg = MI.getOperand(1).getReg(); diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir --- a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir +++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir @@ -86,6 +86,10 @@ # DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected # DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected # +# DEBUG-NEXT: G_CONSTANT_POOL (opcode {{[0-9]+}}): 1 type index, 0 imm indices +# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined +# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined +# # DEBUG-NEXT: G_EXTRACT (opcode {{[0-9]+}}): 2 type indices, 1 imm index # DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected # DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected diff --git a/llvm/test/CodeGen/PowerPC/GlobalISel/fconstant-unsupported.ll b/llvm/test/CodeGen/PowerPC/GlobalISel/fconstant-unsupported.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/GlobalISel/fconstant-unsupported.ll @@ -0,0 +1,13 @@ +; RUN: not --crash llc -global-isel -mtriple=powerpc-unknown-linux-gnu \ +; RUN: -o - < %s 2>&1 | FileCheck %s --check-prefix=BE +; RUN: not --crash llc -global-isel -mtriple=powerpcle-unknown-linux-gnu \ +; RUN: -o - < %s 2>&1 | FileCheck %s --check-prefix=BIT32 + +; BE: LLVM ERROR: unable to translate in big endian mode + +; BIT32: LLVM ERROR: unable to legalize instruction: [[LOAD:%[0-9]+]]:_(s64) = G_LOAD{{.*}}load (s64) from constant-pool + +define double @foo() { + entry: + ret double 1.000000e+00 +} diff --git a/llvm/test/CodeGen/PowerPC/GlobalISel/fconstant.ll b/llvm/test/CodeGen/PowerPC/GlobalISel/fconstant.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/GlobalISel/fconstant.ll @@ -0,0 +1,56 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py + +; RUN: llc -mtriple=powerpc64le-unknown-linux-gnu -global-isel -code-model=small \ +; RUN: -verify-machineinstrs -o - < %s | FileCheck %s --check-prefix=SMALL +; RUN: llc -mtriple=powerpc64le-unknown-linux-gnu -global-isel -code-model=medium \ +; RUN: -verify-machineinstrs -o - < %s | FileCheck %s --check-prefix=MEDIUM +; RUN: llc -mtriple=powerpc64le-unknown-linux-gnu -global-isel -code-model=large \ +; RUN: -verify-machineinstrs -o - < %s | FileCheck %s --check-prefix=LARGE + +define float @foo_float() { +; SMALL-LABEL: foo_float: +; SMALL: # %bb.0: # %entry +; SMALL-NEXT: ld 3, .LC0@toc(2) +; SMALL-NEXT: lfs 1, 0(3) +; SMALL-NEXT: blr +; +; MEDIUM-LABEL: foo_float: +; MEDIUM: # %bb.0: # %entry +; MEDIUM-NEXT: addis 3, 2, .LCPI0_0@toc@ha +; MEDIUM-NEXT: addi 3, 3, .LCPI0_0@toc@l +; MEDIUM-NEXT: lfs 1, 0(3) +; MEDIUM-NEXT: blr +; +; LARGE-LABEL: foo_float: +; LARGE: # %bb.0: # %entry +; LARGE-NEXT: addis 3, 2, .LC0@toc@ha +; LARGE-NEXT: ld 3, .LC0@toc@l(3) +; LARGE-NEXT: lfs 1, 0(3) +; LARGE-NEXT: blr +entry: + ret float 1.000000e+00 +} + +define double @foo_double() { +; SMALL-LABEL: foo_double: +; SMALL: # %bb.0: # %entry +; SMALL-NEXT: ld 3, .LC1@toc(2) +; SMALL-NEXT: lfd 1, 0(3) +; SMALL-NEXT: blr +; +; MEDIUM-LABEL: foo_double: +; MEDIUM: # %bb.0: # %entry +; MEDIUM-NEXT: addis 3, 2, .LCPI1_0@toc@ha +; MEDIUM-NEXT: addi 3, 3, .LCPI1_0@toc@l +; MEDIUM-NEXT: lfd 1, 0(3) +; MEDIUM-NEXT: blr +; +; LARGE-LABEL: foo_double: +; LARGE: # %bb.0: # %entry +; LARGE-NEXT: addis 3, 2, .LC1@toc@ha +; LARGE-NEXT: ld 3, .LC1@toc@l(3) +; LARGE-NEXT: lfd 1, 0(3) +; LARGE-NEXT: blr +entry: + ret double 1.000000e+00 +} diff --git a/llvm/test/MachineVerifier/test_g_constant_pool.mir b/llvm/test/MachineVerifier/test_g_constant_pool.mir new file mode 100644 --- /dev/null +++ b/llvm/test/MachineVerifier/test_g_constant_pool.mir @@ -0,0 +1,40 @@ +# RUN: not --crash llc -o - -march=arm64 -global-isel -run-pass=none \ +# RUN: -verify-machineinstrs %s 2>&1 | FileCheck %s +# REQUIRES: aarch64-registered-target + +--- +name: test_constant_pool +constants: + - id: 0 + value: 'double 3.250000e+00' +stack: + - { id: 0, size: 64, alignment: 8 } +legalized: true +regBankSelected: false +selected: false +tracksRegLiveness: true +liveins: +body: | + bb.0: + + ; CHECK: Bad machine code: Too few operands + %0:_(p0) = G_CONSTANT_POOL + + ; CHECK: Bad machine code: Src operand 1 must be a constant pool index + %1:_(p0) = G_CONSTANT_POOL 1 + + ; CHECK: Bad machine code: Src operand 1 must be a constant pool index + %2:_(p0) = G_CONSTANT_POOL i32 1 + + ; CHECK: Bad machine code: Src operand 1 must be a constant pool index + %3:_(p0) = G_CONSTANT_POOL %stack.0 + + ; CHECK: Dst operand 0 must be a pointer + %4:_(s32) = G_CONSTANT_POOL %const.0 + + ; CHECK: Dst operand 0 must be a pointer + %5:_(s64) = G_CONSTANT_POOL %const.0 + + ; CHECK-NOT: %6 + %6:_(p0) = G_CONSTANT_POOL %const.0 +...