MLIR 24.0.0git
Hoisting.cpp
Go to the documentation of this file.
1//===- Hoisting.cpp - Linalg hoisting transformations ---------------------===//
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// This file implements functions concerned with hoisting invariant operations
10// in the context of Linalg transformations.
11//
12//===----------------------------------------------------------------------===//
13
26#include "mlir/IR/Dominance.h"
28#include "llvm/Support/Debug.h"
29
30using llvm::dbgs;
31
32#define DEBUG_TYPE "linalg-hoisting"
33
34#define DBGS() (dbgs() << '[' << DEBUG_TYPE << "] ")
35
36using namespace mlir;
37using namespace mlir::linalg;
38
39/// Replace `loop` with a new loop that has a different init operand at
40/// position `index`. The body of this loop is moved over to the new loop.
41///
42/// `newInitOperands` specifies the replacement "init" operands.
43/// `newYieldValue` is the replacement yield value of the loop at position
44/// `index`.
45static scf::ForOp replaceWithDifferentYield(RewriterBase &rewriter,
46 scf::ForOp loop,
47 Value newInitOperand,
48 unsigned index,
49 Value newYieldValue) {
50 OpBuilder::InsertionGuard g(rewriter);
51 rewriter.setInsertionPoint(loop.getOperation());
52 auto inits = llvm::to_vector(loop.getInits());
53
54 // Replace the init value with the new operand.
55 assert(index < inits.size());
56 inits[index] = newInitOperand;
57
58 scf::ForOp newLoop = scf::ForOp::create(
59 rewriter, loop.getLoc(), loop.getLowerBound(), loop.getUpperBound(),
60 loop.getStep(), inits, [](OpBuilder &, Location, Value, ValueRange) {},
61 loop.getUnsignedCmp());
62
63 // Generate the new yield with the replaced operand.
64 auto yieldOp = cast<scf::YieldOp>(loop.getBody()->getTerminator());
65 yieldOp.setOperand(index, newYieldValue);
66
67 // Move the loop body to the new op.
68 rewriter.mergeBlocks(loop.getBody(), newLoop.getBody(),
69 newLoop.getBody()->getArguments());
70
71 // Replace the old loop.
72 rewriter.replaceOp(loop.getOperation(), newLoop->getResults());
73 return newLoop;
74}
75
76// Hoist out a pair of corresponding vector.extract+vector.broadcast
77// operations. This function transforms a loop like this:
78// %res = scf.for _ = _ to _ step _ iter_args(%iarg = %v) -> (t1) {
79// %e = vector.extract %iarg : t1 to t2
80// %u = "some_use"(%e) : (t2) -> t2
81// %b = vector.broadcast %u : t2 to t1
82// scf.yield %b : t1
83// }
84// into the following:
85// %e = vector.extract %v: t1 to t2
86// %res' = scf.for _ = _ to _ step _ iter_args(%iarg = %e) -> (t2) {
87// %u' = "some_use"(%iarg) : (t2) -> t2
88// scf.yield %u' : t2
89// }
90// %res = vector.broadcast %res' : t2 to t1
92 Operation *root) {
93 bool changed = true;
94 while (changed) {
95 changed = false;
96 // First move loop invariant ops outside of their loop. This needs to be
97 // done before as we cannot move ops without interrupting the function walk.
98 root->walk(
99 [&](LoopLikeOpInterface loopLike) { moveLoopInvariantCode(loopLike); });
100
101 root->walk([&](vector::ExtractOp extractOp) {
102 LLVM_DEBUG(DBGS() << "Candidate for hoisting: "
103 << *extractOp.getOperation() << "\n");
104
105 auto loop = dyn_cast<scf::ForOp>(extractOp->getParentOp());
106 if (!loop)
107 return WalkResult::advance();
108
109 // Check that the vector to extract from is a BlockArgument.
110 auto blockArg = dyn_cast<BlockArgument>(extractOp.getSource());
111 if (!blockArg)
112 return WalkResult::advance();
113
114 // Check that the blockArg is an iter_arg of the loop.
115 OpOperand *initArg = loop.getTiedLoopInit(blockArg);
116 if (!initArg)
117 return WalkResult::advance();
118
119 // If the iter_arg does not have only one use, it won't be possible to
120 // hoist the extractOp out.
121 if (!blockArg.hasOneUse())
122 return WalkResult::advance();
123
124 unsigned index = blockArg.getArgNumber() - loop.getNumInductionVars();
125
126 // Check that the loop yields a broadcast that has just one use.
127 Operation *yieldedVal =
128 loop.getTiedLoopYieldedValue(blockArg)->get().getDefiningOp();
129 auto broadcast = dyn_cast<vector::BroadcastOp>(yieldedVal);
130 if (!broadcast || !broadcast.getResult().hasOneUse())
131 return WalkResult::advance();
132
133 LLVM_DEBUG(DBGS() << "Candidate broadcast: " << broadcast << "\n");
134
135 Type broadcastInputType = broadcast.getSourceType();
136 if (broadcastInputType != extractOp.getType())
137 return WalkResult::advance();
138
139 // The position of the extract must be defined outside of the loop if
140 // it is dynamic.
141 for (auto operand : extractOp.getDynamicPosition())
142 if (!loop.isDefinedOutsideOfLoop(operand))
143 return WalkResult::advance();
144
145 rewriter.modifyOpInPlace(broadcast, [&] {
146 extractOp.getSourceMutable().assign(initArg->get());
147 });
148 loop.moveOutOfLoop(extractOp);
149 rewriter.moveOpAfter(broadcast, loop);
150
151 scf::ForOp newLoop = replaceWithDifferentYield(
152 rewriter, loop, extractOp.getResult(), index, broadcast.getSource());
153
154 LLVM_DEBUG(DBGS() << "New loop: " << newLoop << "\n");
155
156 rewriter.replaceAllUsesWith(newLoop.getResult(index), broadcast);
157 rewriter.modifyOpInPlace(
158 broadcast, [&] { broadcast.setOperand(newLoop.getResult(index)); });
159
160 changed = true;
161 return WalkResult::interrupt();
162 });
163 }
164}
165
167 bool verifyNonZeroTrip) {
168 bool changed = true;
169 while (changed) {
170 changed = false;
171 // First move loop invariant ops outside of their loop. This needs to be
172 // done before as we cannot move ops without interrupting the function walk.
173 root->walk(
174 [&](LoopLikeOpInterface loopLike) { moveLoopInvariantCode(loopLike); });
175
176 // Find all loops that are certain to have non zero trip count. Any loops
177 // that are not part of this set cannot be hoisted from, since hoisting from
178 // a potentially zero trip count loop may cause a vector transfer to be
179 // executed when it shouldn't be.
180 llvm::DenseSet<LoopLikeOpInterface> definiteNonZeroTripCountLoops;
181 if (verifyNonZeroTrip) {
182 root->walk([&](LoopLikeOpInterface loopLike) {
183 std::optional<SmallVector<OpFoldResult>> lbs =
184 loopLike.getLoopLowerBounds();
185 std::optional<SmallVector<OpFoldResult>> ubs =
186 loopLike.getLoopUpperBounds();
187 // If loop bounds cannot be found, assume possibly zero trip count.
188 if (!lbs || !ubs)
189 return;
190
191 // Otherwise, use ValueBounds to find the maximum lower bound and
192 // minimum upper bound. If the bounds are found, and maxLb is less
193 // than the minUb, then the loop will not have zero trip count.
194 for (auto [lb, ub] : llvm::zip_equal(lbs.value(), ubs.value())) {
195 FailureOr<int64_t> maxLb =
198 /*stopCondition=*/nullptr,
199 ValueBoundsOptions{/*closedUB=*/true});
200 if (failed(maxLb))
201 return;
202 FailureOr<int64_t> minUb =
205 if (failed(minUb))
206 return;
207 if (minUb.value() <= maxLb.value())
208 return;
209 definiteNonZeroTripCountLoops.insert(loopLike);
210 }
211 });
212 }
213
214 // Case 1: hoist a vector.transfer_read and, when present, its matching
215 // vector.transfer_write (a pair); a read with no matching write is hoisted
216 // on its own.
217 root->walk([&](vector::TransferReadOp transferRead) {
218 if (!isa<MemRefType>(transferRead.getShapedType()))
219 return WalkResult::advance();
220
221 LLVM_DEBUG(DBGS() << "Candidate for hoisting: "
222 << *transferRead.getOperation() << "\n");
223 // When masked, the transfer_read is hoisted as its enclosing
224 // `vector.mask` op, so that op is what must sit directly under the loop.
225 Operation *readOrMaskedRead = transferRead;
226 if (auto mask = dyn_cast<vector::MaskOp>(transferRead->getParentOp()))
227 if (mask.getMaskableOp() == transferRead.getOperation())
228 readOrMaskedRead = mask.getOperation();
229 auto loop =
230 dyn_cast<LoopLikeOpInterface>(readOrMaskedRead->getParentOp());
231 LLVM_DEBUG(DBGS() << "Parent op: " << *readOrMaskedRead->getParentOp()
232 << "\n");
233 if (!isa_and_nonnull<scf::ForOp, affine::AffineForOp>(loop))
234 return WalkResult::advance();
235
236 if (verifyNonZeroTrip && !definiteNonZeroTripCountLoops.contains(loop)) {
237 LLVM_DEBUG(DBGS() << "Loop may have zero trip count: " << *loop
238 << "\n");
239 return WalkResult::advance();
240 }
241
242 LLVM_DEBUG(DBGS() << "Candidate read: " << *transferRead.getOperation()
243 << "\n");
244
245 SetVector<Operation *> forwardSlice;
246 getForwardSlice(readOrMaskedRead, &forwardSlice);
247
248 // Look for the last TransferWriteOp in the forwardSlice of
249 // `transferRead` that operates on the same memref. When masked, the write
250 // is the transfer_write nested in its `vector.mask` region, reached as a
251 // user of the read value.
252 vector::TransferWriteOp transferWrite;
253 for (auto *sliceOp : llvm::reverse(forwardSlice)) {
254 auto candidateWrite = dyn_cast<vector::TransferWriteOp>(sliceOp);
255 if (!candidateWrite ||
256 candidateWrite.getBase() != transferRead.getBase())
257 continue;
258 transferWrite = candidateWrite;
259 }
260
261 // All operands of the TransferRead must be defined outside of the loop.
262 for (auto operand : transferRead.getOperands())
263 if (!loop.isDefinedOutsideOfLoop(operand))
264 return WalkResult::advance();
265 // A masked read is hoisted together with the `vector.mask` op wrapping
266 // it, so that op's mask and passthru operands must be loop-invariant too.
267 auto readMask = dyn_cast<vector::MaskOp>(readOrMaskedRead);
268 if (readMask) {
269 if (!loop.isDefinedOutsideOfLoop(readMask.getMask()))
270 return WalkResult::advance();
271 if (readMask.getPassthru() &&
272 !loop.isDefinedOutsideOfLoop(readMask.getPassthru()))
273 return WalkResult::advance();
274 }
275
276 // Only hoist transfer_read / transfer_write pairs and singleton
277 // transfer_reads for now.
278 if (!transferWrite) {
279 // Hoisting a lone read is safe as long as no aliasing write remains in
280 // the loop; other reads never conflict.
281 if (memref::hasNoAliasingAccessInScope(transferRead.getBase(), loop,
282 /*excludedOps=*/{},
283 /*readsAreSafe=*/true))
284 loop.moveOutOfLoop(readOrMaskedRead);
285 return WalkResult::advance();
286 }
287
288 // The write is hoisted with its own `vector.mask` op, if any. A masked
289 // read must pair with a masked write carrying the same mask (and an
290 // unmasked read with an unmasked write).
291 Operation *writeOrMaskedWrite = transferWrite;
292 if (auto mask = dyn_cast<vector::MaskOp>(transferWrite->getParentOp()))
293 if (mask.getMaskableOp() == transferWrite.getOperation())
294 writeOrMaskedWrite = mask.getOperation();
295 auto writeMask = dyn_cast<vector::MaskOp>(writeOrMaskedWrite);
296 if (!!readMask != !!writeMask)
297 return WalkResult::advance();
298 if (readMask && readMask.getMask() != writeMask.getMask())
299 return WalkResult::advance();
300
301 LLVM_DEBUG(DBGS() << "Candidate: " << *transferWrite.getOperation()
302 << "\n");
303
304 // Approximate aliasing by checking that:
305 // 1. indices, vector type and permutation map are the same (i.e., the
306 // transfer_read/transfer_write ops are matching),
307 // 2. source operands for transfer.{read|write} do not originate from
308 // nor have users that are Ops implementing ViewLikeOpInterface.
309 // 3. no other operations in the loop access the same memref except
310 // for transfer_read/transfer_write accessing statically disjoint
311 // slices.
312
313 // Check 1.
314 if (transferRead.getIndices() != transferWrite.getIndices() ||
315 transferRead.getVectorType() != transferWrite.getVectorType() ||
316 transferRead.getPermutationMap() != transferWrite.getPermutationMap())
317 return WalkResult::advance();
318
319 // Check 2. Note, since both xfer Ops share the source, we only need to
320 // look at one of them.
321 auto base = transferRead.getBase();
322 // Whether hoisting is safe despite a view base. Computed lazily and
323 // cached since it is only consulted when a view is present.
324 std::optional<bool> viewAliasingIsSafeCache;
325 auto viewAliasingIsSafe = [&]() {
326 if (!viewAliasingIsSafeCache) {
327 Operation *hoistedPair[] = {transferRead, transferWrite};
328 viewAliasingIsSafeCache =
329 memref::hasNoAliasingAccessInScope(base, loop, hoistedPair);
330 }
331 return *viewAliasingIsSafeCache;
332 };
333 auto *source = base.getDefiningOp();
334 if (source) {
335 // NOTE: We treat `memref.assume_alignment` as a special case.
336 //
337 // The idea is that it is safe to look past AssumeAlignmemtOp (i.e.
338 // MemRef _before_ alignment) iff:
339 // 1. It has exactly two uses (these have to be the xfer Ops
340 // being looked at).
341 // 2. The original MemRef has only one use (i.e.
342 // AssumeAlignmentOp).
343 //
344 // Relaxing these conditions will most likely require proper alias
345 // analysis.
346 if (auto assume = dyn_cast<memref::AssumeAlignmentOp>(source)) {
347 Value memPreAlignment = assume.getMemref();
348 auto numInLoopUses =
349 llvm::count_if(base.getUses(), [&loop](OpOperand &use) {
350 return loop->isAncestor(use.getOwner());
351 });
352
353 if (numInLoopUses && memPreAlignment.hasOneUse())
354 source = memPreAlignment.getDefiningOp();
355 }
356 if (isa_and_nonnull<ViewLikeOpInterface>(source) &&
357 !viewAliasingIsSafe())
358 return WalkResult::advance();
359 }
360
361 if (llvm::any_of(base.getUsers(), llvm::IsaPred<ViewLikeOpInterface>) &&
362 !viewAliasingIsSafe())
363 return WalkResult::advance();
364
365 // Check 3.
366 // TODO: may want to memoize this information for performance but it
367 // likely gets invalidated often.
368 // The read must dominate the write. Compare `readOrMaskedRead` and
369 // `writeOrMaskedWrite`, not the inner transfers: when masked, those live
370 // in separate `vector.mask` regions, so neither dominates the other.
371 DominanceInfo dom(loop);
372 if (!dom.properlyDominates(readOrMaskedRead, writeOrMaskedWrite))
373 return WalkResult::advance();
374 for (auto &use : transferRead.getBase().getUses()) {
375 if (!loop->isAncestor(use.getOwner()))
376 continue;
377 if (use.getOwner() == transferRead.getOperation() ||
378 use.getOwner() == transferWrite.getOperation())
379 continue;
380 if (auto transferWriteUse =
381 dyn_cast<vector::TransferWriteOp>(use.getOwner())) {
383 cast<VectorTransferOpInterface>(*transferWrite),
384 cast<VectorTransferOpInterface>(*transferWriteUse),
385 /*testDynamicValueUsingBounds=*/true))
386 return WalkResult::advance();
387 } else if (auto transferReadUse =
388 dyn_cast<vector::TransferReadOp>(use.getOwner())) {
390 cast<VectorTransferOpInterface>(*transferWrite),
391 cast<VectorTransferOpInterface>(*transferReadUse),
392 /*testDynamicValueUsingBounds=*/true))
393 return WalkResult::advance();
394 } else {
395 // Unknown use, we cannot prove that it doesn't alias with the
396 // transferRead/transferWrite operations.
397 return WalkResult::advance();
398 }
399 }
400
401 // Hoist read before (with its mask op, if masked).
402 loop.moveOutOfLoop(readOrMaskedRead);
403
404 // Hoist write after (with its mask op, if masked).
405 writeOrMaskedWrite->moveAfter(loop);
406
407 // Rewrite `loop` with new yields by cloning and erase the original
408 // loop. The carried value is `readOrMaskedRead`'s result (the masked read
409 // result when masked) and the yielded value is what the write stores.
410 IRRewriter rewriter(transferRead.getContext());
411 NewYieldValuesFn yieldFn = [&](OpBuilder &b, Location loc,
412 ArrayRef<BlockArgument> newBBArgs) {
413 return SmallVector<Value>{transferWrite.getVector()};
414 };
415
416 auto maybeNewLoop = loop.replaceWithAdditionalYields(
417 rewriter, readOrMaskedRead->getResult(0),
418 /*replaceInitOperandUsesInLoop=*/true, yieldFn);
419 if (failed(maybeNewLoop))
420 return WalkResult::interrupt();
421
422 transferWrite.getValueToStoreMutable().assign(
423 maybeNewLoop->getOperation()->getResults().back());
424 changed = true;
425 // Need to interrupt and restart because erasing the loop messes up
426 // the walk.
427 return WalkResult::interrupt();
428 });
429
430 // Case 2: sink a singleton vector.transfer_write. A write whose operands
431 // are loop-invariant and that is the only op touching its memref stores the
432 // same value to the same place every iteration, so it is sunk past the loop
433 // and executed once (if the loop is proven to run at least once).
434 if (changed || !verifyNonZeroTrip)
435 continue;
436 root->walk([&](vector::TransferWriteOp transferWrite) {
437 if (!isa<MemRefType>(transferWrite.getShapedType()))
438 return WalkResult::advance();
439
440 // When masked, the transfer_write is sunk as its enclosing `vector.mask`
441 // op, so that op is what must sit directly under the loop.
442 Operation *writeOrMaskedWrite = transferWrite;
443 if (auto mask = dyn_cast<vector::MaskOp>(transferWrite->getParentOp()))
444 if (mask.getMaskableOp() == transferWrite.getOperation())
445 writeOrMaskedWrite = mask.getOperation();
446 auto loop =
447 dyn_cast<LoopLikeOpInterface>(writeOrMaskedWrite->getParentOp());
448 if (!isa_and_nonnull<scf::ForOp, affine::AffineForOp>(loop) ||
449 !definiteNonZeroTripCountLoops.contains(loop))
450 return WalkResult::advance();
451
452 // All operands of the transfer_write must be defined outside of the loop.
453 for (auto operand : transferWrite.getOperands())
454 if (!loop.isDefinedOutsideOfLoop(operand))
455 return WalkResult::advance();
456 // A masked write is sunk with its `vector.mask` op, so that op's mask
457 // operand must be loop-invariant too.
458 if (auto writeMask = dyn_cast<vector::MaskOp>(writeOrMaskedWrite))
459 if (!loop.isDefinedOutsideOfLoop(writeMask.getMask()))
460 return WalkResult::advance();
461
462 // Sinking is only safe if nothing else in the loop accesses the memref:
463 // any other read or write could observe an intermediate state.
464 Operation *sunkWrite[] = {transferWrite};
465 if (!memref::hasNoAliasingAccessInScope(transferWrite.getBase(), loop,
466 sunkWrite,
467 /*readsAreSafe=*/false))
468 return WalkResult::advance();
469
470 writeOrMaskedWrite->moveAfter(loop);
471 changed = true;
472 // Need to interrupt and restart because moving the write messes up the
473 // walk.
474 return WalkResult::interrupt();
475 });
476 }
477}
static scf::ForOp replaceWithDifferentYield(RewriterBase &rewriter, scf::ForOp loop, Value newInitOperand, unsigned index, Value newYieldValue)
Replace loop with a new loop that has a different init operand at position index.
Definition Hoisting.cpp:45
#define DBGS()
Definition Hoisting.cpp:34
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
static Value broadcast(Location loc, Value toBroadcast, unsigned numElements, const TypeConverter &typeConverter, ConversionPatternRewriter &rewriter)
Broadcasts the value to vector with numElements number of elements.
A class for computing basic dominance information.
Definition Dominance.h:143
bool properlyDominates(Operation *a, Operation *b, bool enclosingOpOk=true) const
Return true if operation A properly dominates operation B, i.e.
IRValueT get() const
Return the current value being used by this operand.
This class coordinates rewriting a piece of IR outside of a pattern rewrite, providing a way to keep ...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
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
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:401
This class represents an operand of an operation.
Definition Value.h:254
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:251
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
Definition Operation.h:849
void moveAfter(Operation *existingOp)
Unlink this operation from its current block and insert it right after existingOp which may be in the...
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...
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.
void moveOpAfter(Operation *op, Operation *existingOp)
Unlink this operation from its current block and insert it right after existingOp which may be in the...
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...
Definition Types.h:74
static FailureOr< int64_t > computeConstantBound(presburger::BoundType type, const Variable &var, const StopConditionFn &stopCondition=nullptr, ValueBoundsOptions options={})
Compute a constant bound for the given variable.
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
bool hasOneUse() const
Returns true if this value has exactly one use.
Definition Value.h:197
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
static WalkResult advance()
Definition WalkResult.h:47
static WalkResult interrupt()
Definition WalkResult.h:46
void hoistRedundantVectorBroadcasts(RewriterBase &rewriter, Operation *root)
Hoist vector.extract/vector.broadcast pairs out of immediately enclosing scf::ForOp iteratively,...
Definition Hoisting.cpp:91
void hoistRedundantVectorTransfers(Operation *root, bool verifyNonZeroTrip=false)
Hoist vector.transfer_read/vector.transfer_write on buffers pairs out of immediately enclosing scf::F...
Definition Hoisting.cpp:166
bool hasNoAliasingAccessInScope(Value base, Operation *scope, ArrayRef< Operation * > excludedOps={}, bool readsAreSafe=false)
Return "true" when no other access nested in scope can alias base.
bool isDisjointTransferSet(VectorTransferOpInterface transferA, VectorTransferOpInterface transferB, bool testDynamicValueUsingBounds=false)
Return true if we can prove that the transfer operations access disjoint memory, requiring the operat...
Include the generated interface declarations.
std::function< SmallVector< Value >( OpBuilder &b, Location loc, ArrayRef< BlockArgument > newBbArgs)> NewYieldValuesFn
A function that returns the additional yielded values during replaceWithAdditionalYields.
llvm::SetVector< T, Vector, Set, N > SetVector
Definition LLVM.h:128
size_t moveLoopInvariantCode(ArrayRef< Region * > regions, function_ref< bool(Value, Region *)> isDefinedOutsideRegion, function_ref< bool(Operation *, Region *)> shouldMoveOutOfRegion, function_ref< void(Operation *, Region *)> moveOutOfRegion)
Given a list of regions, perform loop-invariant code motion.
void getForwardSlice(Operation *op, SetVector< Operation * > *forwardSlice, const ForwardSliceOptions &options={})
Fills forwardSlice with the computed forward slice (i.e.
Options that control value bound computation.