27#include "llvm/ADT/STLExtras.h"
30#include "mlir/Dialect/ControlFlow/IR/ControlFlowOpsDialect.cpp.inc"
41struct ControlFlowInlinerInterface :
public DialectInlinerInterface {
42 using DialectInlinerInterface::DialectInlinerInterface;
43 ~ControlFlowInlinerInterface()
override =
default;
47 bool wouldBeCloned)
const final {
50 bool isLegalToInline(Operation *, Region *,
bool, IRMapping &)
const final {
55 void handleTerminator(Operation *op,
Block *newDest)
const final {}
63void ControlFlowDialect::initialize() {
66#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.cpp.inc"
68 addInterfaces<ControlFlowInlinerInterface>();
69 declarePromisedInterface<ConvertToLLVMPatternInterface, ControlFlowDialect>();
70 declarePromisedInterfaces<bufferization::BufferizableOpInterface, BranchOp,
72 declarePromisedInterface<bufferization::BufferDeallocationOpInterface,
80LogicalResult AssertOp::canonicalize(AssertOp op,
PatternRewriter &rewriter) {
90void AssertOp::getEffects(
110 if (std::next(successor->
begin()) != successor->
end())
113 BranchOp successorBranch = dyn_cast<BranchOp>(successor->
getTerminator());
114 if (!successorBranch)
119 if (user != successorBranch)
123 Block *successorDest = successorBranch.getDest();
124 if (successorDest == successor)
127 BranchOp nextBranch = dyn_cast<BranchOp>(successorDest->
getTerminator());
130 Block *nextBranchDest = nextBranch.getDest();
131 if (visited.contains(nextBranchDest))
133 visited.insert(nextBranchDest);
134 nextBranch = dyn_cast<BranchOp>(nextBranchDest->
getTerminator());
141 successor = successorDest;
142 successorOperands = operands;
147 for (
Value operand : operands) {
148 BlockArgument argOperand = llvm::dyn_cast<BlockArgument>(operand);
149 if (argOperand && argOperand.
getOwner() == successor)
150 argStorage.push_back(successorOperands[argOperand.
getArgNumber()]);
152 argStorage.push_back(operand);
154 successor = successorDest;
155 successorOperands = argStorage;
164 Block *succ = op.getDest();
165 Block *opParent = op->getBlock();
166 if (succ == opParent || !llvm::hasSingleElement(succ->
getPredecessors()))
172 for (
Value operand : op.getOperands())
173 if (
auto ba = dyn_cast<BlockArgument>(operand))
174 if (ba.getOwner() == succ)
192 Block *dest = op.getDest();
198 if (dest == op->getBlock() ||
226 bool changed =
false;
233 auto branch = dyn_cast<BranchOpInterface>(pred->getTerminator());
235 commonValue =
Value();
239 for (
auto [i, succ] : llvm::enumerate(branch->getSuccessors())) {
244 Value val = branch.getSuccessorOperands(i)[arg.getArgNumber()];
245 if (commonValue && commonValue != val) {
246 commonValue =
Value();
256 if (commonValue && commonValue != arg) {
267struct SimplifyUniformBlockArguments
270 LogicalResult matchAndRewrite(BranchOpInterface op,
271 PatternRewriter &rewriter)
const override {
272 bool changed =
false;
273 for (
Block *succ : op->getSuccessors())
280LogicalResult BranchOp::canonicalize(BranchOp op,
PatternRewriter &rewriter) {
286void BranchOp::setDest(
Block *block) {
return setSuccessor(block); }
288void BranchOp::eraseOperand(
unsigned index) { (*this)->eraseOperand(
index); }
291 assert(
index == 0 &&
"invalid successor index");
312 LogicalResult matchAndRewrite(CondBranchOp condbr,
313 PatternRewriter &rewriter)
const override {
317 condbr.getTrueOperands());
323 condbr.getFalseOperands());
338struct SimplifyPassThroughCondBranch :
public OpRewritePattern<CondBranchOp> {
339 using OpRewritePattern<CondBranchOp>::OpRewritePattern;
341 LogicalResult matchAndRewrite(CondBranchOp condbr,
342 PatternRewriter &rewriter)
const override {
343 Block *trueDest = condbr.getTrueDest(), *falseDest = condbr.getFalseDest();
344 ValueRange trueDestOperands = condbr.getTrueOperands();
345 ValueRange falseDestOperands = condbr.getFalseOperands();
346 SmallVector<Value, 4> trueDestOperandStorage, falseDestOperandStorage;
349 LogicalResult collapsedTrue =
350 collapseBranch(trueDest, trueDestOperands, trueDestOperandStorage);
351 LogicalResult collapsedFalse =
352 collapseBranch(falseDest, falseDestOperands, falseDestOperandStorage);
358 condbr, condbr.getCondition(), trueDest, trueDestOperands, falseDest,
359 falseDestOperands, condbr.getWeights());
371struct SimplifyCondBranchIdenticalSuccessors
373 using OpRewritePattern<CondBranchOp>::OpRewritePattern;
375 LogicalResult matchAndRewrite(CondBranchOp condbr,
376 PatternRewriter &rewriter)
const override {
379 Block *trueDest = condbr.getTrueDest();
380 if (trueDest != condbr.getFalseDest())
384 OperandRange trueOperands = condbr.getTrueOperands();
385 OperandRange falseOperands = condbr.getFalseOperands();
386 if (trueOperands == falseOperands) {
397 SmallVector<Value, 8> mergedOperands;
398 mergedOperands.reserve(trueOperands.size());
399 Value condition = condbr.getCondition();
400 for (
auto it : llvm::zip(trueOperands, falseOperands)) {
401 if (std::get<0>(it) == std::get<1>(it))
402 mergedOperands.push_back(std::get<0>(it));
404 mergedOperands.push_back(
405 arith::SelectOp::create(rewriter, condbr.getLoc(), condition,
406 std::get<0>(it), std::get<1>(it)));
430struct SimplifyCondBranchFromCondBranchOnSameCondition
432 using OpRewritePattern<CondBranchOp>::OpRewritePattern;
434 LogicalResult matchAndRewrite(CondBranchOp condbr,
435 PatternRewriter &rewriter)
const override {
437 Block *currentBlock = condbr->getBlock();
444 auto predBranch = dyn_cast<CondBranchOp>(predecessor->
getTerminator());
445 if (!predBranch || condbr.getCondition() != predBranch.getCondition())
449 if (currentBlock == predBranch.getTrueDest())
451 condbr.getTrueDestOperands());
454 condbr.getFalseDestOperands());
480 using OpRewritePattern<CondBranchOp>::OpRewritePattern;
482 LogicalResult matchAndRewrite(CondBranchOp condbr,
483 PatternRewriter &rewriter)
const override {
485 bool replaced =
false;
490 Value constantTrue =
nullptr;
491 Value constantFalse =
nullptr;
498 if (condbr.getTrueDest()->getSinglePredecessor()) {
499 for (OpOperand &use :
500 llvm::make_early_inc_range(condbr.getCondition().getUses())) {
501 if (use.getOwner()->getBlock() == condbr.getTrueDest()) {
505 constantTrue = arith::ConstantOp::create(
506 rewriter, condbr.getLoc(), ty, rewriter.
getBoolAttr(
true));
509 [&] { use.set(constantTrue); });
513 if (condbr.getFalseDest()->getSinglePredecessor()) {
514 for (OpOperand &use :
515 llvm::make_early_inc_range(condbr.getCondition().getUses())) {
516 if (use.getOwner()->getBlock() == condbr.getFalseDest()) {
520 constantFalse = arith::ConstantOp::create(
521 rewriter, condbr.getLoc(), ty, rewriter.
getBoolAttr(
false));
524 [&] { use.set(constantFalse); });
533static bool isUnreachableBlock(
Block *block) {
534 return llvm::hasSingleElement(*block) &&
541 using OpRewritePattern<CondBranchOp>::OpRewritePattern;
543 LogicalResult matchAndRewrite(CondBranchOp condbr,
544 PatternRewriter &rewriter)
const override {
547 Block *trueDest = condbr.getTrueDest();
548 Block *falseDest = condbr.getFalseDest();
549 if (isUnreachableBlock(trueDest)) {
551 condbr.getFalseOperands());
557 if (isUnreachableBlock(falseDest)) {
559 condbr.getTrueOperands());
570 results.
add<SimplifyConstCondBranchPred, SimplifyPassThroughCondBranch,
571 SimplifyCondBranchIdenticalSuccessors,
572 SimplifyCondBranchFromCondBranchOnSameCondition,
573 CondBranchTruthPropagation, DropUnreachableCondBranch,
574 SimplifyUniformBlockArguments>(context);
578 assert(
index < getNumSuccessors() &&
"invalid successor index");
580 : getFalseDestOperandsMutable());
584 if (IntegerAttr condAttr =
585 llvm::dyn_cast_or_null<IntegerAttr>(operands.front()))
586 return condAttr.getValue().isOne() ? getTrueDest() : getFalseDest();
599 build(builder,
result, value, defaultOperands, caseOperands, caseValues,
600 defaultDestination, caseDestinations);
608 if (!caseValues.empty()) {
609 ShapedType caseValueType = VectorType::get(
613 build(builder,
result, value, defaultDestination, defaultOperands,
614 caseValuesAttr, caseDestinations, caseOperands);
622 if (!caseValues.empty()) {
623 ShapedType caseValueType = VectorType::get(
627 build(builder,
result, value, defaultDestination, defaultOperands,
628 caseValuesAttr, caseDestinations, caseOperands);
658 if (value.isNegative() ? !value.isSignedIntN(bitWidth)
659 : !value.isIntN(bitWidth))
661 <<
"case value is out of range for " << flagType;
662 values.push_back(value.sextOrTrunc(bitWidth));
677 caseDestinations.push_back(destination);
678 caseOperands.emplace_back(operands);
679 caseOperandTypes.emplace_back(operandTypes);
682 if (!values.empty()) {
683 ShapedType caseValueType =
684 VectorType::get(
static_cast<int64_t>(values.size()), flagType);
701 for (
const auto &it : llvm::enumerate(caseValues.getValues<APInt>())) {
708 caseOperands[it.index()]);
713LogicalResult SwitchOp::verify() {
714 auto caseValues = getCaseValues();
715 auto caseDestinations = getCaseDestinations();
717 if (!caseValues && caseDestinations.empty())
720 Type flagType = getFlag().getType();
721 Type caseValueType = caseValues->getType().getElementType();
722 if (caseValueType != flagType)
723 return emitOpError() <<
"'flag' type (" << flagType
724 <<
") should match case value type (" << caseValueType
728 caseValues->size() !=
static_cast<int64_t>(caseDestinations.size()))
729 return emitOpError() <<
"number of case values (" << caseValues->size()
730 <<
") should match number of "
731 "case destinations ("
732 << caseDestinations.size() <<
")";
737 assert(
index < getNumSuccessors() &&
"invalid successor index");
739 : getCaseOperandsMutable(
index - 1));
743 std::optional<DenseIntElementsAttr> caseValues = getCaseValues();
746 return getDefaultDestination();
749 if (
auto value = llvm::dyn_cast_or_null<IntegerAttr>(operands.front())) {
750 for (
const auto &it : llvm::enumerate(caseValues->getValues<APInt>()))
751 if (it.value() == value.getValue())
752 return caseDests[it.index()];
753 return getDefaultDestination();
764 if (!op.getCaseDestinations().empty())
768 op.getDefaultOperands());
787 bool requiresChange =
false;
788 auto caseValues = op.getCaseValues();
789 auto caseDests = op.getCaseDestinations();
791 for (
const auto &it : llvm::enumerate(caseValues->getValues<APInt>())) {
792 if (caseDests[it.index()] == op.getDefaultDestination() &&
793 op.getCaseOperands(it.index()) == op.getDefaultOperands()) {
794 requiresChange =
true;
797 newCaseDestinations.push_back(caseDests[it.index()]);
798 newCaseOperands.push_back(op.getCaseOperands(it.index()));
799 newCaseValues.push_back(it.value());
806 op, op.getFlag(), op.getDefaultDestination(), op.getDefaultOperands(),
807 newCaseValues, newCaseDestinations, newCaseOperands);
828 std::optional<DenseIntElementsAttr> caseValues = op.getCaseValues();
835 auto caseDests = op.getCaseDestinations();
836 for (
const auto &it : llvm::enumerate(caseValues->getValues<APInt>())) {
837 if (isUnreachableBlock(caseDests[it.index()]))
839 newCaseValues.push_back(it.value());
840 newCaseDestinations.push_back(caseDests[it.index()]);
841 newCaseOperands.push_back(op.getCaseOperands(it.index()));
844 Block *defaultDest = op.getDefaultDestination();
845 ValueRange defaultOperands = op.getDefaultOperands();
846 if (isUnreachableBlock(defaultDest)) {
848 if (newCaseDestinations.empty()) {
854 defaultDest = newCaseDestinations.pop_back_val();
855 defaultOperands = newCaseOperands.pop_back_val();
856 newCaseValues.pop_back();
857 }
else if (newCaseDestinations.size() == caseDests.size()) {
861 if (newCaseDestinations.empty()) {
866 defaultOperands, newCaseValues,
867 newCaseDestinations, newCaseOperands);
879 const APInt &caseValue) {
880 auto caseValues = op.getCaseValues();
881 for (
const auto &it : llvm::enumerate(caseValues->getValues<APInt>())) {
882 if (it.value() == caseValue) {
884 op, op.getCaseDestinations()[it.index()],
885 op.getCaseOperands(it.index()));
890 op.getDefaultOperands());
925 auto caseValues = op.getCaseValues();
926 argStorage.reserve(caseValues->size() + 1);
927 auto caseDests = op.getCaseDestinations();
928 bool requiresChange =
false;
929 for (
int64_t i = 0, size = caseValues->size(); i < size; ++i) {
930 Block *caseDest = caseDests[i];
931 ValueRange caseOperands = op.getCaseOperands(i);
932 argStorage.emplace_back();
933 if (succeeded(
collapseBranch(caseDest, caseOperands, argStorage.back())))
934 requiresChange =
true;
936 newCaseDests.push_back(caseDest);
937 newCaseOperands.push_back(caseOperands);
940 Block *defaultDest = op.getDefaultDestination();
941 ValueRange defaultOperands = op.getDefaultOperands();
942 argStorage.emplace_back();
946 requiresChange =
true;
952 defaultOperands, *caseValues,
953 newCaseDests, newCaseOperands);
996 Block *currentBlock = op->getBlock();
1004 auto predSwitch = dyn_cast<SwitchOp>(predecessor->
getTerminator());
1005 if (!predSwitch || op.getFlag() != predSwitch.getFlag() ||
1006 predSwitch.getDefaultDestination() == currentBlock)
1011 auto it = llvm::find(predDests, currentBlock);
1012 if (it != predDests.end()) {
1013 std::optional<DenseIntElementsAttr> predCaseValues =
1014 predSwitch.getCaseValues();
1016 predCaseValues->getValues<APInt>()[it - predDests.begin()]);
1019 op.getDefaultOperands());
1048 Block *currentBlock = op->getBlock();
1056 auto predSwitch = dyn_cast<SwitchOp>(predecessor->
getTerminator());
1057 if (!predSwitch || op.getFlag() != predSwitch.getFlag() ||
1058 predSwitch.getDefaultDestination() != currentBlock)
1063 auto predDests = predSwitch.getCaseDestinations();
1064 auto predCaseValues = predSwitch.getCaseValues();
1065 for (
int64_t i = 0, size = predCaseValues->size(); i < size; ++i)
1066 if (currentBlock != predDests[i])
1067 caseValuesToRemove.insert(predCaseValues->getValues<APInt>()[i]);
1072 bool requiresChange =
false;
1074 auto caseValues = op.getCaseValues();
1075 auto caseDests = op.getCaseDestinations();
1076 for (
const auto &it : llvm::enumerate(caseValues->getValues<APInt>())) {
1077 if (caseValuesToRemove.contains(it.value())) {
1078 requiresChange =
true;
1081 newCaseDestinations.push_back(caseDests[it.index()]);
1082 newCaseOperands.push_back(op.getCaseOperands(it.index()));
1083 newCaseValues.push_back(it.value());
1086 if (!requiresChange)
1090 op, op.getFlag(), op.getDefaultDestination(), op.getDefaultOperands(),
1091 newCaseValues, newCaseDestinations, newCaseOperands);
1104 .
add<SimplifyUniformBlockArguments>(context);
1111#define GET_OP_CLASSES
1112#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.cpp.inc"
static LogicalResult simplifyUniformBlockArgs(Block *dest, PatternRewriter &rewriter)
If all incoming values for a block argument from all predecessors are the same SSA value,...
static LogicalResult simplifySwitchFromSwitchOnSameCondition(SwitchOp op, PatternRewriter &rewriter)
switch flag : i32, [ default: ^bb1, 42: ^bb2, ] ^bb2: switch flag : i32, [ default: ^bb3,...
static LogicalResult simplifyConstSwitchValue(SwitchOp op, PatternRewriter &rewriter)
switch c_42 : i32, [ default: ^bb1, 42: ^bb2, 43: ^bb3 ] -> br ^bb2
static LogicalResult simplifySwitchWithOnlyDefault(SwitchOp op, PatternRewriter &rewriter)
switch flag : i32, [ default: ^bb1 ] -> br ^bb1
static void foldSwitch(SwitchOp op, PatternRewriter &rewriter, const APInt &caseValue)
Helper for folding a switch with a constant value.
static void printSwitchOpCases(OpAsmPrinter &p, SwitchOp op, Type flagType, Block *defaultDestination, OperandRange defaultOperands, TypeRange defaultOperandTypes, DenseIntElementsAttr caseValues, SuccessorRange caseDestinations, OperandRangeRange caseOperands, const TypeRangeRange &caseOperandTypes)
static LogicalResult simplifyPassThroughBr(BranchOp op, PatternRewriter &rewriter)
br ^bb1 ^bb1 br ^bbN(...)
static LogicalResult simplifyBrToBlockWithSinglePred(BranchOp op, PatternRewriter &rewriter)
Simplify a branch to a block that has a single predecessor.
static LogicalResult collapseBranch(Block *&successor, ValueRange &successorOperands, SmallVectorImpl< Value > &argStorage)
Given a successor, try to collapse it to a new destination if it only contains a passthrough uncondit...
static LogicalResult dropSwitchCasesThatMatchDefault(SwitchOp op, PatternRewriter &rewriter)
switch flag : i32, [ default: ^bb1, 42: ^bb1, 43: ^bb2 ] -> switch flag : i32, [ default: ^bb1,...
static LogicalResult dropUnreachableSwitchCases(SwitchOp op, PatternRewriter &rewriter)
Drop cases whose destination block contains only ub.unreachable.
static LogicalResult simplifyPassThroughSwitch(SwitchOp op, PatternRewriter &rewriter)
switch c_42 : i32, [ default: ^bb1, 42: ^bb2, ] ^bb2: br ^bb3 -> switch c_42 : i32,...
static LogicalResult simplifySwitchFromDefaultSwitchOnSameCondition(SwitchOp op, PatternRewriter &rewriter)
switch flag : i32, [ default: ^bb1, 42: ^bb2 ] ^bb1: switch flag : i32, [ default: ^bb3,...
static ParseResult parseSwitchOpCases(OpAsmParser &parser, Type &flagType, Block *&defaultDestination, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &defaultOperands, SmallVectorImpl< Type > &defaultOperandTypes, DenseIntElementsAttr &caseValues, SmallVectorImpl< Block * > &caseDestinations, SmallVectorImpl< SmallVector< OpAsmParser::UnresolvedOperand > > &caseOperands, SmallVectorImpl< SmallVector< Type > > &caseOperandTypes)
<cases> ::= default : bb-id (( ssa-use-and-type-list ))?
static bool isLegalToInline(InlinerInterface &interface, Region *src, Region *insertRegion, bool shouldCloneInlinedRegion, IRMapping &valueMapping)
Utility to check that all of the operations within 'src' can be inlined.
@ None
Zero or more operands with no delimiters.
virtual ParseResult parseColonTypeList(SmallVectorImpl< Type > &result)=0
Parse a colon followed by a type list, which must have at least one type.
virtual ParseResult parseRParen()=0
Parse a ) token.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
ParseResult parseInteger(IntT &result)
Parse an integer value from the stream.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseOptionalComma()=0
Parse a , token if present.
virtual ParseResult parseColon()=0
Parse a : token.
virtual ParseResult parseOptionalLParen()=0
Parse a ( token if present.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
virtual void printNewline()
Print a newline and indent the printer to the start of the current operation/attribute/type.
This class represents an argument of a Block.
unsigned getArgNumber() const
Returns the number of this argument.
Block * getOwner() const
Returns the block that owns this argument.
This class provides an abstraction over the different types of ranges over Blocks.
Block represents an ordered list of Operations.
iterator_range< pred_iterator > getPredecessors()
Block * getSinglePredecessor()
If this block has exactly one predecessor, return it.
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgListType getArguments()
Block * getUniquePredecessor()
If this block has a unique predecessor, i.e., all incoming edges originate from one block,...
bool hasNoPredecessors()
Return true if this block has no predecessors.
BoolAttr getBoolAttr(bool value)
An attribute that represents a reference to a dense integer vector or tensor object.
static DenseIntElementsAttr get(const ShapedType &type, Arg &&arg)
Get an instance of a DenseIntElementsAttr with the given arguments.
MLIRContext is the top-level object for a collection of MLIR operations.
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult parseSuccessor(Block *&dest)=0
Parse a single operation successor.
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printSuccessorAndUseList(Block *successor, ValueRange succOperands)=0
Print the successor and its operands.
This class helps build Operations.
This class represents a contiguous range of operand ranges, e.g.
This class implements the operand iterators for the Operation class.
Operation is the basic unit of execution within MLIR.
user_range getUsers()
Returns a range of all users.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void mergeBlocks(Block *source, Block *dest, ValueRange argValues={})
Inline the operations of block 'source' into the end of block 'dest'.
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class represents a specific instance of an effect.
static DerivedEffect * get()
This class models how operands are forwarded to block arguments in control flow.
This class implements the successor iterators for Block.
This class provides an abstraction for a range of TypeRange.
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
detail::constant_int_predicate_matcher m_Zero()
Matches a constant scalar / vector splat / tensor splat integer zero.
detail::constant_int_predicate_matcher m_One()
Matches a constant scalar / vector splat / tensor splat integer one.
detail::constant_int_predicate_matcher m_NonZero()
Matches a constant scalar / vector splat / tensor splat integer that is any non-zero value.
OpInterfaceRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting a...
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
This represents an operation in an abstracted form, suitable for use with the builder APIs.