1 /*
2 * Copyright (C) 2015 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 "intrinsics_arm.h"
18
19 #include "arch/arm/instruction_set_features_arm.h"
20 #include "art_method.h"
21 #include "code_generator_arm.h"
22 #include "entrypoints/quick/quick_entrypoints.h"
23 #include "intrinsics.h"
24 #include "intrinsics_utils.h"
25 #include "mirror/array-inl.h"
26 #include "mirror/string.h"
27 #include "thread.h"
28 #include "utils/arm/assembler_arm.h"
29
30 namespace art {
31
32 namespace arm {
33
GetAssembler()34 ArmAssembler* IntrinsicCodeGeneratorARM::GetAssembler() {
35 return codegen_->GetAssembler();
36 }
37
GetAllocator()38 ArenaAllocator* IntrinsicCodeGeneratorARM::GetAllocator() {
39 return codegen_->GetGraph()->GetArena();
40 }
41
42 using IntrinsicSlowPathARM = IntrinsicSlowPath<InvokeDexCallingConventionVisitorARM>;
43
TryDispatch(HInvoke * invoke)44 bool IntrinsicLocationsBuilderARM::TryDispatch(HInvoke* invoke) {
45 Dispatch(invoke);
46 LocationSummary* res = invoke->GetLocations();
47 if (res == nullptr) {
48 return false;
49 }
50 if (kEmitCompilerReadBarrier && res->CanCall()) {
51 // Generating an intrinsic for this HInvoke may produce an
52 // IntrinsicSlowPathARM slow path. Currently this approach
53 // does not work when using read barriers, as the emitted
54 // calling sequence will make use of another slow path
55 // (ReadBarrierForRootSlowPathARM for HInvokeStaticOrDirect,
56 // ReadBarrierSlowPathARM for HInvokeVirtual). So we bail
57 // out in this case.
58 //
59 // TODO: Find a way to have intrinsics work with read barriers.
60 invoke->SetLocations(nullptr);
61 return false;
62 }
63 return res->Intrinsified();
64 }
65
66 #define __ assembler->
67
CreateFPToIntLocations(ArenaAllocator * arena,HInvoke * invoke)68 static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
69 LocationSummary* locations = new (arena) LocationSummary(invoke,
70 LocationSummary::kNoCall,
71 kIntrinsified);
72 locations->SetInAt(0, Location::RequiresFpuRegister());
73 locations->SetOut(Location::RequiresRegister());
74 }
75
CreateIntToFPLocations(ArenaAllocator * arena,HInvoke * invoke)76 static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
77 LocationSummary* locations = new (arena) LocationSummary(invoke,
78 LocationSummary::kNoCall,
79 kIntrinsified);
80 locations->SetInAt(0, Location::RequiresRegister());
81 locations->SetOut(Location::RequiresFpuRegister());
82 }
83
MoveFPToInt(LocationSummary * locations,bool is64bit,ArmAssembler * assembler)84 static void MoveFPToInt(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
85 Location input = locations->InAt(0);
86 Location output = locations->Out();
87 if (is64bit) {
88 __ vmovrrd(output.AsRegisterPairLow<Register>(),
89 output.AsRegisterPairHigh<Register>(),
90 FromLowSToD(input.AsFpuRegisterPairLow<SRegister>()));
91 } else {
92 __ vmovrs(output.AsRegister<Register>(), input.AsFpuRegister<SRegister>());
93 }
94 }
95
MoveIntToFP(LocationSummary * locations,bool is64bit,ArmAssembler * assembler)96 static void MoveIntToFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
97 Location input = locations->InAt(0);
98 Location output = locations->Out();
99 if (is64bit) {
100 __ vmovdrr(FromLowSToD(output.AsFpuRegisterPairLow<SRegister>()),
101 input.AsRegisterPairLow<Register>(),
102 input.AsRegisterPairHigh<Register>());
103 } else {
104 __ vmovsr(output.AsFpuRegister<SRegister>(), input.AsRegister<Register>());
105 }
106 }
107
VisitDoubleDoubleToRawLongBits(HInvoke * invoke)108 void IntrinsicLocationsBuilderARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
109 CreateFPToIntLocations(arena_, invoke);
110 }
VisitDoubleLongBitsToDouble(HInvoke * invoke)111 void IntrinsicLocationsBuilderARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
112 CreateIntToFPLocations(arena_, invoke);
113 }
114
VisitDoubleDoubleToRawLongBits(HInvoke * invoke)115 void IntrinsicCodeGeneratorARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
116 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
117 }
VisitDoubleLongBitsToDouble(HInvoke * invoke)118 void IntrinsicCodeGeneratorARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
119 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
120 }
121
VisitFloatFloatToRawIntBits(HInvoke * invoke)122 void IntrinsicLocationsBuilderARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
123 CreateFPToIntLocations(arena_, invoke);
124 }
VisitFloatIntBitsToFloat(HInvoke * invoke)125 void IntrinsicLocationsBuilderARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
126 CreateIntToFPLocations(arena_, invoke);
127 }
128
VisitFloatFloatToRawIntBits(HInvoke * invoke)129 void IntrinsicCodeGeneratorARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
130 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
131 }
VisitFloatIntBitsToFloat(HInvoke * invoke)132 void IntrinsicCodeGeneratorARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
133 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
134 }
135
CreateIntToIntLocations(ArenaAllocator * arena,HInvoke * invoke)136 static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
137 LocationSummary* locations = new (arena) LocationSummary(invoke,
138 LocationSummary::kNoCall,
139 kIntrinsified);
140 locations->SetInAt(0, Location::RequiresRegister());
141 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
142 }
143
CreateFPToFPLocations(ArenaAllocator * arena,HInvoke * invoke)144 static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
145 LocationSummary* locations = new (arena) LocationSummary(invoke,
146 LocationSummary::kNoCall,
147 kIntrinsified);
148 locations->SetInAt(0, Location::RequiresFpuRegister());
149 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
150 }
151
GenNumberOfLeadingZeros(LocationSummary * locations,Primitive::Type type,ArmAssembler * assembler)152 static void GenNumberOfLeadingZeros(LocationSummary* locations,
153 Primitive::Type type,
154 ArmAssembler* assembler) {
155 Location in = locations->InAt(0);
156 Register out = locations->Out().AsRegister<Register>();
157
158 DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
159
160 if (type == Primitive::kPrimLong) {
161 Register in_reg_lo = in.AsRegisterPairLow<Register>();
162 Register in_reg_hi = in.AsRegisterPairHigh<Register>();
163 Label end;
164 __ clz(out, in_reg_hi);
165 __ CompareAndBranchIfNonZero(in_reg_hi, &end);
166 __ clz(out, in_reg_lo);
167 __ AddConstant(out, 32);
168 __ Bind(&end);
169 } else {
170 __ clz(out, in.AsRegister<Register>());
171 }
172 }
173
VisitIntegerNumberOfLeadingZeros(HInvoke * invoke)174 void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
175 CreateIntToIntLocations(arena_, invoke);
176 }
177
VisitIntegerNumberOfLeadingZeros(HInvoke * invoke)178 void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
179 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
180 }
181
VisitLongNumberOfLeadingZeros(HInvoke * invoke)182 void IntrinsicLocationsBuilderARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
183 LocationSummary* locations = new (arena_) LocationSummary(invoke,
184 LocationSummary::kNoCall,
185 kIntrinsified);
186 locations->SetInAt(0, Location::RequiresRegister());
187 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
188 }
189
VisitLongNumberOfLeadingZeros(HInvoke * invoke)190 void IntrinsicCodeGeneratorARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
191 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
192 }
193
GenNumberOfTrailingZeros(LocationSummary * locations,Primitive::Type type,ArmAssembler * assembler)194 static void GenNumberOfTrailingZeros(LocationSummary* locations,
195 Primitive::Type type,
196 ArmAssembler* assembler) {
197 DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
198
199 Register out = locations->Out().AsRegister<Register>();
200
201 if (type == Primitive::kPrimLong) {
202 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
203 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
204 Label end;
205 __ rbit(out, in_reg_lo);
206 __ clz(out, out);
207 __ CompareAndBranchIfNonZero(in_reg_lo, &end);
208 __ rbit(out, in_reg_hi);
209 __ clz(out, out);
210 __ AddConstant(out, 32);
211 __ Bind(&end);
212 } else {
213 Register in = locations->InAt(0).AsRegister<Register>();
214 __ rbit(out, in);
215 __ clz(out, out);
216 }
217 }
218
VisitIntegerNumberOfTrailingZeros(HInvoke * invoke)219 void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
220 LocationSummary* locations = new (arena_) LocationSummary(invoke,
221 LocationSummary::kNoCall,
222 kIntrinsified);
223 locations->SetInAt(0, Location::RequiresRegister());
224 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
225 }
226
VisitIntegerNumberOfTrailingZeros(HInvoke * invoke)227 void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
228 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
229 }
230
VisitLongNumberOfTrailingZeros(HInvoke * invoke)231 void IntrinsicLocationsBuilderARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
232 LocationSummary* locations = new (arena_) LocationSummary(invoke,
233 LocationSummary::kNoCall,
234 kIntrinsified);
235 locations->SetInAt(0, Location::RequiresRegister());
236 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
237 }
238
VisitLongNumberOfTrailingZeros(HInvoke * invoke)239 void IntrinsicCodeGeneratorARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
240 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
241 }
242
MathAbsFP(LocationSummary * locations,bool is64bit,ArmAssembler * assembler)243 static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
244 Location in = locations->InAt(0);
245 Location out = locations->Out();
246
247 if (is64bit) {
248 __ vabsd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
249 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
250 } else {
251 __ vabss(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>());
252 }
253 }
254
VisitMathAbsDouble(HInvoke * invoke)255 void IntrinsicLocationsBuilderARM::VisitMathAbsDouble(HInvoke* invoke) {
256 CreateFPToFPLocations(arena_, invoke);
257 }
258
VisitMathAbsDouble(HInvoke * invoke)259 void IntrinsicCodeGeneratorARM::VisitMathAbsDouble(HInvoke* invoke) {
260 MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
261 }
262
VisitMathAbsFloat(HInvoke * invoke)263 void IntrinsicLocationsBuilderARM::VisitMathAbsFloat(HInvoke* invoke) {
264 CreateFPToFPLocations(arena_, invoke);
265 }
266
VisitMathAbsFloat(HInvoke * invoke)267 void IntrinsicCodeGeneratorARM::VisitMathAbsFloat(HInvoke* invoke) {
268 MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
269 }
270
CreateIntToIntPlusTemp(ArenaAllocator * arena,HInvoke * invoke)271 static void CreateIntToIntPlusTemp(ArenaAllocator* arena, HInvoke* invoke) {
272 LocationSummary* locations = new (arena) LocationSummary(invoke,
273 LocationSummary::kNoCall,
274 kIntrinsified);
275 locations->SetInAt(0, Location::RequiresRegister());
276 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
277
278 locations->AddTemp(Location::RequiresRegister());
279 }
280
GenAbsInteger(LocationSummary * locations,bool is64bit,ArmAssembler * assembler)281 static void GenAbsInteger(LocationSummary* locations,
282 bool is64bit,
283 ArmAssembler* assembler) {
284 Location in = locations->InAt(0);
285 Location output = locations->Out();
286
287 Register mask = locations->GetTemp(0).AsRegister<Register>();
288
289 if (is64bit) {
290 Register in_reg_lo = in.AsRegisterPairLow<Register>();
291 Register in_reg_hi = in.AsRegisterPairHigh<Register>();
292 Register out_reg_lo = output.AsRegisterPairLow<Register>();
293 Register out_reg_hi = output.AsRegisterPairHigh<Register>();
294
295 DCHECK_NE(out_reg_lo, in_reg_hi) << "Diagonal overlap unexpected.";
296
297 __ Asr(mask, in_reg_hi, 31);
298 __ adds(out_reg_lo, in_reg_lo, ShifterOperand(mask));
299 __ adc(out_reg_hi, in_reg_hi, ShifterOperand(mask));
300 __ eor(out_reg_lo, mask, ShifterOperand(out_reg_lo));
301 __ eor(out_reg_hi, mask, ShifterOperand(out_reg_hi));
302 } else {
303 Register in_reg = in.AsRegister<Register>();
304 Register out_reg = output.AsRegister<Register>();
305
306 __ Asr(mask, in_reg, 31);
307 __ add(out_reg, in_reg, ShifterOperand(mask));
308 __ eor(out_reg, mask, ShifterOperand(out_reg));
309 }
310 }
311
VisitMathAbsInt(HInvoke * invoke)312 void IntrinsicLocationsBuilderARM::VisitMathAbsInt(HInvoke* invoke) {
313 CreateIntToIntPlusTemp(arena_, invoke);
314 }
315
VisitMathAbsInt(HInvoke * invoke)316 void IntrinsicCodeGeneratorARM::VisitMathAbsInt(HInvoke* invoke) {
317 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
318 }
319
320
VisitMathAbsLong(HInvoke * invoke)321 void IntrinsicLocationsBuilderARM::VisitMathAbsLong(HInvoke* invoke) {
322 CreateIntToIntPlusTemp(arena_, invoke);
323 }
324
VisitMathAbsLong(HInvoke * invoke)325 void IntrinsicCodeGeneratorARM::VisitMathAbsLong(HInvoke* invoke) {
326 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
327 }
328
GenMinMax(LocationSummary * locations,bool is_min,ArmAssembler * assembler)329 static void GenMinMax(LocationSummary* locations,
330 bool is_min,
331 ArmAssembler* assembler) {
332 Register op1 = locations->InAt(0).AsRegister<Register>();
333 Register op2 = locations->InAt(1).AsRegister<Register>();
334 Register out = locations->Out().AsRegister<Register>();
335
336 __ cmp(op1, ShifterOperand(op2));
337
338 __ it((is_min) ? Condition::LT : Condition::GT, kItElse);
339 __ mov(out, ShifterOperand(op1), is_min ? Condition::LT : Condition::GT);
340 __ mov(out, ShifterOperand(op2), is_min ? Condition::GE : Condition::LE);
341 }
342
CreateIntIntToIntLocations(ArenaAllocator * arena,HInvoke * invoke)343 static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
344 LocationSummary* locations = new (arena) LocationSummary(invoke,
345 LocationSummary::kNoCall,
346 kIntrinsified);
347 locations->SetInAt(0, Location::RequiresRegister());
348 locations->SetInAt(1, Location::RequiresRegister());
349 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
350 }
351
VisitMathMinIntInt(HInvoke * invoke)352 void IntrinsicLocationsBuilderARM::VisitMathMinIntInt(HInvoke* invoke) {
353 CreateIntIntToIntLocations(arena_, invoke);
354 }
355
VisitMathMinIntInt(HInvoke * invoke)356 void IntrinsicCodeGeneratorARM::VisitMathMinIntInt(HInvoke* invoke) {
357 GenMinMax(invoke->GetLocations(), /* is_min */ true, GetAssembler());
358 }
359
VisitMathMaxIntInt(HInvoke * invoke)360 void IntrinsicLocationsBuilderARM::VisitMathMaxIntInt(HInvoke* invoke) {
361 CreateIntIntToIntLocations(arena_, invoke);
362 }
363
VisitMathMaxIntInt(HInvoke * invoke)364 void IntrinsicCodeGeneratorARM::VisitMathMaxIntInt(HInvoke* invoke) {
365 GenMinMax(invoke->GetLocations(), /* is_min */ false, GetAssembler());
366 }
367
VisitMathSqrt(HInvoke * invoke)368 void IntrinsicLocationsBuilderARM::VisitMathSqrt(HInvoke* invoke) {
369 CreateFPToFPLocations(arena_, invoke);
370 }
371
VisitMathSqrt(HInvoke * invoke)372 void IntrinsicCodeGeneratorARM::VisitMathSqrt(HInvoke* invoke) {
373 LocationSummary* locations = invoke->GetLocations();
374 ArmAssembler* assembler = GetAssembler();
375 __ vsqrtd(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
376 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
377 }
378
VisitMemoryPeekByte(HInvoke * invoke)379 void IntrinsicLocationsBuilderARM::VisitMemoryPeekByte(HInvoke* invoke) {
380 CreateIntToIntLocations(arena_, invoke);
381 }
382
VisitMemoryPeekByte(HInvoke * invoke)383 void IntrinsicCodeGeneratorARM::VisitMemoryPeekByte(HInvoke* invoke) {
384 ArmAssembler* assembler = GetAssembler();
385 // Ignore upper 4B of long address.
386 __ ldrsb(invoke->GetLocations()->Out().AsRegister<Register>(),
387 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
388 }
389
VisitMemoryPeekIntNative(HInvoke * invoke)390 void IntrinsicLocationsBuilderARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
391 CreateIntToIntLocations(arena_, invoke);
392 }
393
VisitMemoryPeekIntNative(HInvoke * invoke)394 void IntrinsicCodeGeneratorARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
395 ArmAssembler* assembler = GetAssembler();
396 // Ignore upper 4B of long address.
397 __ ldr(invoke->GetLocations()->Out().AsRegister<Register>(),
398 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
399 }
400
VisitMemoryPeekLongNative(HInvoke * invoke)401 void IntrinsicLocationsBuilderARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
402 CreateIntToIntLocations(arena_, invoke);
403 }
404
VisitMemoryPeekLongNative(HInvoke * invoke)405 void IntrinsicCodeGeneratorARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
406 ArmAssembler* assembler = GetAssembler();
407 // Ignore upper 4B of long address.
408 Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
409 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
410 // exception. So we can't use ldrd as addr may be unaligned.
411 Register lo = invoke->GetLocations()->Out().AsRegisterPairLow<Register>();
412 Register hi = invoke->GetLocations()->Out().AsRegisterPairHigh<Register>();
413 if (addr == lo) {
414 __ ldr(hi, Address(addr, 4));
415 __ ldr(lo, Address(addr, 0));
416 } else {
417 __ ldr(lo, Address(addr, 0));
418 __ ldr(hi, Address(addr, 4));
419 }
420 }
421
VisitMemoryPeekShortNative(HInvoke * invoke)422 void IntrinsicLocationsBuilderARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
423 CreateIntToIntLocations(arena_, invoke);
424 }
425
VisitMemoryPeekShortNative(HInvoke * invoke)426 void IntrinsicCodeGeneratorARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
427 ArmAssembler* assembler = GetAssembler();
428 // Ignore upper 4B of long address.
429 __ ldrsh(invoke->GetLocations()->Out().AsRegister<Register>(),
430 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
431 }
432
CreateIntIntToVoidLocations(ArenaAllocator * arena,HInvoke * invoke)433 static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
434 LocationSummary* locations = new (arena) LocationSummary(invoke,
435 LocationSummary::kNoCall,
436 kIntrinsified);
437 locations->SetInAt(0, Location::RequiresRegister());
438 locations->SetInAt(1, Location::RequiresRegister());
439 }
440
VisitMemoryPokeByte(HInvoke * invoke)441 void IntrinsicLocationsBuilderARM::VisitMemoryPokeByte(HInvoke* invoke) {
442 CreateIntIntToVoidLocations(arena_, invoke);
443 }
444
VisitMemoryPokeByte(HInvoke * invoke)445 void IntrinsicCodeGeneratorARM::VisitMemoryPokeByte(HInvoke* invoke) {
446 ArmAssembler* assembler = GetAssembler();
447 __ strb(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
448 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
449 }
450
VisitMemoryPokeIntNative(HInvoke * invoke)451 void IntrinsicLocationsBuilderARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
452 CreateIntIntToVoidLocations(arena_, invoke);
453 }
454
VisitMemoryPokeIntNative(HInvoke * invoke)455 void IntrinsicCodeGeneratorARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
456 ArmAssembler* assembler = GetAssembler();
457 __ str(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
458 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
459 }
460
VisitMemoryPokeLongNative(HInvoke * invoke)461 void IntrinsicLocationsBuilderARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
462 CreateIntIntToVoidLocations(arena_, invoke);
463 }
464
VisitMemoryPokeLongNative(HInvoke * invoke)465 void IntrinsicCodeGeneratorARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
466 ArmAssembler* assembler = GetAssembler();
467 // Ignore upper 4B of long address.
468 Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
469 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
470 // exception. So we can't use ldrd as addr may be unaligned.
471 __ str(invoke->GetLocations()->InAt(1).AsRegisterPairLow<Register>(), Address(addr, 0));
472 __ str(invoke->GetLocations()->InAt(1).AsRegisterPairHigh<Register>(), Address(addr, 4));
473 }
474
VisitMemoryPokeShortNative(HInvoke * invoke)475 void IntrinsicLocationsBuilderARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
476 CreateIntIntToVoidLocations(arena_, invoke);
477 }
478
VisitMemoryPokeShortNative(HInvoke * invoke)479 void IntrinsicCodeGeneratorARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
480 ArmAssembler* assembler = GetAssembler();
481 __ strh(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
482 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
483 }
484
VisitThreadCurrentThread(HInvoke * invoke)485 void IntrinsicLocationsBuilderARM::VisitThreadCurrentThread(HInvoke* invoke) {
486 LocationSummary* locations = new (arena_) LocationSummary(invoke,
487 LocationSummary::kNoCall,
488 kIntrinsified);
489 locations->SetOut(Location::RequiresRegister());
490 }
491
VisitThreadCurrentThread(HInvoke * invoke)492 void IntrinsicCodeGeneratorARM::VisitThreadCurrentThread(HInvoke* invoke) {
493 ArmAssembler* assembler = GetAssembler();
494 __ LoadFromOffset(kLoadWord,
495 invoke->GetLocations()->Out().AsRegister<Register>(),
496 TR,
497 Thread::PeerOffset<kArmPointerSize>().Int32Value());
498 }
499
GenUnsafeGet(HInvoke * invoke,Primitive::Type type,bool is_volatile,CodeGeneratorARM * codegen)500 static void GenUnsafeGet(HInvoke* invoke,
501 Primitive::Type type,
502 bool is_volatile,
503 CodeGeneratorARM* codegen) {
504 LocationSummary* locations = invoke->GetLocations();
505 ArmAssembler* assembler = codegen->GetAssembler();
506 Location base_loc = locations->InAt(1);
507 Register base = base_loc.AsRegister<Register>(); // Object pointer.
508 Location offset_loc = locations->InAt(2);
509 Register offset = offset_loc.AsRegisterPairLow<Register>(); // Long offset, lo part only.
510 Location trg_loc = locations->Out();
511
512 switch (type) {
513 case Primitive::kPrimInt: {
514 Register trg = trg_loc.AsRegister<Register>();
515 __ ldr(trg, Address(base, offset));
516 if (is_volatile) {
517 __ dmb(ISH);
518 }
519 break;
520 }
521
522 case Primitive::kPrimNot: {
523 Register trg = trg_loc.AsRegister<Register>();
524 if (kEmitCompilerReadBarrier) {
525 if (kUseBakerReadBarrier) {
526 Location temp = locations->GetTemp(0);
527 codegen->GenerateArrayLoadWithBakerReadBarrier(
528 invoke, trg_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false);
529 if (is_volatile) {
530 __ dmb(ISH);
531 }
532 } else {
533 __ ldr(trg, Address(base, offset));
534 if (is_volatile) {
535 __ dmb(ISH);
536 }
537 codegen->GenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
538 }
539 } else {
540 __ ldr(trg, Address(base, offset));
541 if (is_volatile) {
542 __ dmb(ISH);
543 }
544 __ MaybeUnpoisonHeapReference(trg);
545 }
546 break;
547 }
548
549 case Primitive::kPrimLong: {
550 Register trg_lo = trg_loc.AsRegisterPairLow<Register>();
551 __ add(IP, base, ShifterOperand(offset));
552 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
553 Register trg_hi = trg_loc.AsRegisterPairHigh<Register>();
554 __ ldrexd(trg_lo, trg_hi, IP);
555 } else {
556 __ ldrd(trg_lo, Address(IP));
557 }
558 if (is_volatile) {
559 __ dmb(ISH);
560 }
561 break;
562 }
563
564 default:
565 LOG(FATAL) << "Unexpected type " << type;
566 UNREACHABLE();
567 }
568 }
569
CreateIntIntIntToIntLocations(ArenaAllocator * arena,HInvoke * invoke,Primitive::Type type)570 static void CreateIntIntIntToIntLocations(ArenaAllocator* arena,
571 HInvoke* invoke,
572 Primitive::Type type) {
573 bool can_call = kEmitCompilerReadBarrier &&
574 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
575 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
576 LocationSummary* locations = new (arena) LocationSummary(invoke,
577 can_call ?
578 LocationSummary::kCallOnSlowPath :
579 LocationSummary::kNoCall,
580 kIntrinsified);
581 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
582 locations->SetInAt(1, Location::RequiresRegister());
583 locations->SetInAt(2, Location::RequiresRegister());
584 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
585 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
586 // We need a temporary register for the read barrier marking slow
587 // path in InstructionCodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
588 locations->AddTemp(Location::RequiresRegister());
589 }
590 }
591
VisitUnsafeGet(HInvoke * invoke)592 void IntrinsicLocationsBuilderARM::VisitUnsafeGet(HInvoke* invoke) {
593 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
594 }
VisitUnsafeGetVolatile(HInvoke * invoke)595 void IntrinsicLocationsBuilderARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
596 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
597 }
VisitUnsafeGetLong(HInvoke * invoke)598 void IntrinsicLocationsBuilderARM::VisitUnsafeGetLong(HInvoke* invoke) {
599 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
600 }
VisitUnsafeGetLongVolatile(HInvoke * invoke)601 void IntrinsicLocationsBuilderARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
602 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
603 }
VisitUnsafeGetObject(HInvoke * invoke)604 void IntrinsicLocationsBuilderARM::VisitUnsafeGetObject(HInvoke* invoke) {
605 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
606 }
VisitUnsafeGetObjectVolatile(HInvoke * invoke)607 void IntrinsicLocationsBuilderARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
608 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
609 }
610
VisitUnsafeGet(HInvoke * invoke)611 void IntrinsicCodeGeneratorARM::VisitUnsafeGet(HInvoke* invoke) {
612 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
613 }
VisitUnsafeGetVolatile(HInvoke * invoke)614 void IntrinsicCodeGeneratorARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
615 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
616 }
VisitUnsafeGetLong(HInvoke * invoke)617 void IntrinsicCodeGeneratorARM::VisitUnsafeGetLong(HInvoke* invoke) {
618 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
619 }
VisitUnsafeGetLongVolatile(HInvoke * invoke)620 void IntrinsicCodeGeneratorARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
621 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
622 }
VisitUnsafeGetObject(HInvoke * invoke)623 void IntrinsicCodeGeneratorARM::VisitUnsafeGetObject(HInvoke* invoke) {
624 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
625 }
VisitUnsafeGetObjectVolatile(HInvoke * invoke)626 void IntrinsicCodeGeneratorARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
627 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
628 }
629
CreateIntIntIntIntToVoid(ArenaAllocator * arena,const ArmInstructionSetFeatures & features,Primitive::Type type,bool is_volatile,HInvoke * invoke)630 static void CreateIntIntIntIntToVoid(ArenaAllocator* arena,
631 const ArmInstructionSetFeatures& features,
632 Primitive::Type type,
633 bool is_volatile,
634 HInvoke* invoke) {
635 LocationSummary* locations = new (arena) LocationSummary(invoke,
636 LocationSummary::kNoCall,
637 kIntrinsified);
638 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
639 locations->SetInAt(1, Location::RequiresRegister());
640 locations->SetInAt(2, Location::RequiresRegister());
641 locations->SetInAt(3, Location::RequiresRegister());
642
643 if (type == Primitive::kPrimLong) {
644 // Potentially need temps for ldrexd-strexd loop.
645 if (is_volatile && !features.HasAtomicLdrdAndStrd()) {
646 locations->AddTemp(Location::RequiresRegister()); // Temp_lo.
647 locations->AddTemp(Location::RequiresRegister()); // Temp_hi.
648 }
649 } else if (type == Primitive::kPrimNot) {
650 // Temps for card-marking.
651 locations->AddTemp(Location::RequiresRegister()); // Temp.
652 locations->AddTemp(Location::RequiresRegister()); // Card.
653 }
654 }
655
VisitUnsafePut(HInvoke * invoke)656 void IntrinsicLocationsBuilderARM::VisitUnsafePut(HInvoke* invoke) {
657 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ false, invoke);
658 }
VisitUnsafePutOrdered(HInvoke * invoke)659 void IntrinsicLocationsBuilderARM::VisitUnsafePutOrdered(HInvoke* invoke) {
660 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ false, invoke);
661 }
VisitUnsafePutVolatile(HInvoke * invoke)662 void IntrinsicLocationsBuilderARM::VisitUnsafePutVolatile(HInvoke* invoke) {
663 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ true, invoke);
664 }
VisitUnsafePutObject(HInvoke * invoke)665 void IntrinsicLocationsBuilderARM::VisitUnsafePutObject(HInvoke* invoke) {
666 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ false, invoke);
667 }
VisitUnsafePutObjectOrdered(HInvoke * invoke)668 void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
669 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ false, invoke);
670 }
VisitUnsafePutObjectVolatile(HInvoke * invoke)671 void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
672 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ true, invoke);
673 }
VisitUnsafePutLong(HInvoke * invoke)674 void IntrinsicLocationsBuilderARM::VisitUnsafePutLong(HInvoke* invoke) {
675 CreateIntIntIntIntToVoid(
676 arena_, features_, Primitive::kPrimLong, /* is_volatile */ false, invoke);
677 }
VisitUnsafePutLongOrdered(HInvoke * invoke)678 void IntrinsicLocationsBuilderARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
679 CreateIntIntIntIntToVoid(
680 arena_, features_, Primitive::kPrimLong, /* is_volatile */ false, invoke);
681 }
VisitUnsafePutLongVolatile(HInvoke * invoke)682 void IntrinsicLocationsBuilderARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
683 CreateIntIntIntIntToVoid(
684 arena_, features_, Primitive::kPrimLong, /* is_volatile */ true, invoke);
685 }
686
GenUnsafePut(LocationSummary * locations,Primitive::Type type,bool is_volatile,bool is_ordered,CodeGeneratorARM * codegen)687 static void GenUnsafePut(LocationSummary* locations,
688 Primitive::Type type,
689 bool is_volatile,
690 bool is_ordered,
691 CodeGeneratorARM* codegen) {
692 ArmAssembler* assembler = codegen->GetAssembler();
693
694 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
695 Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Long offset, lo part only.
696 Register value;
697
698 if (is_volatile || is_ordered) {
699 __ dmb(ISH);
700 }
701
702 if (type == Primitive::kPrimLong) {
703 Register value_lo = locations->InAt(3).AsRegisterPairLow<Register>();
704 value = value_lo;
705 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
706 Register temp_lo = locations->GetTemp(0).AsRegister<Register>();
707 Register temp_hi = locations->GetTemp(1).AsRegister<Register>();
708 Register value_hi = locations->InAt(3).AsRegisterPairHigh<Register>();
709
710 __ add(IP, base, ShifterOperand(offset));
711 Label loop_head;
712 __ Bind(&loop_head);
713 __ ldrexd(temp_lo, temp_hi, IP);
714 __ strexd(temp_lo, value_lo, value_hi, IP);
715 __ cmp(temp_lo, ShifterOperand(0));
716 __ b(&loop_head, NE);
717 } else {
718 __ add(IP, base, ShifterOperand(offset));
719 __ strd(value_lo, Address(IP));
720 }
721 } else {
722 value = locations->InAt(3).AsRegister<Register>();
723 Register source = value;
724 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
725 Register temp = locations->GetTemp(0).AsRegister<Register>();
726 __ Mov(temp, value);
727 __ PoisonHeapReference(temp);
728 source = temp;
729 }
730 __ str(source, Address(base, offset));
731 }
732
733 if (is_volatile) {
734 __ dmb(ISH);
735 }
736
737 if (type == Primitive::kPrimNot) {
738 Register temp = locations->GetTemp(0).AsRegister<Register>();
739 Register card = locations->GetTemp(1).AsRegister<Register>();
740 bool value_can_be_null = true; // TODO: Worth finding out this information?
741 codegen->MarkGCCard(temp, card, base, value, value_can_be_null);
742 }
743 }
744
VisitUnsafePut(HInvoke * invoke)745 void IntrinsicCodeGeneratorARM::VisitUnsafePut(HInvoke* invoke) {
746 GenUnsafePut(invoke->GetLocations(),
747 Primitive::kPrimInt,
748 /* is_volatile */ false,
749 /* is_ordered */ false,
750 codegen_);
751 }
VisitUnsafePutOrdered(HInvoke * invoke)752 void IntrinsicCodeGeneratorARM::VisitUnsafePutOrdered(HInvoke* invoke) {
753 GenUnsafePut(invoke->GetLocations(),
754 Primitive::kPrimInt,
755 /* is_volatile */ false,
756 /* is_ordered */ true,
757 codegen_);
758 }
VisitUnsafePutVolatile(HInvoke * invoke)759 void IntrinsicCodeGeneratorARM::VisitUnsafePutVolatile(HInvoke* invoke) {
760 GenUnsafePut(invoke->GetLocations(),
761 Primitive::kPrimInt,
762 /* is_volatile */ true,
763 /* is_ordered */ false,
764 codegen_);
765 }
VisitUnsafePutObject(HInvoke * invoke)766 void IntrinsicCodeGeneratorARM::VisitUnsafePutObject(HInvoke* invoke) {
767 GenUnsafePut(invoke->GetLocations(),
768 Primitive::kPrimNot,
769 /* is_volatile */ false,
770 /* is_ordered */ false,
771 codegen_);
772 }
VisitUnsafePutObjectOrdered(HInvoke * invoke)773 void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
774 GenUnsafePut(invoke->GetLocations(),
775 Primitive::kPrimNot,
776 /* is_volatile */ false,
777 /* is_ordered */ true,
778 codegen_);
779 }
VisitUnsafePutObjectVolatile(HInvoke * invoke)780 void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
781 GenUnsafePut(invoke->GetLocations(),
782 Primitive::kPrimNot,
783 /* is_volatile */ true,
784 /* is_ordered */ false,
785 codegen_);
786 }
VisitUnsafePutLong(HInvoke * invoke)787 void IntrinsicCodeGeneratorARM::VisitUnsafePutLong(HInvoke* invoke) {
788 GenUnsafePut(invoke->GetLocations(),
789 Primitive::kPrimLong,
790 /* is_volatile */ false,
791 /* is_ordered */ false,
792 codegen_);
793 }
VisitUnsafePutLongOrdered(HInvoke * invoke)794 void IntrinsicCodeGeneratorARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
795 GenUnsafePut(invoke->GetLocations(),
796 Primitive::kPrimLong,
797 /* is_volatile */ false,
798 /* is_ordered */ true,
799 codegen_);
800 }
VisitUnsafePutLongVolatile(HInvoke * invoke)801 void IntrinsicCodeGeneratorARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
802 GenUnsafePut(invoke->GetLocations(),
803 Primitive::kPrimLong,
804 /* is_volatile */ true,
805 /* is_ordered */ false,
806 codegen_);
807 }
808
CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator * arena,HInvoke * invoke,Primitive::Type type)809 static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena,
810 HInvoke* invoke,
811 Primitive::Type type) {
812 LocationSummary* locations = new (arena) LocationSummary(invoke,
813 LocationSummary::kNoCall,
814 kIntrinsified);
815 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
816 locations->SetInAt(1, Location::RequiresRegister());
817 locations->SetInAt(2, Location::RequiresRegister());
818 locations->SetInAt(3, Location::RequiresRegister());
819 locations->SetInAt(4, Location::RequiresRegister());
820
821 // If heap poisoning is enabled, we don't want the unpoisoning
822 // operations to potentially clobber the output.
823 Location::OutputOverlap overlaps = (kPoisonHeapReferences && type == Primitive::kPrimNot)
824 ? Location::kOutputOverlap
825 : Location::kNoOutputOverlap;
826 locations->SetOut(Location::RequiresRegister(), overlaps);
827
828 locations->AddTemp(Location::RequiresRegister()); // Pointer.
829 locations->AddTemp(Location::RequiresRegister()); // Temp 1.
830 }
831
GenCas(LocationSummary * locations,Primitive::Type type,CodeGeneratorARM * codegen)832 static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM* codegen) {
833 DCHECK_NE(type, Primitive::kPrimLong);
834
835 ArmAssembler* assembler = codegen->GetAssembler();
836
837 Register out = locations->Out().AsRegister<Register>(); // Boolean result.
838
839 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
840 Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Offset (discard high 4B).
841 Register expected_lo = locations->InAt(3).AsRegister<Register>(); // Expected.
842 Register value_lo = locations->InAt(4).AsRegister<Register>(); // Value.
843
844 Register tmp_ptr = locations->GetTemp(0).AsRegister<Register>(); // Pointer to actual memory.
845 Register tmp_lo = locations->GetTemp(1).AsRegister<Register>(); // Value in memory.
846
847 if (type == Primitive::kPrimNot) {
848 // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
849 // object and scan the receiver at the next GC for nothing.
850 bool value_can_be_null = true; // TODO: Worth finding out this information?
851 codegen->MarkGCCard(tmp_ptr, tmp_lo, base, value_lo, value_can_be_null);
852 }
853
854 // Prevent reordering with prior memory operations.
855 // Emit a DMB ISH instruction instead of an DMB ISHST one, as the
856 // latter allows a preceding load to be delayed past the STXR
857 // instruction below.
858 __ dmb(ISH);
859
860 __ add(tmp_ptr, base, ShifterOperand(offset));
861
862 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
863 codegen->GetAssembler()->PoisonHeapReference(expected_lo);
864 if (value_lo == expected_lo) {
865 // Do not poison `value_lo`, as it is the same register as
866 // `expected_lo`, which has just been poisoned.
867 } else {
868 codegen->GetAssembler()->PoisonHeapReference(value_lo);
869 }
870 }
871
872 // do {
873 // tmp = [r_ptr] - expected;
874 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
875 // result = tmp != 0;
876
877 Label loop_head;
878 __ Bind(&loop_head);
879
880 // TODO: When `type == Primitive::kPrimNot`, add a read barrier for
881 // the reference stored in the object before attempting the CAS,
882 // similar to the one in the art::Unsafe_compareAndSwapObject JNI
883 // implementation.
884 //
885 // Note that this code is not (yet) used when read barriers are
886 // enabled (see IntrinsicLocationsBuilderARM::VisitUnsafeCASObject).
887 DCHECK(!(type == Primitive::kPrimNot && kEmitCompilerReadBarrier));
888 __ ldrex(tmp_lo, tmp_ptr);
889
890 __ subs(tmp_lo, tmp_lo, ShifterOperand(expected_lo));
891
892 __ it(EQ, ItState::kItT);
893 __ strex(tmp_lo, value_lo, tmp_ptr, EQ);
894 __ cmp(tmp_lo, ShifterOperand(1), EQ);
895
896 __ b(&loop_head, EQ);
897
898 __ dmb(ISH);
899
900 __ rsbs(out, tmp_lo, ShifterOperand(1));
901 __ it(CC);
902 __ mov(out, ShifterOperand(0), CC);
903
904 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
905 codegen->GetAssembler()->UnpoisonHeapReference(expected_lo);
906 if (value_lo == expected_lo) {
907 // Do not unpoison `value_lo`, as it is the same register as
908 // `expected_lo`, which has just been unpoisoned.
909 } else {
910 codegen->GetAssembler()->UnpoisonHeapReference(value_lo);
911 }
912 }
913 }
914
VisitUnsafeCASInt(HInvoke * invoke)915 void IntrinsicLocationsBuilderARM::VisitUnsafeCASInt(HInvoke* invoke) {
916 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimInt);
917 }
VisitUnsafeCASObject(HInvoke * invoke)918 void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke) {
919 // The UnsafeCASObject intrinsic is missing a read barrier, and
920 // therefore sometimes does not work as expected (b/25883050).
921 // Turn it off temporarily as a quick fix, until the read barrier is
922 // implemented (see TODO in GenCAS below).
923 //
924 // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers.
925 if (kEmitCompilerReadBarrier) {
926 return;
927 }
928
929 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimNot);
930 }
VisitUnsafeCASInt(HInvoke * invoke)931 void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) {
932 GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
933 }
VisitUnsafeCASObject(HInvoke * invoke)934 void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) {
935 GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
936 }
937
VisitStringCharAt(HInvoke * invoke)938 void IntrinsicLocationsBuilderARM::VisitStringCharAt(HInvoke* invoke) {
939 LocationSummary* locations = new (arena_) LocationSummary(invoke,
940 LocationSummary::kCallOnSlowPath,
941 kIntrinsified);
942 locations->SetInAt(0, Location::RequiresRegister());
943 locations->SetInAt(1, Location::RequiresRegister());
944 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
945
946 locations->AddTemp(Location::RequiresRegister());
947 locations->AddTemp(Location::RequiresRegister());
948 }
949
VisitStringCharAt(HInvoke * invoke)950 void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) {
951 ArmAssembler* assembler = GetAssembler();
952 LocationSummary* locations = invoke->GetLocations();
953
954 // Location of reference to data array
955 const MemberOffset value_offset = mirror::String::ValueOffset();
956 // Location of count
957 const MemberOffset count_offset = mirror::String::CountOffset();
958
959 Register obj = locations->InAt(0).AsRegister<Register>(); // String object pointer.
960 Register idx = locations->InAt(1).AsRegister<Register>(); // Index of character.
961 Register out = locations->Out().AsRegister<Register>(); // Result character.
962
963 Register temp = locations->GetTemp(0).AsRegister<Register>();
964 Register array_temp = locations->GetTemp(1).AsRegister<Register>();
965
966 // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
967 // the cost.
968 // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
969 // we will not optimize the code for constants (which would save a register).
970
971 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
972 codegen_->AddSlowPath(slow_path);
973
974 __ ldr(temp, Address(obj, count_offset.Int32Value())); // temp = str.length.
975 codegen_->MaybeRecordImplicitNullCheck(invoke);
976 __ cmp(idx, ShifterOperand(temp));
977 __ b(slow_path->GetEntryLabel(), CS);
978
979 __ add(array_temp, obj, ShifterOperand(value_offset.Int32Value())); // array_temp := str.value.
980
981 // Load the value.
982 __ ldrh(out, Address(array_temp, idx, LSL, 1)); // out := array_temp[idx].
983
984 __ Bind(slow_path->GetExitLabel());
985 }
986
VisitStringCompareTo(HInvoke * invoke)987 void IntrinsicLocationsBuilderARM::VisitStringCompareTo(HInvoke* invoke) {
988 // The inputs plus one temp.
989 LocationSummary* locations = new (arena_) LocationSummary(invoke,
990 LocationSummary::kCall,
991 kIntrinsified);
992 InvokeRuntimeCallingConvention calling_convention;
993 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
994 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
995 locations->SetOut(Location::RegisterLocation(R0));
996 }
997
VisitStringCompareTo(HInvoke * invoke)998 void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) {
999 ArmAssembler* assembler = GetAssembler();
1000 LocationSummary* locations = invoke->GetLocations();
1001
1002 // Note that the null check must have been done earlier.
1003 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1004
1005 Register argument = locations->InAt(1).AsRegister<Register>();
1006 __ cmp(argument, ShifterOperand(0));
1007 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
1008 codegen_->AddSlowPath(slow_path);
1009 __ b(slow_path->GetEntryLabel(), EQ);
1010
1011 __ LoadFromOffset(
1012 kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pStringCompareTo).Int32Value());
1013 __ blx(LR);
1014 __ Bind(slow_path->GetExitLabel());
1015 }
1016
VisitStringEquals(HInvoke * invoke)1017 void IntrinsicLocationsBuilderARM::VisitStringEquals(HInvoke* invoke) {
1018 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1019 LocationSummary::kNoCall,
1020 kIntrinsified);
1021 InvokeRuntimeCallingConvention calling_convention;
1022 locations->SetInAt(0, Location::RequiresRegister());
1023 locations->SetInAt(1, Location::RequiresRegister());
1024 // Temporary registers to store lengths of strings and for calculations.
1025 // Using instruction cbz requires a low register, so explicitly set a temp to be R0.
1026 locations->AddTemp(Location::RegisterLocation(R0));
1027 locations->AddTemp(Location::RequiresRegister());
1028 locations->AddTemp(Location::RequiresRegister());
1029
1030 locations->SetOut(Location::RequiresRegister());
1031 }
1032
VisitStringEquals(HInvoke * invoke)1033 void IntrinsicCodeGeneratorARM::VisitStringEquals(HInvoke* invoke) {
1034 ArmAssembler* assembler = GetAssembler();
1035 LocationSummary* locations = invoke->GetLocations();
1036
1037 Register str = locations->InAt(0).AsRegister<Register>();
1038 Register arg = locations->InAt(1).AsRegister<Register>();
1039 Register out = locations->Out().AsRegister<Register>();
1040
1041 Register temp = locations->GetTemp(0).AsRegister<Register>();
1042 Register temp1 = locations->GetTemp(1).AsRegister<Register>();
1043 Register temp2 = locations->GetTemp(2).AsRegister<Register>();
1044
1045 Label loop;
1046 Label end;
1047 Label return_true;
1048 Label return_false;
1049
1050 // Get offsets of count, value, and class fields within a string object.
1051 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
1052 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1053 const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
1054
1055 // Note that the null check must have been done earlier.
1056 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1057
1058 // Check if input is null, return false if it is.
1059 __ CompareAndBranchIfZero(arg, &return_false);
1060
1061 // Instanceof check for the argument by comparing class fields.
1062 // All string objects must have the same type since String cannot be subclassed.
1063 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1064 // If the argument is a string object, its class field must be equal to receiver's class field.
1065 __ ldr(temp, Address(str, class_offset));
1066 __ ldr(temp1, Address(arg, class_offset));
1067 __ cmp(temp, ShifterOperand(temp1));
1068 __ b(&return_false, NE);
1069
1070 // Load lengths of this and argument strings.
1071 __ ldr(temp, Address(str, count_offset));
1072 __ ldr(temp1, Address(arg, count_offset));
1073 // Check if lengths are equal, return false if they're not.
1074 __ cmp(temp, ShifterOperand(temp1));
1075 __ b(&return_false, NE);
1076 // Return true if both strings are empty.
1077 __ cbz(temp, &return_true);
1078
1079 // Reference equality check, return true if same reference.
1080 __ cmp(str, ShifterOperand(arg));
1081 __ b(&return_true, EQ);
1082
1083 // Assertions that must hold in order to compare strings 2 characters at a time.
1084 DCHECK_ALIGNED(value_offset, 4);
1085 static_assert(IsAligned<4>(kObjectAlignment), "String of odd length is not zero padded");
1086
1087 __ LoadImmediate(temp1, value_offset);
1088
1089 // Loop to compare strings 2 characters at a time starting at the front of the string.
1090 // Ok to do this because strings with an odd length are zero-padded.
1091 __ Bind(&loop);
1092 __ ldr(out, Address(str, temp1));
1093 __ ldr(temp2, Address(arg, temp1));
1094 __ cmp(out, ShifterOperand(temp2));
1095 __ b(&return_false, NE);
1096 __ add(temp1, temp1, ShifterOperand(sizeof(uint32_t)));
1097 __ subs(temp, temp, ShifterOperand(sizeof(uint32_t) / sizeof(uint16_t)));
1098 __ b(&loop, GT);
1099
1100 // Return true and exit the function.
1101 // If loop does not result in returning false, we return true.
1102 __ Bind(&return_true);
1103 __ LoadImmediate(out, 1);
1104 __ b(&end);
1105
1106 // Return false and exit the function.
1107 __ Bind(&return_false);
1108 __ LoadImmediate(out, 0);
1109 __ Bind(&end);
1110 }
1111
GenerateVisitStringIndexOf(HInvoke * invoke,ArmAssembler * assembler,CodeGeneratorARM * codegen,ArenaAllocator * allocator,bool start_at_zero)1112 static void GenerateVisitStringIndexOf(HInvoke* invoke,
1113 ArmAssembler* assembler,
1114 CodeGeneratorARM* codegen,
1115 ArenaAllocator* allocator,
1116 bool start_at_zero) {
1117 LocationSummary* locations = invoke->GetLocations();
1118 Register tmp_reg = locations->GetTemp(0).AsRegister<Register>();
1119
1120 // Note that the null check must have been done earlier.
1121 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1122
1123 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
1124 // or directly dispatch if we have a constant.
1125 SlowPathCode* slow_path = nullptr;
1126 if (invoke->InputAt(1)->IsIntConstant()) {
1127 if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) >
1128 std::numeric_limits<uint16_t>::max()) {
1129 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1130 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1131 slow_path = new (allocator) IntrinsicSlowPathARM(invoke);
1132 codegen->AddSlowPath(slow_path);
1133 __ b(slow_path->GetEntryLabel());
1134 __ Bind(slow_path->GetExitLabel());
1135 return;
1136 }
1137 } else {
1138 Register char_reg = locations->InAt(1).AsRegister<Register>();
1139 __ LoadImmediate(tmp_reg, std::numeric_limits<uint16_t>::max());
1140 __ cmp(char_reg, ShifterOperand(tmp_reg));
1141 slow_path = new (allocator) IntrinsicSlowPathARM(invoke);
1142 codegen->AddSlowPath(slow_path);
1143 __ b(slow_path->GetEntryLabel(), HI);
1144 }
1145
1146 if (start_at_zero) {
1147 DCHECK_EQ(tmp_reg, R2);
1148 // Start-index = 0.
1149 __ LoadImmediate(tmp_reg, 0);
1150 }
1151
1152 __ LoadFromOffset(kLoadWord, LR, TR,
1153 QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pIndexOf).Int32Value());
1154 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
1155 __ blx(LR);
1156
1157 if (slow_path != nullptr) {
1158 __ Bind(slow_path->GetExitLabel());
1159 }
1160 }
1161
VisitStringIndexOf(HInvoke * invoke)1162 void IntrinsicLocationsBuilderARM::VisitStringIndexOf(HInvoke* invoke) {
1163 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1164 LocationSummary::kCall,
1165 kIntrinsified);
1166 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1167 // best to align the inputs accordingly.
1168 InvokeRuntimeCallingConvention calling_convention;
1169 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1170 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1171 locations->SetOut(Location::RegisterLocation(R0));
1172
1173 // Need a temp for slow-path codepoint compare, and need to send start-index=0.
1174 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1175 }
1176
VisitStringIndexOf(HInvoke * invoke)1177 void IntrinsicCodeGeneratorARM::VisitStringIndexOf(HInvoke* invoke) {
1178 GenerateVisitStringIndexOf(
1179 invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
1180 }
1181
VisitStringIndexOfAfter(HInvoke * invoke)1182 void IntrinsicLocationsBuilderARM::VisitStringIndexOfAfter(HInvoke* invoke) {
1183 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1184 LocationSummary::kCall,
1185 kIntrinsified);
1186 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1187 // best to align the inputs accordingly.
1188 InvokeRuntimeCallingConvention calling_convention;
1189 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1190 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1191 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1192 locations->SetOut(Location::RegisterLocation(R0));
1193
1194 // Need a temp for slow-path codepoint compare.
1195 locations->AddTemp(Location::RequiresRegister());
1196 }
1197
VisitStringIndexOfAfter(HInvoke * invoke)1198 void IntrinsicCodeGeneratorARM::VisitStringIndexOfAfter(HInvoke* invoke) {
1199 GenerateVisitStringIndexOf(
1200 invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
1201 }
1202
VisitStringNewStringFromBytes(HInvoke * invoke)1203 void IntrinsicLocationsBuilderARM::VisitStringNewStringFromBytes(HInvoke* invoke) {
1204 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1205 LocationSummary::kCall,
1206 kIntrinsified);
1207 InvokeRuntimeCallingConvention calling_convention;
1208 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1209 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1210 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1211 locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
1212 locations->SetOut(Location::RegisterLocation(R0));
1213 }
1214
VisitStringNewStringFromBytes(HInvoke * invoke)1215 void IntrinsicCodeGeneratorARM::VisitStringNewStringFromBytes(HInvoke* invoke) {
1216 ArmAssembler* assembler = GetAssembler();
1217 LocationSummary* locations = invoke->GetLocations();
1218
1219 Register byte_array = locations->InAt(0).AsRegister<Register>();
1220 __ cmp(byte_array, ShifterOperand(0));
1221 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
1222 codegen_->AddSlowPath(slow_path);
1223 __ b(slow_path->GetEntryLabel(), EQ);
1224
1225 __ LoadFromOffset(
1226 kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromBytes).Int32Value());
1227 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
1228 __ blx(LR);
1229 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1230 __ Bind(slow_path->GetExitLabel());
1231 }
1232
VisitStringNewStringFromChars(HInvoke * invoke)1233 void IntrinsicLocationsBuilderARM::VisitStringNewStringFromChars(HInvoke* invoke) {
1234 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1235 LocationSummary::kCall,
1236 kIntrinsified);
1237 InvokeRuntimeCallingConvention calling_convention;
1238 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1239 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1240 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1241 locations->SetOut(Location::RegisterLocation(R0));
1242 }
1243
VisitStringNewStringFromChars(HInvoke * invoke)1244 void IntrinsicCodeGeneratorARM::VisitStringNewStringFromChars(HInvoke* invoke) {
1245 ArmAssembler* assembler = GetAssembler();
1246
1247 // No need to emit code checking whether `locations->InAt(2)` is a null
1248 // pointer, as callers of the native method
1249 //
1250 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1251 //
1252 // all include a null check on `data` before calling that method.
1253 __ LoadFromOffset(
1254 kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromChars).Int32Value());
1255 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
1256 __ blx(LR);
1257 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1258 }
1259
VisitStringNewStringFromString(HInvoke * invoke)1260 void IntrinsicLocationsBuilderARM::VisitStringNewStringFromString(HInvoke* invoke) {
1261 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1262 LocationSummary::kCall,
1263 kIntrinsified);
1264 InvokeRuntimeCallingConvention calling_convention;
1265 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1266 locations->SetOut(Location::RegisterLocation(R0));
1267 }
1268
VisitStringNewStringFromString(HInvoke * invoke)1269 void IntrinsicCodeGeneratorARM::VisitStringNewStringFromString(HInvoke* invoke) {
1270 ArmAssembler* assembler = GetAssembler();
1271 LocationSummary* locations = invoke->GetLocations();
1272
1273 Register string_to_copy = locations->InAt(0).AsRegister<Register>();
1274 __ cmp(string_to_copy, ShifterOperand(0));
1275 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
1276 codegen_->AddSlowPath(slow_path);
1277 __ b(slow_path->GetEntryLabel(), EQ);
1278
1279 __ LoadFromOffset(kLoadWord,
1280 LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromString).Int32Value());
1281 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
1282 __ blx(LR);
1283 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1284 __ Bind(slow_path->GetExitLabel());
1285 }
1286
VisitSystemArrayCopy(HInvoke * invoke)1287 void IntrinsicLocationsBuilderARM::VisitSystemArrayCopy(HInvoke* invoke) {
1288 CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
1289 LocationSummary* locations = invoke->GetLocations();
1290 if (locations == nullptr) {
1291 return;
1292 }
1293
1294 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1295 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
1296 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1297
1298 if (src_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(src_pos->GetValue())) {
1299 locations->SetInAt(1, Location::RequiresRegister());
1300 }
1301 if (dest_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(dest_pos->GetValue())) {
1302 locations->SetInAt(3, Location::RequiresRegister());
1303 }
1304 if (length != nullptr && !assembler_->ShifterOperandCanAlwaysHold(length->GetValue())) {
1305 locations->SetInAt(4, Location::RequiresRegister());
1306 }
1307 }
1308
CheckPosition(ArmAssembler * assembler,Location pos,Register input,Location length,SlowPathCode * slow_path,Register input_len,Register temp,bool length_is_input_length=false)1309 static void CheckPosition(ArmAssembler* assembler,
1310 Location pos,
1311 Register input,
1312 Location length,
1313 SlowPathCode* slow_path,
1314 Register input_len,
1315 Register temp,
1316 bool length_is_input_length = false) {
1317 // Where is the length in the Array?
1318 const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
1319
1320 if (pos.IsConstant()) {
1321 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
1322 if (pos_const == 0) {
1323 if (!length_is_input_length) {
1324 // Check that length(input) >= length.
1325 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
1326 if (length.IsConstant()) {
1327 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1328 } else {
1329 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1330 }
1331 __ b(slow_path->GetEntryLabel(), LT);
1332 }
1333 } else {
1334 // Check that length(input) >= pos.
1335 __ LoadFromOffset(kLoadWord, input_len, input, length_offset);
1336 __ subs(temp, input_len, ShifterOperand(pos_const));
1337 __ b(slow_path->GetEntryLabel(), LT);
1338
1339 // Check that (length(input) - pos) >= length.
1340 if (length.IsConstant()) {
1341 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1342 } else {
1343 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1344 }
1345 __ b(slow_path->GetEntryLabel(), LT);
1346 }
1347 } else if (length_is_input_length) {
1348 // The only way the copy can succeed is if pos is zero.
1349 Register pos_reg = pos.AsRegister<Register>();
1350 __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
1351 } else {
1352 // Check that pos >= 0.
1353 Register pos_reg = pos.AsRegister<Register>();
1354 __ cmp(pos_reg, ShifterOperand(0));
1355 __ b(slow_path->GetEntryLabel(), LT);
1356
1357 // Check that pos <= length(input).
1358 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
1359 __ subs(temp, temp, ShifterOperand(pos_reg));
1360 __ b(slow_path->GetEntryLabel(), LT);
1361
1362 // Check that (length(input) - pos) >= length.
1363 if (length.IsConstant()) {
1364 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1365 } else {
1366 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1367 }
1368 __ b(slow_path->GetEntryLabel(), LT);
1369 }
1370 }
1371
1372 // TODO: Implement read barriers in the SystemArrayCopy intrinsic.
1373 // Note that this code path is not used (yet) because we do not
1374 // intrinsify methods that can go into the IntrinsicSlowPathARM
1375 // slow path.
VisitSystemArrayCopy(HInvoke * invoke)1376 void IntrinsicCodeGeneratorARM::VisitSystemArrayCopy(HInvoke* invoke) {
1377 ArmAssembler* assembler = GetAssembler();
1378 LocationSummary* locations = invoke->GetLocations();
1379
1380 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1381 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
1382 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
1383 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
1384
1385 Register src = locations->InAt(0).AsRegister<Register>();
1386 Location src_pos = locations->InAt(1);
1387 Register dest = locations->InAt(2).AsRegister<Register>();
1388 Location dest_pos = locations->InAt(3);
1389 Location length = locations->InAt(4);
1390 Register temp1 = locations->GetTemp(0).AsRegister<Register>();
1391 Register temp2 = locations->GetTemp(1).AsRegister<Register>();
1392 Register temp3 = locations->GetTemp(2).AsRegister<Register>();
1393
1394 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
1395 codegen_->AddSlowPath(slow_path);
1396
1397 Label conditions_on_positions_validated;
1398 SystemArrayCopyOptimizations optimizations(invoke);
1399
1400 if (!optimizations.GetDestinationIsSource() &&
1401 (!src_pos.IsConstant() || !dest_pos.IsConstant())) {
1402 __ cmp(src, ShifterOperand(dest));
1403 }
1404 // If source and destination are the same, we go to slow path if we need to do
1405 // forward copying.
1406 if (src_pos.IsConstant()) {
1407 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
1408 if (dest_pos.IsConstant()) {
1409 // Checked when building locations.
1410 DCHECK(!optimizations.GetDestinationIsSource()
1411 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
1412 } else {
1413 if (!optimizations.GetDestinationIsSource()) {
1414 __ b(&conditions_on_positions_validated, NE);
1415 }
1416 __ cmp(dest_pos.AsRegister<Register>(), ShifterOperand(src_pos_constant));
1417 __ b(slow_path->GetEntryLabel(), GT);
1418 }
1419 } else {
1420 if (!optimizations.GetDestinationIsSource()) {
1421 __ b(&conditions_on_positions_validated, NE);
1422 }
1423 if (dest_pos.IsConstant()) {
1424 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
1425 __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos_constant));
1426 } else {
1427 __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos.AsRegister<Register>()));
1428 }
1429 __ b(slow_path->GetEntryLabel(), LT);
1430 }
1431
1432 __ Bind(&conditions_on_positions_validated);
1433
1434 if (!optimizations.GetSourceIsNotNull()) {
1435 // Bail out if the source is null.
1436 __ CompareAndBranchIfZero(src, slow_path->GetEntryLabel());
1437 }
1438
1439 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
1440 // Bail out if the destination is null.
1441 __ CompareAndBranchIfZero(dest, slow_path->GetEntryLabel());
1442 }
1443
1444 // If the length is negative, bail out.
1445 // We have already checked in the LocationsBuilder for the constant case.
1446 if (!length.IsConstant() &&
1447 !optimizations.GetCountIsSourceLength() &&
1448 !optimizations.GetCountIsDestinationLength()) {
1449 __ cmp(length.AsRegister<Register>(), ShifterOperand(0));
1450 __ b(slow_path->GetEntryLabel(), LT);
1451 }
1452
1453 // Validity checks: source.
1454 CheckPosition(assembler,
1455 src_pos,
1456 src,
1457 length,
1458 slow_path,
1459 temp1,
1460 temp2,
1461 optimizations.GetCountIsSourceLength());
1462
1463 // Validity checks: dest.
1464 CheckPosition(assembler,
1465 dest_pos,
1466 dest,
1467 length,
1468 slow_path,
1469 temp1,
1470 temp2,
1471 optimizations.GetCountIsDestinationLength());
1472
1473 if (!optimizations.GetDoesNotNeedTypeCheck()) {
1474 // Check whether all elements of the source array are assignable to the component
1475 // type of the destination array. We do two checks: the classes are the same,
1476 // or the destination is Object[]. If none of these checks succeed, we go to the
1477 // slow path.
1478 __ LoadFromOffset(kLoadWord, temp1, dest, class_offset);
1479 __ LoadFromOffset(kLoadWord, temp2, src, class_offset);
1480 bool did_unpoison = false;
1481 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
1482 !optimizations.GetSourceIsNonPrimitiveArray()) {
1483 // One or two of the references need to be unpoisoned. Unpoison them
1484 // both to make the identity check valid.
1485 __ MaybeUnpoisonHeapReference(temp1);
1486 __ MaybeUnpoisonHeapReference(temp2);
1487 did_unpoison = true;
1488 }
1489
1490 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
1491 // Bail out if the destination is not a non primitive array.
1492 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1493 __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
1494 __ CompareAndBranchIfZero(temp3, slow_path->GetEntryLabel());
1495 __ MaybeUnpoisonHeapReference(temp3);
1496 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1497 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1498 __ CompareAndBranchIfNonZero(temp3, slow_path->GetEntryLabel());
1499 }
1500
1501 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1502 // Bail out if the source is not a non primitive array.
1503 // /* HeapReference<Class> */ temp3 = temp2->component_type_
1504 __ LoadFromOffset(kLoadWord, temp3, temp2, component_offset);
1505 __ CompareAndBranchIfZero(temp3, slow_path->GetEntryLabel());
1506 __ MaybeUnpoisonHeapReference(temp3);
1507 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1508 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1509 __ CompareAndBranchIfNonZero(temp3, slow_path->GetEntryLabel());
1510 }
1511
1512 __ cmp(temp1, ShifterOperand(temp2));
1513
1514 if (optimizations.GetDestinationIsTypedObjectArray()) {
1515 Label do_copy;
1516 __ b(&do_copy, EQ);
1517 if (!did_unpoison) {
1518 __ MaybeUnpoisonHeapReference(temp1);
1519 }
1520 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1521 __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
1522 __ MaybeUnpoisonHeapReference(temp1);
1523 // /* HeapReference<Class> */ temp1 = temp1->super_class_
1524 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
1525 // No need to unpoison the result, we're comparing against null.
1526 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
1527 __ Bind(&do_copy);
1528 } else {
1529 __ b(slow_path->GetEntryLabel(), NE);
1530 }
1531 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1532 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
1533 // Bail out if the source is not a non primitive array.
1534 // /* HeapReference<Class> */ temp1 = src->klass_
1535 __ LoadFromOffset(kLoadWord, temp1, src, class_offset);
1536 __ MaybeUnpoisonHeapReference(temp1);
1537 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1538 __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
1539 __ CompareAndBranchIfZero(temp3, slow_path->GetEntryLabel());
1540 __ MaybeUnpoisonHeapReference(temp3);
1541 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1542 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1543 __ CompareAndBranchIfNonZero(temp3, slow_path->GetEntryLabel());
1544 }
1545
1546 // Compute base source address, base destination address, and end source address.
1547
1548 uint32_t element_size = sizeof(int32_t);
1549 uint32_t offset = mirror::Array::DataOffset(element_size).Uint32Value();
1550 if (src_pos.IsConstant()) {
1551 int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
1552 __ AddConstant(temp1, src, element_size * constant + offset);
1553 } else {
1554 __ add(temp1, src, ShifterOperand(src_pos.AsRegister<Register>(), LSL, 2));
1555 __ AddConstant(temp1, offset);
1556 }
1557
1558 if (dest_pos.IsConstant()) {
1559 int32_t constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
1560 __ AddConstant(temp2, dest, element_size * constant + offset);
1561 } else {
1562 __ add(temp2, dest, ShifterOperand(dest_pos.AsRegister<Register>(), LSL, 2));
1563 __ AddConstant(temp2, offset);
1564 }
1565
1566 if (length.IsConstant()) {
1567 int32_t constant = length.GetConstant()->AsIntConstant()->GetValue();
1568 __ AddConstant(temp3, temp1, element_size * constant);
1569 } else {
1570 __ add(temp3, temp1, ShifterOperand(length.AsRegister<Register>(), LSL, 2));
1571 }
1572
1573 // Iterate over the arrays and do a raw copy of the objects. We don't need to
1574 // poison/unpoison, nor do any read barrier as the next uses of the destination
1575 // array will do it.
1576 Label loop, done;
1577 __ cmp(temp1, ShifterOperand(temp3));
1578 __ b(&done, EQ);
1579 __ Bind(&loop);
1580 __ ldr(IP, Address(temp1, element_size, Address::PostIndex));
1581 __ str(IP, Address(temp2, element_size, Address::PostIndex));
1582 __ cmp(temp1, ShifterOperand(temp3));
1583 __ b(&loop, NE);
1584 __ Bind(&done);
1585
1586 // We only need one card marking on the destination array.
1587 codegen_->MarkGCCard(temp1,
1588 temp2,
1589 dest,
1590 Register(kNoRegister),
1591 /* value_can_be_null */ false);
1592
1593 __ Bind(slow_path->GetExitLabel());
1594 }
1595
CreateFPToFPCallLocations(ArenaAllocator * arena,HInvoke * invoke)1596 static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1597 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
1598 // the code generator. Furthermore, the register allocator creates fixed live intervals
1599 // for all caller-saved registers because we are doing a function call. As a result, if
1600 // the input and output locations are unallocated, the register allocator runs out of
1601 // registers and fails; however, a debuggable graph is not the common case.
1602 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
1603 return;
1604 }
1605
1606 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
1607 DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
1608 DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
1609
1610 LocationSummary* const locations = new (arena) LocationSummary(invoke,
1611 LocationSummary::kCall,
1612 kIntrinsified);
1613 const InvokeRuntimeCallingConvention calling_convention;
1614
1615 locations->SetInAt(0, Location::RequiresFpuRegister());
1616 locations->SetOut(Location::RequiresFpuRegister());
1617 // Native code uses the soft float ABI.
1618 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1619 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1620 }
1621
CreateFPFPToFPCallLocations(ArenaAllocator * arena,HInvoke * invoke)1622 static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1623 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
1624 // the code generator. Furthermore, the register allocator creates fixed live intervals
1625 // for all caller-saved registers because we are doing a function call. As a result, if
1626 // the input and output locations are unallocated, the register allocator runs out of
1627 // registers and fails; however, a debuggable graph is not the common case.
1628 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
1629 return;
1630 }
1631
1632 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
1633 DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
1634 DCHECK_EQ(invoke->InputAt(1)->GetType(), Primitive::kPrimDouble);
1635 DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
1636
1637 LocationSummary* const locations = new (arena) LocationSummary(invoke,
1638 LocationSummary::kCall,
1639 kIntrinsified);
1640 const InvokeRuntimeCallingConvention calling_convention;
1641
1642 locations->SetInAt(0, Location::RequiresFpuRegister());
1643 locations->SetInAt(1, Location::RequiresFpuRegister());
1644 locations->SetOut(Location::RequiresFpuRegister());
1645 // Native code uses the soft float ABI.
1646 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1647 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1648 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1649 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
1650 }
1651
GenFPToFPCall(HInvoke * invoke,ArmAssembler * assembler,CodeGeneratorARM * codegen,QuickEntrypointEnum entry)1652 static void GenFPToFPCall(HInvoke* invoke,
1653 ArmAssembler* assembler,
1654 CodeGeneratorARM* codegen,
1655 QuickEntrypointEnum entry) {
1656 LocationSummary* const locations = invoke->GetLocations();
1657 const InvokeRuntimeCallingConvention calling_convention;
1658
1659 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
1660 DCHECK(locations->WillCall() && locations->Intrinsified());
1661 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
1662 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
1663
1664 __ LoadFromOffset(kLoadWord, LR, TR, GetThreadOffset<kArmWordSize>(entry).Int32Value());
1665 // Native code uses the soft float ABI.
1666 __ vmovrrd(calling_convention.GetRegisterAt(0),
1667 calling_convention.GetRegisterAt(1),
1668 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
1669 __ blx(LR);
1670 codegen->RecordPcInfo(invoke, invoke->GetDexPc());
1671 __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
1672 calling_convention.GetRegisterAt(0),
1673 calling_convention.GetRegisterAt(1));
1674 }
1675
GenFPFPToFPCall(HInvoke * invoke,ArmAssembler * assembler,CodeGeneratorARM * codegen,QuickEntrypointEnum entry)1676 static void GenFPFPToFPCall(HInvoke* invoke,
1677 ArmAssembler* assembler,
1678 CodeGeneratorARM* codegen,
1679 QuickEntrypointEnum entry) {
1680 LocationSummary* const locations = invoke->GetLocations();
1681 const InvokeRuntimeCallingConvention calling_convention;
1682
1683 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
1684 DCHECK(locations->WillCall() && locations->Intrinsified());
1685 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
1686 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
1687 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(2)));
1688 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(3)));
1689
1690 __ LoadFromOffset(kLoadWord, LR, TR, GetThreadOffset<kArmWordSize>(entry).Int32Value());
1691 // Native code uses the soft float ABI.
1692 __ vmovrrd(calling_convention.GetRegisterAt(0),
1693 calling_convention.GetRegisterAt(1),
1694 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
1695 __ vmovrrd(calling_convention.GetRegisterAt(2),
1696 calling_convention.GetRegisterAt(3),
1697 FromLowSToD(locations->InAt(1).AsFpuRegisterPairLow<SRegister>()));
1698 __ blx(LR);
1699 codegen->RecordPcInfo(invoke, invoke->GetDexPc());
1700 __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
1701 calling_convention.GetRegisterAt(0),
1702 calling_convention.GetRegisterAt(1));
1703 }
1704
VisitMathCos(HInvoke * invoke)1705 void IntrinsicLocationsBuilderARM::VisitMathCos(HInvoke* invoke) {
1706 CreateFPToFPCallLocations(arena_, invoke);
1707 }
1708
VisitMathCos(HInvoke * invoke)1709 void IntrinsicCodeGeneratorARM::VisitMathCos(HInvoke* invoke) {
1710 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCos);
1711 }
1712
VisitMathSin(HInvoke * invoke)1713 void IntrinsicLocationsBuilderARM::VisitMathSin(HInvoke* invoke) {
1714 CreateFPToFPCallLocations(arena_, invoke);
1715 }
1716
VisitMathSin(HInvoke * invoke)1717 void IntrinsicCodeGeneratorARM::VisitMathSin(HInvoke* invoke) {
1718 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSin);
1719 }
1720
VisitMathAcos(HInvoke * invoke)1721 void IntrinsicLocationsBuilderARM::VisitMathAcos(HInvoke* invoke) {
1722 CreateFPToFPCallLocations(arena_, invoke);
1723 }
1724
VisitMathAcos(HInvoke * invoke)1725 void IntrinsicCodeGeneratorARM::VisitMathAcos(HInvoke* invoke) {
1726 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAcos);
1727 }
1728
VisitMathAsin(HInvoke * invoke)1729 void IntrinsicLocationsBuilderARM::VisitMathAsin(HInvoke* invoke) {
1730 CreateFPToFPCallLocations(arena_, invoke);
1731 }
1732
VisitMathAsin(HInvoke * invoke)1733 void IntrinsicCodeGeneratorARM::VisitMathAsin(HInvoke* invoke) {
1734 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAsin);
1735 }
1736
VisitMathAtan(HInvoke * invoke)1737 void IntrinsicLocationsBuilderARM::VisitMathAtan(HInvoke* invoke) {
1738 CreateFPToFPCallLocations(arena_, invoke);
1739 }
1740
VisitMathAtan(HInvoke * invoke)1741 void IntrinsicCodeGeneratorARM::VisitMathAtan(HInvoke* invoke) {
1742 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan);
1743 }
1744
VisitMathCbrt(HInvoke * invoke)1745 void IntrinsicLocationsBuilderARM::VisitMathCbrt(HInvoke* invoke) {
1746 CreateFPToFPCallLocations(arena_, invoke);
1747 }
1748
VisitMathCbrt(HInvoke * invoke)1749 void IntrinsicCodeGeneratorARM::VisitMathCbrt(HInvoke* invoke) {
1750 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCbrt);
1751 }
1752
VisitMathCosh(HInvoke * invoke)1753 void IntrinsicLocationsBuilderARM::VisitMathCosh(HInvoke* invoke) {
1754 CreateFPToFPCallLocations(arena_, invoke);
1755 }
1756
VisitMathCosh(HInvoke * invoke)1757 void IntrinsicCodeGeneratorARM::VisitMathCosh(HInvoke* invoke) {
1758 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCosh);
1759 }
1760
VisitMathExp(HInvoke * invoke)1761 void IntrinsicLocationsBuilderARM::VisitMathExp(HInvoke* invoke) {
1762 CreateFPToFPCallLocations(arena_, invoke);
1763 }
1764
VisitMathExp(HInvoke * invoke)1765 void IntrinsicCodeGeneratorARM::VisitMathExp(HInvoke* invoke) {
1766 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExp);
1767 }
1768
VisitMathExpm1(HInvoke * invoke)1769 void IntrinsicLocationsBuilderARM::VisitMathExpm1(HInvoke* invoke) {
1770 CreateFPToFPCallLocations(arena_, invoke);
1771 }
1772
VisitMathExpm1(HInvoke * invoke)1773 void IntrinsicCodeGeneratorARM::VisitMathExpm1(HInvoke* invoke) {
1774 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExpm1);
1775 }
1776
VisitMathLog(HInvoke * invoke)1777 void IntrinsicLocationsBuilderARM::VisitMathLog(HInvoke* invoke) {
1778 CreateFPToFPCallLocations(arena_, invoke);
1779 }
1780
VisitMathLog(HInvoke * invoke)1781 void IntrinsicCodeGeneratorARM::VisitMathLog(HInvoke* invoke) {
1782 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog);
1783 }
1784
VisitMathLog10(HInvoke * invoke)1785 void IntrinsicLocationsBuilderARM::VisitMathLog10(HInvoke* invoke) {
1786 CreateFPToFPCallLocations(arena_, invoke);
1787 }
1788
VisitMathLog10(HInvoke * invoke)1789 void IntrinsicCodeGeneratorARM::VisitMathLog10(HInvoke* invoke) {
1790 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog10);
1791 }
1792
VisitMathSinh(HInvoke * invoke)1793 void IntrinsicLocationsBuilderARM::VisitMathSinh(HInvoke* invoke) {
1794 CreateFPToFPCallLocations(arena_, invoke);
1795 }
1796
VisitMathSinh(HInvoke * invoke)1797 void IntrinsicCodeGeneratorARM::VisitMathSinh(HInvoke* invoke) {
1798 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSinh);
1799 }
1800
VisitMathTan(HInvoke * invoke)1801 void IntrinsicLocationsBuilderARM::VisitMathTan(HInvoke* invoke) {
1802 CreateFPToFPCallLocations(arena_, invoke);
1803 }
1804
VisitMathTan(HInvoke * invoke)1805 void IntrinsicCodeGeneratorARM::VisitMathTan(HInvoke* invoke) {
1806 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTan);
1807 }
1808
VisitMathTanh(HInvoke * invoke)1809 void IntrinsicLocationsBuilderARM::VisitMathTanh(HInvoke* invoke) {
1810 CreateFPToFPCallLocations(arena_, invoke);
1811 }
1812
VisitMathTanh(HInvoke * invoke)1813 void IntrinsicCodeGeneratorARM::VisitMathTanh(HInvoke* invoke) {
1814 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTanh);
1815 }
1816
VisitMathAtan2(HInvoke * invoke)1817 void IntrinsicLocationsBuilderARM::VisitMathAtan2(HInvoke* invoke) {
1818 CreateFPFPToFPCallLocations(arena_, invoke);
1819 }
1820
VisitMathAtan2(HInvoke * invoke)1821 void IntrinsicCodeGeneratorARM::VisitMathAtan2(HInvoke* invoke) {
1822 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan2);
1823 }
1824
VisitMathHypot(HInvoke * invoke)1825 void IntrinsicLocationsBuilderARM::VisitMathHypot(HInvoke* invoke) {
1826 CreateFPFPToFPCallLocations(arena_, invoke);
1827 }
1828
VisitMathHypot(HInvoke * invoke)1829 void IntrinsicCodeGeneratorARM::VisitMathHypot(HInvoke* invoke) {
1830 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickHypot);
1831 }
1832
VisitMathNextAfter(HInvoke * invoke)1833 void IntrinsicLocationsBuilderARM::VisitMathNextAfter(HInvoke* invoke) {
1834 CreateFPFPToFPCallLocations(arena_, invoke);
1835 }
1836
VisitMathNextAfter(HInvoke * invoke)1837 void IntrinsicCodeGeneratorARM::VisitMathNextAfter(HInvoke* invoke) {
1838 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter);
1839 }
1840
VisitIntegerReverse(HInvoke * invoke)1841 void IntrinsicLocationsBuilderARM::VisitIntegerReverse(HInvoke* invoke) {
1842 CreateIntToIntLocations(arena_, invoke);
1843 }
1844
VisitIntegerReverse(HInvoke * invoke)1845 void IntrinsicCodeGeneratorARM::VisitIntegerReverse(HInvoke* invoke) {
1846 ArmAssembler* assembler = GetAssembler();
1847 LocationSummary* locations = invoke->GetLocations();
1848
1849 Register out = locations->Out().AsRegister<Register>();
1850 Register in = locations->InAt(0).AsRegister<Register>();
1851
1852 __ rbit(out, in);
1853 }
1854
VisitLongReverse(HInvoke * invoke)1855 void IntrinsicLocationsBuilderARM::VisitLongReverse(HInvoke* invoke) {
1856 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1857 LocationSummary::kNoCall,
1858 kIntrinsified);
1859 locations->SetInAt(0, Location::RequiresRegister());
1860 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1861 }
1862
VisitLongReverse(HInvoke * invoke)1863 void IntrinsicCodeGeneratorARM::VisitLongReverse(HInvoke* invoke) {
1864 ArmAssembler* assembler = GetAssembler();
1865 LocationSummary* locations = invoke->GetLocations();
1866
1867 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
1868 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
1869 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
1870 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
1871
1872 __ rbit(out_reg_lo, in_reg_hi);
1873 __ rbit(out_reg_hi, in_reg_lo);
1874 }
1875
VisitIntegerReverseBytes(HInvoke * invoke)1876 void IntrinsicLocationsBuilderARM::VisitIntegerReverseBytes(HInvoke* invoke) {
1877 CreateIntToIntLocations(arena_, invoke);
1878 }
1879
VisitIntegerReverseBytes(HInvoke * invoke)1880 void IntrinsicCodeGeneratorARM::VisitIntegerReverseBytes(HInvoke* invoke) {
1881 ArmAssembler* assembler = GetAssembler();
1882 LocationSummary* locations = invoke->GetLocations();
1883
1884 Register out = locations->Out().AsRegister<Register>();
1885 Register in = locations->InAt(0).AsRegister<Register>();
1886
1887 __ rev(out, in);
1888 }
1889
VisitLongReverseBytes(HInvoke * invoke)1890 void IntrinsicLocationsBuilderARM::VisitLongReverseBytes(HInvoke* invoke) {
1891 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1892 LocationSummary::kNoCall,
1893 kIntrinsified);
1894 locations->SetInAt(0, Location::RequiresRegister());
1895 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1896 }
1897
VisitLongReverseBytes(HInvoke * invoke)1898 void IntrinsicCodeGeneratorARM::VisitLongReverseBytes(HInvoke* invoke) {
1899 ArmAssembler* assembler = GetAssembler();
1900 LocationSummary* locations = invoke->GetLocations();
1901
1902 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
1903 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
1904 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
1905 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
1906
1907 __ rev(out_reg_lo, in_reg_hi);
1908 __ rev(out_reg_hi, in_reg_lo);
1909 }
1910
VisitShortReverseBytes(HInvoke * invoke)1911 void IntrinsicLocationsBuilderARM::VisitShortReverseBytes(HInvoke* invoke) {
1912 CreateIntToIntLocations(arena_, invoke);
1913 }
1914
VisitShortReverseBytes(HInvoke * invoke)1915 void IntrinsicCodeGeneratorARM::VisitShortReverseBytes(HInvoke* invoke) {
1916 ArmAssembler* assembler = GetAssembler();
1917 LocationSummary* locations = invoke->GetLocations();
1918
1919 Register out = locations->Out().AsRegister<Register>();
1920 Register in = locations->InAt(0).AsRegister<Register>();
1921
1922 __ revsh(out, in);
1923 }
1924
VisitStringGetCharsNoCheck(HInvoke * invoke)1925 void IntrinsicLocationsBuilderARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1926 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1927 LocationSummary::kNoCall,
1928 kIntrinsified);
1929 locations->SetInAt(0, Location::RequiresRegister());
1930 locations->SetInAt(1, Location::RequiresRegister());
1931 locations->SetInAt(2, Location::RequiresRegister());
1932 locations->SetInAt(3, Location::RequiresRegister());
1933 locations->SetInAt(4, Location::RequiresRegister());
1934
1935 locations->AddTemp(Location::RequiresRegister());
1936 locations->AddTemp(Location::RequiresRegister());
1937 locations->AddTemp(Location::RequiresRegister());
1938 locations->AddTemp(Location::RequiresRegister());
1939 }
1940
VisitStringGetCharsNoCheck(HInvoke * invoke)1941 void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1942 ArmAssembler* assembler = GetAssembler();
1943 LocationSummary* locations = invoke->GetLocations();
1944
1945 // Check assumption that sizeof(Char) is 2 (used in scaling below).
1946 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1947 DCHECK_EQ(char_size, 2u);
1948
1949 // Location of data in char array buffer.
1950 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
1951
1952 // Location of char array data in string.
1953 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1954
1955 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
1956 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
1957 Register srcObj = locations->InAt(0).AsRegister<Register>();
1958 Register srcBegin = locations->InAt(1).AsRegister<Register>();
1959 Register srcEnd = locations->InAt(2).AsRegister<Register>();
1960 Register dstObj = locations->InAt(3).AsRegister<Register>();
1961 Register dstBegin = locations->InAt(4).AsRegister<Register>();
1962
1963 Register src_ptr = locations->GetTemp(0).AsRegister<Register>();
1964 Register src_ptr_end = locations->GetTemp(1).AsRegister<Register>();
1965 Register dst_ptr = locations->GetTemp(2).AsRegister<Register>();
1966 Register tmp = locations->GetTemp(3).AsRegister<Register>();
1967
1968 // src range to copy.
1969 __ add(src_ptr, srcObj, ShifterOperand(value_offset));
1970 __ add(src_ptr_end, src_ptr, ShifterOperand(srcEnd, LSL, 1));
1971 __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1));
1972
1973 // dst to be copied.
1974 __ add(dst_ptr, dstObj, ShifterOperand(data_offset));
1975 __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1));
1976
1977 // Do the copy.
1978 Label loop, done;
1979 __ Bind(&loop);
1980 __ cmp(src_ptr, ShifterOperand(src_ptr_end));
1981 __ b(&done, EQ);
1982 __ ldrh(tmp, Address(src_ptr, char_size, Address::PostIndex));
1983 __ strh(tmp, Address(dst_ptr, char_size, Address::PostIndex));
1984 __ b(&loop);
1985 __ Bind(&done);
1986 }
1987
1988 UNIMPLEMENTED_INTRINSIC(ARM, IntegerBitCount)
1989 UNIMPLEMENTED_INTRINSIC(ARM, LongBitCount)
1990 UNIMPLEMENTED_INTRINSIC(ARM, MathMinDoubleDouble)
1991 UNIMPLEMENTED_INTRINSIC(ARM, MathMinFloatFloat)
1992 UNIMPLEMENTED_INTRINSIC(ARM, MathMaxDoubleDouble)
1993 UNIMPLEMENTED_INTRINSIC(ARM, MathMaxFloatFloat)
1994 UNIMPLEMENTED_INTRINSIC(ARM, MathMinLongLong)
1995 UNIMPLEMENTED_INTRINSIC(ARM, MathMaxLongLong)
1996 UNIMPLEMENTED_INTRINSIC(ARM, MathCeil) // Could be done by changing rounding mode, maybe?
1997 UNIMPLEMENTED_INTRINSIC(ARM, MathFloor) // Could be done by changing rounding mode, maybe?
1998 UNIMPLEMENTED_INTRINSIC(ARM, MathRint)
1999 UNIMPLEMENTED_INTRINSIC(ARM, MathRoundDouble) // Could be done by changing rounding mode, maybe?
2000 UNIMPLEMENTED_INTRINSIC(ARM, MathRoundFloat) // Could be done by changing rounding mode, maybe?
2001 UNIMPLEMENTED_INTRINSIC(ARM, UnsafeCASLong) // High register pressure.
2002 UNIMPLEMENTED_INTRINSIC(ARM, SystemArrayCopyChar)
2003 UNIMPLEMENTED_INTRINSIC(ARM, ReferenceGetReferent)
2004 UNIMPLEMENTED_INTRINSIC(ARM, FloatIsInfinite)
2005 UNIMPLEMENTED_INTRINSIC(ARM, DoubleIsInfinite)
2006 UNIMPLEMENTED_INTRINSIC(ARM, IntegerHighestOneBit)
2007 UNIMPLEMENTED_INTRINSIC(ARM, LongHighestOneBit)
2008 UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit)
2009 UNIMPLEMENTED_INTRINSIC(ARM, LongLowestOneBit)
2010
2011 // 1.8.
2012 UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddInt)
2013 UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddLong)
2014 UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetInt)
2015 UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetLong)
2016 UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetObject)
2017
2018 UNREACHABLE_INTRINSICS(ARM)
2019
2020 #undef __
2021
2022 } // namespace arm
2023 } // namespace art
2024