Index: include/llvm/CodeGen/GlobalISel/InstructionSelector.h =================================================================== --- include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -184,6 +184,12 @@ /// - RendererID - The renderer to call GIR_ComplexRenderer, + /// Render a G_CONSTANT operator as an immediate. + /// - NewInsnID - Instruction ID to modify + /// - OldInsnID - Instruction ID to copy from + /// The operand index is implicitly 1. + GIR_CopyConstantAsImm, + /// Constrain an instruction operand to a register class. /// - InsnID - Instruction ID to modify /// - OpIdx - Operand index Index: include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h =================================================================== --- include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h +++ include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h @@ -346,6 +346,19 @@ break; } + case GIR_CopyConstantAsImm: { + int64_t NewInsnID = MatchTable[CurrentIdx++]; + int64_t OldInsnID = MatchTable[CurrentIdx++]; + assert(OutMIs[NewInsnID] && "Attempted to add to undefined instruction"); + assert(State.MIs[OldInsnID]->getOperand(1).isCImm() && + "Expected CImm operand"); + OutMIs[NewInsnID].addImm( + State.MIs[OldInsnID]->getOperand(1).getCImm()->getSExtValue()); + DEBUG(dbgs() << CurrentIdx << ": GIR_CopyConstantAsImm(OutMIs[" << NewInsnID + << "], MIs[" << OldInsnID << "])\n"); + break; + } + case GIR_ConstrainOperandRC: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; Index: include/llvm/Target/GlobalISel/SelectionDAGCompat.td =================================================================== --- include/llvm/Target/GlobalISel/SelectionDAGCompat.td +++ include/llvm/Target/GlobalISel/SelectionDAGCompat.td @@ -33,7 +33,7 @@ def : GINodeEquiv; // G_INTTOPTR - SelectionDAG has no equivalent. // G_PTRTOINT - SelectionDAG has no equivalent. -// G_CONSTANT - Not needed since constants aren't operators. +def : GINodeEquiv; // G_FCONSTANT - Not needed since constants aren't operators. def : GINodeEquiv; def : GINodeEquiv; Index: test/CodeGen/AArch64/GlobalISel/select-imm.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/select-imm.mir @@ -0,0 +1,53 @@ +# RUN: llc -O0 -mtriple=aarch64-- -run-pass=instruction-select -verify-machineinstrs -global-isel %s -o - | FileCheck %s + +--- | + target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + + define void @imm_s32_gpr() { ret void } + define void @imm_s64_gpr() { ret void } + +... + +--- +# Check that we select a 32-bit immediate into a MOVi32imm. +# CHECK-LABEL: name: imm_s32_gpr +name: imm_s32_gpr +legalized: true +regBankSelected: true + +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gpr32, preferred-register: '' } +registers: + - { id: 0, class: gpr } + +# CHECK: body: +# CHECK: %0 = MOVi32imm 1234 +body: | + bb.0: + liveins: %w0, %w1 + + %0(s32) = G_CONSTANT i32 1234 + %w0 = COPY %0(s32) +... + +--- +# Check that we select a 64-bit immediate into a MOVi64imm. +# CHECK-LABEL: name: imm_s64_gpr +name: imm_s64_gpr +legalized: true +regBankSelected: true + +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gpr64, preferred-register: '' } +registers: + - { id: 0, class: gpr } + +# CHECK: body: +# CHECK: %0 = MOVi64imm 1234 +body: | + bb.0: + liveins: %w0, %w1 + + %0(s64) = G_CONSTANT i64 1234 + %w0 = COPY %0(s64) +... Index: test/TableGen/GlobalISelEmitter.td =================================================================== --- test/TableGen/GlobalISelEmitter.td +++ test/TableGen/GlobalISelEmitter.td @@ -599,7 +599,7 @@ def : Pat<(i32 (bitconvert FPR32:$src1)), (COPY_TO_REGCLASS FPR32:$src1, GPR32)>; -//===- Test a simple pattern with just a leaf immediate. ------------------===// +//===- Test a simple pattern with just a specific leaf immediate. ---------===// // CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 15*/ [[LABEL:[0-9]+]], // CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2, @@ -632,11 +632,32 @@ // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, // CHECK-NEXT: GIR_Done, // CHECK-NEXT: // Label 16: @[[LABEL]] + +def BR : I<(outs), (ins unknown:$target), + [(br bb:$target)]>; + +//===- Test a simple pattern with just a leaf immediate. ------------------===// + +// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 17*/ [[LABEL:[0-9]+]], +// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/1, +// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_CONSTANT, +// CHECK-NEXT: // MIs[0] dst +// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32, +// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID, +// CHECK-NEXT: // (imm:i32):$imm => (MOVimm:i32 (imm:i32):$imm) +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MOVimm, +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst +// CHECK-NEXT: GIR_CopyConstantAsImm, /*NewInsnID*/0, /*OldInsnID*/0, // imm +// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0, +// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0, +// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, +// CHECK-NEXT: GIR_Done, +// CHECK-NEXT: // Label 17: @[[LABEL]] + +def MOVimm : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, imm:$imm)]>; + // CHECK-NEXT: GIM_Reject, // CHECK-NEXT: }; // CHECK-NEXT: if (executeMatchTable(*this, OutMIs, State, MatcherInfo, MatchTable0, TII, MRI, TRI, RBI, AvailableFeatures)) { // CHECK-NEXT: return true; // CHECK-NEXT: } - -def BR : I<(outs), (ins unknown:$target), - [(br bb:$target)]>; Index: utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- utils/TableGen/GlobalISelEmitter.cpp +++ utils/TableGen/GlobalISelEmitter.cpp @@ -475,7 +475,7 @@ RuleMatcher(RuleMatcher &&Other) = default; RuleMatcher &operator=(RuleMatcher &&Other) = default; - InstructionMatcher &addInstructionMatcher(); + InstructionMatcher &addInstructionMatcher(StringRef SymbolicName); void addRequiredFeature(Record *Feature); const std::vector &getRequiredFeatures() const; @@ -489,6 +489,8 @@ unsigned InsnVarID, unsigned OpIdx); unsigned getInsnVarID(const InstructionMatcher &InsnMatcher) const; + const InstructionMatcher &getInstructionMatcher(StringRef SymbolicName) const; + void emitCaptureOpcodes(MatchTable &Table); void emit(MatchTable &Table); @@ -992,7 +994,11 @@ /// condition is always true. OperandVec Operands; + std::string SymbolicName; + public: + InstructionMatcher(StringRef SymbolicName) : SymbolicName(SymbolicName) {} + /// Add an operand to the matcher. OperandMatcher &addOperand(unsigned OpIdx, const std::string &SymbolicName, unsigned AllocatedTemporariesBaseID) { @@ -1029,6 +1035,7 @@ llvm_unreachable("Failed to lookup operand"); } + StringRef getSymbolicName() const { return SymbolicName; } unsigned getNumOperands() const { return Operands.size(); } OperandVec::iterator operands_begin() { return Operands.begin(); } OperandVec::iterator operands_end() { return Operands.end(); } @@ -1120,9 +1127,9 @@ std::unique_ptr InsnMatcher; public: - InstructionOperandMatcher() + InstructionOperandMatcher(StringRef SymbolicName) : OperandPredicateMatcher(OPM_Instruction), - InsnMatcher(new InstructionMatcher()) {} + InsnMatcher(new InstructionMatcher(SymbolicName)) {} static bool classof(const OperandPredicateMatcher *P) { return P->getKind() == OPM_Instruction; @@ -1156,6 +1163,7 @@ enum RendererKind { OR_Copy, OR_CopySubReg, + OR_CopyConstantAsImm, OR_Imm, OR_Register, OR_ComplexPattern @@ -1190,7 +1198,9 @@ CopyRenderer(unsigned NewInsnID, const InstructionMatcher &Matched, StringRef SymbolicName) : OperandRenderer(OR_Copy), NewInsnID(NewInsnID), Matched(Matched), - SymbolicName(SymbolicName) {} + SymbolicName(SymbolicName) { + assert(!SymbolicName.empty() && "Cannot copy from an unspecified source"); + } static bool classof(const OperandRenderer *R) { return R->getKind() == OR_Copy; @@ -1209,6 +1219,36 @@ } }; +/// A CopyConstantAsImmRenderer emits code to render a G_CONSTANT instruction to +/// a immediate operand. +class CopyConstantAsImmRenderer : public OperandRenderer { +protected: + unsigned NewInsnID; + /// The name of the operand. + const std::string SymbolicName; + +public: + CopyConstantAsImmRenderer(unsigned NewInsnID, StringRef SymbolicName) + : OperandRenderer(OR_CopyConstantAsImm), NewInsnID(NewInsnID), + SymbolicName(SymbolicName) {} + + static bool classof(const OperandRenderer *R) { + return R->getKind() == OR_CopyConstantAsImm; + } + + const StringRef getSymbolicName() const { return SymbolicName; } + + void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override { + const InstructionMatcher &InsnMatcher = Rule.getInstructionMatcher(SymbolicName); + unsigned OldInsnVarID = Rule.getInsnVarID(InsnMatcher); + Table << MatchTable::Opcode("GIR_CopyConstantAsImm") + << MatchTable::Comment("NewInsnID") << MatchTable::IntValue(NewInsnID) + << MatchTable::Comment("OldInsnID") + << MatchTable::IntValue(OldInsnVarID) + << MatchTable::Comment(SymbolicName) << MatchTable::LineBreak; + } +}; + /// A CopySubRegRenderer emits code to copy a single register operand from an /// existing instruction to the one being built and indicate that only a /// subregister should be copied. @@ -1496,8 +1536,8 @@ } }; -InstructionMatcher &RuleMatcher::addInstructionMatcher() { - Matchers.emplace_back(new InstructionMatcher()); +InstructionMatcher &RuleMatcher::addInstructionMatcher(StringRef SymbolicName) { + Matchers.emplace_back(new InstructionMatcher(SymbolicName)); return *Matchers.back(); } @@ -1542,6 +1582,15 @@ llvm_unreachable("Matched Insn was not captured in a local variable"); } +const InstructionMatcher & +RuleMatcher::getInstructionMatcher(StringRef SymbolicName) const { + for (const auto &I : InsnVariableIDs) + if (I.first->getSymbolicName() == SymbolicName) + return *I.first; + llvm_unreachable( + ("Failed to lookup instruction " + SymbolicName).str().c_str()); +} + /// Emit MatchTable opcodes to check the shape of the match and capture /// instructions into local variables. void RuleMatcher::emitCaptureOpcodes(MatchTable &Table) { @@ -1875,7 +1924,7 @@ if (!SrcChild->isLeaf()) { // Map the node to a gMIR instruction. InstructionOperandMatcher &InsnOperand = - OM.addPredicate(); + OM.addPredicate(SrcChild->getName()); auto InsnMatcherOrError = createAndImportSelDAGMatcher( InsnOperand.getInsnMatcher(), SrcChild, TempOpIdx); if (auto Error = InsnMatcherOrError.takeError()) @@ -1930,9 +1979,9 @@ Error GlobalISelEmitter::importExplicitUseRenderer( BuildMIAction &DstMIBuilder, TreePatternNode *DstChild, const InstructionMatcher &InsnMatcher) const { - // The only non-leaf child we accept is 'bb': it's an operator because - // BasicBlockSDNode isn't inline, but in MI it's just another operand. if (!DstChild->isLeaf()) { + // We accept 'bb' here. It's an operator because BasicBlockSDNode isn't + // inline, but in MI it's just another operand. if (DstChild->getOperator()->isSubClassOf("SDNode")) { auto &ChildSDNI = CGP.getSDNodeInfo(DstChild->getOperator()); if (ChildSDNI.getSDClassName() == "BasicBlockSDNode") { @@ -1941,6 +1990,15 @@ return Error::success(); } } + + // Similarly, imm is an operator in TreePatternNode's view but must be + // rendered as operands. + if (DstChild->getOperator()->getName() == "imm") { + DstMIBuilder.addRenderer(0, + DstChild->getName()); + return Error::success(); + } + return failedImport("Dst pattern child isn't a leaf node or an MBB"); } @@ -2164,7 +2222,7 @@ to_string(Src->getExtTypes().size()) + " def(s) vs " + to_string(DstI.Operands.NumDefs) + " def(s))"); - InstructionMatcher &InsnMatcherTemp = M.addInstructionMatcher(); + InstructionMatcher &InsnMatcherTemp = M.addInstructionMatcher(Src->getName()); unsigned TempOpIdx = 0; auto InsnMatcherOrError = createAndImportSelDAGMatcher(InsnMatcherTemp, Src, TempOpIdx);