blob: 8874edc341d8df0838a211f1b3465d2b719a2964 [file] [log] [blame]
Andreas Gampe878d58c2015-01-15 23:24:00 -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_arm64.h"
18
19#include "code_generator_arm64.h"
20#include "common_arm64.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/arm64/assembler_arm64.h"
28#include "utils/arm64/constants_arm64.h"
29
30#include "a64/disasm-a64.h"
31#include "a64/macro-assembler-a64.h"
32
33using namespace vixl; // NOLINT(build/namespaces)
34
35namespace art {
36
37namespace arm64 {
38
39using helpers::DRegisterFrom;
40using helpers::FPRegisterFrom;
41using helpers::HeapOperand;
Andreas Gampe878d58c2015-01-15 23:24:00 -080042using helpers::RegisterFrom;
43using helpers::SRegisterFrom;
44using helpers::WRegisterFrom;
45using helpers::XRegisterFrom;
46
47
48namespace {
49
50ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) {
51 return MemOperand(XRegisterFrom(location), offset);
52}
53
54} // namespace
55
56vixl::MacroAssembler* IntrinsicCodeGeneratorARM64::GetVIXLAssembler() {
57 return codegen_->GetAssembler()->vixl_masm_;
58}
59
60ArenaAllocator* IntrinsicCodeGeneratorARM64::GetAllocator() {
61 return codegen_->GetGraph()->GetArena();
62}
63
64#define __ codegen->GetAssembler()->vixl_masm_->
65
66static void MoveFromReturnRegister(Location trg,
67 Primitive::Type type,
68 CodeGeneratorARM64* codegen) {
69 if (!trg.IsValid()) {
70 DCHECK(type == Primitive::kPrimVoid);
71 return;
72 }
73
74 DCHECK_NE(type, Primitive::kPrimVoid);
75
Alexandre Rames542361f2015-01-29 16:57:31 +000076 if (Primitive::IsIntegralType(type)) {
Andreas Gampe878d58c2015-01-15 23:24:00 -080077 Register trg_reg = RegisterFrom(trg, type);
78 Register res_reg = RegisterFrom(ARM64ReturnLocation(type), type);
79 __ Mov(trg_reg, res_reg, kDiscardForSameWReg);
80 } else {
81 FPRegister trg_reg = FPRegisterFrom(trg, type);
82 FPRegister res_reg = FPRegisterFrom(ARM64ReturnLocation(type), type);
83 __ Fmov(trg_reg, res_reg);
84 }
85}
86
87static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorARM64* codegen) {
88 if (invoke->InputCount() == 0) {
89 return;
90 }
91
92 LocationSummary* locations = invoke->GetLocations();
93 InvokeDexCallingConventionVisitor calling_convention_visitor;
94
95 // We're moving potentially two or more locations to locations that could overlap, so we need
96 // a parallel move resolver.
97 HParallelMove parallel_move(arena);
98
99 for (size_t i = 0; i < invoke->InputCount(); i++) {
100 HInstruction* input = invoke->InputAt(i);
101 Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType());
102 Location actual_loc = locations->InAt(i);
103
104 parallel_move.AddMove(actual_loc, cc_loc, nullptr);
105 }
106
107 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
108}
109
110// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
111// call. This will copy the arguments into the positions for a regular call.
112//
113// Note: The actual parameters are required to be in the locations given by the invoke's location
114// summary. If an intrinsic modifies those locations before a slowpath call, they must be
115// restored!
116class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 {
117 public:
118 explicit IntrinsicSlowPathARM64(HInvoke* invoke) : invoke_(invoke) { }
119
120 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
121 CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
122 __ Bind(GetEntryLabel());
123
124 codegen->SaveLiveRegisters(invoke_->GetLocations());
125
126 MoveArguments(invoke_, codegen->GetGraph()->GetArena(), codegen);
127
128 if (invoke_->IsInvokeStaticOrDirect()) {
129 codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), kArtMethodRegister);
130 } else {
131 UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
132 UNREACHABLE();
133 }
134
135 // Copy the result back to the expected output.
136 Location out = invoke_->GetLocations()->Out();
137 if (out.IsValid()) {
138 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
139 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
140 MoveFromReturnRegister(out, invoke_->GetType(), codegen);
141 }
142
143 codegen->RestoreLiveRegisters(invoke_->GetLocations());
144 __ B(GetExitLabel());
145 }
146
147 private:
148 // The instruction where this slow path is happening.
149 HInvoke* const invoke_;
150
151 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM64);
152};
153
154#undef __
155
156bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) {
157 Dispatch(invoke);
158 LocationSummary* res = invoke->GetLocations();
159 return res != nullptr && res->Intrinsified();
160}
161
162#define __ masm->
163
164static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
165 LocationSummary* locations = new (arena) LocationSummary(invoke,
166 LocationSummary::kNoCall,
167 kIntrinsified);
168 locations->SetInAt(0, Location::RequiresFpuRegister());
169 locations->SetOut(Location::RequiresRegister());
170}
171
172static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
173 LocationSummary* locations = new (arena) LocationSummary(invoke,
174 LocationSummary::kNoCall,
175 kIntrinsified);
176 locations->SetInAt(0, Location::RequiresRegister());
177 locations->SetOut(Location::RequiresFpuRegister());
178}
179
180static void MoveFPToInt(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
181 Location input = locations->InAt(0);
182 Location output = locations->Out();
183 __ Fmov(is64bit ? XRegisterFrom(output) : WRegisterFrom(output),
184 is64bit ? DRegisterFrom(input) : SRegisterFrom(input));
185}
186
187static void MoveIntToFP(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
188 Location input = locations->InAt(0);
189 Location output = locations->Out();
190 __ Fmov(is64bit ? DRegisterFrom(output) : SRegisterFrom(output),
191 is64bit ? XRegisterFrom(input) : WRegisterFrom(input));
192}
193
194void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
195 CreateFPToIntLocations(arena_, invoke);
196}
197void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
198 CreateIntToFPLocations(arena_, invoke);
199}
200
201void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
202 MoveFPToInt(invoke->GetLocations(), true, GetVIXLAssembler());
203}
204void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
205 MoveIntToFP(invoke->GetLocations(), true, GetVIXLAssembler());
206}
207
208void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
209 CreateFPToIntLocations(arena_, invoke);
210}
211void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
212 CreateIntToFPLocations(arena_, invoke);
213}
214
215void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
216 MoveFPToInt(invoke->GetLocations(), false, GetVIXLAssembler());
217}
218void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
219 MoveIntToFP(invoke->GetLocations(), false, GetVIXLAssembler());
220}
221
222static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
223 LocationSummary* locations = new (arena) LocationSummary(invoke,
224 LocationSummary::kNoCall,
225 kIntrinsified);
226 locations->SetInAt(0, Location::RequiresRegister());
227 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
228}
229
230static void GenReverseBytes(LocationSummary* locations,
231 Primitive::Type type,
232 vixl::MacroAssembler* masm) {
233 Location in = locations->InAt(0);
234 Location out = locations->Out();
235
236 switch (type) {
237 case Primitive::kPrimShort:
238 __ Rev16(WRegisterFrom(out), WRegisterFrom(in));
239 __ Sxth(WRegisterFrom(out), WRegisterFrom(out));
240 break;
241 case Primitive::kPrimInt:
242 case Primitive::kPrimLong:
243 __ Rev(RegisterFrom(out, type), RegisterFrom(in, type));
244 break;
245 default:
246 LOG(FATAL) << "Unexpected size for reverse-bytes: " << type;
247 UNREACHABLE();
248 }
249}
250
251void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
252 CreateIntToIntLocations(arena_, invoke);
253}
254
255void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
256 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
257}
258
259void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) {
260 CreateIntToIntLocations(arena_, invoke);
261}
262
263void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) {
264 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
265}
266
267void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) {
268 CreateIntToIntLocations(arena_, invoke);
269}
270
271void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
272 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
273}
274
275static void GenReverse(LocationSummary* locations,
276 Primitive::Type type,
277 vixl::MacroAssembler* masm) {
278 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
279
280 Location in = locations->InAt(0);
281 Location out = locations->Out();
282
283 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
284}
285
286void IntrinsicLocationsBuilderARM64::VisitIntegerReverse(HInvoke* invoke) {
287 CreateIntToIntLocations(arena_, invoke);
288}
289
290void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) {
291 GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
292}
293
294void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) {
295 CreateIntToIntLocations(arena_, invoke);
296}
297
298void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
299 GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
300}
301
302static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800303 LocationSummary* locations = new (arena) LocationSummary(invoke,
304 LocationSummary::kNoCall,
305 kIntrinsified);
306 locations->SetInAt(0, Location::RequiresFpuRegister());
307 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
308}
309
310static void MathAbsFP(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
311 Location in = locations->InAt(0);
312 Location out = locations->Out();
313
314 FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in);
315 FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out);
316
317 __ Fabs(out_reg, in_reg);
318}
319
320void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) {
321 CreateFPToFPLocations(arena_, invoke);
322}
323
324void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) {
325 MathAbsFP(invoke->GetLocations(), true, GetVIXLAssembler());
326}
327
328void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) {
329 CreateFPToFPLocations(arena_, invoke);
330}
331
332void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) {
333 MathAbsFP(invoke->GetLocations(), false, GetVIXLAssembler());
334}
335
336static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
337 LocationSummary* locations = new (arena) LocationSummary(invoke,
338 LocationSummary::kNoCall,
339 kIntrinsified);
340 locations->SetInAt(0, Location::RequiresRegister());
341 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
342}
343
344static void GenAbsInteger(LocationSummary* locations,
345 bool is64bit,
346 vixl::MacroAssembler* masm) {
347 Location in = locations->InAt(0);
348 Location output = locations->Out();
349
350 Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in);
351 Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output);
352
353 __ Cmp(in_reg, Operand(0));
354 __ Cneg(out_reg, in_reg, lt);
355}
356
357void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
358 CreateIntToInt(arena_, invoke);
359}
360
361void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
362 GenAbsInteger(invoke->GetLocations(), false, GetVIXLAssembler());
363}
364
365void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
366 CreateIntToInt(arena_, invoke);
367}
368
369void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
370 GenAbsInteger(invoke->GetLocations(), true, GetVIXLAssembler());
371}
372
373static void GenMinMaxFP(LocationSummary* locations,
374 bool is_min,
375 bool is_double,
376 vixl::MacroAssembler* masm) {
377 Location op1 = locations->InAt(0);
378 Location op2 = locations->InAt(1);
379 Location out = locations->Out();
380
381 FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1);
382 FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2);
383 FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out);
384 if (is_min) {
385 __ Fmin(out_reg, op1_reg, op2_reg);
386 } else {
387 __ Fmax(out_reg, op1_reg, op2_reg);
388 }
389}
390
391static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
392 LocationSummary* locations = new (arena) LocationSummary(invoke,
393 LocationSummary::kNoCall,
394 kIntrinsified);
395 locations->SetInAt(0, Location::RequiresFpuRegister());
396 locations->SetInAt(1, Location::RequiresFpuRegister());
397 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
398}
399
400void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
401 CreateFPFPToFPLocations(arena_, invoke);
402}
403
404void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
405 GenMinMaxFP(invoke->GetLocations(), true, true, GetVIXLAssembler());
406}
407
408void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
409 CreateFPFPToFPLocations(arena_, invoke);
410}
411
412void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
413 GenMinMaxFP(invoke->GetLocations(), true, false, GetVIXLAssembler());
414}
415
416void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
417 CreateFPFPToFPLocations(arena_, invoke);
418}
419
420void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
421 GenMinMaxFP(invoke->GetLocations(), false, true, GetVIXLAssembler());
422}
423
424void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
425 CreateFPFPToFPLocations(arena_, invoke);
426}
427
428void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
429 GenMinMaxFP(invoke->GetLocations(), false, false, GetVIXLAssembler());
430}
431
432static void GenMinMax(LocationSummary* locations,
433 bool is_min,
434 bool is_long,
435 vixl::MacroAssembler* masm) {
436 Location op1 = locations->InAt(0);
437 Location op2 = locations->InAt(1);
438 Location out = locations->Out();
439
440 Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
441 Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
442 Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
443
444 __ Cmp(op1_reg, op2_reg);
445 __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
446}
447
448static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
449 LocationSummary* locations = new (arena) LocationSummary(invoke,
450 LocationSummary::kNoCall,
451 kIntrinsified);
452 locations->SetInAt(0, Location::RequiresRegister());
453 locations->SetInAt(1, Location::RequiresRegister());
454 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
455}
456
457void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
458 CreateIntIntToIntLocations(arena_, invoke);
459}
460
461void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) {
462 GenMinMax(invoke->GetLocations(), true, false, GetVIXLAssembler());
463}
464
465void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) {
466 CreateIntIntToIntLocations(arena_, invoke);
467}
468
469void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) {
470 GenMinMax(invoke->GetLocations(), true, true, GetVIXLAssembler());
471}
472
473void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) {
474 CreateIntIntToIntLocations(arena_, invoke);
475}
476
477void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) {
478 GenMinMax(invoke->GetLocations(), false, false, GetVIXLAssembler());
479}
480
481void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) {
482 CreateIntIntToIntLocations(arena_, invoke);
483}
484
485void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) {
486 GenMinMax(invoke->GetLocations(), false, true, GetVIXLAssembler());
487}
488
489void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) {
490 CreateFPToFPLocations(arena_, invoke);
491}
492
493void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) {
494 LocationSummary* locations = invoke->GetLocations();
495 vixl::MacroAssembler* masm = GetVIXLAssembler();
496 __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
497}
498
499void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) {
500 CreateFPToFPLocations(arena_, invoke);
501}
502
503void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) {
504 LocationSummary* locations = invoke->GetLocations();
505 vixl::MacroAssembler* masm = GetVIXLAssembler();
506 __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
507}
508
509void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) {
510 CreateFPToFPLocations(arena_, invoke);
511}
512
513void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) {
514 LocationSummary* locations = invoke->GetLocations();
515 vixl::MacroAssembler* masm = GetVIXLAssembler();
516 __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
517}
518
519void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) {
520 CreateFPToFPLocations(arena_, invoke);
521}
522
523void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) {
524 LocationSummary* locations = invoke->GetLocations();
525 vixl::MacroAssembler* masm = GetVIXLAssembler();
526 __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
527}
528
529static void CreateFPToIntPlusTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
530 LocationSummary* locations = new (arena) LocationSummary(invoke,
531 LocationSummary::kNoCall,
532 kIntrinsified);
533 locations->SetInAt(0, Location::RequiresFpuRegister());
534 locations->SetOut(Location::RequiresRegister());
535}
536
537static void GenMathRound(LocationSummary* locations,
538 bool is_double,
539 vixl::MacroAssembler* masm) {
540 FPRegister in_reg = is_double ?
541 DRegisterFrom(locations->InAt(0)) : SRegisterFrom(locations->InAt(0));
542 Register out_reg = is_double ?
543 XRegisterFrom(locations->Out()) : WRegisterFrom(locations->Out());
544 UseScratchRegisterScope temps(masm);
545 FPRegister temp1_reg = temps.AcquireSameSizeAs(in_reg);
546
547 // 0.5 can be encoded as an immediate, so use fmov.
548 if (is_double) {
549 __ Fmov(temp1_reg, static_cast<double>(0.5));
550 } else {
551 __ Fmov(temp1_reg, static_cast<float>(0.5));
552 }
553 __ Fadd(temp1_reg, in_reg, temp1_reg);
554 __ Fcvtms(out_reg, temp1_reg);
555}
556
557void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) {
558 CreateFPToIntPlusTempLocations(arena_, invoke);
559}
560
561void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) {
562 GenMathRound(invoke->GetLocations(), true, GetVIXLAssembler());
563}
564
565void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) {
566 CreateFPToIntPlusTempLocations(arena_, invoke);
567}
568
569void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) {
570 GenMathRound(invoke->GetLocations(), false, GetVIXLAssembler());
571}
572
573void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) {
574 CreateIntToIntLocations(arena_, invoke);
575}
576
577void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) {
578 vixl::MacroAssembler* masm = GetVIXLAssembler();
579 __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()),
580 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
581}
582
583void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
584 CreateIntToIntLocations(arena_, invoke);
585}
586
587void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
588 vixl::MacroAssembler* masm = GetVIXLAssembler();
589 __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()),
590 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
591}
592
593void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
594 CreateIntToIntLocations(arena_, invoke);
595}
596
597void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
598 vixl::MacroAssembler* masm = GetVIXLAssembler();
599 __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()),
600 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
601}
602
603void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
604 CreateIntToIntLocations(arena_, invoke);
605}
606
607void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
608 vixl::MacroAssembler* masm = GetVIXLAssembler();
609 __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()),
610 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
611}
612
613static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
614 LocationSummary* locations = new (arena) LocationSummary(invoke,
615 LocationSummary::kNoCall,
616 kIntrinsified);
617 locations->SetInAt(0, Location::RequiresRegister());
618 locations->SetInAt(1, Location::RequiresRegister());
619}
620
621void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) {
622 CreateIntIntToVoidLocations(arena_, invoke);
623}
624
625void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) {
626 vixl::MacroAssembler* masm = GetVIXLAssembler();
627 __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)),
628 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
629}
630
631void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
632 CreateIntIntToVoidLocations(arena_, invoke);
633}
634
635void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
636 vixl::MacroAssembler* masm = GetVIXLAssembler();
637 __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)),
638 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
639}
640
641void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
642 CreateIntIntToVoidLocations(arena_, invoke);
643}
644
645void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
646 vixl::MacroAssembler* masm = GetVIXLAssembler();
647 __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)),
648 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
649}
650
651void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
652 CreateIntIntToVoidLocations(arena_, invoke);
653}
654
655void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
656 vixl::MacroAssembler* masm = GetVIXLAssembler();
657 __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)),
658 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
659}
660
661void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) {
662 LocationSummary* locations = new (arena_) LocationSummary(invoke,
663 LocationSummary::kNoCall,
664 kIntrinsified);
665 locations->SetOut(Location::RequiresRegister());
666}
667
668void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) {
669 codegen_->Load(Primitive::kPrimNot, WRegisterFrom(invoke->GetLocations()->Out()),
670 MemOperand(tr, Thread::PeerOffset<8>().Int32Value()));
671}
672
673static void GenUnsafeGet(HInvoke* invoke,
674 Primitive::Type type,
675 bool is_volatile,
676 CodeGeneratorARM64* codegen) {
677 LocationSummary* locations = invoke->GetLocations();
678 DCHECK((type == Primitive::kPrimInt) ||
679 (type == Primitive::kPrimLong) ||
680 (type == Primitive::kPrimNot));
681 vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
682 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
683 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
684 Register trg = RegisterFrom(locations->Out(), type);
685
686 MemOperand mem_op(base.X(), offset);
687 if (is_volatile) {
688 if (kUseAcquireRelease) {
689 codegen->LoadAcquire(invoke, trg, mem_op);
690 } else {
691 codegen->Load(type, trg, mem_op);
692 __ Dmb(InnerShareable, BarrierReads);
693 }
694 } else {
695 codegen->Load(type, trg, mem_op);
696 }
697}
698
699static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
700 LocationSummary* locations = new (arena) LocationSummary(invoke,
701 LocationSummary::kNoCall,
702 kIntrinsified);
703 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
704 locations->SetInAt(1, Location::RequiresRegister());
705 locations->SetInAt(2, Location::RequiresRegister());
706 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
707}
708
709void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
710 CreateIntIntIntToIntLocations(arena_, invoke);
711}
712void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
713 CreateIntIntIntToIntLocations(arena_, invoke);
714}
715void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
716 CreateIntIntIntToIntLocations(arena_, invoke);
717}
718void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
719 CreateIntIntIntToIntLocations(arena_, invoke);
720}
721void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
722 CreateIntIntIntToIntLocations(arena_, invoke);
723}
724void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
725 CreateIntIntIntToIntLocations(arena_, invoke);
726}
727
728void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
729 GenUnsafeGet(invoke, Primitive::kPrimInt, false, codegen_);
730}
731void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
732 GenUnsafeGet(invoke, Primitive::kPrimInt, true, codegen_);
733}
734void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
735 GenUnsafeGet(invoke, Primitive::kPrimLong, false, codegen_);
736}
737void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
738 GenUnsafeGet(invoke, Primitive::kPrimLong, true, codegen_);
739}
740void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
741 GenUnsafeGet(invoke, Primitive::kPrimNot, false, codegen_);
742}
743void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
744 GenUnsafeGet(invoke, Primitive::kPrimNot, true, codegen_);
745}
746
747static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
748 LocationSummary* locations = new (arena) LocationSummary(invoke,
749 LocationSummary::kNoCall,
750 kIntrinsified);
751 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
752 locations->SetInAt(1, Location::RequiresRegister());
753 locations->SetInAt(2, Location::RequiresRegister());
754 locations->SetInAt(3, Location::RequiresRegister());
755}
756
757void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
758 CreateIntIntIntIntToVoid(arena_, invoke);
759}
760void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
761 CreateIntIntIntIntToVoid(arena_, invoke);
762}
763void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
764 CreateIntIntIntIntToVoid(arena_, invoke);
765}
766void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
767 CreateIntIntIntIntToVoid(arena_, invoke);
768}
769void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
770 CreateIntIntIntIntToVoid(arena_, invoke);
771}
772void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
773 CreateIntIntIntIntToVoid(arena_, invoke);
774}
775void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
776 CreateIntIntIntIntToVoid(arena_, invoke);
777}
778void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
779 CreateIntIntIntIntToVoid(arena_, invoke);
780}
781void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
782 CreateIntIntIntIntToVoid(arena_, invoke);
783}
784
785static void GenUnsafePut(LocationSummary* locations,
786 Primitive::Type type,
787 bool is_volatile,
788 bool is_ordered,
789 CodeGeneratorARM64* codegen) {
790 vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
791
792 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
793 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
794 Register value = RegisterFrom(locations->InAt(3), type);
795
796 MemOperand mem_op(base.X(), offset);
797
798 if (is_volatile || is_ordered) {
799 if (kUseAcquireRelease) {
800 codegen->StoreRelease(type, value, mem_op);
801 } else {
802 __ Dmb(InnerShareable, BarrierAll);
803 codegen->Store(type, value, mem_op);
804 if (is_volatile) {
805 __ Dmb(InnerShareable, BarrierReads);
806 }
807 }
808 } else {
809 codegen->Store(type, value, mem_op);
810 }
811
812 if (type == Primitive::kPrimNot) {
813 codegen->MarkGCCard(base, value);
814 }
815}
816
817void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
818 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, false, codegen_);
819}
820void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
821 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, true, codegen_);
822}
823void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
824 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, true, false, codegen_);
825}
826void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
827 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, false, codegen_);
828}
829void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
830 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, true, codegen_);
831}
832void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
833 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, true, false, codegen_);
834}
835void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
836 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, false, codegen_);
837}
838void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
839 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, true, codegen_);
840}
841void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
842 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, false, codegen_);
843}
844
845static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
846 LocationSummary* locations = new (arena) LocationSummary(invoke,
847 LocationSummary::kNoCall,
848 kIntrinsified);
849 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
850 locations->SetInAt(1, Location::RequiresRegister());
851 locations->SetInAt(2, Location::RequiresRegister());
852 locations->SetInAt(3, Location::RequiresRegister());
853 locations->SetInAt(4, Location::RequiresRegister());
854
855 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
856}
857
858static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM64* codegen) {
859 // TODO: Currently we use acquire-release load-stores in the CAS loop. One could reasonably write
860 // a version relying on simple exclusive load-stores and barriers instead.
861 static_assert(kUseAcquireRelease, "Non-acquire-release inlined CAS not implemented, yet.");
862
863 vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
864
865 Register out = WRegisterFrom(locations->Out()); // Boolean result.
866
867 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
868 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
869 Register expected = RegisterFrom(locations->InAt(3), type); // Expected.
870 Register value = RegisterFrom(locations->InAt(4), type); // Value.
871
872 // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
873 if (type == Primitive::kPrimNot) {
874 // Mark card for object assuming new value is stored.
875 codegen->MarkGCCard(base, value);
876 }
877
878 UseScratchRegisterScope temps(masm);
879 Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
880 Register tmp_value = temps.AcquireSameSizeAs(value); // Value in memory.
881
882 Register tmp_32 = tmp_value.W();
883
884 __ Add(tmp_ptr, base.X(), Operand(offset));
885
886 // do {
887 // tmp_value = [tmp_ptr] - expected;
888 // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
889 // result = tmp_value != 0;
890
891 vixl::Label loop_head, exit_loop;
892 __ Bind(&loop_head);
893
894 __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
895 __ Cmp(tmp_value, expected);
896 __ B(&exit_loop, ne);
897
898 __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
899 __ Cbnz(tmp_32, &loop_head);
900
901 __ Bind(&exit_loop);
902 __ Cset(out, eq);
903}
904
905void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
906 CreateIntIntIntIntIntToInt(arena_, invoke);
907}
908void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
909 CreateIntIntIntIntIntToInt(arena_, invoke);
910}
911void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
912 CreateIntIntIntIntIntToInt(arena_, invoke);
913}
914
915void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
916 GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
917}
918void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
919 GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_);
920}
921void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
922 GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
923}
924
925void IntrinsicLocationsBuilderARM64::VisitStringCharAt(HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800926 LocationSummary* locations = new (arena_) LocationSummary(invoke,
927 LocationSummary::kCallOnSlowPath,
928 kIntrinsified);
929 locations->SetInAt(0, Location::RequiresRegister());
930 locations->SetInAt(1, Location::RequiresRegister());
Nicolas Geoffray82f34492015-02-04 10:44:23 +0000931 // In case we need to go in the slow path, we can't have the output be the same
932 // as the input: the current liveness analysis considers the input to be live
933 // at the point of the call.
934 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800935}
936
937void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) {
938 vixl::MacroAssembler* masm = GetVIXLAssembler();
939 LocationSummary* locations = invoke->GetLocations();
940
941 // Location of reference to data array
942 const MemberOffset value_offset = mirror::String::ValueOffset();
943 // Location of count
944 const MemberOffset count_offset = mirror::String::CountOffset();
945 // Starting offset within data array
946 const MemberOffset offset_offset = mirror::String::OffsetOffset();
947 // Start of char data with array_
948 const MemberOffset data_offset = mirror::Array::DataOffset(sizeof(uint16_t));
949
950 Register obj = WRegisterFrom(locations->InAt(0)); // String object pointer.
951 Register idx = WRegisterFrom(locations->InAt(1)); // Index of character.
952 Register out = WRegisterFrom(locations->Out()); // Result character.
953
954 UseScratchRegisterScope temps(masm);
955 Register temp = temps.AcquireW();
956 Register array_temp = temps.AcquireW(); // We can trade this for worse scheduling.
957
958 // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
959 // the cost.
960 // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
961 // we will not optimize the code for constants (which would save a register).
962
963 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
964 codegen_->AddSlowPath(slow_path);
965
966 __ Ldr(temp, HeapOperand(obj, count_offset)); // temp = str.length.
967 codegen_->MaybeRecordImplicitNullCheck(invoke);
968 __ Cmp(idx, temp);
969 __ B(hs, slow_path->GetEntryLabel());
970
971 // Index computation.
972 __ Ldr(temp, HeapOperand(obj, offset_offset)); // temp := str.offset.
973 __ Ldr(array_temp, HeapOperand(obj, value_offset)); // array_temp := str.offset.
974 __ Add(temp, temp, idx);
975 DCHECK_EQ(data_offset.Int32Value() % 2, 0); // We'll compensate by shifting.
976 __ Add(temp, temp, Operand(data_offset.Int32Value() / 2));
977
978 // Load the value.
979 __ Ldrh(out, MemOperand(array_temp.X(), temp, UXTW, 1)); // out := array_temp[temp].
980
981 __ Bind(slow_path->GetExitLabel());
982}
983
984// Unimplemented intrinsics.
985
986#define UNIMPLEMENTED_INTRINSIC(Name) \
987void IntrinsicLocationsBuilderARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
988} \
989void IntrinsicCodeGeneratorARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
990}
991
992UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
993UNIMPLEMENTED_INTRINSIC(StringCompareTo)
994UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should
995UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here.
996UNIMPLEMENTED_INTRINSIC(StringIndexOf)
997UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)
998UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
999
1000} // namespace arm64
1001} // namespace art