20 #include "llvm/Support/Debug.h"
23 #define DEBUG_TYPE "licm"
41 for (
OpOperand &operand : child->getOpOperands()) {
43 if (op->
isAncestor(operand.get().getParentRegion()->getParentOp()))
45 if (!condition(operand))
50 return !op->
walk(walkFn).wasInterrupted();
56 op, [&](
OpOperand &operand) {
return definedOutside(operand.
get()); });
66 for (
Region *region : regions) {
67 LLVM_DEBUG(llvm::dbgs() <<
"Original loop:\n"
68 << *region->getParentOp() <<
"\n");
70 std::queue<Operation *> worklist;
75 auto definedOutside = [&](
Value value) {
76 return isDefinedOutsideRegion(value, region);
79 while (!worklist.empty()) {
86 LLVM_DEBUG(llvm::dbgs() <<
"Checking op: " << *op <<
"\n");
87 if (!shouldMoveOutOfRegion(op, region) ||
91 LLVM_DEBUG(llvm::dbgs() <<
"Moving loop-invariant op: " << *op <<
"\n");
92 moveOutOfRegion(op, region);
98 if (user->getParentRegion() == region)
108 loopLike.getLoopRegions(),
110 return loopLike.isDefinedOutsideOfLoop(value);
113 return isMemoryEffectFree(op) && isSpeculatable(op);
120 class MatchingSubsets {
123 void insert(SubsetOpInterface op,
bool collectHoistableOps =
true) {
124 allSubsetOps.push_back(op);
125 if (!collectHoistableOps)
127 if (
auto extractionOp =
128 dyn_cast<SubsetExtractionOpInterface>(op.getOperation()))
129 insertExtractionOp(extractionOp);
130 if (
auto insertionOp =
131 dyn_cast<SubsetInsertionOpInterface>(op.getOperation()))
132 insertInsertionOp(insertionOp);
139 auto getHoistableSubsetOps() {
140 return llvm::make_filter_range(
141 llvm::zip(extractions, insertions), [&](
auto pair) {
142 auto [extractionOp, insertionOp] = pair;
144 if (extractionOp && insertionOp &&
145 extractionOp->getResult(0).getType() !=
146 insertionOp.getSourceOperand().get().getType())
149 return allDisjoint(extractionOp, insertionOp);
158 LogicalResult populateSubsetOpsAtIterArg(LoopLikeOpInterface loopLike,
160 bool collectHoistableOps =
true);
166 static bool isEquivalent(
Value v1,
Value v2) {
return true; }
171 bool allDisjoint(SubsetExtractionOpInterface extractionOp,
172 SubsetInsertionOpInterface insertionOp)
const {
173 for (SubsetOpInterface other : allSubsetOps) {
174 if (other == extractionOp || other == insertionOp)
177 !other.operatesOnDisjointSubset(extractionOp, isEquivalent))
180 !other.operatesOnDisjointSubset(insertionOp, isEquivalent))
189 void insertExtractionOp(SubsetExtractionOpInterface extractionOp) {
193 auto other = cast<SubsetOpInterface>(it.value().getOperation());
194 if (other.operatesOnEquivalentSubset(extractionOp, isEquivalent)) {
195 extractions[it.index()] = extractionOp;
200 extractions.push_back(extractionOp);
201 insertions.push_back({});
207 void insertInsertionOp(SubsetInsertionOpInterface insertionOp) {
211 auto other = cast<SubsetOpInterface>(it.value().getOperation());
212 if (other.operatesOnEquivalentSubset(insertionOp, isEquivalent)) {
213 insertions[it.index()] = insertionOp;
218 extractions.push_back({});
219 insertions.push_back(insertionOp);
240 MatchingSubsets::populateSubsetOpsAtIterArg(LoopLikeOpInterface loopLike,
242 bool collectHoistableOps) {
244 Value value = iterArg;
254 Value nextValue = {};
257 if (
auto nestedLoop = dyn_cast<LoopLikeOpInterface>(use.getOwner())) {
262 auto nestedIterArg = nestedLoop.getTiedLoopRegionIterArg(&use);
268 if (failed(populateSubsetOpsAtIterArg(nestedLoop, nestedIterArg,
271 nextValue = nestedLoop.getTiedLoopResult(&use);
275 auto subsetOp = dyn_cast<SubsetOpInterface>(use.getOwner());
280 if (
auto insertionOp =
281 dyn_cast<SubsetInsertionOpInterface>(use.getOwner())) {
285 if (!isa<DestinationStyleOpInterface>(use.getOwner())) {
291 if (&use != &insertionOp.getDestinationOperand())
297 nextValue = insertionOp.getUpdatedDestination();
311 if (loopLike.getTiedLoopYieldedValue(iterArg) != yieldedOperand)
322 LoopLikeOpInterface loopLike,
325 auto it = llvm::find(loopLike.getRegionIterArgs(), iterArg);
326 int64_t iterArgIdx = std::distance(loopLike.getRegionIterArgs().begin(), it);
327 MatchingSubsets subsets;
328 if (failed(subsets.populateSubsetOpsAtIterArg(loopLike, iterArg)))
332 for (
auto it : subsets.getHoistableSubsetOps()) {
333 auto extractionOp = std::get<0>(it);
334 auto insertionOp = std::get<1>(it);
339 return loopLike.isDefinedOutsideOfLoop(operand.
get()) ||
340 &operand == &extractionOp.getSourceOperand();
346 return loopLike.isDefinedOutsideOfLoop(operand.
get()) ||
347 &operand == &insertionOp.getSourceOperand() ||
348 &operand == &insertionOp.getDestinationOperand();
356 if (extractionOp && insertionOp) {
361 return {insertionOp.getSourceOperand().get()};
363 FailureOr<LoopLikeOpInterface> newLoop =
364 loopLike.replaceWithAdditionalYields(
365 rewriter, extractionOp.getResult(),
366 true, newYieldValuesFn);
372 iterArg = loopLike.getRegionIterArgs()[iterArgIdx];
373 OpResult loopResult = loopLike.getTiedLoopResult(iterArg);
374 OpResult newLoopResult = loopLike.getLoopResults()->back();
378 insertionOp.getDestinationOperand().get());
379 extractionOp.getSourceOperand().set(
380 loopLike.getTiedLoopInit(iterArg)->get());
382 insertionOp.getUpdatedDestination());
383 insertionOp.getSourceOperand().set(newLoopResult);
384 insertionOp.getDestinationOperand().set(loopResult);
393 LoopLikeOpInterface loopLike) {
398 i < static_cast<int64_t>(loopLike.getRegionIterArgs().size()); ++i) {
400 loopLike.getRegionIterArgs()[i]);
static LoopLikeOpInterface hoistSubsetAtIterArg(RewriterBase &rewriter, LoopLikeOpInterface loopLike, BlockArgument iterArg)
Hoist all subset ops that operate on the idx-th region iter_arg of the given loop-like op and index i...
static OpOperand * getSingleTerminatorUse(Value value)
If the given value has a single use by an op that is a terminator, return that use.
static bool canBeHoisted(Operation *op, function_ref< bool(OpOperand &)> condition)
Checks whether the given op can be hoisted by checking that.
This class represents an argument of a Block.
Block * getOwner() const
Returns the block that owns this argument.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
IRValueT get() const
Return the current value being used by this operand.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class helps build Operations.
This class represents an operand of an operation.
This is a value defined by a result of an operation.
This class provides the API for ops that are known to be terminators.
Operation is the basic unit of execution within MLIR.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
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),...
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
user_range getUsers()
Returns a range of all users.
Region * getParentRegion()
Returns the region to which the instruction belongs.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
void moveOpBefore(Operation *op, Operation *existingOp)
Unlink this operation from its current block and insert it right before existingOp which may be in th...
void moveOpAfter(Operation *op, Operation *existingOp)
Unlink this operation from its current block and insert it right after existingOp which may be in the...
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
bool hasOneUse() const
Returns true if this value has exactly one use.
static WalkResult advance()
static WalkResult interrupt()
Operation * getOwner() const
Return the owner of this operand.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Include the generated interface declarations.
LoopLikeOpInterface hoistLoopInvariantSubsets(RewriterBase &rewriter, LoopLikeOpInterface loopLike)
Hoist loop-invariant tensor subsets (subset extraction and subset insertion ops) from loop-like ops.
std::function< SmallVector< Value >(OpBuilder &b, Location loc, ArrayRef< BlockArgument > newBbArgs)> NewYieldValuesFn
A function that returns the additional yielded values during replaceWithAdditionalYields.
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.