blob: e5ff8a886a3672358fafd0f2f477fdebe1e289dd [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) {
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000046 node->AddDataPredecessor(dependency);
47 } else {
Alexandre Rames22aa54b2016-10-18 09:32:29 +010048 node->AddOtherPredecessor(dependency);
49 }
50}
51
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000052bool SideEffectDependencyAnalysis::HasReorderingDependency(const HInstruction* instr1,
53 const HInstruction* instr2) {
54 SideEffects instr1_side_effects = instr1->GetSideEffects();
55 SideEffects instr2_side_effects = instr2->GetSideEffects();
56
Alexandre Rames22aa54b2016-10-18 09:32:29 +010057 // Read after write.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000058 if (instr1_side_effects.MayDependOn(instr2_side_effects)) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +010059 return true;
60 }
61
62 // Write after read.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000063 if (instr2_side_effects.MayDependOn(instr1_side_effects)) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +010064 return true;
65 }
66
67 // Memory write after write.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000068 if (instr1_side_effects.DoesAnyWrite() && instr2_side_effects.DoesAnyWrite()) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +010069 return true;
70 }
71
72 return false;
73}
74
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000075size_t SideEffectDependencyAnalysis::MemoryDependencyAnalysis::ArrayAccessHeapLocation(
76 HInstruction* instruction) const {
xueliang.zhong2a3471f2017-05-08 18:36:40 +010077 DCHECK(heap_location_collector_ != nullptr);
Aart Bikb765a3f2018-05-10 14:47:48 -070078 size_t heap_loc = heap_location_collector_->GetArrayHeapLocation(instruction);
xueliang.zhong2a3471f2017-05-08 18:36:40 +010079 // This array access should be analyzed and added to HeapLocationCollector before.
80 DCHECK(heap_loc != HeapLocationCollector::kHeapLocationNotFound);
81 return heap_loc;
82}
Alexandre Rames22aa54b2016-10-18 09:32:29 +010083
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000084bool SideEffectDependencyAnalysis::MemoryDependencyAnalysis::ArrayAccessMayAlias(
85 HInstruction* instr1, HInstruction* instr2) const {
xueliang.zhong2a3471f2017-05-08 18:36:40 +010086 DCHECK(heap_location_collector_ != nullptr);
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000087 size_t instr1_heap_loc = ArrayAccessHeapLocation(instr1);
88 size_t instr2_heap_loc = ArrayAccessHeapLocation(instr2);
xueliang.zhong2a3471f2017-05-08 18:36:40 +010089
90 // For example: arr[0] and arr[0]
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000091 if (instr1_heap_loc == instr2_heap_loc) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +010092 return true;
93 }
94
xueliang.zhong2a3471f2017-05-08 18:36:40 +010095 // For example: arr[0] and arr[i]
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000096 if (heap_location_collector_->MayAlias(instr1_heap_loc, instr2_heap_loc)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +010097 return true;
98 }
99
100 return false;
101}
102
103static bool IsArrayAccess(const HInstruction* instruction) {
104 return instruction->IsArrayGet() || instruction->IsArraySet();
105}
106
107static bool IsInstanceFieldAccess(const HInstruction* instruction) {
108 return instruction->IsInstanceFieldGet() ||
109 instruction->IsInstanceFieldSet() ||
110 instruction->IsUnresolvedInstanceFieldGet() ||
111 instruction->IsUnresolvedInstanceFieldSet();
112}
113
114static bool IsStaticFieldAccess(const HInstruction* instruction) {
115 return instruction->IsStaticFieldGet() ||
116 instruction->IsStaticFieldSet() ||
117 instruction->IsUnresolvedStaticFieldGet() ||
118 instruction->IsUnresolvedStaticFieldSet();
119}
120
121static bool IsResolvedFieldAccess(const HInstruction* instruction) {
122 return instruction->IsInstanceFieldGet() ||
123 instruction->IsInstanceFieldSet() ||
124 instruction->IsStaticFieldGet() ||
125 instruction->IsStaticFieldSet();
126}
127
128static bool IsUnresolvedFieldAccess(const HInstruction* instruction) {
129 return instruction->IsUnresolvedInstanceFieldGet() ||
130 instruction->IsUnresolvedInstanceFieldSet() ||
131 instruction->IsUnresolvedStaticFieldGet() ||
132 instruction->IsUnresolvedStaticFieldSet();
133}
134
135static bool IsFieldAccess(const HInstruction* instruction) {
136 return IsResolvedFieldAccess(instruction) || IsUnresolvedFieldAccess(instruction);
137}
138
139static const FieldInfo* GetFieldInfo(const HInstruction* instruction) {
140 if (instruction->IsInstanceFieldGet()) {
141 return &instruction->AsInstanceFieldGet()->GetFieldInfo();
142 } else if (instruction->IsInstanceFieldSet()) {
143 return &instruction->AsInstanceFieldSet()->GetFieldInfo();
144 } else if (instruction->IsStaticFieldGet()) {
145 return &instruction->AsStaticFieldGet()->GetFieldInfo();
146 } else if (instruction->IsStaticFieldSet()) {
147 return &instruction->AsStaticFieldSet()->GetFieldInfo();
148 } else {
149 LOG(FATAL) << "Unexpected field access type";
150 UNREACHABLE();
151 }
152}
153
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000154size_t SideEffectDependencyAnalysis::MemoryDependencyAnalysis::FieldAccessHeapLocation(
155 const HInstruction* instr) const {
156 DCHECK(instr != nullptr);
157 DCHECK(GetFieldInfo(instr) != nullptr);
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100158 DCHECK(heap_location_collector_ != nullptr);
159
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000160 size_t heap_loc = heap_location_collector_->GetFieldHeapLocation(instr->InputAt(0),
161 GetFieldInfo(instr));
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100162 // This field access should be analyzed and added to HeapLocationCollector before.
163 DCHECK(heap_loc != HeapLocationCollector::kHeapLocationNotFound);
164
165 return heap_loc;
166}
167
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000168bool SideEffectDependencyAnalysis::MemoryDependencyAnalysis::FieldAccessMayAlias(
169 const HInstruction* instr1, const HInstruction* instr2) const {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100170 DCHECK(heap_location_collector_ != nullptr);
171
172 // Static and instance field accesses should not alias.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000173 if ((IsInstanceFieldAccess(instr1) && IsStaticFieldAccess(instr2)) ||
174 (IsStaticFieldAccess(instr1) && IsInstanceFieldAccess(instr2))) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100175 return false;
176 }
177
178 // If either of the field accesses is unresolved.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000179 if (IsUnresolvedFieldAccess(instr1) || IsUnresolvedFieldAccess(instr2)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100180 // Conservatively treat these two accesses may alias.
181 return true;
182 }
183
184 // If both fields accesses are resolved.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000185 size_t instr1_field_access_heap_loc = FieldAccessHeapLocation(instr1);
186 size_t instr2_field_access_heap_loc = FieldAccessHeapLocation(instr2);
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100187
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000188 if (instr1_field_access_heap_loc == instr2_field_access_heap_loc) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100189 return true;
190 }
191
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000192 if (!heap_location_collector_->MayAlias(instr1_field_access_heap_loc,
193 instr2_field_access_heap_loc)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100194 return false;
195 }
196
197 return true;
198}
199
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000200bool SideEffectDependencyAnalysis::MemoryDependencyAnalysis::HasMemoryDependency(
201 HInstruction* instr1, HInstruction* instr2) const {
202 if (!HasReorderingDependency(instr1, instr2)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100203 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
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000214 if (IsArrayAccess(instr1) && IsArrayAccess(instr2)) {
215 return ArrayAccessMayAlias(instr1, instr2);
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100216 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000217 if (IsFieldAccess(instr1) && IsFieldAccess(instr2)) {
218 return FieldAccessMayAlias(instr1, instr2);
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100219 }
220
221 // TODO(xueliang): LSA to support alias analysis among HVecLoad, HVecStore and ArrayAccess
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000222 if (instr1->IsVecMemoryOperation() && instr2->IsVecMemoryOperation()) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100223 return true;
224 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000225 if (instr1->IsVecMemoryOperation() && IsArrayAccess(instr2)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100226 return true;
227 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000228 if (IsArrayAccess(instr1) && instr2->IsVecMemoryOperation()) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100229 return true;
230 }
231
232 // Heap accesses of different kinds should not alias.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000233 if (IsArrayAccess(instr1) && IsFieldAccess(instr2)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100234 return false;
235 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000236 if (IsFieldAccess(instr1) && IsArrayAccess(instr2)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100237 return false;
238 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000239 if (instr1->IsVecMemoryOperation() && IsFieldAccess(instr2)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100240 return false;
241 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000242 if (IsFieldAccess(instr1) && instr2->IsVecMemoryOperation()) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100243 return false;
244 }
245
246 // We conservatively treat all other cases having dependency,
247 // for example, Invoke and ArrayGet.
248 return true;
249}
250
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000251bool SideEffectDependencyAnalysis::HasExceptionDependency(const HInstruction* instr1,
252 const HInstruction* instr2) {
253 if (instr2->CanThrow() && instr1->GetSideEffects().DoesAnyWrite()) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100254 return true;
255 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000256 if (instr2->GetSideEffects().DoesAnyWrite() && instr1->CanThrow()) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100257 return true;
258 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000259 if (instr2->CanThrow() && instr1->CanThrow()) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100260 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
Vladimir Marko09d041b2018-07-30 12:51:59 +0100268// Check if the specified instruction is a better candidate which more likely will
269// have other instructions depending on it.
270static bool IsBetterCandidateWithMoreLikelyDependencies(HInstruction* new_candidate,
271 HInstruction* old_candidate) {
272 if (!new_candidate->GetSideEffects().Includes(old_candidate->GetSideEffects())) {
273 // Weaker side effects.
274 return false;
275 }
276 if (old_candidate->GetSideEffects().Includes(new_candidate->GetSideEffects())) {
277 // Same side effects, check if `new_candidate` has stronger `CanThrow()`.
278 return new_candidate->CanThrow() && !old_candidate->CanThrow();
279 } else {
280 // Stronger side effects, check if `new_candidate` has at least as strong `CanThrow()`.
281 return new_candidate->CanThrow() || !old_candidate->CanThrow();
282 }
283}
284
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000285void SchedulingGraph::AddDependencies(SchedulingNode* instruction_node,
286 bool is_scheduling_barrier) {
287 HInstruction* instruction = instruction_node->GetInstruction();
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100288
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()) {
Vladimir Marko09d041b2018-07-30 12:51:59 +0100337 HInstruction* dep_chain_candidate = nullptr;
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100338 for (HInstruction* other = instruction->GetNext(); other != nullptr; other = other->GetNext()) {
339 SchedulingNode* other_node = GetNode(other);
340 if (other_node->IsSchedulingBarrier()) {
341 // We have reached a scheduling barrier so we can stop further
342 // processing.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000343 DCHECK(other_node->HasOtherDependency(instruction_node));
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100344 break;
345 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000346 if (side_effect_dependency_analysis_.HasSideEffectDependency(other, instruction)) {
Vladimir Marko09d041b2018-07-30 12:51:59 +0100347 if (dep_chain_candidate != nullptr &&
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000348 side_effect_dependency_analysis_.HasSideEffectDependency(other, dep_chain_candidate)) {
Vladimir Marko09d041b2018-07-30 12:51:59 +0100349 // Skip an explicit dependency to reduce memory usage, rely on the transitive dependency.
350 } else {
351 AddOtherDependency(other_node, instruction_node);
352 }
353 // Check if `other` is a better candidate which more likely will have other instructions
354 // depending on it.
355 if (dep_chain_candidate == nullptr ||
356 IsBetterCandidateWithMoreLikelyDependencies(other, dep_chain_candidate)) {
357 dep_chain_candidate = other;
358 }
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100359 }
360 }
361 }
362
363 // Environment dependencies.
364 // We do not need to process those if the instruction is a scheduling barrier,
365 // since the barrier already has non-data dependencies on all following
366 // instructions.
367 if (!is_scheduling_barrier) {
368 for (const HUseListNode<HEnvironment*>& use : instruction->GetEnvUses()) {
369 // Note that here we could stop processing if the environment holder is
370 // across a scheduling barrier. But checking this would likely require
371 // more work than simply iterating through environment uses.
372 AddOtherDependency(GetNode(use.GetUser()->GetHolder()), instruction_node);
373 }
374 }
375}
376
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100377static const std::string InstructionTypeId(const HInstruction* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100378 return DataType::TypeId(instruction->GetType()) + std::to_string(instruction->GetId());
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100379}
380
381// Ideally we would reuse the graph visualizer code, but it is not available
382// from here and it is not worth moving all that code only for our use.
383static void DumpAsDotNode(std::ostream& output, const SchedulingNode* node) {
384 const HInstruction* instruction = node->GetInstruction();
385 // Use the instruction typed id as the node identifier.
386 std::string instruction_id = InstructionTypeId(instruction);
387 output << instruction_id << "[shape=record, label=\""
388 << instruction_id << ' ' << instruction->DebugName() << " [";
389 // List the instruction's inputs in its description. When visualizing the
390 // graph this helps differentiating data inputs from other dependencies.
391 const char* seperator = "";
392 for (const HInstruction* input : instruction->GetInputs()) {
393 output << seperator << InstructionTypeId(input);
394 seperator = ",";
395 }
396 output << "]";
397 // Other properties of the node.
398 output << "\\ninternal_latency: " << node->GetInternalLatency();
399 output << "\\ncritical_path: " << node->GetCriticalPath();
400 if (node->IsSchedulingBarrier()) {
401 output << "\\n(barrier)";
402 }
403 output << "\"];\n";
404 // We want program order to go from top to bottom in the graph output, so we
405 // reverse the edges and specify `dir=back`.
406 for (const SchedulingNode* predecessor : node->GetDataPredecessors()) {
407 const HInstruction* predecessor_instruction = predecessor->GetInstruction();
408 output << InstructionTypeId(predecessor_instruction) << ":s -> " << instruction_id << ":n "
409 << "[label=\"" << predecessor->GetLatency() << "\",dir=back]\n";
410 }
411 for (const SchedulingNode* predecessor : node->GetOtherPredecessors()) {
412 const HInstruction* predecessor_instruction = predecessor->GetInstruction();
413 output << InstructionTypeId(predecessor_instruction) << ":s -> " << instruction_id << ":n "
414 << "[dir=back,color=blue]\n";
415 }
416}
417
418void SchedulingGraph::DumpAsDotGraph(const std::string& description,
Vladimir Markoca6fff82017-10-03 14:49:14 +0100419 const ScopedArenaVector<SchedulingNode*>& initial_candidates) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100420 // TODO(xueliang): ideally we should move scheduling information into HInstruction, after that
421 // we should move this dotty graph dump feature to visualizer, and have a compiler option for it.
422 std::ofstream output("scheduling_graphs.dot", std::ofstream::out | std::ofstream::app);
423 // Description of this graph, as a comment.
424 output << "// " << description << "\n";
425 // Start the dot graph. Use an increasing index for easier differentiation.
426 output << "digraph G {\n";
427 for (const auto& entry : nodes_map_) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100428 SchedulingNode* node = entry.second.get();
Vladimir Marko7d157fc2017-05-10 16:29:23 +0100429 DumpAsDotNode(output, node);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100430 }
431 // Create a fake 'end_of_scheduling' node to help visualization of critical_paths.
Vladimir Marko7d157fc2017-05-10 16:29:23 +0100432 for (SchedulingNode* node : initial_candidates) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100433 const HInstruction* instruction = node->GetInstruction();
434 output << InstructionTypeId(instruction) << ":s -> end_of_scheduling:n "
435 << "[label=\"" << node->GetLatency() << "\",dir=back]\n";
436 }
437 // End of the dot graph.
438 output << "}\n";
439 output.close();
440}
441
442SchedulingNode* CriticalPathSchedulingNodeSelector::SelectMaterializedCondition(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100443 ScopedArenaVector<SchedulingNode*>* nodes, const SchedulingGraph& graph) const {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100444 // Schedule condition inputs that can be materialized immediately before their use.
445 // In following example, after we've scheduled HSelect, we want LessThan to be scheduled
446 // immediately, because it is a materialized condition, and will be emitted right before HSelect
447 // in codegen phase.
448 //
449 // i20 HLessThan [...] HLessThan HAdd HAdd
450 // i21 HAdd [...] ===> | | |
451 // i22 HAdd [...] +----------+---------+
452 // i23 HSelect [i21, i22, i20] HSelect
453
454 if (prev_select_ == nullptr) {
455 return nullptr;
456 }
457
458 const HInstruction* instruction = prev_select_->GetInstruction();
459 const HCondition* condition = nullptr;
460 DCHECK(instruction != nullptr);
461
462 if (instruction->IsIf()) {
463 condition = instruction->AsIf()->InputAt(0)->AsCondition();
464 } else if (instruction->IsSelect()) {
465 condition = instruction->AsSelect()->GetCondition()->AsCondition();
466 }
467
468 SchedulingNode* condition_node = (condition != nullptr) ? graph.GetNode(condition) : nullptr;
469
470 if ((condition_node != nullptr) &&
471 condition->HasOnlyOneNonEnvironmentUse() &&
472 ContainsElement(*nodes, condition_node)) {
473 DCHECK(!condition_node->HasUnscheduledSuccessors());
474 // Remove the condition from the list of candidates and schedule it.
475 RemoveElement(*nodes, condition_node);
476 return condition_node;
477 }
478
479 return nullptr;
480}
481
482SchedulingNode* CriticalPathSchedulingNodeSelector::PopHighestPriorityNode(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100483 ScopedArenaVector<SchedulingNode*>* nodes, const SchedulingGraph& graph) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100484 DCHECK(!nodes->empty());
485 SchedulingNode* select_node = nullptr;
486
487 // Optimize for materialized condition and its emit before use scenario.
488 select_node = SelectMaterializedCondition(nodes, graph);
489
490 if (select_node == nullptr) {
491 // Get highest priority node based on critical path information.
492 select_node = (*nodes)[0];
493 size_t select = 0;
494 for (size_t i = 1, e = nodes->size(); i < e; i++) {
495 SchedulingNode* check = (*nodes)[i];
496 SchedulingNode* candidate = (*nodes)[select];
497 select_node = GetHigherPrioritySchedulingNode(candidate, check);
498 if (select_node == check) {
499 select = i;
500 }
501 }
502 DeleteNodeAtIndex(nodes, select);
503 }
504
505 prev_select_ = select_node;
506 return select_node;
507}
508
509SchedulingNode* CriticalPathSchedulingNodeSelector::GetHigherPrioritySchedulingNode(
510 SchedulingNode* candidate, SchedulingNode* check) const {
511 uint32_t candidate_path = candidate->GetCriticalPath();
512 uint32_t check_path = check->GetCriticalPath();
513 // First look at the critical_path.
514 if (check_path != candidate_path) {
515 return check_path < candidate_path ? check : candidate;
516 }
517 // If both critical paths are equal, schedule instructions with a higher latency
518 // first in program order.
519 return check->GetLatency() < candidate->GetLatency() ? check : candidate;
520}
521
522void HScheduler::Schedule(HGraph* graph) {
Mingyao Yang179861c2017-08-04 13:21:31 -0700523 // We run lsa here instead of in a separate pass to better control whether we
524 // should run the analysis or not.
Vladimir Markoced04832018-07-26 14:42:17 +0100525 const HeapLocationCollector* heap_location_collector = nullptr;
Mingyao Yang179861c2017-08-04 13:21:31 -0700526 LoadStoreAnalysis lsa(graph);
527 if (!only_optimize_loop_blocks_ || graph->HasLoops()) {
528 lsa.Run();
Vladimir Markoced04832018-07-26 14:42:17 +0100529 heap_location_collector = &lsa.GetHeapLocationCollector();
Mingyao Yang179861c2017-08-04 13:21:31 -0700530 }
531
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100532 for (HBasicBlock* block : graph->GetReversePostOrder()) {
533 if (IsSchedulable(block)) {
Vladimir Markoced04832018-07-26 14:42:17 +0100534 Schedule(block, heap_location_collector);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100535 }
536 }
537}
538
Vladimir Markoced04832018-07-26 14:42:17 +0100539void HScheduler::Schedule(HBasicBlock* block,
540 const HeapLocationCollector* heap_location_collector) {
541 ScopedArenaAllocator allocator(block->GetGraph()->GetArenaStack());
542 ScopedArenaVector<SchedulingNode*> scheduling_nodes(allocator.Adapter(kArenaAllocScheduler));
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100543
544 // Build the scheduling graph.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000545 SchedulingGraph scheduling_graph(&allocator, heap_location_collector);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100546 for (HBackwardInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
547 HInstruction* instruction = it.Current();
Nicolas Geoffray757b26c2017-06-29 16:11:41 +0100548 CHECK_EQ(instruction->GetBlock(), block)
549 << instruction->DebugName()
550 << " is in block " << instruction->GetBlock()->GetBlockId()
551 << ", and expected in block " << block->GetBlockId();
Vladimir Markoced04832018-07-26 14:42:17 +0100552 SchedulingNode* node = scheduling_graph.AddNode(instruction, IsSchedulingBarrier(instruction));
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100553 CalculateLatency(node);
554 scheduling_nodes.push_back(node);
555 }
556
Vladimir Markoced04832018-07-26 14:42:17 +0100557 if (scheduling_graph.Size() <= 1) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100558 return;
559 }
560
561 cursor_ = block->GetLastInstruction();
562
Vladimir Markoced04832018-07-26 14:42:17 +0100563 // The list of candidates for scheduling. A node becomes a candidate when all
564 // its predecessors have been scheduled.
565 ScopedArenaVector<SchedulingNode*> candidates(allocator.Adapter(kArenaAllocScheduler));
566
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100567 // Find the initial candidates for scheduling.
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100568 for (SchedulingNode* node : scheduling_nodes) {
569 if (!node->HasUnscheduledSuccessors()) {
570 node->MaybeUpdateCriticalPath(node->GetLatency());
Vladimir Markoced04832018-07-26 14:42:17 +0100571 candidates.push_back(node);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100572 }
573 }
574
Vladimir Markoced04832018-07-26 14:42:17 +0100575 ScopedArenaVector<SchedulingNode*> initial_candidates(allocator.Adapter(kArenaAllocScheduler));
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100576 if (kDumpDotSchedulingGraphs) {
577 // Remember the list of initial candidates for debug output purposes.
Vladimir Markoced04832018-07-26 14:42:17 +0100578 initial_candidates.assign(candidates.begin(), candidates.end());
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100579 }
580
581 // Schedule all nodes.
Vladimir Markoced04832018-07-26 14:42:17 +0100582 selector_->Reset();
583 while (!candidates.empty()) {
584 SchedulingNode* node = selector_->PopHighestPriorityNode(&candidates, scheduling_graph);
585 Schedule(node, &candidates);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100586 }
587
588 if (kDumpDotSchedulingGraphs) {
589 // Dump the graph in `dot` format.
590 HGraph* graph = block->GetGraph();
591 std::stringstream description;
592 description << graph->GetDexFile().PrettyMethod(graph->GetMethodIdx())
593 << " B" << block->GetBlockId();
Vladimir Markoced04832018-07-26 14:42:17 +0100594 scheduling_graph.DumpAsDotGraph(description.str(), initial_candidates);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100595 }
596}
597
Vladimir Markoced04832018-07-26 14:42:17 +0100598void HScheduler::Schedule(SchedulingNode* scheduling_node,
599 /*inout*/ ScopedArenaVector<SchedulingNode*>* candidates) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100600 // Check whether any of the node's predecessors will be valid candidates after
601 // this node is scheduled.
602 uint32_t path_to_node = scheduling_node->GetCriticalPath();
603 for (SchedulingNode* predecessor : scheduling_node->GetDataPredecessors()) {
604 predecessor->MaybeUpdateCriticalPath(
605 path_to_node + predecessor->GetInternalLatency() + predecessor->GetLatency());
606 predecessor->DecrementNumberOfUnscheduledSuccessors();
607 if (!predecessor->HasUnscheduledSuccessors()) {
Vladimir Markoced04832018-07-26 14:42:17 +0100608 candidates->push_back(predecessor);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100609 }
610 }
611 for (SchedulingNode* predecessor : scheduling_node->GetOtherPredecessors()) {
612 // Do not update the critical path.
613 // The 'other' (so 'non-data') dependencies (usually) do not represent a
614 // 'material' dependency of nodes on others. They exist for program
615 // correctness. So we do not use them to compute the critical path.
616 predecessor->DecrementNumberOfUnscheduledSuccessors();
617 if (!predecessor->HasUnscheduledSuccessors()) {
Vladimir Markoced04832018-07-26 14:42:17 +0100618 candidates->push_back(predecessor);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100619 }
620 }
621
622 Schedule(scheduling_node->GetInstruction());
623}
624
625// Move an instruction after cursor instruction inside one basic block.
626static void MoveAfterInBlock(HInstruction* instruction, HInstruction* cursor) {
627 DCHECK_EQ(instruction->GetBlock(), cursor->GetBlock());
628 DCHECK_NE(cursor, cursor->GetBlock()->GetLastInstruction());
629 DCHECK(!instruction->IsControlFlow());
630 DCHECK(!cursor->IsControlFlow());
Andreas Gampe3db70682018-12-26 15:12:03 -0800631 instruction->MoveBefore(cursor->GetNext(), /* do_checks= */ false);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100632}
633
634void HScheduler::Schedule(HInstruction* instruction) {
635 if (instruction == cursor_) {
636 cursor_ = cursor_->GetPrevious();
637 } else {
638 MoveAfterInBlock(instruction, cursor_);
639 }
640}
641
642bool HScheduler::IsSchedulable(const HInstruction* instruction) const {
643 // We want to avoid exhaustively listing all instructions, so we first check
644 // for instruction categories that we know are safe.
645 if (instruction->IsControlFlow() ||
646 instruction->IsConstant()) {
647 return true;
648 }
649 // Currently all unary and binary operations are safe to schedule, so avoid
650 // checking for each of them individually.
651 // Since nothing prevents a new scheduling-unsafe HInstruction to subclass
652 // HUnaryOperation (or HBinaryOperation), check in debug mode that we have
653 // the exhaustive lists here.
654 if (instruction->IsUnaryOperation()) {
Aart Bik3dad3412018-02-28 12:01:46 -0800655 DCHECK(instruction->IsAbs() ||
656 instruction->IsBooleanNot() ||
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100657 instruction->IsNot() ||
658 instruction->IsNeg()) << "unexpected instruction " << instruction->DebugName();
659 return true;
660 }
661 if (instruction->IsBinaryOperation()) {
662 DCHECK(instruction->IsAdd() ||
663 instruction->IsAnd() ||
664 instruction->IsCompare() ||
665 instruction->IsCondition() ||
666 instruction->IsDiv() ||
Aart Bik1f8d51b2018-02-15 10:42:37 -0800667 instruction->IsMin() ||
668 instruction->IsMax() ||
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100669 instruction->IsMul() ||
670 instruction->IsOr() ||
671 instruction->IsRem() ||
672 instruction->IsRor() ||
673 instruction->IsShl() ||
674 instruction->IsShr() ||
675 instruction->IsSub() ||
676 instruction->IsUShr() ||
677 instruction->IsXor()) << "unexpected instruction " << instruction->DebugName();
678 return true;
679 }
680 // The scheduler should not see any of these.
681 DCHECK(!instruction->IsParallelMove()) << "unexpected instruction " << instruction->DebugName();
682 // List of instructions explicitly excluded:
683 // HClearException
684 // HClinitCheck
685 // HDeoptimize
686 // HLoadClass
687 // HLoadException
688 // HMemoryBarrier
689 // HMonitorOperation
690 // HNativeDebugInfo
691 // HThrow
692 // HTryBoundary
693 // TODO: Some of the instructions above may be safe to schedule (maybe as
694 // scheduling barriers).
695 return instruction->IsArrayGet() ||
696 instruction->IsArraySet() ||
697 instruction->IsArrayLength() ||
698 instruction->IsBoundType() ||
699 instruction->IsBoundsCheck() ||
700 instruction->IsCheckCast() ||
701 instruction->IsClassTableGet() ||
702 instruction->IsCurrentMethod() ||
703 instruction->IsDivZeroCheck() ||
Mingyao Yang1545ccc2017-08-08 15:24:26 -0700704 (instruction->IsInstanceFieldGet() && !instruction->AsInstanceFieldGet()->IsVolatile()) ||
705 (instruction->IsInstanceFieldSet() && !instruction->AsInstanceFieldSet()->IsVolatile()) ||
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100706 instruction->IsInstanceOf() ||
707 instruction->IsInvokeInterface() ||
708 instruction->IsInvokeStaticOrDirect() ||
709 instruction->IsInvokeUnresolved() ||
710 instruction->IsInvokeVirtual() ||
711 instruction->IsLoadString() ||
712 instruction->IsNewArray() ||
713 instruction->IsNewInstance() ||
714 instruction->IsNullCheck() ||
715 instruction->IsPackedSwitch() ||
716 instruction->IsParameterValue() ||
717 instruction->IsPhi() ||
718 instruction->IsReturn() ||
719 instruction->IsReturnVoid() ||
720 instruction->IsSelect() ||
Mingyao Yang1545ccc2017-08-08 15:24:26 -0700721 (instruction->IsStaticFieldGet() && !instruction->AsStaticFieldGet()->IsVolatile()) ||
722 (instruction->IsStaticFieldSet() && !instruction->AsStaticFieldSet()->IsVolatile()) ||
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100723 instruction->IsSuspendCheck() ||
Mingyao Yang1545ccc2017-08-08 15:24:26 -0700724 instruction->IsTypeConversion();
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100725}
726
727bool HScheduler::IsSchedulable(const HBasicBlock* block) const {
728 // We may be only interested in loop blocks.
729 if (only_optimize_loop_blocks_ && !block->IsInLoop()) {
730 return false;
731 }
732 if (block->GetTryCatchInformation() != nullptr) {
733 // Do not schedule blocks that are part of try-catch.
734 // Because scheduler cannot see if catch block has assumptions on the instruction order in
735 // the try block. In following example, if we enable scheduler for the try block,
736 // MulitiplyAccumulate may be scheduled before DivZeroCheck,
737 // which can result in an incorrect value in the catch block.
738 // try {
739 // a = a/b; // DivZeroCheck
740 // // Div
741 // c = c*d+e; // MulitiplyAccumulate
742 // } catch {System.out.print(c); }
743 return false;
744 }
745 // Check whether all instructions in this block are schedulable.
746 for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
747 if (!IsSchedulable(it.Current())) {
748 return false;
749 }
750 }
751 return true;
752}
753
754bool HScheduler::IsSchedulingBarrier(const HInstruction* instr) const {
755 return instr->IsControlFlow() ||
756 // Don't break calling convention.
757 instr->IsParameterValue() ||
758 // Code generation of goto relies on SuspendCheck's position.
759 instr->IsSuspendCheck();
760}
761
Aart Bik24773202018-04-26 10:28:51 -0700762bool HInstructionScheduling::Run(bool only_optimize_loop_blocks,
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100763 bool schedule_randomly) {
xueliang.zhongf7caf682017-03-01 16:07:02 +0000764#if defined(ART_ENABLE_CODEGEN_arm64) || defined(ART_ENABLE_CODEGEN_arm)
765 // Phase-local allocator that allocates scheduler internal data structures like
766 // scheduling nodes, internel nodes map, dependencies, etc.
xueliang.zhongf7caf682017-03-01 16:07:02 +0000767 CriticalPathSchedulingNodeSelector critical_path_selector;
768 RandomSchedulingNodeSelector random_selector;
769 SchedulingNodeSelector* selector = schedule_randomly
770 ? static_cast<SchedulingNodeSelector*>(&random_selector)
771 : static_cast<SchedulingNodeSelector*>(&critical_path_selector);
772#else
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100773 // Avoid compilation error when compiling for unsupported instruction set.
774 UNUSED(only_optimize_loop_blocks);
775 UNUSED(schedule_randomly);
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100776 UNUSED(codegen_);
xueliang.zhongf7caf682017-03-01 16:07:02 +0000777#endif
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100778
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100779 switch (instruction_set_) {
780#ifdef ART_ENABLE_CODEGEN_arm64
Vladimir Marko33bff252017-11-01 14:35:42 +0000781 case InstructionSet::kArm64: {
Vladimir Markoced04832018-07-26 14:42:17 +0100782 arm64::HSchedulerARM64 scheduler(selector);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100783 scheduler.SetOnlyOptimizeLoopBlocks(only_optimize_loop_blocks);
784 scheduler.Schedule(graph_);
785 break;
786 }
787#endif
xueliang.zhongf7caf682017-03-01 16:07:02 +0000788#if defined(ART_ENABLE_CODEGEN_arm)
Vladimir Marko33bff252017-11-01 14:35:42 +0000789 case InstructionSet::kThumb2:
790 case InstructionSet::kArm: {
xueliang.zhongf7caf682017-03-01 16:07:02 +0000791 arm::SchedulingLatencyVisitorARM arm_latency_visitor(codegen_);
Vladimir Markoced04832018-07-26 14:42:17 +0100792 arm::HSchedulerARM scheduler(selector, &arm_latency_visitor);
xueliang.zhongf7caf682017-03-01 16:07:02 +0000793 scheduler.SetOnlyOptimizeLoopBlocks(only_optimize_loop_blocks);
794 scheduler.Schedule(graph_);
795 break;
796 }
797#endif
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100798 default:
799 break;
800 }
Aart Bik24773202018-04-26 10:28:51 -0700801 return true;
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100802}
803
804} // namespace art