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