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