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 
23 namespace 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:
~DataflowIterator()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        */
GetRepeatCount()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        */
GetChanged()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        */
DataflowIterator(MIRGraph * mir_graph,int32_t start_idx,int32_t end_idx)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        */
PreOrderDfsIterator(MIRGraph * mir_graph)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        */
RepeatingPreOrderDfsIterator(MIRGraph * mir_graph)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        */
RepeatingPostOrderDfsIterator(MIRGraph * mir_graph)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        */
ReversePostOrderDfsIterator(MIRGraph * mir_graph)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        */
RepeatingReversePostOrderDfsIterator(MIRGraph * mir_graph)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        */
PostOrderDOMIterator(MIRGraph * mir_graph)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        */
AllNodesIterator(MIRGraph * mir_graph)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        */
Reset()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        */
TopologicalSortIterator(MIRGraph * mir_graph)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       */
RepeatingTopologicalSortIterator(MIRGraph * mir_graph)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       */
LoopRepeatingTopologicalSortIterator(MIRGraph * mir_graph)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 
~LoopRepeatingTopologicalSortIterator()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