• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
34 using namespace vixl;   // NOLINT(build/namespaces)
35 
36 namespace art {
37 
38 namespace arm64 {
39 
40 using helpers::DRegisterFrom;
41 using helpers::FPRegisterFrom;
42 using helpers::HeapOperand;
43 using helpers::LocationFrom;
44 using helpers::OperandFrom;
45 using helpers::RegisterFrom;
46 using helpers::SRegisterFrom;
47 using helpers::WRegisterFrom;
48 using helpers::XRegisterFrom;
49 using helpers::InputRegisterAt;
50 
51 namespace {
52 
AbsoluteHeapOperandFrom(Location location,size_t offset=0)53 ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) {
54   return MemOperand(XRegisterFrom(location), offset);
55 }
56 
57 }  // namespace
58 
GetVIXLAssembler()59 vixl::MacroAssembler* IntrinsicCodeGeneratorARM64::GetVIXLAssembler() {
60   return codegen_->GetAssembler()->vixl_masm_;
61 }
62 
GetAllocator()63 ArenaAllocator* IntrinsicCodeGeneratorARM64::GetAllocator() {
64   return codegen_->GetGraph()->GetArena();
65 }
66 
67 #define __ codegen->GetAssembler()->vixl_masm_->
68 
MoveFromReturnRegister(Location trg,Primitive::Type type,CodeGeneratorARM64 * codegen)69 static 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 
MoveArguments(HInvoke * invoke,CodeGeneratorARM64 * codegen)90 static 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!
101 class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 {
102  public:
IntrinsicSlowPathARM64(HInvoke * invoke)103   explicit IntrinsicSlowPathARM64(HInvoke* invoke)
104       : SlowPathCodeARM64(invoke), invoke_(invoke) { }
105 
EmitNativeCode(CodeGenerator * codegen_in)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 
GetDescription() const134   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 
TryDispatch(HInvoke * invoke)145 bool 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 
CreateFPToIntLocations(ArenaAllocator * arena,HInvoke * invoke)169 static 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 
CreateIntToFPLocations(ArenaAllocator * arena,HInvoke * invoke)177 static 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 
MoveFPToInt(LocationSummary * locations,bool is64bit,vixl::MacroAssembler * masm)185 static 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 
MoveIntToFP(LocationSummary * locations,bool is64bit,vixl::MacroAssembler * masm)192 static 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 
VisitDoubleDoubleToRawLongBits(HInvoke * invoke)199 void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
200   CreateFPToIntLocations(arena_, invoke);
201 }
VisitDoubleLongBitsToDouble(HInvoke * invoke)202 void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
203   CreateIntToFPLocations(arena_, invoke);
204 }
205 
VisitDoubleDoubleToRawLongBits(HInvoke * invoke)206 void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
207   MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
208 }
VisitDoubleLongBitsToDouble(HInvoke * invoke)209 void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
210   MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
211 }
212 
VisitFloatFloatToRawIntBits(HInvoke * invoke)213 void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
214   CreateFPToIntLocations(arena_, invoke);
215 }
VisitFloatIntBitsToFloat(HInvoke * invoke)216 void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
217   CreateIntToFPLocations(arena_, invoke);
218 }
219 
VisitFloatFloatToRawIntBits(HInvoke * invoke)220 void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
221   MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
222 }
VisitFloatIntBitsToFloat(HInvoke * invoke)223 void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
224   MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
225 }
226 
CreateIntToIntLocations(ArenaAllocator * arena,HInvoke * invoke)227 static 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 
GenReverseBytes(LocationSummary * locations,Primitive::Type type,vixl::MacroAssembler * masm)235 static 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 
VisitIntegerReverseBytes(HInvoke * invoke)256 void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
257   CreateIntToIntLocations(arena_, invoke);
258 }
259 
VisitIntegerReverseBytes(HInvoke * invoke)260 void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
261   GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
262 }
263 
VisitLongReverseBytes(HInvoke * invoke)264 void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) {
265   CreateIntToIntLocations(arena_, invoke);
266 }
267 
VisitLongReverseBytes(HInvoke * invoke)268 void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) {
269   GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
270 }
271 
VisitShortReverseBytes(HInvoke * invoke)272 void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) {
273   CreateIntToIntLocations(arena_, invoke);
274 }
275 
VisitShortReverseBytes(HInvoke * invoke)276 void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
277   GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
278 }
279 
CreateIntIntToIntLocations(ArenaAllocator * arena,HInvoke * invoke)280 static 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 
GenNumberOfLeadingZeros(LocationSummary * locations,Primitive::Type type,vixl::MacroAssembler * masm)289 static 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 
VisitIntegerNumberOfLeadingZeros(HInvoke * invoke)300 void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
301   CreateIntToIntLocations(arena_, invoke);
302 }
303 
VisitIntegerNumberOfLeadingZeros(HInvoke * invoke)304 void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
305   GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
306 }
307 
VisitLongNumberOfLeadingZeros(HInvoke * invoke)308 void IntrinsicLocationsBuilderARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
309   CreateIntToIntLocations(arena_, invoke);
310 }
311 
VisitLongNumberOfLeadingZeros(HInvoke * invoke)312 void IntrinsicCodeGeneratorARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
313   GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
314 }
315 
GenNumberOfTrailingZeros(LocationSummary * locations,Primitive::Type type,vixl::MacroAssembler * masm)316 static 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 
VisitIntegerNumberOfTrailingZeros(HInvoke * invoke)328 void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
329   CreateIntToIntLocations(arena_, invoke);
330 }
331 
VisitIntegerNumberOfTrailingZeros(HInvoke * invoke)332 void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
333   GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
334 }
335 
VisitLongNumberOfTrailingZeros(HInvoke * invoke)336 void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
337   CreateIntToIntLocations(arena_, invoke);
338 }
339 
VisitLongNumberOfTrailingZeros(HInvoke * invoke)340 void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
341   GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
342 }
343 
GenReverse(LocationSummary * locations,Primitive::Type type,vixl::MacroAssembler * masm)344 static 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 
VisitIntegerReverse(HInvoke * invoke)355 void IntrinsicLocationsBuilderARM64::VisitIntegerReverse(HInvoke* invoke) {
356   CreateIntToIntLocations(arena_, invoke);
357 }
358 
VisitIntegerReverse(HInvoke * invoke)359 void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) {
360   GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
361 }
362 
VisitLongReverse(HInvoke * invoke)363 void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) {
364   CreateIntToIntLocations(arena_, invoke);
365 }
366 
VisitLongReverse(HInvoke * invoke)367 void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
368   GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
369 }
370 
GenBitCount(HInvoke * instr,Primitive::Type type,vixl::MacroAssembler * masm)371 static void GenBitCount(HInvoke* instr, Primitive::Type type, vixl::MacroAssembler* masm) {
372   DCHECK(Primitive::IsIntOrLongType(type)) << type;
373   DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
374   DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
375 
376   UseScratchRegisterScope temps(masm);
377 
378   Register src = InputRegisterAt(instr, 0);
379   Register dst = RegisterFrom(instr->GetLocations()->Out(), type);
380   FPRegister fpr = (type == Primitive::kPrimLong) ? temps.AcquireD() : temps.AcquireS();
381 
382   __ Fmov(fpr, src);
383   __ Cnt(fpr.V8B(), fpr.V8B());
384   __ Addv(fpr.B(), fpr.V8B());
385   __ Fmov(dst, fpr);
386 }
387 
VisitLongBitCount(HInvoke * invoke)388 void IntrinsicLocationsBuilderARM64::VisitLongBitCount(HInvoke* invoke) {
389   CreateIntToIntLocations(arena_, invoke);
390 }
391 
VisitLongBitCount(HInvoke * invoke)392 void IntrinsicCodeGeneratorARM64::VisitLongBitCount(HInvoke* invoke) {
393   GenBitCount(invoke, Primitive::kPrimLong, GetVIXLAssembler());
394 }
395 
VisitIntegerBitCount(HInvoke * invoke)396 void IntrinsicLocationsBuilderARM64::VisitIntegerBitCount(HInvoke* invoke) {
397   CreateIntToIntLocations(arena_, invoke);
398 }
399 
VisitIntegerBitCount(HInvoke * invoke)400 void IntrinsicCodeGeneratorARM64::VisitIntegerBitCount(HInvoke* invoke) {
401   GenBitCount(invoke, Primitive::kPrimInt, GetVIXLAssembler());
402 }
403 
CreateFPToFPLocations(ArenaAllocator * arena,HInvoke * invoke)404 static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
405   LocationSummary* locations = new (arena) LocationSummary(invoke,
406                                                            LocationSummary::kNoCall,
407                                                            kIntrinsified);
408   locations->SetInAt(0, Location::RequiresFpuRegister());
409   locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
410 }
411 
MathAbsFP(LocationSummary * locations,bool is64bit,vixl::MacroAssembler * masm)412 static void MathAbsFP(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
413   Location in = locations->InAt(0);
414   Location out = locations->Out();
415 
416   FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in);
417   FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out);
418 
419   __ Fabs(out_reg, in_reg);
420 }
421 
VisitMathAbsDouble(HInvoke * invoke)422 void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) {
423   CreateFPToFPLocations(arena_, invoke);
424 }
425 
VisitMathAbsDouble(HInvoke * invoke)426 void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) {
427   MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
428 }
429 
VisitMathAbsFloat(HInvoke * invoke)430 void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) {
431   CreateFPToFPLocations(arena_, invoke);
432 }
433 
VisitMathAbsFloat(HInvoke * invoke)434 void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) {
435   MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
436 }
437 
CreateIntToInt(ArenaAllocator * arena,HInvoke * invoke)438 static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
439   LocationSummary* locations = new (arena) LocationSummary(invoke,
440                                                            LocationSummary::kNoCall,
441                                                            kIntrinsified);
442   locations->SetInAt(0, Location::RequiresRegister());
443   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
444 }
445 
GenAbsInteger(LocationSummary * locations,bool is64bit,vixl::MacroAssembler * masm)446 static void GenAbsInteger(LocationSummary* locations,
447                           bool is64bit,
448                           vixl::MacroAssembler* masm) {
449   Location in = locations->InAt(0);
450   Location output = locations->Out();
451 
452   Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in);
453   Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output);
454 
455   __ Cmp(in_reg, Operand(0));
456   __ Cneg(out_reg, in_reg, lt);
457 }
458 
VisitMathAbsInt(HInvoke * invoke)459 void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
460   CreateIntToInt(arena_, invoke);
461 }
462 
VisitMathAbsInt(HInvoke * invoke)463 void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
464   GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
465 }
466 
VisitMathAbsLong(HInvoke * invoke)467 void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
468   CreateIntToInt(arena_, invoke);
469 }
470 
VisitMathAbsLong(HInvoke * invoke)471 void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
472   GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
473 }
474 
GenMinMaxFP(LocationSummary * locations,bool is_min,bool is_double,vixl::MacroAssembler * masm)475 static void GenMinMaxFP(LocationSummary* locations,
476                         bool is_min,
477                         bool is_double,
478                         vixl::MacroAssembler* masm) {
479   Location op1 = locations->InAt(0);
480   Location op2 = locations->InAt(1);
481   Location out = locations->Out();
482 
483   FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1);
484   FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2);
485   FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out);
486   if (is_min) {
487     __ Fmin(out_reg, op1_reg, op2_reg);
488   } else {
489     __ Fmax(out_reg, op1_reg, op2_reg);
490   }
491 }
492 
CreateFPFPToFPLocations(ArenaAllocator * arena,HInvoke * invoke)493 static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
494   LocationSummary* locations = new (arena) LocationSummary(invoke,
495                                                            LocationSummary::kNoCall,
496                                                            kIntrinsified);
497   locations->SetInAt(0, Location::RequiresFpuRegister());
498   locations->SetInAt(1, Location::RequiresFpuRegister());
499   locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
500 }
501 
VisitMathMinDoubleDouble(HInvoke * invoke)502 void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
503   CreateFPFPToFPLocations(arena_, invoke);
504 }
505 
VisitMathMinDoubleDouble(HInvoke * invoke)506 void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
507   GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetVIXLAssembler());
508 }
509 
VisitMathMinFloatFloat(HInvoke * invoke)510 void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
511   CreateFPFPToFPLocations(arena_, invoke);
512 }
513 
VisitMathMinFloatFloat(HInvoke * invoke)514 void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
515   GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetVIXLAssembler());
516 }
517 
VisitMathMaxDoubleDouble(HInvoke * invoke)518 void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
519   CreateFPFPToFPLocations(arena_, invoke);
520 }
521 
VisitMathMaxDoubleDouble(HInvoke * invoke)522 void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
523   GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetVIXLAssembler());
524 }
525 
VisitMathMaxFloatFloat(HInvoke * invoke)526 void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
527   CreateFPFPToFPLocations(arena_, invoke);
528 }
529 
VisitMathMaxFloatFloat(HInvoke * invoke)530 void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
531   GenMinMaxFP(
532       invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetVIXLAssembler());
533 }
534 
GenMinMax(LocationSummary * locations,bool is_min,bool is_long,vixl::MacroAssembler * masm)535 static void GenMinMax(LocationSummary* locations,
536                       bool is_min,
537                       bool is_long,
538                       vixl::MacroAssembler* masm) {
539   Location op1 = locations->InAt(0);
540   Location op2 = locations->InAt(1);
541   Location out = locations->Out();
542 
543   Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
544   Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
545   Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
546 
547   __ Cmp(op1_reg, op2_reg);
548   __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
549 }
550 
VisitMathMinIntInt(HInvoke * invoke)551 void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
552   CreateIntIntToIntLocations(arena_, invoke);
553 }
554 
VisitMathMinIntInt(HInvoke * invoke)555 void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) {
556   GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetVIXLAssembler());
557 }
558 
VisitMathMinLongLong(HInvoke * invoke)559 void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) {
560   CreateIntIntToIntLocations(arena_, invoke);
561 }
562 
VisitMathMinLongLong(HInvoke * invoke)563 void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) {
564   GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetVIXLAssembler());
565 }
566 
VisitMathMaxIntInt(HInvoke * invoke)567 void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) {
568   CreateIntIntToIntLocations(arena_, invoke);
569 }
570 
VisitMathMaxIntInt(HInvoke * invoke)571 void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) {
572   GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetVIXLAssembler());
573 }
574 
VisitMathMaxLongLong(HInvoke * invoke)575 void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) {
576   CreateIntIntToIntLocations(arena_, invoke);
577 }
578 
VisitMathMaxLongLong(HInvoke * invoke)579 void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) {
580   GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler());
581 }
582 
VisitMathSqrt(HInvoke * invoke)583 void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) {
584   CreateFPToFPLocations(arena_, invoke);
585 }
586 
VisitMathSqrt(HInvoke * invoke)587 void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) {
588   LocationSummary* locations = invoke->GetLocations();
589   vixl::MacroAssembler* masm = GetVIXLAssembler();
590   __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
591 }
592 
VisitMathCeil(HInvoke * invoke)593 void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) {
594   CreateFPToFPLocations(arena_, invoke);
595 }
596 
VisitMathCeil(HInvoke * invoke)597 void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) {
598   LocationSummary* locations = invoke->GetLocations();
599   vixl::MacroAssembler* masm = GetVIXLAssembler();
600   __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
601 }
602 
VisitMathFloor(HInvoke * invoke)603 void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) {
604   CreateFPToFPLocations(arena_, invoke);
605 }
606 
VisitMathFloor(HInvoke * invoke)607 void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) {
608   LocationSummary* locations = invoke->GetLocations();
609   vixl::MacroAssembler* masm = GetVIXLAssembler();
610   __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
611 }
612 
VisitMathRint(HInvoke * invoke)613 void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) {
614   CreateFPToFPLocations(arena_, invoke);
615 }
616 
VisitMathRint(HInvoke * invoke)617 void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) {
618   LocationSummary* locations = invoke->GetLocations();
619   vixl::MacroAssembler* masm = GetVIXLAssembler();
620   __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
621 }
622 
CreateFPToIntPlusTempLocations(ArenaAllocator * arena,HInvoke * invoke)623 static void CreateFPToIntPlusTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
624   LocationSummary* locations = new (arena) LocationSummary(invoke,
625                                                            LocationSummary::kNoCall,
626                                                            kIntrinsified);
627   locations->SetInAt(0, Location::RequiresFpuRegister());
628   locations->SetOut(Location::RequiresRegister());
629 }
630 
GenMathRound(LocationSummary * locations,bool is_double,vixl::MacroAssembler * masm)631 static void GenMathRound(LocationSummary* locations,
632                          bool is_double,
633                          vixl::MacroAssembler* masm) {
634   FPRegister in_reg = is_double ?
635       DRegisterFrom(locations->InAt(0)) : SRegisterFrom(locations->InAt(0));
636   Register out_reg = is_double ?
637       XRegisterFrom(locations->Out()) : WRegisterFrom(locations->Out());
638   UseScratchRegisterScope temps(masm);
639   FPRegister temp1_reg = temps.AcquireSameSizeAs(in_reg);
640 
641   // 0.5 can be encoded as an immediate, so use fmov.
642   if (is_double) {
643     __ Fmov(temp1_reg, static_cast<double>(0.5));
644   } else {
645     __ Fmov(temp1_reg, static_cast<float>(0.5));
646   }
647   __ Fadd(temp1_reg, in_reg, temp1_reg);
648   __ Fcvtms(out_reg, temp1_reg);
649 }
650 
VisitMathRoundDouble(HInvoke * invoke)651 void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) {
652   // See intrinsics.h.
653   if (kRoundIsPlusPointFive) {
654     CreateFPToIntPlusTempLocations(arena_, invoke);
655   }
656 }
657 
VisitMathRoundDouble(HInvoke * invoke)658 void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) {
659   GenMathRound(invoke->GetLocations(), /* is_double */ true, GetVIXLAssembler());
660 }
661 
VisitMathRoundFloat(HInvoke * invoke)662 void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) {
663   // See intrinsics.h.
664   if (kRoundIsPlusPointFive) {
665     CreateFPToIntPlusTempLocations(arena_, invoke);
666   }
667 }
668 
VisitMathRoundFloat(HInvoke * invoke)669 void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) {
670   GenMathRound(invoke->GetLocations(), /* is_double */ false, GetVIXLAssembler());
671 }
672 
VisitMemoryPeekByte(HInvoke * invoke)673 void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) {
674   CreateIntToIntLocations(arena_, invoke);
675 }
676 
VisitMemoryPeekByte(HInvoke * invoke)677 void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) {
678   vixl::MacroAssembler* masm = GetVIXLAssembler();
679   __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()),
680           AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
681 }
682 
VisitMemoryPeekIntNative(HInvoke * invoke)683 void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
684   CreateIntToIntLocations(arena_, invoke);
685 }
686 
VisitMemoryPeekIntNative(HInvoke * invoke)687 void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
688   vixl::MacroAssembler* masm = GetVIXLAssembler();
689   __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()),
690          AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
691 }
692 
VisitMemoryPeekLongNative(HInvoke * invoke)693 void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
694   CreateIntToIntLocations(arena_, invoke);
695 }
696 
VisitMemoryPeekLongNative(HInvoke * invoke)697 void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
698   vixl::MacroAssembler* masm = GetVIXLAssembler();
699   __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()),
700          AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
701 }
702 
VisitMemoryPeekShortNative(HInvoke * invoke)703 void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
704   CreateIntToIntLocations(arena_, invoke);
705 }
706 
VisitMemoryPeekShortNative(HInvoke * invoke)707 void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
708   vixl::MacroAssembler* masm = GetVIXLAssembler();
709   __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()),
710            AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
711 }
712 
CreateIntIntToVoidLocations(ArenaAllocator * arena,HInvoke * invoke)713 static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
714   LocationSummary* locations = new (arena) LocationSummary(invoke,
715                                                            LocationSummary::kNoCall,
716                                                            kIntrinsified);
717   locations->SetInAt(0, Location::RequiresRegister());
718   locations->SetInAt(1, Location::RequiresRegister());
719 }
720 
VisitMemoryPokeByte(HInvoke * invoke)721 void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) {
722   CreateIntIntToVoidLocations(arena_, invoke);
723 }
724 
VisitMemoryPokeByte(HInvoke * invoke)725 void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) {
726   vixl::MacroAssembler* masm = GetVIXLAssembler();
727   __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)),
728           AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
729 }
730 
VisitMemoryPokeIntNative(HInvoke * invoke)731 void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
732   CreateIntIntToVoidLocations(arena_, invoke);
733 }
734 
VisitMemoryPokeIntNative(HInvoke * invoke)735 void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
736   vixl::MacroAssembler* masm = GetVIXLAssembler();
737   __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)),
738          AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
739 }
740 
VisitMemoryPokeLongNative(HInvoke * invoke)741 void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
742   CreateIntIntToVoidLocations(arena_, invoke);
743 }
744 
VisitMemoryPokeLongNative(HInvoke * invoke)745 void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
746   vixl::MacroAssembler* masm = GetVIXLAssembler();
747   __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)),
748          AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
749 }
750 
VisitMemoryPokeShortNative(HInvoke * invoke)751 void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
752   CreateIntIntToVoidLocations(arena_, invoke);
753 }
754 
VisitMemoryPokeShortNative(HInvoke * invoke)755 void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
756   vixl::MacroAssembler* masm = GetVIXLAssembler();
757   __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)),
758           AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
759 }
760 
VisitThreadCurrentThread(HInvoke * invoke)761 void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) {
762   LocationSummary* locations = new (arena_) LocationSummary(invoke,
763                                                             LocationSummary::kNoCall,
764                                                             kIntrinsified);
765   locations->SetOut(Location::RequiresRegister());
766 }
767 
VisitThreadCurrentThread(HInvoke * invoke)768 void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) {
769   codegen_->Load(Primitive::kPrimNot, WRegisterFrom(invoke->GetLocations()->Out()),
770                  MemOperand(tr, Thread::PeerOffset<8>().Int32Value()));
771 }
772 
GenUnsafeGet(HInvoke * invoke,Primitive::Type type,bool is_volatile,CodeGeneratorARM64 * codegen)773 static void GenUnsafeGet(HInvoke* invoke,
774                          Primitive::Type type,
775                          bool is_volatile,
776                          CodeGeneratorARM64* codegen) {
777   LocationSummary* locations = invoke->GetLocations();
778   DCHECK((type == Primitive::kPrimInt) ||
779          (type == Primitive::kPrimLong) ||
780          (type == Primitive::kPrimNot));
781   vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
782   Location base_loc = locations->InAt(1);
783   Register base = WRegisterFrom(base_loc);      // Object pointer.
784   Location offset_loc = locations->InAt(2);
785   Register offset = XRegisterFrom(offset_loc);  // Long offset.
786   Location trg_loc = locations->Out();
787   Register trg = RegisterFrom(trg_loc, type);
788 
789   if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
790     // UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case.
791     UseScratchRegisterScope temps(masm);
792     Register temp = temps.AcquireW();
793     codegen->GenerateArrayLoadWithBakerReadBarrier(
794         invoke, trg_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false);
795   } else {
796     // Other cases.
797     MemOperand mem_op(base.X(), offset);
798     if (is_volatile) {
799       codegen->LoadAcquire(invoke, trg, mem_op, /* needs_null_check */ true);
800     } else {
801       codegen->Load(type, trg, mem_op);
802     }
803 
804     if (type == Primitive::kPrimNot) {
805       DCHECK(trg.IsW());
806       codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
807     }
808   }
809 }
810 
CreateIntIntIntToIntLocations(ArenaAllocator * arena,HInvoke * invoke)811 static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
812   bool can_call = kEmitCompilerReadBarrier &&
813       (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
814        invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
815   LocationSummary* locations = new (arena) LocationSummary(invoke,
816                                                            can_call ?
817                                                                LocationSummary::kCallOnSlowPath :
818                                                                LocationSummary::kNoCall,
819                                                            kIntrinsified);
820   locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
821   locations->SetInAt(1, Location::RequiresRegister());
822   locations->SetInAt(2, Location::RequiresRegister());
823   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
824 }
825 
VisitUnsafeGet(HInvoke * invoke)826 void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
827   CreateIntIntIntToIntLocations(arena_, invoke);
828 }
VisitUnsafeGetVolatile(HInvoke * invoke)829 void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
830   CreateIntIntIntToIntLocations(arena_, invoke);
831 }
VisitUnsafeGetLong(HInvoke * invoke)832 void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
833   CreateIntIntIntToIntLocations(arena_, invoke);
834 }
VisitUnsafeGetLongVolatile(HInvoke * invoke)835 void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
836   CreateIntIntIntToIntLocations(arena_, invoke);
837 }
VisitUnsafeGetObject(HInvoke * invoke)838 void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
839   CreateIntIntIntToIntLocations(arena_, invoke);
840 }
VisitUnsafeGetObjectVolatile(HInvoke * invoke)841 void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
842   CreateIntIntIntToIntLocations(arena_, invoke);
843 }
844 
VisitUnsafeGet(HInvoke * invoke)845 void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
846   GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
847 }
VisitUnsafeGetVolatile(HInvoke * invoke)848 void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
849   GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
850 }
VisitUnsafeGetLong(HInvoke * invoke)851 void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
852   GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
853 }
VisitUnsafeGetLongVolatile(HInvoke * invoke)854 void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
855   GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
856 }
VisitUnsafeGetObject(HInvoke * invoke)857 void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
858   GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
859 }
VisitUnsafeGetObjectVolatile(HInvoke * invoke)860 void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
861   GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
862 }
863 
CreateIntIntIntIntToVoid(ArenaAllocator * arena,HInvoke * invoke)864 static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
865   LocationSummary* locations = new (arena) LocationSummary(invoke,
866                                                            LocationSummary::kNoCall,
867                                                            kIntrinsified);
868   locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
869   locations->SetInAt(1, Location::RequiresRegister());
870   locations->SetInAt(2, Location::RequiresRegister());
871   locations->SetInAt(3, Location::RequiresRegister());
872 }
873 
VisitUnsafePut(HInvoke * invoke)874 void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
875   CreateIntIntIntIntToVoid(arena_, invoke);
876 }
VisitUnsafePutOrdered(HInvoke * invoke)877 void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
878   CreateIntIntIntIntToVoid(arena_, invoke);
879 }
VisitUnsafePutVolatile(HInvoke * invoke)880 void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
881   CreateIntIntIntIntToVoid(arena_, invoke);
882 }
VisitUnsafePutObject(HInvoke * invoke)883 void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
884   CreateIntIntIntIntToVoid(arena_, invoke);
885 }
VisitUnsafePutObjectOrdered(HInvoke * invoke)886 void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
887   CreateIntIntIntIntToVoid(arena_, invoke);
888 }
VisitUnsafePutObjectVolatile(HInvoke * invoke)889 void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
890   CreateIntIntIntIntToVoid(arena_, invoke);
891 }
VisitUnsafePutLong(HInvoke * invoke)892 void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
893   CreateIntIntIntIntToVoid(arena_, invoke);
894 }
VisitUnsafePutLongOrdered(HInvoke * invoke)895 void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
896   CreateIntIntIntIntToVoid(arena_, invoke);
897 }
VisitUnsafePutLongVolatile(HInvoke * invoke)898 void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
899   CreateIntIntIntIntToVoid(arena_, invoke);
900 }
901 
GenUnsafePut(LocationSummary * locations,Primitive::Type type,bool is_volatile,bool is_ordered,CodeGeneratorARM64 * codegen)902 static void GenUnsafePut(LocationSummary* locations,
903                          Primitive::Type type,
904                          bool is_volatile,
905                          bool is_ordered,
906                          CodeGeneratorARM64* codegen) {
907   vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
908 
909   Register base = WRegisterFrom(locations->InAt(1));    // Object pointer.
910   Register offset = XRegisterFrom(locations->InAt(2));  // Long offset.
911   Register value = RegisterFrom(locations->InAt(3), type);
912   Register source = value;
913   MemOperand mem_op(base.X(), offset);
914 
915   {
916     // We use a block to end the scratch scope before the write barrier, thus
917     // freeing the temporary registers so they can be used in `MarkGCCard`.
918     UseScratchRegisterScope temps(masm);
919 
920     if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
921       DCHECK(value.IsW());
922       Register temp = temps.AcquireW();
923       __ Mov(temp.W(), value.W());
924       codegen->GetAssembler()->PoisonHeapReference(temp.W());
925       source = temp;
926     }
927 
928     if (is_volatile || is_ordered) {
929       codegen->StoreRelease(type, source, mem_op);
930     } else {
931       codegen->Store(type, source, mem_op);
932     }
933   }
934 
935   if (type == Primitive::kPrimNot) {
936     bool value_can_be_null = true;  // TODO: Worth finding out this information?
937     codegen->MarkGCCard(base, value, value_can_be_null);
938   }
939 }
940 
VisitUnsafePut(HInvoke * invoke)941 void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
942   GenUnsafePut(invoke->GetLocations(),
943                Primitive::kPrimInt,
944                /* is_volatile */ false,
945                /* is_ordered */ false,
946                codegen_);
947 }
VisitUnsafePutOrdered(HInvoke * invoke)948 void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
949   GenUnsafePut(invoke->GetLocations(),
950                Primitive::kPrimInt,
951                /* is_volatile */ false,
952                /* is_ordered */ true,
953                codegen_);
954 }
VisitUnsafePutVolatile(HInvoke * invoke)955 void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
956   GenUnsafePut(invoke->GetLocations(),
957                Primitive::kPrimInt,
958                /* is_volatile */ true,
959                /* is_ordered */ false,
960                codegen_);
961 }
VisitUnsafePutObject(HInvoke * invoke)962 void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
963   GenUnsafePut(invoke->GetLocations(),
964                Primitive::kPrimNot,
965                /* is_volatile */ false,
966                /* is_ordered */ false,
967                codegen_);
968 }
VisitUnsafePutObjectOrdered(HInvoke * invoke)969 void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
970   GenUnsafePut(invoke->GetLocations(),
971                Primitive::kPrimNot,
972                /* is_volatile */ false,
973                /* is_ordered */ true,
974                codegen_);
975 }
VisitUnsafePutObjectVolatile(HInvoke * invoke)976 void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
977   GenUnsafePut(invoke->GetLocations(),
978                Primitive::kPrimNot,
979                /* is_volatile */ true,
980                /* is_ordered */ false,
981                codegen_);
982 }
VisitUnsafePutLong(HInvoke * invoke)983 void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
984   GenUnsafePut(invoke->GetLocations(),
985                Primitive::kPrimLong,
986                /* is_volatile */ false,
987                /* is_ordered */ false,
988                codegen_);
989 }
VisitUnsafePutLongOrdered(HInvoke * invoke)990 void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
991   GenUnsafePut(invoke->GetLocations(),
992                Primitive::kPrimLong,
993                /* is_volatile */ false,
994                /* is_ordered */ true,
995                codegen_);
996 }
VisitUnsafePutLongVolatile(HInvoke * invoke)997 void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
998   GenUnsafePut(invoke->GetLocations(),
999                Primitive::kPrimLong,
1000                /* is_volatile */ true,
1001                /* is_ordered */ false,
1002                codegen_);
1003 }
1004 
CreateIntIntIntIntIntToInt(ArenaAllocator * arena,HInvoke * invoke,Primitive::Type type)1005 static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
1006                                        HInvoke* invoke,
1007                                        Primitive::Type type) {
1008   LocationSummary* locations = new (arena) LocationSummary(invoke,
1009                                                            LocationSummary::kNoCall,
1010                                                            kIntrinsified);
1011   locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
1012   locations->SetInAt(1, Location::RequiresRegister());
1013   locations->SetInAt(2, Location::RequiresRegister());
1014   locations->SetInAt(3, Location::RequiresRegister());
1015   locations->SetInAt(4, Location::RequiresRegister());
1016 
1017   // If heap poisoning is enabled, we don't want the unpoisoning
1018   // operations to potentially clobber the output.
1019   Location::OutputOverlap overlaps = (kPoisonHeapReferences && type == Primitive::kPrimNot)
1020       ? Location::kOutputOverlap
1021       : Location::kNoOutputOverlap;
1022   locations->SetOut(Location::RequiresRegister(), overlaps);
1023 }
1024 
GenCas(LocationSummary * locations,Primitive::Type type,CodeGeneratorARM64 * codegen)1025 static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM64* codegen) {
1026   vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
1027 
1028   Register out = WRegisterFrom(locations->Out());                  // Boolean result.
1029 
1030   Register base = WRegisterFrom(locations->InAt(1));               // Object pointer.
1031   Register offset = XRegisterFrom(locations->InAt(2));             // Long offset.
1032   Register expected = RegisterFrom(locations->InAt(3), type);      // Expected.
1033   Register value = RegisterFrom(locations->InAt(4), type);         // Value.
1034 
1035   // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
1036   if (type == Primitive::kPrimNot) {
1037     // Mark card for object assuming new value is stored.
1038     bool value_can_be_null = true;  // TODO: Worth finding out this information?
1039     codegen->MarkGCCard(base, value, value_can_be_null);
1040   }
1041 
1042   UseScratchRegisterScope temps(masm);
1043   Register tmp_ptr = temps.AcquireX();                             // Pointer to actual memory.
1044   Register tmp_value = temps.AcquireSameSizeAs(value);             // Value in memory.
1045 
1046   Register tmp_32 = tmp_value.W();
1047 
1048   __ Add(tmp_ptr, base.X(), Operand(offset));
1049 
1050   if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1051     codegen->GetAssembler()->PoisonHeapReference(expected);
1052     if (value.Is(expected)) {
1053       // Do not poison `value`, as it is the same register as
1054       // `expected`, which has just been poisoned.
1055     } else {
1056       codegen->GetAssembler()->PoisonHeapReference(value);
1057     }
1058   }
1059 
1060   // do {
1061   //   tmp_value = [tmp_ptr] - expected;
1062   // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1063   // result = tmp_value != 0;
1064 
1065   vixl::Label loop_head, exit_loop;
1066   __ Bind(&loop_head);
1067   // TODO: When `type == Primitive::kPrimNot`, add a read barrier for
1068   // the reference stored in the object before attempting the CAS,
1069   // similar to the one in the art::Unsafe_compareAndSwapObject JNI
1070   // implementation.
1071   //
1072   // Note that this code is not (yet) used when read barriers are
1073   // enabled (see IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject).
1074   DCHECK(!(type == Primitive::kPrimNot && kEmitCompilerReadBarrier));
1075   __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
1076   __ Cmp(tmp_value, expected);
1077   __ B(&exit_loop, ne);
1078   __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
1079   __ Cbnz(tmp_32, &loop_head);
1080   __ Bind(&exit_loop);
1081   __ Cset(out, eq);
1082 
1083   if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1084     codegen->GetAssembler()->UnpoisonHeapReference(expected);
1085     if (value.Is(expected)) {
1086       // Do not unpoison `value`, as it is the same register as
1087       // `expected`, which has just been unpoisoned.
1088     } else {
1089       codegen->GetAssembler()->UnpoisonHeapReference(value);
1090     }
1091   }
1092 }
1093 
VisitUnsafeCASInt(HInvoke * invoke)1094 void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
1095   CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimInt);
1096 }
VisitUnsafeCASLong(HInvoke * invoke)1097 void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
1098   CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimLong);
1099 }
VisitUnsafeCASObject(HInvoke * invoke)1100 void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
1101   // The UnsafeCASObject intrinsic is missing a read barrier, and
1102   // therefore sometimes does not work as expected (b/25883050).
1103   // Turn it off temporarily as a quick fix, until the read barrier is
1104   // implemented (see TODO in GenCAS below).
1105   //
1106   // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers.
1107   if (kEmitCompilerReadBarrier) {
1108     return;
1109   }
1110 
1111   CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimNot);
1112 }
1113 
VisitUnsafeCASInt(HInvoke * invoke)1114 void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
1115   GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
1116 }
VisitUnsafeCASLong(HInvoke * invoke)1117 void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
1118   GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_);
1119 }
VisitUnsafeCASObject(HInvoke * invoke)1120 void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
1121   GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
1122 }
1123 
VisitStringCharAt(HInvoke * invoke)1124 void IntrinsicLocationsBuilderARM64::VisitStringCharAt(HInvoke* invoke) {
1125   LocationSummary* locations = new (arena_) LocationSummary(invoke,
1126                                                             LocationSummary::kCallOnSlowPath,
1127                                                             kIntrinsified);
1128   locations->SetInAt(0, Location::RequiresRegister());
1129   locations->SetInAt(1, Location::RequiresRegister());
1130   // In case we need to go in the slow path, we can't have the output be the same
1131   // as the input: the current liveness analysis considers the input to be live
1132   // at the point of the call.
1133   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1134 }
1135 
VisitStringCharAt(HInvoke * invoke)1136 void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) {
1137   vixl::MacroAssembler* masm = GetVIXLAssembler();
1138   LocationSummary* locations = invoke->GetLocations();
1139 
1140   // Location of reference to data array
1141   const MemberOffset value_offset = mirror::String::ValueOffset();
1142   // Location of count
1143   const MemberOffset count_offset = mirror::String::CountOffset();
1144 
1145   Register obj = WRegisterFrom(locations->InAt(0));  // String object pointer.
1146   Register idx = WRegisterFrom(locations->InAt(1));  // Index of character.
1147   Register out = WRegisterFrom(locations->Out());    // Result character.
1148 
1149   UseScratchRegisterScope temps(masm);
1150   Register temp = temps.AcquireW();
1151   Register array_temp = temps.AcquireW();            // We can trade this for worse scheduling.
1152 
1153   // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
1154   //       the cost.
1155   // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
1156   //       we will not optimize the code for constants (which would save a register).
1157 
1158   SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1159   codegen_->AddSlowPath(slow_path);
1160 
1161   __ Ldr(temp, HeapOperand(obj, count_offset));          // temp = str.length.
1162   codegen_->MaybeRecordImplicitNullCheck(invoke);
1163   __ Cmp(idx, temp);
1164   __ B(hs, slow_path->GetEntryLabel());
1165 
1166   __ Add(array_temp, obj, Operand(value_offset.Int32Value()));  // array_temp := str.value.
1167 
1168   // Load the value.
1169   __ Ldrh(out, MemOperand(array_temp.X(), idx, UXTW, 1));  // out := array_temp[idx].
1170 
1171   __ Bind(slow_path->GetExitLabel());
1172 }
1173 
VisitStringCompareTo(HInvoke * invoke)1174 void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
1175   LocationSummary* locations = new (arena_) LocationSummary(invoke,
1176                                                             LocationSummary::kCall,
1177                                                             kIntrinsified);
1178   InvokeRuntimeCallingConvention calling_convention;
1179   locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1180   locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1181   locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1182 }
1183 
VisitStringCompareTo(HInvoke * invoke)1184 void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
1185   vixl::MacroAssembler* masm = GetVIXLAssembler();
1186   LocationSummary* locations = invoke->GetLocations();
1187 
1188   // Note that the null check must have been done earlier.
1189   DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1190 
1191   Register argument = WRegisterFrom(locations->InAt(1));
1192   __ Cmp(argument, 0);
1193   SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1194   codegen_->AddSlowPath(slow_path);
1195   __ B(eq, slow_path->GetEntryLabel());
1196 
1197   __ Ldr(
1198       lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pStringCompareTo).Int32Value()));
1199   __ Blr(lr);
1200   __ Bind(slow_path->GetExitLabel());
1201 }
1202 
VisitStringEquals(HInvoke * invoke)1203 void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
1204   LocationSummary* locations = new (arena_) LocationSummary(invoke,
1205                                                             LocationSummary::kNoCall,
1206                                                             kIntrinsified);
1207   locations->SetInAt(0, Location::RequiresRegister());
1208   locations->SetInAt(1, Location::RequiresRegister());
1209   // Temporary registers to store lengths of strings and for calculations.
1210   locations->AddTemp(Location::RequiresRegister());
1211   locations->AddTemp(Location::RequiresRegister());
1212 
1213   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1214 }
1215 
VisitStringEquals(HInvoke * invoke)1216 void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
1217   vixl::MacroAssembler* masm = GetVIXLAssembler();
1218   LocationSummary* locations = invoke->GetLocations();
1219 
1220   Register str = WRegisterFrom(locations->InAt(0));
1221   Register arg = WRegisterFrom(locations->InAt(1));
1222   Register out = XRegisterFrom(locations->Out());
1223 
1224   UseScratchRegisterScope scratch_scope(masm);
1225   Register temp = scratch_scope.AcquireW();
1226   Register temp1 = WRegisterFrom(locations->GetTemp(0));
1227   Register temp2 = WRegisterFrom(locations->GetTemp(1));
1228 
1229   vixl::Label loop;
1230   vixl::Label end;
1231   vixl::Label return_true;
1232   vixl::Label return_false;
1233 
1234   // Get offsets of count, value, and class fields within a string object.
1235   const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1236   const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1237   const int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1238 
1239   // Note that the null check must have been done earlier.
1240   DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1241 
1242   // Check if input is null, return false if it is.
1243   __ Cbz(arg, &return_false);
1244 
1245   // Reference equality check, return true if same reference.
1246   __ Cmp(str, arg);
1247   __ B(&return_true, eq);
1248 
1249   // Instanceof check for the argument by comparing class fields.
1250   // All string objects must have the same type since String cannot be subclassed.
1251   // Receiver must be a string object, so its class field is equal to all strings' class fields.
1252   // If the argument is a string object, its class field must be equal to receiver's class field.
1253   __ Ldr(temp, MemOperand(str.X(), class_offset));
1254   __ Ldr(temp1, MemOperand(arg.X(), class_offset));
1255   __ Cmp(temp, temp1);
1256   __ B(&return_false, ne);
1257 
1258   // Load lengths of this and argument strings.
1259   __ Ldr(temp, MemOperand(str.X(), count_offset));
1260   __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1261   // Check if lengths are equal, return false if they're not.
1262   __ Cmp(temp, temp1);
1263   __ B(&return_false, ne);
1264   // Store offset of string value in preparation for comparison loop
1265   __ Mov(temp1, value_offset);
1266   // Return true if both strings are empty.
1267   __ Cbz(temp, &return_true);
1268 
1269   // Assertions that must hold in order to compare strings 4 characters at a time.
1270   DCHECK_ALIGNED(value_offset, 8);
1271   static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1272 
1273   temp1 = temp1.X();
1274   temp2 = temp2.X();
1275 
1276   // Loop to compare strings 4 characters at a time starting at the beginning of the string.
1277   // Ok to do this because strings are zero-padded to be 8-byte aligned.
1278   __ Bind(&loop);
1279   __ Ldr(out, MemOperand(str.X(), temp1));
1280   __ Ldr(temp2, MemOperand(arg.X(), temp1));
1281   __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
1282   __ Cmp(out, temp2);
1283   __ B(&return_false, ne);
1284   __ Sub(temp, temp, Operand(4), SetFlags);
1285   __ B(&loop, gt);
1286 
1287   // Return true and exit the function.
1288   // If loop does not result in returning false, we return true.
1289   __ Bind(&return_true);
1290   __ Mov(out, 1);
1291   __ B(&end);
1292 
1293   // Return false and exit the function.
1294   __ Bind(&return_false);
1295   __ Mov(out, 0);
1296   __ Bind(&end);
1297 }
1298 
GenerateVisitStringIndexOf(HInvoke * invoke,vixl::MacroAssembler * masm,CodeGeneratorARM64 * codegen,ArenaAllocator * allocator,bool start_at_zero)1299 static void GenerateVisitStringIndexOf(HInvoke* invoke,
1300                                        vixl::MacroAssembler* masm,
1301                                        CodeGeneratorARM64* codegen,
1302                                        ArenaAllocator* allocator,
1303                                        bool start_at_zero) {
1304   LocationSummary* locations = invoke->GetLocations();
1305   Register tmp_reg = WRegisterFrom(locations->GetTemp(0));
1306 
1307   // Note that the null check must have been done earlier.
1308   DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1309 
1310   // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
1311   // or directly dispatch if we have a constant.
1312   SlowPathCodeARM64* slow_path = nullptr;
1313   if (invoke->InputAt(1)->IsIntConstant()) {
1314     if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > 0xFFFFU) {
1315       // Always needs the slow-path. We could directly dispatch to it, but this case should be
1316       // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1317       slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1318       codegen->AddSlowPath(slow_path);
1319       __ B(slow_path->GetEntryLabel());
1320       __ Bind(slow_path->GetExitLabel());
1321       return;
1322     }
1323   } else {
1324     Register char_reg = WRegisterFrom(locations->InAt(1));
1325     __ Mov(tmp_reg, 0xFFFF);
1326     __ Cmp(char_reg, Operand(tmp_reg));
1327     slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1328     codegen->AddSlowPath(slow_path);
1329     __ B(hi, slow_path->GetEntryLabel());
1330   }
1331 
1332   if (start_at_zero) {
1333     // Start-index = 0.
1334     __ Mov(tmp_reg, 0);
1335   }
1336 
1337   __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pIndexOf).Int32Value()));
1338   CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
1339   __ Blr(lr);
1340 
1341   if (slow_path != nullptr) {
1342     __ Bind(slow_path->GetExitLabel());
1343   }
1344 }
1345 
VisitStringIndexOf(HInvoke * invoke)1346 void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) {
1347   LocationSummary* locations = new (arena_) LocationSummary(invoke,
1348                                                             LocationSummary::kCall,
1349                                                             kIntrinsified);
1350   // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1351   // best to align the inputs accordingly.
1352   InvokeRuntimeCallingConvention calling_convention;
1353   locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1354   locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1355   locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1356 
1357   // Need a temp for slow-path codepoint compare, and need to send start_index=0.
1358   locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1359 }
1360 
VisitStringIndexOf(HInvoke * invoke)1361 void IntrinsicCodeGeneratorARM64::VisitStringIndexOf(HInvoke* invoke) {
1362   GenerateVisitStringIndexOf(
1363       invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
1364 }
1365 
VisitStringIndexOfAfter(HInvoke * invoke)1366 void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
1367   LocationSummary* locations = new (arena_) LocationSummary(invoke,
1368                                                             LocationSummary::kCall,
1369                                                             kIntrinsified);
1370   // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1371   // best to align the inputs accordingly.
1372   InvokeRuntimeCallingConvention calling_convention;
1373   locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1374   locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1375   locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1376   locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1377 
1378   // Need a temp for slow-path codepoint compare.
1379   locations->AddTemp(Location::RequiresRegister());
1380 }
1381 
VisitStringIndexOfAfter(HInvoke * invoke)1382 void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
1383   GenerateVisitStringIndexOf(
1384       invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
1385 }
1386 
VisitStringNewStringFromBytes(HInvoke * invoke)1387 void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1388   LocationSummary* locations = new (arena_) LocationSummary(invoke,
1389                                                             LocationSummary::kCall,
1390                                                             kIntrinsified);
1391   InvokeRuntimeCallingConvention calling_convention;
1392   locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1393   locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1394   locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1395   locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1396   locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1397 }
1398 
VisitStringNewStringFromBytes(HInvoke * invoke)1399 void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1400   vixl::MacroAssembler* masm = GetVIXLAssembler();
1401   LocationSummary* locations = invoke->GetLocations();
1402 
1403   Register byte_array = WRegisterFrom(locations->InAt(0));
1404   __ Cmp(byte_array, 0);
1405   SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1406   codegen_->AddSlowPath(slow_path);
1407   __ B(eq, slow_path->GetEntryLabel());
1408 
1409   __ Ldr(lr,
1410       MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromBytes).Int32Value()));
1411   CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
1412   __ Blr(lr);
1413   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1414   __ Bind(slow_path->GetExitLabel());
1415 }
1416 
VisitStringNewStringFromChars(HInvoke * invoke)1417 void 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 
VisitStringNewStringFromChars(HInvoke * invoke)1428 void 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   CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
1440   __ Blr(lr);
1441   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1442 }
1443 
VisitStringNewStringFromString(HInvoke * invoke)1444 void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(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->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1451 }
1452 
VisitStringNewStringFromString(HInvoke * invoke)1453 void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) {
1454   vixl::MacroAssembler* masm = GetVIXLAssembler();
1455   LocationSummary* locations = invoke->GetLocations();
1456 
1457   Register string_to_copy = WRegisterFrom(locations->InAt(0));
1458   __ Cmp(string_to_copy, 0);
1459   SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1460   codegen_->AddSlowPath(slow_path);
1461   __ B(eq, slow_path->GetEntryLabel());
1462 
1463   __ Ldr(lr,
1464       MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromString).Int32Value()));
1465   CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
1466   __ Blr(lr);
1467   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1468   __ Bind(slow_path->GetExitLabel());
1469 }
1470 
CreateFPToFPCallLocations(ArenaAllocator * arena,HInvoke * invoke)1471 static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1472   DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
1473   DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1474   DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1475 
1476   LocationSummary* const locations = new (arena) LocationSummary(invoke,
1477                                                                  LocationSummary::kCall,
1478                                                                  kIntrinsified);
1479   InvokeRuntimeCallingConvention calling_convention;
1480 
1481   locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1482   locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1483 }
1484 
CreateFPFPToFPCallLocations(ArenaAllocator * arena,HInvoke * invoke)1485 static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1486   DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
1487   DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1488   DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(1)->GetType()));
1489   DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1490 
1491   LocationSummary* const locations = new (arena) LocationSummary(invoke,
1492                                                                  LocationSummary::kCall,
1493                                                                  kIntrinsified);
1494   InvokeRuntimeCallingConvention calling_convention;
1495 
1496   locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1497   locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
1498   locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1499 }
1500 
GenFPToFPCall(HInvoke * invoke,vixl::MacroAssembler * masm,CodeGeneratorARM64 * codegen,QuickEntrypointEnum entry)1501 static void GenFPToFPCall(HInvoke* invoke,
1502                           vixl::MacroAssembler* masm,
1503                           CodeGeneratorARM64* codegen,
1504                           QuickEntrypointEnum entry) {
1505   __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArm64WordSize>(entry).Int32Value()));
1506   __ Blr(lr);
1507   codegen->RecordPcInfo(invoke, invoke->GetDexPc());
1508 }
1509 
VisitMathCos(HInvoke * invoke)1510 void IntrinsicLocationsBuilderARM64::VisitMathCos(HInvoke* invoke) {
1511   CreateFPToFPCallLocations(arena_, invoke);
1512 }
1513 
VisitMathCos(HInvoke * invoke)1514 void IntrinsicCodeGeneratorARM64::VisitMathCos(HInvoke* invoke) {
1515   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCos);
1516 }
1517 
VisitMathSin(HInvoke * invoke)1518 void IntrinsicLocationsBuilderARM64::VisitMathSin(HInvoke* invoke) {
1519   CreateFPToFPCallLocations(arena_, invoke);
1520 }
1521 
VisitMathSin(HInvoke * invoke)1522 void IntrinsicCodeGeneratorARM64::VisitMathSin(HInvoke* invoke) {
1523   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickSin);
1524 }
1525 
VisitMathAcos(HInvoke * invoke)1526 void IntrinsicLocationsBuilderARM64::VisitMathAcos(HInvoke* invoke) {
1527   CreateFPToFPCallLocations(arena_, invoke);
1528 }
1529 
VisitMathAcos(HInvoke * invoke)1530 void IntrinsicCodeGeneratorARM64::VisitMathAcos(HInvoke* invoke) {
1531   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAcos);
1532 }
1533 
VisitMathAsin(HInvoke * invoke)1534 void IntrinsicLocationsBuilderARM64::VisitMathAsin(HInvoke* invoke) {
1535   CreateFPToFPCallLocations(arena_, invoke);
1536 }
1537 
VisitMathAsin(HInvoke * invoke)1538 void IntrinsicCodeGeneratorARM64::VisitMathAsin(HInvoke* invoke) {
1539   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAsin);
1540 }
1541 
VisitMathAtan(HInvoke * invoke)1542 void IntrinsicLocationsBuilderARM64::VisitMathAtan(HInvoke* invoke) {
1543   CreateFPToFPCallLocations(arena_, invoke);
1544 }
1545 
VisitMathAtan(HInvoke * invoke)1546 void IntrinsicCodeGeneratorARM64::VisitMathAtan(HInvoke* invoke) {
1547   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAtan);
1548 }
1549 
VisitMathCbrt(HInvoke * invoke)1550 void IntrinsicLocationsBuilderARM64::VisitMathCbrt(HInvoke* invoke) {
1551   CreateFPToFPCallLocations(arena_, invoke);
1552 }
1553 
VisitMathCbrt(HInvoke * invoke)1554 void IntrinsicCodeGeneratorARM64::VisitMathCbrt(HInvoke* invoke) {
1555   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCbrt);
1556 }
1557 
VisitMathCosh(HInvoke * invoke)1558 void IntrinsicLocationsBuilderARM64::VisitMathCosh(HInvoke* invoke) {
1559   CreateFPToFPCallLocations(arena_, invoke);
1560 }
1561 
VisitMathCosh(HInvoke * invoke)1562 void IntrinsicCodeGeneratorARM64::VisitMathCosh(HInvoke* invoke) {
1563   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCosh);
1564 }
1565 
VisitMathExp(HInvoke * invoke)1566 void IntrinsicLocationsBuilderARM64::VisitMathExp(HInvoke* invoke) {
1567   CreateFPToFPCallLocations(arena_, invoke);
1568 }
1569 
VisitMathExp(HInvoke * invoke)1570 void IntrinsicCodeGeneratorARM64::VisitMathExp(HInvoke* invoke) {
1571   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickExp);
1572 }
1573 
VisitMathExpm1(HInvoke * invoke)1574 void IntrinsicLocationsBuilderARM64::VisitMathExpm1(HInvoke* invoke) {
1575   CreateFPToFPCallLocations(arena_, invoke);
1576 }
1577 
VisitMathExpm1(HInvoke * invoke)1578 void IntrinsicCodeGeneratorARM64::VisitMathExpm1(HInvoke* invoke) {
1579   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickExpm1);
1580 }
1581 
VisitMathLog(HInvoke * invoke)1582 void IntrinsicLocationsBuilderARM64::VisitMathLog(HInvoke* invoke) {
1583   CreateFPToFPCallLocations(arena_, invoke);
1584 }
1585 
VisitMathLog(HInvoke * invoke)1586 void IntrinsicCodeGeneratorARM64::VisitMathLog(HInvoke* invoke) {
1587   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickLog);
1588 }
1589 
VisitMathLog10(HInvoke * invoke)1590 void IntrinsicLocationsBuilderARM64::VisitMathLog10(HInvoke* invoke) {
1591   CreateFPToFPCallLocations(arena_, invoke);
1592 }
1593 
VisitMathLog10(HInvoke * invoke)1594 void IntrinsicCodeGeneratorARM64::VisitMathLog10(HInvoke* invoke) {
1595   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickLog10);
1596 }
1597 
VisitMathSinh(HInvoke * invoke)1598 void IntrinsicLocationsBuilderARM64::VisitMathSinh(HInvoke* invoke) {
1599   CreateFPToFPCallLocations(arena_, invoke);
1600 }
1601 
VisitMathSinh(HInvoke * invoke)1602 void IntrinsicCodeGeneratorARM64::VisitMathSinh(HInvoke* invoke) {
1603   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickSinh);
1604 }
1605 
VisitMathTan(HInvoke * invoke)1606 void IntrinsicLocationsBuilderARM64::VisitMathTan(HInvoke* invoke) {
1607   CreateFPToFPCallLocations(arena_, invoke);
1608 }
1609 
VisitMathTan(HInvoke * invoke)1610 void IntrinsicCodeGeneratorARM64::VisitMathTan(HInvoke* invoke) {
1611   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickTan);
1612 }
1613 
VisitMathTanh(HInvoke * invoke)1614 void IntrinsicLocationsBuilderARM64::VisitMathTanh(HInvoke* invoke) {
1615   CreateFPToFPCallLocations(arena_, invoke);
1616 }
1617 
VisitMathTanh(HInvoke * invoke)1618 void IntrinsicCodeGeneratorARM64::VisitMathTanh(HInvoke* invoke) {
1619   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickTanh);
1620 }
1621 
VisitMathAtan2(HInvoke * invoke)1622 void IntrinsicLocationsBuilderARM64::VisitMathAtan2(HInvoke* invoke) {
1623   CreateFPFPToFPCallLocations(arena_, invoke);
1624 }
1625 
VisitMathAtan2(HInvoke * invoke)1626 void IntrinsicCodeGeneratorARM64::VisitMathAtan2(HInvoke* invoke) {
1627   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAtan2);
1628 }
1629 
VisitMathHypot(HInvoke * invoke)1630 void IntrinsicLocationsBuilderARM64::VisitMathHypot(HInvoke* invoke) {
1631   CreateFPFPToFPCallLocations(arena_, invoke);
1632 }
1633 
VisitMathHypot(HInvoke * invoke)1634 void IntrinsicCodeGeneratorARM64::VisitMathHypot(HInvoke* invoke) {
1635   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickHypot);
1636 }
1637 
VisitMathNextAfter(HInvoke * invoke)1638 void IntrinsicLocationsBuilderARM64::VisitMathNextAfter(HInvoke* invoke) {
1639   CreateFPFPToFPCallLocations(arena_, invoke);
1640 }
1641 
VisitMathNextAfter(HInvoke * invoke)1642 void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) {
1643   GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickNextAfter);
1644 }
1645 
VisitStringGetCharsNoCheck(HInvoke * invoke)1646 void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1647   LocationSummary* locations = new (arena_) LocationSummary(invoke,
1648                                                             LocationSummary::kNoCall,
1649                                                             kIntrinsified);
1650   locations->SetInAt(0, Location::RequiresRegister());
1651   locations->SetInAt(1, Location::RequiresRegister());
1652   locations->SetInAt(2, Location::RequiresRegister());
1653   locations->SetInAt(3, Location::RequiresRegister());
1654   locations->SetInAt(4, Location::RequiresRegister());
1655 
1656   locations->AddTemp(Location::RequiresRegister());
1657   locations->AddTemp(Location::RequiresRegister());
1658 }
1659 
VisitStringGetCharsNoCheck(HInvoke * invoke)1660 void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1661   vixl::MacroAssembler* masm = GetVIXLAssembler();
1662   LocationSummary* locations = invoke->GetLocations();
1663 
1664   // Check assumption that sizeof(Char) is 2 (used in scaling below).
1665   const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1666   DCHECK_EQ(char_size, 2u);
1667 
1668   // Location of data in char array buffer.
1669   const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
1670 
1671   // Location of char array data in string.
1672   const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1673 
1674   // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
1675   // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
1676   Register srcObj = XRegisterFrom(locations->InAt(0));
1677   Register srcBegin = XRegisterFrom(locations->InAt(1));
1678   Register srcEnd = XRegisterFrom(locations->InAt(2));
1679   Register dstObj = XRegisterFrom(locations->InAt(3));
1680   Register dstBegin = XRegisterFrom(locations->InAt(4));
1681 
1682   Register src_ptr = XRegisterFrom(locations->GetTemp(0));
1683   Register src_ptr_end = XRegisterFrom(locations->GetTemp(1));
1684 
1685   UseScratchRegisterScope temps(masm);
1686   Register dst_ptr = temps.AcquireX();
1687   Register tmp = temps.AcquireW();
1688 
1689   // src range to copy.
1690   __ Add(src_ptr, srcObj, Operand(value_offset));
1691   __ Add(src_ptr_end, src_ptr, Operand(srcEnd, LSL, 1));
1692   __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
1693 
1694   // dst to be copied.
1695   __ Add(dst_ptr, dstObj, Operand(data_offset));
1696   __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
1697 
1698   // Do the copy.
1699   vixl::Label loop, done;
1700   __ Bind(&loop);
1701   __ Cmp(src_ptr, src_ptr_end);
1702   __ B(&done, eq);
1703   __ Ldrh(tmp, MemOperand(src_ptr, char_size, vixl::PostIndex));
1704   __ Strh(tmp, MemOperand(dst_ptr, char_size, vixl::PostIndex));
1705   __ B(&loop);
1706   __ Bind(&done);
1707 }
1708 
1709 // Mirrors ARRAYCOPY_SHORT_CHAR_ARRAY_THRESHOLD in libcore, so we can choose to use the native
1710 // implementation there for longer copy lengths.
1711 static constexpr int32_t kSystemArrayCopyCharThreshold = 32;
1712 
SetSystemArrayCopyLocationRequires(LocationSummary * locations,uint32_t at,HInstruction * input)1713 static void SetSystemArrayCopyLocationRequires(LocationSummary* locations,
1714                                                uint32_t at,
1715                                                HInstruction* input) {
1716   HIntConstant* const_input = input->AsIntConstant();
1717   if (const_input != nullptr && !vixl::Assembler::IsImmAddSub(const_input->GetValue())) {
1718     locations->SetInAt(at, Location::RequiresRegister());
1719   } else {
1720     locations->SetInAt(at, Location::RegisterOrConstant(input));
1721   }
1722 }
1723 
VisitSystemArrayCopyChar(HInvoke * invoke)1724 void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
1725   // Check to see if we have known failures that will cause us to have to bail out
1726   // to the runtime, and just generate the runtime call directly.
1727   HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1728   HIntConstant* dst_pos = invoke->InputAt(3)->AsIntConstant();
1729 
1730   // The positions must be non-negative.
1731   if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
1732       (dst_pos != nullptr && dst_pos->GetValue() < 0)) {
1733     // We will have to fail anyways.
1734     return;
1735   }
1736 
1737   // The length must be >= 0 and not so long that we would (currently) prefer libcore's
1738   // native implementation.
1739   HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1740   if (length != nullptr) {
1741     int32_t len = length->GetValue();
1742     if (len < 0 || len > kSystemArrayCopyCharThreshold) {
1743       // Just call as normal.
1744       return;
1745     }
1746   }
1747 
1748   ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
1749   LocationSummary* locations = new (allocator) LocationSummary(invoke,
1750                                                                LocationSummary::kCallOnSlowPath,
1751                                                                kIntrinsified);
1752   // arraycopy(char[] src, int src_pos, char[] dst, int dst_pos, int length).
1753   locations->SetInAt(0, Location::RequiresRegister());
1754   SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
1755   locations->SetInAt(2, Location::RequiresRegister());
1756   SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
1757   SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
1758 
1759   locations->AddTemp(Location::RequiresRegister());
1760   locations->AddTemp(Location::RequiresRegister());
1761   locations->AddTemp(Location::RequiresRegister());
1762 }
1763 
CheckSystemArrayCopyPosition(vixl::MacroAssembler * masm,const Location & pos,const Register & input,const Location & length,SlowPathCodeARM64 * slow_path,const Register & input_len,const Register & temp,bool length_is_input_length=false)1764 static void CheckSystemArrayCopyPosition(vixl::MacroAssembler* masm,
1765                                          const Location& pos,
1766                                          const Register& input,
1767                                          const Location& length,
1768                                          SlowPathCodeARM64* slow_path,
1769                                          const Register& input_len,
1770                                          const Register& temp,
1771                                          bool length_is_input_length = false) {
1772   const int32_t length_offset = mirror::Array::LengthOffset().Int32Value();
1773   if (pos.IsConstant()) {
1774     int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
1775     if (pos_const == 0) {
1776       if (!length_is_input_length) {
1777         // Check that length(input) >= length.
1778         __ Ldr(temp, MemOperand(input, length_offset));
1779         __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
1780         __ B(slow_path->GetEntryLabel(), lt);
1781       }
1782     } else {
1783       // Check that length(input) >= pos.
1784       __ Ldr(input_len, MemOperand(input, length_offset));
1785       __ Subs(temp, input_len, pos_const);
1786       __ B(slow_path->GetEntryLabel(), lt);
1787 
1788       // Check that (length(input) - pos) >= length.
1789       __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
1790       __ B(slow_path->GetEntryLabel(), lt);
1791     }
1792   } else if (length_is_input_length) {
1793     // The only way the copy can succeed is if pos is zero.
1794     __ Cbnz(WRegisterFrom(pos), slow_path->GetEntryLabel());
1795   } else {
1796     // Check that pos >= 0.
1797     Register pos_reg = WRegisterFrom(pos);
1798     __ Tbnz(pos_reg, pos_reg.size() - 1, slow_path->GetEntryLabel());
1799 
1800     // Check that pos <= length(input) && (length(input) - pos) >= length.
1801     __ Ldr(temp, MemOperand(input, length_offset));
1802     __ Subs(temp, temp, pos_reg);
1803     // Ccmp if length(input) >= pos, else definitely bail to slow path (N!=V == lt).
1804     __ Ccmp(temp, OperandFrom(length, Primitive::kPrimInt), NFlag, ge);
1805     __ B(slow_path->GetEntryLabel(), lt);
1806   }
1807 }
1808 
1809 // Compute base source address, base destination address, and end source address
1810 // for System.arraycopy* intrinsics.
GenSystemArrayCopyAddresses(vixl::MacroAssembler * masm,Primitive::Type type,const Register & src,const Location & src_pos,const Register & dst,const Location & dst_pos,const Location & copy_length,const Register & src_base,const Register & dst_base,const Register & src_end)1811 static void GenSystemArrayCopyAddresses(vixl::MacroAssembler* masm,
1812                                         Primitive::Type type,
1813                                         const Register& src,
1814                                         const Location& src_pos,
1815                                         const Register& dst,
1816                                         const Location& dst_pos,
1817                                         const Location& copy_length,
1818                                         const Register& src_base,
1819                                         const Register& dst_base,
1820                                         const Register& src_end) {
1821   DCHECK(type == Primitive::kPrimNot || type == Primitive::kPrimChar)
1822       << "Unexpected element type: " << type;
1823   const int32_t element_size = Primitive::ComponentSize(type);
1824   const int32_t element_size_shift = Primitive::ComponentSizeShift(type);
1825 
1826   uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
1827   if (src_pos.IsConstant()) {
1828     int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
1829     __ Add(src_base, src, element_size * constant + data_offset);
1830   } else {
1831     __ Add(src_base, src, data_offset);
1832     __ Add(src_base, src_base, Operand(XRegisterFrom(src_pos), LSL, element_size_shift));
1833   }
1834 
1835   if (dst_pos.IsConstant()) {
1836     int32_t constant = dst_pos.GetConstant()->AsIntConstant()->GetValue();
1837     __ Add(dst_base, dst, element_size * constant + data_offset);
1838   } else {
1839     __ Add(dst_base, dst, data_offset);
1840     __ Add(dst_base, dst_base, Operand(XRegisterFrom(dst_pos), LSL, element_size_shift));
1841   }
1842 
1843   if (copy_length.IsConstant()) {
1844     int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
1845     __ Add(src_end, src_base, element_size * constant);
1846   } else {
1847     __ Add(src_end, src_base, Operand(XRegisterFrom(copy_length), LSL, element_size_shift));
1848   }
1849 }
1850 
VisitSystemArrayCopyChar(HInvoke * invoke)1851 void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
1852   vixl::MacroAssembler* masm = GetVIXLAssembler();
1853   LocationSummary* locations = invoke->GetLocations();
1854   Register src = XRegisterFrom(locations->InAt(0));
1855   Location src_pos = locations->InAt(1);
1856   Register dst = XRegisterFrom(locations->InAt(2));
1857   Location dst_pos = locations->InAt(3);
1858   Location length = locations->InAt(4);
1859 
1860   SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1861   codegen_->AddSlowPath(slow_path);
1862 
1863   // If source and destination are the same, take the slow path. Overlapping copy regions must be
1864   // copied in reverse and we can't know in all cases if it's needed.
1865   __ Cmp(src, dst);
1866   __ B(slow_path->GetEntryLabel(), eq);
1867 
1868   // Bail out if the source is null.
1869   __ Cbz(src, slow_path->GetEntryLabel());
1870 
1871   // Bail out if the destination is null.
1872   __ Cbz(dst, slow_path->GetEntryLabel());
1873 
1874   if (!length.IsConstant()) {
1875     // If the length is negative, bail out.
1876     __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel());
1877     // If the length > 32 then (currently) prefer libcore's native implementation.
1878     __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold);
1879     __ B(slow_path->GetEntryLabel(), gt);
1880   } else {
1881     // We have already checked in the LocationsBuilder for the constant case.
1882     DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0);
1883     DCHECK_LE(length.GetConstant()->AsIntConstant()->GetValue(), 32);
1884   }
1885 
1886   Register src_curr_addr = WRegisterFrom(locations->GetTemp(0));
1887   Register dst_curr_addr = WRegisterFrom(locations->GetTemp(1));
1888   Register src_stop_addr = WRegisterFrom(locations->GetTemp(2));
1889 
1890   CheckSystemArrayCopyPosition(masm,
1891                                src_pos,
1892                                src,
1893                                length,
1894                                slow_path,
1895                                src_curr_addr,
1896                                dst_curr_addr,
1897                                false);
1898 
1899   CheckSystemArrayCopyPosition(masm,
1900                                dst_pos,
1901                                dst,
1902                                length,
1903                                slow_path,
1904                                src_curr_addr,
1905                                dst_curr_addr,
1906                                false);
1907 
1908   src_curr_addr = src_curr_addr.X();
1909   dst_curr_addr = dst_curr_addr.X();
1910   src_stop_addr = src_stop_addr.X();
1911 
1912   GenSystemArrayCopyAddresses(masm,
1913                               Primitive::kPrimChar,
1914                               src,
1915                               src_pos,
1916                               dst,
1917                               dst_pos,
1918                               length,
1919                               src_curr_addr,
1920                               dst_curr_addr,
1921                               src_stop_addr);
1922 
1923   // Iterate over the arrays and do a raw copy of the chars.
1924   const int32_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1925   UseScratchRegisterScope temps(masm);
1926   Register tmp = temps.AcquireW();
1927   vixl::Label loop, done;
1928   __ Bind(&loop);
1929   __ Cmp(src_curr_addr, src_stop_addr);
1930   __ B(&done, eq);
1931   __ Ldrh(tmp, MemOperand(src_curr_addr, char_size, vixl::PostIndex));
1932   __ Strh(tmp, MemOperand(dst_curr_addr, char_size, vixl::PostIndex));
1933   __ B(&loop);
1934   __ Bind(&done);
1935 
1936   __ Bind(slow_path->GetExitLabel());
1937 }
1938 
1939 // We can choose to use the native implementation there for longer copy lengths.
1940 static constexpr int32_t kSystemArrayCopyThreshold = 128;
1941 
1942 // CodeGenerator::CreateSystemArrayCopyLocationSummary use three temporary registers.
1943 // We want to use two temporary registers in order to reduce the register pressure in arm64.
1944 // So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary.
VisitSystemArrayCopy(HInvoke * invoke)1945 void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) {
1946   // Check to see if we have known failures that will cause us to have to bail out
1947   // to the runtime, and just generate the runtime call directly.
1948   HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1949   HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
1950 
1951   // The positions must be non-negative.
1952   if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
1953       (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
1954     // We will have to fail anyways.
1955     return;
1956   }
1957 
1958   // The length must be >= 0.
1959   HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1960   if (length != nullptr) {
1961     int32_t len = length->GetValue();
1962     if (len < 0 || len >= kSystemArrayCopyThreshold) {
1963       // Just call as normal.
1964       return;
1965     }
1966   }
1967 
1968   SystemArrayCopyOptimizations optimizations(invoke);
1969 
1970   if (optimizations.GetDestinationIsSource()) {
1971     if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) {
1972       // We only support backward copying if source and destination are the same.
1973       return;
1974     }
1975   }
1976 
1977   if (optimizations.GetDestinationIsPrimitiveArray() || optimizations.GetSourceIsPrimitiveArray()) {
1978     // We currently don't intrinsify primitive copying.
1979     return;
1980   }
1981 
1982   ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
1983   LocationSummary* locations = new (allocator) LocationSummary(invoke,
1984                                                                LocationSummary::kCallOnSlowPath,
1985                                                                kIntrinsified);
1986   // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length).
1987   locations->SetInAt(0, Location::RequiresRegister());
1988   SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
1989   locations->SetInAt(2, Location::RequiresRegister());
1990   SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
1991   SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
1992 
1993   locations->AddTemp(Location::RequiresRegister());
1994   locations->AddTemp(Location::RequiresRegister());
1995 }
1996 
VisitSystemArrayCopy(HInvoke * invoke)1997 void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) {
1998   vixl::MacroAssembler* masm = GetVIXLAssembler();
1999   LocationSummary* locations = invoke->GetLocations();
2000 
2001   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2002   uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2003   uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2004   uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
2005 
2006   Register src = XRegisterFrom(locations->InAt(0));
2007   Location src_pos = locations->InAt(1);
2008   Register dest = XRegisterFrom(locations->InAt(2));
2009   Location dest_pos = locations->InAt(3);
2010   Location length = locations->InAt(4);
2011   Register temp1 = WRegisterFrom(locations->GetTemp(0));
2012   Register temp2 = WRegisterFrom(locations->GetTemp(1));
2013 
2014   SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2015   codegen_->AddSlowPath(slow_path);
2016 
2017   vixl::Label conditions_on_positions_validated;
2018   SystemArrayCopyOptimizations optimizations(invoke);
2019 
2020   // If source and destination are the same, we go to slow path if we need to do
2021   // forward copying.
2022   if (src_pos.IsConstant()) {
2023     int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
2024     if (dest_pos.IsConstant()) {
2025       int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
2026       if (optimizations.GetDestinationIsSource()) {
2027         // Checked when building locations.
2028         DCHECK_GE(src_pos_constant, dest_pos_constant);
2029       } else if (src_pos_constant < dest_pos_constant) {
2030         __ Cmp(src, dest);
2031         __ B(slow_path->GetEntryLabel(), eq);
2032       }
2033       // Checked when building locations.
2034       DCHECK(!optimizations.GetDestinationIsSource()
2035              || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
2036     } else {
2037       if (!optimizations.GetDestinationIsSource()) {
2038         __ Cmp(src, dest);
2039         __ B(&conditions_on_positions_validated, ne);
2040       }
2041       __ Cmp(WRegisterFrom(dest_pos), src_pos_constant);
2042       __ B(slow_path->GetEntryLabel(), gt);
2043     }
2044   } else {
2045     if (!optimizations.GetDestinationIsSource()) {
2046       __ Cmp(src, dest);
2047       __ B(&conditions_on_positions_validated, ne);
2048     }
2049     __ Cmp(RegisterFrom(src_pos, invoke->InputAt(1)->GetType()),
2050            OperandFrom(dest_pos, invoke->InputAt(3)->GetType()));
2051     __ B(slow_path->GetEntryLabel(), lt);
2052   }
2053 
2054   __ Bind(&conditions_on_positions_validated);
2055 
2056   if (!optimizations.GetSourceIsNotNull()) {
2057     // Bail out if the source is null.
2058     __ Cbz(src, slow_path->GetEntryLabel());
2059   }
2060 
2061   if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
2062     // Bail out if the destination is null.
2063     __ Cbz(dest, slow_path->GetEntryLabel());
2064   }
2065 
2066   // We have already checked in the LocationsBuilder for the constant case.
2067   if (!length.IsConstant() &&
2068       !optimizations.GetCountIsSourceLength() &&
2069       !optimizations.GetCountIsDestinationLength()) {
2070     // If the length is negative, bail out.
2071     __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel());
2072     // If the length >= 128 then (currently) prefer native implementation.
2073     __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold);
2074     __ B(slow_path->GetEntryLabel(), ge);
2075   }
2076   // Validity checks: source.
2077   CheckSystemArrayCopyPosition(masm,
2078                                src_pos,
2079                                src,
2080                                length,
2081                                slow_path,
2082                                temp1,
2083                                temp2,
2084                                optimizations.GetCountIsSourceLength());
2085 
2086   // Validity checks: dest.
2087   CheckSystemArrayCopyPosition(masm,
2088                                dest_pos,
2089                                dest,
2090                                length,
2091                                slow_path,
2092                                temp1,
2093                                temp2,
2094                                optimizations.GetCountIsDestinationLength());
2095   {
2096     // We use a block to end the scratch scope before the write barrier, thus
2097     // freeing the temporary registers so they can be used in `MarkGCCard`.
2098     UseScratchRegisterScope temps(masm);
2099     Register temp3 = temps.AcquireW();
2100     if (!optimizations.GetDoesNotNeedTypeCheck()) {
2101       // Check whether all elements of the source array are assignable to the component
2102       // type of the destination array. We do two checks: the classes are the same,
2103       // or the destination is Object[]. If none of these checks succeed, we go to the
2104       // slow path.
2105       __ Ldr(temp1, MemOperand(dest, class_offset));
2106       __ Ldr(temp2, MemOperand(src, class_offset));
2107       bool did_unpoison = false;
2108       if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2109           !optimizations.GetSourceIsNonPrimitiveArray()) {
2110         // One or two of the references need to be unpoisoned. Unpoison them
2111         // both to make the identity check valid.
2112         codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2113         codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2114         did_unpoison = true;
2115       }
2116 
2117       if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2118         // Bail out if the destination is not a non primitive array.
2119         // /* HeapReference<Class> */ temp3 = temp1->component_type_
2120         __ Ldr(temp3, HeapOperand(temp1, component_offset));
2121         __ Cbz(temp3, slow_path->GetEntryLabel());
2122         codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2123         __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2124         static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2125         __ Cbnz(temp3, slow_path->GetEntryLabel());
2126       }
2127 
2128       if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2129         // Bail out if the source is not a non primitive array.
2130         // /* HeapReference<Class> */ temp3 = temp2->component_type_
2131         __ Ldr(temp3, HeapOperand(temp2, component_offset));
2132         __ Cbz(temp3, slow_path->GetEntryLabel());
2133         codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2134         __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2135         static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2136         __ Cbnz(temp3, slow_path->GetEntryLabel());
2137       }
2138 
2139       __ Cmp(temp1, temp2);
2140 
2141       if (optimizations.GetDestinationIsTypedObjectArray()) {
2142         vixl::Label do_copy;
2143         __ B(&do_copy, eq);
2144         if (!did_unpoison) {
2145           codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2146         }
2147         // /* HeapReference<Class> */ temp1 = temp1->component_type_
2148         __ Ldr(temp1, HeapOperand(temp1, component_offset));
2149         codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2150         // /* HeapReference<Class> */ temp1 = temp1->super_class_
2151         __ Ldr(temp1, HeapOperand(temp1, super_offset));
2152         // No need to unpoison the result, we're comparing against null.
2153         __ Cbnz(temp1, slow_path->GetEntryLabel());
2154         __ Bind(&do_copy);
2155       } else {
2156         __ B(slow_path->GetEntryLabel(), ne);
2157       }
2158     } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2159       DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2160       // Bail out if the source is not a non primitive array.
2161       // /* HeapReference<Class> */ temp1 = src->klass_
2162       __ Ldr(temp1, HeapOperand(src.W(), class_offset));
2163       codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2164       // /* HeapReference<Class> */ temp3 = temp1->component_type_
2165       __ Ldr(temp3, HeapOperand(temp1, component_offset));
2166       __ Cbz(temp3, slow_path->GetEntryLabel());
2167       codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2168       __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2169       static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2170       __ Cbnz(temp3, slow_path->GetEntryLabel());
2171     }
2172 
2173     Register src_curr_addr = temp1.X();
2174     Register dst_curr_addr = temp2.X();
2175     Register src_stop_addr = temp3.X();
2176 
2177     GenSystemArrayCopyAddresses(masm,
2178                                 Primitive::kPrimNot,
2179                                 src,
2180                                 src_pos,
2181                                 dest,
2182                                 dest_pos,
2183                                 length,
2184                                 src_curr_addr,
2185                                 dst_curr_addr,
2186                                 src_stop_addr);
2187 
2188     // Iterate over the arrays and do a raw copy of the objects. We don't need to
2189     // poison/unpoison, nor do any read barrier as the next uses of the destination
2190     // array will do it.
2191     vixl::Label loop, done;
2192     const int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
2193     __ Bind(&loop);
2194     __ Cmp(src_curr_addr, src_stop_addr);
2195     __ B(&done, eq);
2196     {
2197       Register tmp = temps.AcquireW();
2198       __ Ldr(tmp, MemOperand(src_curr_addr, element_size, vixl::PostIndex));
2199       __ Str(tmp, MemOperand(dst_curr_addr, element_size, vixl::PostIndex));
2200     }
2201     __ B(&loop);
2202     __ Bind(&done);
2203   }
2204   // We only need one card marking on the destination array.
2205   codegen_->MarkGCCard(dest.W(), Register(), /* value_can_be_null */ false);
2206 
2207   __ Bind(slow_path->GetExitLabel());
2208 }
2209 
2210 UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
2211 UNIMPLEMENTED_INTRINSIC(ARM64, FloatIsInfinite)
2212 UNIMPLEMENTED_INTRINSIC(ARM64, DoubleIsInfinite)
2213 UNIMPLEMENTED_INTRINSIC(ARM64, IntegerHighestOneBit)
2214 UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit)
2215 UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit)
2216 UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit)
2217 
2218 // 1.8.
2219 UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt)
2220 UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong)
2221 UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt)
2222 UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong)
2223 UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject)
2224 
2225 UNREACHABLE_INTRINSICS(ARM64)
2226 
2227 #undef __
2228 
2229 }  // namespace arm64
2230 }  // namespace art
2231