-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[TableGen] HasOneUse builtin predicate on PatFrags #91578
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-backend-amdgpu @llvm/pr-subscribers-llvm-globalisel Author: None (jofrn) ChangesThis predicate tells GlobalISelEmitter and DAGISelEmitter to check that the instruction to emit has only one use of its result. This can be used on a PatFrag instead of defining custom predicates for both emitters per record that requires it. Full diff: https://github.com/llvm/llvm-project/pull/91578.diff 8 Files Affected:
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h
index 72c63ecba529f..acaf0ab05f900 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h
@@ -212,6 +212,10 @@ enum {
/// - InsnID(ULEB128) - Instruction ID
GIM_CheckHasNoUse,
+ /// Check if there's one use of the first result.
+ /// - InsnID(ULEB128) - Instruction ID
+ GIM_CheckHasOneUse,
+
/// Check the type for the specified operand
/// - InsnID(ULEB128) - Instruction ID
/// - OpIdx(ULEB128) - Operand index
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h
index 4d147bf20c26a..e5fb0f56e51aa 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h
@@ -468,7 +468,23 @@ bool GIMatchTableExecutor::executeMatchTable(
if (handleReject() == RejectAndGiveUp)
return false;
}
+ break;
+ }
+ case GIM_CheckHasOneUse: {
+ uint64_t InsnID = readULEB();
+ DEBUG_WITH_TYPE(TgtExecutor::getName(),
+ dbgs() << CurrentIdx << ": GIM_CheckHasOneUse(MIs["
+ << InsnID << "]\n");
+
+ const MachineInstr *MI = State.MIs[InsnID];
+ assert(MI && "Used insn before defined");
+ assert(MI->getNumDefs() > 0 && "No defs");
+ const Register Res = MI->getOperand(0).getReg();
+
+ if (!MRI.hasOneNonDBGUse(Res))
+ if (handleReject() == RejectAndGiveUp)
+ return false;
break;
}
case GIM_CheckAtomicOrdering: {
diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td
index 1684b424e3b44..1c95a60909846 100644
--- a/llvm/include/llvm/Target/TargetSelectionDAG.td
+++ b/llvm/include/llvm/Target/TargetSelectionDAG.td
@@ -884,6 +884,9 @@ class PatFrags<dag ops, list<dag> frags, code pred = [{}],
// If set to true, a predicate is added that checks for the absence of use of
// the first result.
bit HasNoUse = ?;
+ // If set to true, a predicate is added that checks for the sole use of
+ // the first result.
+ bit HasOneUse = ?;
// Is the desired pre-packaged predicate for a load?
bit IsLoad = ?;
diff --git a/llvm/test/TableGen/predicate-patfags.td b/llvm/test/TableGen/predicate-patfags.td
index 2cf29769dc13a..a7e82f3302042 100644
--- a/llvm/test/TableGen/predicate-patfags.td
+++ b/llvm/test/TableGen/predicate-patfags.td
@@ -1,5 +1,7 @@
-// RUN: llvm-tblgen -gen-dag-isel -I %p/../../include -I %p/Common %s 2>&1 | FileCheck -check-prefix=SDAG %s
-// RUN: llvm-tblgen -gen-global-isel -I %p/../../include -I %p/Common %s 2>&1 | FileCheck -check-prefix=GISEL %s
+// RUN: llvm-tblgen -gen-dag-isel -I %p/../../include -I %p/Common %s 2>&1 | FileCheck -check-prefixes=SDAG,SCUSTOM %s
+// RUN: llvm-tblgen -gen-dag-isel -I %p/../../include -I %p/Common %s -DHASONEUSE 2>&1 | FileCheck -check-prefixes=SDAG,SBUILTIN %s
+// RUN: llvm-tblgen -gen-global-isel -I %p/../../include -I %p/Common %s 2>&1 | FileCheck -check-prefixes=GISEL,GCUSTOM %s
+// RUN: llvm-tblgen -gen-global-isel -I %p/../../include -I %p/Common %s -DHASONEUSE 2>&1 | FileCheck -check-prefixes=GISEL,GBUILTIN %s
include "llvm/Target/Target.td"
include "GlobalISelEmitterCommon.td"
@@ -32,10 +34,15 @@ def : GINodeEquiv<G_TGT_MUL24, TGTmul24_impl>;
def TGTmul24_oneuse : PatFrag<
(ops node:$src0, node:$src1),
(TGTmul24 $src0, $src1),
+#ifndef HASONEUSE
[{ return N->hasOneUse(); }]> {
let GISelPredicateCode = [{
return MRI->hasOneNonDBGUse(MI.getOperand(0).getReg());
}];
+#else
+ [{ }]> {
+ let HasOneUse = 1;
+#endif
}
// SDAG: OPC_CheckOpcode, TARGET_VAL(ISD::INTRINSIC_W_CHAIN),
@@ -44,19 +51,26 @@ def TGTmul24_oneuse : PatFrag<
// SDAG: OPC_CheckOpcode, TARGET_VAL(TargetISD::MUL24),
// SDAG: OPC_CheckPredicate0, // Predicate_TGTmul24_oneuse
+// SCUSTOM: return N->hasOneUse();
+// SBUILTIN: if (!SDValue(N, 0).hasOneUse()) return false;
+
// GISEL: GIM_CheckOpcode, /*MI*/1, GIMT_Encode2(TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS),
// GISEL: GIM_CheckIntrinsicID, /*MI*/1, /*Op*/1, GIMT_Encode2(Intrinsic::tgt_mul24),
-// GISEL: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
+// GBUILTIN: GIM_CheckHasOneUse, /*MI*/1,
+// GCUSTOM: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
// GISEL: GIM_CheckOpcode, /*MI*/1, GIMT_Encode2(TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS),
// GISEL: GIM_CheckIntrinsicID, /*MI*/1, /*Op*/1, GIMT_Encode2(Intrinsic::tgt_mul24),
-// GISEL: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
+// GBUILTIN: GIM_CheckHasOneUse, /*MI*/1,
+// GCUSTOM: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
// GISEL: GIM_CheckOpcode, /*MI*/1, GIMT_Encode2(MyTarget::G_TGT_MUL24),
-// GISEL: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
+// GBUILTIN: GIM_CheckHasOneUse, /*MI*/1,
+// GCUSTOM: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
// GISEL: GIM_CheckOpcode, /*MI*/1, GIMT_Encode2(MyTarget::G_TGT_MUL24),
-// GISEL: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
+// GBUILTIN: GIM_CheckHasOneUse, /*MI*/1,
+// GCUSTOM: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
def inst_mad24 : I<
(outs GPR32:$dst),
(ins GPR32:$src0, GPR32:$src1, GPR32:$src2),
diff --git a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
index 88d353e89a461..709aa00ae8b32 100644
--- a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
@@ -903,7 +903,7 @@ TreePredicateFn::TreePredicateFn(TreePattern *N) : PatFragRec(N) {
}
bool TreePredicateFn::hasPredCode() const {
- return isLoad() || isStore() || isAtomic() || hasNoUse() ||
+ return isLoad() || isStore() || isAtomic() || hasNoUse() || hasOneUse() ||
!PatFragRec->getRecord()->getValueAsString("PredicateCode").empty();
}
@@ -1140,6 +1140,8 @@ std::string TreePredicateFn::getPredCode() const {
if (hasNoUse())
Code += "if (!SDValue(N, 0).use_empty()) return false;\n";
+ if (hasOneUse())
+ Code += "if (!SDValue(N, 0).hasOneUse()) return false;\n";
std::string PredicateCode =
std::string(PatFragRec->getRecord()->getValueAsString("PredicateCode"));
@@ -1187,6 +1189,9 @@ bool TreePredicateFn::usesOperands() const {
bool TreePredicateFn::hasNoUse() const {
return isPredefinedPredicateEqualTo("HasNoUse", true);
}
+bool TreePredicateFn::hasOneUse() const {
+ return isPredefinedPredicateEqualTo("HasOneUse", true);
+}
bool TreePredicateFn::isLoad() const {
return isPredefinedPredicateEqualTo("IsLoad", true);
}
diff --git a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h
index 7f94db0b7d5d7..1f4d45d81fd33 100644
--- a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h
+++ b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h
@@ -533,6 +533,8 @@ class TreePredicateFn {
// Check if the HasNoUse predicate is set.
bool hasNoUse() const;
+ // Check if the HasOneUse predicate is set.
+ bool hasOneUse() const;
// Is the desired predefined predicate for a load?
bool isLoad() const;
diff --git a/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h b/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h
index 30301c28ce6c4..40e9ba7b899f9 100644
--- a/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h
+++ b/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h
@@ -798,6 +798,7 @@ class PredicateMatcher {
IPM_MemoryAlignment,
IPM_VectorSplatImm,
IPM_NoUse,
+ IPM_OneUse,
IPM_GenericPredicate,
IPM_MIFlags,
OPM_SameOperand,
@@ -1683,6 +1684,28 @@ class NoUsePredicateMatcher : public InstructionPredicateMatcher {
}
};
+/// Generates code to check for the sole use of the result.
+class OneUsePredicateMatcher : public InstructionPredicateMatcher {
+public:
+ OneUsePredicateMatcher(unsigned InsnVarID)
+ : InstructionPredicateMatcher(IPM_OneUse, InsnVarID) {}
+
+ static bool classof(const PredicateMatcher *P) {
+ return P->getKind() == IPM_OneUse;
+ }
+
+ bool isIdentical(const PredicateMatcher &B) const override {
+ return InstructionPredicateMatcher::isIdentical(B);
+ }
+
+ void emitPredicateOpcodes(MatchTable &Table,
+ RuleMatcher &Rule) const override {
+ Table << MatchTable::Opcode("GIM_CheckHasOneUse")
+ << MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
+ << MatchTable::LineBreak;
+ }
+};
+
/// Generates code to check that a set of predicates and operands match for a
/// particular instruction.
///
diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp
index cf7e4398741ca..0f5c48c351008 100644
--- a/llvm/utils/TableGen/GlobalISelEmitter.cpp
+++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp
@@ -210,6 +210,9 @@ static Error isTrivialOperatorNode(const TreePatternNode &N) {
if (Predicate.hasNoUse())
continue;
+ if (Predicate.hasOneUse())
+ continue;
+
if (Predicate.isNonExtLoad() || Predicate.isAnyExtLoad() ||
Predicate.isSignExtLoad() || Predicate.isZeroExtLoad())
continue;
@@ -782,6 +785,10 @@ Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher(
InsnMatcher.addPredicate<NoUsePredicateMatcher>();
HasAddedBuiltinMatcher = true;
}
+ if (Predicate.hasOneUse()) {
+ InsnMatcher.addPredicate<OneUsePredicateMatcher>();
+ HasAddedBuiltinMatcher = true;
+ }
if (Predicate.hasGISelPredicateCode()) {
if (Predicate.usesOperands()) {
|
@llvm/pr-subscribers-llvm-selectiondag Author: None (jofrn) ChangesThis predicate tells GlobalISelEmitter and DAGISelEmitter to check that the instruction to emit has only one use of its result. This can be used on a PatFrag instead of defining custom predicates for both emitters per record that requires it. Full diff: https://github.com/llvm/llvm-project/pull/91578.diff 8 Files Affected:
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h
index 72c63ecba529f..acaf0ab05f900 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h
@@ -212,6 +212,10 @@ enum {
/// - InsnID(ULEB128) - Instruction ID
GIM_CheckHasNoUse,
+ /// Check if there's one use of the first result.
+ /// - InsnID(ULEB128) - Instruction ID
+ GIM_CheckHasOneUse,
+
/// Check the type for the specified operand
/// - InsnID(ULEB128) - Instruction ID
/// - OpIdx(ULEB128) - Operand index
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h
index 4d147bf20c26a..e5fb0f56e51aa 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h
@@ -468,7 +468,23 @@ bool GIMatchTableExecutor::executeMatchTable(
if (handleReject() == RejectAndGiveUp)
return false;
}
+ break;
+ }
+ case GIM_CheckHasOneUse: {
+ uint64_t InsnID = readULEB();
+ DEBUG_WITH_TYPE(TgtExecutor::getName(),
+ dbgs() << CurrentIdx << ": GIM_CheckHasOneUse(MIs["
+ << InsnID << "]\n");
+
+ const MachineInstr *MI = State.MIs[InsnID];
+ assert(MI && "Used insn before defined");
+ assert(MI->getNumDefs() > 0 && "No defs");
+ const Register Res = MI->getOperand(0).getReg();
+
+ if (!MRI.hasOneNonDBGUse(Res))
+ if (handleReject() == RejectAndGiveUp)
+ return false;
break;
}
case GIM_CheckAtomicOrdering: {
diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td
index 1684b424e3b44..1c95a60909846 100644
--- a/llvm/include/llvm/Target/TargetSelectionDAG.td
+++ b/llvm/include/llvm/Target/TargetSelectionDAG.td
@@ -884,6 +884,9 @@ class PatFrags<dag ops, list<dag> frags, code pred = [{}],
// If set to true, a predicate is added that checks for the absence of use of
// the first result.
bit HasNoUse = ?;
+ // If set to true, a predicate is added that checks for the sole use of
+ // the first result.
+ bit HasOneUse = ?;
// Is the desired pre-packaged predicate for a load?
bit IsLoad = ?;
diff --git a/llvm/test/TableGen/predicate-patfags.td b/llvm/test/TableGen/predicate-patfags.td
index 2cf29769dc13a..a7e82f3302042 100644
--- a/llvm/test/TableGen/predicate-patfags.td
+++ b/llvm/test/TableGen/predicate-patfags.td
@@ -1,5 +1,7 @@
-// RUN: llvm-tblgen -gen-dag-isel -I %p/../../include -I %p/Common %s 2>&1 | FileCheck -check-prefix=SDAG %s
-// RUN: llvm-tblgen -gen-global-isel -I %p/../../include -I %p/Common %s 2>&1 | FileCheck -check-prefix=GISEL %s
+// RUN: llvm-tblgen -gen-dag-isel -I %p/../../include -I %p/Common %s 2>&1 | FileCheck -check-prefixes=SDAG,SCUSTOM %s
+// RUN: llvm-tblgen -gen-dag-isel -I %p/../../include -I %p/Common %s -DHASONEUSE 2>&1 | FileCheck -check-prefixes=SDAG,SBUILTIN %s
+// RUN: llvm-tblgen -gen-global-isel -I %p/../../include -I %p/Common %s 2>&1 | FileCheck -check-prefixes=GISEL,GCUSTOM %s
+// RUN: llvm-tblgen -gen-global-isel -I %p/../../include -I %p/Common %s -DHASONEUSE 2>&1 | FileCheck -check-prefixes=GISEL,GBUILTIN %s
include "llvm/Target/Target.td"
include "GlobalISelEmitterCommon.td"
@@ -32,10 +34,15 @@ def : GINodeEquiv<G_TGT_MUL24, TGTmul24_impl>;
def TGTmul24_oneuse : PatFrag<
(ops node:$src0, node:$src1),
(TGTmul24 $src0, $src1),
+#ifndef HASONEUSE
[{ return N->hasOneUse(); }]> {
let GISelPredicateCode = [{
return MRI->hasOneNonDBGUse(MI.getOperand(0).getReg());
}];
+#else
+ [{ }]> {
+ let HasOneUse = 1;
+#endif
}
// SDAG: OPC_CheckOpcode, TARGET_VAL(ISD::INTRINSIC_W_CHAIN),
@@ -44,19 +51,26 @@ def TGTmul24_oneuse : PatFrag<
// SDAG: OPC_CheckOpcode, TARGET_VAL(TargetISD::MUL24),
// SDAG: OPC_CheckPredicate0, // Predicate_TGTmul24_oneuse
+// SCUSTOM: return N->hasOneUse();
+// SBUILTIN: if (!SDValue(N, 0).hasOneUse()) return false;
+
// GISEL: GIM_CheckOpcode, /*MI*/1, GIMT_Encode2(TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS),
// GISEL: GIM_CheckIntrinsicID, /*MI*/1, /*Op*/1, GIMT_Encode2(Intrinsic::tgt_mul24),
-// GISEL: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
+// GBUILTIN: GIM_CheckHasOneUse, /*MI*/1,
+// GCUSTOM: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
// GISEL: GIM_CheckOpcode, /*MI*/1, GIMT_Encode2(TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS),
// GISEL: GIM_CheckIntrinsicID, /*MI*/1, /*Op*/1, GIMT_Encode2(Intrinsic::tgt_mul24),
-// GISEL: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
+// GBUILTIN: GIM_CheckHasOneUse, /*MI*/1,
+// GCUSTOM: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
// GISEL: GIM_CheckOpcode, /*MI*/1, GIMT_Encode2(MyTarget::G_TGT_MUL24),
-// GISEL: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
+// GBUILTIN: GIM_CheckHasOneUse, /*MI*/1,
+// GCUSTOM: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
// GISEL: GIM_CheckOpcode, /*MI*/1, GIMT_Encode2(MyTarget::G_TGT_MUL24),
-// GISEL: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
+// GBUILTIN: GIM_CheckHasOneUse, /*MI*/1,
+// GCUSTOM: GIM_CheckCxxInsnPredicate, /*MI*/1, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_TGTmul24_oneuse),
def inst_mad24 : I<
(outs GPR32:$dst),
(ins GPR32:$src0, GPR32:$src1, GPR32:$src2),
diff --git a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
index 88d353e89a461..709aa00ae8b32 100644
--- a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
@@ -903,7 +903,7 @@ TreePredicateFn::TreePredicateFn(TreePattern *N) : PatFragRec(N) {
}
bool TreePredicateFn::hasPredCode() const {
- return isLoad() || isStore() || isAtomic() || hasNoUse() ||
+ return isLoad() || isStore() || isAtomic() || hasNoUse() || hasOneUse() ||
!PatFragRec->getRecord()->getValueAsString("PredicateCode").empty();
}
@@ -1140,6 +1140,8 @@ std::string TreePredicateFn::getPredCode() const {
if (hasNoUse())
Code += "if (!SDValue(N, 0).use_empty()) return false;\n";
+ if (hasOneUse())
+ Code += "if (!SDValue(N, 0).hasOneUse()) return false;\n";
std::string PredicateCode =
std::string(PatFragRec->getRecord()->getValueAsString("PredicateCode"));
@@ -1187,6 +1189,9 @@ bool TreePredicateFn::usesOperands() const {
bool TreePredicateFn::hasNoUse() const {
return isPredefinedPredicateEqualTo("HasNoUse", true);
}
+bool TreePredicateFn::hasOneUse() const {
+ return isPredefinedPredicateEqualTo("HasOneUse", true);
+}
bool TreePredicateFn::isLoad() const {
return isPredefinedPredicateEqualTo("IsLoad", true);
}
diff --git a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h
index 7f94db0b7d5d7..1f4d45d81fd33 100644
--- a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h
+++ b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h
@@ -533,6 +533,8 @@ class TreePredicateFn {
// Check if the HasNoUse predicate is set.
bool hasNoUse() const;
+ // Check if the HasOneUse predicate is set.
+ bool hasOneUse() const;
// Is the desired predefined predicate for a load?
bool isLoad() const;
diff --git a/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h b/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h
index 30301c28ce6c4..40e9ba7b899f9 100644
--- a/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h
+++ b/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h
@@ -798,6 +798,7 @@ class PredicateMatcher {
IPM_MemoryAlignment,
IPM_VectorSplatImm,
IPM_NoUse,
+ IPM_OneUse,
IPM_GenericPredicate,
IPM_MIFlags,
OPM_SameOperand,
@@ -1683,6 +1684,28 @@ class NoUsePredicateMatcher : public InstructionPredicateMatcher {
}
};
+/// Generates code to check for the sole use of the result.
+class OneUsePredicateMatcher : public InstructionPredicateMatcher {
+public:
+ OneUsePredicateMatcher(unsigned InsnVarID)
+ : InstructionPredicateMatcher(IPM_OneUse, InsnVarID) {}
+
+ static bool classof(const PredicateMatcher *P) {
+ return P->getKind() == IPM_OneUse;
+ }
+
+ bool isIdentical(const PredicateMatcher &B) const override {
+ return InstructionPredicateMatcher::isIdentical(B);
+ }
+
+ void emitPredicateOpcodes(MatchTable &Table,
+ RuleMatcher &Rule) const override {
+ Table << MatchTable::Opcode("GIM_CheckHasOneUse")
+ << MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
+ << MatchTable::LineBreak;
+ }
+};
+
/// Generates code to check that a set of predicates and operands match for a
/// particular instruction.
///
diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp
index cf7e4398741ca..0f5c48c351008 100644
--- a/llvm/utils/TableGen/GlobalISelEmitter.cpp
+++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp
@@ -210,6 +210,9 @@ static Error isTrivialOperatorNode(const TreePatternNode &N) {
if (Predicate.hasNoUse())
continue;
+ if (Predicate.hasOneUse())
+ continue;
+
if (Predicate.isNonExtLoad() || Predicate.isAnyExtLoad() ||
Predicate.isSignExtLoad() || Predicate.isZeroExtLoad())
continue;
@@ -782,6 +785,10 @@ Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher(
InsnMatcher.addPredicate<NoUsePredicateMatcher>();
HasAddedBuiltinMatcher = true;
}
+ if (Predicate.hasOneUse()) {
+ InsnMatcher.addPredicate<OneUsePredicateMatcher>();
+ HasAddedBuiltinMatcher = true;
+ }
if (Predicate.hasGISelPredicateCode()) {
if (Predicate.usesOperands()) {
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should also switch the AMDGPU HasOneUseUnaryOp/HasOneUseBinOp/HasOneUseTernaryOp to use this
const MachineInstr *MI = State.MIs[InsnID]; | ||
assert(MI && "Used insn before defined"); | ||
assert(MI->getNumDefs() > 0 && "No defs"); | ||
const Register Res = MI->getOperand(0).getReg(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might want an extended variant that can handle multiple defs at some point
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it depends on how many of these are emitted at once for a given pattern, we need to see more use first
Though this is just 2 bytes on average (1 byte opcode + 1 byte id) so it's not a big deal unless this is emitted in bulk in a ton of rules
Right, thanks. I was thinking that these changes can go in a follow-up commit. The current change is a general one for [TableGen], but the next one can be [AMDGPU] with the actual usages of the |
Can do it either way. At least it's more clear it works with the real uses |
const MachineInstr *MI = State.MIs[InsnID]; | ||
assert(MI && "Used insn before defined"); | ||
assert(MI->getNumDefs() > 0 && "No defs"); | ||
const Register Res = MI->getOperand(0).getReg(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it depends on how many of these are emitted at once for a given pattern, we need to see more use first
Though this is just 2 bytes on average (1 byte opcode + 1 byte id) so it's not a big deal unless this is emitted in bulk in a ton of rules
llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h
Outdated
Show resolved
Hide resolved
e61aba4
to
1bd40f1
Compare
826d910
to
e1efefa
Compare
This predicate tells GlobalISelEmitter and DAGISelEmitter to check that the instruction to emit has only one use of its result. This can be used on a PatFrag instead of defining custom predicates for both emitters per record that requires it.
e1efefa
to
2441f99
Compare
#91578 implements `HasOneUse` predicate on `PatFrag`, so this commit uses it within AMDGPU.
This predicate tells GlobalISelEmitter and DAGISelEmitter to check that the instruction to emit has only one use of its result. This can be used on a PatFrag instead of defining custom predicates for both emitters per record that requires it.