Re-commit: [globalisel][tablegen] Add support for ImmLeaf without SDNodeXForm

Summary:
This patch adds support for predicates on imm nodes but only for ImmLeaf and not
for PatLeaf or PatFrag and only where the value does not need to be transformed
before being rendered into the instruction.

The limitation on PatLeaf/PatFrag/SDNodeXForm is due to differences in the
necessary target-supplied C++ for GlobalISel.

Depends on D36085

The previous commit was reverted for breaking the build but this appears to have
been the recurring problem on the Windows bots with tablegen not being re-run
when llvm-tblgen is changed but the .td's aren't. If it re-occurs then forcing a
build with clean=True should fix it but this string should do this in advance:
    Requires a clean build.

Reviewers: ab, t.p.northover, qcolombet, rovka, aditya_nandakumar

Reviewed By: rovka

Subscribers: kristof.beyls, javed.absar, igorb, llvm-commits

Differential Revision: https://reviews.llvm.org/D36086

llvm-svn: 311645
This commit is contained in:
Daniel Sanders 2017-08-24 09:11:20 +00:00
parent 21c312d8c6
commit 2c269f6bf8
5 changed files with 235 additions and 20 deletions

View File

@ -93,6 +93,10 @@ enum {
/// - InsnID - Instruction ID /// - InsnID - Instruction ID
/// - Expected number of operands /// - Expected number of operands
GIM_CheckNumOperands, 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 /// Check the type for the specified operand
/// - InsnID - Instruction ID /// - InsnID - Instruction ID
@ -222,6 +226,8 @@ enum {
/// Provides the logic to select generic machine instructions. /// Provides the logic to select generic machine instructions.
class InstructionSelector { class InstructionSelector {
public: public:
typedef bool(*ImmediatePredicateFn)(int64_t);
virtual ~InstructionSelector() = default; virtual ~InstructionSelector() = default;
/// Select the (possibly generic) instruction \p I to only use target-specific /// Select the (possibly generic) instruction \p I to only use target-specific
@ -254,6 +260,7 @@ public:
struct MatcherInfoTy { struct MatcherInfoTy {
const LLT *TypeObjects; const LLT *TypeObjects;
const PredicateBitset *FeatureBitsets; const PredicateBitset *FeatureBitsets;
const ImmediatePredicateFn *ImmPredicateFns;
const std::vector<ComplexMatcherMemFn> ComplexPredicates; const std::vector<ComplexMatcherMemFn> ComplexPredicates;
}; };

View File

@ -17,6 +17,12 @@
#define LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTORIMPL_H #define LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTORIMPL_H
namespace llvm { namespace llvm {
/// GlobalISel PatFrag Predicates
enum {
GIPFP_Invalid,
};
template <class TgtInstructionSelector, class PredicateBitset, template <class TgtInstructionSelector, class PredicateBitset,
class ComplexMatcherMemFn> class ComplexMatcherMemFn>
bool InstructionSelector::executeMatchTable( bool InstructionSelector::executeMatchTable(
@ -126,6 +132,28 @@ bool InstructionSelector::executeMatchTable(
} }
break; 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: { case GIM_CheckType: {
int64_t InsnID = MatchTable[CurrentIdx++]; int64_t InsnID = MatchTable[CurrentIdx++];

View File

@ -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)
...

View File

@ -53,7 +53,7 @@ def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; }
// CHECK-LABEL: #ifdef GET_GLOBALISEL_TEMPORARIES_INIT // CHECK-LABEL: #ifdef GET_GLOBALISEL_TEMPORARIES_INIT
// CHECK-NEXT: , State(2), // CHECK-NEXT: , State(2),
// CHECK-NEXT: MatcherInfo({TypeObjects, FeatureBitsets, { // CHECK-NEXT: MatcherInfo({TypeObjects, FeatureBitsets, ImmPredicateFns, {
// CHECK-NEXT: nullptr, // GICP_Invalid // CHECK-NEXT: nullptr, // GICP_Invalid
// CHECK-NEXT: &MyTargetInstructionSelector::selectComplexPattern, // gi_complex // CHECK-NEXT: &MyTargetInstructionSelector::selectComplexPattern, // gi_complex
// CHECK-NEXT: }}) // CHECK-NEXT: }})
@ -109,6 +109,15 @@ def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; }
// CHECK-NEXT: GICP_gi_complex, // CHECK-NEXT: GICP_gi_complex,
// CHECK-NEXT: }; // 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: bool MyTargetInstructionSelector::selectImpl(MachineInstr &I) const {
// CHECK-NEXT: MachineFunction &MF = *I.getParent()->getParent(); // CHECK-NEXT: MachineFunction &MF = *I.getParent()->getParent();
// CHECK-NEXT: MachineRegisterInfo &MRI = MF.getRegInfo(); // CHECK-NEXT: MachineRegisterInfo &MRI = MF.getRegInfo();
@ -635,18 +644,19 @@ def : Pat<(i32 (bitconvert FPR32:$src1)),
def MOV1 : I<(outs GPR32:$dst), (ins), [(set GPR32:$dst, 1)]>; 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_Try, /*On fail goto*//*Label 16*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2, // CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_CONSTANT, // 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: // MIs[0] dst
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32, // CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32,
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID, // CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
// CHECK-NEXT: // MIs[0] Operand 1 // CHECK-NEXT: // MIs[0] Operand 1
// CHECK-NEXT: // No predicates // CHECK-NEXT: // No operand predicates
// CHECK-NEXT: // (imm:i32):$imm => (MOVimm:i32 (imm:i32):$imm) // CHECK-NEXT: // (imm:i32)<<P:Predicate_simm8>>:$imm => (MOVimm8:i32 (imm:i32):$imm)
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MOVimm, // CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MOVimm8,
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst // CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
// CHECK-NEXT: GIR_CopyConstantAsSImm, /*NewInsnID*/0, /*OldInsnID*/0, // imm // CHECK-NEXT: GIR_CopyConstantAsSImm, /*NewInsnID*/0, /*OldInsnID*/0, // imm
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0, // CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
@ -654,11 +664,33 @@ def MOV1 : I<(outs GPR32:$dst), (ins), [(set GPR32:$dst, 1)]>;
// CHECK-NEXT: GIR_Done, // CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 16: @[[LABEL]] // 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_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: // (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_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 17: @[[LABEL]]
def MOVimm : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, imm:$imm)]>; def MOVimm : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, imm:$imm)]>;
//===- Test a pattern with an MBB operand. --------------------------------===// //===- 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_CheckNumOperands, /*MI*/0, /*Expected*/1,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_BR, // CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_BR,
// CHECK-NEXT: // MIs[0] target // CHECK-NEXT: // MIs[0] target
@ -667,7 +699,7 @@ def MOVimm : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, imm:$imm)
// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::BR, // CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::BR,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done, // CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 17: @[[LABEL]] // CHECK-NEXT: // Label 18: @[[LABEL]]
def BR : I<(outs), (ins unknown:$target), def BR : I<(outs), (ins unknown:$target),
[(br bb:$target)]>; [(br bb:$target)]>;

View File

@ -180,9 +180,19 @@ static Error failedImport(const Twine &Reason) {
static Error isTrivialOperatorNode(const TreePatternNode *N) { static Error isTrivialOperatorNode(const TreePatternNode *N) {
std::string Explanation = ""; std::string Explanation = "";
std::string Separator = ""; 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) + ")"; Explanation = Separator + "Has a predicate (" + explainPredicates(N) + ")";
Separator = ", "; Separator = ", ";
break;
} }
if (N->getTransformFn()) { if (N->getTransformFn()) {
@ -190,7 +200,7 @@ static Error isTrivialOperatorNode(const TreePatternNode *N) {
Separator = ", "; Separator = ", ";
} }
if (!N->hasAnyPredicate() && !N->getTransformFn()) if (!HasUnsupportedPredicate && !N->getTransformFn())
return Error::success(); return Error::success();
return failedImport(Explanation); return failedImport(Explanation);
@ -515,6 +525,10 @@ private:
typedef std::vector<std::unique_ptr<PredicateTy>> PredicateVec; typedef std::vector<std::unique_ptr<PredicateTy>> PredicateVec;
PredicateVec Predicates; 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: public:
/// Construct a new operand predicate and add it to the matcher. /// Construct a new operand predicate and add it to the matcher.
template <class Kind, class... Args> template <class Kind, class... Args>
@ -541,7 +555,8 @@ public:
template <class... Args> template <class... Args>
void emitPredicateListOpcodes(MatchTable &Table, Args &&... args) const { void emitPredicateListOpcodes(MatchTable &Table, Args &&... args) const {
if (Predicates.empty()) { if (Predicates.empty()) {
Table << MatchTable::Comment("No predicates") << MatchTable::LineBreak; Table << MatchTable::Comment(getNoPredicateComment())
<< MatchTable::LineBreak;
return; return;
} }
@ -618,6 +633,12 @@ public:
virtual unsigned countRendererFns() const { return 0; } 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. /// Generates code to check that an operand is a particular LLT.
class LLTOperandMatcher : public OperandPredicateMatcher { class LLTOperandMatcher : public OperandPredicateMatcher {
protected: protected:
@ -915,6 +936,7 @@ protected:
/// must be tested first. /// must be tested first.
enum PredicateKind { enum PredicateKind {
IPM_Opcode, IPM_Opcode,
IPM_ImmPredicate,
}; };
PredicateKind Kind; PredicateKind Kind;
@ -943,6 +965,12 @@ public:
virtual unsigned countRendererFns() const { return 0; } 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. /// Generates code to check the opcode of an instruction.
class InstructionOpcodeMatcher : public InstructionPredicateMatcher { class InstructionOpcodeMatcher : public InstructionPredicateMatcher {
protected: protected:
@ -989,6 +1017,54 @@ public:
} }
}; };
/// 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 /// Generates code to check that a set of predicates and operands match for a
/// particular instruction. /// particular instruction.
/// ///
@ -1923,6 +1999,19 @@ GlobalISelEmitter::createAndImportSelDAGMatcher(InstructionMatcher &InsnMatcher,
OM.addPredicate<LLTOperandMatcher>(*OpTyOrNone); 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()) { if (Src->isLeaf()) {
Init *SrcInit = Src->getLeafValue(); Init *SrcInit = Src->getLeafValue();
if (IntInit *SrcIntInit = dyn_cast<IntInit>(SrcInit)) { if (IntInit *SrcIntInit = dyn_cast<IntInit>(SrcInit)) {
@ -1975,10 +2064,6 @@ Error GlobalISelEmitter::importChildMatcher(InstructionMatcher &InsnMatcher,
OperandMatcher &OM = OperandMatcher &OM =
InsnMatcher.addOperand(OpIdx, SrcChild->getName(), TempOpIdx); InsnMatcher.addOperand(OpIdx, SrcChild->getName(), TempOpIdx);
if (SrcChild->hasAnyPredicate())
return failedImport("Src pattern child has predicate (" +
explainPredicates(SrcChild) + ")");
ArrayRef<EEVT::TypeSet> ChildTypes = SrcChild->getExtTypes(); ArrayRef<EEVT::TypeSet> ChildTypes = SrcChild->getExtTypes();
if (ChildTypes.size() != 1) if (ChildTypes.size() != 1)
return failedImport("Src pattern child has multiple results"); return failedImport("Src pattern child has multiple results");
@ -2058,6 +2143,11 @@ Error GlobalISelEmitter::importChildMatcher(InstructionMatcher &InsnMatcher,
Error GlobalISelEmitter::importExplicitUseRenderer( Error GlobalISelEmitter::importExplicitUseRenderer(
BuildMIAction &DstMIBuilder, TreePatternNode *DstChild, BuildMIAction &DstMIBuilder, TreePatternNode *DstChild,
const InstructionMatcher &InsnMatcher) const { const InstructionMatcher &InsnMatcher) const {
if (DstChild->getTransformFn() != nullptr) {
return failedImport("Dst pattern child has transform fn " +
DstChild->getTransformFn()->getName());
}
if (!DstChild->isLeaf()) { if (!DstChild->isLeaf()) {
// We accept 'bb' here. It's an operator because BasicBlockSDNode isn't // We accept 'bb' here. It's an operator because BasicBlockSDNode isn't
// inline, but in MI it's just another operand. // inline, but in MI it's just another operand.
@ -2080,14 +2170,10 @@ Error GlobalISelEmitter::importExplicitUseRenderer(
return Error::success(); 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. // 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())) { if (auto *ChildDefInit = dyn_cast<DefInit>(DstChild->getLeafValue())) {
auto *ChildRec = ChildDefInit->getDef(); auto *ChildRec = ChildDefInit->getDef();
@ -2526,7 +2612,7 @@ void GlobalISelEmitter::run(raw_ostream &OS) {
OS << "#ifdef GET_GLOBALISEL_TEMPORARIES_INIT\n" OS << "#ifdef GET_GLOBALISEL_TEMPORARIES_INIT\n"
<< ", State(" << MaxTemporaries << "),\n" << ", State(" << MaxTemporaries << "),\n"
<< "MatcherInfo({TypeObjects, FeatureBitsets, {\n" << "MatcherInfo({TypeObjects, FeatureBitsets, ImmPredicateFns, {\n"
<< " nullptr, // GICP_Invalid\n"; << " nullptr, // GICP_Invalid\n";
for (const auto &Record : ComplexPredicates) for (const auto &Record : ComplexPredicates)
OS << " &" << Target.getName() OS << " &" << Target.getName()
@ -2639,6 +2725,31 @@ void GlobalISelEmitter::run(raw_ostream &OS) {
OS << "};\n" OS << "};\n"
<< "// See constructor for table contents\n\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() OS << "bool " << Target.getName()
<< "InstructionSelector::selectImpl(MachineInstr &I) const {\n" << "InstructionSelector::selectImpl(MachineInstr &I) const {\n"
<< " MachineFunction &MF = *I.getParent()->getParent();\n" << " MachineFunction &MF = *I.getParent()->getParent();\n"