[globalisel][tablegen] Add support for C++ predicates on PatFrags and use it to support BFC on ARM.

So far, we've only handled special cases of PatFrag like ImmLeaf. This patch
adds support for the remaining cases using similar mechanisms.

Like most C++ code from SelectionDAG, GISel and DAGISel expect to operate on
different types and representations and as such the code is not compatible
between the two. It's therefore necessary to add an alternative implementation
in the GISelPredicateCode field.

The target test for this feature could easily be done with IntImmLeaf and this
would save on a little boilerplate. The reason I've chosen to implement this
using PatFrag.GISelPredicateCode and not IntImmLeaf is because I was unable to
find a rule that was blocked solely by lack of support for PatFrag predicates. I
found that the ones I investigated as being likely candidates for the test
were further blocked by other things.

llvm-svn: 334871
This commit is contained in:
Daniel Sanders 2018-06-15 23:13:43 +00:00
parent 4c35d598e0
commit 8ead1290e6
8 changed files with 171 additions and 29 deletions

View File

@ -147,6 +147,10 @@ enum {
GIM_CheckMemorySizeEqualToLLT,
GIM_CheckMemorySizeLessThanLLT,
GIM_CheckMemorySizeGreaterThanLLT,
/// Check a generic C++ instruction predicate
/// - InsnID - Instruction ID
/// - PredicateID - The ID of the predicate function to call
GIM_CheckCxxInsnPredicate,
/// Check the type for the specified operand
/// - InsnID - Instruction ID
@ -413,13 +417,20 @@ protected:
}
virtual bool testImmPredicate_I64(unsigned, int64_t) const {
llvm_unreachable("Subclasses must override this to use tablegen");
llvm_unreachable(
"Subclasses must override this with a tablegen-erated function");
}
virtual bool testImmPredicate_APInt(unsigned, const APInt &) const {
llvm_unreachable("Subclasses must override this to use tablegen");
llvm_unreachable(
"Subclasses must override this with a tablegen-erated function");
}
virtual bool testImmPredicate_APFloat(unsigned, const APFloat &) const {
llvm_unreachable("Subclasses must override this to use tablegen");
llvm_unreachable(
"Subclasses must override this with a tablegen-erated function");
}
virtual bool testMIPredicate_MI(unsigned, const MachineInstr &) const {
llvm_unreachable(
"Subclasses must override this with a tablegen-erated function");
}
/// Constrain a register operand of an instruction \p I to a specified

View File

