Skip to content

Commit 9df8361

Browse files
authored
[GlobalISelMatchTable] Don't hoist C++ predicates over operand recorders (#159329)
The pattern optimizations in GlobalISelMatchTable.cpp can extract common predicates out of pattern alternatives by putting the pattern alternatives into a GroupMatcher and moving common predicates into the GroupMatcher's predicate list. This patch adds checks to avoid hoisting a common predicate before matchers that record named operands that the predicate uses, which would lead to segfaults when the imported patterns are matched. See the added test for a concrete example inspired by the AMDGPU backend. This fixes a bug encountered in #143881.
1 parent 53c386f commit 9df8361

File tree

3 files changed

+107
-3
lines changed

3 files changed

+107
-3
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// RUN: llvm-tblgen %s -gen-global-isel -optimize-match-table=true -I %p/../../../include -I %p/../Common | FileCheck %s
2+
3+
include "llvm/Target/Target.td"
4+
include "GlobalISelEmitterCommon.td"
5+
6+
def InstThreeOperands : I<(outs GPR32:$dst), (ins GPR32:$src0, GPR32:$src1, GPR32:$src2), []>;
7+
8+
class ThreeOpFrag<SDPatternOperator op1, SDPatternOperator op2> : PatFrag<
9+
(ops node:$x, node:$y, node:$z),
10+
(op2 (op1 node:$x, node:$y), node:$z),
11+
[{
12+
return Operands[0] && Operands[1] && Operands[2];
13+
}]> {
14+
let PredicateCodeUsesOperands = 1;
15+
let GISelPredicateCode = [{
16+
return Operands[0] && Operands[1] && Operands[2];
17+
}];
18+
}
19+
20+
def ptradd_commutative : PatFrags<(ops node:$src0, node:$src1),
21+
[(ptradd node:$src0, node:$src1), (ptradd node:$src1, node:$src0)]>;
22+
23+
// ptradd_commutative has two PatFrags, therefore there are two ways how the
24+
// below pattern could match. Both require checking the C++ predicate, but that
25+
// check cannot be hoisted because it relies on recorded operands, which differ
26+
// between the PatFrags. This is inspired by a similar construct in the AMDGPU
27+
// backend.
28+
29+
// CHECK: GIM_Try, /*On fail goto*//*Label 1*/
30+
// CHECK: GIM_RecordNamedOperand
31+
// CHECK: GIM_RecordNamedOperand
32+
// CHECK: GIM_RecordNamedOperand
33+
// CHECK: GIM_CheckCxxInsnPredicate
34+
// CHECK: // Label 1
35+
// CHECK: GIM_Try, /*On fail goto*//*Label 2*/
36+
// CHECK: GIM_RecordNamedOperand
37+
// CHECK: GIM_RecordNamedOperand
38+
// CHECK: GIM_RecordNamedOperand
39+
// CHECK: GIM_CheckCxxInsnPredicate
40+
// CHECK: // Label 2
41+
def : Pat <(i32 (ThreeOpFrag<shl, ptradd_commutative> GPR32:$src0, GPR32:$src1, GPR32:$src2)),
42+
(InstThreeOperands GPR32:$src0, GPR32:$src1, GPR32:$src2)> ;
43+

llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.cpp

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ static std::string getEncodedEmitStr(StringRef NamedValue, unsigned NumBytes) {
155155
llvm_unreachable("Unsupported number of bytes!");
156156
}
157157

158+
template <class Range> static bool matchersRecordOperand(Range &&R) {
159+
return any_of(R, [](const auto &I) { return I->recordsOperand(); });
160+
}
161+
158162
//===- Global Data --------------------------------------------------------===//
159163

160164
std::set<LLTCodeGen> llvm::gi::KnownTypes;
@@ -457,6 +461,10 @@ Matcher::~Matcher() {}
457461

458462
//===- GroupMatcher -------------------------------------------------------===//
459463

