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