Index: include/llvm/CodeGen/GlobalISel/InstructionSelector.h =================================================================== --- include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -93,6 +93,10 @@ /// - InsnID - Instruction ID /// - Expected number of operands GIM_CheckNumOperands, + /// Check an immediate predicate on the specified truction + /// - InsnID - Instruction ID + /// - The predicate to test + GIM_CheckImmPredicate, /// Check the type for the specified operand /// - InsnID - Instruction ID @@ -213,6 +217,8 @@ /// Provides the logic to select generic machine instructions. class InstructionSelector { public: + typedef bool(*ImmediatePredicateFn)(int64_t); + virtual ~InstructionSelector() = default; /// Select the (possibly generic) instruction \p I to only use target-specific @@ -245,6 +251,7 @@ struct MatcherInfoTy { const LLT *TypeObjects; const PredicateBitset *FeatureBitsets; + const ImmediatePredicateFn *ImmPredicateFns; const std::vector ComplexPredicates; }; Index: include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h =================================================================== --- include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h +++ include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h @@ -126,6 +126,26 @@ } break; } + case GIM_CheckImmPredicate: { + int64_t InsnID = MatchTable[CurrentIdx++]; + int64_t Predicate = MatchTable[CurrentIdx++]; + DEBUG(dbgs() << CurrentIdx << ": GIM_CheckImmPredicate(MIs[" << InsnID + << "], Predicate=" << Predicate << ")\n"); + assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); + assert(State.MIs[InsnID]->getOpcode() && "Expected G_CONSTANT"); + int64_t Value = 0; + if (State.MIs[InsnID]->getOperand(1).isCImm()) + Value = State.MIs[InsnID]->getOperand(1).getCImm()->getSExtValue(); + else if (State.MIs[InsnID]->getOperand(1).isImm()) + Value = State.MIs[InsnID]->getOperand(1).getImm(); + else + llvm_unreachable("Expected Imm or CImm operand"); + + if (!MatcherInfo.ImmPredicateFns[Predicate](Value)) + if (handleReject() == RejectAndGiveUp) + return false; + break; + } case GIM_CheckType: { int64_t InsnID = MatchTable[CurrentIdx++]; Index: test/CodeGen/AArch64/GlobalISel/select-neon-vcvtfxu2fp.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/select-neon-vcvtfxu2fp.mir @@ -0,0 +1,37 @@ +# RUN: llc -mtriple=aarch64-- -mattr=+neon,+fullfp16 -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 @vcvtfxu2fp_s64_fpr() { ret void } +... + +--- +# Check that we select a 64-bit FPR vcvtfxu2fp intrinsic into UCVTFd for FPR64. +# CHECK-LABEL: name: vcvtfxu2fp_s64_fpr +name: vcvtfxu2fp_s64_fpr +legalized: true +regBankSelected: true + +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: fpr64, preferred-register: '' } +# CHECK-NEXT: - { id: 1, class: gpr, preferred-register: '' } +# CHECK-NEXT: - { id: 2, class: fpr64, preferred-register: '' } +registers: + - { id: 0, class: fpr } + - { id: 1, class: gpr } + - { id: 2, class: fpr } + +# CHECK: body: +# CHECK: %0 = COPY %d0 +# CHECK: %2 = UCVTFd %0, 12 +# CHECK: %d1 = COPY %2 +body: | + bb.0: + liveins: %d0 + + %0(s64) = COPY %d0 + %1(s32) = G_CONSTANT i32 12 + %2(s64) = G_INTRINSIC intrinsic(@llvm.aarch64.neon.vcvtfxu2fp.f64), %0, %1 + %d1 = COPY %2(s64) +... Index: test/TableGen/GlobalISelEmitter.td =================================================================== --- test/TableGen/GlobalISelEmitter.td +++ test/TableGen/GlobalISelEmitter.td @@ -53,7 +53,7 @@ // CHECK-LABEL: #ifdef GET_GLOBALISEL_TEMPORARIES_INIT // CHECK-NEXT: , State(2), -// CHECK-NEXT: MatcherInfo({TypeObjects, FeatureBitsets, { +// CHECK-NEXT: MatcherInfo({TypeObjects, FeatureBitsets, ImmPredicateFns, { // CHECK-NEXT: nullptr, // GICP_Invalid // CHECK-NEXT: &MyTargetInstructionSelector::selectComplexPattern, // gi_complex // CHECK-NEXT: }}) @@ -620,11 +620,35 @@ def MOV1 : I<(outs GPR32:$dst), (ins), [(set GPR32:$dst, 1)]>; -//===- Test a simple pattern with just a leaf immediate. ------------------===// +//===- Test a simple pattern with a leaf immediate and a predicate. -------===// // CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 16*/ [[LABEL:[0-9]+]], // CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2, // CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_CONSTANT, +// CHECK-NEXT: GIM_CheckImmPredicate, /*MI*/0, /*Predicate*/GIPFP_Predicate_simm8, +// 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: // MIs[0] Operand 1 +// CHECK-NEXT: // No predicates +// CHECK-NEXT: // (imm:i32)<>:$imm => (MOVimm8:i32 (imm:i32):$imm) +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MOVimm8, +// 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 16: @[[LABEL]] + +def simm8 : ImmLeaf(Imm); }]>; +def MOVimm8 : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, simm8:$imm)]>; + +//===- 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*/2, +// 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, @@ -638,13 +662,13 @@ // CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0, // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, // CHECK-NEXT: GIR_Done, -// CHECK-NEXT: // Label 16: @[[LABEL]] +// CHECK-NEXT: // Label 17: @[[LABEL]] def MOVimm : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, imm:$imm)]>; //===- Test a pattern with an MBB operand. --------------------------------===// -// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 17*/ [[LABEL:[0-9]+]], +// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 18*/ [[LABEL:[0-9]+]], // CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/1, // CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_BR, // CHECK-NEXT: // MIs[0] target @@ -653,7 +677,7 @@ // CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::BR, // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, // CHECK-NEXT: GIR_Done, -// CHECK-NEXT: // Label 17: @[[LABEL]] +// CHECK-NEXT: // Label 18: @[[LABEL]] def BR : I<(outs), (ins unknown:$target), [(br bb:$target)]>; Index: utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- utils/TableGen/GlobalISelEmitter.cpp +++ utils/TableGen/GlobalISelEmitter.cpp @@ -177,9 +177,19 @@ static Error isTrivialOperatorNode(const TreePatternNode *N) { std::string Explanation = ""; std::string Separator = ""; - if (N->hasAnyPredicate()) { + + bool HasUnsupportedPredicate = false; + for (const auto &Predicate : N->getPredicateFns()) { + if (Predicate.isAlwaysTrue()) + continue; + + if (Predicate.isImmediatePattern()) + continue; + + HasUnsupportedPredicate = true; Explanation = Separator + "Has a predicate (" + explainPredicates(N) + ")"; Separator = ", "; + break; } if (N->getTransformFn()) { @@ -187,7 +197,7 @@ Separator = ", "; } - if (!N->hasAnyPredicate() && !N->getTransformFn()) + if (!HasUnsupportedPredicate && !N->getTransformFn()) return Error::success(); return failedImport(Explanation); @@ -900,6 +910,7 @@ /// must be tested first. enum PredicateKind { IPM_Opcode, + IPM_ImmPredicate, }; PredicateKind Kind; @@ -974,6 +985,29 @@ } }; +/// Generates code to check that an operand meets an immediate predicate. +class InstructionImmPredicateMatcher : public InstructionPredicateMatcher { +protected: + TreePredicateFn Predicate; + +public: + InstructionImmPredicateMatcher(const TreePredicateFn &Predicate) + : InstructionPredicateMatcher(IPM_ImmPredicate), Predicate(Predicate) {} + + static bool classof(const InstructionPredicateMatcher *P) { + return P->getKind() == IPM_ImmPredicate; + } + + void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule, + unsigned InsnVarID) const override { + Table << MatchTable::Opcode("GIM_CheckImmPredicate") + << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) + << MatchTable::Comment("Predicate") + << MatchTable::NamedValue("GIPFP_" + Predicate.getFnName()) + << MatchTable::LineBreak; + } +}; + /// Generates code to check that a set of predicates and operands match for a /// particular instruction. /// @@ -1881,6 +1915,19 @@ OM.addPredicate(*OpTyOrNone); } + for (const auto &Predicate : Src->getPredicateFns()) { + if (Predicate.isAlwaysTrue()) + continue; + + if (Predicate.isImmediatePattern()) { + InsnMatcher.addPredicate(Predicate); + continue; + } + + return failedImport("Src pattern child has predicate (" + + explainPredicates(Src) + ")"); + } + if (Src->isLeaf()) { Init *SrcInit = Src->getLeafValue(); if (IntInit *SrcIntInit = dyn_cast(SrcInit)) { @@ -1933,10 +1980,6 @@ OperandMatcher &OM = InsnMatcher.addOperand(OpIdx, SrcChild->getName(), TempOpIdx); - if (SrcChild->hasAnyPredicate()) - return failedImport("Src pattern child has predicate (" + - explainPredicates(SrcChild) + ")"); - ArrayRef ChildTypes = SrcChild->getExtTypes(); if (ChildTypes.size() != 1) return failedImport("Src pattern child has multiple results"); @@ -2016,6 +2059,11 @@ Error GlobalISelEmitter::importExplicitUseRenderer( BuildMIAction &DstMIBuilder, TreePatternNode *DstChild, const InstructionMatcher &InsnMatcher) const { + if (DstChild->getTransformFn() != nullptr) { + return failedImport("Dst pattern child has transform fn " + + DstChild->getTransformFn()->getName()); + } + if (!DstChild->isLeaf()) { // We accept 'bb' here. It's an operator because BasicBlockSDNode isn't // inline, but in MI it's just another operand. @@ -2036,14 +2084,10 @@ return Error::success(); } - return failedImport("Dst pattern child isn't a leaf node or an MBB"); + return failedImport("Dst pattern child isn't a leaf node or an MBB" + llvm::to_string(*DstChild)); } // Otherwise, we're looking for a bog-standard RegisterClass operand. - if (DstChild->hasAnyPredicate()) - return failedImport("Dst pattern child has predicate (" + - explainPredicates(DstChild) + ")"); - if (auto *ChildDefInit = dyn_cast(DstChild->getLeafValue())) { auto *ChildRec = ChildDefInit->getDef(); @@ -2482,7 +2526,7 @@ OS << "#ifdef GET_GLOBALISEL_TEMPORARIES_INIT\n" << ", State(" << MaxTemporaries << "),\n" - << "MatcherInfo({TypeObjects, FeatureBitsets, {\n" + << "MatcherInfo({TypeObjects, FeatureBitsets, ImmPredicateFns, {\n" << " nullptr, // GICP_Invalid\n"; for (const auto &Record : ComplexPredicates) OS << " &" << Target.getName() @@ -2592,6 +2636,25 @@ OS << "};\n" << "// See constructor for table contents\n\n"; + // Emit imm predicate table and an enum to reference them with. + // The 'Predicate_' part of the name is redundant but eliminating it is more + // trouble than it's worth. + OS << "enum {\n" + << " GIPFP_Invalid,\n"; + for (const auto *Record : RK.getAllDerivedDefinitions("PatFrag")) + if (!Record->getValueAsString("ImmediateCode").empty()) + OS << " GIPFP_Predicate_" << Record->getName() << ",\n"; + OS << "};\n"; + for (const auto *Record : RK.getAllDerivedDefinitions("PatFrag")) + if (!Record->getValueAsString("ImmediateCode").empty()) + OS << " static bool Predicate_" << Record->getName() << "(int64_t Imm) {" + << Record->getValueAsString("ImmediateCode") << " }\n"; + OS << "static InstructionSelector::ImmediatePredicateFn ImmPredicateFns[] = {\n"; + for (const auto *Record : RK.getAllDerivedDefinitions("PatFrag")) + if (!Record->getValueAsString("ImmediateCode").empty()) + OS << " Predicate_" << Record->getName() << ",\n"; + OS << "};\n"; + OS << "bool " << Target.getName() << "InstructionSelector::selectImpl(MachineInstr &I) const {\n" << " MachineFunction &MF = *I.getParent()->getParent();\n"