dataflow_iterator.h revision 1fd4821f6b3ac57a44c2ce91025686da4641d197
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef ART_COMPILER_DEX_DATAFLOW_ITERATOR_H_
18#define ART_COMPILER_DEX_DATAFLOW_ITERATOR_H_
19
20#include "compiler_ir.h"
21#include "mir_graph.h"
22
23namespace art {
24
25  /*
26   * This class supports iterating over lists of basic blocks in various
27   * interesting orders.  Note that for efficiency, the visit orders have been pre-computed.
28   * The order itself will not change during the iteration.  However, for some uses,
29   * auxiliary data associated with the basic blocks may be changed during the iteration,
30   * necessitating another pass over the list.  If this behavior is required, use the
31   * "Repeating" variant.  For the repeating variant, the caller must tell the iterator
32   * whether a change has been made that necessitates another pass.  Note that calling Next(true)
33   * does not affect the iteration order or short-circuit the current pass - it simply tells
34   * the iterator that once it has finished walking through the block list it should reset and
35   * do another full pass through the list.
36   */
37  /**
38   * @class DataflowIterator
39   * @brief The main iterator class, all other iterators derive of this one to define an iteration order.
40   */
41  class DataflowIterator {
42    public:
43      virtual ~DataflowIterator() {}
44
45      /**
46       * @brief How many times have we repeated the iterator across the BasicBlocks?
47       * @return the number of iteration repetitions.
48       */
49      int32_t GetRepeatCount() { return repeats_; }
50
51      /**
52       * @brief Has the user of the iterator reported a change yet?
53       * @details Does not mean there was or not a change, it is only whether the user passed a true to the Next function call.
54       * @return whether the user of the iterator reported a change yet.
55       */
56      int32_t GetChanged() { return changed_; }
57
58      /**
59       * @brief Get the next BasicBlock depending on iteration order.
60       * @param had_change did the user of the iteration change the previous BasicBlock.
61       * @return the next BasicBlock following the iteration order, 0 if finished.
62       */
63      virtual BasicBlock* Next(bool had_change = false) = 0;
64
65    protected:
66      /**
67       * @param mir_graph the MIRGraph we are interested in.
68       * @param start_idx the first index we want to iterate across.
69       * @param end_idx the last index we want to iterate (not included).
70       */
71      DataflowIterator(MIRGraph* mir_graph, int32_t start_idx, int32_t end_idx)
72          : mir_graph_(mir_graph),
73            start_idx_(start_idx),
74            end_idx_(end_idx),
75            block_id_list_(NULL),
76            idx_(0),
77            repeats_(0),
78            changed_(false) {}
79
80      /**
81       * @brief Get the next BasicBlock iterating forward.
82       * @return the next BasicBlock iterating forward.
83       */
84      virtual BasicBlock* ForwardSingleNext() ALWAYS_INLINE;
85
86      /**
87       * @brief Get the next BasicBlock iterating backward.
88       * @return the next BasicBlock iterating backward.
89       */
90      virtual BasicBlock* ReverseSingleNext() ALWAYS_INLINE;
91
92      /**
93       * @brief Get the next BasicBlock iterating forward, restart if a BasicBlock was reported changed during the last iteration.
94       * @return the next BasicBlock iterating forward, with chance of repeating the iteration.
95       */
96      virtual BasicBlock* ForwardRepeatNext() ALWAYS_INLINE;
97
98      /**
99       * @brief Get the next BasicBlock iterating backward, restart if a BasicBlock was reported changed during the last iteration.
100       * @return the next BasicBlock iterating backward, with chance of repeating the iteration.
101       */
102      virtual BasicBlock* ReverseRepeatNext() ALWAYS_INLINE;
103
104      MIRGraph* const mir_graph_;                       /**< @brief the MIRGraph */
105      const int32_t start_idx_;                         /**< @brief the start index for the iteration */
106      const int32_t end_idx_;                           /**< @brief the last index for the iteration */
107      GrowableArray<BasicBlockId>* block_id_list_;      /**< @brief the list of BasicBlocks we want to iterate on */
108      int32_t idx_;                                     /**< @brief Current index for the iterator */
109      int32_t repeats_;                                 /**< @brief Number of repeats over the iteration */
110      bool changed_;                                    /**< @brief Has something changed during the current iteration? */
111  };  // DataflowIterator
112
113  /**
114   * @class PreOrderDfsIterator
115   * @brief Used to perform a Pre-order Depth-First-Search Iteration of a MIRGraph.
116   */
117  class PreOrderDfsIterator : public DataflowIterator {
118    public:
119      /**
120       * @brief The constructor, using all of the reachable blocks of the MIRGraph.
121       * @param mir_graph The MIRGraph considered.
122       */
123      explicit PreOrderDfsIterator(MIRGraph* mir_graph)
124          : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
125        // Extra setup for the PreOrderDfsIterator.
126        idx_ = start_idx_;
127        block_id_list_ = mir_graph->GetDfsOrder();
128      }
129
130      /**
131       * @brief Get the next BasicBlock depending on iteration order.
132       * @param had_change did the user of the iteration change the previous BasicBlock.
133       * @return the next BasicBlock following the iteration order, 0 if finished.
134       */
135      virtual BasicBlock* Next(bool had_change = false) {
136        // Update changed: if had_changed is true, we remember it for the whole iteration.
137        changed_ |= had_change;
138
139        return ForwardSingleNext();
140      }
141  };
142
143  /**
144   * @class RepeatingPreOrderDfsIterator
145   * @brief Used to perform a Repeating Pre-order Depth-First-Search Iteration of a MIRGraph.
146   * @details If there is a change during an iteration, the iteration starts over at the end of the iteration.
147   */
148  class RepeatingPreOrderDfsIterator : public DataflowIterator {
149    public:
150      /**
151       * @brief The constructor, using all of the reachable blocks of the MIRGraph.
152       * @param mir_graph The MIRGraph considered.
153       */
154      explicit RepeatingPreOrderDfsIterator(MIRGraph* mir_graph)
155          : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
156        // Extra setup for the RepeatingPreOrderDfsIterator.
157        idx_ = start_idx_;
158        block_id_list_ = mir_graph->GetDfsOrder();
159      }
160
161      /**
162       * @brief Get the next BasicBlock depending on iteration order.
163       * @param had_change did the user of the iteration change the previous BasicBlock.
164       * @return the next BasicBlock following the iteration order, 0 if finished.
165       */
166      virtual BasicBlock* Next(bool had_change = false) {
167        // Update changed: if had_changed is true, we remember it for the whole iteration.
168        changed_ |= had_change;
169
170        return ForwardRepeatNext();
171      }
172  };
173
174  /**
175   * @class RepeatingPostOrderDfsIterator
176   * @brief Used to perform a Repeating Post-order Depth-First-Search Iteration of a MIRGraph.
177   * @details If there is a change during an iteration, the iteration starts over at the end of the iteration.
178   */
179  class RepeatingPostOrderDfsIterator : public DataflowIterator {
180    public:
181      /**
182       * @brief The constructor, using all of the reachable blocks of the MIRGraph.
183       * @param mir_graph The MIRGraph considered.
184       */
185      explicit RepeatingPostOrderDfsIterator(MIRGraph* mir_graph)
186          : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
187        // Extra setup for the RepeatingPostOrderDfsIterator.
188        idx_ = start_idx_;
189        block_id_list_ = mir_graph->GetDfsPostOrder();
190      }
191
192      /**
193       * @brief Get the next BasicBlock depending on iteration order.
194       * @param had_change did the user of the iteration change the previous BasicBlock.
195       * @return the next BasicBlock following the iteration order, 0 if finished.
196       */
197      virtual BasicBlock* Next(bool had_change = false) {
198        // Update changed: if had_changed is true, we remember it for the whole iteration.
199        changed_ |= had_change;
200
201        return ForwardRepeatNext();
202      }
203  };
204
205  /**
206   * @class ReversePostOrderDfsIterator
207   * @brief Used to perform a Reverse Post-order Depth-First-Search Iteration of a MIRGraph.
208   */
209  class ReversePostOrderDfsIterator : public DataflowIterator {
210    public:
211      /**
212       * @brief The constructor, using all of the reachable blocks of the MIRGraph.
213       * @param mir_graph The MIRGraph considered.
214       */
215      explicit ReversePostOrderDfsIterator(MIRGraph* mir_graph)
216          : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) {
217        // Extra setup for the ReversePostOrderDfsIterator.
218        idx_ = start_idx_;
219        block_id_list_ = mir_graph->GetDfsPostOrder();
220      }
221
222      /**
223       * @brief Get the next BasicBlock depending on iteration order.
224       * @param had_change did the user of the iteration change the previous BasicBlock.
225       * @return the next BasicBlock following the iteration order, 0 if finished.
226       */
227      virtual BasicBlock* Next(bool had_change = false) {
228        // Update changed: if had_changed is true, we remember it for the whole iteration.
229        changed_ |= had_change;
230
231        return ReverseSingleNext();
232      }
233  };
234
235  /**
236   * @class ReversePostOrderDfsIterator
237   * @brief Used to perform a Repeating Reverse Post-order Depth-First-Search Iteration of a MIRGraph.
238   * @details If there is a change during an iteration, the iteration starts over at the end of the iteration.
239   */
240  class RepeatingReversePostOrderDfsIterator : public DataflowIterator {
241    public:
242      /**
243       * @brief The constructor, using all of the reachable blocks of the MIRGraph.
244       * @param mir_graph The MIRGraph considered.
245       */
246      explicit RepeatingReversePostOrderDfsIterator(MIRGraph* mir_graph)
247          : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) {
248        // Extra setup for the RepeatingReversePostOrderDfsIterator
249        idx_ = start_idx_;
250        block_id_list_ = mir_graph->GetDfsPostOrder();
251      }
252
253      /**
254       * @brief Get the next BasicBlock depending on iteration order.
255       * @param had_change did the user of the iteration change the previous BasicBlock.
256       * @return the next BasicBlock following the iteration order, 0 if finished.
257       */
258      virtual BasicBlock* Next(bool had_change = false) {
259        // Update changed: if had_changed is true, we remember it for the whole iteration.
260        changed_ |= had_change;
261
262        return ReverseRepeatNext();
263      }
264  };
265
266  /**
267   * @class PostOrderDOMIterator
268   * @brief Used to perform a Post-order Domination Iteration of a MIRGraph.
269   */
270  class PostOrderDOMIterator : public DataflowIterator {
271    public:
272      /**
273       * @brief The constructor, using all of the reachable blocks of the MIRGraph.
274       * @param mir_graph The MIRGraph considered.
275       */
276      explicit PostOrderDOMIterator(MIRGraph* mir_graph)
277          : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
278        // Extra setup for thePostOrderDOMIterator.
279        idx_ = start_idx_;
280        block_id_list_ = mir_graph->GetDomPostOrder();
281      }
282
283      /**
284       * @brief Get the next BasicBlock depending on iteration order.
285       * @param had_change did the user of the iteration change the previous BasicBlock.
286       * @return the next BasicBlock following the iteration order, 0 if finished.
287       */
288      virtual BasicBlock* Next(bool had_change = false) {
289        // Update changed: if had_changed is true, we remember it for the whole iteration.
290        changed_ |= had_change;
291
292        return ForwardSingleNext();
293      }
294  };
295
296  /**
297   * @class AllNodesIterator
298   * @brief Used to perform an iteration on all the BasicBlocks a MIRGraph.
299   */
300  class AllNodesIterator : public DataflowIterator {
301    public:
302      /**
303       * @brief The constructor, using all of the reachable blocks of the MIRGraph.
304       * @param mir_graph The MIRGraph considered.
305       */
306      explicit AllNodesIterator(MIRGraph* mir_graph)
307          : DataflowIterator(mir_graph, 0, 0),
308            all_nodes_iterator_(mir_graph->GetBlockList()) {
309      }
310
311      /**
312       * @brief Resetting the iterator.
313       */
314      void Reset() {
315        all_nodes_iterator_.Reset();
316      }
317
318      /**
319       * @brief Get the next BasicBlock depending on iteration order.
320       * @param had_change did the user of the iteration change the previous BasicBlock.
321       * @return the next BasicBlock following the iteration order, 0 if finished.
322       */
323      virtual BasicBlock* Next(bool had_change = false) ALWAYS_INLINE;
324
325    private:
326      GrowableArray<BasicBlock*>::Iterator all_nodes_iterator_;    /**< @brief The list of all the nodes */
327  };
328
329  /**
330   * @class TopologicalSortIterator
331   * @brief Used to perform a Topological Sort Iteration of a MIRGraph.
332   */
333  class TopologicalSortIterator : public DataflowIterator {
334    public:
335      /**
336       * @brief The constructor, using all of the reachable blocks of the MIRGraph.
337       * @param mir_graph The MIRGraph considered.
338       */
339      explicit TopologicalSortIterator(MIRGraph* mir_graph)
340          : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder()->Size()) {
341        // Extra setup for TopologicalSortIterator.
342        idx_ = start_idx_;
343        block_id_list_ = mir_graph->GetTopologicalSortOrder();
344      }
345
346      /**
347       * @brief Get the next BasicBlock depending on iteration order.
348       * @param had_change did the user of the iteration change the previous BasicBlock.
349       * @return the next BasicBlock following the iteration order, 0 if finished.
350       */
351      virtual BasicBlock* Next(bool had_change = false) {
352        // Update changed: if had_changed is true, we remember it for the whole iteration.
353        changed_ |= had_change;
354
355        return ForwardSingleNext();
356      }
357  };
358
359  /**
360   * @class RepeatingTopologicalSortIterator
361   * @brief Used to perform a Topological Sort Iteration of a MIRGraph.
362   * @details If there is a change during an iteration, the iteration starts over at the end of the
363   *          iteration.
364   */
365  class RepeatingTopologicalSortIterator : public DataflowIterator {
366    public:
367     /**
368      * @brief The constructor, using all of the reachable blocks of the MIRGraph.
369      * @param mir_graph The MIRGraph considered.
370      */
371     explicit RepeatingTopologicalSortIterator(MIRGraph* mir_graph)
372         : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder()->Size()) {
373       // Extra setup for RepeatingTopologicalSortIterator.
374       idx_ = start_idx_;
375       block_id_list_ = mir_graph->GetTopologicalSortOrder();
376     }
377
378     /**
379      * @brief Get the next BasicBlock depending on iteration order.
380      * @param had_change did the user of the iteration change the previous BasicBlock.
381      * @return the next BasicBlock following the iteration order, 0 if finished.
382      */
383     virtual BasicBlock* Next(bool had_change = false) {
384       // Update changed: if had_changed is true, we remember it for the whole iteration.
385       changed_ |= had_change;
386
387       return ForwardRepeatNext();
388     }
389  };
390
391  /**
392   * @class LoopRepeatingTopologicalSortIterator
393   * @brief Used to perform a Topological Sort Iteration of a MIRGraph, repeating loops as needed.
394   * @details The iterator uses the visited flags to keep track of the blocks that need
395   * recalculation and keeps a stack of loop heads in the MIRGraph. At the end of the loop
396   * it returns back to the loop head if it needs to be recalculated. Due to the use of
397   * the visited flags and the loop head stack in the MIRGraph, it's not possible to use
398   * two iterators at the same time or modify this data during iteration (though inspection
399   * of this data is allowed and sometimes even expected).
400   *
401   * NOTE: This iterator is not suitable for passes that need to propagate changes to
402   * predecessors, such as type inferrence.
403   */
404  class LoopRepeatingTopologicalSortIterator : public DataflowIterator {
405    public:
406     /**
407      * @brief The constructor, using all of the reachable blocks of the MIRGraph.
408      * @param mir_graph The MIRGraph considered.
409      */
410     explicit LoopRepeatingTopologicalSortIterator(MIRGraph* mir_graph)
411         : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder()->Size()),
412           loop_ends_(mir_graph->GetTopologicalSortOrderLoopEnds()),
413           loop_head_stack_(mir_graph_->GetTopologicalSortOrderLoopHeadStack()) {
414       // Extra setup for RepeatingTopologicalSortIterator.
415       idx_ = start_idx_;
416       block_id_list_ = mir_graph->GetTopologicalSortOrder();
417       // Clear visited flags and check that the loop head stack is empty.
418       mir_graph->ClearAllVisitedFlags();
419       DCHECK_EQ(loop_head_stack_->Size(), 0u);
420     }
421
422     ~LoopRepeatingTopologicalSortIterator() {
423       DCHECK_EQ(loop_head_stack_->Size(), 0u);
424     }
425
426     /**
427      * @brief Get the next BasicBlock depending on iteration order.
428      * @param had_change did the user of the iteration change the previous BasicBlock.
429      * @return the next BasicBlock following the iteration order, 0 if finished.
430      */
431     virtual BasicBlock* Next(bool had_change = false) OVERRIDE;
432
433    private:
434     const GrowableArray<BasicBlockId>* const loop_ends_;
435     GrowableArray<std::pair<uint16_t, bool>>* const loop_head_stack_;
436  };
437
438}  // namespace art
439
440#endif  // ART_COMPILER_DEX_DATAFLOW_ITERATOR_H_
441