MLIR  22.0.0git
EliminateBarriers.cpp
Go to the documentation of this file.
1 //===- EliminateBarriers.cpp - Eliminate extra barriers --===//
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 // Barrier elimination pattern and pass. If a barrier does not enforce any
10 // conflicting pair of memory effects, including a pair that is enforced by
11 // another barrier, it is unnecessary and can be removed. Adapted from
12 // "High-Performance GPU-to-CPU Transpilation and Optimization via High-Level
13 // Parallel Constructs" by Moses, Ivanov, Domke, Endo, Doerfert, and Zinenko in
14 // PPoPP 2023 and implementation in Polygeist.
15 //
16 //===----------------------------------------------------------------------===//
17 
24 #include "mlir/IR/Operation.h"
26 #include "llvm/ADT/TypeSwitch.h"
27 #include "llvm/Support/Debug.h"
28 
29 namespace mlir {
30 #define GEN_PASS_DEF_GPUELIMINATEBARRIERS
31 #include "mlir/Dialect/GPU/Transforms/Passes.h.inc"
32 } // namespace mlir
33 
34 using namespace mlir;
35 using namespace mlir::gpu;
36 
37 #define DEBUG_TYPE "gpu-erase-barriers"
38 #define DEBUG_TYPE_ALIAS "gpu-erase-barries-alias"
39 
40 #define DBGS() (llvm::dbgs() << '[' << DEBUG_TYPE << "] ")
41 #define DBGS_ALIAS() (llvm::dbgs() << '[' << DEBUG_TYPE_ALIAS << "] ")
42 
43 // The functions below provide interface-like verification, but are too specific
44 // to barrier elimination to become interfaces.
45 
46 /// Returns `true` if the op is defines the parallel region that is subject to
47 /// barrier synchronization.
49  if (op->hasAttr("__parallel_region_boundary_for_test"))
50  return true;
51 
52  return isa<GPUFuncOp, LaunchOp>(op);
53 }
54 
55 /// Returns `true` if the op behaves like a sequential loop, e.g., the control
56 /// flow "wraps around" from the end of the body region back to its start.
57 static bool isSequentialLoopLike(Operation *op) { return isa<scf::ForOp>(op); }
58 
59 /// Returns `true` if the regions of the op are guaranteed to be executed at
60 /// most once. Thus, if an operation in one of the nested regions of `op` is
61 /// executed than so are all the other operations in this region.
63  return isa<FunctionOpInterface, scf::IfOp, memref::AllocaScopeOp>(op);
64 }
65 
66 /// Returns `true` if the operation is known to produce a pointer-like object
67 /// distinct from any other object produced by a similar operation. For example,
68 /// an allocation produces such an object.
69 static bool producesDistinctBase(Operation *op) {
70  return isa_and_nonnull<memref::AllocOp, memref::AllocaOp>(op);
71 }
72 
73 /// Populates `effects` with all memory effects without associating them to a
74 /// specific value.
77  effects.emplace_back(MemoryEffects::Effect::get<MemoryEffects::Read>());
78  effects.emplace_back(MemoryEffects::Effect::get<MemoryEffects::Write>());
79  effects.emplace_back(MemoryEffects::Effect::get<MemoryEffects::Allocate>());
80  effects.emplace_back(MemoryEffects::Effect::get<MemoryEffects::Free>());
81 }
82 
83 /// Collect the memory effects of the given op in 'effects'. Returns 'true' if
84 /// it could extract the effect information from the op, otherwise returns
85 /// 'false' and conservatively populates the list with all possible effects
86 /// associated with no particular value or symbol.
87 static bool
90  bool ignoreBarriers = true) {
91  // Skip over barriers to avoid infinite recursion (those barriers would ask
92  // this barrier again).
93  if (ignoreBarriers && isa<BarrierOp>(op))
94  return true;
95 
96  // Collect effect instances the operation. Note that the implementation of
97  // getEffects erases all effect instances that have the type other than the
98  // template parameter so we collect them first in a local buffer and then
99  // copy.
100  if (auto iface = dyn_cast<MemoryEffectOpInterface>(op)) {
102  iface.getEffects(localEffects);
103  llvm::append_range(effects, localEffects);
104  return true;
105  }
107  for (auto &region : op->getRegions()) {
108  for (auto &block : region) {
109  for (auto &innerOp : block)
110  if (!collectEffects(&innerOp, effects, ignoreBarriers))
111  return false;
112  }
113  }
114  return true;
115  }
116 
117  // We need to be conservative here in case the op doesn't have the interface
118  // and assume it can have any possible effect.
119  addAllValuelessEffects(effects);
120  return false;
121 }
122 
123 /// Get all effects before the given operation caused by other operations in the
124 /// same block. That is, this will not consider operations beyond the block.
125 static bool
128  bool stopAtBarrier) {
129  if (op == &op->getBlock()->front())
130  return true;
131 
132  for (Operation *it = op->getPrevNode(); it != nullptr;
133  it = it->getPrevNode()) {
134  if (isa<BarrierOp>(it)) {
135  if (stopAtBarrier)
136  return true;
137  continue;
138  }
139 
140  if (!collectEffects(it, effects))
141  return false;
142  }
143  return true;
144 }
145 
146 /// Collects memory effects from operations that may be executed before `op` in
147 /// a trivial structured control flow, e.g., without branches. Stops at the
148 /// parallel region boundary or at the barrier operation if `stopAtBarrier` is
149 /// set. Returns `true` if the memory effects added to `effects` are exact,
150 /// `false` if they are a conservative over-approximation. The latter means that
151 /// `effects` contain instances not associated with a specific value.
152 static bool
155  bool stopAtBarrier) {
156  if (!op->getBlock())
157  return true;
158 
159  // If there is a non-structured control flow, bail.
160  Region *region = op->getBlock()->getParent();
161  if (region && !region->hasOneBlock()) {
162  addAllValuelessEffects(effects);
163  return false;
164  }
165 
166  // Collect all effects before the op.
167  getEffectsBeforeInBlock(op, effects, stopAtBarrier);
168 
169  // Stop if reached the parallel region boundary.
171  return true;
172 
173  Operation *parent = op->getParentOp();
174  // Otherwise, keep collecting above the parent operation.
175  if (!parent->hasTrait<OpTrait::IsIsolatedFromAbove>() &&
176  !getEffectsBefore(parent, effects, stopAtBarrier))
177  return false;
178 
179  // If the op is loop-like, collect effects from the trailing operations until
180  // we hit a barrier because they can executed before the current operation by
181  // the previous iteration of this loop. For example, in the following loop
182  //
183  // for i = ... {
184  // op1
185  // ...
186  // barrier
187  // op2
188  // }
189  //
190  // the operation `op2` at iteration `i` is known to be executed before the
191  // operation `op1` at iteration `i+1` and the side effects must be ordered
192  // appropriately.
193  if (isSequentialLoopLike(parent)) {
194  // Assuming loop terminators have no side effects.
195  return getEffectsBeforeInBlock(op->getBlock()->getTerminator(), effects,
196  /*stopAtBarrier=*/true);
197  }
198 
199  // If the parent operation is not guaranteed to execute its (single-block)
200  // region once, walk the block.
201  bool conservative = false;
203  op->getParentOp()->walk([&](Operation *in) {
204  if (conservative)
205  return WalkResult::interrupt();
206  if (!collectEffects(in, effects)) {
207  conservative = true;
208  return WalkResult::interrupt();
209  }
210  return WalkResult::advance();
211  });
212 
213  return !conservative;
214 }
215 
216 /// Get all effects after the given operation caused by other operations in the
217 /// same block. That is, this will not consider operations beyond the block.
218 static bool
221  bool stopAtBarrier) {
222  if (op == &op->getBlock()->back())
223  return true;
224 
225  for (Operation *it = op->getNextNode(); it != nullptr;
226  it = it->getNextNode()) {
227  if (isa<BarrierOp>(it)) {
228  if (stopAtBarrier)
229  return true;
230  continue;
231  }
232  if (!collectEffects(it, effects))
233  return false;
234  }
235  return true;
236 }
237 
238 /// Collects memory effects from operations that may be executed after `op` in
239 /// a trivial structured control flow, e.g., without branches. Stops at the
240 /// parallel region boundary or at the barrier operation if `stopAtBarrier` is
241 /// set. Returns `true` if the memory effects added to `effects` are exact,
242 /// `false` if they are a conservative over-approximation. The latter means that
243 /// `effects` contain instances not associated with a specific value.
244 static bool
247  bool stopAtBarrier) {
248  if (!op->getBlock())
249  return true;
250 
251  // If there is a non-structured control flow, bail.
252  Region *region = op->getBlock()->getParent();
253  if (region && !region->hasOneBlock()) {
254  addAllValuelessEffects(effects);
255  return false;
256  }
257 
258  // Collect all effects after the op.
259  getEffectsAfterInBlock(op, effects, stopAtBarrier);
260 
261  Operation *parent = op->getParentOp();
262  // Stop if reached the parallel region boundary.
263  if (isParallelRegionBoundary(parent))
264  return true;
265 
266  // Otherwise, keep collecting below the parent operation.
267  // Don't look into, for example, neighboring functions
268  if (!parent->hasTrait<OpTrait::IsIsolatedFromAbove>() &&
269  !getEffectsAfter(parent, effects, stopAtBarrier))
270  return false;
271 
272  // If the op is loop-like, collect effects from the leading operations until
273  // we hit a barrier because they can executed after the current operation by
274  // the next iteration of this loop. For example, in the following loop
275  //
276  // for i = ... {
277  // op1
278  // ...
279  // barrier
280  // op2
281  // }
282  //
283  // the operation `op1` at iteration `i` is known to be executed after the
284  // operation `op2` at iteration `i-1` and the side effects must be ordered
285  // appropriately.
286  if (isSequentialLoopLike(parent)) {
287  if (isa<BarrierOp>(op->getBlock()->front()))
288  return true;
289 
290  bool exact = collectEffects(&op->getBlock()->front(), effects);
291  return getEffectsAfterInBlock(&op->getBlock()->front(), effects,
292  /*stopAtBarrier=*/true) &&
293  exact;
294  }
295 
296  // If the parent operation is not guaranteed to execute its (single-block)
297  // region once, walk the block.
298  bool conservative = false;
300  op->getParentOp()->walk([&](Operation *in) {
301  if (conservative)
302  return WalkResult::interrupt();
303  if (!collectEffects(in, effects)) {
304  conservative = true;
305  return WalkResult::interrupt();
306  }
307  return WalkResult::advance();
308  });
309 
310  return !conservative;
311 }
312 
313 /// Looks through known "view-like" ops to find the base memref.
314 static Value getBase(Value v) {
315  while (true) {
316  Operation *definingOp = v.getDefiningOp();
317  if (!definingOp)
318  break;
319 
320  bool shouldContinue =
322  .Case<memref::CastOp, memref::SubViewOp, memref::ViewOp>(
323  [&](auto op) {
324  v = op.getSource();
325  return true;
326  })
327  .Case<memref::TransposeOp>([&](auto op) {
328  v = op.getIn();
329  return true;
330  })
331  .Case<memref::CollapseShapeOp, memref::ExpandShapeOp>([&](auto op) {
332  v = op.getSrc();
333  return true;
334  })
335  .Default([](Operation *) { return false; });
336  if (!shouldContinue)
337  break;
338  }
339  return v;
340 }
341 
342 /// Returns `true` if the value is defined as a function argument.
343 static bool isFunctionArgument(Value v) {
344  auto arg = dyn_cast<BlockArgument>(v);
345  return arg && isa<FunctionOpInterface>(arg.getOwner()->getParentOp());
346 }
347 
348 /// Returns the operand that the operation "propagates" through it for capture
349 /// purposes. That is, if the value produced by this operation is captured, then
350 /// so is the returned value.
353  .Case(
354  [](ViewLikeOpInterface viewLike) { return viewLike.getViewSource(); })
355  .Case([](CastOpInterface castLike) { return castLike->getOperand(0); })
356  .Case([](memref::TransposeOp transpose) { return transpose.getIn(); })
357  .Case<memref::ExpandShapeOp, memref::CollapseShapeOp>(
358  [](auto op) { return op.getSrc(); })
359  .Default([](Operation *) { return Value(); });
360 }
361 
362 /// Returns `true` if the given operation is known to capture the given value,
363 /// `false` if it is known not to capture the given value, `nullopt` if neither
364 /// is known.
365 static std::optional<bool> getKnownCapturingStatus(Operation *op, Value v) {
367  // Store-like operations don't capture the destination, but do capture
368  // the value.
369  .Case<memref::StoreOp, vector::TransferWriteOp>(
370  [&](auto op) { return op.getValue() == v; })
371  .Case<vector::StoreOp, vector::MaskedStoreOp>(
372  [&](auto op) { return op.getValueToStore() == v; })
373  // These operations are known not to capture.
374  .Case([](memref::DeallocOp) { return false; })
375  // By default, we don't know anything.
376  .Default([](Operation *) { return std::nullopt; });
377 }
378 
379 /// Returns `true` if the value may be captured by any of its users, i.e., if
380 /// the user may be storing this value into memory. This makes aliasing analysis
381 /// more conservative as it cannot assume the pointer-like value is only passed
382 /// around through SSA use-def.
383 static bool maybeCaptured(Value v) {
384  SmallVector<Value> todo = {v};
385  while (!todo.empty()) {
386  Value v = todo.pop_back_val();
387  for (Operation *user : v.getUsers()) {
388  // A user that is known to only read cannot capture.
389  auto iface = dyn_cast<MemoryEffectOpInterface>(user);
390  if (iface) {
392  iface.getEffects(effects);
393  if (llvm::all_of(effects,
394  [](const MemoryEffects::EffectInstance &effect) {
395  return isa<MemoryEffects::Read>(effect.getEffect());
396  })) {
397  continue;
398  }
399  }
400 
401  // When an operation is known to create an alias, consider if the
402  // source is captured as well.
403  if (Value v = propagatesCapture(user)) {
404  todo.push_back(v);
405  continue;
406  }
407 
408  std::optional<bool> knownCaptureStatus = getKnownCapturingStatus(user, v);
409  if (!knownCaptureStatus || *knownCaptureStatus)
410  return true;
411  }
412  }
413 
414  return false;
415 }
416 
417 /// Returns true if two values may be referencing aliasing memory. This is a
418 /// rather naive and conservative analysis. Values defined by different
419 /// allocation-like operations as well as values derived from those by casts and
420 /// views cannot alias each other. Similarly, values defined by allocations
421 /// inside a function cannot alias function arguments. Global values cannot
422 /// alias each other or local allocations. Values that are captured, i.e.
423 /// themselves potentially stored in memory, are considered as aliasing with
424 /// everything. This seems sufficient to achieve barrier removal in structured
425 /// control flow, more complex cases would require a proper dataflow analysis.
426 static bool mayAlias(Value first, Value second) {
427  DEBUG_WITH_TYPE(DEBUG_TYPE_ALIAS, {
428  DBGS_ALIAS() << "checking aliasing between ";
429  DBGS_ALIAS() << first << "\n";
430  DBGS_ALIAS() << " and ";
431  DBGS_ALIAS() << second << "\n";
432  });
433 
434  first = getBase(first);
435  second = getBase(second);
436 
437  DEBUG_WITH_TYPE(DEBUG_TYPE_ALIAS, {
438  DBGS_ALIAS() << "base ";
439  DBGS_ALIAS() << first << "\n";
440  DBGS_ALIAS() << " and ";
441  DBGS_ALIAS() << second << "\n";
442  });
443 
444  // Values derived from the same base memref do alias (unless we do a more
445  // advanced analysis to prove non-overlapping accesses).
446  if (first == second) {
447  DEBUG_WITH_TYPE(DEBUG_TYPE_ALIAS, DBGS_ALIAS() << "-> do alias!\n");
448  return true;
449  }
450 
451  // Different globals cannot alias.
452  if (auto globFirst = first.getDefiningOp<memref::GetGlobalOp>()) {
453  if (auto globSecond = second.getDefiningOp<memref::GetGlobalOp>()) {
454  return globFirst.getNameAttr() == globSecond.getNameAttr();
455  }
456  }
457 
458  // Two function arguments marked as noalias do not alias.
459  auto isNoaliasFuncArgument = [](Value value) {
460  auto bbArg = dyn_cast<BlockArgument>(value);
461  if (!bbArg)
462  return false;
463  auto iface = dyn_cast<FunctionOpInterface>(bbArg.getOwner()->getParentOp());
464  if (!iface)
465  return false;
466  // TODO: we need a way to not depend on the LLVM dialect here.
467  return iface.getArgAttr(bbArg.getArgNumber(), "llvm.noalias") != nullptr;
468  };
469  if (isNoaliasFuncArgument(first) && isNoaliasFuncArgument(second))
470  return false;
471 
472  bool isDistinct[] = {producesDistinctBase(first.getDefiningOp()),
474  bool isGlobal[] = {first.getDefiningOp<memref::GetGlobalOp>() != nullptr,
475  second.getDefiningOp<memref::GetGlobalOp>() != nullptr};
476 
477  // Non-equivalent distinct bases and globals cannot alias. At this point, we
478  // have already filtered out based on values being equal and global name being
479  // equal.
480  if ((isDistinct[0] || isGlobal[0]) && (isDistinct[1] || isGlobal[1]))
481  return false;
482 
483  bool isArg[] = {isFunctionArgument(first), isFunctionArgument(second)};
484 
485  // Distinct bases (allocations) cannot have been passed as an argument.
486  if ((isDistinct[0] && isArg[1]) || (isDistinct[1] && isArg[0]))
487  return false;
488 
489  // Non-captured base distinct values cannot conflict with another base value.
490  if (isDistinct[0] && !maybeCaptured(first))
491  return false;
492  if (isDistinct[1] && !maybeCaptured(second))
493  return false;
494 
495  // Otherwise, conservatively assume aliasing.
496  DEBUG_WITH_TYPE(DEBUG_TYPE_ALIAS, DBGS_ALIAS() << "-> may alias!\n");
497  return true;
498 }
499 
500 /// Returns `true` if the effect may be affecting memory aliasing the value. If
501 /// the effect is not associated with any value, it is assumed to affect all
502 /// memory and therefore aliases with everything.
504  if (Value v = a.getValue()) {
505  return mayAlias(v, v2);
506  }
507  return true;
508 }
509 
510 /// Returns `true` if the two effects may be affecting aliasing memory. If
511 /// an effect is not associated with any value, it is assumed to affect all
512 /// memory and therefore aliases with everything. Effects on different resources
513 /// cannot alias.
516  if (a.getResource()->getResourceID() != b.getResource()->getResourceID())
517  return false;
518  if (Value v2 = b.getValue()) {
519  return mayAlias(a, v2);
520  } else if (Value v = a.getValue()) {
521  return mayAlias(b, v);
522  }
523  return true;
524 }
525 
526 /// Returns `true` if any of the "before" effect instances has a conflict with
527 /// any "after" instance for the purpose of barrier elimination. The effects are
528 /// supposed to be limited to a barrier synchronization scope. A conflict exists
529 /// if effects instances affect aliasing memory locations and at least on of
530 /// then as a write. As an exception, if the non-write effect is an allocation
531 /// effect, there is no conflict since we are only expected to see the
532 /// allocation happening in the same thread and it cannot be accessed from
533 /// another thread without capture (which we do handle in alias analysis).
534 static bool
537  for (const MemoryEffects::EffectInstance &before : beforeEffects) {
538  for (const MemoryEffects::EffectInstance &after : afterEffects) {
539  // If cannot alias, definitely no conflict.
540  if (!mayAlias(before, after))
541  continue;
542 
543  // Read/read is not a conflict.
544  if (isa<MemoryEffects::Read>(before.getEffect()) &&
545  isa<MemoryEffects::Read>(after.getEffect())) {
546  continue;
547  }
548 
549  // Allocate/* is not a conflict since the allocation happens within the
550  // thread context.
551  // TODO: This is not the case for */Free unless the allocation happened in
552  // the thread context, which we could also check for.
553  if (isa<MemoryEffects::Allocate>(before.getEffect()) ||
554  isa<MemoryEffects::Allocate>(after.getEffect())) {
555  continue;
556  }
557 
558  // In the particular case that the before effect is a free, we only have 2
559  // possibilities:
560  // 1. either the program is well-formed and there must be an interleaved
561  // alloc that must limit the scope of effect lookback and we can
562  // safely ignore the free -> read / free -> write and free -> free
563  // conflicts.
564  // 2. either the program is ill-formed and we are in undefined behavior
565  // territory.
566  if (isa<MemoryEffects::Free>(before.getEffect()))
567  continue;
568 
569  // Other kinds of effects create a conflict, e.g. read-after-write.
570  LLVM_DEBUG(
571  DBGS() << "found a conflict between (before): " << before.getValue()
572  << " read:" << isa<MemoryEffects::Read>(before.getEffect())
573  << " write:" << isa<MemoryEffects::Write>(before.getEffect())
574  << " alloc:"
575  << isa<MemoryEffects::Allocate>(before.getEffect()) << " free:"
576  << isa<MemoryEffects::Free>(before.getEffect()) << "\n");
577  LLVM_DEBUG(
578  DBGS() << "and (after): " << after.getValue()
579  << " read:" << isa<MemoryEffects::Read>(after.getEffect())
580  << " write:" << isa<MemoryEffects::Write>(after.getEffect())
581  << " alloc:" << isa<MemoryEffects::Allocate>(after.getEffect())
582  << " free:" << isa<MemoryEffects::Free>(after.getEffect())
583  << "\n");
584  return true;
585  }
586  }
587 
588  return false;
589 }
590 
591 namespace {
592 class BarrierElimination final : public OpRewritePattern<BarrierOp> {
593 public:
595 
596  LogicalResult matchAndRewrite(BarrierOp barrier,
597  PatternRewriter &rewriter) const override {
598  LLVM_DEBUG(DBGS() << "checking the necessity of: " << barrier << " "
599  << barrier.getLoc() << "\n");
600 
602  getEffectsBefore(barrier, beforeEffects, /*stopAtBarrier=*/true);
603 
605  getEffectsAfter(barrier, afterEffects, /*stopAtBarrier=*/true);
606 
607  if (!haveConflictingEffects(beforeEffects, afterEffects)) {
608  LLVM_DEBUG(DBGS() << "the surrounding barriers are sufficient, removing "
609  << barrier << "\n");
610  rewriter.eraseOp(barrier);
611  return success();
612  }
613 
614  LLVM_DEBUG(DBGS() << "barrier is necessary: " << barrier << " "
615  << barrier.getLoc() << "\n");
616  return failure();
617  }
618 };
619 
620 class GpuEliminateBarriersPass
621  : public impl::GpuEliminateBarriersBase<GpuEliminateBarriersPass> {
622  void runOnOperation() override {
623  auto funcOp = getOperation();
626  if (failed(applyPatternsGreedily(funcOp, std::move(patterns)))) {
627  return signalPassFailure();
628  }
629  }
630 };
631 
632 } // namespace
633 
635  patterns.insert<BarrierElimination>(patterns.getContext());
636 }
static bool isSequentialLoopLike(Operation *op)
Returns true if the op behaves like a sequential loop, e.g., the control flow "wraps around" from the...
static bool isFunctionArgument(Value v)
Returns true if the value is defined as a function argument.
static Value getBase(Value v)
Looks through known "view-like" ops to find the base memref.
static Value propagatesCapture(Operation *op)
Returns the operand that the operation "propagates" through it for capture purposes.
static bool hasSingleExecutionBody(Operation *op)
Returns true if the regions of the op are guaranteed to be executed at most once.
static bool producesDistinctBase(Operation *op)
Returns true if the operation is known to produce a pointer-like object distinct from any other objec...
static bool mayAlias(Value first, Value second)
Returns true if two values may be referencing aliasing memory.
static bool getEffectsBeforeInBlock(Operation *op, SmallVectorImpl< MemoryEffects::EffectInstance > &effects, bool stopAtBarrier)
Get all effects before the given operation caused by other operations in the same block.
static bool isParallelRegionBoundary(Operation *op)
Returns true if the op is defines the parallel region that is subject to barrier synchronization.
#define DEBUG_TYPE_ALIAS
static bool getEffectsAfter(Operation *op, SmallVectorImpl< MemoryEffects::EffectInstance > &effects, bool stopAtBarrier)
Collects memory effects from operations that may be executed after op in a trivial structured control...
static std::optional< bool > getKnownCapturingStatus(Operation *op, Value v)
Returns true if the given operation is known to capture the given value, false if it is known not to ...
static bool collectEffects(Operation *op, SmallVectorImpl< MemoryEffects::EffectInstance > &effects, bool ignoreBarriers=true)
Collect the memory effects of the given op in 'effects'.
#define DBGS_ALIAS()
static bool haveConflictingEffects(ArrayRef< MemoryEffects::EffectInstance > beforeEffects, ArrayRef< MemoryEffects::EffectInstance > afterEffects)
Returns true if any of the "before" effect instances has a conflict with any "after" instance for the...
#define DBGS()
static bool getEffectsAfterInBlock(Operation *op, SmallVectorImpl< MemoryEffects::EffectInstance > &effects, bool stopAtBarrier)
Get all effects after the given operation caused by other operations in the same block.
static void addAllValuelessEffects(SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
Populates effects with all memory effects without associating them to a specific value.
static bool maybeCaptured(Value v)
Returns true if the value may be captured by any of its users, i.e., if the user may be storing this ...
static bool getEffectsBefore(Operation *op, SmallVectorImpl< MemoryEffects::EffectInstance > &effects, bool stopAtBarrier)
Collects memory effects from operations that may be executed before op in a trivial structured contro...
static MLIRContext * getContext(OpFoldResult val)
Operation & back()
Definition: Block.h:152
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition: Block.cpp:27
Operation * getTerminator()
Get the terminator operation of this block.
Definition: Block.cpp:244
Operation & front()
Definition: Block.h:153
This trait indicates that the memory effects of an operation includes the effects of operations neste...
This class provides the API for ops that are known to be isolated from above.
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition: Operation.h:749
bool hasAttr(StringAttr name)
Return true if the operation has an attribute with the provided name, false otherwise.
Definition: Operation.h:560
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:797
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition: Operation.h:234
Block * getBlock()
Returns the operation block that contains this operation.
Definition: Operation.h:213
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition: Operation.h:677
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
Definition: PatternMatch.h:769
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition: Region.h:26
bool hasOneBlock()
Return true if this region has exactly one block.
Definition: Region.h:68
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
This class represents a specific instance of an effect.
Resource * getResource() const
Return the resource that the effect applies to.
EffectT * getEffect() const
Return the effect being applied.
Value getValue() const
Return the value the effect is applied on, or nullptr if there isn't a known value being affected.
TypeID getResourceID() const
Return the unique identifier for the base resource class.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
user_range getUsers() const
Definition: Value.h:218
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
Include the generated interface declarations.
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...
const FrozenRewritePatternSet & patterns
void populateGpuEliminateBarriersPatterns(RewritePatternSet &patterns)
Erase barriers that do not enforce conflicting memory side effects.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
Definition: PatternMatch.h:314