/// - InsnID - Instruction ID
/// - Expected number of operands
GIM_CheckNumOperands,
- /// Check an immediate predicate on the specified instruction
- /// - InsnID - Instruction ID
- /// - The predicate to test
- GIM_CheckImmPredicate,
/// Check the type for the specified operand
/// - InsnID - Instruction ID
/// 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
struct MatcherInfoTy {
const LLT *TypeObjects;
const PredicateBitset *FeatureBitsets;
- const ImmediatePredicateFn *ImmPredicateFns;
const std::vector<ComplexMatcherMemFn> ComplexPredicates;
};
#define LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTORIMPL_H
namespace llvm {
-
-/// GlobalISel PatFrag Predicates
-enum {
- GIPFP_Invalid,
-};
-
template <class TgtInstructionSelector, class PredicateBitset,
class ComplexMatcherMemFn>
bool InstructionSelector::executeMatchTable(
}
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() == TargetOpcode::G_CONSTANT &&
- "Expected G_CONSTANT");
- assert(Predicate > GIPFP_Invalid && "Expected a valid predicate");
- 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++];
+++ /dev/null
-# 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)
-...
// CHECK-LABEL: #ifdef GET_GLOBALISEL_TEMPORARIES_INIT
// CHECK-NEXT: , State(2),
-// CHECK-NEXT: MatcherInfo({TypeObjects, FeatureBitsets, ImmPredicateFns, {
+// CHECK-NEXT: MatcherInfo({TypeObjects, FeatureBitsets, {
// CHECK-NEXT: nullptr, // GICP_Invalid
// CHECK-NEXT: &MyTargetInstructionSelector::selectComplexPattern, // gi_complex
// CHECK-NEXT: }})
// CHECK-NEXT: GICP_gi_complex,
// CHECK-NEXT: };
-// CHECK-LABEL: // PatFrag predicates.
-// CHECK-NEXT: enum {
-// CHECK-NEXT: GIPFP_Predicate_simm8 = GIPFP_Invalid,
-// CHECK-NEXT: };
-// CHECK-NEXT: static bool Predicate_simm8(int64_t Imm) { return isInt<8>(Imm); }
-// CHECK-NEXT: static InstructionSelector::ImmediatePredicateFn ImmPredicateFns[] = {
-// CHECK-NEXT: Predicate_simm8,
-// CHECK-NEXT: };
-
// CHECK: bool MyTargetInstructionSelector::selectImpl(MachineInstr &I) const {
// CHECK-NEXT: MachineFunction &MF = *I.getParent()->getParent();
// CHECK-NEXT: MachineRegisterInfo &MRI = MF.getRegInfo();
def MOV1 : I<(outs GPR32:$dst), (ins), [(set GPR32:$dst, 1)]>;
-//===- 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 operand predicates
-// CHECK-NEXT: // (imm:i32)<<P:Predicate_simm8>>:$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_CopyConstantAsSImm, /*NewInsnID*/0, /*OldInsnID*/0, // imm
-// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
-// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
-// CHECK-NEXT: GIR_Done,
-// CHECK-NEXT: // Label 16: @[[LABEL]]
-
-def simm8 : ImmLeaf<i32, [{ return isInt<8>(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_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: // 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 operand predicates
+// CHECK-NEXT: // No predicates
// 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_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
-// CHECK-NEXT: // Label 17: @[[LABEL]]
+// CHECK-NEXT: // Label 16: @[[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 18*/ [[LABEL:[0-9]+]],
+// 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_BR,
// CHECK-NEXT: // MIs[0] target
// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::BR,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
-// CHECK-NEXT: // Label 18: @[[LABEL]]
+// CHECK-NEXT: // Label 17: @[[LABEL]]
def BR : I<(outs), (ins unknown:$target),
[(br bb:$target)]>;
static Error isTrivialOperatorNode(const TreePatternNode *N) {
std::string Explanation = "";
std::string Separator = "";
-
- bool HasUnsupportedPredicate = false;
- for (const auto &Predicate : N->getPredicateFns()) {
- if (Predicate.isAlwaysTrue())
- continue;
-
- if (Predicate.isImmediatePattern())
- continue;
-
- HasUnsupportedPredicate = true;
+ if (N->hasAnyPredicate()) {
Explanation = Separator + "Has a predicate (" + explainPredicates(N) + ")";
Separator = ", ";
- break;
}
if (N->getTransformFn()) {
Separator = ", ";
}
- if (!HasUnsupportedPredicate && !N->getTransformFn())
+ if (!N->hasAnyPredicate() && !N->getTransformFn())
return Error::success();
return failedImport(Explanation);
typedef std::vector<std::unique_ptr<PredicateTy>> PredicateVec;
PredicateVec Predicates;
- /// Template instantiations should specialize this to return a string to use
- /// for the comment emitted when there are no predicates.
- std::string getNoPredicateComment() const;
-
public:
/// Construct a new operand predicate and add it to the matcher.
template <class Kind, class... Args>
template <class... Args>
void emitPredicateListOpcodes(MatchTable &Table, Args &&... args) const {
if (Predicates.empty()) {
- Table << MatchTable::Comment(getNoPredicateComment())
- << MatchTable::LineBreak;
+ Table << MatchTable::Comment("No predicates") << MatchTable::LineBreak;
return;
}
virtual unsigned countRendererFns() const { return 0; }
};
-template <>
-std::string
-PredicateListMatcher<OperandPredicateMatcher>::getNoPredicateComment() const {
- return "No operand predicates";
-}
-
/// Generates code to check that an operand is a particular LLT.
class LLTOperandMatcher : public OperandPredicateMatcher {
protected:
/// must be tested first.
enum PredicateKind {
IPM_Opcode,
- IPM_ImmPredicate,
};
PredicateKind Kind;
virtual unsigned countRendererFns() const { return 0; }
};
-template <>
-std::string
-PredicateListMatcher<InstructionPredicateMatcher>::getNoPredicateComment() const {
- return "No instruction predicates";
-}
-
/// Generates code to check the opcode of an instruction.
class InstructionOpcodeMatcher : public InstructionPredicateMatcher {
protected:
}
};
-/// Generates code to check that this instruction is a constant whose value
-/// meets an immediate predicate.
-///
-/// Immediates are slightly odd since they are typically used like an operand
-/// but are represented as an operator internally. We typically write simm8:$src
-/// in a tablegen pattern, but this is just syntactic sugar for
-/// (imm:i32)<<P:Predicate_simm8>>:$imm which more directly describes the nodes
-/// that will be matched and the predicate (which is attached to the imm
-/// operator) that will be tested. In SelectionDAG this describes a
-/// ConstantSDNode whose internal value will be tested using the simm8 predicate.
-///
-/// The corresponding GlobalISel representation is %1 = G_CONSTANT iN Value. In
-/// this representation, the immediate could be tested with an
-/// InstructionMatcher, InstructionOpcodeMatcher, OperandMatcher, and a
-/// OperandPredicateMatcher-subclass to check the Value meets the predicate but
-/// there are two implementation issues with producing that matcher
-/// configuration from the SelectionDAG pattern:
-/// * ImmLeaf is a PatFrag whose root is an InstructionMatcher. This means that
-/// were we to sink the immediate predicate to the operand we would have to
-/// have two partial implementations of PatFrag support, one for immediates
-/// and one for non-immediates.
-/// * At the point we handle the predicate, the OperandMatcher hasn't been
-/// created yet. If we were to sink the predicate to the OperandMatcher we
-/// would also have to complicate (or duplicate) the code that descends and
-/// creates matchers for the subtree.
-/// Overall, it's simpler to handle it in the place it was found.
-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.
///
OM.addPredicate<LLTOperandMatcher>(*OpTyOrNone);
}
- for (const auto &Predicate : Src->getPredicateFns()) {
- if (Predicate.isAlwaysTrue())
- continue;
-
- if (Predicate.isImmediatePattern()) {
- InsnMatcher.addPredicate<InstructionImmPredicateMatcher>(Predicate);
- continue;
- }
-
- return failedImport("Src pattern child has predicate (" +
- explainPredicates(Src) + ")");
- }
-
if (Src->isLeaf()) {
Init *SrcInit = Src->getLeafValue();
if (IntInit *SrcIntInit = dyn_cast<IntInit>(SrcInit)) {
OperandMatcher &OM =
InsnMatcher.addOperand(OpIdx, SrcChild->getName(), TempOpIdx);
+ if (SrcChild->hasAnyPredicate())
+ return failedImport("Src pattern child has predicate (" +
+ explainPredicates(SrcChild) + ")");
+
ArrayRef<EEVT::TypeSet> ChildTypes = SrcChild->getExtTypes();
if (ChildTypes.size() != 1)
return failedImport("Src pattern child has multiple results");
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.
return Error::success();
}
- return failedImport("Dst pattern child isn't a leaf node or an MBB" + llvm::to_string(*DstChild));
+ return failedImport("Dst pattern child isn't a leaf node or an MBB");
}
// 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<DefInit>(DstChild->getLeafValue())) {
auto *ChildRec = ChildDefInit->getDef();
OS << "#ifdef GET_GLOBALISEL_TEMPORARIES_INIT\n"
<< ", State(" << MaxTemporaries << "),\n"
- << "MatcherInfo({TypeObjects, FeatureBitsets, ImmPredicateFns, {\n"
+ << "MatcherInfo({TypeObjects, FeatureBitsets, {\n"
<< " nullptr, // GICP_Invalid\n";
for (const auto &Record : ComplexPredicates)
OS << " &" << Target.getName()
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 << "// PatFrag predicates.\n"
- << "enum {\n";
- StringRef EnumeratorSeparator = " = GIPFP_Invalid,\n";
- for (const auto *Record : RK.getAllDerivedDefinitions("PatFrag")) {
- if (!Record->getValueAsString("ImmediateCode").empty()) {
- OS << " GIPFP_Predicate_" << Record->getName() << EnumeratorSeparator;
- EnumeratorSeparator = ",\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"