blob: a82d80af13c3b5768bb557dc0390e038835cfb9a [file] [log] [blame]
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001/*
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 "code_generator_arm.h"
21#include "entrypoints/quick/quick_entrypoints.h"
22#include "intrinsics.h"
23#include "mirror/array-inl.h"
24#include "mirror/art_method.h"
25#include "mirror/string.h"
26#include "thread.h"
27#include "utils/arm/assembler_arm.h"
28
29namespace art {
30
31namespace arm {
32
33ArmAssembler* IntrinsicCodeGeneratorARM::GetAssembler() {
34 return codegen_->GetAssembler();
35}
36
37ArenaAllocator* IntrinsicCodeGeneratorARM::GetAllocator() {
38 return codegen_->GetGraph()->GetArena();
39}
40
41#define __ codegen->GetAssembler()->
42
43static void MoveFromReturnRegister(Location trg, Primitive::Type type, CodeGeneratorARM* codegen) {
44 if (!trg.IsValid()) {
45 DCHECK(type == Primitive::kPrimVoid);
46 return;
47 }
48
49 DCHECK_NE(type, Primitive::kPrimVoid);
50
51 if (Primitive::IsIntegralType(type)) {
52 if (type == Primitive::kPrimLong) {
53 Register trg_reg_lo = trg.AsRegisterPairLow<Register>();
54 Register trg_reg_hi = trg.AsRegisterPairHigh<Register>();
55 Register res_reg_lo = R0;
56 Register res_reg_hi = R1;
57 if (trg_reg_lo != res_reg_hi) {
58 if (trg_reg_lo != res_reg_lo) {
59 __ mov(trg_reg_lo, ShifterOperand(res_reg_lo));
60 __ mov(trg_reg_hi, ShifterOperand(res_reg_hi));
61 } else {
62 DCHECK_EQ(trg_reg_lo + 1, trg_reg_hi);
63 }
64 } else {
65 __ mov(trg_reg_hi, ShifterOperand(res_reg_hi));
66 __ mov(trg_reg_lo, ShifterOperand(res_reg_lo));
67 }
68 } else {
69 Register trg_reg = trg.AsRegister<Register>();
70 Register res_reg = R0;
71 if (trg_reg != res_reg) {
72 __ mov(trg_reg, ShifterOperand(res_reg));
73 }
74 }
75 } else {
76 UNIMPLEMENTED(FATAL) << "Floating-point return.";
77 }
78}
79
80static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorARM* codegen) {
81 if (invoke->InputCount() == 0) {
82 return;
83 }
84
85 LocationSummary* locations = invoke->GetLocations();
86 InvokeDexCallingConventionVisitor calling_convention_visitor;
87
88 // We're moving potentially two or more locations to locations that could overlap, so we need
89 // a parallel move resolver.
90 HParallelMove parallel_move(arena);
91
92 for (size_t i = 0; i < invoke->InputCount(); i++) {
93 HInstruction* input = invoke->InputAt(i);
94 Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType());
95 Location actual_loc = locations->InAt(i);
96
97 parallel_move.AddMove(actual_loc, cc_loc, nullptr);
98 }
99
100 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
101}
102
103// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
104// call. This will copy the arguments into the positions for a regular call.
105//
106// Note: The actual parameters are required to be in the locations given by the invoke's location
107// summary. If an intrinsic modifies those locations before a slowpath call, they must be
108// restored!
109class IntrinsicSlowPathARM : public SlowPathCodeARM {
110 public:
111 explicit IntrinsicSlowPathARM(HInvoke* invoke) : invoke_(invoke) { }
112
113 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
114 CodeGeneratorARM* codegen = down_cast<CodeGeneratorARM*>(codegen_in);
115 __ Bind(GetEntryLabel());
116
117 codegen->SaveLiveRegisters(invoke_->GetLocations());
118
119 MoveArguments(invoke_, codegen->GetGraph()->GetArena(), codegen);
120
121 if (invoke_->IsInvokeStaticOrDirect()) {
122 codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), kArtMethodRegister);
123 } else {
124 UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
125 UNREACHABLE();
126 }
127
128 // Copy the result back to the expected output.
129 Location out = invoke_->GetLocations()->Out();
130 if (out.IsValid()) {
131 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
132 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
133 MoveFromReturnRegister(out, invoke_->GetType(), codegen);
134 }
135
136 codegen->RestoreLiveRegisters(invoke_->GetLocations());
137 __ b(GetExitLabel());
138 }
139
140 private:
141 // The instruction where this slow path is happening.
142 HInvoke* const invoke_;
143
144 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM);
145};
146
147#undef __
148
149bool IntrinsicLocationsBuilderARM::TryDispatch(HInvoke* invoke) {
150 Dispatch(invoke);
151 LocationSummary* res = invoke->GetLocations();
152 return res != nullptr && res->Intrinsified();
153}
154
155#define __ assembler->
156
157static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
158 LocationSummary* locations = new (arena) LocationSummary(invoke,
159 LocationSummary::kNoCall,
160 kIntrinsified);
161 locations->SetInAt(0, Location::RequiresFpuRegister());
162 locations->SetOut(Location::RequiresRegister());
163}
164
165static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
166 LocationSummary* locations = new (arena) LocationSummary(invoke,
167 LocationSummary::kNoCall,
168 kIntrinsified);
169 locations->SetInAt(0, Location::RequiresRegister());
170 locations->SetOut(Location::RequiresFpuRegister());
171}
172
173static void MoveFPToInt(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
174 Location input = locations->InAt(0);
175 Location output = locations->Out();
176 if (is64bit) {
177 __ vmovrrd(output.AsRegisterPairLow<Register>(),
178 output.AsRegisterPairHigh<Register>(),
179 FromLowSToD(input.AsFpuRegisterPairLow<SRegister>()));
180 } else {
181 __ vmovrs(output.AsRegister<Register>(), input.AsFpuRegister<SRegister>());
182 }
183}
184
185static void MoveIntToFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
186 Location input = locations->InAt(0);
187 Location output = locations->Out();
188 if (is64bit) {
189 __ vmovdrr(FromLowSToD(output.AsFpuRegisterPairLow<SRegister>()),
190 input.AsRegisterPairLow<Register>(),
191 input.AsRegisterPairHigh<Register>());
192 } else {
193 __ vmovsr(output.AsFpuRegister<SRegister>(), input.AsRegister<Register>());
194 }
195}
196
197void IntrinsicLocationsBuilderARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
198 CreateFPToIntLocations(arena_, invoke);
199}
200void IntrinsicLocationsBuilderARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
201 CreateIntToFPLocations(arena_, invoke);
202}
203
204void IntrinsicCodeGeneratorARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
205 MoveFPToInt(invoke->GetLocations(), true, GetAssembler());
206}
207void IntrinsicCodeGeneratorARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
208 MoveIntToFP(invoke->GetLocations(), true, GetAssembler());
209}
210
211void IntrinsicLocationsBuilderARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
212 CreateFPToIntLocations(arena_, invoke);
213}
214void IntrinsicLocationsBuilderARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
215 CreateIntToFPLocations(arena_, invoke);
216}
217
218void IntrinsicCodeGeneratorARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
219 MoveFPToInt(invoke->GetLocations(), false, GetAssembler());
220}
221void IntrinsicCodeGeneratorARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
222 MoveIntToFP(invoke->GetLocations(), false, GetAssembler());
223}
224
225static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
226 LocationSummary* locations = new (arena) LocationSummary(invoke,
227 LocationSummary::kNoCall,
228 kIntrinsified);
229 locations->SetInAt(0, Location::RequiresRegister());
230 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
231}
232
233static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
234 LocationSummary* locations = new (arena) LocationSummary(invoke,
235 LocationSummary::kNoCall,
236 kIntrinsified);
237 locations->SetInAt(0, Location::RequiresFpuRegister());
238 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
239}
240
241static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
242 Location in = locations->InAt(0);
243 Location out = locations->Out();
244
245 if (is64bit) {
246 __ vabsd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
247 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
248 } else {
249 __ vabss(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>());
250 }
251}
252
253void IntrinsicLocationsBuilderARM::VisitMathAbsDouble(HInvoke* invoke) {
254 CreateFPToFPLocations(arena_, invoke);
255}
256
257void IntrinsicCodeGeneratorARM::VisitMathAbsDouble(HInvoke* invoke) {
258 MathAbsFP(invoke->GetLocations(), true, GetAssembler());
259}
260
261void IntrinsicLocationsBuilderARM::VisitMathAbsFloat(HInvoke* invoke) {
262 CreateFPToFPLocations(arena_, invoke);
263}
264
265void IntrinsicCodeGeneratorARM::VisitMathAbsFloat(HInvoke* invoke) {
266 MathAbsFP(invoke->GetLocations(), false, GetAssembler());
267}
268
269static void CreateIntToIntPlusTemp(ArenaAllocator* arena, HInvoke* invoke) {
270 LocationSummary* locations = new (arena) LocationSummary(invoke,
271 LocationSummary::kNoCall,
272 kIntrinsified);
273 locations->SetInAt(0, Location::RequiresRegister());
274 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
275
276 locations->AddTemp(Location::RequiresRegister());
277}
278
279static void GenAbsInteger(LocationSummary* locations,
280 bool is64bit,
281 ArmAssembler* assembler) {
282 Location in = locations->InAt(0);
283 Location output = locations->Out();
284
285 Register mask = locations->GetTemp(0).AsRegister<Register>();
286
287 if (is64bit) {
288 Register in_reg_lo = in.AsRegisterPairLow<Register>();
289 Register in_reg_hi = in.AsRegisterPairHigh<Register>();
290 Register out_reg_lo = output.AsRegisterPairLow<Register>();
291 Register out_reg_hi = output.AsRegisterPairHigh<Register>();
292
293 DCHECK_NE(out_reg_lo, in_reg_hi) << "Diagonal overlap unexpected.";
294
295 __ Asr(mask, in_reg_hi, 31);
296 __ adds(out_reg_lo, in_reg_lo, ShifterOperand(mask));
297 __ adc(out_reg_hi, in_reg_hi, ShifterOperand(mask));
298 __ eor(out_reg_lo, mask, ShifterOperand(out_reg_lo));
299 __ eor(out_reg_hi, mask, ShifterOperand(out_reg_hi));
300 } else {
301 Register in_reg = in.AsRegister<Register>();
302 Register out_reg = output.AsRegister<Register>();
303
304 __ Asr(mask, in_reg, 31);
305 __ add(out_reg, in_reg, ShifterOperand(mask));
306 __ eor(out_reg, mask, ShifterOperand(out_reg));
307 }
308}
309
310void IntrinsicLocationsBuilderARM::VisitMathAbsInt(HInvoke* invoke) {
311 CreateIntToIntPlusTemp(arena_, invoke);
312}
313
314void IntrinsicCodeGeneratorARM::VisitMathAbsInt(HInvoke* invoke) {
315 GenAbsInteger(invoke->GetLocations(), false, GetAssembler());
316}
317
318
319void IntrinsicLocationsBuilderARM::VisitMathAbsLong(HInvoke* invoke) {
320 CreateIntToIntPlusTemp(arena_, invoke);
321}
322
323void IntrinsicCodeGeneratorARM::VisitMathAbsLong(HInvoke* invoke) {
324 GenAbsInteger(invoke->GetLocations(), true, GetAssembler());
325}
326
327static void GenMinMax(LocationSummary* locations,
328 bool is_min,
329 ArmAssembler* assembler) {
330 Register op1 = locations->InAt(0).AsRegister<Register>();
331 Register op2 = locations->InAt(1).AsRegister<Register>();
332 Register out = locations->Out().AsRegister<Register>();
333
334 __ cmp(op1, ShifterOperand(op2));
335
336 __ it((is_min) ? Condition::LT : Condition::GT, kItElse);
337 __ mov(out, ShifterOperand(op1), is_min ? Condition::LT : Condition::GT);
338 __ mov(out, ShifterOperand(op2), is_min ? Condition::GE : Condition::LE);
339}
340
341static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
342 LocationSummary* locations = new (arena) LocationSummary(invoke,
343 LocationSummary::kNoCall,
344 kIntrinsified);
345 locations->SetInAt(0, Location::RequiresRegister());
346 locations->SetInAt(1, Location::RequiresRegister());
347 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
348}
349
350void IntrinsicLocationsBuilderARM::VisitMathMinIntInt(HInvoke* invoke) {
351 CreateIntIntToIntLocations(arena_, invoke);
352}
353
354void IntrinsicCodeGeneratorARM::VisitMathMinIntInt(HInvoke* invoke) {
355 GenMinMax(invoke->GetLocations(), true, GetAssembler());
356}
357
358void IntrinsicLocationsBuilderARM::VisitMathMaxIntInt(HInvoke* invoke) {
359 CreateIntIntToIntLocations(arena_, invoke);
360}
361
362void IntrinsicCodeGeneratorARM::VisitMathMaxIntInt(HInvoke* invoke) {
363 GenMinMax(invoke->GetLocations(), false, GetAssembler());
364}
365
366void IntrinsicLocationsBuilderARM::VisitMathSqrt(HInvoke* invoke) {
367 CreateFPToFPLocations(arena_, invoke);
368}
369
370void IntrinsicCodeGeneratorARM::VisitMathSqrt(HInvoke* invoke) {
371 LocationSummary* locations = invoke->GetLocations();
372 ArmAssembler* assembler = GetAssembler();
373 __ vsqrtd(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
374 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
375}
376
377void IntrinsicLocationsBuilderARM::VisitMemoryPeekByte(HInvoke* invoke) {
378 CreateIntToIntLocations(arena_, invoke);
379}
380
381void IntrinsicCodeGeneratorARM::VisitMemoryPeekByte(HInvoke* invoke) {
382 ArmAssembler* assembler = GetAssembler();
383 // Ignore upper 4B of long address.
384 __ ldrsb(invoke->GetLocations()->Out().AsRegister<Register>(),
385 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
386}
387
388void IntrinsicLocationsBuilderARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
389 CreateIntToIntLocations(arena_, invoke);
390}
391
392void IntrinsicCodeGeneratorARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
393 ArmAssembler* assembler = GetAssembler();
394 // Ignore upper 4B of long address.
395 __ ldr(invoke->GetLocations()->Out().AsRegister<Register>(),
396 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
397}
398
399void IntrinsicLocationsBuilderARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
400 CreateIntToIntLocations(arena_, invoke);
401}
402
403void IntrinsicCodeGeneratorARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
404 ArmAssembler* assembler = GetAssembler();
405 // Ignore upper 4B of long address.
406 Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
407 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
408 // exception. So we can't use ldrd as addr may be unaligned.
409 Register lo = invoke->GetLocations()->Out().AsRegisterPairLow<Register>();
410 Register hi = invoke->GetLocations()->Out().AsRegisterPairHigh<Register>();
411 if (addr == lo) {
412 __ ldr(hi, Address(addr, 4));
413 __ ldr(lo, Address(addr, 0));
414 } else {
415 __ ldr(lo, Address(addr, 0));
416 __ ldr(hi, Address(addr, 4));
417 }
418}
419
420void IntrinsicLocationsBuilderARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
421 CreateIntToIntLocations(arena_, invoke);
422}
423
424void IntrinsicCodeGeneratorARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
425 ArmAssembler* assembler = GetAssembler();
426 // Ignore upper 4B of long address.
427 __ ldrsh(invoke->GetLocations()->Out().AsRegister<Register>(),
428 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
429}
430
431static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
432 LocationSummary* locations = new (arena) LocationSummary(invoke,
433 LocationSummary::kNoCall,
434 kIntrinsified);
435 locations->SetInAt(0, Location::RequiresRegister());
436 locations->SetInAt(1, Location::RequiresRegister());
437}
438
439void IntrinsicLocationsBuilderARM::VisitMemoryPokeByte(HInvoke* invoke) {
440 CreateIntIntToVoidLocations(arena_, invoke);
441}
442
443void IntrinsicCodeGeneratorARM::VisitMemoryPokeByte(HInvoke* invoke) {
444 ArmAssembler* assembler = GetAssembler();
445 __ strb(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
446 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
447}
448
449void IntrinsicLocationsBuilderARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
450 CreateIntIntToVoidLocations(arena_, invoke);
451}
452
453void IntrinsicCodeGeneratorARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
454 ArmAssembler* assembler = GetAssembler();
455 __ str(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
456 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
457}
458
459void IntrinsicLocationsBuilderARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
460 CreateIntIntToVoidLocations(arena_, invoke);
461}
462
463void IntrinsicCodeGeneratorARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
464 ArmAssembler* assembler = GetAssembler();
465 // Ignore upper 4B of long address.
466 Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
467 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
468 // exception. So we can't use ldrd as addr may be unaligned.
469 __ str(invoke->GetLocations()->InAt(1).AsRegisterPairLow<Register>(), Address(addr, 0));
470 __ str(invoke->GetLocations()->InAt(1).AsRegisterPairHigh<Register>(), Address(addr, 4));
471}
472
473void IntrinsicLocationsBuilderARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
474 CreateIntIntToVoidLocations(arena_, invoke);
475}
476
477void IntrinsicCodeGeneratorARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
478 ArmAssembler* assembler = GetAssembler();
479 __ strh(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
480 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
481}
482
483void IntrinsicLocationsBuilderARM::VisitThreadCurrentThread(HInvoke* invoke) {
484 LocationSummary* locations = new (arena_) LocationSummary(invoke,
485 LocationSummary::kNoCall,
486 kIntrinsified);
487 locations->SetOut(Location::RequiresRegister());
488}
489
490void IntrinsicCodeGeneratorARM::VisitThreadCurrentThread(HInvoke* invoke) {
491 ArmAssembler* assembler = GetAssembler();
492 __ LoadFromOffset(kLoadWord,
493 invoke->GetLocations()->Out().AsRegister<Register>(),
494 TR,
495 Thread::PeerOffset<kArmPointerSize>().Int32Value());
496}
497
498static void GenUnsafeGet(HInvoke* invoke,
499 Primitive::Type type,
500 bool is_volatile,
501 CodeGeneratorARM* codegen) {
502 LocationSummary* locations = invoke->GetLocations();
503 DCHECK((type == Primitive::kPrimInt) ||
504 (type == Primitive::kPrimLong) ||
505 (type == Primitive::kPrimNot));
506 ArmAssembler* assembler = codegen->GetAssembler();
507 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
508 Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Long offset, lo part only.
509
510 if (type == Primitive::kPrimLong) {
511 Register trg_lo = locations->Out().AsRegisterPairLow<Register>();
512 __ add(IP, base, ShifterOperand(offset));
513 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
514 Register trg_hi = locations->Out().AsRegisterPairHigh<Register>();
515 __ ldrexd(trg_lo, trg_hi, IP);
516 } else {
517 __ ldrd(trg_lo, Address(IP));
518 }
519 } else {
520 Register trg = locations->Out().AsRegister<Register>();
521 __ ldr(trg, Address(base, offset));
522 }
523
524 if (is_volatile) {
525 __ dmb(ISH);
526 }
527}
528
529static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
530 LocationSummary* locations = new (arena) LocationSummary(invoke,
531 LocationSummary::kNoCall,
532 kIntrinsified);
533 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
534 locations->SetInAt(1, Location::RequiresRegister());
535 locations->SetInAt(2, Location::RequiresRegister());
536 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
537}
538
539void IntrinsicLocationsBuilderARM::VisitUnsafeGet(HInvoke* invoke) {
540 CreateIntIntIntToIntLocations(arena_, invoke);
541}
542void IntrinsicLocationsBuilderARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
543 CreateIntIntIntToIntLocations(arena_, invoke);
544}
545void IntrinsicLocationsBuilderARM::VisitUnsafeGetLong(HInvoke* invoke) {
546 CreateIntIntIntToIntLocations(arena_, invoke);
547}
548void IntrinsicLocationsBuilderARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
549 CreateIntIntIntToIntLocations(arena_, invoke);
550}
551void IntrinsicLocationsBuilderARM::VisitUnsafeGetObject(HInvoke* invoke) {
552 CreateIntIntIntToIntLocations(arena_, invoke);
553}
554void IntrinsicLocationsBuilderARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
555 CreateIntIntIntToIntLocations(arena_, invoke);
556}
557
558void IntrinsicCodeGeneratorARM::VisitUnsafeGet(HInvoke* invoke) {
559 GenUnsafeGet(invoke, Primitive::kPrimInt, false, codegen_);
560}
561void IntrinsicCodeGeneratorARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
562 GenUnsafeGet(invoke, Primitive::kPrimInt, true, codegen_);
563}
564void IntrinsicCodeGeneratorARM::VisitUnsafeGetLong(HInvoke* invoke) {
565 GenUnsafeGet(invoke, Primitive::kPrimLong, false, codegen_);
566}
567void IntrinsicCodeGeneratorARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
568 GenUnsafeGet(invoke, Primitive::kPrimLong, true, codegen_);
569}
570void IntrinsicCodeGeneratorARM::VisitUnsafeGetObject(HInvoke* invoke) {
571 GenUnsafeGet(invoke, Primitive::kPrimNot, false, codegen_);
572}
573void IntrinsicCodeGeneratorARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
574 GenUnsafeGet(invoke, Primitive::kPrimNot, true, codegen_);
575}
576
577static void CreateIntIntIntIntToVoid(ArenaAllocator* arena,
578 const ArmInstructionSetFeatures& features,
579 Primitive::Type type,
580 bool is_volatile,
581 HInvoke* invoke) {
582 LocationSummary* locations = new (arena) LocationSummary(invoke,
583 LocationSummary::kNoCall,
584 kIntrinsified);
585 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
586 locations->SetInAt(1, Location::RequiresRegister());
587 locations->SetInAt(2, Location::RequiresRegister());
588 locations->SetInAt(3, Location::RequiresRegister());
589
590 if (type == Primitive::kPrimLong) {
591 // Potentially need temps for ldrexd-strexd loop.
592 if (is_volatile && !features.HasAtomicLdrdAndStrd()) {
593 locations->AddTemp(Location::RequiresRegister()); // Temp_lo.
594 locations->AddTemp(Location::RequiresRegister()); // Temp_hi.
595 }
596 } else if (type == Primitive::kPrimNot) {
597 // Temps for card-marking.
598 locations->AddTemp(Location::RequiresRegister()); // Temp.
599 locations->AddTemp(Location::RequiresRegister()); // Card.
600 }
601}
602
603void IntrinsicLocationsBuilderARM::VisitUnsafePut(HInvoke* invoke) {
604 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, false, invoke);
605}
606void IntrinsicLocationsBuilderARM::VisitUnsafePutOrdered(HInvoke* invoke) {
607 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, false, invoke);
608}
609void IntrinsicLocationsBuilderARM::VisitUnsafePutVolatile(HInvoke* invoke) {
610 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, true, invoke);
611}
612void IntrinsicLocationsBuilderARM::VisitUnsafePutObject(HInvoke* invoke) {
613 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, false, invoke);
614}
615void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
616 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, false, invoke);
617}
618void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
619 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, true, invoke);
620}
621void IntrinsicLocationsBuilderARM::VisitUnsafePutLong(HInvoke* invoke) {
622 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, false, invoke);
623}
624void IntrinsicLocationsBuilderARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
625 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, false, invoke);
626}
627void IntrinsicLocationsBuilderARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
628 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, true, invoke);
629}
630
631static void GenUnsafePut(LocationSummary* locations,
632 Primitive::Type type,
633 bool is_volatile,
634 bool is_ordered,
635 CodeGeneratorARM* codegen) {
636 ArmAssembler* assembler = codegen->GetAssembler();
637
638 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
639 Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Long offset, lo part only.
640 Register value;
641
642 if (is_volatile || is_ordered) {
643 __ dmb(ISH);
644 }
645
646 if (type == Primitive::kPrimLong) {
647 Register value_lo = locations->InAt(3).AsRegisterPairLow<Register>();
648 value = value_lo;
649 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
650 Register temp_lo = locations->GetTemp(0).AsRegister<Register>();
651 Register temp_hi = locations->GetTemp(1).AsRegister<Register>();
652 Register value_hi = locations->InAt(3).AsRegisterPairHigh<Register>();
653
654 __ add(IP, base, ShifterOperand(offset));
655 Label loop_head;
656 __ Bind(&loop_head);
657 __ ldrexd(temp_lo, temp_hi, IP);
658 __ strexd(temp_lo, value_lo, value_hi, IP);
659 __ cmp(temp_lo, ShifterOperand(0));
660 __ b(&loop_head, NE);
661 } else {
662 __ add(IP, base, ShifterOperand(offset));
663 __ strd(value_lo, Address(IP));
664 }
665 } else {
666 value = locations->InAt(3).AsRegister<Register>();
667 __ str(value, Address(base, offset));
668 }
669
670 if (is_volatile) {
671 __ dmb(ISH);
672 }
673
674 if (type == Primitive::kPrimNot) {
675 Register temp = locations->GetTemp(0).AsRegister<Register>();
676 Register card = locations->GetTemp(1).AsRegister<Register>();
677 codegen->MarkGCCard(temp, card, base, value);
678 }
679}
680
681void IntrinsicCodeGeneratorARM::VisitUnsafePut(HInvoke* invoke) {
682 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, false, codegen_);
683}
684void IntrinsicCodeGeneratorARM::VisitUnsafePutOrdered(HInvoke* invoke) {
685 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, true, codegen_);
686}
687void IntrinsicCodeGeneratorARM::VisitUnsafePutVolatile(HInvoke* invoke) {
688 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, true, false, codegen_);
689}
690void IntrinsicCodeGeneratorARM::VisitUnsafePutObject(HInvoke* invoke) {
691 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, false, codegen_);
692}
693void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
694 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, true, codegen_);
695}
696void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
697 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, true, false, codegen_);
698}
699void IntrinsicCodeGeneratorARM::VisitUnsafePutLong(HInvoke* invoke) {
700 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, false, codegen_);
701}
702void IntrinsicCodeGeneratorARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
703 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, true, codegen_);
704}
705void IntrinsicCodeGeneratorARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
706 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, false, codegen_);
707}
708
709static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena,
710 HInvoke* invoke) {
711 LocationSummary* locations = new (arena) LocationSummary(invoke,
712 LocationSummary::kNoCall,
713 kIntrinsified);
714 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
715 locations->SetInAt(1, Location::RequiresRegister());
716 locations->SetInAt(2, Location::RequiresRegister());
717 locations->SetInAt(3, Location::RequiresRegister());
718 locations->SetInAt(4, Location::RequiresRegister());
719
720 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
721
722 locations->AddTemp(Location::RequiresRegister()); // Pointer.
723 locations->AddTemp(Location::RequiresRegister()); // Temp 1.
724 locations->AddTemp(Location::RequiresRegister()); // Temp 2.
725}
726
727static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM* codegen) {
728 DCHECK_NE(type, Primitive::kPrimLong);
729
730 ArmAssembler* assembler = codegen->GetAssembler();
731
732 Register out = locations->Out().AsRegister<Register>(); // Boolean result.
733
734 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
735 Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Offset (discard high 4B).
736 Register expected_lo = locations->InAt(3).AsRegister<Register>(); // Expected.
737 Register value_lo = locations->InAt(4).AsRegister<Register>(); // Value.
738
739 Register tmp_ptr = locations->GetTemp(0).AsRegister<Register>(); // Pointer to actual memory.
740 Register tmp_lo = locations->GetTemp(1).AsRegister<Register>(); // Value in memory.
741
742 if (type == Primitive::kPrimNot) {
743 // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
744 // object and scan the receiver at the next GC for nothing.
745 codegen->MarkGCCard(tmp_ptr, tmp_lo, base, value_lo);
746 }
747
748 // Prevent reordering with prior memory operations.
749 __ dmb(ISH);
750
751 __ add(tmp_ptr, base, ShifterOperand(offset));
752
753 // do {
754 // tmp = [r_ptr] - expected;
755 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
756 // result = tmp != 0;
757
758 Label loop_head;
759 __ Bind(&loop_head);
760
761 __ ldrex(tmp_lo, tmp_ptr);
762
763 __ subs(tmp_lo, tmp_lo, ShifterOperand(expected_lo));
764
765 __ it(EQ, ItState::kItT);
766 __ strex(tmp_lo, value_lo, tmp_ptr, EQ);
767 __ cmp(tmp_lo, ShifterOperand(1), EQ);
768
769 __ b(&loop_head, EQ);
770
771 __ dmb(ISH);
772
773 __ rsbs(out, tmp_lo, ShifterOperand(1));
774 __ it(CC);
775 __ mov(out, ShifterOperand(0), CC);
776}
777
778void IntrinsicLocationsBuilderARM::VisitUnsafeCASInt(HInvoke* invoke ATTRIBUTE_UNUSED) {
779 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
780}
781void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke ATTRIBUTE_UNUSED) {
782 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
783}
784void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) {
785 GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
786}
787void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) {
788 GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
789}
790
791void IntrinsicLocationsBuilderARM::VisitStringCharAt(HInvoke* invoke) {
792 LocationSummary* locations = new (arena_) LocationSummary(invoke,
793 LocationSummary::kCallOnSlowPath,
794 kIntrinsified);
795 locations->SetInAt(0, Location::RequiresRegister());
796 locations->SetInAt(1, Location::RequiresRegister());
797 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
798
799 locations->AddTemp(Location::RequiresRegister());
800 locations->AddTemp(Location::RequiresRegister());
801}
802
803void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) {
804 ArmAssembler* assembler = GetAssembler();
805 LocationSummary* locations = invoke->GetLocations();
806
807 // Location of reference to data array
808 const MemberOffset value_offset = mirror::String::ValueOffset();
809 // Location of count
810 const MemberOffset count_offset = mirror::String::CountOffset();
811 // Starting offset within data array
812 const MemberOffset offset_offset = mirror::String::OffsetOffset();
813 // Start of char data with array_
814 const MemberOffset data_offset = mirror::Array::DataOffset(sizeof(uint16_t));
815
816 Register obj = locations->InAt(0).AsRegister<Register>(); // String object pointer.
817 Register idx = locations->InAt(1).AsRegister<Register>(); // Index of character.
818 Register out = locations->Out().AsRegister<Register>(); // Result character.
819
820 Register temp = locations->GetTemp(0).AsRegister<Register>();
821 Register array_temp = locations->GetTemp(1).AsRegister<Register>();
822
823 // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
824 // the cost.
825 // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
826 // we will not optimize the code for constants (which would save a register).
827
828 SlowPathCodeARM* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
829 codegen_->AddSlowPath(slow_path);
830
831 __ ldr(temp, Address(obj, count_offset.Int32Value())); // temp = str.length.
832 codegen_->MaybeRecordImplicitNullCheck(invoke);
833 __ cmp(idx, ShifterOperand(temp));
834 __ b(slow_path->GetEntryLabel(), CS);
835
836 // Index computation.
837 __ ldr(temp, Address(obj, offset_offset.Int32Value())); // temp := str.offset.
838 __ ldr(array_temp, Address(obj, value_offset.Int32Value())); // array_temp := str.offset.
839 __ add(temp, temp, ShifterOperand(idx));
840 DCHECK_EQ(data_offset.Int32Value() % 2, 0); // We'll compensate by shifting.
841 __ add(temp, temp, ShifterOperand(data_offset.Int32Value() / 2));
842
843 // Load the value.
844 __ ldrh(out, Address(array_temp, temp, LSL, 1)); // out := array_temp[temp].
845
846 __ Bind(slow_path->GetExitLabel());
847}
848
849// Unimplemented intrinsics.
850
851#define UNIMPLEMENTED_INTRINSIC(Name) \
852void IntrinsicLocationsBuilderARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
853} \
854void IntrinsicCodeGeneratorARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
855}
856
857UNIMPLEMENTED_INTRINSIC(IntegerReverse)
858UNIMPLEMENTED_INTRINSIC(IntegerReverseBytes)
859UNIMPLEMENTED_INTRINSIC(LongReverse)
860UNIMPLEMENTED_INTRINSIC(LongReverseBytes)
861UNIMPLEMENTED_INTRINSIC(ShortReverseBytes)
862UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble)
863UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat)
864UNIMPLEMENTED_INTRINSIC(MathMaxDoubleDouble)
865UNIMPLEMENTED_INTRINSIC(MathMaxFloatFloat)
866UNIMPLEMENTED_INTRINSIC(MathMinLongLong)
867UNIMPLEMENTED_INTRINSIC(MathMaxLongLong)
868UNIMPLEMENTED_INTRINSIC(MathCeil) // Could be done by changing rounding mode, maybe?
869UNIMPLEMENTED_INTRINSIC(MathFloor) // Could be done by changing rounding mode, maybe?
870UNIMPLEMENTED_INTRINSIC(MathRint)
871UNIMPLEMENTED_INTRINSIC(MathRoundDouble) // Could be done by changing rounding mode, maybe?
872UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding mode, maybe?
873UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure.
874UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
875UNIMPLEMENTED_INTRINSIC(StringCompareTo)
876UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should
877UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here.
878UNIMPLEMENTED_INTRINSIC(StringIndexOf)
879UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)
880UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
881
882} // namespace arm
883} // namespace art