// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src1
// CHECK-NEXT: GIR_ComplexRenderer, /*InsnID*/0, /*RendererID*/1,
// CHECK-NEXT: GIR_ComplexRenderer, /*InsnID*/0, /*RendererID*/0,
-// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0, /*MergeInsnID's*/0, GIU_MergeMemOperands_EndOfList, GIR_EraseFromParent, /*InsnID*/0,
+// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0, /*MergeInsnID's*/0, GIU_MergeMemOperands_EndOfList,
+// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 0: @[[LABEL]]
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,
def MOV1 : I<(outs GPR32:$dst), (ins), [(set GPR32:$dst, 1)]>;
-//===- Test a pattern with an MBB operand. --------------------------------===//
+//===- Test a simple pattern with just a leaf immediate. ------------------===//
// 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 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 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 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 16: @[[LABEL]]
+// CHECK-NEXT: // Label 17: @[[LABEL]]
+
+def BR : I<(outs), (ins unknown:$target),
+ [(br bb:$target)]>;
+
// 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)]>;
RuleMatcher(RuleMatcher &&Other) = default;
RuleMatcher &operator=(RuleMatcher &&Other) = default;
- InstructionMatcher &addInstructionMatcher();
+ InstructionMatcher &addInstructionMatcher(StringRef SymbolicName);
void addRequiredFeature(Record *Feature);
const std::vector<Record *> &getRequiredFeatures() const;
return make_range(defined_insn_vars_begin(), defined_insn_vars_end());
}
+ const InstructionMatcher &getInstructionMatcher(StringRef SymbolicName) const;
+
void emitCaptureOpcodes(MatchTable &Table);
void emit(MatchTable &Table);
/// are represented by a virtual register defined by a G_CONSTANT instruction.
enum PredicateKind {
OPM_ComplexPattern,
- OPM_Instruction,
OPM_IntrinsicID,
+ OPM_Instruction,
OPM_Int,
OPM_LiteralInt,
OPM_LLT,
/// Compare the priority of this object and B.
///
/// Returns true if this object is more important than B.
- virtual bool isHigherPriorityThan(const OperandPredicateMatcher &B) const {
- return Kind < B.Kind;
- };
+ virtual bool isHigherPriorityThan(const OperandPredicateMatcher &B) const;
/// Report the maximum number of temporary operands needed by the predicate
/// matcher.
return false;
};
+
+ bool isConstantInstruction() const {
+ return I->TheDef->getName() == "G_CONSTANT";
+ }
};
/// Generates code to check that a set of predicates and operands match for a
/// 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) {
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(); }
return A + Operand->countRendererFns();
});
}
+
+ bool isConstantInstruction() const {
+ for (const auto &P : predicates())
+ if (const InstructionOpcodeMatcher *Opcode =
+ dyn_cast<InstructionOpcodeMatcher>(P.get()))
+ return Opcode->isConstantInstruction();
+ return false;
+ }
};
/// Generates code to check that the operand is a register defined by an
std::unique_ptr<InstructionMatcher> 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;
enum RendererKind {
OR_Copy,
OR_CopySubReg,
+ OR_CopyConstantAsImm,
OR_Imm,
OR_Register,
OR_ComplexPattern
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;
}
};
+/// A CopyConstantAsImmRenderer emits code to render a G_CONSTANT instruction to
+/// an extended immediate operand.
+class CopyConstantAsImmRenderer : public OperandRenderer {
+protected:
+ unsigned NewInsnID;
+ /// The name of the operand.
+ const std::string SymbolicName;
+ bool Signed;
+
+public:
+ CopyConstantAsImmRenderer(unsigned NewInsnID, StringRef SymbolicName)
+ : OperandRenderer(OR_CopyConstantAsImm), NewInsnID(NewInsnID),
+ SymbolicName(SymbolicName), Signed(true) {}
+
+ 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(Signed ? "GIR_CopyConstantAsSImm"
+ : "GIR_CopyConstantAsUImm")
+ << 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.
std::sort(MergeInsnIDs.begin(), MergeInsnIDs.end());
for (const auto &MergeInsnID : MergeInsnIDs)
Table << MatchTable::IntValue(MergeInsnID);
- Table << MatchTable::NamedValue("GIU_MergeMemOperands_EndOfList");
+ Table << MatchTable::NamedValue("GIU_MergeMemOperands_EndOfList")
+ << MatchTable::LineBreak;
}
Table << MatchTable::Opcode("GIR_EraseFromParent")
}
};
-InstructionMatcher &RuleMatcher::addInstructionMatcher() {
- Matchers.emplace_back(new InstructionMatcher());
+InstructionMatcher &RuleMatcher::addInstructionMatcher(StringRef SymbolicName) {
+ Matchers.emplace_back(new InstructionMatcher(SymbolicName));
return *Matchers.back();
}
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) {
});
}
+bool OperandPredicateMatcher::isHigherPriorityThan(
+ const OperandPredicateMatcher &B) const {
+ // Generally speaking, an instruction is more important than an Int or a
+ // LiteralInt because it can cover more nodes but theres an exception to
+ // this. G_CONSTANT's are less important than either of those two because they
+ // are more permissive.
+ if (const InstructionOperandMatcher *AOM =
+ dyn_cast<InstructionOperandMatcher>(this)) {
+ if (AOM->getInsnMatcher().isConstantInstruction()) {
+ if (B.Kind == OPM_Int) {
+ return false;
+ }
+ }
+ }
+ if (const InstructionOperandMatcher *BOM =
+ dyn_cast<InstructionOperandMatcher>(&B)) {
+ if (BOM->getInsnMatcher().isConstantInstruction()) {
+ if (Kind == OPM_Int) {
+ return true;
+ }
+ }
+ }
+
+ return Kind < B.Kind;
+};
+
//===- GlobalISelEmitter class --------------------------------------------===//
class GlobalISelEmitter {
} else {
assert(SrcGIOrNull &&
"Expected to have already found an equivalent Instruction");
+ if (SrcGIOrNull->TheDef->getName() == "G_CONSTANT") {
+ // imm still has an operand but we don't need to do anything with it
+ // here since we don't support ImmLeaf predicates yet. However, we still
+ // need to note the hidden operand to get GIM_CheckNumOperands correct.
+ InsnMatcher.addOperand(OpIdx++, "", TempOpIdx);
+ return InsnMatcher;
+ }
+
// Match the used operands (i.e. the children of the operator).
for (unsigned i = 0, e = Src->getNumChildren(); i != e; ++i) {
TreePatternNode *SrcChild = Src->getChild(i);
if (!SrcChild->isLeaf()) {
// Map the node to a gMIR instruction.
InstructionOperandMatcher &InsnOperand =
- OM.addPredicate<InstructionOperandMatcher>();
+ OM.addPredicate<InstructionOperandMatcher>(SrcChild->getName());
auto InsnMatcherOrError = createAndImportSelDAGMatcher(
InsnOperand.getInsnMatcher(), SrcChild, TempOpIdx);
if (auto Error = InsnMatcherOrError.takeError())
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") {
return Error::success();
}
}
+
+ // Similarly, imm is an operator in TreePatternNode's view but must be
+ // rendered as operands.
+ // FIXME: The target should be able to choose sign-extended when appropriate
+ // (e.g. on Mips).
+ if (DstChild->getOperator()->getName() == "imm") {
+ DstMIBuilder.addRenderer<CopyConstantAsImmRenderer>(0,
+ DstChild->getName());
+ return Error::success();
+ }
+
return failedImport("Dst pattern child isn't a leaf node or an MBB");
}
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);