blob: 1cdc98a8be14b99b2c93ff93e21b94cd527d7d97 [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
Nicolas Geoffray791df7a2021-01-23 13:28:56 +000017#include "scheduler.h"
18
Alex Light3a73ffb2021-01-25 14:11:05 +000019#include <string>
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"
Alex Light3a73ffb2021-01-25 14:11:05 +000024#include "optimizing/load_store_analysis.h"
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010025#include "prepare_for_register_allocation.h"
26
Alexandre Rames22aa54b2016-10-18 09:32:29 +010027#ifdef ART_ENABLE_CODEGEN_arm64
28#include "scheduler_arm64.h"
29#endif
30
xueliang.zhongf7caf682017-03-01 16:07:02 +000031#ifdef ART_ENABLE_CODEGEN_arm
32#include "scheduler_arm.h"
33#endif
34
VladimĂ­r Marko434d9682022-11-04 14:04:17 +000035namespace art HIDDEN {
Alexandre Rames22aa54b2016-10-18 09:32:29 +010036
37void SchedulingGraph::AddDependency(SchedulingNode* node,
38 SchedulingNode* dependency,
39 bool is_data_dependency) {
40 if (node == nullptr || dependency == nullptr) {
41 // A `nullptr` node indicates an instruction out of scheduling range (eg. in
42 // an other block), so we do not need to add a dependency edge to the graph.
43 return;
44 }
45
46 if (is_data_dependency) {
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000047 node->AddDataPredecessor(dependency);
48 } else {
Alexandre Rames22aa54b2016-10-18 09:32:29 +010049 node->AddOtherPredecessor(dependency);
50 }
51}
52
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000053bool SideEffectDependencyAnalysis::HasReorderingDependency(const HInstruction* instr1,
54 const HInstruction* instr2) {
55 SideEffects instr1_side_effects = instr1->GetSideEffects();
56 SideEffects instr2_side_effects = instr2->GetSideEffects();
57
Alexandre Rames22aa54b2016-10-18 09:32:29 +010058 // Read after write.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000059 if (instr1_side_effects.MayDependOn(instr2_side_effects)) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +010060 return true;
61 }
62
63 // Write after read.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000064 if (instr2_side_effects.MayDependOn(instr1_side_effects)) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +010065 return true;
66 }
67
68 // Memory write after write.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000069 if (instr1_side_effects.DoesAnyWrite() && instr2_side_effects.DoesAnyWrite()) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +010070 return true;
71 }
72
73 return false;
74}
75
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000076size_t SideEffectDependencyAnalysis::MemoryDependencyAnalysis::ArrayAccessHeapLocation(
77 HInstruction* instruction) const {
xueliang.zhong2a3471f2017-05-08 18:36:40 +010078 DCHECK(heap_location_collector_ != nullptr);
Aart Bikb765a3f2018-05-10 14:47:48 -070079 size_t heap_loc = heap_location_collector_->GetArrayHeapLocation(instruction);
xueliang.zhong2a3471f2017-05-08 18:36:40 +010080 // This array access should be analyzed and added to HeapLocationCollector before.
81 DCHECK(heap_loc != HeapLocationCollector::kHeapLocationNotFound);
82 return heap_loc;
83}
Alexandre Rames22aa54b2016-10-18 09:32:29 +010084
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000085bool SideEffectDependencyAnalysis::MemoryDependencyAnalysis::ArrayAccessMayAlias(
86 HInstruction* instr1, HInstruction* instr2) const {
xueliang.zhong2a3471f2017-05-08 18:36:40 +010087 DCHECK(heap_location_collector_ != nullptr);
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000088 size_t instr1_heap_loc = ArrayAccessHeapLocation(instr1);
89 size_t instr2_heap_loc = ArrayAccessHeapLocation(instr2);
xueliang.zhong2a3471f2017-05-08 18:36:40 +010090
91 // For example: arr[0] and arr[0]
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000092 if (instr1_heap_loc == instr2_heap_loc) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +010093 return true;
94 }
95
xueliang.zhong2a3471f2017-05-08 18:36:40 +010096 // For example: arr[0] and arr[i]
Evgeny Astigeevich957c5382019-03-18 12:37:58 +000097 if (heap_location_collector_->MayAlias(instr1_heap_loc, instr2_heap_loc)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +010098 return true;
99 }
100
101 return false;
102}
103
104static bool IsArrayAccess(const HInstruction* instruction) {
105 return instruction->IsArrayGet() || instruction->IsArraySet();
106}
107
108static bool IsInstanceFieldAccess(const HInstruction* instruction) {
109 return instruction->IsInstanceFieldGet() ||
110 instruction->IsInstanceFieldSet() ||
Alex Light3a73ffb2021-01-25 14:11:05 +0000111 instruction->IsPredicatedInstanceFieldGet() ||
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100112 instruction->IsUnresolvedInstanceFieldGet() ||
113 instruction->IsUnresolvedInstanceFieldSet();
114}
115
116static bool IsStaticFieldAccess(const HInstruction* instruction) {
117 return instruction->IsStaticFieldGet() ||
118 instruction->IsStaticFieldSet() ||
119 instruction->IsUnresolvedStaticFieldGet() ||
120 instruction->IsUnresolvedStaticFieldSet();
121}
122
123static bool IsResolvedFieldAccess(const HInstruction* instruction) {
124 return instruction->IsInstanceFieldGet() ||
125 instruction->IsInstanceFieldSet() ||
Alex Light3a73ffb2021-01-25 14:11:05 +0000126 instruction->IsPredicatedInstanceFieldGet() ||
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100127 instruction->IsStaticFieldGet() ||
128 instruction->IsStaticFieldSet();
129}
130
131static bool IsUnresolvedFieldAccess(const HInstruction* instruction) {
132 return instruction->IsUnresolvedInstanceFieldGet() ||
133 instruction->IsUnresolvedInstanceFieldSet() ||
134 instruction->IsUnresolvedStaticFieldGet() ||
135 instruction->IsUnresolvedStaticFieldSet();
136}
137
138static bool IsFieldAccess(const HInstruction* instruction) {
139 return IsResolvedFieldAccess(instruction) || IsUnresolvedFieldAccess(instruction);
140}
141
142static const FieldInfo* GetFieldInfo(const HInstruction* instruction) {
Alex Light3a73ffb2021-01-25 14:11:05 +0000143 return &instruction->GetFieldInfo();
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100144}
145
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000146size_t SideEffectDependencyAnalysis::MemoryDependencyAnalysis::FieldAccessHeapLocation(
147 const HInstruction* instr) const {
148 DCHECK(instr != nullptr);
149 DCHECK(GetFieldInfo(instr) != nullptr);
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100150 DCHECK(heap_location_collector_ != nullptr);
151
Vladimir Marko642c8f62021-05-21 09:24:03 +0100152 HInstruction* ref = instr->IsPredicatedInstanceFieldGet()
Vladimir Markocde64972023-04-25 16:40:06 +0000153 ? instr->AsPredicatedInstanceFieldGet()->GetTarget()
Vladimir Marko642c8f62021-05-21 09:24:03 +0100154 : instr->InputAt(0);
155 size_t heap_loc = heap_location_collector_->GetFieldHeapLocation(ref, GetFieldInfo(instr));
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100156 // This field access should be analyzed and added to HeapLocationCollector before.
157 DCHECK(heap_loc != HeapLocationCollector::kHeapLocationNotFound);
158
159 return heap_loc;
160}
161
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000162bool SideEffectDependencyAnalysis::MemoryDependencyAnalysis::FieldAccessMayAlias(
163 const HInstruction* instr1, const HInstruction* instr2) const {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100164 DCHECK(heap_location_collector_ != nullptr);
165
166 // Static and instance field accesses should not alias.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000167 if ((IsInstanceFieldAccess(instr1) && IsStaticFieldAccess(instr2)) ||
168 (IsStaticFieldAccess(instr1) && IsInstanceFieldAccess(instr2))) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100169 return false;
170 }
171
172 // If either of the field accesses is unresolved.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000173 if (IsUnresolvedFieldAccess(instr1) || IsUnresolvedFieldAccess(instr2)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100174 // Conservatively treat these two accesses may alias.
175 return true;
176 }
177
178 // If both fields accesses are resolved.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000179 size_t instr1_field_access_heap_loc = FieldAccessHeapLocation(instr1);
180 size_t instr2_field_access_heap_loc = FieldAccessHeapLocation(instr2);
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100181
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000182 if (instr1_field_access_heap_loc == instr2_field_access_heap_loc) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100183 return true;
184 }
185
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000186 if (!heap_location_collector_->MayAlias(instr1_field_access_heap_loc,
187 instr2_field_access_heap_loc)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100188 return false;
189 }
190
191 return true;
192}
193
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000194bool SideEffectDependencyAnalysis::MemoryDependencyAnalysis::HasMemoryDependency(
195 HInstruction* instr1, HInstruction* instr2) const {
196 if (!HasReorderingDependency(instr1, instr2)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100197 return false;
198 }
199
200 if (heap_location_collector_ == nullptr ||
201 heap_location_collector_->GetNumberOfHeapLocations() == 0) {
202 // Without HeapLocation information from load store analysis,
203 // we cannot do further disambiguation analysis on these two instructions.
204 // Just simply say that those two instructions have memory dependency.
205 return true;
206 }
207
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000208 if (IsArrayAccess(instr1) && IsArrayAccess(instr2)) {
209 return ArrayAccessMayAlias(instr1, instr2);
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100210 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000211 if (IsFieldAccess(instr1) && IsFieldAccess(instr2)) {
212 return FieldAccessMayAlias(instr1, instr2);
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100213 }
214
215 // TODO(xueliang): LSA to support alias analysis among HVecLoad, HVecStore and ArrayAccess
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000216 if (instr1->IsVecMemoryOperation() && instr2->IsVecMemoryOperation()) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100217 return true;
218 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000219 if (instr1->IsVecMemoryOperation() && IsArrayAccess(instr2)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100220 return true;
221 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000222 if (IsArrayAccess(instr1) && instr2->IsVecMemoryOperation()) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100223 return true;
224 }
225
226 // Heap accesses of different kinds should not alias.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000227 if (IsArrayAccess(instr1) && IsFieldAccess(instr2)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100228 return false;
229 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000230 if (IsFieldAccess(instr1) && IsArrayAccess(instr2)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100231 return false;
232 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000233 if (instr1->IsVecMemoryOperation() && IsFieldAccess(instr2)) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100234 return false;
235 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000236 if (IsFieldAccess(instr1) && instr2->IsVecMemoryOperation()) {
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100237 return false;
238 }
239
240 // We conservatively treat all other cases having dependency,
241 // for example, Invoke and ArrayGet.
242 return true;
243}
244
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000245bool SideEffectDependencyAnalysis::HasExceptionDependency(const HInstruction* instr1,
246 const HInstruction* instr2) {
247 if (instr2->CanThrow() && instr1->GetSideEffects().DoesAnyWrite()) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100248 return true;
249 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000250 if (instr2->GetSideEffects().DoesAnyWrite() && instr1->CanThrow()) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100251 return true;
252 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000253 if (instr2->CanThrow() && instr1->CanThrow()) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100254 return true;
255 }
256
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100257 // Above checks should cover all cases where we cannot reorder two
258 // instructions which may throw exception.
259 return false;
260}
261
Vladimir Marko09d041b2018-07-30 12:51:59 +0100262// Check if the specified instruction is a better candidate which more likely will
263// have other instructions depending on it.
264static bool IsBetterCandidateWithMoreLikelyDependencies(HInstruction* new_candidate,
265 HInstruction* old_candidate) {
266 if (!new_candidate->GetSideEffects().Includes(old_candidate->GetSideEffects())) {
267 // Weaker side effects.
268 return false;
269 }
270 if (old_candidate->GetSideEffects().Includes(new_candidate->GetSideEffects())) {
271 // Same side effects, check if `new_candidate` has stronger `CanThrow()`.
272 return new_candidate->CanThrow() && !old_candidate->CanThrow();
273 } else {
274 // Stronger side effects, check if `new_candidate` has at least as strong `CanThrow()`.
275 return new_candidate->CanThrow() || !old_candidate->CanThrow();
276 }
277}
278
Evgeny Astigeevich45217372019-04-03 10:46:13 +0100279void SchedulingGraph::AddCrossIterationDependencies(SchedulingNode* node) {
280 for (HInstruction* instruction : node->GetInstruction()->GetInputs()) {
281 // Having a phi-function from a loop header as an input means the current node of the
282 // scheduling graph has a cross-iteration dependency because such phi-functions bring values
283 // from the previous iteration to the current iteration.
284 if (!instruction->IsLoopHeaderPhi()) {
285 continue;
286 }
287 for (HInstruction* phi_input : instruction->GetInputs()) {
288 // As a scheduling graph of the current basic block is built by
289 // processing instructions bottom-up, nullptr returned by GetNode means
290 // an instruction defining a value for the phi is either before the
291 // instruction represented by node or it is in a different basic block.
292 SchedulingNode* def_node = GetNode(phi_input);
293
294 // We don't create a dependency if there are uses besides the use in phi.
295 // In such cases a register to hold phi_input is usually allocated and
296 // a MOV instruction is generated. In cases with multiple uses and no MOV
297 // instruction, reordering creating a MOV instruction can improve
298 // performance more than an attempt to avoid a MOV instruction.
299 if (def_node != nullptr && def_node != node && phi_input->GetUses().HasExactlyOneElement()) {
300 // We have an implicit data dependency between node and def_node.
301 // AddAddDataDependency cannot be used because it is for explicit data dependencies.
302 // So AddOtherDependency is used.
303 AddOtherDependency(def_node, node);
304 }
305 }
306 }
307}
308
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000309void SchedulingGraph::AddDependencies(SchedulingNode* instruction_node,
310 bool is_scheduling_barrier) {
311 HInstruction* instruction = instruction_node->GetInstruction();
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100312
313 // Define-use dependencies.
314 for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
315 AddDataDependency(GetNode(use.GetUser()), instruction_node);
316 }
317
318 // Scheduling barrier dependencies.
Santiago Aboy Solanes872ec722022-02-18 14:10:25 +0000319 DCHECK_IMPLIES(is_scheduling_barrier, contains_scheduling_barrier_);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100320 if (contains_scheduling_barrier_) {
321 // A barrier depends on instructions after it. And instructions before the
322 // barrier depend on it.
323 for (HInstruction* other = instruction->GetNext(); other != nullptr; other = other->GetNext()) {
324 SchedulingNode* other_node = GetNode(other);
Nicolas Geoffray757b26c2017-06-29 16:11:41 +0100325 CHECK(other_node != nullptr)
326 << other->DebugName()
327 << " is in block " << other->GetBlock()->GetBlockId()
328 << ", and expected in block " << instruction->GetBlock()->GetBlockId();
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100329 bool other_is_barrier = other_node->IsSchedulingBarrier();
330 if (is_scheduling_barrier || other_is_barrier) {
331 AddOtherDependency(other_node, instruction_node);
332 }
333 if (other_is_barrier) {
334 // This other scheduling barrier guarantees ordering of instructions after
335 // it, so avoid creating additional useless dependencies in the graph.
336 // For example if we have
337 // instr_1
338 // barrier_2
339 // instr_3
340 // barrier_4
341 // instr_5
342 // we only create the following non-data dependencies
343 // 1 -> 2
344 // 2 -> 3
345 // 2 -> 4
346 // 3 -> 4
347 // 4 -> 5
348 // and do not create
349 // 1 -> 4
350 // 2 -> 5
351 // Note that in this example we could also avoid creating the dependency
352 // `2 -> 4`. But if we remove `instr_3` that dependency is required to
353 // order the barriers. So we generate it to avoid a special case.
354 break;
355 }
356 }
357 }
358
359 // Side effect dependencies.
360 if (!instruction->GetSideEffects().DoesNothing() || instruction->CanThrow()) {
Vladimir Marko09d041b2018-07-30 12:51:59 +0100361 HInstruction* dep_chain_candidate = nullptr;
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100362 for (HInstruction* other = instruction->GetNext(); other != nullptr; other = other->GetNext()) {
363 SchedulingNode* other_node = GetNode(other);
364 if (other_node->IsSchedulingBarrier()) {
365 // We have reached a scheduling barrier so we can stop further
366 // processing.
Evgeny Astigeevich45217372019-04-03 10:46:13 +0100367 //
368 // As a "other" dependency is not set up if a data dependency exists, we need to check that
369 // one of them must exist.
370 DCHECK(other_node->HasOtherDependency(instruction_node)
371 || other_node->HasDataDependency(instruction_node));
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100372 break;
373 }
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000374 if (side_effect_dependency_analysis_.HasSideEffectDependency(other, instruction)) {
Vladimir Marko09d041b2018-07-30 12:51:59 +0100375 if (dep_chain_candidate != nullptr &&
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000376 side_effect_dependency_analysis_.HasSideEffectDependency(other, dep_chain_candidate)) {
Vladimir Marko09d041b2018-07-30 12:51:59 +0100377 // Skip an explicit dependency to reduce memory usage, rely on the transitive dependency.
378 } else {
379 AddOtherDependency(other_node, instruction_node);
380 }
381 // Check if `other` is a better candidate which more likely will have other instructions
382 // depending on it.
383 if (dep_chain_candidate == nullptr ||
384 IsBetterCandidateWithMoreLikelyDependencies(other, dep_chain_candidate)) {
385 dep_chain_candidate = other;
386 }
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100387 }
388 }
389 }
390
391 // Environment dependencies.
392 // We do not need to process those if the instruction is a scheduling barrier,
393 // since the barrier already has non-data dependencies on all following
394 // instructions.
395 if (!is_scheduling_barrier) {
396 for (const HUseListNode<HEnvironment*>& use : instruction->GetEnvUses()) {
397 // Note that here we could stop processing if the environment holder is
398 // across a scheduling barrier. But checking this would likely require
399 // more work than simply iterating through environment uses.
400 AddOtherDependency(GetNode(use.GetUser()->GetHolder()), instruction_node);
401 }
402 }
Evgeny Astigeevich45217372019-04-03 10:46:13 +0100403
404 AddCrossIterationDependencies(instruction_node);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100405}
406
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100407static const std::string InstructionTypeId(const HInstruction* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100408 return DataType::TypeId(instruction->GetType()) + std::to_string(instruction->GetId());
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100409}
410
411// Ideally we would reuse the graph visualizer code, but it is not available
412// from here and it is not worth moving all that code only for our use.
413static void DumpAsDotNode(std::ostream& output, const SchedulingNode* node) {
414 const HInstruction* instruction = node->GetInstruction();
415 // Use the instruction typed id as the node identifier.
416 std::string instruction_id = InstructionTypeId(instruction);
417 output << instruction_id << "[shape=record, label=\""
418 << instruction_id << ' ' << instruction->DebugName() << " [";
419 // List the instruction's inputs in its description. When visualizing the
420 // graph this helps differentiating data inputs from other dependencies.
421 const char* seperator = "";
422 for (const HInstruction* input : instruction->GetInputs()) {
423 output << seperator << InstructionTypeId(input);
424 seperator = ",";
425 }
426 output << "]";
427 // Other properties of the node.
428 output << "\\ninternal_latency: " << node->GetInternalLatency();
429 output << "\\ncritical_path: " << node->GetCriticalPath();
430 if (node->IsSchedulingBarrier()) {
431 output << "\\n(barrier)";
432 }
433 output << "\"];\n";
434 // We want program order to go from top to bottom in the graph output, so we
435 // reverse the edges and specify `dir=back`.
436 for (const SchedulingNode* predecessor : node->GetDataPredecessors()) {
437 const HInstruction* predecessor_instruction = predecessor->GetInstruction();
438 output << InstructionTypeId(predecessor_instruction) << ":s -> " << instruction_id << ":n "
439 << "[label=\"" << predecessor->GetLatency() << "\",dir=back]\n";
440 }
441 for (const SchedulingNode* predecessor : node->GetOtherPredecessors()) {
442 const HInstruction* predecessor_instruction = predecessor->GetInstruction();
443 output << InstructionTypeId(predecessor_instruction) << ":s -> " << instruction_id << ":n "
444 << "[dir=back,color=blue]\n";
445 }
446}
447
448void SchedulingGraph::DumpAsDotGraph(const std::string& description,
Vladimir Markoca6fff82017-10-03 14:49:14 +0100449 const ScopedArenaVector<SchedulingNode*>& initial_candidates) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100450 // TODO(xueliang): ideally we should move scheduling information into HInstruction, after that
451 // we should move this dotty graph dump feature to visualizer, and have a compiler option for it.
452 std::ofstream output("scheduling_graphs.dot", std::ofstream::out | std::ofstream::app);
453 // Description of this graph, as a comment.
454 output << "// " << description << "\n";
455 // Start the dot graph. Use an increasing index for easier differentiation.
456 output << "digraph G {\n";
457 for (const auto& entry : nodes_map_) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100458 SchedulingNode* node = entry.second.get();
Vladimir Marko7d157fc2017-05-10 16:29:23 +0100459 DumpAsDotNode(output, node);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100460 }
461 // Create a fake 'end_of_scheduling' node to help visualization of critical_paths.
Vladimir Marko7d157fc2017-05-10 16:29:23 +0100462 for (SchedulingNode* node : initial_candidates) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100463 const HInstruction* instruction = node->GetInstruction();
464 output << InstructionTypeId(instruction) << ":s -> end_of_scheduling:n "
465 << "[label=\"" << node->GetLatency() << "\",dir=back]\n";
466 }
467 // End of the dot graph.
468 output << "}\n";
469 output.close();
470}
471
472SchedulingNode* CriticalPathSchedulingNodeSelector::SelectMaterializedCondition(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100473 ScopedArenaVector<SchedulingNode*>* nodes, const SchedulingGraph& graph) const {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100474 // Schedule condition inputs that can be materialized immediately before their use.
475 // In following example, after we've scheduled HSelect, we want LessThan to be scheduled
476 // immediately, because it is a materialized condition, and will be emitted right before HSelect
477 // in codegen phase.
478 //
479 // i20 HLessThan [...] HLessThan HAdd HAdd
480 // i21 HAdd [...] ===> | | |
481 // i22 HAdd [...] +----------+---------+
482 // i23 HSelect [i21, i22, i20] HSelect
483
484 if (prev_select_ == nullptr) {
485 return nullptr;
486 }
487
488 const HInstruction* instruction = prev_select_->GetInstruction();
489 const HCondition* condition = nullptr;
490 DCHECK(instruction != nullptr);
491
492 if (instruction->IsIf()) {
Vladimir Markocde64972023-04-25 16:40:06 +0000493 condition = instruction->AsIf()->InputAt(0)->AsConditionOrNull();
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100494 } else if (instruction->IsSelect()) {
Vladimir Markocde64972023-04-25 16:40:06 +0000495 condition = instruction->AsSelect()->GetCondition()->AsConditionOrNull();
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100496 }
497
498 SchedulingNode* condition_node = (condition != nullptr) ? graph.GetNode(condition) : nullptr;
499
500 if ((condition_node != nullptr) &&
501 condition->HasOnlyOneNonEnvironmentUse() &&
502 ContainsElement(*nodes, condition_node)) {
503 DCHECK(!condition_node->HasUnscheduledSuccessors());
504 // Remove the condition from the list of candidates and schedule it.
505 RemoveElement(*nodes, condition_node);
506 return condition_node;
507 }
508
509 return nullptr;
510}
511
512SchedulingNode* CriticalPathSchedulingNodeSelector::PopHighestPriorityNode(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100513 ScopedArenaVector<SchedulingNode*>* nodes, const SchedulingGraph& graph) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100514 DCHECK(!nodes->empty());
515 SchedulingNode* select_node = nullptr;
516
517 // Optimize for materialized condition and its emit before use scenario.
518 select_node = SelectMaterializedCondition(nodes, graph);
519
520 if (select_node == nullptr) {
521 // Get highest priority node based on critical path information.
522 select_node = (*nodes)[0];
523 size_t select = 0;
524 for (size_t i = 1, e = nodes->size(); i < e; i++) {
525 SchedulingNode* check = (*nodes)[i];
526 SchedulingNode* candidate = (*nodes)[select];
527 select_node = GetHigherPrioritySchedulingNode(candidate, check);
528 if (select_node == check) {
529 select = i;
530 }
531 }
532 DeleteNodeAtIndex(nodes, select);
533 }
534
535 prev_select_ = select_node;
536 return select_node;
537}
538
539SchedulingNode* CriticalPathSchedulingNodeSelector::GetHigherPrioritySchedulingNode(
540 SchedulingNode* candidate, SchedulingNode* check) const {
541 uint32_t candidate_path = candidate->GetCriticalPath();
542 uint32_t check_path = check->GetCriticalPath();
543 // First look at the critical_path.
544 if (check_path != candidate_path) {
545 return check_path < candidate_path ? check : candidate;
546 }
547 // If both critical paths are equal, schedule instructions with a higher latency
548 // first in program order.
549 return check->GetLatency() < candidate->GetLatency() ? check : candidate;
550}
551
552void HScheduler::Schedule(HGraph* graph) {
Mingyao Yang179861c2017-08-04 13:21:31 -0700553 // We run lsa here instead of in a separate pass to better control whether we
554 // should run the analysis or not.
Vladimir Markoced04832018-07-26 14:42:17 +0100555 const HeapLocationCollector* heap_location_collector = nullptr;
Vladimir Markoef898422020-06-08 10:26:06 +0100556 ScopedArenaAllocator allocator(graph->GetArenaStack());
Alex Light3a73ffb2021-01-25 14:11:05 +0000557 LoadStoreAnalysis lsa(graph, /*stats=*/nullptr, &allocator, LoadStoreAnalysisType::kBasic);
Mingyao Yang179861c2017-08-04 13:21:31 -0700558 if (!only_optimize_loop_blocks_ || graph->HasLoops()) {
559 lsa.Run();
Vladimir Markoced04832018-07-26 14:42:17 +0100560 heap_location_collector = &lsa.GetHeapLocationCollector();
Mingyao Yang179861c2017-08-04 13:21:31 -0700561 }
562
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100563 for (HBasicBlock* block : graph->GetReversePostOrder()) {
564 if (IsSchedulable(block)) {
Vladimir Markoced04832018-07-26 14:42:17 +0100565 Schedule(block, heap_location_collector);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100566 }
567 }
568}
569
Vladimir Markoced04832018-07-26 14:42:17 +0100570void HScheduler::Schedule(HBasicBlock* block,
571 const HeapLocationCollector* heap_location_collector) {
572 ScopedArenaAllocator allocator(block->GetGraph()->GetArenaStack());
573 ScopedArenaVector<SchedulingNode*> scheduling_nodes(allocator.Adapter(kArenaAllocScheduler));
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100574
575 // Build the scheduling graph.
Evgeny Astigeevich957c5382019-03-18 12:37:58 +0000576 SchedulingGraph scheduling_graph(&allocator, heap_location_collector);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100577 for (HBackwardInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
578 HInstruction* instruction = it.Current();
Nicolas Geoffray757b26c2017-06-29 16:11:41 +0100579 CHECK_EQ(instruction->GetBlock(), block)
580 << instruction->DebugName()
581 << " is in block " << instruction->GetBlock()->GetBlockId()
582 << ", and expected in block " << block->GetBlockId();
Vladimir Markoced04832018-07-26 14:42:17 +0100583 SchedulingNode* node = scheduling_graph.AddNode(instruction, IsSchedulingBarrier(instruction));
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100584 CalculateLatency(node);
585 scheduling_nodes.push_back(node);
586 }
587
Vladimir Markoced04832018-07-26 14:42:17 +0100588 if (scheduling_graph.Size() <= 1) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100589 return;
590 }
591
592 cursor_ = block->GetLastInstruction();
593
Vladimir Markoced04832018-07-26 14:42:17 +0100594 // The list of candidates for scheduling. A node becomes a candidate when all
595 // its predecessors have been scheduled.
596 ScopedArenaVector<SchedulingNode*> candidates(allocator.Adapter(kArenaAllocScheduler));
597
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100598 // Find the initial candidates for scheduling.
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100599 for (SchedulingNode* node : scheduling_nodes) {
600 if (!node->HasUnscheduledSuccessors()) {
601 node->MaybeUpdateCriticalPath(node->GetLatency());
Vladimir Markoced04832018-07-26 14:42:17 +0100602 candidates.push_back(node);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100603 }
604 }
605
Vladimir Markoced04832018-07-26 14:42:17 +0100606 ScopedArenaVector<SchedulingNode*> initial_candidates(allocator.Adapter(kArenaAllocScheduler));
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100607 if (kDumpDotSchedulingGraphs) {
608 // Remember the list of initial candidates for debug output purposes.
Vladimir Markoced04832018-07-26 14:42:17 +0100609 initial_candidates.assign(candidates.begin(), candidates.end());
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100610 }
611
612 // Schedule all nodes.
Vladimir Markoced04832018-07-26 14:42:17 +0100613 selector_->Reset();
614 while (!candidates.empty()) {
615 SchedulingNode* node = selector_->PopHighestPriorityNode(&candidates, scheduling_graph);
616 Schedule(node, &candidates);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100617 }
618
619 if (kDumpDotSchedulingGraphs) {
620 // Dump the graph in `dot` format.
621 HGraph* graph = block->GetGraph();
622 std::stringstream description;
623 description << graph->GetDexFile().PrettyMethod(graph->GetMethodIdx())
624 << " B" << block->GetBlockId();
Vladimir Markoced04832018-07-26 14:42:17 +0100625 scheduling_graph.DumpAsDotGraph(description.str(), initial_candidates);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100626 }
627}
628
Vladimir Markoced04832018-07-26 14:42:17 +0100629void HScheduler::Schedule(SchedulingNode* scheduling_node,
630 /*inout*/ ScopedArenaVector<SchedulingNode*>* candidates) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100631 // Check whether any of the node's predecessors will be valid candidates after
632 // this node is scheduled.
633 uint32_t path_to_node = scheduling_node->GetCriticalPath();
634 for (SchedulingNode* predecessor : scheduling_node->GetDataPredecessors()) {
635 predecessor->MaybeUpdateCriticalPath(
636 path_to_node + predecessor->GetInternalLatency() + predecessor->GetLatency());
637 predecessor->DecrementNumberOfUnscheduledSuccessors();
638 if (!predecessor->HasUnscheduledSuccessors()) {
Vladimir Markoced04832018-07-26 14:42:17 +0100639 candidates->push_back(predecessor);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100640 }
641 }
642 for (SchedulingNode* predecessor : scheduling_node->GetOtherPredecessors()) {
643 // Do not update the critical path.
644 // The 'other' (so 'non-data') dependencies (usually) do not represent a
645 // 'material' dependency of nodes on others. They exist for program
646 // correctness. So we do not use them to compute the critical path.
647 predecessor->DecrementNumberOfUnscheduledSuccessors();
648 if (!predecessor->HasUnscheduledSuccessors()) {
Vladimir Markoced04832018-07-26 14:42:17 +0100649 candidates->push_back(predecessor);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100650 }
651 }
652
653 Schedule(scheduling_node->GetInstruction());
654}
655
656// Move an instruction after cursor instruction inside one basic block.
657static void MoveAfterInBlock(HInstruction* instruction, HInstruction* cursor) {
658 DCHECK_EQ(instruction->GetBlock(), cursor->GetBlock());
659 DCHECK_NE(cursor, cursor->GetBlock()->GetLastInstruction());
660 DCHECK(!instruction->IsControlFlow());
661 DCHECK(!cursor->IsControlFlow());
Andreas Gampe3db70682018-12-26 15:12:03 -0800662 instruction->MoveBefore(cursor->GetNext(), /* do_checks= */ false);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100663}
664
665void HScheduler::Schedule(HInstruction* instruction) {
666 if (instruction == cursor_) {
667 cursor_ = cursor_->GetPrevious();
668 } else {
669 MoveAfterInBlock(instruction, cursor_);
670 }
671}
672
673bool HScheduler::IsSchedulable(const HInstruction* instruction) const {
674 // We want to avoid exhaustively listing all instructions, so we first check
675 // for instruction categories that we know are safe.
676 if (instruction->IsControlFlow() ||
677 instruction->IsConstant()) {
678 return true;
679 }
680 // Currently all unary and binary operations are safe to schedule, so avoid
681 // checking for each of them individually.
682 // Since nothing prevents a new scheduling-unsafe HInstruction to subclass
683 // HUnaryOperation (or HBinaryOperation), check in debug mode that we have
684 // the exhaustive lists here.
685 if (instruction->IsUnaryOperation()) {
Aart Bik3dad3412018-02-28 12:01:46 -0800686 DCHECK(instruction->IsAbs() ||
687 instruction->IsBooleanNot() ||
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100688 instruction->IsNot() ||
689 instruction->IsNeg()) << "unexpected instruction " << instruction->DebugName();
690 return true;
691 }
692 if (instruction->IsBinaryOperation()) {
693 DCHECK(instruction->IsAdd() ||
694 instruction->IsAnd() ||
695 instruction->IsCompare() ||
696 instruction->IsCondition() ||
697 instruction->IsDiv() ||
Aart Bik1f8d51b2018-02-15 10:42:37 -0800698 instruction->IsMin() ||
699 instruction->IsMax() ||
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100700 instruction->IsMul() ||
701 instruction->IsOr() ||
702 instruction->IsRem() ||
703 instruction->IsRor() ||
704 instruction->IsShl() ||
705 instruction->IsShr() ||
706 instruction->IsSub() ||
707 instruction->IsUShr() ||
708 instruction->IsXor()) << "unexpected instruction " << instruction->DebugName();
709 return true;
710 }
711 // The scheduler should not see any of these.
712 DCHECK(!instruction->IsParallelMove()) << "unexpected instruction " << instruction->DebugName();
713 // List of instructions explicitly excluded:
714 // HClearException
715 // HClinitCheck
716 // HDeoptimize
717 // HLoadClass
718 // HLoadException
719 // HMemoryBarrier
720 // HMonitorOperation
Santiago Aboy Solanescaf9b652022-06-24 10:03:30 +0100721 // HNop
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100722 // HThrow
723 // HTryBoundary
Santiago Aboy Solanes2e2c94d2022-10-14 16:55:47 +0100724 // All volatile field access e.g. HInstanceFieldGet
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100725 // TODO: Some of the instructions above may be safe to schedule (maybe as
726 // scheduling barriers).
727 return instruction->IsArrayGet() ||
Alex Light3a73ffb2021-01-25 14:11:05 +0000728 instruction->IsArraySet() ||
729 instruction->IsArrayLength() ||
730 instruction->IsBoundType() ||
731 instruction->IsBoundsCheck() ||
732 instruction->IsCheckCast() ||
733 instruction->IsClassTableGet() ||
734 instruction->IsCurrentMethod() ||
735 instruction->IsDivZeroCheck() ||
Vladimir Markocde64972023-04-25 16:40:06 +0000736 (instruction->IsInstanceFieldGet() && !instruction->AsInstanceFieldGet()->IsVolatile()) ||
Alex Light3a73ffb2021-01-25 14:11:05 +0000737 (instruction->IsPredicatedInstanceFieldGet() &&
Vladimir Markocde64972023-04-25 16:40:06 +0000738 !instruction->AsPredicatedInstanceFieldGet()->IsVolatile()) ||
739 (instruction->IsInstanceFieldSet() && !instruction->AsInstanceFieldSet()->IsVolatile()) ||
Alex Light3a73ffb2021-01-25 14:11:05 +0000740 instruction->IsInstanceOf() ||
741 instruction->IsInvokeInterface() ||
742 instruction->IsInvokeStaticOrDirect() ||
743 instruction->IsInvokeUnresolved() ||
744 instruction->IsInvokeVirtual() ||
745 instruction->IsLoadString() ||
746 instruction->IsNewArray() ||
747 instruction->IsNewInstance() ||
748 instruction->IsNullCheck() ||
749 instruction->IsPackedSwitch() ||
750 instruction->IsParameterValue() ||
751 instruction->IsPhi() ||
752 instruction->IsReturn() ||
753 instruction->IsReturnVoid() ||
754 instruction->IsSelect() ||
Vladimir Markocde64972023-04-25 16:40:06 +0000755 (instruction->IsStaticFieldGet() && !instruction->AsStaticFieldGet()->IsVolatile()) ||
756 (instruction->IsStaticFieldSet() && !instruction->AsStaticFieldSet()->IsVolatile()) ||
Alex Light3a73ffb2021-01-25 14:11:05 +0000757 instruction->IsSuspendCheck() ||
758 instruction->IsTypeConversion();
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100759}
760
761bool HScheduler::IsSchedulable(const HBasicBlock* block) const {
762 // We may be only interested in loop blocks.
763 if (only_optimize_loop_blocks_ && !block->IsInLoop()) {
764 return false;
765 }
766 if (block->GetTryCatchInformation() != nullptr) {
767 // Do not schedule blocks that are part of try-catch.
768 // Because scheduler cannot see if catch block has assumptions on the instruction order in
769 // the try block. In following example, if we enable scheduler for the try block,
770 // MulitiplyAccumulate may be scheduled before DivZeroCheck,
771 // which can result in an incorrect value in the catch block.
772 // try {
773 // a = a/b; // DivZeroCheck
774 // // Div
775 // c = c*d+e; // MulitiplyAccumulate
776 // } catch {System.out.print(c); }
777 return false;
778 }
779 // Check whether all instructions in this block are schedulable.
780 for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
781 if (!IsSchedulable(it.Current())) {
782 return false;
783 }
784 }
785 return true;
786}
787
788bool HScheduler::IsSchedulingBarrier(const HInstruction* instr) const {
789 return instr->IsControlFlow() ||
790 // Don't break calling convention.
791 instr->IsParameterValue() ||
792 // Code generation of goto relies on SuspendCheck's position.
793 instr->IsSuspendCheck();
794}
795
Aart Bik24773202018-04-26 10:28:51 -0700796bool HInstructionScheduling::Run(bool only_optimize_loop_blocks,
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100797 bool schedule_randomly) {
xueliang.zhongf7caf682017-03-01 16:07:02 +0000798#if defined(ART_ENABLE_CODEGEN_arm64) || defined(ART_ENABLE_CODEGEN_arm)
799 // Phase-local allocator that allocates scheduler internal data structures like
800 // scheduling nodes, internel nodes map, dependencies, etc.
xueliang.zhongf7caf682017-03-01 16:07:02 +0000801 CriticalPathSchedulingNodeSelector critical_path_selector;
802 RandomSchedulingNodeSelector random_selector;
803 SchedulingNodeSelector* selector = schedule_randomly
804 ? static_cast<SchedulingNodeSelector*>(&random_selector)
805 : static_cast<SchedulingNodeSelector*>(&critical_path_selector);
806#else
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100807 // Avoid compilation error when compiling for unsupported instruction set.
808 UNUSED(only_optimize_loop_blocks);
809 UNUSED(schedule_randomly);
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100810 UNUSED(codegen_);
xueliang.zhongf7caf682017-03-01 16:07:02 +0000811#endif
xueliang.zhong2a3471f2017-05-08 18:36:40 +0100812
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100813 switch (instruction_set_) {
814#ifdef ART_ENABLE_CODEGEN_arm64
Vladimir Marko33bff252017-11-01 14:35:42 +0000815 case InstructionSet::kArm64: {
Vladimir Markoced04832018-07-26 14:42:17 +0100816 arm64::HSchedulerARM64 scheduler(selector);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100817 scheduler.SetOnlyOptimizeLoopBlocks(only_optimize_loop_blocks);
818 scheduler.Schedule(graph_);
819 break;
820 }
821#endif
xueliang.zhongf7caf682017-03-01 16:07:02 +0000822#if defined(ART_ENABLE_CODEGEN_arm)
Vladimir Marko33bff252017-11-01 14:35:42 +0000823 case InstructionSet::kThumb2:
824 case InstructionSet::kArm: {
xueliang.zhongf7caf682017-03-01 16:07:02 +0000825 arm::SchedulingLatencyVisitorARM arm_latency_visitor(codegen_);
Vladimir Markoced04832018-07-26 14:42:17 +0100826 arm::HSchedulerARM scheduler(selector, &arm_latency_visitor);
xueliang.zhongf7caf682017-03-01 16:07:02 +0000827 scheduler.SetOnlyOptimizeLoopBlocks(only_optimize_loop_blocks);
828 scheduler.Schedule(graph_);
829 break;
830 }
831#endif
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100832 default:
833 break;
834 }
Aart Bik24773202018-04-26 10:28:51 -0700835 return true;
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100836}
837
838} // namespace art