MLIR  20.0.0git
IntRangeOptimizations.cpp
Go to the documentation of this file.
1 //===- IntRangeOptimizations.cpp - Optimizations based on integer ranges --===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <utility>
10 
13 
18 #include "mlir/IR/IRMapping.h"
19 #include "mlir/IR/Matchers.h"
20 #include "mlir/IR/PatternMatch.h"
21 #include "mlir/IR/TypeUtilities.h"
25 
26 namespace mlir::arith {
27 #define GEN_PASS_DEF_ARITHINTRANGEOPTS
28 #include "mlir/Dialect/Arith/Transforms/Passes.h.inc"
29 
30 #define GEN_PASS_DEF_ARITHINTRANGENARROWING
31 #include "mlir/Dialect/Arith/Transforms/Passes.h.inc"
32 } // namespace mlir::arith
33 
34 using namespace mlir;
35 using namespace mlir::arith;
36 using namespace mlir::dataflow;
37 
38 static std::optional<APInt> getMaybeConstantValue(DataFlowSolver &solver,
39  Value value) {
40  auto *maybeInferredRange =
42  if (!maybeInferredRange || maybeInferredRange->getValue().isUninitialized())
43  return std::nullopt;
44  const ConstantIntRanges &inferredRange =
45  maybeInferredRange->getValue().getValue();
46  return inferredRange.getConstantValue();
47 }
48 
49 /// Patterned after SCCP
50 static LogicalResult maybeReplaceWithConstant(DataFlowSolver &solver,
51  PatternRewriter &rewriter,
52  Value value) {
53  if (value.use_empty())
54  return failure();
55  std::optional<APInt> maybeConstValue = getMaybeConstantValue(solver, value);
56  if (!maybeConstValue.has_value())
57  return failure();
58 
59  Type type = value.getType();
60  Location loc = value.getLoc();
61  Operation *maybeDefiningOp = value.getDefiningOp();
62  Dialect *valueDialect =
63  maybeDefiningOp ? maybeDefiningOp->getDialect()
64  : value.getParentRegion()->getParentOp()->getDialect();
65 
66  Attribute constAttr;
67  if (auto shaped = dyn_cast<ShapedType>(type)) {
68  constAttr = mlir::DenseIntElementsAttr::get(shaped, *maybeConstValue);
69  } else {
70  constAttr = rewriter.getIntegerAttr(type, *maybeConstValue);
71  }
72  Operation *constOp =
73  valueDialect->materializeConstant(rewriter, constAttr, type, loc);
74  // Fall back to arith.constant if the dialect materializer doesn't know what
75  // to do with an integer constant.
76  if (!constOp)
77  constOp = rewriter.getContext()
78  ->getLoadedDialect<ArithDialect>()
79  ->materializeConstant(rewriter, constAttr, type, loc);
80  if (!constOp)
81  return failure();
82 
83  rewriter.replaceAllUsesWith(value, constOp->getResult(0));
84  return success();
85 }
86 
87 namespace {
88 class DataFlowListener : public RewriterBase::Listener {
89 public:
90  DataFlowListener(DataFlowSolver &s) : s(s) {}
91 
92 protected:
93  void notifyOperationErased(Operation *op) override {
94  s.eraseState(s.getProgramPointAfter(op));
95  for (Value res : op->getResults())
96  s.eraseState(res);
97  }
98 
99  DataFlowSolver &s;
100 };
101 
102 /// Rewrite any results of `op` that were inferred to be constant integers to
103 /// and replace their uses with that constant. Return success() if all results
104 /// where thus replaced and the operation is erased. Also replace any block
105 /// arguments with their constant values.
106 struct MaterializeKnownConstantValues : public RewritePattern {
107  MaterializeKnownConstantValues(MLIRContext *context, DataFlowSolver &s)
108  : RewritePattern(Pattern::MatchAnyOpTypeTag(), /*benefit=*/1, context),
109  solver(s) {}
110 
111  LogicalResult match(Operation *op) const override {
112  if (matchPattern(op, m_Constant()))
113  return failure();
114 
115  auto needsReplacing = [&](Value v) {
116  return getMaybeConstantValue(solver, v).has_value() && !v.use_empty();
117  };
118  bool hasConstantResults = llvm::any_of(op->getResults(), needsReplacing);
119  if (op->getNumRegions() == 0)
120  return success(hasConstantResults);
121  bool hasConstantRegionArgs = false;
122  for (Region &region : op->getRegions()) {
123  for (Block &block : region.getBlocks()) {
124  hasConstantRegionArgs |=
125  llvm::any_of(block.getArguments(), needsReplacing);
126  }
127  }
128  return success(hasConstantResults || hasConstantRegionArgs);
129  }
130 
131  void rewrite(Operation *op, PatternRewriter &rewriter) const override {
132  bool replacedAll = (op->getNumResults() != 0);
133  for (Value v : op->getResults())
134  replacedAll &=
135  (succeeded(maybeReplaceWithConstant(solver, rewriter, v)) ||
136  v.use_empty());
137  if (replacedAll && isOpTriviallyDead(op)) {
138  rewriter.eraseOp(op);
139  return;
140  }
141 
142  PatternRewriter::InsertionGuard guard(rewriter);
143  for (Region &region : op->getRegions()) {
144  for (Block &block : region.getBlocks()) {
145  rewriter.setInsertionPointToStart(&block);
146  for (BlockArgument &arg : block.getArguments()) {
147  (void)maybeReplaceWithConstant(solver, rewriter, arg);
148  }
149  }
150  }
151  }
152 
153 private:
154  DataFlowSolver &solver;
155 };
156 
157 template <typename RemOp>
158 struct DeleteTrivialRem : public OpRewritePattern<RemOp> {
159  DeleteTrivialRem(MLIRContext *context, DataFlowSolver &s)
160  : OpRewritePattern<RemOp>(context), solver(s) {}
161 
162  LogicalResult matchAndRewrite(RemOp op,
163  PatternRewriter &rewriter) const override {
164  Value lhs = op.getOperand(0);
165  Value rhs = op.getOperand(1);
166  auto maybeModulus = getConstantIntValue(rhs);
167  if (!maybeModulus.has_value())
168  return failure();
169  int64_t modulus = *maybeModulus;
170  if (modulus <= 0)
171  return failure();
172  auto *maybeLhsRange = solver.lookupState<IntegerValueRangeLattice>(lhs);
173  if (!maybeLhsRange || maybeLhsRange->getValue().isUninitialized())
174  return failure();
175  const ConstantIntRanges &lhsRange = maybeLhsRange->getValue().getValue();
176  const APInt &min = isa<RemUIOp>(op) ? lhsRange.umin() : lhsRange.smin();
177  const APInt &max = isa<RemUIOp>(op) ? lhsRange.umax() : lhsRange.smax();
178  // The minima and maxima here are given as closed ranges, we must be
179  // strictly less than the modulus.
180  if (min.isNegative() || min.uge(modulus))
181  return failure();
182  if (max.isNegative() || max.uge(modulus))
183  return failure();
184  if (!min.ule(max))
185  return failure();
186 
187  // With all those conditions out of the way, we know thas this invocation of
188  // a remainder is a noop because the input is strictly within the range
189  // [0, modulus), so get rid of it.
190  rewriter.replaceOp(op, ValueRange{lhs});
191  return success();
192  }
193 
194 private:
195  DataFlowSolver &solver;
196 };
197 
198 /// Check if `type` is index or integer type with `getWidth() > targetBitwidth`.
199 static LogicalResult checkIntType(Type type, unsigned targetBitwidth) {
200  Type elemType = getElementTypeOrSelf(type);
201  if (isa<IndexType>(elemType))
202  return success();
203 
204  if (auto intType = dyn_cast<IntegerType>(elemType))
205  if (intType.getWidth() > targetBitwidth)
206  return success();
207 
208  return failure();
209 }
210 
211 /// Check if op have same type for all operands and results and this type
212 /// is suitable for truncation.
213 static LogicalResult checkElementwiseOpType(Operation *op,
214  unsigned targetBitwidth) {
215  if (op->getNumOperands() == 0 || op->getNumResults() == 0)
216  return failure();
217 
218  Type type;
219  for (Value val : llvm::concat<Value>(op->getOperands(), op->getResults())) {
220  if (!type) {
221  type = val.getType();
222  continue;
223  }
224 
225  if (type != val.getType())
226  return failure();
227  }
228 
229  return checkIntType(type, targetBitwidth);
230 }
231 
232 /// Return union of all operands values ranges.
233 static std::optional<ConstantIntRanges> getOperandsRange(DataFlowSolver &solver,
234  ValueRange operands) {
235  std::optional<ConstantIntRanges> ret;
236  for (Value value : operands) {
237  auto *maybeInferredRange =
238  solver.lookupState<IntegerValueRangeLattice>(value);
239  if (!maybeInferredRange || maybeInferredRange->getValue().isUninitialized())
240  return std::nullopt;
241 
242  const ConstantIntRanges &inferredRange =
243  maybeInferredRange->getValue().getValue();
244 
245  ret = (ret ? ret->rangeUnion(inferredRange) : inferredRange);
246  }
247  return ret;
248 }
249 
250 /// Return int type truncated to `targetBitwidth`. If `srcType` is shaped,
251 /// return shaped type as well.
252 static Type getTargetType(Type srcType, unsigned targetBitwidth) {
253  auto dstType = IntegerType::get(srcType.getContext(), targetBitwidth);
254  if (auto shaped = dyn_cast<ShapedType>(srcType))
255  return shaped.clone(dstType);
256 
257  assert(srcType.isIntOrIndex() && "Invalid src type");
258  return dstType;
259 }
260 
261 /// Check provided `range` is inside `smin, smax, umin, umax` bounds.
262 static LogicalResult checkRange(const ConstantIntRanges &range, APInt smin,
263  APInt smax, APInt umin, APInt umax) {
264  auto sge = [](APInt val1, APInt val2) -> bool {
265  unsigned width = std::max(val1.getBitWidth(), val2.getBitWidth());
266  val1 = val1.sext(width);
267  val2 = val2.sext(width);
268  return val1.sge(val2);
269  };
270  auto sle = [](APInt val1, APInt val2) -> bool {
271  unsigned width = std::max(val1.getBitWidth(), val2.getBitWidth());
272  val1 = val1.sext(width);
273  val2 = val2.sext(width);
274  return val1.sle(val2);
275  };
276  auto uge = [](APInt val1, APInt val2) -> bool {
277  unsigned width = std::max(val1.getBitWidth(), val2.getBitWidth());
278  val1 = val1.zext(width);
279  val2 = val2.zext(width);
280  return val1.uge(val2);
281  };
282  auto ule = [](APInt val1, APInt val2) -> bool {
283  unsigned width = std::max(val1.getBitWidth(), val2.getBitWidth());
284  val1 = val1.zext(width);
285  val2 = val2.zext(width);
286  return val1.ule(val2);
287  };
288  return success(sge(range.smin(), smin) && sle(range.smax(), smax) &&
289  uge(range.umin(), umin) && ule(range.umax(), umax));
290 }
291 
292 static Value doCast(OpBuilder &builder, Location loc, Value src, Type dstType) {
293  Type srcType = src.getType();
294  assert(isa<VectorType>(srcType) == isa<VectorType>(dstType) &&
295  "Mixing vector and non-vector types");
296  Type srcElemType = getElementTypeOrSelf(srcType);
297  Type dstElemType = getElementTypeOrSelf(dstType);
298  assert(srcElemType.isIntOrIndex() && "Invalid src type");
299  assert(dstElemType.isIntOrIndex() && "Invalid dst type");
300  if (srcType == dstType)
301  return src;
302 
303  if (isa<IndexType>(srcElemType) || isa<IndexType>(dstElemType))
304  return builder.create<arith::IndexCastUIOp>(loc, dstType, src);
305 
306  auto srcInt = cast<IntegerType>(srcElemType);
307  auto dstInt = cast<IntegerType>(dstElemType);
308  if (dstInt.getWidth() < srcInt.getWidth())
309  return builder.create<arith::TruncIOp>(loc, dstType, src);
310 
311  return builder.create<arith::ExtUIOp>(loc, dstType, src);
312 }
313 
314 struct NarrowElementwise final : OpTraitRewritePattern<OpTrait::Elementwise> {
315  NarrowElementwise(MLIRContext *context, DataFlowSolver &s,
316  ArrayRef<unsigned> target)
317  : OpTraitRewritePattern(context), solver(s), targetBitwidths(target) {}
318 
320  LogicalResult matchAndRewrite(Operation *op,
321  PatternRewriter &rewriter) const override {
322  std::optional<ConstantIntRanges> range =
323  getOperandsRange(solver, op->getResults());
324  if (!range)
325  return failure();
326 
327  for (unsigned targetBitwidth : targetBitwidths) {
328  if (failed(checkElementwiseOpType(op, targetBitwidth)))
329  continue;
330 
331  Type srcType = op->getResult(0).getType();
332 
333  // We are truncating op args to the desired bitwidth before the op and
334  // then extending op results back to the original width after. extui and
335  // exti will produce different results for negative values, so limit
336  // signed range to non-negative values.
337  auto smin = APInt::getZero(targetBitwidth);
338  auto smax = APInt::getSignedMaxValue(targetBitwidth);
339  auto umin = APInt::getMinValue(targetBitwidth);
340  auto umax = APInt::getMaxValue(targetBitwidth);
341  if (failed(checkRange(*range, smin, smax, umin, umax)))
342  continue;
343 
344  Type targetType = getTargetType(srcType, targetBitwidth);
345  if (targetType == srcType)
346  continue;
347 
348  Location loc = op->getLoc();
349  IRMapping mapping;
350  for (Value arg : op->getOperands()) {
351  Value newArg = doCast(rewriter, loc, arg, targetType);
352  mapping.map(arg, newArg);
353  }
354 
355  Operation *newOp = rewriter.clone(*op, mapping);
356  rewriter.modifyOpInPlace(newOp, [&]() {
357  for (OpResult res : newOp->getResults()) {
358  res.setType(targetType);
359  }
360  });
361  SmallVector<Value> newResults;
362  for (Value res : newOp->getResults())
363  newResults.emplace_back(doCast(rewriter, loc, res, srcType));
364 
365  rewriter.replaceOp(op, newResults);
366  return success();
367  }
368  return failure();
369  }
370 
371 private:
372  DataFlowSolver &solver;
373  SmallVector<unsigned, 4> targetBitwidths;
374 };
375 
376 struct NarrowCmpI final : OpRewritePattern<arith::CmpIOp> {
377  NarrowCmpI(MLIRContext *context, DataFlowSolver &s, ArrayRef<unsigned> target)
378  : OpRewritePattern(context), solver(s), targetBitwidths(target) {}
379 
380  LogicalResult matchAndRewrite(arith::CmpIOp op,
381  PatternRewriter &rewriter) const override {
382  Value lhs = op.getLhs();
383  Value rhs = op.getRhs();
384 
385  std::optional<ConstantIntRanges> range =
386  getOperandsRange(solver, {lhs, rhs});
387  if (!range)
388  return failure();
389 
390  for (unsigned targetBitwidth : targetBitwidths) {
391  Type srcType = lhs.getType();
392  if (failed(checkIntType(srcType, targetBitwidth)))
393  continue;
394 
395  auto smin = APInt::getSignedMinValue(targetBitwidth);
396  auto smax = APInt::getSignedMaxValue(targetBitwidth);
397  auto umin = APInt::getMinValue(targetBitwidth);
398  auto umax = APInt::getMaxValue(targetBitwidth);
399  if (failed(checkRange(*range, smin, smax, umin, umax)))
400  continue;
401 
402  Type targetType = getTargetType(srcType, targetBitwidth);
403  if (targetType == srcType)
404  continue;
405 
406  Location loc = op->getLoc();
407  IRMapping mapping;
408  for (Value arg : op->getOperands()) {
409  Value newArg = doCast(rewriter, loc, arg, targetType);
410  mapping.map(arg, newArg);
411  }
412 
413  Operation *newOp = rewriter.clone(*op, mapping);
414  rewriter.replaceOp(op, newOp->getResults());
415  return success();
416  }
417  return failure();
418  }
419 
420 private:
421  DataFlowSolver &solver;
422  SmallVector<unsigned, 4> targetBitwidths;
423 };
424 
425 /// Fold index_cast(index_cast(%arg: i8, index), i8) -> %arg
426 /// This pattern assumes all passed `targetBitwidths` are not wider than index
427 /// type.
428 struct FoldIndexCastChain final : OpRewritePattern<arith::IndexCastUIOp> {
429  FoldIndexCastChain(MLIRContext *context, ArrayRef<unsigned> target)
430  : OpRewritePattern(context), targetBitwidths(target) {}
431 
432  LogicalResult matchAndRewrite(arith::IndexCastUIOp op,
433  PatternRewriter &rewriter) const override {
434  auto srcOp = op.getIn().getDefiningOp<arith::IndexCastUIOp>();
435  if (!srcOp)
436  return failure();
437 
438  Value src = srcOp.getIn();
439  if (src.getType() != op.getType())
440  return failure();
441 
442  auto intType = dyn_cast<IntegerType>(op.getType());
443  if (!intType || !llvm::is_contained(targetBitwidths, intType.getWidth()))
444  return failure();
445 
446  rewriter.replaceOp(op, src);
447  return success();
448  }
449 
450 private:
451  SmallVector<unsigned, 4> targetBitwidths;
452 };
453 
454 struct IntRangeOptimizationsPass final
455  : arith::impl::ArithIntRangeOptsBase<IntRangeOptimizationsPass> {
456 
457  void runOnOperation() override {
458  Operation *op = getOperation();
459  MLIRContext *ctx = op->getContext();
460  DataFlowSolver solver;
461  solver.load<DeadCodeAnalysis>();
462  solver.load<IntegerRangeAnalysis>();
463  if (failed(solver.initializeAndRun(op)))
464  return signalPassFailure();
465 
466  DataFlowListener listener(solver);
467 
468  RewritePatternSet patterns(ctx);
469  populateIntRangeOptimizationsPatterns(patterns, solver);
470 
471  GreedyRewriteConfig config;
472  config.listener = &listener;
473 
474  if (failed(applyPatternsAndFoldGreedily(op, std::move(patterns), config)))
475  signalPassFailure();
476  }
477 };
478 
479 struct IntRangeNarrowingPass final
480  : arith::impl::ArithIntRangeNarrowingBase<IntRangeNarrowingPass> {
481  using ArithIntRangeNarrowingBase::ArithIntRangeNarrowingBase;
482 
483  void runOnOperation() override {
484  Operation *op = getOperation();
485  MLIRContext *ctx = op->getContext();
486  DataFlowSolver solver;
487  solver.load<DeadCodeAnalysis>();
488  solver.load<IntegerRangeAnalysis>();
489  if (failed(solver.initializeAndRun(op)))
490  return signalPassFailure();
491 
492  DataFlowListener listener(solver);
493 
494  RewritePatternSet patterns(ctx);
495  populateIntRangeNarrowingPatterns(patterns, solver, bitwidthsSupported);
496 
497  GreedyRewriteConfig config;
498  // We specifically need bottom-up traversal as cmpi pattern needs range
499  // data, attached to its original argument values.
500  config.useTopDownTraversal = false;
501  config.listener = &listener;
502 
503  if (failed(applyPatternsAndFoldGreedily(op, std::move(patterns), config)))
504  signalPassFailure();
505  }
506 };
507 } // namespace
508 
510  RewritePatternSet &patterns, DataFlowSolver &solver) {
511  patterns.add<MaterializeKnownConstantValues, DeleteTrivialRem<RemSIOp>,
512  DeleteTrivialRem<RemUIOp>>(patterns.getContext(), solver);
513 }
514 
516  RewritePatternSet &patterns, DataFlowSolver &solver,
517  ArrayRef<unsigned> bitwidthsSupported) {
518  patterns.add<NarrowElementwise, NarrowCmpI>(patterns.getContext(), solver,
519  bitwidthsSupported);
520  patterns.add<FoldIndexCastChain>(patterns.getContext(), bitwidthsSupported);
521 }
522 
524  return std::make_unique<IntRangeOptimizationsPass>();
525 }
static Value getZero(OpBuilder &b, Location loc, Type elementType)
Get zero value for an element type.
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.
Definition: FoldUtils.cpp:50
static std::optional< APInt > getMaybeConstantValue(DataFlowSolver &solver, Value value)
static LogicalResult maybeReplaceWithConstant(DataFlowSolver &solver, PatternRewriter &rewriter, Value value)
Patterned after SCCP.
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
static void rewrite(DataFlowSolver &solver, MLIRContext *context, MutableArrayRef< Region > initialRegions)
Rewrite the given regions using the computing analysis.
Definition: SCCP.cpp:67
Attributes are known-constant values of operations.
Definition: Attributes.h:25
This class represents an argument of a Block.
Definition: Value.h:319
Block represents an ordered list of Operations.
Definition: Block.h:33
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition: Builders.cpp:268
MLIRContext * getContext() const
Definition: Builders.h:55
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.
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...
Definition: Dialect.h:38
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...
Definition: Dialect.h:83
This class allows control over how the GreedyPatternRewriteDriver works.
bool useTopDownTraversal
This specifies the order of initial traversal that populates the rewriters worklist.
RewriterBase::Listener * listener
An optional listener that should be notified about IR modifications.
This is a utility class for mapping one set of IR entities to another.
Definition: IRMapping.h:26
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
Definition: IRMapping.h:30
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:66
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:60
Dialect * getLoadedDialect(StringRef name)
Get a registered IR dialect with the given namespace.
This class helps build Operations.
Definition: Builders.h:215
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
Definition: Builders.cpp:588
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition: Builders.h:439
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition: Builders.cpp:497
This is a value defined by a result of an operation.
Definition: Value.h:457
OpTraitRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting again...
Definition: PatternMatch.h:384
OpTraitRewritePattern(MLIRContext *context, PatternBenefit benefit=1)
Definition: PatternMatch.h:386
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
Definition: Operation.h:220
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition: Operation.h:402
MLIRContext * getContext()
Return the context this operation is associated with.
Definition: Operation.h:216
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition: Operation.h:669
Location getLoc()
The source location the operation was defined or derived from.
Definition: Operation.h:223
unsigned getNumOperands()
Definition: Operation.h:341
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition: Operation.h:672
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition: Operation.h:373
result_range getResults()
Definition: Operation.h:410
unsigned getNumResults()
Return the number of results held by this operation.
Definition: Operation.h:399
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
Definition: PatternMatch.h:791
This class contains all of the data related to a pattern, but does not contain any methods or logic f...
Definition: PatternMatch.h:73
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition: Region.h:26
Operation * getParentOp()
Return the parent operation this region is attached to.
Definition: Region.h:200
MLIRContext * getContext() const
Definition: PatternMatch.h:829
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
Definition: PatternMatch.h:853
RewritePattern is the common base class for all DAG to DAG replacements.
Definition: PatternMatch.h:246
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
Definition: PatternMatch.h:644
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
Definition: PatternMatch.h:636
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition: Types.cpp:35
bool isIntOrIndex() const
Return true if this is an integer (of any signedness) or an index type.
Definition: Types.cpp:123
This class provides an abstraction over the different types of ranges over Values.
Definition: ValueRange.h:381
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
bool use_empty() const
Returns true if this value has no uses.
Definition: Value.h:218
Type getType() const
Return the type of this value.
Definition: Value.h:129
Location getLoc() const
Return the location of this value.
Definition: Value.cpp:26
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition: Value.cpp:20
Region * getParentRegion()
Return the Region in which this Value is defined.
Definition: Value.cpp:41
Dead code analysis analyzes control-flow, as understood by RegionBranchOpInterface and BranchOpInterf...
Integer range analysis determines the integer value range of SSA values using operations that define ...
This lattice element represents the integer value range of an SSA value.
std::unique_ptr< Pass > createIntRangeOptimizationsPass()
Create a pass which do optimizations based on integer 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.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition: Matchers.h:485
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
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.
LogicalResult applyPatternsAndFoldGreedily(Region &region, 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...
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.
Definition: Matchers.h:369
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
Definition: PatternMatch.h:358