11#include "llvm/ADT/TypeSwitch.h"
32#define GEN_PASS_DEF_ARITHINTRANGEOPTS
33#include "mlir/Dialect/Arith/Transforms/Passes.h.inc"
35#define GEN_PASS_DEF_ARITHINTRANGENARROWING
36#include "mlir/Dialect/Arith/Transforms/Passes.h.inc"
45 auto *maybeInferredRange =
47 if (!maybeInferredRange || maybeInferredRange->getValue().isUninitialized())
50 maybeInferredRange->getValue().getValue();
70 if (!maybeConstValue.has_value())
77 maybeDefiningOp ? maybeDefiningOp->
getDialect()
81 if (
auto shaped = dyn_cast<ShapedType>(type)) {
112 void notifyOperationErased(Operation *op)
override {
126 MaterializeKnownConstantValues(MLIRContext *context, DataFlowSolver &s)
127 : RewritePattern::RewritePattern(Pattern::MatchAnyOpTypeTag(),
131 LogicalResult matchAndRewrite(Operation *op,
132 PatternRewriter &rewriter)
const override {
136 auto needsReplacing = [&](Value v) {
139 bool hasConstantResults = llvm::any_of(op->
getResults(), needsReplacing);
141 if (!hasConstantResults)
143 bool hasConstantRegionArgs =
false;
145 for (
Block &block : region.getBlocks()) {
146 hasConstantRegionArgs |=
147 llvm::any_of(block.getArguments(), needsReplacing);
150 if (!hasConstantResults && !hasConstantRegionArgs)
163 PatternRewriter::InsertionGuard guard(rewriter);
165 for (
Block &block : region.getBlocks()) {
167 for (BlockArgument &arg : block.getArguments()) {
177 DataFlowSolver &solver;
180template <
typename RemOp>
182 DeleteTrivialRem(MLIRContext *context, DataFlowSolver &s)
183 : OpRewritePattern<RemOp>(context), solver(s) {}
185 LogicalResult matchAndRewrite(RemOp op,
186 PatternRewriter &rewriter)
const override {
187 Value
lhs = op.getOperand(0);
188 Value
rhs = op.getOperand(1);
190 if (!maybeModulus.has_value())
192 int64_t modulus = *maybeModulus;
195 auto *maybeLhsRange = solver.lookupState<IntegerValueRangeLattice>(
lhs);
196 if (!maybeLhsRange || maybeLhsRange->getValue().isUninitialized())
198 const ConstantIntRanges &lhsRange = maybeLhsRange->getValue().getValue();
199 const APInt &
min = isa<RemUIOp>(op) ? lhsRange.
umin() : lhsRange.
smin();
200 const APInt &
max = isa<RemUIOp>(op) ? lhsRange.
umax() : lhsRange.
smax();
203 if (
min.isNegative() ||
min.uge(modulus))
205 if (
max.isNegative() ||
max.uge(modulus))
218 DataFlowSolver &solver;
225 for (
Value val : values) {
226 auto *maybeInferredRange =
228 if (!maybeInferredRange || maybeInferredRange->getValue().isUninitialized())
232 maybeInferredRange->getValue().getValue();
233 ranges.push_back(inferredRange);
240static Type getTargetType(
Type srcType,
unsigned targetBitwidth) {
241 auto dstType = IntegerType::get(srcType.
getContext(), targetBitwidth);
242 if (
auto shaped = dyn_cast<ShapedType>(srcType))
243 return shaped.clone(dstType);
262 unsigned targetWidth) {
263 unsigned srcWidth = range.
smin().getBitWidth();
264 if (srcWidth <= targetWidth)
265 return CastKind::None;
266 unsigned removedWidth = srcWidth - targetWidth;
270 bool canTruncateSigned =
271 range.
smin().getNumSignBits() >= (removedWidth + 1) &&
272 range.
smax().getNumSignBits() >= (removedWidth + 1);
273 bool canTruncateUnsigned = range.
umin().countLeadingZeros() >= removedWidth &&
274 range.
umax().countLeadingZeros() >= removedWidth;
275 if (canTruncateSigned && canTruncateUnsigned)
276 return CastKind::Both;
277 if (canTruncateSigned)
278 return CastKind::Signed;
279 if (canTruncateUnsigned)
280 return CastKind::Unsigned;
281 return CastKind::None;
284static CastKind mergeCastKinds(CastKind
lhs, CastKind
rhs) {
285 if (
lhs == CastKind::None ||
rhs == CastKind::None)
286 return CastKind::None;
287 if (
lhs == CastKind::Both)
289 if (
rhs == CastKind::Both)
293 return CastKind::None;
299 assert(isa<VectorType>(srcType) == isa<VectorType>(dstType) &&
300 "Mixing vector and non-vector types");
301 assert(castKind != CastKind::None &&
"Can't cast when casting isn't allowed");
304 assert(srcElemType.
isIntOrIndex() &&
"Invalid src type");
305 assert(dstElemType.
isIntOrIndex() &&
"Invalid dst type");
306 if (srcType == dstType)
309 if (isa<IndexType>(srcElemType) || isa<IndexType>(dstElemType)) {
310 if (castKind == CastKind::Signed)
311 return arith::IndexCastOp::create(builder, loc, dstType, src);
312 return arith::IndexCastUIOp::create(builder, loc, dstType, src);
315 auto srcInt = cast<IntegerType>(srcElemType);
316 auto dstInt = cast<IntegerType>(dstElemType);
317 if (dstInt.getWidth() < srcInt.getWidth())
318 return arith::TruncIOp::create(builder, loc, dstType, src);
320 if (castKind == CastKind::Signed)
321 return arith::ExtSIOp::create(builder, loc, dstType, src);
322 return arith::ExtUIOp::create(builder, loc, dstType, src);
326 NarrowElementwise(MLIRContext *context, DataFlowSolver &s,
327 ArrayRef<unsigned>
target)
328 : OpTraitRewritePattern(context), solver(s), targetBitwidths(
target) {}
331 LogicalResult matchAndRewrite(Operation *op,
332 PatternRewriter &rewriter)
const override {
338 SmallVector<ConstantIntRanges, 4> ranges;
349 [=](Type t) { return t == srcType; }))
351 op,
"no operands or operand types don't match result type");
353 for (
unsigned targetBitwidth : targetBitwidths) {
354 CastKind castKind = CastKind::Both;
355 for (
const ConstantIntRanges &range : ranges) {
356 castKind = mergeCastKinds(castKind,
357 checkTruncatability(range, targetBitwidth));
358 if (castKind == CastKind::None)
365 llvm::TypeSwitch<Operation *, CastKind>(op)
366 .Case<arith::DivSIOp, arith::CeilDivSIOp, arith::FloorDivSIOp,
367 arith::RemSIOp, arith::MaxSIOp, arith::MinSIOp,
368 arith::ShRSIOp>([](
auto) {
return CastKind::Signed; })
369 .Default(CastKind::Both);
370 castKind = mergeCastKinds(castKind, castKindForOp);
371 if (castKind == CastKind::None)
373 Type targetType = getTargetType(srcType, targetBitwidth);
374 if (targetType == srcType)
377 Location loc = op->
getLoc();
379 for (
auto [arg, argRange] : llvm::zip_first(op->
getOperands(), ranges)) {
380 CastKind argCastKind = castKind;
383 if (argCastKind == CastKind::Signed && argRange.smin().isNonNegative())
384 argCastKind = CastKind::Both;
385 Value newArg = doCast(rewriter, loc, arg, targetType, argCastKind);
386 mapping.
map(arg, newArg);
389 Operation *newOp = rewriter.
clone(*op, mapping);
392 res.setType(targetType);
395 SmallVector<Value> newResults;
396 for (
auto [newRes, oldRes] :
398 Value castBack = doCast(rewriter, loc, newRes, srcType, castKind);
400 newResults.push_back(castBack);
410 DataFlowSolver &solver;
411 SmallVector<unsigned, 4> targetBitwidths;
415 NarrowCmpI(MLIRContext *context, DataFlowSolver &s, ArrayRef<unsigned>
target)
416 : OpRewritePattern(context), solver(s), targetBitwidths(
target) {}
418 LogicalResult matchAndRewrite(arith::CmpIOp op,
419 PatternRewriter &rewriter)
const override {
420 Value
lhs = op.getLhs();
421 Value
rhs = op.getRhs();
423 SmallVector<ConstantIntRanges> ranges;
424 if (
failed(collectRanges(solver, op.getOperands(), ranges)))
426 const ConstantIntRanges &lhsRange = ranges[0];
427 const ConstantIntRanges &rhsRange = ranges[1];
429 auto isSignedCmpPredicate = [](arith::CmpIPredicate pred) ->
bool {
430 return pred == arith::CmpIPredicate::sge ||
431 pred == arith::CmpIPredicate::sgt ||
432 pred == arith::CmpIPredicate::sle ||
433 pred == arith::CmpIPredicate::slt;
437 CastKind predicateBasedCastRestriction =
438 isSignedCmpPredicate(op.getPredicate()) ? CastKind::Signed
441 Type srcType =
lhs.getType();
442 for (
unsigned targetBitwidth : targetBitwidths) {
443 CastKind lhsCastKind = checkTruncatability(lhsRange, targetBitwidth);
444 CastKind rhsCastKind = checkTruncatability(rhsRange, targetBitwidth);
445 CastKind castKind = mergeCastKinds(lhsCastKind, rhsCastKind);
446 castKind = mergeCastKinds(castKind, predicateBasedCastRestriction);
449 if (castKind == CastKind::None)
452 Type targetType = getTargetType(srcType, targetBitwidth);
453 if (targetType == srcType)
456 Location loc = op->getLoc();
458 Value lhsCast = doCast(rewriter, loc,
lhs, targetType, lhsCastKind);
459 Value rhsCast = doCast(rewriter, loc,
rhs, targetType, rhsCastKind);
460 mapping.
map(
lhs, lhsCast);
461 mapping.
map(
rhs, rhsCast);
463 Operation *newOp = rewriter.
clone(*op, mapping);
472 DataFlowSolver &solver;
473 SmallVector<unsigned, 4> targetBitwidths;
479template <
typename CastOp>
481 FoldIndexCastChain(MLIRContext *context, ArrayRef<unsigned>
target)
482 : OpRewritePattern<CastOp>(context), targetBitwidths(
target) {}
484 LogicalResult matchAndRewrite(CastOp op,
485 PatternRewriter &rewriter)
const override {
486 auto srcOp = op.getIn().template getDefiningOp<CastOp>();
490 Value src = srcOp.getIn();
491 if (src.
getType() != op.getType())
494 if (!srcOp.getType().isIndex())
497 auto intType = dyn_cast<IntegerType>(op.getType());
498 if (!intType || !llvm::is_contained(targetBitwidths, intType.getWidth()))
506 SmallVector<unsigned, 4> targetBitwidths;
510 NarrowLoopBounds(MLIRContext *context, DataFlowSolver &s,
511 ArrayRef<unsigned>
target)
512 : OpInterfaceRewritePattern<LoopLikeOpInterface>(context), solver(s),
514 boundsNarrowingFailedAttr(
515 StringAttr::
get(context,
"arith.bounds_narrowing_failed")) {}
517 LogicalResult matchAndRewrite(LoopLikeOpInterface loopLike,
518 PatternRewriter &rewriter)
const override {
520 if (loopLike->hasAttr(boundsNarrowingFailedAttr))
522 "bounds narrowing previously failed");
524 std::optional<SmallVector<Value>> inductionVars =
525 loopLike.getLoopInductionVars();
526 if (!inductionVars.has_value() || inductionVars->empty())
529 std::optional<SmallVector<OpFoldResult>> lowerBounds =
530 loopLike.getLoopLowerBounds();
531 std::optional<SmallVector<OpFoldResult>> upperBounds =
532 loopLike.getLoopUpperBounds();
533 std::optional<SmallVector<OpFoldResult>> steps = loopLike.getLoopSteps();
535 if (!lowerBounds.has_value() || !upperBounds.has_value() ||
539 if (lowerBounds->size() != inductionVars->size() ||
540 upperBounds->size() != inductionVars->size() ||
541 steps->size() != inductionVars->size())
543 "mismatched bounds/steps count");
545 Location loc = loopLike->getLoc();
546 SmallVector<OpFoldResult> newLowerBounds(*lowerBounds);
547 SmallVector<OpFoldResult> newUpperBounds(*upperBounds);
548 SmallVector<OpFoldResult> newSteps(*steps);
549 SmallVector<std::tuple<size_t, Type, CastKind>> narrowings;
552 for (
auto [idx, indVar, lbOFR, ubOFR, stepOFR] :
553 llvm::enumerate(*inductionVars, *lowerBounds, *upperBounds, *steps)) {
556 auto maybeLb = dyn_cast<Value>(lbOFR);
557 auto maybeUb = dyn_cast<Value>(ubOFR);
558 auto maybeStep = dyn_cast<Value>(stepOFR);
560 if (!maybeLb || !maybeUb || !maybeStep)
564 SmallVector<ConstantIntRanges> ranges;
566 solver,
ValueRange{maybeLb, maybeUb, maybeStep, indVar}, ranges)))
569 const ConstantIntRanges &stepRange = ranges[2];
570 const ConstantIntRanges &indVarRange = ranges[3];
572 Type srcType = maybeLb.getType();
575 for (
unsigned targetBitwidth : targetBitwidths) {
576 Type targetType = getTargetType(srcType, targetBitwidth);
577 if (targetType == srcType)
582 if (!loopLike.isValidInductionVarType(targetType))
586 CastKind castKind = CastKind::Both;
587 for (
const ConstantIntRanges &range : ranges) {
588 castKind = mergeCastKinds(castKind,
589 checkTruncatability(range, targetBitwidth));
590 if (castKind == CastKind::None)
594 if (castKind == CastKind::None)
604 ConstantIntRanges indVarPlusStepRange(
605 indVarRange.
smin().sadd_sat(stepRange.
smin()),
606 indVarRange.
smax().sadd_sat(stepRange.
smax()),
607 indVarRange.
umin().uadd_sat(stepRange.
umin()),
608 indVarRange.
umax().uadd_sat(stepRange.
umax()));
610 if (checkTruncatability(indVarPlusStepRange, targetBitwidth) !=
615 Value newLb = doCast(rewriter, loc, maybeLb, targetType, castKind);
616 Value newUb = doCast(rewriter, loc, maybeUb, targetType, castKind);
617 Value newStep = doCast(rewriter, loc, maybeStep, targetType, castKind);
619 newLowerBounds[idx] = newLb;
620 newUpperBounds[idx] = newUb;
621 newSteps[idx] = newStep;
622 narrowings.push_back({idx, targetType, castKind});
627 if (narrowings.empty())
631 SmallVector<Type> origTypes;
632 for (
auto [idx, targetType, castKind] : narrowings) {
633 Value indVar = (*inductionVars)[idx];
634 origTypes.push_back(indVar.
getType());
639 bool updateFailed =
false;
642 if (
failed(loopLike.setLoopLowerBounds(newLowerBounds)) ||
643 failed(loopLike.setLoopUpperBounds(newUpperBounds)) ||
644 failed(loopLike.setLoopSteps(newSteps))) {
647 loopLike->setAttr(boundsNarrowingFailedAttr, rewriter.
getUnitAttr());
653 for (
auto [idx, targetType, castKind] : narrowings) {
654 Value indVar = (*inductionVars)[idx];
655 auto blockArg = cast<BlockArgument>(indVar);
658 blockArg.setType(targetType);
666 for (
auto [narrowingIdx, narrowingInfo] : llvm::enumerate(narrowings)) {
667 auto [idx, targetType, castKind] = narrowingInfo;
668 Value indVar = (*inductionVars)[idx];
669 auto blockArg = cast<BlockArgument>(indVar);
670 Type origType = origTypes[narrowingIdx];
672 OpBuilder::InsertionGuard guard(rewriter);
674 Value casted = doCast(rewriter, loc, blockArg, origType, castKind);
685 DataFlowSolver &solver;
686 SmallVector<unsigned, 4> targetBitwidths;
687 StringAttr boundsNarrowingFailedAttr;
690struct IntRangeOptimizationsPass final
691 : arith::impl::ArithIntRangeOptsBase<IntRangeOptimizationsPass> {
693 void runOnOperation()
override {
694 Operation *op = getOperation();
696 DataFlowSolver solver;
698 solver.
load<IntegerRangeAnalysis>();
700 return signalPassFailure();
702 DataFlowListener listener(solver);
704 RewritePatternSet patterns(ctx);
715 GreedyRewriteConfig()
716 .enableFolding(
false)
717 .setRegionSimplificationLevel(
718 GreedySimplifyRegionLevel::Disabled)
719 .setListener(&listener))))
724struct IntRangeNarrowingPass final
725 : arith::impl::ArithIntRangeNarrowingBase<IntRangeNarrowingPass> {
726 using ArithIntRangeNarrowingBase::ArithIntRangeNarrowingBase;
728 void runOnOperation()
override {
729 Operation *op = getOperation();
731 DataFlowSolver solver;
733 solver.
load<IntegerRangeAnalysis>();
735 return signalPassFailure();
737 DataFlowListener listener(solver);
739 RewritePatternSet patterns(ctx);
747 op, std::move(patterns),
748 GreedyRewriteConfig().setUseTopDownTraversal(
false).setListener(
757 patterns.
add<MaterializeKnownConstantValues, DeleteTrivialRem<RemSIOp>,
758 DeleteTrivialRem<RemUIOp>>(patterns.
getContext(), solver);
764 patterns.
add<NarrowElementwise, NarrowCmpI>(patterns.
getContext(), solver,
766 patterns.
add<FoldIndexCastChain<arith::IndexCastUIOp>,
767 FoldIndexCastChain<arith::IndexCastOp>>(patterns.
getContext(),
774 patterns.
add<NarrowLoopBounds>(patterns.
getContext(), solver,
779 return std::make_unique<IntRangeOptimizationsPass>();
static Operation * materializeConstant(Dialect *dialect, OpBuilder &builder, Attribute value, Type type, Location loc)
A utility function used to materialize a constant for a given attribute and type.
static void copyIntegerRange(DataFlowSolver &solver, Value oldVal, Value newVal)
static std::optional< APInt > getMaybeConstantValue(DataFlowSolver &solver, Value value)
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
Attributes are known-constant values of operations.
IntegerAttr getIntegerAttr(Type type, int64_t value)
MLIRContext * getContext() const
A set of arbitrary-precision integers representing bounds on a given integer value.
const APInt & smax() const
The maximum value of an integer when it is interpreted as signed.
const APInt & smin() const
The minimum value of an integer when it is interpreted as signed.
std::optional< APInt > getConstantValue() const
If either the signed or unsigned interpretations of the range indicate that the value it bounds is a ...
const APInt & umax() const
The maximum value of an integer when it is interpreted as unsigned.
const APInt & umin() const
The minimum value of an integer when it is interpreted as unsigned.
The general data-flow analysis solver.
void eraseState(AnchorT anchor)
Erase any analysis state associated with the given lattice anchor.
const StateT * lookupState(AnchorT anchor) const
Lookup an analysis state for the given lattice anchor.
StateT * getOrCreateState(AnchorT anchor)
Get the state associated with the given lattice anchor.
AnalysisT * load(Args &&...args)
Load an analysis into the solver. Return the analysis instance.
LogicalResult initializeAndRun(Operation *top)
Initialize the children analyses starting from the provided top-level operation and run the analysis ...
static DenseIntElementsAttr get(const ShapedType &type, Arg &&arg)
Get an instance of a DenseIntElementsAttr with the given arguments.
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
virtual Operation * materializeConstant(OpBuilder &builder, Attribute value, Type type, Location loc)
Registered hook to materialize a single constant operation from a given attribute value with the desi...
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Dialect * getLoadedDialect(StringRef name)
Get a registered IR dialect with the given namespace.
This class helps build Operations.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
This is a value defined by a result of an operation.
OpTraitRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting again...
OpTraitRewritePattern(MLIRContext *context, PatternBenefit benefit=1)
Operation is the basic unit of execution within MLIR.
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
unsigned getNumRegions()
Returns the number of regions held by this operation.
Location getLoc()
The source location the operation was defined or derived from.
unsigned getNumOperands()
operand_type_range getOperandTypes()
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
result_range getResults()
MLIRContext * getContext()
Return the context this operation is associated with.
unsigned getNumResults()
Return the number of results held by this operation.
Operation * getParentOp()
Return the parent operation this region is attached to.
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
RewritePattern is the common base class for all DAG to DAG replacements.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void replaceAllUsesExcept(Value from, Value to, Operation *exceptedUser)
Find uses of from and replace them with to except if the user is exceptedUser.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
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.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
bool isIntOrIndex() const
Return true if this is an integer (of any signedness) or an index type.
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...
bool use_empty() const
Returns true if this value has no uses.
Type getType() const
Return the type of this value.
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Region * getParentRegion()
Return the Region in which this Value is defined.
This lattice element represents the integer value range of an SSA value.
ChangeResult join(const AbstractSparseLattice &rhs) override
Join the information contained in the 'rhs' lattice into this lattice.
std::unique_ptr< Pass > createIntRangeOptimizationsPass()
Create a pass which do optimizations based on integer range analysis.
void populateControlFlowValuesNarrowingPatterns(RewritePatternSet &patterns, DataFlowSolver &solver, ArrayRef< unsigned > bitwidthsSupported)
Add patterns for narrowing control flow values (loop bounds, steps, etc.) based on int range analysis...
void populateIntRangeOptimizationsPatterns(RewritePatternSet &patterns, DataFlowSolver &solver)
Add patterns for int range based optimizations.
void populateIntRangeNarrowingPatterns(RewritePatternSet &patterns, DataFlowSolver &solver, ArrayRef< unsigned > bitwidthsSupported)
Add patterns for int range based narrowing.
LogicalResult maybeReplaceWithConstant(DataFlowSolver &solver, RewriterBase &rewriter, Value value)
Patterned after SCCP.
void loadBaselineAnalyses(DataFlowSolver &solver)
Populates a DataFlowSolver with analyses that are required to ensure user-defined analyses are run pr...
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
LogicalResult applyPatternsGreedily(Region ®ion, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
bool isOpTriviallyDead(Operation *op)
Return true if the given operation is unused, and has no side effects on memory that prevent erasing.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
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...