1 /*
2  * Copyright (C) 2016 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_OPTIMIZING_SCHEDULER_H_
18 #define ART_COMPILER_OPTIMIZING_SCHEDULER_H_
19 
20 #include <fstream>
21 
22 #include "base/scoped_arena_allocator.h"
23 #include "base/scoped_arena_containers.h"
24 #include "base/stl_util.h"
25 #include "base/time_utils.h"
26 #include "code_generator.h"
27 #include "load_store_analysis.h"
28 #include "nodes.h"
29 #include "optimization.h"
30 
31 namespace art {
32 
33 // General description of instruction scheduling.
34 //
35 // This pass tries to improve the quality of the generated code by reordering
36 // instructions in the graph to avoid execution delays caused by execution
37 // dependencies.
38 // Currently, scheduling is performed at the block level, so no `HInstruction`
39 // ever leaves its block in this pass.
40 //
41 // The scheduling process iterates through blocks in the graph. For blocks that
42 // we can and want to schedule:
43 // 1) Build a dependency graph for instructions.
44 //    It includes data dependencies (inputs/uses), but also environment
45 //    dependencies and side-effect dependencies.
46 // 2) Schedule the dependency graph.
47 //    This is a topological sort of the dependency graph, using heuristics to
48 //    decide what node to scheduler first when there are multiple candidates.
49 //
50 // A few factors impacting the quality of the scheduling are:
51 // - The heuristics used to decide what node to schedule in the topological sort
52 //   when there are multiple valid candidates. There is a wide range of
53 //   complexity possible here, going from a simple model only considering
54 //   latencies, to a super detailed CPU pipeline model.
55 // - Fewer dependencies in the dependency graph give more freedom for the
56 //   scheduling heuristics. For example de-aliasing can allow possibilities for
57 //   reordering of memory accesses.
58 // - The level of abstraction of the IR. It is easier to evaluate scheduling for
59 //   IRs that translate to a single assembly instruction than for IRs
60 //   that generate multiple assembly instructions or generate different code
61 //   depending on properties of the IR.
62 // - Scheduling is performed before register allocation, it is not aware of the
63 //   impact of moving instructions on register allocation.
64 //
65 //
66 // The scheduling code uses the terms predecessors, successors, and dependencies.
67 // This can be confusing at times, so here are clarifications.
68 // These terms are used from the point of view of the program dependency graph. So
69 // the inputs of an instruction are part of its dependencies, and hence part its
70 // predecessors. So the uses of an instruction are (part of) its successors.
71 // (Side-effect dependencies can yield predecessors or successors that are not
72 // inputs or uses.)
73 //
74 // Here is a trivial example. For the Java code:
75 //
76 //    int a = 1 + 2;
77 //
78 // we would have the instructions
79 //
80 //    i1 HIntConstant 1
81 //    i2 HIntConstant 2
82 //    i3 HAdd [i1,i2]
83 //
84 // `i1` and `i2` are predecessors of `i3`.
85 // `i3` is a successor of `i1` and a successor of `i2`.
86 // In a scheduling graph for this code we would have three nodes `n1`, `n2`,
87 // and `n3` (respectively for instructions `i1`, `i1`, and `i3`).
88 // Conceptually the program dependency graph for this would contain two edges
89 //
90 //    n1 -> n3
91 //    n2 -> n3
92 //
93 // Since we schedule backwards (starting from the last instruction in each basic
94 // block), the implementation of nodes keeps a list of pointers their
95 // predecessors. So `n3` would keep pointers to its predecessors `n1` and `n2`.
96 //
97 // Node dependencies are also referred to from the program dependency graph
98 // point of view: we say that node `B` immediately depends on `A` if there is an
99 // edge from `A` to `B` in the program dependency graph. `A` is a predecessor of
100 // `B`, `B` is a successor of `A`. In the example above `n3` depends on `n1` and
101 // `n2`.
102 // Since nodes in the scheduling graph keep a list of their predecessors, node
103 // `B` will have a pointer to its predecessor `A`.
104 // As we schedule backwards, `B` will be selected for scheduling before `A` is.
105 //
106 // So the scheduling for the example above could happen as follow
107 //
108 //    |---------------------------+------------------------|
109 //    | candidates for scheduling | instructions scheduled |
110 //    | --------------------------+------------------------|
111 //
112 // The only node without successors is `n3`, so it is the only initial
113 // candidate.
114 //
115 //    | n3                        | (none)                 |
116 //
117 // We schedule `n3` as the last (and only) instruction. All its predecessors
118 // that do not have any unscheduled successors become candidate. That is, `n1`
119 // and `n2` become candidates.
120 //
121 //    | n1, n2                    | n3                     |
122 //
123 // One of the candidates is selected. In practice this is where scheduling
124 // heuristics kick in, to decide which of the candidates should be selected.
125 // In this example, let it be `n1`. It is scheduled before previously scheduled
126 // nodes (in program order). There are no other nodes to add to the list of
127 // candidates.
128 //
129 //    | n2                        | n1                     |
130 //    |                           | n3                     |
131 //
132 // The only candidate available for scheduling is `n2`. Schedule it before
133 // (in program order) the previously scheduled nodes.
134 //
135 //    | (none)                    | n2                     |
136 //    |                           | n1                     |
137 //    |                           | n3                     |
138 //    |---------------------------+------------------------|
139 //
140 // So finally the instructions will be executed in the order `i2`, `i1`, and `i3`.
141 // In this trivial example, it does not matter which of `i1` and `i2` is
142 // scheduled first since they are constants. However the same process would
143 // apply if `i1` and `i2` were actual operations (for example `HMul` and `HDiv`).
144 
145 // Set to true to have instruction scheduling dump scheduling graphs to the file
146 // `scheduling_graphs.dot`. See `SchedulingGraph::DumpAsDotGraph()`.
147 static constexpr bool kDumpDotSchedulingGraphs = false;
148 
149 // Typically used as a default instruction latency.
150 static constexpr uint32_t kGenericInstructionLatency = 1;
151 
152 class HScheduler;
153 
154 /**
155  * A node representing an `HInstruction` in the `SchedulingGraph`.
156  */
157 class SchedulingNode : public DeletableArenaObject<kArenaAllocScheduler> {
158  public:
SchedulingNode(HInstruction * instr,ScopedArenaAllocator * allocator,bool is_scheduling_barrier)159   SchedulingNode(HInstruction* instr, ScopedArenaAllocator* allocator, bool is_scheduling_barrier)
160       : latency_(0),
161         internal_latency_(0),
162         critical_path_(0),
163         instruction_(instr),
164         is_scheduling_barrier_(is_scheduling_barrier),
165         data_predecessors_(allocator->Adapter(kArenaAllocScheduler)),
166         other_predecessors_(allocator->Adapter(kArenaAllocScheduler)),
167         num_unscheduled_successors_(0) {
168     data_predecessors_.reserve(kPreallocatedPredecessors);
169   }
170 
AddDataPredecessor(SchedulingNode * predecessor)171   void AddDataPredecessor(SchedulingNode* predecessor) {
172     // Check whether the predecessor has been added earlier.
173     if (HasDataDependency(predecessor)) {
174       return;
175     }
176     data_predecessors_.push_back(predecessor);
177     predecessor->num_unscheduled_successors_++;
178   }
179 
GetDataPredecessors()180   const ScopedArenaVector<SchedulingNode*>& GetDataPredecessors() const {
181     return data_predecessors_;
182   }
183 
AddOtherPredecessor(SchedulingNode * predecessor)184   void AddOtherPredecessor(SchedulingNode* predecessor) {
185     // Check whether the predecessor has been added earlier.
186     // As an optimization of the scheduling graph, we don't need to create another dependency if
187     // there is a data dependency between scheduling nodes.
188     if (HasOtherDependency(predecessor) || HasDataDependency(predecessor)) {
189       return;
190     }
191     other_predecessors_.push_back(predecessor);
192     predecessor->num_unscheduled_successors_++;
193   }
194 
GetOtherPredecessors()195   const ScopedArenaVector<SchedulingNode*>& GetOtherPredecessors() const {
196     return other_predecessors_;
197   }
198 
DecrementNumberOfUnscheduledSuccessors()199   void DecrementNumberOfUnscheduledSuccessors() {
200     num_unscheduled_successors_--;
201   }
202 
MaybeUpdateCriticalPath(uint32_t other_critical_path)203   void MaybeUpdateCriticalPath(uint32_t other_critical_path) {
204     critical_path_ = std::max(critical_path_, other_critical_path);
205   }
206 
HasUnscheduledSuccessors()207   bool HasUnscheduledSuccessors() const {
208     return num_unscheduled_successors_ != 0;
209   }
210 
GetInstruction()211   HInstruction* GetInstruction() const { return instruction_; }
GetLatency()212   uint32_t GetLatency() const { return latency_; }
SetLatency(uint32_t latency)213   void SetLatency(uint32_t latency) { latency_ = latency; }
GetInternalLatency()214   uint32_t GetInternalLatency() const { return internal_latency_; }
SetInternalLatency(uint32_t internal_latency)215   void SetInternalLatency(uint32_t internal_latency) { internal_latency_ = internal_latency; }
GetCriticalPath()216   uint32_t GetCriticalPath() const { return critical_path_; }
IsSchedulingBarrier()217   bool IsSchedulingBarrier() const { return is_scheduling_barrier_; }
218 
HasDataDependency(const SchedulingNode * node)219   bool HasDataDependency(const SchedulingNode* node) const {
220     return ContainsElement(data_predecessors_, node);
221   }
222 
HasOtherDependency(const SchedulingNode * node)223   bool HasOtherDependency(const SchedulingNode* node) const {
224     return ContainsElement(other_predecessors_, node);
225   }
226 
227  private:
228   // The latency of this node. It represents the latency between the moment the
229   // last instruction for this node has executed to the moment the result
230   // produced by this node is available to users.
231   uint32_t latency_;
232   // This represents the time spent *within* the generated code for this node.
233   // It should be zero for nodes that only generate a single instruction.
234   uint32_t internal_latency_;
235 
236   // The critical path from this instruction to the end of scheduling. It is
237   // used by the scheduling heuristics to measure the priority of this instruction.
238   // It is defined as
239   //     critical_path_ = latency_ + max((use.internal_latency_ + use.critical_path_) for all uses)
240   // (Note that here 'uses' is equivalent to 'data successors'. Also see comments in
241   // `HScheduler::Schedule(SchedulingNode* scheduling_node)`).
242   uint32_t critical_path_;
243 
244   // The instruction that this node represents.
245   HInstruction* const instruction_;
246 
247   // If a node is scheduling barrier, other nodes cannot be scheduled before it.
248   const bool is_scheduling_barrier_;
249 
250   // The lists of predecessors. They cannot be scheduled before this node. Once
251   // this node is scheduled, we check whether any of its predecessors has become a
252   // valid candidate for scheduling.
253   // Predecessors in `data_predecessors_` are data dependencies. Those in
254   // `other_predecessors_` contain side-effect dependencies, environment
255   // dependencies, and scheduling barrier dependencies.
256   ScopedArenaVector<SchedulingNode*> data_predecessors_;
257   ScopedArenaVector<SchedulingNode*> other_predecessors_;
258 
259   // The number of unscheduled successors for this node. This number is
260   // decremented as successors are scheduled. When it reaches zero this node
261   // becomes a valid candidate to schedule.
262   uint32_t num_unscheduled_successors_;
263 
264   static constexpr size_t kPreallocatedPredecessors = 4;
265 };
266 
267 /*
268  * Provide analysis of instruction dependencies (side effects) which are not in a form of explicit
269  * def-use data dependencies.
270  */
271 class SideEffectDependencyAnalysis {
272  public:
SideEffectDependencyAnalysis(const HeapLocationCollector * heap_location_collector)273   explicit SideEffectDependencyAnalysis(const HeapLocationCollector* heap_location_collector)
274       : memory_dependency_analysis_(heap_location_collector) {}
275 
HasSideEffectDependency(HInstruction * instr1,HInstruction * instr2)276   bool HasSideEffectDependency(HInstruction* instr1, HInstruction* instr2) const {
277     if (memory_dependency_analysis_.HasMemoryDependency(instr1, instr2)) {
278       return true;
279     }
280 
281     // Even if above memory dependency check has passed, it is still necessary to
282     // check dependencies between instructions that can throw and instructions
283     // that write to memory.
284     if (HasExceptionDependency(instr1, instr2)) {
285       return true;
286     }
287 
288     return false;
289   }
290 
291  private:
292   static bool HasExceptionDependency(const HInstruction* instr1, const HInstruction* instr2);
293   static bool HasReorderingDependency(const HInstruction* instr1, const HInstruction* instr2);
294 
295   /*
296    * Memory dependency analysis of instructions based on their memory side effects
297    * and heap location information from the LCA pass if it is provided.
298    */
299   class MemoryDependencyAnalysis {
300    public:
MemoryDependencyAnalysis(const HeapLocationCollector * heap_location_collector)301     explicit MemoryDependencyAnalysis(const HeapLocationCollector* heap_location_collector)
302         : heap_location_collector_(heap_location_collector) {}
303 
304     bool HasMemoryDependency(HInstruction* instr1, HInstruction* instr2) const;
305 
306    private:
307     bool ArrayAccessMayAlias(HInstruction* instr1, HInstruction* instr2) const;
308     bool FieldAccessMayAlias(const HInstruction* instr1, const HInstruction* instr2) const;
309     size_t ArrayAccessHeapLocation(HInstruction* instruction) const;
310     size_t FieldAccessHeapLocation(const HInstruction* instruction) const;
311 
312     const HeapLocationCollector* const heap_location_collector_;
313   };
314 
315   MemoryDependencyAnalysis memory_dependency_analysis_;
316 };
317 
318 /*
319  * Directed acyclic graph for scheduling.
320  */
321 class SchedulingGraph : public ValueObject {
322  public:
SchedulingGraph(ScopedArenaAllocator * allocator,const HeapLocationCollector * heap_location_collector)323   SchedulingGraph(ScopedArenaAllocator* allocator,
324                   const HeapLocationCollector* heap_location_collector)
325       : allocator_(allocator),
326         contains_scheduling_barrier_(false),
327         nodes_map_(allocator_->Adapter(kArenaAllocScheduler)),
328         side_effect_dependency_analysis_(heap_location_collector) {}
329 
330   SchedulingNode* AddNode(HInstruction* instr, bool is_scheduling_barrier = false) {
331     std::unique_ptr<SchedulingNode> node(
332         new (allocator_) SchedulingNode(instr, allocator_, is_scheduling_barrier));
333     SchedulingNode* result = node.get();
334     nodes_map_.insert(std::make_pair(instr, std::move(node)));
335     contains_scheduling_barrier_ |= is_scheduling_barrier;
336     AddDependencies(result, is_scheduling_barrier);
337     return result;
338   }
339 
GetNode(const HInstruction * instr)340   SchedulingNode* GetNode(const HInstruction* instr) const {
341     auto it = nodes_map_.find(instr);
342     if (it == nodes_map_.end()) {
343       return nullptr;
344     } else {
345       return it->second.get();
346     }
347   }
348 
Size()349   size_t Size() const {
350     return nodes_map_.size();
351   }
352 
353   // Dump the scheduling graph, in dot file format, appending it to the file
354   // `scheduling_graphs.dot`.
355   void DumpAsDotGraph(const std::string& description,
356                       const ScopedArenaVector<SchedulingNode*>& initial_candidates);
357 
358  protected:
359   void AddDependency(SchedulingNode* node, SchedulingNode* dependency, bool is_data_dependency);
AddDataDependency(SchedulingNode * node,SchedulingNode * dependency)360   void AddDataDependency(SchedulingNode* node, SchedulingNode* dependency) {
361     AddDependency(node, dependency, /*is_data_dependency*/true);
362   }
AddOtherDependency(SchedulingNode * node,SchedulingNode * dependency)363   void AddOtherDependency(SchedulingNode* node, SchedulingNode* dependency) {
364     AddDependency(node, dependency, /*is_data_dependency*/false);
365   }
366 
367   // Analyze whether the scheduling node has cross-iteration dependencies which mean it uses
368   // values defined on the previous iteration.
369   //
370   // Supported cases:
371   //
372   //   L:
373   //     v2 = loop_head_phi(v1)
374   //     instr1(v2)
375   //     v1 = instr2
376   //     goto L
377   //
378   // In such cases moving instr2 before instr1 creates intersecting live ranges
379   // of v1 and v2. As a result a separate register is needed to keep the value
380   // defined by instr2 which is only used on the next iteration.
381   // If instr2 is not moved, no additional register is needed. The register
382   // used by instr1 is reused.
383   // To prevent such a situation a "other" dependency between instr1 and instr2 must be set.
384   void AddCrossIterationDependencies(SchedulingNode* node);
385 
386   // Add dependencies nodes for the given `SchedulingNode`: inputs, environments, and side-effects.
387   void AddDependencies(SchedulingNode* node, bool is_scheduling_barrier = false);
388 
389   ScopedArenaAllocator* const allocator_;
390   bool contains_scheduling_barrier_;
391   ScopedArenaHashMap<const HInstruction*, std::unique_ptr<SchedulingNode>> nodes_map_;
392   SideEffectDependencyAnalysis side_effect_dependency_analysis_;
393 };
394 
395 /*
396  * The visitors derived from this base class are used by schedulers to evaluate
397  * the latencies of `HInstruction`s.
398  */
399 class SchedulingLatencyVisitor : public HGraphDelegateVisitor {
400  public:
401   // This class and its sub-classes will never be used to drive a visit of an
402   // `HGraph` but only to visit `HInstructions` one at a time, so we do not need
403   // to pass a valid graph to `HGraphDelegateVisitor()`.
SchedulingLatencyVisitor()404   SchedulingLatencyVisitor()
405       : HGraphDelegateVisitor(nullptr),
406         last_visited_latency_(0),
407         last_visited_internal_latency_(0) {}
408 
VisitInstruction(HInstruction * instruction)409   void VisitInstruction(HInstruction* instruction) override {
410     LOG(FATAL) << "Error visiting " << instruction->DebugName() << ". "
411         "Architecture-specific scheduling latency visitors must handle all instructions"
412         " (potentially by overriding the generic `VisitInstruction()`.";
413     UNREACHABLE();
414   }
415 
Visit(HInstruction * instruction)416   void Visit(HInstruction* instruction) {
417     instruction->Accept(this);
418   }
419 
CalculateLatency(SchedulingNode * node)420   void CalculateLatency(SchedulingNode* node) {
421     // By default nodes have no internal latency.
422     last_visited_internal_latency_ = 0;
423     Visit(node->GetInstruction());
424   }
425 
GetLastVisitedLatency()426   uint32_t GetLastVisitedLatency() const { return last_visited_latency_; }
GetLastVisitedInternalLatency()427   uint32_t GetLastVisitedInternalLatency() const { return last_visited_internal_latency_; }
428 
429  protected:
430   // The latency of the most recent visited SchedulingNode.
431   // This is for reporting the latency value to the user of this visitor.
432   uint32_t last_visited_latency_;
433   // This represents the time spent *within* the generated code for the most recent visited
434   // SchedulingNode. This is for reporting the internal latency value to the user of this visitor.
435   uint32_t last_visited_internal_latency_;
436 };
437 
438 class SchedulingNodeSelector : public ArenaObject<kArenaAllocScheduler> {
439  public:
Reset()440   virtual void Reset() {}
441   virtual SchedulingNode* PopHighestPriorityNode(ScopedArenaVector<SchedulingNode*>* nodes,
442                                                  const SchedulingGraph& graph) = 0;
~SchedulingNodeSelector()443   virtual ~SchedulingNodeSelector() {}
444  protected:
DeleteNodeAtIndex(ScopedArenaVector<SchedulingNode * > * nodes,size_t index)445   static void DeleteNodeAtIndex(ScopedArenaVector<SchedulingNode*>* nodes, size_t index) {
446     (*nodes)[index] = nodes->back();
447     nodes->pop_back();
448   }
449 };
450 
451 /*
452  * Select a `SchedulingNode` at random within the candidates.
453  */
454 class RandomSchedulingNodeSelector : public SchedulingNodeSelector {
455  public:
RandomSchedulingNodeSelector()456   RandomSchedulingNodeSelector() : seed_(0) {
457     seed_  = static_cast<uint32_t>(NanoTime());
458     srand(seed_);
459   }
460 
PopHighestPriorityNode(ScopedArenaVector<SchedulingNode * > * nodes,const SchedulingGraph & graph)461   SchedulingNode* PopHighestPriorityNode(ScopedArenaVector<SchedulingNode*>* nodes,
462                                          const SchedulingGraph& graph) override {
463     UNUSED(graph);
464     DCHECK(!nodes->empty());
465     size_t select = rand_r(&seed_) % nodes->size();
466     SchedulingNode* select_node = (*nodes)[select];
467     DeleteNodeAtIndex(nodes, select);
468     return select_node;
469   }
470 
471   uint32_t seed_;
472 };
473 
474 /*
475  * Select a `SchedulingNode` according to critical path information,
476  * with heuristics to favor certain instruction patterns like materialized condition.
477  */
478 class CriticalPathSchedulingNodeSelector : public SchedulingNodeSelector {
479  public:
CriticalPathSchedulingNodeSelector()480   CriticalPathSchedulingNodeSelector() : prev_select_(nullptr) {}
481 
Reset()482   void Reset() override { prev_select_ = nullptr; }
483   SchedulingNode* PopHighestPriorityNode(ScopedArenaVector<SchedulingNode*>* nodes,
484                                          const SchedulingGraph& graph) override;
485 
486  protected:
487   SchedulingNode* GetHigherPrioritySchedulingNode(SchedulingNode* candidate,
488                                                   SchedulingNode* check) const;
489 
490   SchedulingNode* SelectMaterializedCondition(ScopedArenaVector<SchedulingNode*>* nodes,
491                                               const SchedulingGraph& graph) const;
492 
493  private:
494   const SchedulingNode* prev_select_;
495 };
496 
497 class HScheduler {
498  public:
HScheduler(SchedulingLatencyVisitor * latency_visitor,SchedulingNodeSelector * selector)499   HScheduler(SchedulingLatencyVisitor* latency_visitor, SchedulingNodeSelector* selector)
500       : latency_visitor_(latency_visitor),
501         selector_(selector),
502         only_optimize_loop_blocks_(true),
503         cursor_(nullptr) {}
~HScheduler()504   virtual ~HScheduler() {}
505 
506   void Schedule(HGraph* graph);
507 
SetOnlyOptimizeLoopBlocks(bool loop_only)508   void SetOnlyOptimizeLoopBlocks(bool loop_only) { only_optimize_loop_blocks_ = loop_only; }
509 
510   // Instructions can not be rescheduled across a scheduling barrier.
511   virtual bool IsSchedulingBarrier(const HInstruction* instruction) const;
512 
513  protected:
514   void Schedule(HBasicBlock* block, const HeapLocationCollector* heap_location_collector);
515   void Schedule(SchedulingNode* scheduling_node,
516                 /*inout*/ ScopedArenaVector<SchedulingNode*>* candidates);
517   void Schedule(HInstruction* instruction);
518 
519   // Any instruction returning `false` via this method will prevent its
520   // containing basic block from being scheduled.
521   // This method is used to restrict scheduling to instructions that we know are
522   // safe to handle.
523   //
524   // For newly introduced instructions by default HScheduler::IsSchedulable returns false.
525   // HScheduler${ARCH}::IsSchedulable can be overridden to return true for an instruction (see
526   // scheduler_arm64.h for example) if it is safe to schedule it; in this case one *must* also
527   // look at/update HScheduler${ARCH}::IsSchedulingBarrier for this instruction.
528   virtual bool IsSchedulable(const HInstruction* instruction) const;
529   bool IsSchedulable(const HBasicBlock* block) const;
530 
CalculateLatency(SchedulingNode * node)531   void CalculateLatency(SchedulingNode* node) {
532     latency_visitor_->CalculateLatency(node);
533     node->SetLatency(latency_visitor_->GetLastVisitedLatency());
534     node->SetInternalLatency(latency_visitor_->GetLastVisitedInternalLatency());
535   }
536 
537   SchedulingLatencyVisitor* const latency_visitor_;
538   SchedulingNodeSelector* const selector_;
539   bool only_optimize_loop_blocks_;
540 
541   // A pointer indicating where the next instruction to be scheduled will be inserted.
542   HInstruction* cursor_;
543 
544  private:
545   DISALLOW_COPY_AND_ASSIGN(HScheduler);
546 };
547 
548 class HInstructionScheduling : public HOptimization {
549  public:
550   HInstructionScheduling(HGraph* graph,
551                          InstructionSet instruction_set,
552                          CodeGenerator* cg = nullptr,
553                          const char* name = kInstructionSchedulingPassName)
HOptimization(graph,name)554       : HOptimization(graph, name),
555         codegen_(cg),
556         instruction_set_(instruction_set) {}
557 
Run()558   bool Run() override {
559     return Run(/*only_optimize_loop_blocks*/ true, /*schedule_randomly*/ false);
560   }
561 
562   bool Run(bool only_optimize_loop_blocks, bool schedule_randomly);
563 
564   static constexpr const char* kInstructionSchedulingPassName = "scheduler";
565 
566  private:
567   CodeGenerator* const codegen_;
568   const InstructionSet instruction_set_;
569   DISALLOW_COPY_AND_ASSIGN(HInstructionScheduling);
570 };
571 
572 }  // namespace art
573 
574 #endif  // ART_COMPILER_OPTIMIZING_SCHEDULER_H_
575