MLIR  22.0.0git
Visitors.h
Go to the documentation of this file.
1 //===- Visitors.h - Utilities for visiting operations -----------*- C++ -*-===//
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 defines utilities for walking and visiting operations.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef MLIR_IR_VISITORS_H
14 #define MLIR_IR_VISITORS_H
15 
16 #include "mlir/Support/LLVM.h"
18 #include "llvm/ADT/STLExtras.h"
19 
20 namespace mlir {
21 class Diagnostic;
22 class InFlightDiagnostic;
23 class Operation;
24 class Block;
25 class Region;
26 
27 /// Traversal order for region, block and operation walk utilities.
28 enum class WalkOrder { PreOrder, PostOrder };
29 
30 /// This iterator enumerates the elements in "forward" order.
32  /// Make operations iterable: return the list of regions.
34 
35  /// Regions and block are already iterable.
36  template <typename T>
37  static constexpr T &makeIterable(T &range) {
38  return range;
39  }
40 };
41 
42 /// A utility class to encode the current walk stage for "generic" walkers.
43 /// When walking an operation, we can either choose a Pre/Post order walker
44 /// which invokes the callback on an operation before/after all its attached
45 /// regions have been visited, or choose a "generic" walker where the callback
46 /// is invoked on the operation N+1 times where N is the number of regions
47 /// attached to that operation. The `WalkStage` class below encodes the current
48 /// stage of the walk, i.e., which regions have already been visited, and the
49 /// callback accepts an additional argument for the current stage. Such
50 /// generic walkers that accept stage-aware callbacks are only applicable when
51 /// the callback operates on an operation (i.e., not applicable for callbacks
52 /// on Blocks or Regions).
53 class WalkStage {
54 public:
55  explicit WalkStage(Operation *op);
56 
57  /// Return true if parent operation is being visited before all regions.
58  bool isBeforeAllRegions() const { return nextRegion == 0; }
59  /// Returns true if parent operation is being visited just before visiting
60  /// region number `region`.
61  bool isBeforeRegion(int region) const { return nextRegion == region; }
62  /// Returns true if parent operation is being visited just after visiting
63  /// region number `region`.
64  bool isAfterRegion(int region) const { return nextRegion == region + 1; }
65  /// Return true if parent operation is being visited after all regions.
66  bool isAfterAllRegions() const { return nextRegion == numRegions; }
67  /// Advance the walk stage.
68  void advance() { nextRegion++; }
69  /// Returns the next region that will be visited.
70  int getNextRegion() const { return nextRegion; }
71 
72 private:
73  const int numRegions;
74  int nextRegion;
75 };
76 
77 namespace detail {
78 /// Helper templates to deduce the first argument of a callback parameter.
79 template <typename Ret, typename Arg, typename... Rest>
80 Arg first_argument_type(Ret (*)(Arg, Rest...));
81 template <typename Ret, typename F, typename Arg, typename... Rest>
82 Arg first_argument_type(Ret (F::*)(Arg, Rest...));
83 template <typename Ret, typename F, typename Arg, typename... Rest>
84 Arg first_argument_type(Ret (F::*)(Arg, Rest...) const);
85 template <typename F>
86 decltype(first_argument_type(&F::operator())) first_argument_type(F);
87 
88 /// Type definition of the first argument to the given callable 'T'.
89 template <typename T>
90 using first_argument = decltype(first_argument_type(std::declval<T>()));
91 
92 /// Walk all of the regions, blocks, or operations nested under (and including)
93 /// the given operation. The order in which regions, blocks and operations at
94 /// the same nesting level are visited (e.g., lexicographical or reverse
95 /// lexicographical order) is determined by 'Iterator'. The walk order for
96 /// enclosing regions, blocks and operations with respect to their nested ones
97 /// is specified by 'order'. These methods are invoked for void-returning
98 /// callbacks. A callback on a block or operation is allowed to erase that block
99 /// or operation only if the walk is in post-order. See non-void method for
100 /// pre-order erasure.
101 template <typename Iterator>
102 void walk(Operation *op, function_ref<void(Region *)> callback,
103  WalkOrder order) {
104  // We don't use early increment for regions because they can't be erased from
105  // a callback.
106  for (auto &region : Iterator::makeIterable(*op)) {
107  if (order == WalkOrder::PreOrder)
108  callback(&region);
109  for (auto &block : Iterator::makeIterable(region)) {
110  for (auto &nestedOp : Iterator::makeIterable(block))
111  walk<Iterator>(&nestedOp, callback, order);
112  }
113  if (order == WalkOrder::PostOrder)
114  callback(&region);
115  }
116 }
117 
118 template <typename Iterator>
119 void walk(Operation *op, function_ref<void(Block *)> callback,
120  WalkOrder order) {
121  for (auto &region : Iterator::makeIterable(*op)) {
122  // Early increment here in the case where the block is erased.
123  for (auto &block :
124  llvm::make_early_inc_range(Iterator::makeIterable(region))) {
125  if (order == WalkOrder::PreOrder)
126  callback(&block);
127  for (auto &nestedOp : Iterator::makeIterable(block))
128  walk<Iterator>(&nestedOp, callback, order);
129  if (order == WalkOrder::PostOrder)
130  callback(&block);
131  }
132  }
133 }
134 
135 template <typename Iterator>
136 void walk(Operation *op, function_ref<void(Operation *)> callback,
137  WalkOrder order) {
138  if (order == WalkOrder::PreOrder)
139  callback(op);
140 
141  // TODO: This walk should be iterative over the operations.
142  for (auto &region : Iterator::makeIterable(*op)) {
143  for (auto &block : Iterator::makeIterable(region)) {
144  // Early increment here in the case where the operation is erased.
145  for (auto &nestedOp :
146  llvm::make_early_inc_range(Iterator::makeIterable(block)))
147  walk<Iterator>(&nestedOp, callback, order);
148  }
149  }
150 
151  if (order == WalkOrder::PostOrder)
152  callback(op);
153 }
154 
155 /// Walk all of the regions, blocks, or operations nested under (and including)
156 /// the given operation. The order in which regions, blocks and operations at
157 /// the same nesting level are visited (e.g., lexicographical or reverse
158 /// lexicographical order) is determined by 'Iterator'. The walk order for
159 /// enclosing regions, blocks and operations with respect to their nested ones
160 /// is specified by 'order'. This method is invoked for skippable or
161 /// interruptible callbacks. A callback on a block or operation is allowed to
162 /// erase that block or operation if either:
163 /// * the walk is in post-order, or
164 /// * the walk is in pre-order and the walk is skipped after the erasure.
165 template <typename Iterator>
167  WalkOrder order) {
168  // We don't use early increment for regions because they can't be erased from
169  // a callback.
170  for (auto &region : Iterator::makeIterable(*op)) {
171  if (order == WalkOrder::PreOrder) {
172  WalkResult result = callback(&region);
173  if (result.wasSkipped())
174  continue;
175  if (result.wasInterrupted())
176  return WalkResult::interrupt();
177  }
178  for (auto &block : Iterator::makeIterable(region)) {
179  for (auto &nestedOp : Iterator::makeIterable(block))
180  if (walk<Iterator>(&nestedOp, callback, order).wasInterrupted())
181  return WalkResult::interrupt();
182  }
183  if (order == WalkOrder::PostOrder) {
184  if (callback(&region).wasInterrupted())
185  return WalkResult::interrupt();
186  // We don't check if this region was skipped because its walk already
187  // finished and the walk will continue with the next region.
188  }
189  }
190  return WalkResult::advance();
191 }
192 
193 template <typename Iterator>
195  WalkOrder order) {
196  for (auto &region : Iterator::makeIterable(*op)) {
197  // Early increment here in the case where the block is erased.
198  for (auto &block :
199  llvm::make_early_inc_range(Iterator::makeIterable(region))) {
200  if (order == WalkOrder::PreOrder) {
201  WalkResult result = callback(&block);
202  if (result.wasSkipped())
203  continue;
204  if (result.wasInterrupted())
205  return WalkResult::interrupt();
206  }
207  for (auto &nestedOp : Iterator::makeIterable(block))
208  if (walk<Iterator>(&nestedOp, callback, order).wasInterrupted())
209  return WalkResult::interrupt();
210  if (order == WalkOrder::PostOrder) {
211  if (callback(&block).wasInterrupted())
212  return WalkResult::interrupt();
213  // We don't check if this block was skipped because its walk already
214  // finished and the walk will continue with the next block.
215  }
216  }
217  }
218  return WalkResult::advance();
219 }
220 
221 template <typename Iterator>
223  WalkOrder order) {
224  if (order == WalkOrder::PreOrder) {
225  WalkResult result = callback(op);
226  // If skipped, caller will continue the walk on the next operation.
227  if (result.wasSkipped())
228  return WalkResult::advance();
229  if (result.wasInterrupted())
230  return WalkResult::interrupt();
231  }
232 
233  // TODO: This walk should be iterative over the operations.
234  for (auto &region : Iterator::makeIterable(*op)) {
235  for (auto &block : Iterator::makeIterable(region)) {
236  // Early increment here in the case where the operation is erased.
237  for (auto &nestedOp :
238  llvm::make_early_inc_range(Iterator::makeIterable(block))) {
239  if (walk<Iterator>(&nestedOp, callback, order).wasInterrupted())
240  return WalkResult::interrupt();
241  }
242  }
243  }
244 
245  if (order == WalkOrder::PostOrder)
246  return callback(op);
247  return WalkResult::advance();
248 }
249 
250 // Below are a set of functions to walk nested operations. Users should favor
251 // the direct `walk` methods on the IR classes(Operation/Block/etc) over these
252 // methods. They are also templated to allow for statically dispatching based
253 // upon the type of the callback function.
254 
255 /// Walk all of the regions, blocks, or operations nested under (and including)
256 /// the given operation. The order in which regions, blocks and operations at
257 /// the same nesting level are visited (e.g., lexicographical or reverse
258 /// lexicographical order) is determined by 'Iterator'. The walk order for
259 /// enclosing regions, blocks and operations with respect to their nested ones
260 /// is specified by 'Order' (post-order by default). A callback on a block or
261 /// operation is allowed to erase that block or operation if either:
262 /// * the walk is in post-order, or
263 /// * the walk is in pre-order and the walk is skipped after the erasure.
264 /// This method is selected for callbacks that operate on Region*, Block*, and
265 /// Operation*.
266 ///
267 /// Example:
268 /// op->walk([](Region *r) { ... });
269 /// op->walk([](Block *b) { ... });
270 /// op->walk([](Operation *op) { ... });
271 template <
272  WalkOrder Order = WalkOrder::PostOrder, typename Iterator = ForwardIterator,
273  typename FuncTy, typename ArgT = detail::first_argument<FuncTy>,
274  typename RetT = decltype(std::declval<FuncTy>()(std::declval<ArgT>()))>
275 std::enable_if_t<llvm::is_one_of<ArgT, Operation *, Region *, Block *>::value,
276  RetT>
277 walk(Operation *op, FuncTy &&callback) {
278  return detail::walk<Iterator>(op, function_ref<RetT(ArgT)>(callback), Order);
279 }
280 
281 /// Walk all of the operations of type 'ArgT' nested under and including the
282 /// given operation. The order in which regions, blocks and operations at
283 /// the same nesting are visited (e.g., lexicographical or reverse
284 /// lexicographical order) is determined by 'Iterator'. The walk order for
285 /// enclosing regions, blocks and operations with respect to their nested ones
286 /// is specified by 'order' (post-order by default). This method is selected for
287 /// void-returning callbacks that operate on a specific derived operation type.
288 /// A callback on an operation is allowed to erase that operation only if the
289 /// walk is in post-order. See non-void method for pre-order erasure.
290 ///
291 /// Example:
292 /// op->walk([](ReturnOp op) { ... });
293 template <
294  WalkOrder Order = WalkOrder::PostOrder, typename Iterator = ForwardIterator,
295  typename FuncTy, typename ArgT = detail::first_argument<FuncTy>,
296  typename RetT = decltype(std::declval<FuncTy>()(std::declval<ArgT>()))>
297 std::enable_if_t<
298  !llvm::is_one_of<ArgT, Operation *, Region *, Block *>::value &&
299  std::is_same<RetT, void>::value,
300  RetT>
301 walk(Operation *op, FuncTy &&callback) {
302  auto wrapperFn = [&](Operation *op) {
303  if (auto derivedOp = dyn_cast<ArgT>(op))
304  callback(derivedOp);
305  };
306  return detail::walk<Iterator>(op, function_ref<RetT(Operation *)>(wrapperFn),
307  Order);
308 }
309 
310 /// Walk all of the operations of type 'ArgT' nested under and including the
311 /// given operation. The order in which regions, blocks and operations at
312 /// the same nesting are visited (e.g., lexicographical or reverse
313 /// lexicographical order) is determined by 'Iterator'. The walk order for
314 /// enclosing regions, blocks and operations with respect to their nested ones
315 /// is specified by 'Order' (post-order by default). This method is selected for
316 /// WalkReturn returning skippable or interruptible callbacks that operate on a
317 /// specific derived operation type. A callback on an operation is allowed to
318 /// erase that operation if either:
319 /// * the walk is in post-order, or
320 /// * the walk is in pre-order and the walk is skipped after the erasure.
321 ///
322 /// Example:
323 /// op->walk([](ReturnOp op) {
324 /// if (some_invariant)
325 /// return WalkResult::skip();
326 /// if (another_invariant)
327 /// return WalkResult::interrupt();
328 /// return WalkResult::advance();
329 /// });
330 template <
331  WalkOrder Order = WalkOrder::PostOrder, typename Iterator = ForwardIterator,
332  typename FuncTy, typename ArgT = detail::first_argument<FuncTy>,
333  typename RetT = decltype(std::declval<FuncTy>()(std::declval<ArgT>()))>
334 std::enable_if_t<
335  !llvm::is_one_of<ArgT, Operation *, Region *, Block *>::value &&
336  std::is_same<RetT, WalkResult>::value,
337  RetT>
338 walk(Operation *op, FuncTy &&callback) {
339  auto wrapperFn = [&](Operation *op) {
340  if (auto derivedOp = dyn_cast<ArgT>(op))
341  return callback(derivedOp);
342  return WalkResult::advance();
343  };
344  return detail::walk<Iterator>(op, function_ref<RetT(Operation *)>(wrapperFn),
345  Order);
346 }
347 
348 /// Generic walkers with stage aware callbacks.
349 
350 /// Walk all the operations nested under (and including) the given operation,
351 /// with the callback being invoked on each operation N+1 times, where N is the
352 /// number of regions attached to the operation. The `stage` input to the
353 /// callback indicates the current walk stage. This method is invoked for void
354 /// returning callbacks.
355 void walk(Operation *op,
356  function_ref<void(Operation *, const WalkStage &stage)> callback);
357 
358 /// Walk all the operations nested under (and including) the given operation,
359 /// with the callback being invoked on each operation N+1 times, where N is the
360 /// number of regions attached to the operation. The `stage` input to the
361 /// callback indicates the current walk stage. This method is invoked for
362 /// skippable or interruptible callbacks.
365  function_ref<WalkResult(Operation *, const WalkStage &stage)> callback);
366 
367 /// Walk all of the operations nested under and including the given operation.
368 /// This method is selected for stage-aware callbacks that operate on
369 /// Operation*.
370 ///
371 /// Example:
372 /// op->walk([](Operation *op, const WalkStage &stage) { ... });
373 template <typename FuncTy, typename ArgT = detail::first_argument<FuncTy>,
374  typename RetT = decltype(std::declval<FuncTy>()(
375  std::declval<ArgT>(), std::declval<const WalkStage &>()))>
376 std::enable_if_t<std::is_same<ArgT, Operation *>::value, RetT>
377 walk(Operation *op, FuncTy &&callback) {
378  return detail::walk(op,
379  function_ref<RetT(ArgT, const WalkStage &)>(callback));
380 }
381 
382 /// Walk all of the operations of type 'ArgT' nested under and including the
383 /// given operation. This method is selected for void returning callbacks that
384 /// operate on a specific derived operation type.
385 ///
386 /// Example:
387 /// op->walk([](ReturnOp op, const WalkStage &stage) { ... });
388 template <typename FuncTy, typename ArgT = detail::first_argument<FuncTy>,
389  typename RetT = decltype(std::declval<FuncTy>()(
390  std::declval<ArgT>(), std::declval<const WalkStage &>()))>
391 std::enable_if_t<!std::is_same<ArgT, Operation *>::value &&
392  std::is_same<RetT, void>::value,
393  RetT>
394 walk(Operation *op, FuncTy &&callback) {
395  auto wrapperFn = [&](Operation *op, const WalkStage &stage) {
396  if (auto derivedOp = dyn_cast<ArgT>(op))
397  callback(derivedOp, stage);
398  };
399  return detail::walk(
400  op, function_ref<RetT(Operation *, const WalkStage &)>(wrapperFn));
401 }
402 
403 /// Walk all of the operations of type 'ArgT' nested under and including the
404 /// given operation. This method is selected for WalkReturn returning
405 /// interruptible callbacks that operate on a specific derived operation type.
406 ///
407 /// Example:
408 /// op->walk(op, [](ReturnOp op, const WalkStage &stage) {
409 /// if (some_invariant)
410 /// return WalkResult::interrupt();
411 /// return WalkResult::advance();
412 /// });
413 template <typename FuncTy, typename ArgT = detail::first_argument<FuncTy>,
414  typename RetT = decltype(std::declval<FuncTy>()(
415  std::declval<ArgT>(), std::declval<const WalkStage &>()))>
416 std::enable_if_t<!std::is_same<ArgT, Operation *>::value &&
417  std::is_same<RetT, WalkResult>::value,
418  RetT>
419 walk(Operation *op, FuncTy &&callback) {
420  auto wrapperFn = [&](Operation *op, const WalkStage &stage) {
421  if (auto derivedOp = dyn_cast<ArgT>(op))
422  return callback(derivedOp, stage);
423  return WalkResult::advance();
424  };
425  return detail::walk(
426  op, function_ref<RetT(Operation *, const WalkStage &)>(wrapperFn));
427 }
428 
429 /// Utility to provide the return type of a templated walk method.
430 template <typename FnT>
431 using walkResultType = decltype(walk(nullptr, std::declval<FnT>()));
432 } // namespace detail
433 
434 } // namespace mlir
435 
436 #endif
Block represents an ordered list of Operations.
Definition: Block.h:33
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition: Region.h:26
A utility result that is used to signal how to proceed with an ongoing walk:
Definition: WalkResult.h:29
bool wasSkipped() const
Returns true if the walk was skipped.
Definition: WalkResult.h:54
static WalkResult advance()
Definition: WalkResult.h:47
bool wasInterrupted() const
Returns true if the walk was interrupted.
Definition: WalkResult.h:51
static WalkResult interrupt()
Definition: WalkResult.h:46
A utility class to encode the current walk stage for "generic" walkers.
Definition: Visitors.h:53
void advance()
Advance the walk stage.
Definition: Visitors.h:68
int getNextRegion() const
Returns the next region that will be visited.
Definition: Visitors.h:70
bool isBeforeRegion(int region) const
Returns true if parent operation is being visited just before visiting region number region.
Definition: Visitors.h:61
WalkStage(Operation *op)
Definition: Visitors.cpp:14
bool isAfterAllRegions() const
Return true if parent operation is being visited after all regions.
Definition: Visitors.h:66
bool isAfterRegion(int region) const
Returns true if parent operation is being visited just after visiting region number region.
Definition: Visitors.h:64
bool isBeforeAllRegions() const
Return true if parent operation is being visited before all regions.
Definition: Visitors.h:58
void walk(Operation *op, function_ref< void(Region *)> callback, WalkOrder order)
Walk all of the regions, blocks, or operations nested under (and including) the given operation.
Definition: Visitors.h:102
decltype(walk(nullptr, std::declval< FnT >())) walkResultType
Utility to provide the return type of a templated walk method.
Definition: Visitors.h:431
decltype(first_argument_type(&F::operator())) first_argument_type(F)
Definition: Visitors.h:86
decltype(first_argument_type(std::declval< T >())) first_argument
Type definition of the first argument to the given callable 'T'.
Definition: Visitors.h:90
Include the generated interface declarations.
WalkOrder
Traversal order for region, block and operation walk utilities.
Definition: Visitors.h:28
This iterator enumerates the elements in "forward" order.
Definition: Visitors.h:31
static MutableArrayRef< Region > makeIterable(Operation &range)
Make operations iterable: return the list of regions.
Definition: Visitors.cpp:17
static constexpr T & makeIterable(T &range)
Regions and block are already iterable.
Definition: Visitors.h:37