def fpimmz : FPImmLeaf<f32, [{ return Imm->isExactlyValue(0.0); }]>;
def MOVfpimmz : I<(outs FPR32:$dst), (ins f32imm:$imm), [(set FPR32:$dst, fpimmz:$imm)]>;
-//===- Test a pattern with an MBB operand. --------------------------------===//
+//===- Test a simple pattern with inferred pointer operands. ---------------===//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 22*/ [[LABEL:[0-9]+]],
+// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
+// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_LOAD,
+// CHECK-NEXT: GIM_CheckNonAtomic, /*MI*/0,
+// 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] src1
+// CHECK-NEXT: GIM_CheckPointerToAny, /*MI*/0, /*Op*/1, /*SizeInBits*/32,
+// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID,
+// CHECK-NEXT: // (ld:{ *:[i32] } GPR32:{ *:[i32] }:$src1)<<P:Predicate_unindexedload>><<P:Predicate_load>> => (LOAD:{ *:[i32] } GPR32:{ *:[i32] }:$src1)
+// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::LOAD,
+// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
+// CHECK-NEXT: GIR_Done,
+// CHECK-NEXT: // Label 22: @[[LABEL]]
+
+def LOAD : I<(outs GPR32:$dst), (ins GPR32:$src1),
+ [(set GPR32:$dst, (load GPR32:$src1))]>;
+//===- Test a pattern with an MBB operand. --------------------------------===//
+
+// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 23*/ [[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 22: @[[LABEL]]
+// CHECK-NEXT: // Label 23: @[[LABEL]]
def BR : I<(outs), (ins unknown:$target),
[(br bb:$target)]>;
OS << "GILLT_v" << Ty.getNumElements() << "s" << Ty.getScalarSizeInBits();
return;
}
+ if (Ty.isPointer()) {
+ OS << "GILLT_p" << Ty.getAddressSpace();
+ if (Ty.getSizeInBits() > 0)
+ OS << "s" << Ty.getSizeInBits();
+ return;
+ }
llvm_unreachable("Unhandled LLT");
}
<< Ty.getScalarSizeInBits() << ")";
return;
}
+ if (Ty.isPointer() && Ty.getSizeInBits() > 0) {
+ OS << "LLT::pointer(" << Ty.getAddressSpace() << ", "
+ << Ty.getSizeInBits() << ")";
+ return;
+ }
llvm_unreachable("Unhandled LLT");
}
/// MVTs that don't map cleanly to an LLT (e.g., iPTR, *any, ...).
static Optional<LLTCodeGen> MVTToLLT(MVT::SimpleValueType SVT) {
MVT VT(SVT);
+
if (VT.isVector() && VT.getVectorNumElements() != 1)
return LLTCodeGen(
LLT::vector(VT.getVectorNumElements(), VT.getScalarSizeInBits()));
+
if (VT.isInteger() || VT.isFloatingPoint())
return LLTCodeGen(LLT::scalar(VT.getSizeInBits()));
return None;
if (Predicate.isImmediatePattern())
continue;
+ if (Predicate.isLoad() && Predicate.isUnindexed())
+ continue;
+
+ if (Predicate.isNonExtLoad())
+ continue;
HasUnsupportedPredicate = true;
Explanation = Separator + "Has a predicate (" + explainPredicates(N) + ")";
Separator = ", ";
OPM_Int,
OPM_LiteralInt,
OPM_LLT,
+ OPM_PointerToAny,
OPM_RegBank,
OPM_MBB,
};
std::set<LLTCodeGen> LLTOperandMatcher::KnownTypes;
+/// Generates code to check that an operand is a pointer to any address space.
+///
+/// In SelectionDAG, the types did not describe pointers or address spaces. As a
+/// result, iN is used to describe a pointer of N bits to any address space and
+/// PatFrag predicates are typically used to constrain the address space. There's
+/// no reliable means to derive the missing type information from the pattern so
+/// imported rules must test the components of a pointer separately.
+///
+/// SizeInBits must be non-zero and the matched pointer must be that size.
+/// TODO: Add support for iPTR via SizeInBits==0 and a subtarget query.
+class PointerToAnyOperandMatcher : public OperandPredicateMatcher {
+protected:
+ unsigned SizeInBits;
+
+public:
+ PointerToAnyOperandMatcher(unsigned SizeInBits)
+ : OperandPredicateMatcher(OPM_PointerToAny), SizeInBits(SizeInBits) {}
+
+ static bool classof(const OperandPredicateMatcher *P) {
+ return P->getKind() == OPM_PointerToAny;
+ }
+
+ void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule,
+ unsigned InsnVarID, unsigned OpIdx) const override {
+ Table << MatchTable::Opcode("GIM_CheckPointerToAny") << MatchTable::Comment("MI")
+ << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op")
+ << MatchTable::IntValue(OpIdx) << MatchTable::Comment("SizeInBits")
+ << MatchTable::IntValue(SizeInBits) << MatchTable::LineBreak;
+ }
+};
+
/// Generates code to check that an operand is a particular target constant.
class ComplexPatternOperandMatcher : public OperandPredicateMatcher {
protected:
InstructionMatcher &getInstructionMatcher() const { return Insn; }
+ Error addTypeCheckPredicate(const TypeSetByHwMode &VTy,
+ bool OperandIsAPointer) {
+ auto OpTyOrNone = VTy.isMachineValueType()
+ ? MVTToLLT(VTy.getMachineValueType().SimpleTy)
+ : None;
+ if (!OpTyOrNone)
+ return failedImport("unsupported type");
+
+ if (OperandIsAPointer)
+ addPredicate<PointerToAnyOperandMatcher>(
+ OpTyOrNone->get().getSizeInBits());
+ else
+ addPredicate<LLTOperandMatcher>(*OpTyOrNone);
+ return Error::success();
+ }
+
/// Emit MatchTable opcodes to capture instructions into the MIs table.
void emitCaptureOpcodes(MatchTable &Table, RuleMatcher &Rule,
unsigned InsnVarID) const {
Error importComplexPatternOperandMatcher(OperandMatcher &OM, Record *R,
unsigned &TempOpIdx) const;
Error importChildMatcher(RuleMatcher &Rule, InstructionMatcher &InsnMatcher,
- const TreePatternNode *SrcChild, unsigned OpIdx,
+ const TreePatternNode *SrcChild,
+ bool OperandIsAPointer, unsigned OpIdx,
unsigned &TempOpIdx) const;
Expected<BuildMIAction &>
createAndImportInstructionRenderer(RuleMatcher &M, const TreePatternNode *Dst,
unsigned OpIdx = 0;
for (const TypeSetByHwMode &VTy : Src->getExtTypes()) {
- auto OpTyOrNone = VTy.isMachineValueType()
- ? MVTToLLT(VTy.getMachineValueType().SimpleTy)
- : None;
- if (!OpTyOrNone)
- return failedImport(
- "Result of Src pattern operator has an unsupported type");
-
// Results don't have a name unless they are the root node. The caller will
// set the name if appropriate.
OperandMatcher &OM = InsnMatcher.addOperand(OpIdx++, "", TempOpIdx);
- OM.addPredicate<LLTOperandMatcher>(*OpTyOrNone);
+ if (auto Error = OM.addTypeCheckPredicate(VTy, false /* OperandIsAPointer */))
+ return failedImport(toString(std::move(Error)) +
+ " for result of Src pattern operator");
}
for (const auto &Predicate : Src->getPredicateFns()) {
continue;
}
+ // No check required. A G_LOAD is an unindexed load.
+ if (Predicate.isLoad() && Predicate.isUnindexed())
+ continue;
+
+ // No check required. G_LOAD by itself is a non-extending load.
+ if (Predicate.isNonExtLoad())
+ continue;
+
+ if (Predicate.isLoad() && Predicate.getMemoryVT() != nullptr) {
+ Optional<LLTCodeGen> MemTyOrNone =
+ MVTToLLT(getValueType(Predicate.getMemoryVT()));
+
+ if (!MemTyOrNone)
+ return failedImport("MemVT could not be converted to LLT");
+
+ InsnMatcher.getOperand(0).addPredicate<LLTOperandMatcher>(MemTyOrNone.getValue());
+ continue;
+ }
+
return failedImport("Src pattern child has predicate (" +
explainPredicates(Src) + ")");
}
for (unsigned i = 0, e = Src->getNumChildren(); i != e; ++i) {
TreePatternNode *SrcChild = Src->getChild(i);
+ // SelectionDAG allows pointers to be represented with iN since it doesn't
+ // distinguish between pointers and integers but they are different types in GlobalISel.
+ // Coerce integers to pointers to address space 0 if the context indicates a pointer.
+ // TODO: Find a better way to do this, SDTCisPtrTy?
+ bool OperandIsAPointer =
+ SrcGIOrNull->TheDef->getName() == "G_LOAD" && i == 0;
+
// For G_INTRINSIC/G_INTRINSIC_W_SIDE_EFFECTS, the operand immediately
// following the defs is an intrinsic ID.
if ((SrcGIOrNull->TheDef->getName() == "G_INTRINSIC" ||
return failedImport("Expected IntInit containing instrinsic ID)");
}
- if (auto Error = importChildMatcher(Rule, InsnMatcher, SrcChild, OpIdx++,
- TempOpIdx))
+ if (auto Error =
+ importChildMatcher(Rule, InsnMatcher, SrcChild, OperandIsAPointer,
+ OpIdx++, TempOpIdx))
return std::move(Error);
}
}
Error GlobalISelEmitter::importChildMatcher(RuleMatcher &Rule,
InstructionMatcher &InsnMatcher,
const TreePatternNode *SrcChild,
+ bool OperandIsAPointer,
unsigned OpIdx,
unsigned &TempOpIdx) const {
OperandMatcher &OM =
}
}
- Optional<LLTCodeGen> OpTyOrNone = None;
- if (ChildTypes.front().isMachineValueType())
- OpTyOrNone = MVTToLLT(ChildTypes.front().getMachineValueType().SimpleTy);
- if (!OpTyOrNone)
- return failedImport("Src operand has an unsupported type (" + to_string(*SrcChild) + ")");
- OM.addPredicate<LLTOperandMatcher>(*OpTyOrNone);
+ if (auto Error =
+ OM.addTypeCheckPredicate(ChildTypes.front(), OperandIsAPointer))
+ return failedImport(toString(std::move(Error)) + " for Src operand (" +
+ to_string(*SrcChild) + ")");
// Check for nested instructions.
if (!SrcChild->isLeaf()) {