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