• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <string>
18 
19 #include "scheduler.h"
20 
21 #include "base/scoped_arena_allocator.h"
22 #include "base/scoped_arena_containers.h"
23 #include "data_type-inl.h"
24 #include "prepare_for_register_allocation.h"
25 
26 #ifdef ART_ENABLE_CODEGEN_arm64
27 #include "scheduler_arm64.h"
28 #endif
29 
30 #ifdef ART_ENABLE_CODEGEN_arm
31 #include "scheduler_arm.h"
32 #endif
33 
34 namespace art {
35 
AddDependency(SchedulingNode * node,SchedulingNode * dependency,bool is_data_dependency)36 void SchedulingGraph::AddDependency(SchedulingNode* node,
37                                     SchedulingNode* dependency,
38                                     bool is_data_dependency) {
39   if (node == nullptr || dependency == nullptr) {
40     // A `nullptr` node indicates an instruction out of scheduling range (eg. in
41     // an other block), so we do not need to add a dependency edge to the graph.
42     return;
43   }
44 
45   if (is_data_dependency) {
46     if (!HasImmediateDataDependency(node, dependency)) {
47       node->AddDataPredecessor(dependency);
48     }
49   } else if (!HasImmediateOtherDependency(node, dependency)) {
50     node->AddOtherPredecessor(dependency);
51   }
52 }
53 
MayHaveReorderingDependency(SideEffects node,SideEffects other)54 static bool MayHaveReorderingDependency(SideEffects node, SideEffects other) {
55   // Read after write.
56   if (node.MayDependOn(other)) {
57     return true;
58   }
59 
60   // Write after read.
61   if (other.MayDependOn(node)) {
62     return true;
63   }
64 
65   // Memory write after write.
66   if (node.DoesAnyWrite() && other.DoesAnyWrite()) {
67     return true;
68   }
69 
70   return false;
71 }
72 
ArrayAccessHeapLocation(HInstruction * instruction) const73 size_t SchedulingGraph::ArrayAccessHeapLocation(HInstruction* instruction) const {
74   DCHECK(heap_location_collector_ != nullptr);
75   size_t heap_loc = heap_location_collector_->GetArrayHeapLocation(instruction);
76   // This array access should be analyzed and added to HeapLocationCollector before.
77   DCHECK(heap_loc != HeapLocationCollector::kHeapLocationNotFound);
78   return heap_loc;
79 }
80 
ArrayAccessMayAlias(HInstruction * node,HInstruction * other) const81 bool SchedulingGraph::ArrayAccessMayAlias(HInstruction* node,
82                                           HInstruction* other) const {
83   DCHECK(heap_location_collector_ != nullptr);
84   size_t node_heap_loc = ArrayAccessHeapLocation(node);
85   size_t other_heap_loc = ArrayAccessHeapLocation(other);
86 
87   // For example: arr[0] and arr[0]
88   if (node_heap_loc == other_heap_loc) {
89     return true;
90   }
91 
92   // For example: arr[0] and arr[i]
93   if (heap_location_collector_->MayAlias(node_heap_loc, other_heap_loc)) {
94     return true;
95   }
96 
97   return false;
98 }
99 
IsArrayAccess(const HInstruction * instruction)100 static bool IsArrayAccess(const HInstruction* instruction) {
101   return instruction->IsArrayGet() || instruction->IsArraySet();
102 }
103 
IsInstanceFieldAccess(const HInstruction * instruction)104 static bool IsInstanceFieldAccess(const HInstruction* instruction) {
105   return instruction->IsInstanceFieldGet() ||
106          instruction->IsInstanceFieldSet() ||
107          instruction->IsUnresolvedInstanceFieldGet() ||
108          instruction->IsUnresolvedInstanceFieldSet();
109 }
110 
IsStaticFieldAccess(const HInstruction * instruction)111 static bool IsStaticFieldAccess(const HInstruction* instruction) {
112   return instruction->IsStaticFieldGet() ||
113          instruction->IsStaticFieldSet() ||
114          instruction->IsUnresolvedStaticFieldGet() ||
115          instruction->IsUnresolvedStaticFieldSet();
116 }
117 
IsResolvedFieldAccess(const HInstruction * instruction)118 static bool IsResolvedFieldAccess(const HInstruction* instruction) {
119   return instruction->IsInstanceFieldGet() ||
120          instruction->IsInstanceFieldSet() ||
121          instruction->IsStaticFieldGet() ||
122          instruction->IsStaticFieldSet();
123 }
124 
IsUnresolvedFieldAccess(const HInstruction * instruction)125 static bool IsUnresolvedFieldAccess(const HInstruction* instruction) {
126   return instruction->IsUnresolvedInstanceFieldGet() ||
127          instruction->IsUnresolvedInstanceFieldSet() ||
128          instruction->IsUnresolvedStaticFieldGet() ||
129          instruction->IsUnresolvedStaticFieldSet();
130 }
131 
IsFieldAccess(const HInstruction * instruction)132 static bool IsFieldAccess(const HInstruction* instruction) {
133   return IsResolvedFieldAccess(instruction) || IsUnresolvedFieldAccess(instruction);
134 }
135 
GetFieldInfo(const HInstruction * instruction)136 static const FieldInfo* GetFieldInfo(const HInstruction* instruction) {
137   if (instruction->IsInstanceFieldGet()) {
138     return &instruction->AsInstanceFieldGet()->GetFieldInfo();
139   } else if (instruction->IsInstanceFieldSet()) {
140     return &instruction->AsInstanceFieldSet()->GetFieldInfo();
141   } else if (instruction->IsStaticFieldGet()) {
142     return &instruction->AsStaticFieldGet()->GetFieldInfo();
143   } else if (instruction->IsStaticFieldSet()) {
144     return &instruction->AsStaticFieldSet()->GetFieldInfo();
145   } else {
146     LOG(FATAL) << "Unexpected field access type";
147     UNREACHABLE();
148   }
149 }
150 
FieldAccessHeapLocation(HInstruction * obj,const FieldInfo * field) const151 size_t SchedulingGraph::FieldAccessHeapLocation(HInstruction* obj, const FieldInfo* field) const {
152   DCHECK(obj != nullptr);
153   DCHECK(field != nullptr);
154   DCHECK(heap_location_collector_ != nullptr);
155 
156   size_t heap_loc = heap_location_collector_->GetFieldHeapLocation(obj, field);
157   // This field access should be analyzed and added to HeapLocationCollector before.
158   DCHECK(heap_loc != HeapLocationCollector::kHeapLocationNotFound);
159 
160   return heap_loc;
161 }
162 
FieldAccessMayAlias(const HInstruction * node,const HInstruction * other) const163 bool SchedulingGraph::FieldAccessMayAlias(const HInstruction* node,
164                                           const HInstruction* other) const {
165   DCHECK(heap_location_collector_ != nullptr);
166 
167   // Static and instance field accesses should not alias.
168   if ((IsInstanceFieldAccess(node) && IsStaticFieldAccess(other)) ||
169       (IsStaticFieldAccess(node) && IsInstanceFieldAccess(other))) {
170     return false;
171   }
172 
173   // If either of the field accesses is unresolved.
174   if (IsUnresolvedFieldAccess(node) || IsUnresolvedFieldAccess(other)) {
175     // Conservatively treat these two accesses may alias.
176     return true;
177   }
178 
179   // If both fields accesses are resolved.
180   const FieldInfo* node_field = GetFieldInfo(node);
181   const FieldInfo* other_field = GetFieldInfo(other);
182 
183   size_t node_loc = FieldAccessHeapLocation(node->InputAt(0), node_field);
184   size_t other_loc = FieldAccessHeapLocation(other->InputAt(0), other_field);
185 
186   if (node_loc == other_loc) {
187     return true;
188   }
189 
190   if (!heap_location_collector_->MayAlias(node_loc, other_loc)) {
191     return false;
192   }
193 
194   return true;
195 }
196 
HasMemoryDependency(HInstruction * node,HInstruction * other) const197 bool SchedulingGraph::HasMemoryDependency(HInstruction* node,
198                                           HInstruction* other) const {
199   if (!MayHaveReorderingDependency(node->GetSideEffects(), other->GetSideEffects())) {
200     return false;
201   }
202 
203   if (heap_location_collector_ == nullptr ||
204       heap_location_collector_->GetNumberOfHeapLocations() == 0) {
205     // Without HeapLocation information from load store analysis,
206     // we cannot do further disambiguation analysis on these two instructions.
207     // Just simply say that those two instructions have memory dependency.
208     return true;
209   }
210 
211   if (IsArrayAccess(node) && IsArrayAccess(other)) {
212     return ArrayAccessMayAlias(node, other);
213   }
214   if (IsFieldAccess(node) && IsFieldAccess(other)) {
215     return FieldAccessMayAlias(node, other);
216   }
217 
218   // TODO(xueliang): LSA to support alias analysis among HVecLoad, HVecStore and ArrayAccess
219   if (node->IsVecMemoryOperation() && other->IsVecMemoryOperation()) {
220     return true;
221   }
222   if (node->IsVecMemoryOperation() && IsArrayAccess(other)) {
223     return true;
224   }
225   if (IsArrayAccess(node) && other->IsVecMemoryOperation()) {
226     return true;
227   }
228 
229   // Heap accesses of different kinds should not alias.
230   if (IsArrayAccess(node) && IsFieldAccess(other)) {
231     return false;
232   }
233   if (IsFieldAccess(node) && IsArrayAccess(other)) {
234     return false;
235   }
236   if (node->IsVecMemoryOperation() && IsFieldAccess(other)) {
237     return false;
238   }
239   if (IsFieldAccess(node) && other->IsVecMemoryOperation()) {
240     return false;
241   }
242 
243   // We conservatively treat all other cases having dependency,
244   // for example, Invoke and ArrayGet.
245   return true;
246 }
247 
HasExceptionDependency(const HInstruction * node,const HInstruction * other) const248 bool SchedulingGraph::HasExceptionDependency(const HInstruction* node,
249                                              const HInstruction* other) const {
250   if (other->CanThrow() && node->GetSideEffects().DoesAnyWrite()) {
251     return true;
252   }
253   if (other->GetSideEffects().DoesAnyWrite() && node->CanThrow()) {
254     return true;
255   }
256   if (other->CanThrow() && node->CanThrow()) {
257     return true;
258   }
259 
260   // Above checks should cover all cases where we cannot reorder two
261   // instructions which may throw exception.
262   return false;
263 }
264 
265 // Check whether `node` depends on `other`, taking into account `SideEffect`
266 // information and `CanThrow` information.
HasSideEffectDependency(HInstruction * node,HInstruction * other) const267 bool SchedulingGraph::HasSideEffectDependency(HInstruction* node,
268                                               HInstruction* other) const {
269   if (HasMemoryDependency(node, other)) {
270     return true;
271   }
272 
273   // Even if above memory dependency check has passed, it is still necessary to
274   // check dependencies between instructions that can throw and instructions
275   // that write to memory.
276   if (HasExceptionDependency(node, other)) {
277     return true;
278   }
279 
280   return false;
281 }
282 
283 // Check if the specified instruction is a better candidate which more likely will
284 // have other instructions depending on it.
IsBetterCandidateWithMoreLikelyDependencies(HInstruction * new_candidate,HInstruction * old_candidate)285 static bool IsBetterCandidateWithMoreLikelyDependencies(HInstruction* new_candidate,
286                                                         HInstruction* old_candidate) {
287   if (!new_candidate->GetSideEffects().Includes(old_candidate->GetSideEffects())) {
288     // Weaker side effects.
289     return false;
290   }
291   if (old_candidate->GetSideEffects().Includes(new_candidate->GetSideEffects())) {
292     // Same side effects, check if `new_candidate` has stronger `CanThrow()`.
293     return new_candidate->CanThrow() && !old_candidate->CanThrow();
294   } else {
295     // Stronger side effects, check if `new_candidate` has at least as strong `CanThrow()`.
296     return new_candidate->CanThrow() || !old_candidate->CanThrow();
297   }
298 }
299 
AddDependencies(HInstruction * instruction,bool is_scheduling_barrier)300 void SchedulingGraph::AddDependencies(HInstruction* instruction, bool is_scheduling_barrier) {
301   SchedulingNode* instruction_node = GetNode(instruction);
302 
303   // Define-use dependencies.
304   for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
305     AddDataDependency(GetNode(use.GetUser()), instruction_node);
306   }
307 
308   // Scheduling barrier dependencies.
309   DCHECK(!is_scheduling_barrier || contains_scheduling_barrier_);
310   if (contains_scheduling_barrier_) {
311     // A barrier depends on instructions after it. And instructions before the
312     // barrier depend on it.
313     for (HInstruction* other = instruction->GetNext(); other != nullptr; other = other->GetNext()) {
314       SchedulingNode* other_node = GetNode(other);
315       CHECK(other_node != nullptr)
316           << other->DebugName()
317           << " is in block " << other->GetBlock()->GetBlockId()
318           << ", and expected in block " << instruction->GetBlock()->GetBlockId();
319       bool other_is_barrier = other_node->IsSchedulingBarrier();
320       if (is_scheduling_barrier || other_is_barrier) {
321         AddOtherDependency(other_node, instruction_node);
322       }
323       if (other_is_barrier) {
324         // This other scheduling barrier guarantees ordering of instructions after
325         // it, so avoid creating additional useless dependencies in the graph.
326         // For example if we have
327         //     instr_1
328         //     barrier_2
329         //     instr_3
330         //     barrier_4
331         //     instr_5
332         // we only create the following non-data dependencies
333         //     1 -> 2
334         //     2 -> 3
335         //     2 -> 4
336         //     3 -> 4
337         //     4 -> 5
338         // and do not create
339         //     1 -> 4
340         //     2 -> 5
341         // Note that in this example we could also avoid creating the dependency
342         // `2 -> 4`.  But if we remove `instr_3` that dependency is required to
343         // order the barriers. So we generate it to avoid a special case.
344         break;
345       }
346     }
347   }
348 
349   // Side effect dependencies.
350   if (!instruction->GetSideEffects().DoesNothing() || instruction->CanThrow()) {
351     HInstruction* dep_chain_candidate = nullptr;
352     for (HInstruction* other = instruction->GetNext(); other != nullptr; other = other->GetNext()) {
353       SchedulingNode* other_node = GetNode(other);
354       if (other_node->IsSchedulingBarrier()) {
355         // We have reached a scheduling barrier so we can stop further
356         // processing.
357         DCHECK(HasImmediateOtherDependency(other_node, instruction_node));
358         break;
359       }
360       if (HasSideEffectDependency(other, instruction)) {
361         if (dep_chain_candidate != nullptr &&
362             HasSideEffectDependency(other, dep_chain_candidate)) {
363           // Skip an explicit dependency to reduce memory usage, rely on the transitive dependency.
364         } else {
365           AddOtherDependency(other_node, instruction_node);
366         }
367         // Check if `other` is a better candidate which more likely will have other instructions
368         // depending on it.
369         if (dep_chain_candidate == nullptr ||
370             IsBetterCandidateWithMoreLikelyDependencies(other, dep_chain_candidate)) {
371           dep_chain_candidate = other;
372         }
373       }
374     }
375   }
376 
377   // Environment dependencies.
378   // We do not need to process those if the instruction is a scheduling barrier,
379   // since the barrier already has non-data dependencies on all following
380   // instructions.
381   if (!is_scheduling_barrier) {
382     for (const HUseListNode<HEnvironment*>& use : instruction->GetEnvUses()) {
383       // Note that here we could stop processing if the environment holder is
384       // across a scheduling barrier. But checking this would likely require
385       // more work than simply iterating through environment uses.
386       AddOtherDependency(GetNode(use.GetUser()->GetHolder()), instruction_node);
387     }
388   }
389 }
390 
HasImmediateDataDependency(const SchedulingNode * node,const SchedulingNode * other) const391 bool SchedulingGraph::HasImmediateDataDependency(const SchedulingNode* node,
392                                                  const SchedulingNode* other) const {
393   return ContainsElement(node->GetDataPredecessors(), other);
394 }
395 
HasImmediateDataDependency(const HInstruction * instruction,const HInstruction * other_instruction) const396 bool SchedulingGraph::HasImmediateDataDependency(const HInstruction* instruction,
397                                                  const HInstruction* other_instruction) const {
398   const SchedulingNode* node = GetNode(instruction);
399   const SchedulingNode* other = GetNode(other_instruction);
400   if (node == nullptr || other == nullptr) {
401     // Both instructions must be in current basic block, i.e. the SchedulingGraph can see their
402     // corresponding SchedulingNode in the graph, and tell whether there is a dependency.
403     // Otherwise there is no dependency from SchedulingGraph's perspective, for example,
404     // instruction and other_instruction are in different basic blocks.
405     return false;
406   }
407   return HasImmediateDataDependency(node, other);
408 }
409 
HasImmediateOtherDependency(const SchedulingNode * node,const SchedulingNode * other) const410 bool SchedulingGraph::HasImmediateOtherDependency(const SchedulingNode* node,
411                                                   const SchedulingNode* other) const {
412   return ContainsElement(node->GetOtherPredecessors(), other);
413 }
414 
HasImmediateOtherDependency(const HInstruction * instruction,const HInstruction * other_instruction) const415 bool SchedulingGraph::HasImmediateOtherDependency(const HInstruction* instruction,
416                                                   const HInstruction* other_instruction) const {
417   const SchedulingNode* node = GetNode(instruction);
418   const SchedulingNode* other = GetNode(other_instruction);
419   if (node == nullptr || other == nullptr) {
420     // Both instructions must be in current basic block, i.e. the SchedulingGraph can see their
421     // corresponding SchedulingNode in the graph, and tell whether there is a dependency.
422     // Otherwise there is no dependency from SchedulingGraph's perspective, for example,
423     // instruction and other_instruction are in different basic blocks.
424     return false;
425   }
426   return HasImmediateOtherDependency(node, other);
427 }
428 
InstructionTypeId(const HInstruction * instruction)429 static const std::string InstructionTypeId(const HInstruction* instruction) {
430   return DataType::TypeId(instruction->GetType()) + std::to_string(instruction->GetId());
431 }
432 
433 // Ideally we would reuse the graph visualizer code, but it is not available
434 // from here and it is not worth moving all that code only for our use.
DumpAsDotNode(std::ostream & output,const SchedulingNode * node)435 static void DumpAsDotNode(std::ostream& output, const SchedulingNode* node) {
436   const HInstruction* instruction = node->GetInstruction();
437   // Use the instruction typed id as the node identifier.
438   std::string instruction_id = InstructionTypeId(instruction);
439   output << instruction_id << "[shape=record, label=\""
440       << instruction_id << ' ' << instruction->DebugName() << " [";
441   // List the instruction's inputs in its description. When visualizing the
442   // graph this helps differentiating data inputs from other dependencies.
443   const char* seperator = "";
444   for (const HInstruction* input : instruction->GetInputs()) {
445     output << seperator << InstructionTypeId(input);
446     seperator = ",";
447   }
448   output << "]";
449   // Other properties of the node.
450   output << "\\ninternal_latency: " << node->GetInternalLatency();
451   output << "\\ncritical_path: " << node->GetCriticalPath();
452   if (node->IsSchedulingBarrier()) {
453     output << "\\n(barrier)";
454   }
455   output << "\"];\n";
456   // We want program order to go from top to bottom in the graph output, so we
457   // reverse the edges and specify `dir=back`.
458   for (const SchedulingNode* predecessor : node->GetDataPredecessors()) {
459     const HInstruction* predecessor_instruction = predecessor->GetInstruction();
460     output << InstructionTypeId(predecessor_instruction) << ":s -> " << instruction_id << ":n "
461         << "[label=\"" << predecessor->GetLatency() << "\",dir=back]\n";
462   }
463   for (const SchedulingNode* predecessor : node->GetOtherPredecessors()) {
464     const HInstruction* predecessor_instruction = predecessor->GetInstruction();
465     output << InstructionTypeId(predecessor_instruction) << ":s -> " << instruction_id << ":n "
466         << "[dir=back,color=blue]\n";
467   }
468 }
469 
DumpAsDotGraph(const std::string & description,const ScopedArenaVector<SchedulingNode * > & initial_candidates)470 void SchedulingGraph::DumpAsDotGraph(const std::string& description,
471                                      const ScopedArenaVector<SchedulingNode*>& initial_candidates) {
472   // TODO(xueliang): ideally we should move scheduling information into HInstruction, after that
473   // we should move this dotty graph dump feature to visualizer, and have a compiler option for it.
474   std::ofstream output("scheduling_graphs.dot", std::ofstream::out | std::ofstream::app);
475   // Description of this graph, as a comment.
476   output << "// " << description << "\n";
477   // Start the dot graph. Use an increasing index for easier differentiation.
478   output << "digraph G {\n";
479   for (const auto& entry : nodes_map_) {
480     SchedulingNode* node = entry.second.get();
481     DumpAsDotNode(output, node);
482   }
483   // Create a fake 'end_of_scheduling' node to help visualization of critical_paths.
484   for (SchedulingNode* node : initial_candidates) {
485     const HInstruction* instruction = node->GetInstruction();
486     output << InstructionTypeId(instruction) << ":s -> end_of_scheduling:n "
487       << "[label=\"" << node->GetLatency() << "\",dir=back]\n";
488   }
489   // End of the dot graph.
490   output << "}\n";
491   output.close();
492 }
493 
SelectMaterializedCondition(ScopedArenaVector<SchedulingNode * > * nodes,const SchedulingGraph & graph) const494 SchedulingNode* CriticalPathSchedulingNodeSelector::SelectMaterializedCondition(
495     ScopedArenaVector<SchedulingNode*>* nodes, const SchedulingGraph& graph) const {
496   // Schedule condition inputs that can be materialized immediately before their use.
497   // In following example, after we've scheduled HSelect, we want LessThan to be scheduled
498   // immediately, because it is a materialized condition, and will be emitted right before HSelect
499   // in codegen phase.
500   //
501   // i20 HLessThan [...]                  HLessThan    HAdd      HAdd
502   // i21 HAdd [...]                ===>      |          |         |
503   // i22 HAdd [...]                          +----------+---------+
504   // i23 HSelect [i21, i22, i20]                     HSelect
505 
506   if (prev_select_ == nullptr) {
507     return nullptr;
508   }
509 
510   const HInstruction* instruction = prev_select_->GetInstruction();
511   const HCondition* condition = nullptr;
512   DCHECK(instruction != nullptr);
513 
514   if (instruction->IsIf()) {
515     condition = instruction->AsIf()->InputAt(0)->AsCondition();
516   } else if (instruction->IsSelect()) {
517     condition = instruction->AsSelect()->GetCondition()->AsCondition();
518   }
519 
520   SchedulingNode* condition_node = (condition != nullptr) ? graph.GetNode(condition) : nullptr;
521 
522   if ((condition_node != nullptr) &&
523       condition->HasOnlyOneNonEnvironmentUse() &&
524       ContainsElement(*nodes, condition_node)) {
525     DCHECK(!condition_node->HasUnscheduledSuccessors());
526     // Remove the condition from the list of candidates and schedule it.
527     RemoveElement(*nodes, condition_node);
528     return condition_node;
529   }
530 
531   return nullptr;
532 }
533 
PopHighestPriorityNode(ScopedArenaVector<SchedulingNode * > * nodes,const SchedulingGraph & graph)534 SchedulingNode* CriticalPathSchedulingNodeSelector::PopHighestPriorityNode(
535     ScopedArenaVector<SchedulingNode*>* nodes, const SchedulingGraph& graph) {
536   DCHECK(!nodes->empty());
537   SchedulingNode* select_node = nullptr;
538 
539   // Optimize for materialized condition and its emit before use scenario.
540   select_node = SelectMaterializedCondition(nodes, graph);
541 
542   if (select_node == nullptr) {
543     // Get highest priority node based on critical path information.
544     select_node = (*nodes)[0];
545     size_t select = 0;
546     for (size_t i = 1, e = nodes->size(); i < e; i++) {
547       SchedulingNode* check = (*nodes)[i];
548       SchedulingNode* candidate = (*nodes)[select];
549       select_node = GetHigherPrioritySchedulingNode(candidate, check);
550       if (select_node == check) {
551         select = i;
552       }
553     }
554     DeleteNodeAtIndex(nodes, select);
555   }
556 
557   prev_select_ = select_node;
558   return select_node;
559 }
560 
GetHigherPrioritySchedulingNode(SchedulingNode * candidate,SchedulingNode * check) const561 SchedulingNode* CriticalPathSchedulingNodeSelector::GetHigherPrioritySchedulingNode(
562     SchedulingNode* candidate, SchedulingNode* check) const {
563   uint32_t candidate_path = candidate->GetCriticalPath();
564   uint32_t check_path = check->GetCriticalPath();
565   // First look at the critical_path.
566   if (check_path != candidate_path) {
567     return check_path < candidate_path ? check : candidate;
568   }
569   // If both critical paths are equal, schedule instructions with a higher latency
570   // first in program order.
571   return check->GetLatency() < candidate->GetLatency() ? check : candidate;
572 }
573 
Schedule(HGraph * graph)574 void HScheduler::Schedule(HGraph* graph) {
575   // We run lsa here instead of in a separate pass to better control whether we
576   // should run the analysis or not.
577   const HeapLocationCollector* heap_location_collector = nullptr;
578   LoadStoreAnalysis lsa(graph);
579   if (!only_optimize_loop_blocks_ || graph->HasLoops()) {
580     lsa.Run();
581     heap_location_collector = &lsa.GetHeapLocationCollector();
582   }
583 
584   for (HBasicBlock* block : graph->GetReversePostOrder()) {
585     if (IsSchedulable(block)) {
586       Schedule(block, heap_location_collector);
587     }
588   }
589 }
590 
Schedule(HBasicBlock * block,const HeapLocationCollector * heap_location_collector)591 void HScheduler::Schedule(HBasicBlock* block,
592                           const HeapLocationCollector* heap_location_collector) {
593   ScopedArenaAllocator allocator(block->GetGraph()->GetArenaStack());
594   ScopedArenaVector<SchedulingNode*> scheduling_nodes(allocator.Adapter(kArenaAllocScheduler));
595 
596   // Build the scheduling graph.
597   SchedulingGraph scheduling_graph(this, &allocator, heap_location_collector);
598   for (HBackwardInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
599     HInstruction* instruction = it.Current();
600     CHECK_EQ(instruction->GetBlock(), block)
601         << instruction->DebugName()
602         << " is in block " << instruction->GetBlock()->GetBlockId()
603         << ", and expected in block " << block->GetBlockId();
604     SchedulingNode* node = scheduling_graph.AddNode(instruction, IsSchedulingBarrier(instruction));
605     CalculateLatency(node);
606     scheduling_nodes.push_back(node);
607   }
608 
609   if (scheduling_graph.Size() <= 1) {
610     return;
611   }
612 
613   cursor_ = block->GetLastInstruction();
614 
615   // The list of candidates for scheduling. A node becomes a candidate when all
616   // its predecessors have been scheduled.
617   ScopedArenaVector<SchedulingNode*> candidates(allocator.Adapter(kArenaAllocScheduler));
618 
619   // Find the initial candidates for scheduling.
620   for (SchedulingNode* node : scheduling_nodes) {
621     if (!node->HasUnscheduledSuccessors()) {
622       node->MaybeUpdateCriticalPath(node->GetLatency());
623       candidates.push_back(node);
624     }
625   }
626 
627   ScopedArenaVector<SchedulingNode*> initial_candidates(allocator.Adapter(kArenaAllocScheduler));
628   if (kDumpDotSchedulingGraphs) {
629     // Remember the list of initial candidates for debug output purposes.
630     initial_candidates.assign(candidates.begin(), candidates.end());
631   }
632 
633   // Schedule all nodes.
634   selector_->Reset();
635   while (!candidates.empty()) {
636     SchedulingNode* node = selector_->PopHighestPriorityNode(&candidates, scheduling_graph);
637     Schedule(node, &candidates);
638   }
639 
640   if (kDumpDotSchedulingGraphs) {
641     // Dump the graph in `dot` format.
642     HGraph* graph = block->GetGraph();
643     std::stringstream description;
644     description << graph->GetDexFile().PrettyMethod(graph->GetMethodIdx())
645         << " B" << block->GetBlockId();
646     scheduling_graph.DumpAsDotGraph(description.str(), initial_candidates);
647   }
648 }
649 
Schedule(SchedulingNode * scheduling_node,ScopedArenaVector<SchedulingNode * > * candidates)650 void HScheduler::Schedule(SchedulingNode* scheduling_node,
651                           /*inout*/ ScopedArenaVector<SchedulingNode*>* candidates) {
652   // Check whether any of the node's predecessors will be valid candidates after
653   // this node is scheduled.
654   uint32_t path_to_node = scheduling_node->GetCriticalPath();
655   for (SchedulingNode* predecessor : scheduling_node->GetDataPredecessors()) {
656     predecessor->MaybeUpdateCriticalPath(
657         path_to_node + predecessor->GetInternalLatency() + predecessor->GetLatency());
658     predecessor->DecrementNumberOfUnscheduledSuccessors();
659     if (!predecessor->HasUnscheduledSuccessors()) {
660       candidates->push_back(predecessor);
661     }
662   }
663   for (SchedulingNode* predecessor : scheduling_node->GetOtherPredecessors()) {
664     // Do not update the critical path.
665     // The 'other' (so 'non-data') dependencies (usually) do not represent a
666     // 'material' dependency of nodes on others. They exist for program
667     // correctness. So we do not use them to compute the critical path.
668     predecessor->DecrementNumberOfUnscheduledSuccessors();
669     if (!predecessor->HasUnscheduledSuccessors()) {
670       candidates->push_back(predecessor);
671     }
672   }
673 
674   Schedule(scheduling_node->GetInstruction());
675 }
676 
677 // Move an instruction after cursor instruction inside one basic block.
MoveAfterInBlock(HInstruction * instruction,HInstruction * cursor)678 static void MoveAfterInBlock(HInstruction* instruction, HInstruction* cursor) {
679   DCHECK_EQ(instruction->GetBlock(), cursor->GetBlock());
680   DCHECK_NE(cursor, cursor->GetBlock()->GetLastInstruction());
681   DCHECK(!instruction->IsControlFlow());
682   DCHECK(!cursor->IsControlFlow());
683   instruction->MoveBefore(cursor->GetNext(), /* do_checks= */ false);
684 }
685 
Schedule(HInstruction * instruction)686 void HScheduler::Schedule(HInstruction* instruction) {
687   if (instruction == cursor_) {
688     cursor_ = cursor_->GetPrevious();
689   } else {
690     MoveAfterInBlock(instruction, cursor_);
691   }
692 }
693 
IsSchedulable(const HInstruction * instruction) const694 bool HScheduler::IsSchedulable(const HInstruction* instruction) const {
695   // We want to avoid exhaustively listing all instructions, so we first check
696   // for instruction categories that we know are safe.
697   if (instruction->IsControlFlow() ||
698       instruction->IsConstant()) {
699     return true;
700   }
701   // Currently all unary and binary operations are safe to schedule, so avoid
702   // checking for each of them individually.
703   // Since nothing prevents a new scheduling-unsafe HInstruction to subclass
704   // HUnaryOperation (or HBinaryOperation), check in debug mode that we have
705   // the exhaustive lists here.
706   if (instruction->IsUnaryOperation()) {
707     DCHECK(instruction->IsAbs() ||
708            instruction->IsBooleanNot() ||
709            instruction->IsNot() ||
710            instruction->IsNeg()) << "unexpected instruction " << instruction->DebugName();
711     return true;
712   }
713   if (instruction->IsBinaryOperation()) {
714     DCHECK(instruction->IsAdd() ||
715            instruction->IsAnd() ||
716            instruction->IsCompare() ||
717            instruction->IsCondition() ||
718            instruction->IsDiv() ||
719            instruction->IsMin() ||
720            instruction->IsMax() ||
721            instruction->IsMul() ||
722            instruction->IsOr() ||
723            instruction->IsRem() ||
724            instruction->IsRor() ||
725            instruction->IsShl() ||
726            instruction->IsShr() ||
727            instruction->IsSub() ||
728            instruction->IsUShr() ||
729            instruction->IsXor()) << "unexpected instruction " << instruction->DebugName();
730     return true;
731   }
732   // The scheduler should not see any of these.
733   DCHECK(!instruction->IsParallelMove()) << "unexpected instruction " << instruction->DebugName();
734   // List of instructions explicitly excluded:
735   //    HClearException
736   //    HClinitCheck
737   //    HDeoptimize
738   //    HLoadClass
739   //    HLoadException
740   //    HMemoryBarrier
741   //    HMonitorOperation
742   //    HNativeDebugInfo
743   //    HThrow
744   //    HTryBoundary
745   // TODO: Some of the instructions above may be safe to schedule (maybe as
746   // scheduling barriers).
747   return instruction->IsArrayGet() ||
748       instruction->IsArraySet() ||
749       instruction->IsArrayLength() ||
750       instruction->IsBoundType() ||
751       instruction->IsBoundsCheck() ||
752       instruction->IsCheckCast() ||
753       instruction->IsClassTableGet() ||
754       instruction->IsCurrentMethod() ||
755       instruction->IsDivZeroCheck() ||
756       (instruction->IsInstanceFieldGet() && !instruction->AsInstanceFieldGet()->IsVolatile()) ||
757       (instruction->IsInstanceFieldSet() && !instruction->AsInstanceFieldSet()->IsVolatile()) ||
758       instruction->IsInstanceOf() ||
759       instruction->IsInvokeInterface() ||
760       instruction->IsInvokeStaticOrDirect() ||
761       instruction->IsInvokeUnresolved() ||
762       instruction->IsInvokeVirtual() ||
763       instruction->IsLoadString() ||
764       instruction->IsNewArray() ||
765       instruction->IsNewInstance() ||
766       instruction->IsNullCheck() ||
767       instruction->IsPackedSwitch() ||
768       instruction->IsParameterValue() ||
769       instruction->IsPhi() ||
770       instruction->IsReturn() ||
771       instruction->IsReturnVoid() ||
772       instruction->IsSelect() ||
773       (instruction->IsStaticFieldGet() && !instruction->AsStaticFieldGet()->IsVolatile()) ||
774       (instruction->IsStaticFieldSet() && !instruction->AsStaticFieldSet()->IsVolatile()) ||
775       instruction->IsSuspendCheck() ||
776       instruction->IsTypeConversion();
777 }
778 
IsSchedulable(const HBasicBlock * block) const779 bool HScheduler::IsSchedulable(const HBasicBlock* block) const {
780   // We may be only interested in loop blocks.
781   if (only_optimize_loop_blocks_ && !block->IsInLoop()) {
782     return false;
783   }
784   if (block->GetTryCatchInformation() != nullptr) {
785     // Do not schedule blocks that are part of try-catch.
786     // Because scheduler cannot see if catch block has assumptions on the instruction order in
787     // the try block. In following example, if we enable scheduler for the try block,
788     // MulitiplyAccumulate may be scheduled before DivZeroCheck,
789     // which can result in an incorrect value in the catch block.
790     //   try {
791     //     a = a/b;    // DivZeroCheck
792     //                 // Div
793     //     c = c*d+e;  // MulitiplyAccumulate
794     //   } catch {System.out.print(c); }
795     return false;
796   }
797   // Check whether all instructions in this block are schedulable.
798   for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
799     if (!IsSchedulable(it.Current())) {
800       return false;
801     }
802   }
803   return true;
804 }
805 
IsSchedulingBarrier(const HInstruction * instr) const806 bool HScheduler::IsSchedulingBarrier(const HInstruction* instr) const {
807   return instr->IsControlFlow() ||
808       // Don't break calling convention.
809       instr->IsParameterValue() ||
810       // Code generation of goto relies on SuspendCheck's position.
811       instr->IsSuspendCheck();
812 }
813 
Run(bool only_optimize_loop_blocks,bool schedule_randomly)814 bool HInstructionScheduling::Run(bool only_optimize_loop_blocks,
815                                  bool schedule_randomly) {
816 #if defined(ART_ENABLE_CODEGEN_arm64) || defined(ART_ENABLE_CODEGEN_arm)
817   // Phase-local allocator that allocates scheduler internal data structures like
818   // scheduling nodes, internel nodes map, dependencies, etc.
819   CriticalPathSchedulingNodeSelector critical_path_selector;
820   RandomSchedulingNodeSelector random_selector;
821   SchedulingNodeSelector* selector = schedule_randomly
822       ? static_cast<SchedulingNodeSelector*>(&random_selector)
823       : static_cast<SchedulingNodeSelector*>(&critical_path_selector);
824 #else
825   // Avoid compilation error when compiling for unsupported instruction set.
826   UNUSED(only_optimize_loop_blocks);
827   UNUSED(schedule_randomly);
828   UNUSED(codegen_);
829 #endif
830 
831   switch (instruction_set_) {
832 #ifdef ART_ENABLE_CODEGEN_arm64
833     case InstructionSet::kArm64: {
834       arm64::HSchedulerARM64 scheduler(selector);
835       scheduler.SetOnlyOptimizeLoopBlocks(only_optimize_loop_blocks);
836       scheduler.Schedule(graph_);
837       break;
838     }
839 #endif
840 #if defined(ART_ENABLE_CODEGEN_arm)
841     case InstructionSet::kThumb2:
842     case InstructionSet::kArm: {
843       arm::SchedulingLatencyVisitorARM arm_latency_visitor(codegen_);
844       arm::HSchedulerARM scheduler(selector, &arm_latency_visitor);
845       scheduler.SetOnlyOptimizeLoopBlocks(only_optimize_loop_blocks);
846       scheduler.Schedule(graph_);
847       break;
848     }
849 #endif
850     default:
851       break;
852   }
853   return true;
854 }
855 
856 }  // namespace art
857