diff --git a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h index 371c5c5a0a1e19..cc2dd2f4e489c7 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 2ea9d11779f0a1..05f1a7e57e56b4 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h @@ -468,7 +468,24 @@ 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 1684b424e3b442..1c95a609098469 100644 --- a/llvm/include/llvm/Target/TargetSelectionDAG.td +++ b/llvm/include/llvm/Target/TargetSelectionDAG.td @@ -884,6 +884,9 @@ class PatFrags 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 2cf29769dc13a7..39133f324f305d 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" @@ -31,11 +33,16 @@ def : GINodeEquiv; def TGTmul24_oneuse : PatFrag< (ops node:$src0, node:$src1), - (TGTmul24 $src0, $src1), - [{ return N->hasOneUse(); }]> { + (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 88d353e89a4614..709aa00ae8b328 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 7f94db0b7d5d76..1f4d45d81fd333 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 5fe3f9a32c0164..edddc051c162c4 100644 --- a/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h +++ b/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h @@ -806,6 +806,7 @@ class PredicateMatcher { IPM_MemoryAlignment, IPM_VectorSplatImm, IPM_NoUse, + IPM_OneUse, IPM_GenericPredicate, IPM_MIFlags, OPM_SameOperand, @@ -1691,6 +1692,28 @@ class NoUsePredicateMatcher : public InstructionPredicateMatcher { } }; +/// Generates code to check that the first result has only one use. +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 9b356148cc171d..ec41cd9fec072d 100644 --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -207,7 +207,7 @@ static Error isTrivialOperatorNode(const TreePatternNode &N) { if (Predicate.isImmediatePattern()) continue; - if (Predicate.hasNoUse()) + if (Predicate.hasNoUse() || Predicate.hasOneUse()) continue; if (Predicate.isNonExtLoad() || Predicate.isAnyExtLoad() || @@ -782,6 +782,10 @@ Expected GlobalISelEmitter::createAndImportSelDAGMatcher( InsnMatcher.addPredicate(); HasAddedBuiltinMatcher = true; } + if (Predicate.hasOneUse()) { + InsnMatcher.addPredicate(); + HasAddedBuiltinMatcher = true; + } if (Predicate.hasGISelPredicateCode()) { if (Predicate.usesOperands()) {