464+
bool GroupMatcher::recordsOperand() const {
465+
return matchersRecordOperand(Conditions) || matchersRecordOperand(Matchers);
466+
}
467+
460468
bool GroupMatcher::candidateConditionMatches(
461469
const PredicateMatcher &Predicate) const {
462470

@@ -486,12 +494,24 @@ std::unique_ptr<PredicateMatcher> GroupMatcher::popFirstCondition() {
486494
return P;
487495
}
488496

497+
/// Check if the Condition, which is a predicate of M, cannot be hoisted outside
498+
/// of (i.e., checked before) M.
499+
static bool cannotHoistCondition(const PredicateMatcher &Condition,
500+
const Matcher &M) {
501+
// The condition can't be hoisted if it is a C++ predicate that refers to
502+
// operands and the operands are registered within the matcher.
503+
return Condition.dependsOnOperands() && M.recordsOperand();
504+
}
505+
489506
bool GroupMatcher::addMatcher(Matcher &Candidate) {
490507
if (!Candidate.hasFirstCondition())
491508
return false;
492509

510+
// Only add candidates that have a matching first condition that can be
511+
// hoisted into the GroupMatcher.
493512
const PredicateMatcher &Predicate = Candidate.getFirstCondition();
494-
if (!candidateConditionMatches(Predicate))
513+
if (!candidateConditionMatches(Predicate) ||
514+
cannotHoistCondition(Predicate, Candidate))
495515
return false;
496516

497517
Matchers.push_back(&Candidate);
@@ -509,10 +529,17 @@ void GroupMatcher::finalize() {
509529
for (const auto &Rule : Matchers)
510530
if (!Rule->hasFirstCondition())
511531
return;
532+
// Hoist the first condition if it is identical in all matchers in the group
533+
// and it can be hoisted in every matcher.
512534
const auto &FirstCondition = FirstRule.getFirstCondition();
513-
for (unsigned I = 1, E = Matchers.size(); I < E; ++I)
514-
if (!Matchers[I]->getFirstCondition().isIdentical(FirstCondition))
535+
if (cannotHoistCondition(FirstCondition, FirstRule))
536+
return;
537+
for (unsigned I = 1, E = Matchers.size(); I < E; ++I) {
538+
const auto &OtherFirstCondition = Matchers[I]->getFirstCondition();
539+
if (!OtherFirstCondition.isIdentical(FirstCondition) ||
540+
cannotHoistCondition(OtherFirstCondition, *Matchers[I]))
515541
return;
542+
}
516543

517544
Conditions.push_back(FirstRule.popFirstCondition());
518545
for (unsigned I = 1, E = Matchers.size(); I < E; ++I)
@@ -569,6 +596,12 @@ void GroupMatcher::optimize() {
569596

570597
//===- SwitchMatcher ------------------------------------------------------===//
571598

599+
bool SwitchMatcher::recordsOperand() const {
600+
assert(!isa_and_present<RecordNamedOperandMatcher>(Condition.get()) &&
601+
"Switch conditions should not record named operands");
602+
return matchersRecordOperand(Matchers);
603+
}
604+
572605
bool SwitchMatcher::isSupportedPredicateType(const PredicateMatcher &P) {
573606
return isa<InstructionOpcodeMatcher>(P) || isa<LLTOperandMatcher>(P);
574607
}
@@ -709,6 +742,10 @@ StringRef RuleMatcher::getOpcode() const {
709742
return Matchers.front()->getOpcode();
710743
}
711744

745+
bool RuleMatcher::recordsOperand() const {
746+
return matchersRecordOperand(Matchers);
747+
}
748+
712749
LLTCodeGen RuleMatcher::getFirstConditionAsRootType() {
713750
InstructionMatcher &InsnMatcher = *Matchers.front();
714751
if (!InsnMatcher.predicates_empty())
@@ -1378,6 +1415,10 @@ TempTypeIdx OperandMatcher::getTempTypeIdx(RuleMatcher &Rule) {
13781415
return TTIdx;
13791416
}
13801417

1418+
bool OperandMatcher::recordsOperand() const {
1419+
return matchersRecordOperand(Predicates);
1420+
}
1421+
13811422
void OperandMatcher::emitPredicateOpcodes(MatchTable &Table,
13821423
RuleMatcher &Rule) {
13831424
if (!Optimized) {
@@ -1759,6 +1800,10 @@ OperandMatcher &InstructionMatcher::addPhysRegInput(const Record *Reg,
17591800
return *OM;
17601801
}
17611802

1803+
bool InstructionMatcher::recordsOperand() const {
1804+
return matchersRecordOperand(Predicates) || matchersRecordOperand(operands());
1805+
}
1806+
17621807
void InstructionMatcher::emitPredicateOpcodes(MatchTable &Table,
17631808
RuleMatcher &Rule) {
17641809
if (canAddNumOperandsCheck()) {

llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,10 @@ class Matcher {
313313
virtual bool hasFirstCondition() const = 0;
314314
virtual const PredicateMatcher &getFirstCondition() const = 0;
315315
virtual std::unique_ptr<PredicateMatcher> popFirstCondition() = 0;
316+
317+
/// Check recursively if the matcher records named operands for use in C++
318+
/// predicates.
319+
virtual bool recordsOperand() const = 0;
316320
};
317321

318322
class GroupMatcher final : public Matcher {
@@ -374,6 +378,8 @@ class GroupMatcher final : public Matcher {
374378
}
375379
bool hasFirstCondition() const override { return !Conditions.empty(); }
376380

381+
bool recordsOperand() const override;
382+
377383
private:
378384
/// See if a candidate matcher could be added to this group solely by
379385
/// analyzing its first condition.
@@ -439,6 +445,8 @@ class SwitchMatcher : public Matcher {
439445

440446
bool hasFirstCondition() const override { return false; }
441447

448+
bool recordsOperand() const override;
449+
442450
private:
443451
/// See if the predicate type has a Switch-implementation for it.
444452
static bool isSupportedPredicateType(const PredicateMatcher &Predicate);
@@ -669,6 +677,8 @@ class RuleMatcher : public Matcher {
669677
void optimize() override;
670678
void emit(MatchTable &Table) override;
671679

680+
bool recordsOperand() const override;
681+
672682
/// Compare the priority of this object and B.
673683
///
674684
/// Returns true if this object is more important than B.
@@ -858,6 +868,8 @@ class PredicateMatcher {
858868
return Kind == IPM_GenericPredicate;
859869
}
860870

871+
bool recordsOperand() const { return Kind == OPM_RecordNamedOperand; }
872+
861873
virtual bool isIdentical(const PredicateMatcher &B) const {
862874
return B.getKind() == getKind() && InsnVarID == B.InsnVarID &&
863875
OpIdx == B.OpIdx;
@@ -1322,6 +1334,8 @@ class OperandMatcher : public PredicateListMatcher<OperandPredicateMatcher> {
13221334
/// already been assigned, simply returns it.
13231335
TempTypeIdx getTempTypeIdx(RuleMatcher &Rule);
13241336

1337+
bool recordsOperand() const;
1338+
13251339
std::string getOperandExpr(unsigned InsnVarID) const;
13261340

13271341
InstructionMatcher &getInstructionMatcher() const { return Insn; }
@@ -1840,6 +1854,8 @@ class InstructionMatcher final : public PredicateListMatcher<PredicateMatcher> {
18401854

18411855
void optimize();
18421856

1857+
bool recordsOperand() const;
1858+
18431859
/// Emit MatchTable opcodes that test whether the instruction named in
18441860
/// InsnVarName matches all the predicates and all the operands.
18451861
void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule);

0 commit comments

Comments
 (0)