1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "scheduler_arm64.h"
18 
19 #include "code_generator_utils.h"
20 #include "mirror/array-inl.h"
21 #include "mirror/string.h"
22 
23 namespace art {
24 namespace arm64 {
25 
VisitBinaryOperation(HBinaryOperation * instr)26 void SchedulingLatencyVisitorARM64::VisitBinaryOperation(HBinaryOperation* instr) {
27   last_visited_latency_ = DataType::IsFloatingPointType(instr->GetResultType())
28       ? kArm64FloatingPointOpLatency
29       : kArm64IntegerOpLatency;
30 }
31 
VisitBitwiseNegatedRight(HBitwiseNegatedRight * ATTRIBUTE_UNUSED)32 void SchedulingLatencyVisitorARM64::VisitBitwiseNegatedRight(
33     HBitwiseNegatedRight* ATTRIBUTE_UNUSED) {
34   last_visited_latency_ = kArm64IntegerOpLatency;
35 }
36 
VisitDataProcWithShifterOp(HDataProcWithShifterOp * ATTRIBUTE_UNUSED)37 void SchedulingLatencyVisitorARM64::VisitDataProcWithShifterOp(
38     HDataProcWithShifterOp* ATTRIBUTE_UNUSED) {
39   last_visited_latency_ = kArm64DataProcWithShifterOpLatency;
40 }
41 
VisitIntermediateAddress(HIntermediateAddress * ATTRIBUTE_UNUSED)42 void SchedulingLatencyVisitorARM64::VisitIntermediateAddress(
43     HIntermediateAddress* ATTRIBUTE_UNUSED) {
44   // Although the code generated is a simple `add` instruction, we found through empirical results
45   // that spacing it from its use in memory accesses was beneficial.
46   last_visited_latency_ = kArm64IntegerOpLatency + 2;
47 }
48 
VisitIntermediateAddressIndex(HIntermediateAddressIndex * instr ATTRIBUTE_UNUSED)49 void SchedulingLatencyVisitorARM64::VisitIntermediateAddressIndex(
50     HIntermediateAddressIndex* instr ATTRIBUTE_UNUSED) {
51   // Although the code generated is a simple `add` instruction, we found through empirical results
52   // that spacing it from its use in memory accesses was beneficial.
53   last_visited_latency_ = kArm64DataProcWithShifterOpLatency + 2;
54 }
55 
VisitMultiplyAccumulate(HMultiplyAccumulate * ATTRIBUTE_UNUSED)56 void SchedulingLatencyVisitorARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* ATTRIBUTE_UNUSED) {
57   last_visited_latency_ = kArm64MulIntegerLatency;
58 }
59 
VisitArrayGet(HArrayGet * instruction)60 void SchedulingLatencyVisitorARM64::VisitArrayGet(HArrayGet* instruction) {
61   if (!instruction->GetArray()->IsIntermediateAddress()) {
62     // Take the intermediate address computation into account.
63     last_visited_internal_latency_ = kArm64IntegerOpLatency;
64   }
65   last_visited_latency_ = kArm64MemoryLoadLatency;
66 }
67 
VisitArrayLength(HArrayLength * ATTRIBUTE_UNUSED)68 void SchedulingLatencyVisitorARM64::VisitArrayLength(HArrayLength* ATTRIBUTE_UNUSED) {
69   last_visited_latency_ = kArm64MemoryLoadLatency;
70 }
71 
VisitArraySet(HArraySet * ATTRIBUTE_UNUSED)72 void SchedulingLatencyVisitorARM64::VisitArraySet(HArraySet* ATTRIBUTE_UNUSED) {
73   last_visited_latency_ = kArm64MemoryStoreLatency;
74 }
75 
VisitBoundsCheck(HBoundsCheck * ATTRIBUTE_UNUSED)76 void SchedulingLatencyVisitorARM64::VisitBoundsCheck(HBoundsCheck* ATTRIBUTE_UNUSED) {
77   last_visited_internal_latency_ = kArm64IntegerOpLatency;
78   // Users do not use any data results.
79   last_visited_latency_ = 0;
80 }
81 
VisitDiv(HDiv * instr)82 void SchedulingLatencyVisitorARM64::VisitDiv(HDiv* instr) {
83   DataType::Type type = instr->GetResultType();
84   switch (type) {
85     case DataType::Type::kFloat32:
86       last_visited_latency_ = kArm64DivFloatLatency;
87       break;
88     case DataType::Type::kFloat64:
89       last_visited_latency_ = kArm64DivDoubleLatency;
90       break;
91     default:
92       // Follow the code path used by code generation.
93       if (instr->GetRight()->IsConstant()) {
94         int64_t imm = Int64FromConstant(instr->GetRight()->AsConstant());
95         if (imm == 0) {
96           last_visited_internal_latency_ = 0;
97           last_visited_latency_ = 0;
98         } else if (imm == 1 || imm == -1) {
99           last_visited_internal_latency_ = 0;
100           last_visited_latency_ = kArm64IntegerOpLatency;
101         } else if (IsPowerOfTwo(AbsOrMin(imm))) {
102           last_visited_internal_latency_ = 4 * kArm64IntegerOpLatency;
103           last_visited_latency_ = kArm64IntegerOpLatency;
104         } else {
105           DCHECK(imm <= -2 || imm >= 2);
106           last_visited_internal_latency_ = 4 * kArm64IntegerOpLatency;
107           last_visited_latency_ = kArm64MulIntegerLatency;
108         }
109       } else {
110         last_visited_latency_ = kArm64DivIntegerLatency;
111       }
112       break;
113   }
114 }
115 
VisitInstanceFieldGet(HInstanceFieldGet * ATTRIBUTE_UNUSED)116 void SchedulingLatencyVisitorARM64::VisitInstanceFieldGet(HInstanceFieldGet* ATTRIBUTE_UNUSED) {
117   last_visited_latency_ = kArm64MemoryLoadLatency;
118 }
119 
VisitInstanceOf(HInstanceOf * ATTRIBUTE_UNUSED)120 void SchedulingLatencyVisitorARM64::VisitInstanceOf(HInstanceOf* ATTRIBUTE_UNUSED) {
121   last_visited_internal_latency_ = kArm64CallInternalLatency;
122   last_visited_latency_ = kArm64IntegerOpLatency;
123 }
124 
VisitInvoke(HInvoke * ATTRIBUTE_UNUSED)125 void SchedulingLatencyVisitorARM64::VisitInvoke(HInvoke* ATTRIBUTE_UNUSED) {
126   last_visited_internal_latency_ = kArm64CallInternalLatency;
127   last_visited_latency_ = kArm64CallLatency;
128 }
129 
VisitLoadString(HLoadString * ATTRIBUTE_UNUSED)130 void SchedulingLatencyVisitorARM64::VisitLoadString(HLoadString* ATTRIBUTE_UNUSED) {
131   last_visited_internal_latency_ = kArm64LoadStringInternalLatency;
132   last_visited_latency_ = kArm64MemoryLoadLatency;
133 }
134 
VisitMul(HMul * instr)135 void SchedulingLatencyVisitorARM64::VisitMul(HMul* instr) {
136   last_visited_latency_ = DataType::IsFloatingPointType(instr->GetResultType())
137       ? kArm64MulFloatingPointLatency
138       : kArm64MulIntegerLatency;
139 }
140 
VisitNewArray(HNewArray * ATTRIBUTE_UNUSED)141 void SchedulingLatencyVisitorARM64::VisitNewArray(HNewArray* ATTRIBUTE_UNUSED) {
142   last_visited_internal_latency_ = kArm64IntegerOpLatency + kArm64CallInternalLatency;
143   last_visited_latency_ = kArm64CallLatency;
144 }
145 
VisitNewInstance(HNewInstance * instruction)146 void SchedulingLatencyVisitorARM64::VisitNewInstance(HNewInstance* instruction) {
147   if (instruction->IsStringAlloc()) {
148     last_visited_internal_latency_ = 2 + kArm64MemoryLoadLatency + kArm64CallInternalLatency;
149   } else {
150     last_visited_internal_latency_ = kArm64CallInternalLatency;
151   }
152   last_visited_latency_ = kArm64CallLatency;
153 }
154 
VisitRem(HRem * instruction)155 void SchedulingLatencyVisitorARM64::VisitRem(HRem* instruction) {
156   if (DataType::IsFloatingPointType(instruction->GetResultType())) {
157     last_visited_internal_latency_ = kArm64CallInternalLatency;
158     last_visited_latency_ = kArm64CallLatency;
159   } else {
160     // Follow the code path used by code generation.
161     if (instruction->GetRight()->IsConstant()) {
162       int64_t imm = Int64FromConstant(instruction->GetRight()->AsConstant());
163       if (imm == 0) {
164         last_visited_internal_latency_ = 0;
165         last_visited_latency_ = 0;
166       } else if (imm == 1 || imm == -1) {
167         last_visited_internal_latency_ = 0;
168         last_visited_latency_ = kArm64IntegerOpLatency;
169       } else if (IsPowerOfTwo(AbsOrMin(imm))) {
170         last_visited_internal_latency_ = 4 * kArm64IntegerOpLatency;
171         last_visited_latency_ = kArm64IntegerOpLatency;
172       } else {
173         DCHECK(imm <= -2 || imm >= 2);
174         last_visited_internal_latency_ = 4 * kArm64IntegerOpLatency;
175         last_visited_latency_ = kArm64MulIntegerLatency;
176       }
177     } else {
178       last_visited_internal_latency_ = kArm64DivIntegerLatency;
179       last_visited_latency_ = kArm64MulIntegerLatency;
180     }
181   }
182 }
183 
VisitStaticFieldGet(HStaticFieldGet * ATTRIBUTE_UNUSED)184 void SchedulingLatencyVisitorARM64::VisitStaticFieldGet(HStaticFieldGet* ATTRIBUTE_UNUSED) {
185   last_visited_latency_ = kArm64MemoryLoadLatency;
186 }
187 
VisitSuspendCheck(HSuspendCheck * instruction)188 void SchedulingLatencyVisitorARM64::VisitSuspendCheck(HSuspendCheck* instruction) {
189   HBasicBlock* block = instruction->GetBlock();
190   DCHECK((block->GetLoopInformation() != nullptr) ||
191          (block->IsEntryBlock() && instruction->GetNext()->IsGoto()));
192   // Users do not use any data results.
193   last_visited_latency_ = 0;
194 }
195 
VisitTypeConversion(HTypeConversion * instr)196 void SchedulingLatencyVisitorARM64::VisitTypeConversion(HTypeConversion* instr) {
197   if (DataType::IsFloatingPointType(instr->GetResultType()) ||
198       DataType::IsFloatingPointType(instr->GetInputType())) {
199     last_visited_latency_ = kArm64TypeConversionFloatingPointIntegerLatency;
200   } else {
201     last_visited_latency_ = kArm64IntegerOpLatency;
202   }
203 }
204 
HandleSimpleArithmeticSIMD(HVecOperation * instr)205 void SchedulingLatencyVisitorARM64::HandleSimpleArithmeticSIMD(HVecOperation *instr) {
206   if (DataType::IsFloatingPointType(instr->GetPackedType())) {
207     last_visited_latency_ = kArm64SIMDFloatingPointOpLatency;
208   } else {
209     last_visited_latency_ = kArm64SIMDIntegerOpLatency;
210   }
211 }
212 
VisitVecReplicateScalar(HVecReplicateScalar * instr ATTRIBUTE_UNUSED)213 void SchedulingLatencyVisitorARM64::VisitVecReplicateScalar(
214     HVecReplicateScalar* instr ATTRIBUTE_UNUSED) {
215   last_visited_latency_ = kArm64SIMDReplicateOpLatency;
216 }
217 
VisitVecExtractScalar(HVecExtractScalar * instr)218 void SchedulingLatencyVisitorARM64::VisitVecExtractScalar(HVecExtractScalar* instr) {
219   HandleSimpleArithmeticSIMD(instr);
220 }
221 
VisitVecReduce(HVecReduce * instr)222 void SchedulingLatencyVisitorARM64::VisitVecReduce(HVecReduce* instr) {
223   HandleSimpleArithmeticSIMD(instr);
224 }
225 
VisitVecCnv(HVecCnv * instr ATTRIBUTE_UNUSED)226 void SchedulingLatencyVisitorARM64::VisitVecCnv(HVecCnv* instr ATTRIBUTE_UNUSED) {
227   last_visited_latency_ = kArm64SIMDTypeConversionInt2FPLatency;
228 }
229 
VisitVecNeg(HVecNeg * instr)230 void SchedulingLatencyVisitorARM64::VisitVecNeg(HVecNeg* instr) {
231   HandleSimpleArithmeticSIMD(instr);
232 }
233 
VisitVecAbs(HVecAbs * instr)234 void SchedulingLatencyVisitorARM64::VisitVecAbs(HVecAbs* instr) {
235   HandleSimpleArithmeticSIMD(instr);
236 }
237 
VisitVecNot(HVecNot * instr)238 void SchedulingLatencyVisitorARM64::VisitVecNot(HVecNot* instr) {
239   if (instr->GetPackedType() == DataType::Type::kBool) {
240     last_visited_internal_latency_ = kArm64SIMDIntegerOpLatency;
241   }
242   last_visited_latency_ = kArm64SIMDIntegerOpLatency;
243 }
244 
VisitVecAdd(HVecAdd * instr)245 void SchedulingLatencyVisitorARM64::VisitVecAdd(HVecAdd* instr) {
246   HandleSimpleArithmeticSIMD(instr);
247 }
248 
VisitVecHalvingAdd(HVecHalvingAdd * instr)249 void SchedulingLatencyVisitorARM64::VisitVecHalvingAdd(HVecHalvingAdd* instr) {
250   HandleSimpleArithmeticSIMD(instr);
251 }
252 
VisitVecSub(HVecSub * instr)253 void SchedulingLatencyVisitorARM64::VisitVecSub(HVecSub* instr) {
254   HandleSimpleArithmeticSIMD(instr);
255 }
256 
VisitVecMul(HVecMul * instr)257 void SchedulingLatencyVisitorARM64::VisitVecMul(HVecMul* instr) {
258   if (DataType::IsFloatingPointType(instr->GetPackedType())) {
259     last_visited_latency_ = kArm64SIMDMulFloatingPointLatency;
260   } else {
261     last_visited_latency_ = kArm64SIMDMulIntegerLatency;
262   }
263 }
264 
VisitVecDiv(HVecDiv * instr)265 void SchedulingLatencyVisitorARM64::VisitVecDiv(HVecDiv* instr) {
266   if (instr->GetPackedType() == DataType::Type::kFloat32) {
267     last_visited_latency_ = kArm64SIMDDivFloatLatency;
268   } else {
269     DCHECK(instr->GetPackedType() == DataType::Type::kFloat64);
270     last_visited_latency_ = kArm64SIMDDivDoubleLatency;
271   }
272 }
273 
VisitVecMin(HVecMin * instr)274 void SchedulingLatencyVisitorARM64::VisitVecMin(HVecMin* instr) {
275   HandleSimpleArithmeticSIMD(instr);
276 }
277 
VisitVecMax(HVecMax * instr)278 void SchedulingLatencyVisitorARM64::VisitVecMax(HVecMax* instr) {
279   HandleSimpleArithmeticSIMD(instr);
280 }
281 
VisitVecAnd(HVecAnd * instr ATTRIBUTE_UNUSED)282 void SchedulingLatencyVisitorARM64::VisitVecAnd(HVecAnd* instr ATTRIBUTE_UNUSED) {
283   last_visited_latency_ = kArm64SIMDIntegerOpLatency;
284 }
285 
VisitVecAndNot(HVecAndNot * instr ATTRIBUTE_UNUSED)286 void SchedulingLatencyVisitorARM64::VisitVecAndNot(HVecAndNot* instr ATTRIBUTE_UNUSED) {
287   last_visited_latency_ = kArm64SIMDIntegerOpLatency;
288 }
289 
VisitVecOr(HVecOr * instr ATTRIBUTE_UNUSED)290 void SchedulingLatencyVisitorARM64::VisitVecOr(HVecOr* instr ATTRIBUTE_UNUSED) {
291   last_visited_latency_ = kArm64SIMDIntegerOpLatency;
292 }
293 
VisitVecXor(HVecXor * instr ATTRIBUTE_UNUSED)294 void SchedulingLatencyVisitorARM64::VisitVecXor(HVecXor* instr ATTRIBUTE_UNUSED) {
295   last_visited_latency_ = kArm64SIMDIntegerOpLatency;
296 }
297 
VisitVecShl(HVecShl * instr)298 void SchedulingLatencyVisitorARM64::VisitVecShl(HVecShl* instr) {
299   HandleSimpleArithmeticSIMD(instr);
300 }
301 
VisitVecShr(HVecShr * instr)302 void SchedulingLatencyVisitorARM64::VisitVecShr(HVecShr* instr) {
303   HandleSimpleArithmeticSIMD(instr);
304 }
305 
VisitVecUShr(HVecUShr * instr)306 void SchedulingLatencyVisitorARM64::VisitVecUShr(HVecUShr* instr) {
307   HandleSimpleArithmeticSIMD(instr);
308 }
309 
VisitVecSetScalars(HVecSetScalars * instr)310 void SchedulingLatencyVisitorARM64::VisitVecSetScalars(HVecSetScalars* instr) {
311   HandleSimpleArithmeticSIMD(instr);
312 }
313 
VisitVecMultiplyAccumulate(HVecMultiplyAccumulate * instr ATTRIBUTE_UNUSED)314 void SchedulingLatencyVisitorARM64::VisitVecMultiplyAccumulate(
315     HVecMultiplyAccumulate* instr ATTRIBUTE_UNUSED) {
316   last_visited_latency_ = kArm64SIMDMulIntegerLatency;
317 }
318 
HandleVecAddress(HVecMemoryOperation * instruction,size_t size ATTRIBUTE_UNUSED)319 void SchedulingLatencyVisitorARM64::HandleVecAddress(
320     HVecMemoryOperation* instruction,
321     size_t size ATTRIBUTE_UNUSED) {
322   HInstruction* index = instruction->InputAt(1);
323   if (!index->IsConstant()) {
324     last_visited_internal_latency_ += kArm64DataProcWithShifterOpLatency;
325   }
326 }
327 
VisitVecLoad(HVecLoad * instr)328 void SchedulingLatencyVisitorARM64::VisitVecLoad(HVecLoad* instr) {
329   last_visited_internal_latency_ = 0;
330   size_t size = DataType::Size(instr->GetPackedType());
331 
332   if (instr->GetPackedType() == DataType::Type::kUint16
333       && mirror::kUseStringCompression
334       && instr->IsStringCharAt()) {
335     // Set latencies for the uncompressed case.
336     last_visited_internal_latency_ += kArm64MemoryLoadLatency + kArm64BranchLatency;
337     HandleVecAddress(instr, size);
338     last_visited_latency_ = kArm64SIMDMemoryLoadLatency;
339   } else {
340     HandleVecAddress(instr, size);
341     last_visited_latency_ = kArm64SIMDMemoryLoadLatency;
342   }
343 }
344 
VisitVecStore(HVecStore * instr)345 void SchedulingLatencyVisitorARM64::VisitVecStore(HVecStore* instr) {
346   last_visited_internal_latency_ = 0;
347   size_t size = DataType::Size(instr->GetPackedType());
348   HandleVecAddress(instr, size);
349   last_visited_latency_ = kArm64SIMDMemoryStoreLatency;
350 }
351 
352 }  // namespace arm64
353 }  // namespace art
354