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
20namespace mlir {
21class Diagnostic;
23class Operation;
24class Block;
25class Region;
26
27/// Traversal order for region, block and operation walk utilities.
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).
53class WalkStage {
54public:
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
72private:
73 const int numRegions;
74 int nextRegion;
75};
76
77namespace detail {
78/// Helper templates to deduce the first argument of a callback parameter.
79template <typename Ret, typename Arg, typename... Rest>
80Arg first_argument_type(Ret (*)(Arg, Rest...));
81template <typename Ret, typename F, typename Arg, typename... Rest>
82Arg first_argument_type(Ret (F::*)(Arg, Rest...));
83template <typename Ret, typename F, typename Arg, typename... Rest>
84Arg first_argument_type(Ret (F::*)(Arg, Rest...) const);
85template <typename F>
86decltype(first_argument_type(&F::operator())) first_argument_type(F);
87
88/// Type definition of the first argument to the given callable 'T'.
89template <typename T>
90using 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.
101template <typename Iterator>
102void 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
118template <typename Iterator>
119void 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
135template <typename Iterator>
136void 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.
165template <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
193template <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
221template <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) { ... });
271template <
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>()))>
275std::enable_if_t<llvm::is_one_of<ArgT, Operation *, Region *, Block *>::value,
276 RetT>
277walk(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) { ... });
293template <
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>()))>
297std::enable_if_t<
298 !llvm::is_one_of<ArgT, Operation *, Region *, Block *>::value &&
299 std::is_same<RetT, void>::value,
300 RetT>
301walk(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/// });
330template <
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>()))>
334std::enable_if_t<
335 !llvm::is_one_of<ArgT, Operation *, Region *, Block *>::value &&
336 std::is_same<RetT, WalkResult>::value,
337 RetT>
338walk(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.
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) { ... });
373template <typename FuncTy, typename ArgT = detail::first_argument<FuncTy>,
374 typename RetT = decltype(std::declval<FuncTy>()(
375 std::declval<ArgT>(), std::declval<const WalkStage &>()))>
376std::enable_if_t<std::is_same<ArgT, Operation *>::value, RetT>
377walk(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) { ... });
388template <typename FuncTy, typename ArgT = detail::first_argument<FuncTy>,
389 typename RetT = decltype(std::declval<FuncTy>()(
390 std::declval<ArgT>(), std::declval<const WalkStage &>()))>
391std::enable_if_t<!std::is_same<ArgT, Operation *>::value &&
392 std::is_same<RetT, void>::value,
393 RetT>
394walk(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/// });
413template <typename FuncTy, typename ArgT = detail::first_argument<FuncTy>,
414 typename RetT = decltype(std::declval<FuncTy>()(
415 std::declval<ArgT>(), std::declval<const WalkStage &>()))>
416std::enable_if_t<!std::is_same<ArgT, Operation *>::value &&
417 std::is_same<RetT, WalkResult>::value,
418 RetT>
419walk(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.
430template <typename FnT>
431using 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
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
This class represents a diagnostic that is inflight and set to be reported.
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
static WalkResult advance()
Definition WalkResult.h:47
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(first_argument_type(std::declval< T >())) first_argument
Type definition of the first argument to the given callable 'T'.
Definition Visitors.h:90
Arg first_argument_type(Ret(*)(Arg, Rest...))
Helper templates to deduce the first argument of a callback parameter.
decltype(walk(nullptr, std::declval< FnT >())) walkResultType
Utility to provide the return type of a templated walk method.
Definition Visitors.h:431
Include the generated interface declarations.
WalkOrder
Traversal order for region, block and operation walk utilities.
Definition Visitors.h:28
llvm::function_ref< Fn > function_ref
Definition LLVM.h:152
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