intrinsics_arm64.cc revision a4f1220c1518074db18ca1044e9201492975750b
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;
49
50namespace {
51
52ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) {
53  return MemOperand(XRegisterFrom(location), offset);
54}
55
56}  // namespace
57
58vixl::MacroAssembler* IntrinsicCodeGeneratorARM64::GetVIXLAssembler() {
59  return codegen_->GetAssembler()->vixl_masm_;
60}
61
62ArenaAllocator* IntrinsicCodeGeneratorARM64::GetAllocator() {
63  return codegen_->GetGraph()->GetArena();
64}
65
66#define __ codegen->GetAssembler()->vixl_masm_->
67
68static void MoveFromReturnRegister(Location trg,
69                                   Primitive::Type type,
70                                   CodeGeneratorARM64* codegen) {
71  if (!trg.IsValid()) {
72    DCHECK(type == Primitive::kPrimVoid);
73    return;
74  }
75
76  DCHECK_NE(type, Primitive::kPrimVoid);
77
78  if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) {
79    Register trg_reg = RegisterFrom(trg, type);
80    Register res_reg = RegisterFrom(ARM64ReturnLocation(type), type);
81    __ Mov(trg_reg, res_reg, kDiscardForSameWReg);
82  } else {
83    FPRegister trg_reg = FPRegisterFrom(trg, type);
84    FPRegister res_reg = FPRegisterFrom(ARM64ReturnLocation(type), type);
85    __ Fmov(trg_reg, res_reg);
86  }
87}
88
89static void MoveArguments(HInvoke* invoke, CodeGeneratorARM64* codegen) {
90  InvokeDexCallingConventionVisitorARM64 calling_convention_visitor;
91  IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor);
92}
93
94// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
95// call. This will copy the arguments into the positions for a regular call.
96//
97// Note: The actual parameters are required to be in the locations given by the invoke's location
98//       summary. If an intrinsic modifies those locations before a slowpath call, they must be
99//       restored!
100class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 {
101 public:
102  explicit IntrinsicSlowPathARM64(HInvoke* invoke) : invoke_(invoke) { }
103
104  void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
105    CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
106    __ Bind(GetEntryLabel());
107
108    SaveLiveRegisters(codegen, invoke_->GetLocations());
109
110    MoveArguments(invoke_, codegen);
111
112    if (invoke_->IsInvokeStaticOrDirect()) {
113      codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
114                                          LocationFrom(kArtMethodRegister));
115    } else {
116      codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister));
117    }
118    codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
119
120    // Copy the result back to the expected output.
121    Location out = invoke_->GetLocations()->Out();
122    if (out.IsValid()) {
123      DCHECK(out.IsRegister());  // TODO: Replace this when we support output in memory.
124      DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
125      MoveFromReturnRegister(out, invoke_->GetType(), codegen);
126    }
127
128    RestoreLiveRegisters(codegen, invoke_->GetLocations());
129    __ B(GetExitLabel());
130  }
131
132  const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathARM64"; }
133
134 private:
135  // The instruction where this slow path is happening.
136  HInvoke* const invoke_;
137
138  DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM64);
139};
140
141#undef __
142
143bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) {
144  Dispatch(invoke);
145  LocationSummary* res = invoke->GetLocations();
146  if (res == nullptr) {
147    return false;
148  }
149  if (kEmitCompilerReadBarrier && res->CanCall()) {
150    // Generating an intrinsic for this HInvoke may produce an
151    // IntrinsicSlowPathARM64 slow path.  Currently this approach
152    // does not work when using read barriers, as the emitted
153    // calling sequence will make use of another slow path
154    // (ReadBarrierForRootSlowPathARM64 for HInvokeStaticOrDirect,
155    // ReadBarrierSlowPathARM64 for HInvokeVirtual).  So we bail
156    // out in this case.
157    //
158    // TODO: Find a way to have intrinsics work with read barriers.
159    invoke->SetLocations(nullptr);
160    return false;
161  }
162  return res->Intrinsified();
163}
164
165#define __ masm->
166
167static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
168  LocationSummary* locations = new (arena) LocationSummary(invoke,
169                                                           LocationSummary::kNoCall,
170                                                           kIntrinsified);
171  locations->SetInAt(0, Location::RequiresFpuRegister());
172  locations->SetOut(Location::RequiresRegister());
173}
174
175static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
176  LocationSummary* locations = new (arena) LocationSummary(invoke,
177                                                           LocationSummary::kNoCall,
178                                                           kIntrinsified);
179  locations->SetInAt(0, Location::RequiresRegister());
180  locations->SetOut(Location::RequiresFpuRegister());
181}
182
183static void MoveFPToInt(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
184  Location input = locations->InAt(0);
185  Location output = locations->Out();
186  __ Fmov(is64bit ? XRegisterFrom(output) : WRegisterFrom(output),
187          is64bit ? DRegisterFrom(input) : SRegisterFrom(input));
188}
189
190static void MoveIntToFP(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
191  Location input = locations->InAt(0);
192  Location output = locations->Out();
193  __ Fmov(is64bit ? DRegisterFrom(output) : SRegisterFrom(output),
194          is64bit ? XRegisterFrom(input) : WRegisterFrom(input));
195}
196
197void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
198  CreateFPToIntLocations(arena_, invoke);
199}
200void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
201  CreateIntToFPLocations(arena_, invoke);
202}
203
204void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
205  MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
206}
207void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
208  MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
209}
210
211void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
212  CreateFPToIntLocations(arena_, invoke);
213}
214void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
215  CreateIntToFPLocations(arena_, invoke);
216}
217
218void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
219  MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
220}
221void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
222  MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
223}
224
225static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
226  LocationSummary* locations = new (arena) LocationSummary(invoke,
227                                                           LocationSummary::kNoCall,
228                                                           kIntrinsified);
229  locations->SetInAt(0, Location::RequiresRegister());
230  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
231}
232
233static void GenReverseBytes(LocationSummary* locations,
234                            Primitive::Type type,
235                            vixl::MacroAssembler* masm) {
236  Location in = locations->InAt(0);
237  Location out = locations->Out();
238
239  switch (type) {
240    case Primitive::kPrimShort:
241      __ Rev16(WRegisterFrom(out), WRegisterFrom(in));
242      __ Sxth(WRegisterFrom(out), WRegisterFrom(out));
243      break;
244    case Primitive::kPrimInt:
245    case Primitive::kPrimLong:
246      __ Rev(RegisterFrom(out, type), RegisterFrom(in, type));
247      break;
248    default:
249      LOG(FATAL) << "Unexpected size for reverse-bytes: " << type;
250      UNREACHABLE();
251  }
252}
253
254void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
255  CreateIntToIntLocations(arena_, invoke);
256}
257
258void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
259  GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
260}
261
262void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) {
263  CreateIntToIntLocations(arena_, invoke);
264}
265
266void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) {
267  GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
268}
269
270void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) {
271  CreateIntToIntLocations(arena_, invoke);
272}
273
274void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
275  GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
276}
277
278static void GenNumberOfLeadingZeros(LocationSummary* locations,
279                                    Primitive::Type type,
280                                    vixl::MacroAssembler* masm) {
281  DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
282
283  Location in = locations->InAt(0);
284  Location out = locations->Out();
285
286  __ Clz(RegisterFrom(out, type), RegisterFrom(in, type));
287}
288
289void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
290  CreateIntToIntLocations(arena_, invoke);
291}
292
293void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
294  GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
295}
296
297void IntrinsicLocationsBuilderARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
298  CreateIntToIntLocations(arena_, invoke);
299}
300
301void IntrinsicCodeGeneratorARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
302  GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
303}
304
305static void GenNumberOfTrailingZeros(LocationSummary* locations,
306                                     Primitive::Type type,
307                                     vixl::MacroAssembler* masm) {
308  DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
309
310  Location in = locations->InAt(0);
311  Location out = locations->Out();
312
313  __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
314  __ Clz(RegisterFrom(out, type), RegisterFrom(out, type));
315}
316
317void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
318  CreateIntToIntLocations(arena_, invoke);
319}
320
321void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
322  GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
323}
324
325void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
326  CreateIntToIntLocations(arena_, invoke);
327}
328
329void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
330  GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
331}
332
333static void GenRotateRight(LocationSummary* locations,
334                           Primitive::Type type,
335                           vixl::MacroAssembler* masm) {
336  DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
337
338  Location in = locations->InAt(0);
339  Location out = locations->Out();
340  Operand rhs = OperandFrom(locations->InAt(1), type);
341
342  if (rhs.IsImmediate()) {
343    uint32_t shift = rhs.immediate() & (RegisterFrom(in, type).SizeInBits() - 1);
344    __ Ror(RegisterFrom(out, type),
345           RegisterFrom(in, type),
346           shift);
347  } else {
348    DCHECK(rhs.shift() == vixl::LSL && rhs.shift_amount() == 0);
349    __ Ror(RegisterFrom(out, type),
350           RegisterFrom(in, type),
351           rhs.reg());
352  }
353}
354
355void IntrinsicLocationsBuilderARM64::VisitIntegerRotateRight(HInvoke* invoke) {
356  LocationSummary* locations = new (arena_) LocationSummary(invoke,
357                                                           LocationSummary::kNoCall,
358                                                           kIntrinsified);
359  locations->SetInAt(0, Location::RequiresRegister());
360  locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
361  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
362}
363
364void IntrinsicCodeGeneratorARM64::VisitIntegerRotateRight(HInvoke* invoke) {
365  GenRotateRight(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
366}
367
368void IntrinsicLocationsBuilderARM64::VisitLongRotateRight(HInvoke* invoke) {
369  LocationSummary* locations = new (arena_) LocationSummary(invoke,
370                                                           LocationSummary::kNoCall,
371                                                           kIntrinsified);
372  locations->SetInAt(0, Location::RequiresRegister());
373  locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
374  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
375}
376
377void IntrinsicCodeGeneratorARM64::VisitLongRotateRight(HInvoke* invoke) {
378  GenRotateRight(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
379}
380
381static void GenRotateLeft(LocationSummary* locations,
382                           Primitive::Type type,
383                           vixl::MacroAssembler* masm) {
384  DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
385
386  Location in = locations->InAt(0);
387  Location out = locations->Out();
388  Operand rhs = OperandFrom(locations->InAt(1), type);
389
390  if (rhs.IsImmediate()) {
391    uint32_t regsize = RegisterFrom(in, type).SizeInBits();
392    uint32_t shift = (regsize - rhs.immediate()) & (regsize - 1);
393    __ Ror(RegisterFrom(out, type), RegisterFrom(in, type), shift);
394  } else {
395    DCHECK(rhs.shift() == vixl::LSL && rhs.shift_amount() == 0);
396    __ Neg(RegisterFrom(out, type),
397           Operand(RegisterFrom(locations->InAt(1), type)));
398    __ Ror(RegisterFrom(out, type),
399           RegisterFrom(in, type),
400           RegisterFrom(out, type));
401  }
402}
403
404void IntrinsicLocationsBuilderARM64::VisitIntegerRotateLeft(HInvoke* invoke) {
405  LocationSummary* locations = new (arena_) LocationSummary(invoke,
406                                                           LocationSummary::kNoCall,
407                                                           kIntrinsified);
408  locations->SetInAt(0, Location::RequiresRegister());
409  locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
410  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
411}
412
413void IntrinsicCodeGeneratorARM64::VisitIntegerRotateLeft(HInvoke* invoke) {
414  GenRotateLeft(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
415}
416
417void IntrinsicLocationsBuilderARM64::VisitLongRotateLeft(HInvoke* invoke) {
418  LocationSummary* locations = new (arena_) LocationSummary(invoke,
419                                                           LocationSummary::kNoCall,
420                                                           kIntrinsified);
421  locations->SetInAt(0, Location::RequiresRegister());
422  locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
423  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
424}
425
426void IntrinsicCodeGeneratorARM64::VisitLongRotateLeft(HInvoke* invoke) {
427  GenRotateLeft(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
428}
429
430static void GenReverse(LocationSummary* locations,
431                       Primitive::Type type,
432                       vixl::MacroAssembler* masm) {
433  DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
434
435  Location in = locations->InAt(0);
436  Location out = locations->Out();
437
438  __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
439}
440
441void IntrinsicLocationsBuilderARM64::VisitIntegerReverse(HInvoke* invoke) {
442  CreateIntToIntLocations(arena_, invoke);
443}
444
445void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) {
446  GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
447}
448
449void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) {
450  CreateIntToIntLocations(arena_, invoke);
451}
452
453void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
454  GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
455}
456
457static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
458  LocationSummary* locations = new (arena) LocationSummary(invoke,
459                                                           LocationSummary::kNoCall,
460                                                           kIntrinsified);
461  locations->SetInAt(0, Location::RequiresFpuRegister());
462  locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
463}
464
465static void MathAbsFP(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
466  Location in = locations->InAt(0);
467  Location out = locations->Out();
468
469  FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in);
470  FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out);
471
472  __ Fabs(out_reg, in_reg);
473}
474
475void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) {
476  CreateFPToFPLocations(arena_, invoke);
477}
478
479void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) {
480  MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
481}
482
483void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) {
484  CreateFPToFPLocations(arena_, invoke);
485}
486
487void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) {
488  MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
489}
490
491static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
492  LocationSummary* locations = new (arena) LocationSummary(invoke,
493                                                           LocationSummary::kNoCall,
494                                                           kIntrinsified);
495  locations->SetInAt(0, Location::RequiresRegister());
496  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
497}
498
499static void GenAbsInteger(LocationSummary* locations,
500                          bool is64bit,
501                          vixl::MacroAssembler* masm) {
502  Location in = locations->InAt(0);
503  Location output = locations->Out();
504
505  Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in);
506  Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output);
507
508  __ Cmp(in_reg, Operand(0));
509  __ Cneg(out_reg, in_reg, lt);
510}
511
512void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
513  CreateIntToInt(arena_, invoke);
514}
515
516void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
517  GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
518}
519
520void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
521  CreateIntToInt(arena_, invoke);
522}
523
524void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
525  GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
526}
527
528static void GenMinMaxFP(LocationSummary* locations,
529                        bool is_min,
530                        bool is_double,
531                        vixl::MacroAssembler* masm) {
532  Location op1 = locations->InAt(0);
533  Location op2 = locations->InAt(1);
534  Location out = locations->Out();
535
536  FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1);
537  FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2);
538  FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out);
539  if (is_min) {
540    __ Fmin(out_reg, op1_reg, op2_reg);
541  } else {
542    __ Fmax(out_reg, op1_reg, op2_reg);
543  }
544}
545
546static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
547  LocationSummary* locations = new (arena) LocationSummary(invoke,
548                                                           LocationSummary::kNoCall,
549                                                           kIntrinsified);
550  locations->SetInAt(0, Location::RequiresFpuRegister());
551  locations->SetInAt(1, Location::RequiresFpuRegister());
552  locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
553}
554
555void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
556  CreateFPFPToFPLocations(arena_, invoke);
557}
558
559void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
560  GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetVIXLAssembler());
561}
562
563void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
564  CreateFPFPToFPLocations(arena_, invoke);
565}
566
567void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
568  GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetVIXLAssembler());
569}
570
571void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
572  CreateFPFPToFPLocations(arena_, invoke);
573}
574
575void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
576  GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetVIXLAssembler());
577}
578
579void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
580  CreateFPFPToFPLocations(arena_, invoke);
581}
582
583void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
584  GenMinMaxFP(
585      invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetVIXLAssembler());
586}
587
588static void GenMinMax(LocationSummary* locations,
589                      bool is_min,
590                      bool is_long,
591                      vixl::MacroAssembler* masm) {
592  Location op1 = locations->InAt(0);
593  Location op2 = locations->InAt(1);
594  Location out = locations->Out();
595
596  Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
597  Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
598  Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
599
600  __ Cmp(op1_reg, op2_reg);
601  __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
602}
603
604static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
605  LocationSummary* locations = new (arena) LocationSummary(invoke,
606                                                           LocationSummary::kNoCall,
607                                                           kIntrinsified);
608  locations->SetInAt(0, Location::RequiresRegister());
609  locations->SetInAt(1, Location::RequiresRegister());
610  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
611}
612
613void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
614  CreateIntIntToIntLocations(arena_, invoke);
615}
616
617void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) {
618  GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetVIXLAssembler());
619}
620
621void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) {
622  CreateIntIntToIntLocations(arena_, invoke);
623}
624
625void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) {
626  GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetVIXLAssembler());
627}
628
629void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) {
630  CreateIntIntToIntLocations(arena_, invoke);
631}
632
633void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) {
634  GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetVIXLAssembler());
635}
636
637void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) {
638  CreateIntIntToIntLocations(arena_, invoke);
639}
640
641void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) {
642  GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler());
643}
644
645void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) {
646  CreateFPToFPLocations(arena_, invoke);
647}
648
649void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) {
650  LocationSummary* locations = invoke->GetLocations();
651  vixl::MacroAssembler* masm = GetVIXLAssembler();
652  __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
653}
654
655void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) {
656  CreateFPToFPLocations(arena_, invoke);
657}
658
659void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) {
660  LocationSummary* locations = invoke->GetLocations();
661  vixl::MacroAssembler* masm = GetVIXLAssembler();
662  __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
663}
664
665void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) {
666  CreateFPToFPLocations(arena_, invoke);
667}
668
669void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) {
670  LocationSummary* locations = invoke->GetLocations();
671  vixl::MacroAssembler* masm = GetVIXLAssembler();
672  __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
673}
674
675void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) {
676  CreateFPToFPLocations(arena_, invoke);
677}
678
679void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) {
680  LocationSummary* locations = invoke->GetLocations();
681  vixl::MacroAssembler* masm = GetVIXLAssembler();
682  __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
683}
684
685static void CreateFPToIntPlusTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
686  LocationSummary* locations = new (arena) LocationSummary(invoke,
687                                                           LocationSummary::kNoCall,
688                                                           kIntrinsified);
689  locations->SetInAt(0, Location::RequiresFpuRegister());
690  locations->SetOut(Location::RequiresRegister());
691}
692
693static void GenMathRound(LocationSummary* locations,
694                         bool is_double,
695                         vixl::MacroAssembler* masm) {
696  FPRegister in_reg = is_double ?
697      DRegisterFrom(locations->InAt(0)) : SRegisterFrom(locations->InAt(0));
698  Register out_reg = is_double ?
699      XRegisterFrom(locations->Out()) : WRegisterFrom(locations->Out());
700  UseScratchRegisterScope temps(masm);
701  FPRegister temp1_reg = temps.AcquireSameSizeAs(in_reg);
702
703  // 0.5 can be encoded as an immediate, so use fmov.
704  if (is_double) {
705    __ Fmov(temp1_reg, static_cast<double>(0.5));
706  } else {
707    __ Fmov(temp1_reg, static_cast<float>(0.5));
708  }
709  __ Fadd(temp1_reg, in_reg, temp1_reg);
710  __ Fcvtms(out_reg, temp1_reg);
711}
712
713void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) {
714  CreateFPToIntPlusTempLocations(arena_, invoke);
715}
716
717void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) {
718  GenMathRound(invoke->GetLocations(), /* is_double */ true, GetVIXLAssembler());
719}
720
721void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) {
722  CreateFPToIntPlusTempLocations(arena_, invoke);
723}
724
725void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) {
726  GenMathRound(invoke->GetLocations(), /* is_double */ false, GetVIXLAssembler());
727}
728
729void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) {
730  CreateIntToIntLocations(arena_, invoke);
731}
732
733void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) {
734  vixl::MacroAssembler* masm = GetVIXLAssembler();
735  __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()),
736          AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
737}
738
739void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
740  CreateIntToIntLocations(arena_, invoke);
741}
742
743void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
744  vixl::MacroAssembler* masm = GetVIXLAssembler();
745  __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()),
746         AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
747}
748
749void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
750  CreateIntToIntLocations(arena_, invoke);
751}
752
753void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
754  vixl::MacroAssembler* masm = GetVIXLAssembler();
755  __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()),
756         AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
757}
758
759void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
760  CreateIntToIntLocations(arena_, invoke);
761}
762
763void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
764  vixl::MacroAssembler* masm = GetVIXLAssembler();
765  __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()),
766           AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
767}
768
769static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
770  LocationSummary* locations = new (arena) LocationSummary(invoke,
771                                                           LocationSummary::kNoCall,
772                                                           kIntrinsified);
773  locations->SetInAt(0, Location::RequiresRegister());
774  locations->SetInAt(1, Location::RequiresRegister());
775}
776
777void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) {
778  CreateIntIntToVoidLocations(arena_, invoke);
779}
780
781void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) {
782  vixl::MacroAssembler* masm = GetVIXLAssembler();
783  __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)),
784          AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
785}
786
787void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
788  CreateIntIntToVoidLocations(arena_, invoke);
789}
790
791void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
792  vixl::MacroAssembler* masm = GetVIXLAssembler();
793  __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)),
794         AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
795}
796
797void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
798  CreateIntIntToVoidLocations(arena_, invoke);
799}
800
801void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
802  vixl::MacroAssembler* masm = GetVIXLAssembler();
803  __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)),
804         AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
805}
806
807void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
808  CreateIntIntToVoidLocations(arena_, invoke);
809}
810
811void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
812  vixl::MacroAssembler* masm = GetVIXLAssembler();
813  __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)),
814          AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
815}
816
817void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) {
818  LocationSummary* locations = new (arena_) LocationSummary(invoke,
819                                                            LocationSummary::kNoCall,
820                                                            kIntrinsified);
821  locations->SetOut(Location::RequiresRegister());
822}
823
824void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) {
825  codegen_->Load(Primitive::kPrimNot, WRegisterFrom(invoke->GetLocations()->Out()),
826                 MemOperand(tr, Thread::PeerOffset<8>().Int32Value()));
827}
828
829static void GenUnsafeGet(HInvoke* invoke,
830                         Primitive::Type type,
831                         bool is_volatile,
832                         CodeGeneratorARM64* codegen) {
833  LocationSummary* locations = invoke->GetLocations();
834  DCHECK((type == Primitive::kPrimInt) ||
835         (type == Primitive::kPrimLong) ||
836         (type == Primitive::kPrimNot));
837  vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
838  Location base_loc = locations->InAt(1);
839  Register base = WRegisterFrom(base_loc);      // Object pointer.
840  Location offset_loc = locations->InAt(2);
841  Register offset = XRegisterFrom(offset_loc);  // Long offset.
842  Location trg_loc = locations->Out();
843  Register trg = RegisterFrom(trg_loc, type);
844  bool use_acquire_release = codegen->GetInstructionSetFeatures().PreferAcquireRelease();
845
846  MemOperand mem_op(base.X(), offset);
847  if (is_volatile) {
848    if (use_acquire_release) {
849      codegen->LoadAcquire(invoke, trg, mem_op);
850    } else {
851      codegen->Load(type, trg, mem_op);
852      __ Dmb(InnerShareable, BarrierReads);
853    }
854  } else {
855    codegen->Load(type, trg, mem_op);
856  }
857
858  if (type == Primitive::kPrimNot) {
859    DCHECK(trg.IsW());
860    codegen->MaybeGenerateReadBarrier(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
861  }
862}
863
864static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
865  bool can_call = kEmitCompilerReadBarrier &&
866      (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
867       invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
868  LocationSummary* locations = new (arena) LocationSummary(invoke,
869                                                           can_call ?
870                                                               LocationSummary::kCallOnSlowPath :
871                                                               LocationSummary::kNoCall,
872                                                           kIntrinsified);
873  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
874  locations->SetInAt(1, Location::RequiresRegister());
875  locations->SetInAt(2, Location::RequiresRegister());
876  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
877}
878
879void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
880  CreateIntIntIntToIntLocations(arena_, invoke);
881}
882void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
883  CreateIntIntIntToIntLocations(arena_, invoke);
884}
885void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
886  CreateIntIntIntToIntLocations(arena_, invoke);
887}
888void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
889  CreateIntIntIntToIntLocations(arena_, invoke);
890}
891void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
892  CreateIntIntIntToIntLocations(arena_, invoke);
893}
894void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
895  CreateIntIntIntToIntLocations(arena_, invoke);
896}
897
898void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
899  GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
900}
901void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
902  GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
903}
904void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
905  GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
906}
907void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
908  GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
909}
910void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
911  GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
912}
913void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
914  GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
915}
916
917static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
918  LocationSummary* locations = new (arena) LocationSummary(invoke,
919                                                           LocationSummary::kNoCall,
920                                                           kIntrinsified);
921  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
922  locations->SetInAt(1, Location::RequiresRegister());
923  locations->SetInAt(2, Location::RequiresRegister());
924  locations->SetInAt(3, Location::RequiresRegister());
925}
926
927void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
928  CreateIntIntIntIntToVoid(arena_, invoke);
929}
930void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
931  CreateIntIntIntIntToVoid(arena_, invoke);
932}
933void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
934  CreateIntIntIntIntToVoid(arena_, invoke);
935}
936void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
937  CreateIntIntIntIntToVoid(arena_, invoke);
938}
939void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
940  CreateIntIntIntIntToVoid(arena_, invoke);
941}
942void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
943  CreateIntIntIntIntToVoid(arena_, invoke);
944}
945void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
946  CreateIntIntIntIntToVoid(arena_, invoke);
947}
948void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
949  CreateIntIntIntIntToVoid(arena_, invoke);
950}
951void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
952  CreateIntIntIntIntToVoid(arena_, invoke);
953}
954
955static void GenUnsafePut(LocationSummary* locations,
956                         Primitive::Type type,
957                         bool is_volatile,
958                         bool is_ordered,
959                         CodeGeneratorARM64* codegen) {
960  vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
961
962  Register base = WRegisterFrom(locations->InAt(1));    // Object pointer.
963  Register offset = XRegisterFrom(locations->InAt(2));  // Long offset.
964  Register value = RegisterFrom(locations->InAt(3), type);
965  Register source = value;
966  bool use_acquire_release = codegen->GetInstructionSetFeatures().PreferAcquireRelease();
967
968  MemOperand mem_op(base.X(), offset);
969
970  {
971    // We use a block to end the scratch scope before the write barrier, thus
972    // freeing the temporary registers so they can be used in `MarkGCCard`.
973    UseScratchRegisterScope temps(masm);
974
975    if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
976      DCHECK(value.IsW());
977      Register temp = temps.AcquireW();
978      __ Mov(temp.W(), value.W());
979      codegen->GetAssembler()->PoisonHeapReference(temp.W());
980      source = temp;
981    }
982
983    if (is_volatile || is_ordered) {
984      if (use_acquire_release) {
985        codegen->StoreRelease(type, source, mem_op);
986      } else {
987        __ Dmb(InnerShareable, BarrierAll);
988        codegen->Store(type, source, mem_op);
989        if (is_volatile) {
990          __ Dmb(InnerShareable, BarrierReads);
991        }
992      }
993    } else {
994      codegen->Store(type, source, mem_op);
995    }
996  }
997
998  if (type == Primitive::kPrimNot) {
999    bool value_can_be_null = true;  // TODO: Worth finding out this information?
1000    codegen->MarkGCCard(base, value, value_can_be_null);
1001  }
1002}
1003
1004void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
1005  GenUnsafePut(invoke->GetLocations(),
1006               Primitive::kPrimInt,
1007               /* is_volatile */ false,
1008               /* is_ordered */ false,
1009               codegen_);
1010}
1011void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
1012  GenUnsafePut(invoke->GetLocations(),
1013               Primitive::kPrimInt,
1014               /* is_volatile */ false,
1015               /* is_ordered */ true,
1016               codegen_);
1017}
1018void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
1019  GenUnsafePut(invoke->GetLocations(),
1020               Primitive::kPrimInt,
1021               /* is_volatile */ true,
1022               /* is_ordered */ false,
1023               codegen_);
1024}
1025void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
1026  GenUnsafePut(invoke->GetLocations(),
1027               Primitive::kPrimNot,
1028               /* is_volatile */ false,
1029               /* is_ordered */ false,
1030               codegen_);
1031}
1032void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
1033  GenUnsafePut(invoke->GetLocations(),
1034               Primitive::kPrimNot,
1035               /* is_volatile */ false,
1036               /* is_ordered */ true,
1037               codegen_);
1038}
1039void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
1040  GenUnsafePut(invoke->GetLocations(),
1041               Primitive::kPrimNot,
1042               /* is_volatile */ true,
1043               /* is_ordered */ false,
1044               codegen_);
1045}
1046void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
1047  GenUnsafePut(invoke->GetLocations(),
1048               Primitive::kPrimLong,
1049               /* is_volatile */ false,
1050               /* is_ordered */ false,
1051               codegen_);
1052}
1053void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
1054  GenUnsafePut(invoke->GetLocations(),
1055               Primitive::kPrimLong,
1056               /* is_volatile */ false,
1057               /* is_ordered */ true,
1058               codegen_);
1059}
1060void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
1061  GenUnsafePut(invoke->GetLocations(),
1062               Primitive::kPrimLong,
1063               /* is_volatile */ true,
1064               /* is_ordered */ false,
1065               codegen_);
1066}
1067
1068static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
1069  LocationSummary* locations = new (arena) LocationSummary(invoke,
1070                                                           LocationSummary::kNoCall,
1071                                                           kIntrinsified);
1072  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
1073  locations->SetInAt(1, Location::RequiresRegister());
1074  locations->SetInAt(2, Location::RequiresRegister());
1075  locations->SetInAt(3, Location::RequiresRegister());
1076  locations->SetInAt(4, Location::RequiresRegister());
1077
1078  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1079}
1080
1081static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM64* codegen) {
1082  bool use_acquire_release = codegen->GetInstructionSetFeatures().PreferAcquireRelease();
1083  vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
1084
1085  Register out = WRegisterFrom(locations->Out());                  // Boolean result.
1086
1087  Register base = WRegisterFrom(locations->InAt(1));               // Object pointer.
1088  Register offset = XRegisterFrom(locations->InAt(2));             // Long offset.
1089  Register expected = RegisterFrom(locations->InAt(3), type);      // Expected.
1090  Register value = RegisterFrom(locations->InAt(4), type);         // Value.
1091
1092  // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
1093  if (type == Primitive::kPrimNot) {
1094    // Mark card for object assuming new value is stored.
1095    bool value_can_be_null = true;  // TODO: Worth finding out this information?
1096    codegen->MarkGCCard(base, value, value_can_be_null);
1097  }
1098
1099  UseScratchRegisterScope temps(masm);
1100  Register tmp_ptr = temps.AcquireX();                             // Pointer to actual memory.
1101  Register tmp_value = temps.AcquireSameSizeAs(value);             // Value in memory.
1102
1103  Register tmp_32 = tmp_value.W();
1104
1105  __ Add(tmp_ptr, base.X(), Operand(offset));
1106
1107  if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1108    codegen->GetAssembler()->PoisonHeapReference(expected);
1109    codegen->GetAssembler()->PoisonHeapReference(value);
1110  }
1111
1112  // do {
1113  //   tmp_value = [tmp_ptr] - expected;
1114  // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1115  // result = tmp_value != 0;
1116
1117  vixl::Label loop_head, exit_loop;
1118  if (use_acquire_release) {
1119    __ Bind(&loop_head);
1120    __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
1121    // TODO: Do we need a read barrier here when `type == Primitive::kPrimNot`?
1122    // Note that this code is not (yet) used when read barriers are
1123    // enabled (see IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject).
1124    __ Cmp(tmp_value, expected);
1125    __ B(&exit_loop, ne);
1126    __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
1127    __ Cbnz(tmp_32, &loop_head);
1128  } else {
1129    __ Dmb(InnerShareable, BarrierWrites);
1130    __ Bind(&loop_head);
1131    __ Ldxr(tmp_value, MemOperand(tmp_ptr));
1132    // TODO: Do we need a read barrier here when `type == Primitive::kPrimNot`?
1133    // Note that this code is not (yet) used when read barriers are
1134    // enabled (see IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject).
1135    __ Cmp(tmp_value, expected);
1136    __ B(&exit_loop, ne);
1137    __ Stxr(tmp_32, value, MemOperand(tmp_ptr));
1138    __ Cbnz(tmp_32, &loop_head);
1139    __ Dmb(InnerShareable, BarrierAll);
1140  }
1141  __ Bind(&exit_loop);
1142  __ Cset(out, eq);
1143
1144  if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1145    codegen->GetAssembler()->UnpoisonHeapReference(value);
1146    codegen->GetAssembler()->UnpoisonHeapReference(expected);
1147  }
1148}
1149
1150void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
1151  CreateIntIntIntIntIntToInt(arena_, invoke);
1152}
1153void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
1154  CreateIntIntIntIntIntToInt(arena_, invoke);
1155}
1156void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
1157  // The UnsafeCASObject intrinsic does not always work when heap
1158  // poisoning is enabled (it breaks run-test 004-UnsafeTest); turn it
1159  // off temporarily as a quick fix.
1160  //
1161  // TODO(rpl): Fix it and turn it back on.
1162  //
1163  // TODO(rpl): Also, we should investigate whether we need a read
1164  // barrier in the generated code.
1165  if (kPoisonHeapReferences) {
1166    return;
1167  }
1168
1169  CreateIntIntIntIntIntToInt(arena_, invoke);
1170}
1171
1172void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
1173  GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
1174}
1175void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
1176  GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_);
1177}
1178void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
1179  GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
1180}
1181
1182void IntrinsicLocationsBuilderARM64::VisitStringCharAt(HInvoke* invoke) {
1183  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1184                                                            LocationSummary::kCallOnSlowPath,
1185                                                            kIntrinsified);
1186  locations->SetInAt(0, Location::RequiresRegister());
1187  locations->SetInAt(1, Location::RequiresRegister());
1188  // In case we need to go in the slow path, we can't have the output be the same
1189  // as the input: the current liveness analysis considers the input to be live
1190  // at the point of the call.
1191  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1192}
1193
1194void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) {
1195  vixl::MacroAssembler* masm = GetVIXLAssembler();
1196  LocationSummary* locations = invoke->GetLocations();
1197
1198  // Location of reference to data array
1199  const MemberOffset value_offset = mirror::String::ValueOffset();
1200  // Location of count
1201  const MemberOffset count_offset = mirror::String::CountOffset();
1202
1203  Register obj = WRegisterFrom(locations->InAt(0));  // String object pointer.
1204  Register idx = WRegisterFrom(locations->InAt(1));  // Index of character.
1205  Register out = WRegisterFrom(locations->Out());    // Result character.
1206
1207  UseScratchRegisterScope temps(masm);
1208  Register temp = temps.AcquireW();
1209  Register array_temp = temps.AcquireW();            // We can trade this for worse scheduling.
1210
1211  // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
1212  //       the cost.
1213  // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
1214  //       we will not optimize the code for constants (which would save a register).
1215
1216  SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1217  codegen_->AddSlowPath(slow_path);
1218
1219  __ Ldr(temp, HeapOperand(obj, count_offset));          // temp = str.length.
1220  codegen_->MaybeRecordImplicitNullCheck(invoke);
1221  __ Cmp(idx, temp);
1222  __ B(hs, slow_path->GetEntryLabel());
1223
1224  __ Add(array_temp, obj, Operand(value_offset.Int32Value()));  // array_temp := str.value.
1225
1226  // Load the value.
1227  __ Ldrh(out, MemOperand(array_temp.X(), idx, UXTW, 1));  // out := array_temp[idx].
1228
1229  __ Bind(slow_path->GetExitLabel());
1230}
1231
1232void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
1233  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1234                                                            LocationSummary::kCall,
1235                                                            kIntrinsified);
1236  InvokeRuntimeCallingConvention calling_convention;
1237  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1238  locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1239  locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1240}
1241
1242void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
1243  vixl::MacroAssembler* masm = GetVIXLAssembler();
1244  LocationSummary* locations = invoke->GetLocations();
1245
1246  // Note that the null check must have been done earlier.
1247  DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1248
1249  Register argument = WRegisterFrom(locations->InAt(1));
1250  __ Cmp(argument, 0);
1251  SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1252  codegen_->AddSlowPath(slow_path);
1253  __ B(eq, slow_path->GetEntryLabel());
1254
1255  __ Ldr(
1256      lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pStringCompareTo).Int32Value()));
1257  __ Blr(lr);
1258  __ Bind(slow_path->GetExitLabel());
1259}
1260
1261void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
1262  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1263                                                            LocationSummary::kNoCall,
1264                                                            kIntrinsified);
1265  locations->SetInAt(0, Location::RequiresRegister());
1266  locations->SetInAt(1, Location::RequiresRegister());
1267  // Temporary registers to store lengths of strings and for calculations.
1268  locations->AddTemp(Location::RequiresRegister());
1269  locations->AddTemp(Location::RequiresRegister());
1270
1271  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1272}
1273
1274void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
1275  vixl::MacroAssembler* masm = GetVIXLAssembler();
1276  LocationSummary* locations = invoke->GetLocations();
1277
1278  Register str = WRegisterFrom(locations->InAt(0));
1279  Register arg = WRegisterFrom(locations->InAt(1));
1280  Register out = XRegisterFrom(locations->Out());
1281
1282  UseScratchRegisterScope scratch_scope(masm);
1283  Register temp = scratch_scope.AcquireW();
1284  Register temp1 = WRegisterFrom(locations->GetTemp(0));
1285  Register temp2 = WRegisterFrom(locations->GetTemp(1));
1286
1287  vixl::Label loop;
1288  vixl::Label end;
1289  vixl::Label return_true;
1290  vixl::Label return_false;
1291
1292  // Get offsets of count, value, and class fields within a string object.
1293  const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1294  const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1295  const int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1296
1297  // Note that the null check must have been done earlier.
1298  DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1299
1300  // Check if input is null, return false if it is.
1301  __ Cbz(arg, &return_false);
1302
1303  // Reference equality check, return true if same reference.
1304  __ Cmp(str, arg);
1305  __ B(&return_true, eq);
1306
1307  // Instanceof check for the argument by comparing class fields.
1308  // All string objects must have the same type since String cannot be subclassed.
1309  // Receiver must be a string object, so its class field is equal to all strings' class fields.
1310  // If the argument is a string object, its class field must be equal to receiver's class field.
1311  __ Ldr(temp, MemOperand(str.X(), class_offset));
1312  __ Ldr(temp1, MemOperand(arg.X(), class_offset));
1313  __ Cmp(temp, temp1);
1314  __ B(&return_false, ne);
1315
1316  // Load lengths of this and argument strings.
1317  __ Ldr(temp, MemOperand(str.X(), count_offset));
1318  __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1319  // Check if lengths are equal, return false if they're not.
1320  __ Cmp(temp, temp1);
1321  __ B(&return_false, ne);
1322  // Store offset of string value in preparation for comparison loop
1323  __ Mov(temp1, value_offset);
1324  // Return true if both strings are empty.
1325  __ Cbz(temp, &return_true);
1326
1327  // Assertions that must hold in order to compare strings 4 characters at a time.
1328  DCHECK_ALIGNED(value_offset, 8);
1329  static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1330
1331  temp1 = temp1.X();
1332  temp2 = temp2.X();
1333
1334  // Loop to compare strings 4 characters at a time starting at the beginning of the string.
1335  // Ok to do this because strings are zero-padded to be 8-byte aligned.
1336  __ Bind(&loop);
1337  __ Ldr(out, MemOperand(str.X(), temp1));
1338  __ Ldr(temp2, MemOperand(arg.X(), temp1));
1339  __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
1340  __ Cmp(out, temp2);
1341  __ B(&return_false, ne);
1342  __ Sub(temp, temp, Operand(4), SetFlags);
1343  __ B(&loop, gt);
1344
1345  // Return true and exit the function.
1346  // If loop does not result in returning false, we return true.
1347  __ Bind(&return_true);
1348  __ Mov(out, 1);
1349  __ B(&end);
1350
1351  // Return false and exit the function.
1352  __ Bind(&return_false);
1353  __ Mov(out, 0);
1354  __ Bind(&end);
1355}
1356
1357static void GenerateVisitStringIndexOf(HInvoke* invoke,
1358                                       vixl::MacroAssembler* masm,
1359                                       CodeGeneratorARM64* codegen,
1360                                       ArenaAllocator* allocator,
1361                                       bool start_at_zero) {
1362  LocationSummary* locations = invoke->GetLocations();
1363  Register tmp_reg = WRegisterFrom(locations->GetTemp(0));
1364
1365  // Note that the null check must have been done earlier.
1366  DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1367
1368  // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
1369  // or directly dispatch if we have a constant.
1370  SlowPathCodeARM64* slow_path = nullptr;
1371  if (invoke->InputAt(1)->IsIntConstant()) {
1372    if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > 0xFFFFU) {
1373      // Always needs the slow-path. We could directly dispatch to it, but this case should be
1374      // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1375      slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1376      codegen->AddSlowPath(slow_path);
1377      __ B(slow_path->GetEntryLabel());
1378      __ Bind(slow_path->GetExitLabel());
1379      return;
1380    }
1381  } else {
1382    Register char_reg = WRegisterFrom(locations->InAt(1));
1383    __ Mov(tmp_reg, 0xFFFF);
1384    __ Cmp(char_reg, Operand(tmp_reg));
1385    slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1386    codegen->AddSlowPath(slow_path);
1387    __ B(hi, slow_path->GetEntryLabel());
1388  }
1389
1390  if (start_at_zero) {
1391    // Start-index = 0.
1392    __ Mov(tmp_reg, 0);
1393  }
1394
1395  __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pIndexOf).Int32Value()));
1396  __ Blr(lr);
1397
1398  if (slow_path != nullptr) {
1399    __ Bind(slow_path->GetExitLabel());
1400  }
1401}
1402
1403void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) {
1404  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1405                                                            LocationSummary::kCall,
1406                                                            kIntrinsified);
1407  // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1408  // best to align the inputs accordingly.
1409  InvokeRuntimeCallingConvention calling_convention;
1410  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1411  locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1412  locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1413
1414  // Need a temp for slow-path codepoint compare, and need to send start_index=0.
1415  locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1416}
1417
1418void IntrinsicCodeGeneratorARM64::VisitStringIndexOf(HInvoke* invoke) {
1419  GenerateVisitStringIndexOf(
1420      invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
1421}
1422
1423void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
1424  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1425                                                            LocationSummary::kCall,
1426                                                            kIntrinsified);
1427  // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1428  // best to align the inputs accordingly.
1429  InvokeRuntimeCallingConvention calling_convention;
1430  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1431  locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1432  locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1433  locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1434
1435  // Need a temp for slow-path codepoint compare.
1436  locations->AddTemp(Location::RequiresRegister());
1437}
1438
1439void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
1440  GenerateVisitStringIndexOf(
1441      invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
1442}
1443
1444void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
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->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1453  locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1454}
1455
1456void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1457  vixl::MacroAssembler* masm = GetVIXLAssembler();
1458  LocationSummary* locations = invoke->GetLocations();
1459
1460  Register byte_array = WRegisterFrom(locations->InAt(0));
1461  __ Cmp(byte_array, 0);
1462  SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1463  codegen_->AddSlowPath(slow_path);
1464  __ B(eq, slow_path->GetEntryLabel());
1465
1466  __ Ldr(lr,
1467      MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromBytes).Int32Value()));
1468  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1469  __ Blr(lr);
1470  __ Bind(slow_path->GetExitLabel());
1471}
1472
1473void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
1474  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1475                                                            LocationSummary::kCall,
1476                                                            kIntrinsified);
1477  InvokeRuntimeCallingConvention calling_convention;
1478  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1479  locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1480  locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1481  locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1482}
1483
1484void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
1485  vixl::MacroAssembler* masm = GetVIXLAssembler();
1486
1487  __ Ldr(lr,
1488      MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromChars).Int32Value()));
1489  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1490  __ Blr(lr);
1491}
1492
1493void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) {
1494  // The inputs plus one temp.
1495  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1496                                                            LocationSummary::kCall,
1497                                                            kIntrinsified);
1498  InvokeRuntimeCallingConvention calling_convention;
1499  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1500  locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1501  locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1502  locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1503}
1504
1505void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) {
1506  vixl::MacroAssembler* masm = GetVIXLAssembler();
1507  LocationSummary* locations = invoke->GetLocations();
1508
1509  Register string_to_copy = WRegisterFrom(locations->InAt(0));
1510  __ Cmp(string_to_copy, 0);
1511  SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1512  codegen_->AddSlowPath(slow_path);
1513  __ B(eq, slow_path->GetEntryLabel());
1514
1515  __ Ldr(lr,
1516      MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromString).Int32Value()));
1517  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1518  __ Blr(lr);
1519  __ Bind(slow_path->GetExitLabel());
1520}
1521
1522// Unimplemented intrinsics.
1523
1524#define UNIMPLEMENTED_INTRINSIC(Name)                                                  \
1525void IntrinsicLocationsBuilderARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
1526}                                                                                      \
1527void IntrinsicCodeGeneratorARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) {    \
1528}
1529
1530UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
1531UNIMPLEMENTED_INTRINSIC(SystemArrayCopy)
1532UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
1533UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
1534
1535UNIMPLEMENTED_INTRINSIC(MathCos)
1536UNIMPLEMENTED_INTRINSIC(MathSin)
1537UNIMPLEMENTED_INTRINSIC(MathAcos)
1538UNIMPLEMENTED_INTRINSIC(MathAsin)
1539UNIMPLEMENTED_INTRINSIC(MathAtan)
1540UNIMPLEMENTED_INTRINSIC(MathAtan2)
1541UNIMPLEMENTED_INTRINSIC(MathCbrt)
1542UNIMPLEMENTED_INTRINSIC(MathCosh)
1543UNIMPLEMENTED_INTRINSIC(MathExp)
1544UNIMPLEMENTED_INTRINSIC(MathExpm1)
1545UNIMPLEMENTED_INTRINSIC(MathHypot)
1546UNIMPLEMENTED_INTRINSIC(MathLog)
1547UNIMPLEMENTED_INTRINSIC(MathLog10)
1548UNIMPLEMENTED_INTRINSIC(MathNextAfter)
1549UNIMPLEMENTED_INTRINSIC(MathSinh)
1550UNIMPLEMENTED_INTRINSIC(MathTan)
1551UNIMPLEMENTED_INTRINSIC(MathTanh)
1552
1553#undef UNIMPLEMENTED_INTRINSIC
1554
1555#undef __
1556
1557}  // namespace arm64
1558}  // namespace art
1559