@ -41,6 +41,7 @@ enum {
GIPFP_I64_Invalid = 0,
GIPFP_APInt_Invalid = 0,
GIPFP_APFloat_Invalid = 0,
GIPFP_MI_Invalid = 0,
};
template <class TgtInstructionSelector, class PredicateBitset,
@ -302,6 +303,21 @@ bool InstructionSelector::executeMatchTable(
return false;
break;
}
case GIM_CheckCxxInsnPredicate: {
int64_t InsnID = MatchTable[CurrentIdx++];
int64_t Predicate = MatchTable[CurrentIdx++];
DEBUG_WITH_TYPE(TgtInstructionSelector::getName(),
dbgs()
<< CurrentIdx << ": GIM_CheckCxxPredicate(MIs["
<< InsnID << "], Predicate=" << Predicate << ")\n");
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
assert(Predicate > GIPFP_MI_Invalid && "Expected a valid predicate");
if (!testMIPredicate_MI(Predicate, *State.MIs[InsnID]))
if (handleReject() == RejectAndGiveUp)
return false;
break;
}
case GIM_CheckAtomicOrdering: {
int64_t InsnID = MatchTable[CurrentIdx++];
AtomicOrdering Ordering = (AtomicOrdering)MatchTable[CurrentIdx++];

View File

@ -623,6 +623,7 @@ class PatFrag<dag ops, dag frag, code pred = [{}],
dag Operands = ops;
dag Fragment = frag;
code PredicateCode = pred;
code GISelPredicateCode = [{}];
code ImmediateCode = [{}];
SDNodeXForm OperandTransform = xform;

View File

@ -883,6 +883,16 @@ def bf_inv_mask_imm : Operand<i32>,
let PrintMethod = "printBitfieldInvMaskImmOperand";
let DecoderMethod = "DecodeBitfieldMaskOperand";
let ParserMatchClass = BitfieldAsmOperand;
let GISelPredicateCode = [{
// There's better methods of implementing this check. IntImmLeaf<> would be
// equivalent and have less boilerplate but we need a test for C++
// predicates and this one causes new rules to be imported into GlobalISel
// without requiring additional features first.
const auto &MO = MI.getOperand(1);
if (!MO.isCImm())
return false;
return ARM::isBitFieldInvertedMask(MO.getCImm()->getZExtValue());
}];
}
def imm1_32_XFORM: SDNodeXForm<imm, [{

View File

@ -94,6 +94,7 @@ def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; }
// CHECK-NEXT: bool testImmPredicate_APInt(unsigned PredicateID, const APInt &Imm) const override;
// CHECK-NEXT: bool testImmPredicate_APFloat(unsigned PredicateID, const APFloat &Imm) const override;
// CHECK-NEXT: const int64_t *getMatchTable() const override;
// CHECK-NEXT: bool testMIPredicate_MI(unsigned PredicateID, const MachineInstr &MI) const override;
// CHECK-NEXT: #endif // ifdef GET_GLOBALISEL_TEMPORARIES_DECL
// CHECK-LABEL: #ifdef GET_GLOBALISEL_TEMPORARIES_INIT
@ -256,11 +257,11 @@ def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; }
// R19O-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32,
// R19O-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
// R19O-NEXT: GIM_CheckType, /*MI*/0, /*Op*/3, /*Type*/GILLT_s32,
// R19O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
// R19O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID,
//
// R19C-NEXT: GIM_Try, /*On fail goto*//*Label [[LABEL_NUM:[0-9]+]]*/ [[LABEL:[0-9]+]],
//
// R19O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
// R19O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID,
// R19N-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/4,
// R19N-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_SELECT,
// R19N-NEXT: // MIs[0] dst
@ -345,8 +346,6 @@ def : Pat<(select GPR32:$src1, (complex_rr GPR32:$src2a, GPR32:$src2b),
// R21O-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32,
// R21O-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
// R21O-NEXT: GIM_CheckType, /*MI*/0, /*Op*/3, /*Type*/GILLT_s32,
// R21O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
// R21O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID,
//
// R21C-NEXT: GIM_Try, /*On fail goto*//*Label [[PREV_NUM:[0-9]+]]*/ [[PREV:[0-9]+]], // Rule ID 19 //
// R21C-NOT: GIR_Done,
@ -355,8 +354,12 @@ def : Pat<(select GPR32:$src1, (complex_rr GPR32:$src2a, GPR32:$src2b),
// R21C-NEXT: // Label [[PREV_NUM]]: @[[PREV]]
// R21C-NEXT: GIM_Try, /*On fail goto*//*Label [[LABEL_NUM:[0-9]+]]*/ [[LABEL:[0-9]+]], // Rule ID 21 //
//
// R21O-NEXT: GIM_CheckCxxInsnPredicate, /*MI*/0, /*FnId*/GIPFP_MI_Predicate_frag,
// R21O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
// R21O-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID,
// R21N-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/4,
// R21N-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_SELECT,
// R21N-NEXT: GIM_CheckCxxInsnPredicate, /*MI*/0, /*FnId*/GIPFP_MI_Predicate_frag,
// R21N-NEXT: // MIs[0] dst
// R21N-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32,
// R21N-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
@ -370,7 +373,8 @@ def : Pat<(select GPR32:$src1, (complex_rr GPR32:$src2a, GPR32:$src2b),
// R21N-NEXT: // MIs[0] src3
// R21N-NEXT: GIM_CheckType, /*MI*/0, /*Op*/3, /*Type*/GILLT_s32,
// R21C-NEXT: GIM_CheckComplexPattern, /*MI*/0, /*Op*/3, /*Renderer*/1, GICP_gi_complex,
// R21C-NEXT: // (select:{ *:[i32] } GPR32:{ *:[i32] }:$src1, complex:{ *:[i32] }:$src2, complex:{ *:[i32] }:$src3) => (INSN2:{ *:[i32] } GPR32:{ *:[i32] }:$src1, complex:{ *:[i32] }:$src3, complex:{ *:[i32] }:$src2)
// R21C-NEXT: // (select:{ *:[i32] } GPR32:{ *:[i32] }:$src1, complex:{ *:[i32] }:$src2, complex:{ *:[i32] }:$src3)<<P:Predicate_frag>> => (INSN2:{ *:[i32] } GPR32:{ *:[i32] }:$src1, complex:{ *:[i32] }:$src3, complex:{ *:[i32] }:$src2)
// R21C-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::INSN2,
// R21C-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
// R21C-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src1
@ -447,7 +451,12 @@ def : GINodeEquiv<G_SELECT, select>;
let mayLoad = 1 in {
def INSN2 : I<(outs GPR32:$dst), (ins GPR32Op:$src1, complex:$src2, complex:$src3), []>;
}
def : Pat<(select GPR32:$src1, complex:$src2, complex:$src3),
def frag : PatFrag<(ops node:$a, node:$b, node:$c),
(select node:$a, node:$b, node:$c),
[{ return true; // C++ code }]> {
let GISelPredicateCode = [{ return true; // C++ code }];
}
def : Pat<(frag GPR32:$src1, complex:$src2, complex:$src3),
(INSN2 GPR32:$src1, complex:$src3, complex:$src2)>;
//===- Test a more complex multi-instruction match. -----------------------===//

View File

@ -1134,6 +1134,14 @@ Record *TreePredicateFn::getScalarMemoryVT() const {
return nullptr;
return R->getValueAsDef("ScalarMemoryVT");
}
bool TreePredicateFn::hasGISelPredicateCode() const {
return !PatFragRec->getRecord()
->getValueAsString("GISelPredicateCode")
.empty();
}
std::string TreePredicateFn::getGISelPredicateCode() const {
return PatFragRec->getRecord()->getValueAsString("GISelPredicateCode");
}
StringRef TreePredicateFn::getImmType() const {
if (immCodeUsesAPInt())

View File

@ -543,6 +543,10 @@ public:
/// ValueType record for the memory VT.
Record *getScalarMemoryVT() const;
// If true, indicates that GlobalISel-based C++ code was supplied.
bool hasGISelPredicateCode() const;
std::string getGISelPredicateCode() const;
private:
bool hasPredCode() const;
bool hasImmCode() const;

View File

@ -84,6 +84,8 @@ namespace {
/// Get the name of the enum value used to number the predicate function.
std::string getEnumNameForPredicate(const TreePredicateFn &Predicate) {
if (Predicate.hasGISelPredicateCode())
return "GIPFP_MI_" + Predicate.getFnName();
return "GIPFP_" + Predicate.getImmTypeIdentifier().str() + "_" +
Predicate.getFnName();
}
@ -319,6 +321,9 @@ static Error isTrivialOperatorNode(const TreePatternNode *N) {
Predicate.isAtomicOrderingWeakerThanRelease()))
continue;
if (Predicate.hasGISelPredicateCode())
continue;
HasUnsupportedPredicate = true;
Explanation = Separator + "Has a predicate (" + explainPredicates(N) + ")";
Separator = ", ";
@ -1014,6 +1019,7 @@ public:
IPM_AtomicOrderingMMO,
IPM_MemoryLLTSize,
IPM_MemoryVsLLTSize,
IPM_GenericPredicate,
OPM_SameOperand,
OPM_ComplexPattern,
OPM_IntrinsicID,
@ -1817,6 +1823,30 @@ public:
}
};
/// Generates code to check an arbitrary C++ instruction predicate.
class GenericInstructionPredicateMatcher : public InstructionPredicateMatcher {
protected:
TreePredicateFn Predicate;
public:
GenericInstructionPredicateMatcher(unsigned InsnVarID,
TreePredicateFn Predicate)
: InstructionPredicateMatcher(IPM_GenericPredicate, InsnVarID),
Predicate(Predicate) {}
static bool classof(const InstructionPredicateMatcher *P) {
return P->getKind() == IPM_GenericPredicate;
}
void emitPredicateOpcodes(MatchTable &Table,
RuleMatcher &Rule) const override {
Table << MatchTable::Opcode("GIM_CheckCxxInsnPredicate")
<< MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID)
<< MatchTable::Comment("FnId")
<< MatchTable::NamedValue(getEnumNameForPredicate(Predicate))
<< MatchTable::LineBreak;
}
};
/// Generates code to check that a set of predicates and operands match for a
/// particular instruction.
///
@ -2954,20 +2984,25 @@ private:
void gatherOpcodeValues();
void gatherTypeIDValues();
void gatherNodeEquivs();
// Instruction predicate code that will be emitted in generated functions.
SmallVector<std::string, 2> InstructionPredicateCodes;
unsigned getOrCreateInstructionPredicateFnId(StringRef Code);
Record *findNodeEquiv(Record *N) const;
const CodeGenInstruction *getEquivNode(Record &Equiv,
const TreePatternNode *N) const;
Error importRulePredicates(RuleMatcher &M, ArrayRef<Predicate> Predicates);
Expected<InstructionMatcher &> createAndImportSelDAGMatcher(
RuleMatcher &Rule, InstructionMatcher &InsnMatcher,
const TreePatternNode *Src, unsigned &TempOpIdx) const;
Expected<InstructionMatcher &>
createAndImportSelDAGMatcher(RuleMatcher &Rule,
InstructionMatcher &InsnMatcher,
const TreePatternNode *Src, unsigned &TempOpIdx);
Error importComplexPatternOperandMatcher(OperandMatcher &OM, Record *R,
unsigned &TempOpIdx) const;
Error importChildMatcher(RuleMatcher &Rule, InstructionMatcher &InsnMatcher,
const TreePatternNode *SrcChild,
bool OperandIsAPointer, unsigned OpIdx,
unsigned &TempOpIdx) const;
unsigned &TempOpIdx);
Expected<BuildMIAction &>
createAndImportInstructionRenderer(RuleMatcher &M,
@ -2993,9 +3028,14 @@ private:
importImplicitDefRenderers(BuildMIAction &DstMIBuilder,
const std::vector<Record *> &ImplicitDefs) const;
void emitImmPredicates(raw_ostream &OS, StringRef TypeIdentifier,
StringRef Type,
std::function<bool(const Record *R)> Filter);
void emitCxxPredicateFns(raw_ostream &OS, StringRef CodeFieldName,
StringRef TypeIdentifier, StringRef ArgType,
StringRef ArgName, StringRef AdditionalDeclarations,
std::function<bool(const Record *R)> Filter);
void emitImmPredicateFns(raw_ostream &OS, StringRef TypeIdentifier,
StringRef ArgType,
std::function<bool(const Record *R)> Filter);
void emitMIPredicateFns(raw_ostream &OS);
/// Analyze pattern \p P, returning a matcher for it if possible.
/// Otherwise, return an Error explaining why we don't support it.
@ -3045,6 +3085,20 @@ void GlobalISelEmitter::gatherOpcodeValues() {
void GlobalISelEmitter::gatherTypeIDValues() {
LLTOperandMatcher::initTypeIDValuesMap();
}
unsigned GlobalISelEmitter::getOrCreateInstructionPredicateFnId(StringRef Code) {
// There's not very many predicates that need to be here at the moment so we
// just maintain a simple set-like vector. If it grows then we'll need to do
// something more efficient.
const auto &I = std::find(InstructionPredicateCodes.begin(),
InstructionPredicateCodes.end(),
Code);
if (I == InstructionPredicateCodes.end()) {
unsigned ID = InstructionPredicateCodes.size();
InstructionPredicateCodes.push_back(Code);
return ID;
}
return std::distance(InstructionPredicateCodes.begin(), I);
}
void GlobalISelEmitter::gatherNodeEquivs() {
assert(NodeEquivs.empty());
@ -3106,7 +3160,7 @@ GlobalISelEmitter::importRulePredicates(RuleMatcher &M,
Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher(
RuleMatcher &Rule, InstructionMatcher &InsnMatcher,
const TreePatternNode *Src, unsigned &TempOpIdx) const {
const TreePatternNode *Src, unsigned &TempOpIdx) {
Record *SrcGIEquivOrNull = nullptr;
const CodeGenInstruction *SrcGIOrNull = nullptr;
@ -3251,6 +3305,11 @@ Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher(
}
}
if (Predicate.hasGISelPredicateCode()) {
InsnMatcher.addPredicate<GenericInstructionPredicateMatcher>(Predicate);
continue;
}
return failedImport("Src pattern child has predicate (" +
explainPredicates(Src) + ")");
}
@ -3329,7 +3388,7 @@ Error GlobalISelEmitter::importChildMatcher(RuleMatcher &Rule,
const TreePatternNode *SrcChild,
bool OperandIsAPointer,
unsigned OpIdx,
unsigned &TempOpIdx) const {
unsigned &TempOpIdx) {
OperandMatcher &OM =
InsnMatcher.addOperand(OpIdx, SrcChild->getName(), TempOpIdx);
if (OM.isSameAsAnotherOperand())
@ -3991,14 +4050,15 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
// 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.
void GlobalISelEmitter::emitImmPredicates(
raw_ostream &OS, StringRef TypeIdentifier, StringRef Type,
void GlobalISelEmitter::emitCxxPredicateFns(
raw_ostream &OS, StringRef CodeFieldName, StringRef TypeIdentifier,
StringRef ArgType, StringRef ArgName, StringRef AdditionalDeclarations,
std::function<bool(const Record *R)> Filter) {
std::vector<const Record *> MatchedRecords;
const auto &Defs = RK.getAllDerivedDefinitions("PatFrag");
std::copy_if(Defs.begin(), Defs.end(), std::back_inserter(MatchedRecords),
[&](Record *Record) {
return !Record->getValueAsString("ImmediateCode").empty() &&
return !Record->getValueAsString(CodeFieldName).empty() &&
Filter(Record);
});
@ -4015,16 +4075,20 @@ void GlobalISelEmitter::emitImmPredicates(
OS << "};\n";
}
OS << "bool " << Target.getName() << "InstructionSelector::testImmPredicate_"
<< TypeIdentifier << "(unsigned PredicateID, " << Type
<< " Imm) const {\n";
OS << "bool " << Target.getName() << "InstructionSelector::test" << ArgName
<< "Predicate_" << TypeIdentifier << "(unsigned PredicateID, " << ArgType << " "
<< ArgName << ") const {\n"
<< AdditionalDeclarations;
if (!AdditionalDeclarations.empty())
OS << "\n";
if (!MatchedRecords.empty())
OS << " switch (PredicateID) {\n";
for (const auto *Record : MatchedRecords) {
OS << " case GIPFP_" << TypeIdentifier << "_Predicate_"
<< Record->getName() << ": {\n"
<< " " << Record->getValueAsString("ImmediateCode") << "\n"
<< " llvm_unreachable(\"ImmediateCode should have returned\");\n"
<< " " << Record->getValueAsString(CodeFieldName) << "\n"
<< " llvm_unreachable(\"" << CodeFieldName
<< " should have returned\");\n"
<< " return false;\n"
<< " }\n";
}
@ -4035,6 +4099,22 @@ void GlobalISelEmitter::emitImmPredicates(
<< "}\n";
}
void GlobalISelEmitter::emitImmPredicateFns(
raw_ostream &OS, StringRef TypeIdentifier, StringRef ArgType,
std::function<bool(const Record *R)> Filter) {
return emitCxxPredicateFns(OS, "ImmediateCode", TypeIdentifier, ArgType,
"Imm", "", Filter);
}
void GlobalISelEmitter::emitMIPredicateFns(raw_ostream &OS) {
return emitCxxPredicateFns(
OS, "GISelPredicateCode", "MI", "const MachineInstr &", "MI",
" const MachineFunction &MF = *MI.getParent()->getParent();\n"
" const LLVM_ATTRIBUTE_UNUSED MachineRegisterInfo &MRI = "
"MF.getRegInfo();",
[](const Record *R) { return true; });
}
template <class GroupT>
std::vector<Matcher *> GlobalISelEmitter::optimizeRules(
ArrayRef<Matcher *> Rules,
@ -4261,6 +4341,8 @@ void GlobalISelEmitter::run(raw_ostream &OS) {
<< " bool testImmPredicate_APFloat(unsigned PredicateID, const APFloat "
"&Imm) const override;\n"
<< " const int64_t *getMatchTable() const override;\n"
<< " bool testMIPredicate_MI(unsigned PredicateID, const MachineInstr &MI) "
"const override;\n"
<< "#endif // ifdef GET_GLOBALISEL_TEMPORARIES_DECL\n\n";
OS << "#ifdef GET_GLOBALISEL_TEMPORARIES_INIT\n"
@ -4374,18 +4456,19 @@ void GlobalISelEmitter::run(raw_ostream &OS) {
OS << "};\n"
<< "// See constructor for table contents\n\n";
emitImmPredicates(OS, "I64", "int64_t", [](const Record *R) {
emitImmPredicateFns(OS, "I64", "int64_t", [](const Record *R) {
bool Unset;
return !R->getValueAsBitOrUnset("IsAPFloat", Unset) &&
!R->getValueAsBit("IsAPInt");
});
emitImmPredicates(OS, "APFloat", "const APFloat &", [](const Record *R) {
emitImmPredicateFns(OS, "APFloat", "const APFloat &", [](const Record *R) {
bool Unset;
return R->getValueAsBitOrUnset("IsAPFloat", Unset);
});
emitImmPredicates(OS, "APInt", "const APInt &", [](const Record *R) {
emitImmPredicateFns(OS, "APInt", "const APInt &", [](const Record *R) {
return R->getValueAsBit("IsAPInt");
});
emitMIPredicateFns(OS);
OS << "\n";
OS << Target.getName() << "InstructionSelector::ComplexMatcherMemFn\n"