MLIR 24.0.0git
LoopSpecialization.cpp
Go to the documentation of this file.
1//===- LoopSpecialization.cpp - scf.parallel/SCR.for specialization -------===//
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// Specializes parallel loops and for loops for easier unrolling and
10// vectorization.
11//
12//===----------------------------------------------------------------------===//
13
15
23#include "mlir/IR/AffineExpr.h"
24#include "mlir/IR/IRMapping.h"
27
28namespace mlir {
29#define GEN_PASS_DEF_SCFFORLOOPPEELING
30#define GEN_PASS_DEF_SCFFORLOOPSPECIALIZATION
31#define GEN_PASS_DEF_SCFPARALLELLOOPSPECIALIZATION
32#include "mlir/Dialect/SCF/Transforms/Passes.h.inc"
33} // namespace mlir
34
35using namespace mlir;
36using namespace mlir::affine;
37using scf::ForOp;
38using scf::ParallelOp;
39
40/// Rewrite a parallel loop with bounds defined by an affine.min with a constant
41/// into 2 loops after checking if the bounds are equal to that constant. This
42/// is beneficial if the loop will almost always have the constant bound and
43/// that version can be fully unrolled and vectorized.
44static void specializeParallelLoopForUnrolling(ParallelOp op) {
45 SmallVector<int64_t, 2> constantIndices;
46 constantIndices.reserve(op.getUpperBound().size());
47 for (auto bound : op.getUpperBound()) {
48 auto minOp = bound.getDefiningOp<AffineMinOp>();
49 if (!minOp)
50 return;
51 int64_t minConstant = std::numeric_limits<int64_t>::max();
52 for (AffineExpr expr : minOp.getMap().getResults()) {
53 if (auto constantIndex = dyn_cast<AffineConstantExpr>(expr))
54 minConstant = std::min(minConstant, constantIndex.getValue());
55 }
56 if (minConstant == std::numeric_limits<int64_t>::max())
57 return;
58 constantIndices.push_back(minConstant);
59 }
60
61 OpBuilder b(op);
62 IRMapping map;
63 Value cond;
64 for (auto bound : llvm::zip(op.getUpperBound(), constantIndices)) {
65 Value constant =
66 arith::ConstantIndexOp::create(b, op.getLoc(), std::get<1>(bound));
67 Value cmp = arith::CmpIOp::create(b, op.getLoc(), arith::CmpIPredicate::eq,
68 std::get<0>(bound), constant);
69 cond = cond ? arith::AndIOp::create(b, op.getLoc(), cond, cmp) : cmp;
70 map.map(std::get<0>(bound), constant);
71 }
72 auto ifOp = scf::IfOp::create(b, op.getLoc(), cond, /*withElseRegion=*/true);
73 ifOp.getThenBodyBuilder().clone(*op.getOperation(), map);
74 ifOp.getElseBodyBuilder().clone(*op.getOperation());
75 op.erase();
76}
77
78/// Rewrite a for loop with bounds defined by an affine.min with a constant into
79/// 2 loops after checking if the bounds are equal to that constant. This is
80/// beneficial if the loop will almost always have the constant bound and that
81/// version can be fully unrolled and vectorized.
82static void specializeForLoopForUnrolling(ForOp op) {
83 auto bound = op.getUpperBound();
84 auto minOp = bound.getDefiningOp<AffineMinOp>();
85 if (!minOp)
86 return;
87 AffineMap canonicalizedMap = minOp.getMap();
88 SmallVector<Value> operands(minOp.getMapOperands());
89 affine::canonicalizeMapAndOperands(&canonicalizedMap, &operands);
90 int64_t minConstant = std::numeric_limits<int64_t>::max();
91 for (AffineExpr expr : canonicalizedMap.getResults()) {
92 if (auto constantIndex = dyn_cast<AffineConstantExpr>(expr))
93 minConstant = std::min(minConstant, constantIndex.getValue());
94 }
95 if (minConstant == std::numeric_limits<int64_t>::max())
96 return;
97
98 OpBuilder b(op);
99 IRMapping map;
100 Value constant = arith::ConstantOp::create(
101 b, op.getLoc(),
102 IntegerAttr::get(op.getUpperBound().getType(), minConstant));
103 Value cond = arith::CmpIOp::create(b, op.getLoc(), arith::CmpIPredicate::eq,
104 bound, constant);
105 map.map(bound, constant);
106 auto ifOp = scf::IfOp::create(b, op.getLoc(), cond, /*withElseRegion=*/true);
107 ifOp.getThenBodyBuilder().clone(*op.getOperation(), map);
108 ifOp.getElseBodyBuilder().clone(*op.getOperation());
109 op.erase();
110}
112/// Rewrite a for loop with bounds/step that potentially do not divide evenly
113/// into a for loop where the step divides the iteration space evenly, followed
114/// by an scf.if for the last (partial) iteration (if any).
115///
116/// This function rewrites the given scf.for loop in-place and creates a new
117/// scf.if operation for the last iteration. It replaces all uses of the
118/// unpeeled loop with the results of the newly generated scf.if.
119///
120/// The newly generated scf.if operation is returned via `ifOp`. The boundary
121/// at which the loop is split (new upper bound) is returned via `splitBound`.
122/// The return value indicates whether the loop was rewritten or not.
123///
124/// Note: Loops with a step size of 0 cannot be peeled. Applying this function
125/// to such a loop may result in IR with undefined behavior.
126static LogicalResult peelForLoop(RewriterBase &b, ForOp forOp,
127 ForOp &partialIteration, Value &splitBound) {
129 Value lbVal = forOp.getLowerBound();
130 Value ubVal = forOp.getUpperBound();
131 Value stepVal = forOp.getStep();
132 b.setInsertionPoint(forOp);
133 auto loc = forOp.getLoc();
134
135 // Check whether we have a unsigned loop
136 if (forOp.getUnsignedCmp()) {
137 IntegerAttr lbAttr, ubAttr, stepAttr;
138 if (matchPattern(lbVal, m_Constant(&lbAttr)) &&
139 matchPattern(ubVal, m_Constant(&ubAttr)) &&
140 matchPattern(stepVal, m_Constant(&stepAttr))) {
141
142 // Get the constants as APInt at the loop’s own bit width
143 APInt lb = lbAttr.getValue();
144 APInt ub = ubAttr.getValue();
145 APInt step = stepAttr.getValue();
146
147 if (step.ule(1) || ub.ule(lb) || (ub - lb).urem(step).isZero())
148 return failure();
149 }
150
151 // split = ub - ((ub - min(lb, ub)) remui step). The min makes an empty loop
152 // (ub <= lb) give a zero remainder, so split = ub and both loops stay
153 // empty. The wrapping subtraction is exact for ub > lb, even above the
154 // signed range.
155 Value lbEff = b.createOrFold<arith::MinUIOp>(loc, lbVal, ubVal);
156 Value diff = b.createOrFold<arith::SubIOp>(loc, ubVal, lbEff);
157 Value rem = b.createOrFold<arith::RemUIOp>(loc, diff, stepVal);
158 splitBound = b.createOrFold<arith::SubIOp>(loc, ubVal, rem);
160 } else {
161 auto lbInt = getConstantIntValue(lbVal);
162 auto ubInt = getConstantIntValue(ubVal);
163 auto stepInt = getConstantIntValue(stepVal);
164 // No specialization necessary if step size is 1. Also bail out in case of
165 // an invalid zero or negative step which might have happened during
166 // folding.
167 if (stepInt && *stepInt <= 1)
168 return failure();
169
170 // No specialization necessary if step already divides upper bound evenly.
171 // Fast path: lb, ub and step are constants.
172 if (lbInt && ubInt && stepInt) {
174 // bail out if the subtraction overflows
175 if (llvm::SubOverflow(*ubInt, *lbInt, diff) || diff % *stepInt == 0)
176 return failure();
178
179 // Only the dynamic path computes the peeling bound with affine.apply, which
180 // accepts only index operands.
181 if ((!lbInt || !ubInt || !stepInt) &&
182 !forOp.getInductionVar().getType().isIndex())
183 return failure();
184
185 // Slow path: Examine the ops that define lb, ub and step.
186 AffineExpr sym0, sym1, sym2;
187 bindSymbols(b.getContext(), sym0, sym1, sym2);
188 SmallVector<Value> operands{forOp.getLowerBound(), forOp.getUpperBound(),
189 forOp.getStep()};
190 AffineMap map = AffineMap::get(0, 3, {(sym1 - sym0) % sym2});
192 if (auto constExpr = dyn_cast<AffineConstantExpr>(map.getResult(0)))
193 if (constExpr.getValue() == 0)
194 return failure();
195
196 // New upper bound: max(%lb, %ub - (%ub - %lb) mod %step). Affine `mod` is
197 // non-negative, so without the clamp an empty loop (%lb >= %ub) gets a
198 // split bound below %lb and its partial iteration executes.
199 auto modMap = AffineMap::get(0, 3, {sym1 - ((sym1 - sym0) % sym2), sym0},
200 b.getContext());
201 splitBound = b.createOrFold<AffineMaxOp>(loc, modMap,
202 ValueRange{forOp.getLowerBound(),
203 forOp.getUpperBound(),
204 forOp.getStep()});
205 if (splitBound.getType() != forOp.getLowerBound().getType())
206 splitBound = b.createOrFold<arith::IndexCastOp>(
207 loc, forOp.getLowerBound().getType(), splitBound);
208 }
209 // Create ForOp for partial iteration.
210 b.setInsertionPointAfter(forOp);
211 partialIteration = cast<ForOp>(b.clone(*forOp.getOperation()));
212 partialIteration.getLowerBoundMutable().assign(splitBound);
213 b.replaceAllUsesWith(forOp.getResults(), partialIteration->getResults());
214 partialIteration.getInitArgsMutable().assign(forOp->getResults());
215
216 // Set new upper loop bound.
217 b.modifyOpInPlace(forOp,
218 [&]() { forOp.getUpperBoundMutable().assign(splitBound); });
219
220 return success();
221}
222
223static void rewriteAffineOpAfterPeeling(RewriterBase &rewriter, ForOp forOp,
224 ForOp partialIteration,
225 Value previousUb) {
226 Value mainIv = forOp.getInductionVar();
227 Value partialIv = partialIteration.getInductionVar();
228 assert(forOp.getStep() == partialIteration.getStep() &&
229 "expected same step in main and partial loop");
230 Value step = forOp.getStep();
231
232 forOp.walk([&](Operation *affineOp) {
233 if (!isa<AffineMinOp, AffineMaxOp>(affineOp))
234 return WalkResult::advance();
235 (void)scf::rewritePeeledMinMaxOp(rewriter, affineOp, mainIv, previousUb,
236 step,
237 /*insideLoop=*/true);
238 return WalkResult::advance();
239 });
240 partialIteration.walk([&](Operation *affineOp) {
241 if (!isa<AffineMinOp, AffineMaxOp>(affineOp))
242 return WalkResult::advance();
243 (void)scf::rewritePeeledMinMaxOp(rewriter, affineOp, partialIv, previousUb,
244 step, /*insideLoop=*/false);
245 return WalkResult::advance();
246 });
247}
248
250 ForOp forOp,
251 ForOp &partialIteration) {
252 Value previousUb = forOp.getUpperBound();
253 Value splitBound;
254 if (failed(peelForLoop(rewriter, forOp, partialIteration, splitBound)))
255 return failure();
256
257 // The affine.min/max simplification relies on signed reasoning, which does
258 // not hold for unsigned loops, so leave those ops untouched.
259 if (forOp.getUnsignedCmp())
260 return success();
262 // Rewrite affine.min and affine.max ops.
263 rewriteAffineOpAfterPeeling(rewriter, forOp, partialIteration, previousUb);
265 return success();
266}
268/// Rewrites the original scf::ForOp as two scf::ForOp Ops, the first
269/// scf::ForOp corresponds to the first iteration of the loop which can be
270/// canonicalized away in the following optimizations. The second loop Op
271/// contains the remaining iterations, with a lower bound updated as the
272/// original lower bound plus the step (i.e. skips the first iteration).
273LogicalResult mlir::scf::peelForLoopFirstIteration(RewriterBase &b, ForOp forOp,
274 ForOp &firstIteration) {
276 Value lbVal = forOp.getLowerBound();
277 Value ubVal = forOp.getUpperBound();
278 Value stepVal = forOp.getStep();
279 auto loc = forOp.getLoc();
280
281 Value splitBound;
282 b.setInsertionPoint(forOp);
283
284 // Check whether we have a unsigned loop
285 if (forOp.getUnsignedCmp()) {
286 IntegerAttr lbAttr, ubAttr, stepAttr;
287 if (matchPattern(forOp.getLowerBound(), m_Constant(&lbAttr)) &&
288 matchPattern(forOp.getUpperBound(), m_Constant(&ubAttr)) &&
289 matchPattern(forOp.getStep(), m_Constant(&stepAttr))) {
290
291 // Get the constants as APInt at the loop’s own bit width
292 APInt lb = lbAttr.getValue();
293 APInt ub = ubAttr.getValue();
294 APInt step = stepAttr.getValue();
295
296 // bail out if ub <= lb or ub - lb <= step (unsigned constants)
297 if (ub.ule(lb) || (ub - lb).ule(step))
298 return failure();
299 }
300
301 // split = max(lb, min(lb + step, ub)): the first loop runs one iteration,
302 // or none if the loop is empty. If lb + step wraps, the loop has at most
303 // one iteration and split falls back to lb, so the main loop does the work.
304 // use unsigned ops (MinUIOp, MaxUIOp)
305 Value sum = b.createOrFold<arith::AddIOp>(loc, lbVal, stepVal);
306 Value capped = b.createOrFold<arith::MinUIOp>(loc, sum, ubVal);
307 splitBound = b.createOrFold<arith::MaxUIOp>(loc, lbVal, capped);
308 } else {
309 auto lbInt = getConstantIntValue(lbVal);
310 auto ubInt = getConstantIntValue(ubVal);
311 auto stepInt = getConstantIntValue(stepVal);
312
313 // Peeling is not needed if there is at most one iteration. scf.for requires
314 // a positive step, so the trip count is ceil((UB - LB) / Step) when UB > LB
315 // and 0 otherwise. With a positive step, "UB - LB <= Step" therefore covers
316 // both the empty loop and the single-iteration loop. Bail out if the
317 // subtraction overflows, since we can't reason about the trip count then.
318 if (lbInt && ubInt && stepInt) {
319 int64_t diff;
320 if (llvm::SubOverflow(*ubInt, *lbInt, diff) || diff <= *stepInt)
321 return failure();
322 }
323
324 // split = max(lb, min(lb + step, ub)): the first loop runs one iteration,
325 // or none if the loop is empty. If lb + step wraps, the loop has at most
326 // one iteration and split falls back to lb, so the main loop does the work.
327 Value sum = b.createOrFold<arith::AddIOp>(loc, lbVal, stepVal);
328 Value capped = b.createOrFold<arith::MinSIOp>(loc, sum, ubVal);
329 splitBound = b.createOrFold<arith::MaxSIOp>(loc, lbVal, capped);
330 }
331 // Peel the first iteration.
332 firstIteration = cast<ForOp>(b.clone(*forOp.getOperation()));
333 b.modifyOpInPlace(firstIteration, [&]() {
334 firstIteration.getUpperBoundMutable().assign(splitBound);
335 });
336 // Update main loop with new lower bound.
337 b.modifyOpInPlace(forOp, [&]() {
338 forOp.getInitArgsMutable().assign(firstIteration->getResults());
339 forOp.getLowerBoundMutable().assign(splitBound);
340 });
341
342 return success();
343}
344
345static constexpr char kPeeledLoopLabel[] = "__peeled_loop__";
346static constexpr char kPartialIterationLabel[] = "__partial_iteration__";
347
348namespace {
349struct ForLoopPeelingPattern : public OpRewritePattern<ForOp> {
350 ForLoopPeelingPattern(MLIRContext *ctx, bool peelFront, bool skipPartial)
351 : OpRewritePattern<ForOp>(ctx), peelFront(peelFront),
352 skipPartial(skipPartial) {}
353
354 LogicalResult matchAndRewrite(ForOp forOp,
355 PatternRewriter &rewriter) const override {
356
357 // Do not peel already peeled loops.
358 if (forOp->hasDiscardableAttr(kPeeledLoopLabel))
359 return failure();
360
361 scf::ForOp partialIteration;
362 // The case for peeling the first iteration of the loop.
363 if (peelFront) {
364 if (failed(
365 peelForLoopFirstIteration(rewriter, forOp, partialIteration))) {
366 return failure();
367 }
368 } else {
369 if (skipPartial) {
370 // No peeling of loops inside the partial iteration of another peeled
371 // loop.
372 Operation *op = forOp.getOperation();
373 while ((op = op->getParentOfType<scf::ForOp>())) {
375 return failure();
376 }
377 }
378 // Apply loop peeling.
379 if (failed(
380 peelForLoopAndSimplifyBounds(rewriter, forOp, partialIteration)))
381 return failure();
382 }
383
384 // Apply label, so that the same loop is not rewritten a second time.
385 rewriter.modifyOpInPlace(partialIteration, [&]() {
386 partialIteration->setDiscardableAttr(kPeeledLoopLabel,
387 rewriter.getUnitAttr());
388 partialIteration->setDiscardableAttr(kPartialIterationLabel,
389 rewriter.getUnitAttr());
390 });
391 rewriter.modifyOpInPlace(forOp, [&]() {
392 forOp->setDiscardableAttr(kPeeledLoopLabel, rewriter.getUnitAttr());
393 });
394 return success();
395 }
396
397 // If set to true, the first iteration of the loop will be peeled. Otherwise,
398 // the unevenly divisible loop will be peeled at the end.
399 bool peelFront;
400
401 /// If set to true, loops inside partial iterations of another peeled loop
402 /// are not peeled. This reduces the size of the generated code. Partial
403 /// iterations are not usually performance critical.
404 /// Note: Takes into account the entire chain of parent operations, not just
405 /// the direct parent.
406 bool skipPartial;
407};
408} // namespace
409
410namespace {
411struct ParallelLoopSpecialization
413 ParallelLoopSpecialization> {
414 void runOnOperation() override {
415 getOperation()->walk(
416 [](ParallelOp op) { specializeParallelLoopForUnrolling(op); });
417 }
418};
419
420struct ForLoopSpecialization
421 : public impl::SCFForLoopSpecializationBase<ForLoopSpecialization> {
422 void runOnOperation() override {
423 getOperation()->walk([](ForOp op) { specializeForLoopForUnrolling(op); });
424 }
425};
426
427struct ForLoopPeeling : public impl::SCFForLoopPeelingBase<ForLoopPeeling> {
428 using impl::SCFForLoopPeelingBase<ForLoopPeeling>::SCFForLoopPeelingBase;
429
430 void runOnOperation() override {
431 auto *parentOp = getOperation();
432 MLIRContext *ctx = parentOp->getContext();
433 RewritePatternSet patterns(ctx);
434 patterns.add<ForLoopPeelingPattern>(ctx, peelFront, skipPartial);
435 (void)applyPatternsGreedily(parentOp, std::move(patterns));
436
437 // Drop the markers.
438 parentOp->walk([](Operation *op) {
441 });
442 }
443};
444} // namespace
445
447 return std::make_unique<ParallelLoopSpecialization>();
448}
449
451 return std::make_unique<ForLoopSpecialization>();
452}
return success()
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
static void specializeForLoopForUnrolling(ForOp op)
Rewrite a for loop with bounds defined by an affine.min with a constant into 2 loops after checking i...
static void specializeParallelLoopForUnrolling(ParallelOp op)
Rewrite a parallel loop with bounds defined by an affine.min with a constant into 2 loops after check...
static constexpr char kPeeledLoopLabel[]
static void rewriteAffineOpAfterPeeling(RewriterBase &rewriter, ForOp forOp, ForOp partialIteration, Value previousUb)
static LogicalResult peelForLoop(RewriterBase &b, ForOp forOp, ForOp &partialIteration, Value &splitBound)
Rewrite a for loop with bounds/step that potentially do not divide evenly into a for loop where the s...
static constexpr char kPartialIterationLabel[]
#define rem(a, b)
Base type for affine expression.
Definition AffineExpr.h:68
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Definition AffineMap.h:46
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
ArrayRef< AffineExpr > getResults() const
AffineExpr getResult(unsigned idx) const
UnitAttr getUnitAttr()
Definition Builders.cpp:106
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
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
This class helps build Operations.
Definition Builders.h:210
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
bool hasDiscardableAttr(StringRef name)
Return true if this operation has a discardable attribute with the provided name.
Definition Operation.h:503
Attribute removeDiscardableAttr(StringAttr name)
Remove the discardable attribute with the specified name if it exists.
Definition Operation.h:524
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
static WalkResult advance()
Definition WalkResult.h:47
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:434
void canonicalizeMapAndOperands(AffineMap *map, SmallVectorImpl< Value > *operands)
Modifies both map and operands in-place so as to:
void fullyComposeAffineMapAndOperands(AffineMap *map, SmallVectorImpl< Value > *operands, bool composeAffineMin=false)
Given an affine map map and its input operands, this method composes into map, maps of AffineApplyOps...
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
LogicalResult peelForLoopAndSimplifyBounds(RewriterBase &rewriter, ForOp forOp, scf::ForOp &partialIteration)
Rewrite a for loop with bounds/step that potentially do not divide evenly into a for loop where the s...
LogicalResult peelForLoopFirstIteration(RewriterBase &rewriter, ForOp forOp, scf::ForOp &partialIteration)
Peel the first iteration out of the scf.for loop.
LogicalResult rewritePeeledMinMaxOp(RewriterBase &rewriter, Operation *op, Value iv, Value ub, Value step, bool insideLoop)
Try to simplify the given affine.min/max operation op after loop peeling.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition Matchers.h:490
std::unique_ptr< Pass > createParallelLoopSpecializationPass()
Creates a pass that specializes parallel loop for unrolling and vectorization.
std::unique_ptr< Pass > createForLoopSpecializationPass()
Creates a pass that specializes for loop for unrolling and vectorization.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:311
LogicalResult applyPatternsGreedily(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...
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
Definition AffineExpr.h:325
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...