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