blob: 4da0843a76cc449e77cb52f23864048a6a92f434 [file] [log] [blame]
Andreas Gampe878d58c2015-01-15 23:24:00 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "intrinsics_arm64.h"
18
Serban Constantinescu579885a2015-02-22 20:51:33 +000019#include "arch/arm64/instruction_set_features_arm64.h"
Mathieu Chartiere401d142015-04-22 13:56:20 -070020#include "art_method.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080021#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"
Andreas Gampe878d58c2015-01-15 23:24:00 -080026#include "mirror/string.h"
27#include "thread.h"
28#include "utils/arm64/assembler_arm64.h"
29#include "utils/arm64/constants_arm64.h"
30
Serban Constantinescu82e52ce2015-03-26 16:50:57 +000031#include "vixl/a64/disasm-a64.h"
32#include "vixl/a64/macro-assembler-a64.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080033
34using namespace vixl; // NOLINT(build/namespaces)
35
36namespace art {
37
38namespace arm64 {
39
40using helpers::DRegisterFrom;
41using helpers::FPRegisterFrom;
42using helpers::HeapOperand;
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +000043using helpers::LocationFrom;
Scott Wakeling9ee23f42015-07-23 10:44:35 +010044using helpers::OperandFrom;
Andreas Gampe878d58c2015-01-15 23:24:00 -080045using helpers::RegisterFrom;
46using helpers::SRegisterFrom;
47using helpers::WRegisterFrom;
48using helpers::XRegisterFrom;
xueliang.zhong49924c92016-03-03 10:52:51 +000049using helpers::InputRegisterAt;
Scott Wakeling1f36f412016-04-21 11:13:45 +010050using helpers::OutputRegister;
Andreas Gampe878d58c2015-01-15 23:24:00 -080051
Andreas Gampe878d58c2015-01-15 23:24:00 -080052namespace {
53
54ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) {
55 return MemOperand(XRegisterFrom(location), offset);
56}
57
58} // namespace
59
60vixl::MacroAssembler* IntrinsicCodeGeneratorARM64::GetVIXLAssembler() {
61 return codegen_->GetAssembler()->vixl_masm_;
62}
63
64ArenaAllocator* IntrinsicCodeGeneratorARM64::GetAllocator() {
65 return codegen_->GetGraph()->GetArena();
66}
67
68#define __ codegen->GetAssembler()->vixl_masm_->
69
70static void MoveFromReturnRegister(Location trg,
71 Primitive::Type type,
72 CodeGeneratorARM64* codegen) {
73 if (!trg.IsValid()) {
74 DCHECK(type == Primitive::kPrimVoid);
75 return;
76 }
77
78 DCHECK_NE(type, Primitive::kPrimVoid);
79
Jeff Hao848f70a2014-01-15 13:49:50 -080080 if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) {
Andreas Gampe878d58c2015-01-15 23:24:00 -080081 Register trg_reg = RegisterFrom(trg, type);
82 Register res_reg = RegisterFrom(ARM64ReturnLocation(type), type);
83 __ Mov(trg_reg, res_reg, kDiscardForSameWReg);
84 } else {
85 FPRegister trg_reg = FPRegisterFrom(trg, type);
86 FPRegister res_reg = FPRegisterFrom(ARM64ReturnLocation(type), type);
87 __ Fmov(trg_reg, res_reg);
88 }
89}
90
Roland Levillainec525fc2015-04-28 15:50:20 +010091static void MoveArguments(HInvoke* invoke, CodeGeneratorARM64* codegen) {
Roland Levillain2d27c8e2015-04-28 15:48:45 +010092 InvokeDexCallingConventionVisitorARM64 calling_convention_visitor;
Roland Levillainec525fc2015-04-28 15:50:20 +010093 IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor);
Andreas Gampe878d58c2015-01-15 23:24:00 -080094}
95
96// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
97// call. This will copy the arguments into the positions for a regular call.
98//
99// Note: The actual parameters are required to be in the locations given by the invoke's location
100// summary. If an intrinsic modifies those locations before a slowpath call, they must be
101// restored!
102class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 {
103 public:
David Srbecky9cd6d372016-02-09 15:24:47 +0000104 explicit IntrinsicSlowPathARM64(HInvoke* invoke)
105 : SlowPathCodeARM64(invoke), invoke_(invoke) { }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800106
107 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
108 CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
109 __ Bind(GetEntryLabel());
110
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000111 SaveLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800112
Roland Levillainec525fc2015-04-28 15:50:20 +0100113 MoveArguments(invoke_, codegen);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800114
115 if (invoke_->IsInvokeStaticOrDirect()) {
Nicolas Geoffray94015b92015-06-04 18:21:04 +0100116 codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
117 LocationFrom(kArtMethodRegister));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800118 } else {
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000119 codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800120 }
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000121 codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800122
123 // Copy the result back to the expected output.
124 Location out = invoke_->GetLocations()->Out();
125 if (out.IsValid()) {
126 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
127 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
128 MoveFromReturnRegister(out, invoke_->GetType(), codegen);
129 }
130
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000131 RestoreLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800132 __ B(GetExitLabel());
133 }
134
Alexandre Rames9931f312015-06-19 14:47:01 +0100135 const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathARM64"; }
136
Andreas Gampe878d58c2015-01-15 23:24:00 -0800137 private:
138 // The instruction where this slow path is happening.
139 HInvoke* const invoke_;
140
141 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM64);
142};
143
144#undef __
145
146bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) {
147 Dispatch(invoke);
148 LocationSummary* res = invoke->GetLocations();
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000149 if (res == nullptr) {
150 return false;
151 }
152 if (kEmitCompilerReadBarrier && res->CanCall()) {
153 // Generating an intrinsic for this HInvoke may produce an
154 // IntrinsicSlowPathARM64 slow path. Currently this approach
155 // does not work when using read barriers, as the emitted
156 // calling sequence will make use of another slow path
157 // (ReadBarrierForRootSlowPathARM64 for HInvokeStaticOrDirect,
158 // ReadBarrierSlowPathARM64 for HInvokeVirtual). So we bail
159 // out in this case.
160 //
161 // TODO: Find a way to have intrinsics work with read barriers.
162 invoke->SetLocations(nullptr);
163 return false;
164 }
165 return res->Intrinsified();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800166}
167
168#define __ masm->
169
170static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
171 LocationSummary* locations = new (arena) LocationSummary(invoke,
172 LocationSummary::kNoCall,
173 kIntrinsified);
174 locations->SetInAt(0, Location::RequiresFpuRegister());
175 locations->SetOut(Location::RequiresRegister());
176}
177
178static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
179 LocationSummary* locations = new (arena) LocationSummary(invoke,
180 LocationSummary::kNoCall,
181 kIntrinsified);
182 locations->SetInAt(0, Location::RequiresRegister());
183 locations->SetOut(Location::RequiresFpuRegister());
184}
185
186static void MoveFPToInt(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
187 Location input = locations->InAt(0);
188 Location output = locations->Out();
189 __ Fmov(is64bit ? XRegisterFrom(output) : WRegisterFrom(output),
190 is64bit ? DRegisterFrom(input) : SRegisterFrom(input));
191}
192
193static void MoveIntToFP(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
194 Location input = locations->InAt(0);
195 Location output = locations->Out();
196 __ Fmov(is64bit ? DRegisterFrom(output) : SRegisterFrom(output),
197 is64bit ? XRegisterFrom(input) : WRegisterFrom(input));
198}
199
200void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
201 CreateFPToIntLocations(arena_, invoke);
202}
203void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
204 CreateIntToFPLocations(arena_, invoke);
205}
206
207void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000208 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800209}
210void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000211 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800212}
213
214void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
215 CreateFPToIntLocations(arena_, invoke);
216}
217void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
218 CreateIntToFPLocations(arena_, invoke);
219}
220
221void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000222 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800223}
224void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000225 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800226}
227
228static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
229 LocationSummary* locations = new (arena) LocationSummary(invoke,
230 LocationSummary::kNoCall,
231 kIntrinsified);
232 locations->SetInAt(0, Location::RequiresRegister());
233 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
234}
235
236static void GenReverseBytes(LocationSummary* locations,
237 Primitive::Type type,
238 vixl::MacroAssembler* masm) {
239 Location in = locations->InAt(0);
240 Location out = locations->Out();
241
242 switch (type) {
243 case Primitive::kPrimShort:
244 __ Rev16(WRegisterFrom(out), WRegisterFrom(in));
245 __ Sxth(WRegisterFrom(out), WRegisterFrom(out));
246 break;
247 case Primitive::kPrimInt:
248 case Primitive::kPrimLong:
249 __ Rev(RegisterFrom(out, type), RegisterFrom(in, type));
250 break;
251 default:
252 LOG(FATAL) << "Unexpected size for reverse-bytes: " << type;
253 UNREACHABLE();
254 }
255}
256
257void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
258 CreateIntToIntLocations(arena_, invoke);
259}
260
261void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
262 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
263}
264
265void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) {
266 CreateIntToIntLocations(arena_, invoke);
267}
268
269void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) {
270 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
271}
272
273void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) {
274 CreateIntToIntLocations(arena_, invoke);
275}
276
277void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
278 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
279}
280
Aart Bik7b565022016-01-28 14:36:22 -0800281static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
282 LocationSummary* locations = new (arena) LocationSummary(invoke,
283 LocationSummary::kNoCall,
284 kIntrinsified);
285 locations->SetInAt(0, Location::RequiresRegister());
286 locations->SetInAt(1, Location::RequiresRegister());
287 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
288}
289
Scott Wakeling611d3392015-07-10 11:42:06 +0100290static void GenNumberOfLeadingZeros(LocationSummary* locations,
291 Primitive::Type type,
292 vixl::MacroAssembler* masm) {
293 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
294
295 Location in = locations->InAt(0);
296 Location out = locations->Out();
297
298 __ Clz(RegisterFrom(out, type), RegisterFrom(in, type));
299}
300
301void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
302 CreateIntToIntLocations(arena_, invoke);
303}
304
305void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
306 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
307}
308
309void IntrinsicLocationsBuilderARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
310 CreateIntToIntLocations(arena_, invoke);
311}
312
313void IntrinsicCodeGeneratorARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
314 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
315}
316
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100317static void GenNumberOfTrailingZeros(LocationSummary* locations,
318 Primitive::Type type,
319 vixl::MacroAssembler* masm) {
320 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
321
322 Location in = locations->InAt(0);
323 Location out = locations->Out();
324
325 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
326 __ Clz(RegisterFrom(out, type), RegisterFrom(out, type));
327}
328
329void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
330 CreateIntToIntLocations(arena_, invoke);
331}
332
333void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
334 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
335}
336
337void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
338 CreateIntToIntLocations(arena_, invoke);
339}
340
341void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
342 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
343}
344
Andreas Gampe878d58c2015-01-15 23:24:00 -0800345static void GenReverse(LocationSummary* locations,
346 Primitive::Type type,
347 vixl::MacroAssembler* masm) {
348 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
349
350 Location in = locations->InAt(0);
351 Location out = locations->Out();
352
353 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
354}
355
356void IntrinsicLocationsBuilderARM64::VisitIntegerReverse(HInvoke* invoke) {
357 CreateIntToIntLocations(arena_, invoke);
358}
359
360void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) {
361 GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
362}
363
364void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) {
365 CreateIntToIntLocations(arena_, invoke);
366}
367
368void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
369 GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
370}
371
Roland Levillainfa3912e2016-04-01 18:21:55 +0100372static void GenBitCount(HInvoke* instr, Primitive::Type type, vixl::MacroAssembler* masm) {
373 DCHECK(Primitive::IsIntOrLongType(type)) << type;
374 DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
375 DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
xueliang.zhong49924c92016-03-03 10:52:51 +0000376
xueliang.zhong49924c92016-03-03 10:52:51 +0000377 UseScratchRegisterScope temps(masm);
378
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000379 Register src = InputRegisterAt(instr, 0);
Roland Levillainfa3912e2016-04-01 18:21:55 +0100380 Register dst = RegisterFrom(instr->GetLocations()->Out(), type);
381 FPRegister fpr = (type == Primitive::kPrimLong) ? temps.AcquireD() : temps.AcquireS();
xueliang.zhong49924c92016-03-03 10:52:51 +0000382
383 __ Fmov(fpr, src);
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000384 __ Cnt(fpr.V8B(), fpr.V8B());
385 __ Addv(fpr.B(), fpr.V8B());
xueliang.zhong49924c92016-03-03 10:52:51 +0000386 __ Fmov(dst, fpr);
387}
388
389void IntrinsicLocationsBuilderARM64::VisitLongBitCount(HInvoke* invoke) {
390 CreateIntToIntLocations(arena_, invoke);
391}
392
393void IntrinsicCodeGeneratorARM64::VisitLongBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100394 GenBitCount(invoke, Primitive::kPrimLong, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000395}
396
397void IntrinsicLocationsBuilderARM64::VisitIntegerBitCount(HInvoke* invoke) {
398 CreateIntToIntLocations(arena_, invoke);
399}
400
401void IntrinsicCodeGeneratorARM64::VisitIntegerBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100402 GenBitCount(invoke, Primitive::kPrimInt, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000403}
404
Andreas Gampe878d58c2015-01-15 23:24:00 -0800405static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800406 LocationSummary* locations = new (arena) LocationSummary(invoke,
407 LocationSummary::kNoCall,
408 kIntrinsified);
409 locations->SetInAt(0, Location::RequiresFpuRegister());
410 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
411}
412
413static void MathAbsFP(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
414 Location in = locations->InAt(0);
415 Location out = locations->Out();
416
417 FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in);
418 FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out);
419
420 __ Fabs(out_reg, in_reg);
421}
422
423void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) {
424 CreateFPToFPLocations(arena_, invoke);
425}
426
427void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000428 MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800429}
430
431void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) {
432 CreateFPToFPLocations(arena_, invoke);
433}
434
435void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000436 MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800437}
438
439static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
440 LocationSummary* locations = new (arena) LocationSummary(invoke,
441 LocationSummary::kNoCall,
442 kIntrinsified);
443 locations->SetInAt(0, Location::RequiresRegister());
444 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
445}
446
447static void GenAbsInteger(LocationSummary* locations,
448 bool is64bit,
449 vixl::MacroAssembler* masm) {
450 Location in = locations->InAt(0);
451 Location output = locations->Out();
452
453 Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in);
454 Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output);
455
456 __ Cmp(in_reg, Operand(0));
457 __ Cneg(out_reg, in_reg, lt);
458}
459
460void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
461 CreateIntToInt(arena_, invoke);
462}
463
464void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000465 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800466}
467
468void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
469 CreateIntToInt(arena_, invoke);
470}
471
472void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000473 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800474}
475
476static void GenMinMaxFP(LocationSummary* locations,
477 bool is_min,
478 bool is_double,
479 vixl::MacroAssembler* masm) {
480 Location op1 = locations->InAt(0);
481 Location op2 = locations->InAt(1);
482 Location out = locations->Out();
483
484 FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1);
485 FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2);
486 FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out);
487 if (is_min) {
488 __ Fmin(out_reg, op1_reg, op2_reg);
489 } else {
490 __ Fmax(out_reg, op1_reg, op2_reg);
491 }
492}
493
494static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
495 LocationSummary* locations = new (arena) LocationSummary(invoke,
496 LocationSummary::kNoCall,
497 kIntrinsified);
498 locations->SetInAt(0, Location::RequiresFpuRegister());
499 locations->SetInAt(1, Location::RequiresFpuRegister());
500 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
501}
502
503void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
504 CreateFPFPToFPLocations(arena_, invoke);
505}
506
507void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000508 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800509}
510
511void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
512 CreateFPFPToFPLocations(arena_, invoke);
513}
514
515void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000516 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800517}
518
519void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
520 CreateFPFPToFPLocations(arena_, invoke);
521}
522
523void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000524 GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800525}
526
527void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
528 CreateFPFPToFPLocations(arena_, invoke);
529}
530
531void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000532 GenMinMaxFP(
533 invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800534}
535
536static void GenMinMax(LocationSummary* locations,
537 bool is_min,
538 bool is_long,
539 vixl::MacroAssembler* masm) {
540 Location op1 = locations->InAt(0);
541 Location op2 = locations->InAt(1);
542 Location out = locations->Out();
543
544 Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
545 Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
546 Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
547
548 __ Cmp(op1_reg, op2_reg);
549 __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
550}
551
Andreas Gampe878d58c2015-01-15 23:24:00 -0800552void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
553 CreateIntIntToIntLocations(arena_, invoke);
554}
555
556void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000557 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800558}
559
560void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) {
561 CreateIntIntToIntLocations(arena_, invoke);
562}
563
564void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000565 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800566}
567
568void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) {
569 CreateIntIntToIntLocations(arena_, invoke);
570}
571
572void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000573 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800574}
575
576void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) {
577 CreateIntIntToIntLocations(arena_, invoke);
578}
579
580void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000581 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800582}
583
584void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) {
585 CreateFPToFPLocations(arena_, invoke);
586}
587
588void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) {
589 LocationSummary* locations = invoke->GetLocations();
590 vixl::MacroAssembler* masm = GetVIXLAssembler();
591 __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
592}
593
594void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) {
595 CreateFPToFPLocations(arena_, invoke);
596}
597
598void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) {
599 LocationSummary* locations = invoke->GetLocations();
600 vixl::MacroAssembler* masm = GetVIXLAssembler();
601 __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
602}
603
604void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) {
605 CreateFPToFPLocations(arena_, invoke);
606}
607
608void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) {
609 LocationSummary* locations = invoke->GetLocations();
610 vixl::MacroAssembler* masm = GetVIXLAssembler();
611 __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
612}
613
614void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) {
615 CreateFPToFPLocations(arena_, invoke);
616}
617
618void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) {
619 LocationSummary* locations = invoke->GetLocations();
620 vixl::MacroAssembler* masm = GetVIXLAssembler();
621 __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
622}
623
624static void CreateFPToIntPlusTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
625 LocationSummary* locations = new (arena) LocationSummary(invoke,
626 LocationSummary::kNoCall,
627 kIntrinsified);
628 locations->SetInAt(0, Location::RequiresFpuRegister());
629 locations->SetOut(Location::RequiresRegister());
630}
631
632static void GenMathRound(LocationSummary* locations,
633 bool is_double,
634 vixl::MacroAssembler* masm) {
635 FPRegister in_reg = is_double ?
636 DRegisterFrom(locations->InAt(0)) : SRegisterFrom(locations->InAt(0));
637 Register out_reg = is_double ?
638 XRegisterFrom(locations->Out()) : WRegisterFrom(locations->Out());
639 UseScratchRegisterScope temps(masm);
640 FPRegister temp1_reg = temps.AcquireSameSizeAs(in_reg);
641
642 // 0.5 can be encoded as an immediate, so use fmov.
643 if (is_double) {
644 __ Fmov(temp1_reg, static_cast<double>(0.5));
645 } else {
646 __ Fmov(temp1_reg, static_cast<float>(0.5));
647 }
648 __ Fadd(temp1_reg, in_reg, temp1_reg);
649 __ Fcvtms(out_reg, temp1_reg);
650}
651
652void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) {
Andreas Gampee6d0d8d2015-12-28 09:54:29 -0800653 // See intrinsics.h.
654 if (kRoundIsPlusPointFive) {
655 CreateFPToIntPlusTempLocations(arena_, invoke);
656 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800657}
658
659void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000660 GenMathRound(invoke->GetLocations(), /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800661}
662
663void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) {
Andreas Gampee6d0d8d2015-12-28 09:54:29 -0800664 // See intrinsics.h.
665 if (kRoundIsPlusPointFive) {
666 CreateFPToIntPlusTempLocations(arena_, invoke);
667 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800668}
669
670void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000671 GenMathRound(invoke->GetLocations(), /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800672}
673
674void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) {
675 CreateIntToIntLocations(arena_, invoke);
676}
677
678void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) {
679 vixl::MacroAssembler* masm = GetVIXLAssembler();
680 __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()),
681 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
682}
683
684void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
685 CreateIntToIntLocations(arena_, invoke);
686}
687
688void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
689 vixl::MacroAssembler* masm = GetVIXLAssembler();
690 __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()),
691 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
692}
693
694void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
695 CreateIntToIntLocations(arena_, invoke);
696}
697
698void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
699 vixl::MacroAssembler* masm = GetVIXLAssembler();
700 __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()),
701 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
702}
703
704void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
705 CreateIntToIntLocations(arena_, invoke);
706}
707
708void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
709 vixl::MacroAssembler* masm = GetVIXLAssembler();
710 __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()),
711 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
712}
713
714static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
715 LocationSummary* locations = new (arena) LocationSummary(invoke,
716 LocationSummary::kNoCall,
717 kIntrinsified);
718 locations->SetInAt(0, Location::RequiresRegister());
719 locations->SetInAt(1, Location::RequiresRegister());
720}
721
722void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) {
723 CreateIntIntToVoidLocations(arena_, invoke);
724}
725
726void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) {
727 vixl::MacroAssembler* masm = GetVIXLAssembler();
728 __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)),
729 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
730}
731
732void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
733 CreateIntIntToVoidLocations(arena_, invoke);
734}
735
736void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
737 vixl::MacroAssembler* masm = GetVIXLAssembler();
738 __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)),
739 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
740}
741
742void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
743 CreateIntIntToVoidLocations(arena_, invoke);
744}
745
746void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
747 vixl::MacroAssembler* masm = GetVIXLAssembler();
748 __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)),
749 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
750}
751
752void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
753 CreateIntIntToVoidLocations(arena_, invoke);
754}
755
756void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
757 vixl::MacroAssembler* masm = GetVIXLAssembler();
758 __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)),
759 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
760}
761
762void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) {
763 LocationSummary* locations = new (arena_) LocationSummary(invoke,
764 LocationSummary::kNoCall,
765 kIntrinsified);
766 locations->SetOut(Location::RequiresRegister());
767}
768
769void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) {
770 codegen_->Load(Primitive::kPrimNot, WRegisterFrom(invoke->GetLocations()->Out()),
771 MemOperand(tr, Thread::PeerOffset<8>().Int32Value()));
772}
773
774static void GenUnsafeGet(HInvoke* invoke,
775 Primitive::Type type,
776 bool is_volatile,
777 CodeGeneratorARM64* codegen) {
778 LocationSummary* locations = invoke->GetLocations();
779 DCHECK((type == Primitive::kPrimInt) ||
780 (type == Primitive::kPrimLong) ||
781 (type == Primitive::kPrimNot));
782 vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000783 Location base_loc = locations->InAt(1);
784 Register base = WRegisterFrom(base_loc); // Object pointer.
785 Location offset_loc = locations->InAt(2);
786 Register offset = XRegisterFrom(offset_loc); // Long offset.
787 Location trg_loc = locations->Out();
788 Register trg = RegisterFrom(trg_loc, type);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800789
Roland Levillain44015862016-01-22 11:47:17 +0000790 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
791 // UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case.
792 UseScratchRegisterScope temps(masm);
793 Register temp = temps.AcquireW();
794 codegen->GenerateArrayLoadWithBakerReadBarrier(
795 invoke, trg_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800796 } else {
Roland Levillain44015862016-01-22 11:47:17 +0000797 // Other cases.
798 MemOperand mem_op(base.X(), offset);
799 if (is_volatile) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +0000800 codegen->LoadAcquire(invoke, trg, mem_op, /* needs_null_check */ true);
Roland Levillain44015862016-01-22 11:47:17 +0000801 } else {
802 codegen->Load(type, trg, mem_op);
803 }
Roland Levillain4d027112015-07-01 15:41:14 +0100804
Roland Levillain44015862016-01-22 11:47:17 +0000805 if (type == Primitive::kPrimNot) {
806 DCHECK(trg.IsW());
807 codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
808 }
Roland Levillain4d027112015-07-01 15:41:14 +0100809 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800810}
811
812static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000813 bool can_call = kEmitCompilerReadBarrier &&
814 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
815 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800816 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000817 can_call ?
818 LocationSummary::kCallOnSlowPath :
819 LocationSummary::kNoCall,
Andreas Gampe878d58c2015-01-15 23:24:00 -0800820 kIntrinsified);
821 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
822 locations->SetInAt(1, Location::RequiresRegister());
823 locations->SetInAt(2, Location::RequiresRegister());
824 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
825}
826
827void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
828 CreateIntIntIntToIntLocations(arena_, invoke);
829}
830void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
831 CreateIntIntIntToIntLocations(arena_, invoke);
832}
833void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
834 CreateIntIntIntToIntLocations(arena_, invoke);
835}
836void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
837 CreateIntIntIntToIntLocations(arena_, invoke);
838}
839void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
840 CreateIntIntIntToIntLocations(arena_, invoke);
841}
842void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
843 CreateIntIntIntToIntLocations(arena_, invoke);
844}
845
846void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000847 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800848}
849void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000850 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800851}
852void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000853 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800854}
855void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000856 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800857}
858void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000859 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800860}
861void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000862 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800863}
864
865static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
866 LocationSummary* locations = new (arena) LocationSummary(invoke,
867 LocationSummary::kNoCall,
868 kIntrinsified);
869 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
870 locations->SetInAt(1, Location::RequiresRegister());
871 locations->SetInAt(2, Location::RequiresRegister());
872 locations->SetInAt(3, Location::RequiresRegister());
873}
874
875void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
876 CreateIntIntIntIntToVoid(arena_, invoke);
877}
878void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
879 CreateIntIntIntIntToVoid(arena_, invoke);
880}
881void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
882 CreateIntIntIntIntToVoid(arena_, invoke);
883}
884void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
885 CreateIntIntIntIntToVoid(arena_, invoke);
886}
887void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
888 CreateIntIntIntIntToVoid(arena_, invoke);
889}
890void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
891 CreateIntIntIntIntToVoid(arena_, invoke);
892}
893void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
894 CreateIntIntIntIntToVoid(arena_, invoke);
895}
896void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
897 CreateIntIntIntIntToVoid(arena_, invoke);
898}
899void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
900 CreateIntIntIntIntToVoid(arena_, invoke);
901}
902
903static void GenUnsafePut(LocationSummary* locations,
904 Primitive::Type type,
905 bool is_volatile,
906 bool is_ordered,
907 CodeGeneratorARM64* codegen) {
908 vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
909
910 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
911 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
912 Register value = RegisterFrom(locations->InAt(3), type);
Roland Levillain4d027112015-07-01 15:41:14 +0100913 Register source = value;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800914 MemOperand mem_op(base.X(), offset);
915
Roland Levillain4d027112015-07-01 15:41:14 +0100916 {
917 // We use a block to end the scratch scope before the write barrier, thus
918 // freeing the temporary registers so they can be used in `MarkGCCard`.
919 UseScratchRegisterScope temps(masm);
920
921 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
922 DCHECK(value.IsW());
923 Register temp = temps.AcquireW();
924 __ Mov(temp.W(), value.W());
925 codegen->GetAssembler()->PoisonHeapReference(temp.W());
926 source = temp;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800927 }
Roland Levillain4d027112015-07-01 15:41:14 +0100928
929 if (is_volatile || is_ordered) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +0000930 codegen->StoreRelease(type, source, mem_op);
Roland Levillain4d027112015-07-01 15:41:14 +0100931 } else {
932 codegen->Store(type, source, mem_op);
933 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800934 }
935
936 if (type == Primitive::kPrimNot) {
Nicolas Geoffray07276db2015-05-18 14:22:09 +0100937 bool value_can_be_null = true; // TODO: Worth finding out this information?
938 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800939 }
940}
941
942void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000943 GenUnsafePut(invoke->GetLocations(),
944 Primitive::kPrimInt,
945 /* is_volatile */ false,
946 /* is_ordered */ false,
947 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800948}
949void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000950 GenUnsafePut(invoke->GetLocations(),
951 Primitive::kPrimInt,
952 /* is_volatile */ false,
953 /* is_ordered */ true,
954 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800955}
956void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000957 GenUnsafePut(invoke->GetLocations(),
958 Primitive::kPrimInt,
959 /* is_volatile */ true,
960 /* is_ordered */ false,
961 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800962}
963void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000964 GenUnsafePut(invoke->GetLocations(),
965 Primitive::kPrimNot,
966 /* is_volatile */ false,
967 /* is_ordered */ false,
968 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800969}
970void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000971 GenUnsafePut(invoke->GetLocations(),
972 Primitive::kPrimNot,
973 /* is_volatile */ false,
974 /* is_ordered */ true,
975 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800976}
977void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000978 GenUnsafePut(invoke->GetLocations(),
979 Primitive::kPrimNot,
980 /* is_volatile */ true,
981 /* is_ordered */ false,
982 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800983}
984void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000985 GenUnsafePut(invoke->GetLocations(),
986 Primitive::kPrimLong,
987 /* is_volatile */ false,
988 /* is_ordered */ false,
989 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800990}
991void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000992 GenUnsafePut(invoke->GetLocations(),
993 Primitive::kPrimLong,
994 /* is_volatile */ false,
995 /* is_ordered */ true,
996 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800997}
998void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000999 GenUnsafePut(invoke->GetLocations(),
1000 Primitive::kPrimLong,
1001 /* is_volatile */ true,
1002 /* is_ordered */ false,
1003 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001004}
1005
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001006static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
1007 HInvoke* invoke,
1008 Primitive::Type type) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08001009 LocationSummary* locations = new (arena) LocationSummary(invoke,
1010 LocationSummary::kNoCall,
1011 kIntrinsified);
1012 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1013 locations->SetInAt(1, Location::RequiresRegister());
1014 locations->SetInAt(2, Location::RequiresRegister());
1015 locations->SetInAt(3, Location::RequiresRegister());
1016 locations->SetInAt(4, Location::RequiresRegister());
1017
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001018 // If heap poisoning is enabled, we don't want the unpoisoning
1019 // operations to potentially clobber the output.
1020 Location::OutputOverlap overlaps = (kPoisonHeapReferences && type == Primitive::kPrimNot)
1021 ? Location::kOutputOverlap
1022 : Location::kNoOutputOverlap;
1023 locations->SetOut(Location::RequiresRegister(), overlaps);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001024}
1025
1026static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM64* codegen) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08001027 vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
1028
1029 Register out = WRegisterFrom(locations->Out()); // Boolean result.
1030
1031 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
1032 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
1033 Register expected = RegisterFrom(locations->InAt(3), type); // Expected.
1034 Register value = RegisterFrom(locations->InAt(4), type); // Value.
1035
1036 // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
1037 if (type == Primitive::kPrimNot) {
1038 // Mark card for object assuming new value is stored.
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001039 bool value_can_be_null = true; // TODO: Worth finding out this information?
1040 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001041 }
1042
1043 UseScratchRegisterScope temps(masm);
1044 Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
1045 Register tmp_value = temps.AcquireSameSizeAs(value); // Value in memory.
1046
1047 Register tmp_32 = tmp_value.W();
1048
1049 __ Add(tmp_ptr, base.X(), Operand(offset));
1050
Roland Levillain4d027112015-07-01 15:41:14 +01001051 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1052 codegen->GetAssembler()->PoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001053 if (value.Is(expected)) {
1054 // Do not poison `value`, as it is the same register as
1055 // `expected`, which has just been poisoned.
1056 } else {
1057 codegen->GetAssembler()->PoisonHeapReference(value);
1058 }
Roland Levillain4d027112015-07-01 15:41:14 +01001059 }
1060
Andreas Gampe878d58c2015-01-15 23:24:00 -08001061 // do {
1062 // tmp_value = [tmp_ptr] - expected;
1063 // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1064 // result = tmp_value != 0;
1065
1066 vixl::Label loop_head, exit_loop;
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001067 __ Bind(&loop_head);
1068 // TODO: When `type == Primitive::kPrimNot`, add a read barrier for
1069 // the reference stored in the object before attempting the CAS,
1070 // similar to the one in the art::Unsafe_compareAndSwapObject JNI
1071 // implementation.
1072 //
1073 // Note that this code is not (yet) used when read barriers are
1074 // enabled (see IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject).
1075 DCHECK(!(type == Primitive::kPrimNot && kEmitCompilerReadBarrier));
1076 __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
1077 __ Cmp(tmp_value, expected);
1078 __ B(&exit_loop, ne);
1079 __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
1080 __ Cbnz(tmp_32, &loop_head);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001081 __ Bind(&exit_loop);
1082 __ Cset(out, eq);
Roland Levillain4d027112015-07-01 15:41:14 +01001083
1084 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillain4d027112015-07-01 15:41:14 +01001085 codegen->GetAssembler()->UnpoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001086 if (value.Is(expected)) {
1087 // Do not unpoison `value`, as it is the same register as
1088 // `expected`, which has just been unpoisoned.
1089 } else {
1090 codegen->GetAssembler()->UnpoisonHeapReference(value);
1091 }
Roland Levillain4d027112015-07-01 15:41:14 +01001092 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001093}
1094
1095void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001096 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001097}
1098void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001099 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001100}
1101void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillain391b8662015-12-18 11:43:38 +00001102 // The UnsafeCASObject intrinsic is missing a read barrier, and
1103 // therefore sometimes does not work as expected (b/25883050).
1104 // Turn it off temporarily as a quick fix, until the read barrier is
1105 // implemented (see TODO in GenCAS below).
1106 //
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001107 // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers.
1108 if (kEmitCompilerReadBarrier) {
Roland Levillain985ff702015-10-23 13:25:35 +01001109 return;
1110 }
1111
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001112 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001113}
1114
1115void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
1116 GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
1117}
1118void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
1119 GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_);
1120}
1121void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
1122 GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
1123}
1124
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001125void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001126 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Scott Wakeling1f36f412016-04-21 11:13:45 +01001127 invoke->InputAt(1)->CanBeNull()
1128 ? LocationSummary::kCallOnSlowPath
1129 : LocationSummary::kNoCall,
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001130 kIntrinsified);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001131 locations->SetInAt(0, Location::RequiresRegister());
1132 locations->SetInAt(1, Location::RequiresRegister());
1133 locations->AddTemp(Location::RequiresRegister());
1134 locations->AddTemp(Location::RequiresRegister());
1135 locations->AddTemp(Location::RequiresRegister());
1136 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001137}
1138
1139void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
1140 vixl::MacroAssembler* masm = GetVIXLAssembler();
1141 LocationSummary* locations = invoke->GetLocations();
1142
Scott Wakeling1f36f412016-04-21 11:13:45 +01001143 Register str = XRegisterFrom(locations->InAt(0));
1144 Register arg = XRegisterFrom(locations->InAt(1));
1145 Register out = OutputRegister(invoke);
1146
1147 Register temp0 = WRegisterFrom(locations->GetTemp(0));
1148 Register temp1 = WRegisterFrom(locations->GetTemp(1));
1149 Register temp2 = WRegisterFrom(locations->GetTemp(2));
1150
1151 vixl::Label loop;
1152 vixl::Label find_char_diff;
1153 vixl::Label end;
1154
1155 // Get offsets of count and value fields within a string object.
1156 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1157 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1158
Nicolas Geoffray512e04d2015-03-27 17:21:24 +00001159 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +01001160 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001161
Scott Wakeling1f36f412016-04-21 11:13:45 +01001162 // Take slow path and throw if input can be and is null.
1163 SlowPathCodeARM64* slow_path = nullptr;
1164 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1165 if (can_slow_path) {
1166 slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1167 codegen_->AddSlowPath(slow_path);
1168 __ Cbz(arg, slow_path->GetEntryLabel());
1169 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001170
Scott Wakeling1f36f412016-04-21 11:13:45 +01001171 // Reference equality check, return 0 if same reference.
1172 __ Subs(out, str, arg);
1173 __ B(&end, eq);
1174 // Load lengths of this and argument strings.
1175 __ Ldr(temp0, MemOperand(str.X(), count_offset));
1176 __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1177 // Return zero if both strings are empty.
1178 __ Orr(out, temp0, temp1);
1179 __ Cbz(out, &end);
1180 // out = length diff.
1181 __ Subs(out, temp0, temp1);
1182 // temp2 = min(len(str), len(arg)).
1183 __ Csel(temp2, temp1, temp0, ge);
1184 // Shorter string is empty?
1185 __ Cbz(temp2, &end);
1186
1187 // Store offset of string value in preparation for comparison loop.
1188 __ Mov(temp1, value_offset);
1189
1190 UseScratchRegisterScope scratch_scope(masm);
1191 Register temp4 = scratch_scope.AcquireX();
1192
1193 // Assertions that must hold in order to compare strings 4 characters at a time.
1194 DCHECK_ALIGNED(value_offset, 8);
1195 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1196
1197 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1198 DCHECK_EQ(char_size, 2u);
1199
1200 // Promote temp0 to an X reg, ready for LDR.
1201 temp0 = temp0.X();
1202
1203 // Loop to compare 4x16-bit characters at a time (ok because of string data alignment).
1204 __ Bind(&loop);
1205 __ Ldr(temp4, MemOperand(str.X(), temp1));
1206 __ Ldr(temp0, MemOperand(arg.X(), temp1));
1207 __ Cmp(temp4, temp0);
1208 __ B(ne, &find_char_diff);
1209 __ Add(temp1, temp1, char_size * 4);
1210 __ Subs(temp2, temp2, 4);
1211 __ B(gt, &loop);
1212 __ B(&end);
1213
1214 // Promote temp1 to an X reg, ready for EOR.
1215 temp1 = temp1.X();
1216
1217 // Find the single 16-bit character difference.
1218 __ Bind(&find_char_diff);
1219 // Get the bit position of the first character that differs.
1220 __ Eor(temp1, temp0, temp4);
1221 __ Rbit(temp1, temp1);
1222 __ Clz(temp1, temp1);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001223 // If the number of 16-bit chars remaining <= the index where the difference occurs (0-3), then
1224 // the difference occurs outside the remaining string data, so just return length diff (out).
1225 __ Cmp(temp2, Operand(temp1, LSR, 4));
1226 __ B(le, &end);
1227 // Extract the characters and calculate the difference.
Scott Wakelinge5ed20b2016-05-20 10:41:38 +01001228 __ Bic(temp1, temp1, 0xf);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001229 __ Lsr(temp0, temp0, temp1);
1230 __ Lsr(temp4, temp4, temp1);
1231 __ And(temp4, temp4, 0xffff);
1232 __ Sub(out, temp4, Operand(temp0, UXTH));
1233
1234 __ Bind(&end);
1235
1236 if (can_slow_path) {
1237 __ Bind(slow_path->GetExitLabel());
1238 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001239}
1240
Agi Csakiea34b402015-08-13 17:51:19 -07001241void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
1242 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1243 LocationSummary::kNoCall,
1244 kIntrinsified);
1245 locations->SetInAt(0, Location::RequiresRegister());
1246 locations->SetInAt(1, Location::RequiresRegister());
1247 // Temporary registers to store lengths of strings and for calculations.
1248 locations->AddTemp(Location::RequiresRegister());
1249 locations->AddTemp(Location::RequiresRegister());
1250
1251 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1252}
1253
1254void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
1255 vixl::MacroAssembler* masm = GetVIXLAssembler();
1256 LocationSummary* locations = invoke->GetLocations();
1257
1258 Register str = WRegisterFrom(locations->InAt(0));
1259 Register arg = WRegisterFrom(locations->InAt(1));
1260 Register out = XRegisterFrom(locations->Out());
1261
1262 UseScratchRegisterScope scratch_scope(masm);
1263 Register temp = scratch_scope.AcquireW();
1264 Register temp1 = WRegisterFrom(locations->GetTemp(0));
1265 Register temp2 = WRegisterFrom(locations->GetTemp(1));
1266
1267 vixl::Label loop;
1268 vixl::Label end;
1269 vixl::Label return_true;
1270 vixl::Label return_false;
1271
1272 // Get offsets of count, value, and class fields within a string object.
1273 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1274 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1275 const int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1276
1277 // Note that the null check must have been done earlier.
1278 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1279
Vladimir Marko53b52002016-05-24 19:30:45 +01001280 StringEqualsOptimizations optimizations(invoke);
1281 if (!optimizations.GetArgumentNotNull()) {
1282 // Check if input is null, return false if it is.
1283 __ Cbz(arg, &return_false);
1284 }
Agi Csakiea34b402015-08-13 17:51:19 -07001285
1286 // Reference equality check, return true if same reference.
1287 __ Cmp(str, arg);
1288 __ B(&return_true, eq);
1289
Vladimir Marko53b52002016-05-24 19:30:45 +01001290 if (!optimizations.GetArgumentIsString()) {
1291 // Instanceof check for the argument by comparing class fields.
1292 // All string objects must have the same type since String cannot be subclassed.
1293 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1294 // If the argument is a string object, its class field must be equal to receiver's class field.
1295 __ Ldr(temp, MemOperand(str.X(), class_offset));
1296 __ Ldr(temp1, MemOperand(arg.X(), class_offset));
1297 __ Cmp(temp, temp1);
1298 __ B(&return_false, ne);
1299 }
Agi Csakiea34b402015-08-13 17:51:19 -07001300
1301 // Load lengths of this and argument strings.
1302 __ Ldr(temp, MemOperand(str.X(), count_offset));
1303 __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1304 // Check if lengths are equal, return false if they're not.
1305 __ Cmp(temp, temp1);
1306 __ B(&return_false, ne);
1307 // Store offset of string value in preparation for comparison loop
1308 __ Mov(temp1, value_offset);
1309 // Return true if both strings are empty.
1310 __ Cbz(temp, &return_true);
1311
1312 // Assertions that must hold in order to compare strings 4 characters at a time.
1313 DCHECK_ALIGNED(value_offset, 8);
1314 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1315
1316 temp1 = temp1.X();
1317 temp2 = temp2.X();
1318
1319 // Loop to compare strings 4 characters at a time starting at the beginning of the string.
1320 // Ok to do this because strings are zero-padded to be 8-byte aligned.
1321 __ Bind(&loop);
1322 __ Ldr(out, MemOperand(str.X(), temp1));
1323 __ Ldr(temp2, MemOperand(arg.X(), temp1));
1324 __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
1325 __ Cmp(out, temp2);
1326 __ B(&return_false, ne);
1327 __ Sub(temp, temp, Operand(4), SetFlags);
1328 __ B(&loop, gt);
1329
1330 // Return true and exit the function.
1331 // If loop does not result in returning false, we return true.
1332 __ Bind(&return_true);
1333 __ Mov(out, 1);
1334 __ B(&end);
1335
1336 // Return false and exit the function.
1337 __ Bind(&return_false);
1338 __ Mov(out, 0);
1339 __ Bind(&end);
1340}
1341
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001342static void GenerateVisitStringIndexOf(HInvoke* invoke,
1343 vixl::MacroAssembler* masm,
1344 CodeGeneratorARM64* codegen,
1345 ArenaAllocator* allocator,
1346 bool start_at_zero) {
1347 LocationSummary* locations = invoke->GetLocations();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001348
1349 // Note that the null check must have been done earlier.
1350 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1351
1352 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001353 // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001354 SlowPathCodeARM64* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001355 HInstruction* code_point = invoke->InputAt(1);
1356 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01001357 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > 0xFFFFU) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001358 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1359 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1360 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1361 codegen->AddSlowPath(slow_path);
1362 __ B(slow_path->GetEntryLabel());
1363 __ Bind(slow_path->GetExitLabel());
1364 return;
1365 }
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001366 } else if (code_point->GetType() != Primitive::kPrimChar) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001367 Register char_reg = WRegisterFrom(locations->InAt(1));
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001368 __ Tst(char_reg, 0xFFFF0000);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001369 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1370 codegen->AddSlowPath(slow_path);
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001371 __ B(ne, slow_path->GetEntryLabel());
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001372 }
1373
1374 if (start_at_zero) {
1375 // Start-index = 0.
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001376 Register tmp_reg = WRegisterFrom(locations->GetTemp(0));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001377 __ Mov(tmp_reg, 0);
1378 }
1379
1380 __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pIndexOf).Int32Value()));
Roland Levillain42ad2882016-02-29 18:26:54 +00001381 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001382 __ Blr(lr);
1383
1384 if (slow_path != nullptr) {
1385 __ Bind(slow_path->GetExitLabel());
1386 }
1387}
1388
1389void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) {
1390 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1391 LocationSummary::kCall,
1392 kIntrinsified);
1393 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1394 // best to align the inputs accordingly.
1395 InvokeRuntimeCallingConvention calling_convention;
1396 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1397 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1398 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1399
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001400 // Need to send start_index=0.
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001401 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1402}
1403
1404void IntrinsicCodeGeneratorARM64::VisitStringIndexOf(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001405 GenerateVisitStringIndexOf(
1406 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001407}
1408
1409void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
1410 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1411 LocationSummary::kCall,
1412 kIntrinsified);
1413 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1414 // best to align the inputs accordingly.
1415 InvokeRuntimeCallingConvention calling_convention;
1416 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1417 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1418 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1419 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001420}
1421
1422void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001423 GenerateVisitStringIndexOf(
1424 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001425}
1426
Jeff Hao848f70a2014-01-15 13:49:50 -08001427void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1428 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1429 LocationSummary::kCall,
1430 kIntrinsified);
1431 InvokeRuntimeCallingConvention calling_convention;
1432 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1433 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1434 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1435 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1436 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1437}
1438
1439void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1440 vixl::MacroAssembler* masm = GetVIXLAssembler();
1441 LocationSummary* locations = invoke->GetLocations();
1442
1443 Register byte_array = WRegisterFrom(locations->InAt(0));
1444 __ Cmp(byte_array, 0);
1445 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1446 codegen_->AddSlowPath(slow_path);
1447 __ B(eq, slow_path->GetEntryLabel());
1448
1449 __ Ldr(lr,
1450 MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromBytes).Int32Value()));
Roland Levillainf969a202016-03-09 16:14:00 +00001451 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001452 __ Blr(lr);
Roland Levillainf969a202016-03-09 16:14:00 +00001453 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Jeff Hao848f70a2014-01-15 13:49:50 -08001454 __ Bind(slow_path->GetExitLabel());
1455}
1456
1457void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
1458 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1459 LocationSummary::kCall,
1460 kIntrinsified);
1461 InvokeRuntimeCallingConvention calling_convention;
1462 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1463 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1464 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1465 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1466}
1467
1468void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
1469 vixl::MacroAssembler* masm = GetVIXLAssembler();
1470
Roland Levillaincc3839c2016-02-29 16:23:48 +00001471 // No need to emit code checking whether `locations->InAt(2)` is a null
1472 // pointer, as callers of the native method
1473 //
1474 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1475 //
1476 // all include a null check on `data` before calling that method.
Jeff Hao848f70a2014-01-15 13:49:50 -08001477 __ Ldr(lr,
1478 MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromChars).Int32Value()));
Roland Levillainf969a202016-03-09 16:14:00 +00001479 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001480 __ Blr(lr);
Roland Levillainf969a202016-03-09 16:14:00 +00001481 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Jeff Hao848f70a2014-01-15 13:49:50 -08001482}
1483
1484void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Jeff Hao848f70a2014-01-15 13:49:50 -08001485 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1486 LocationSummary::kCall,
1487 kIntrinsified);
1488 InvokeRuntimeCallingConvention calling_convention;
1489 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Jeff Hao848f70a2014-01-15 13:49:50 -08001490 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1491}
1492
1493void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) {
1494 vixl::MacroAssembler* masm = GetVIXLAssembler();
1495 LocationSummary* locations = invoke->GetLocations();
1496
1497 Register string_to_copy = WRegisterFrom(locations->InAt(0));
1498 __ Cmp(string_to_copy, 0);
1499 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1500 codegen_->AddSlowPath(slow_path);
1501 __ B(eq, slow_path->GetEntryLabel());
1502
1503 __ Ldr(lr,
1504 MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromString).Int32Value()));
Roland Levillainf969a202016-03-09 16:14:00 +00001505 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001506 __ Blr(lr);
Roland Levillainf969a202016-03-09 16:14:00 +00001507 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Jeff Hao848f70a2014-01-15 13:49:50 -08001508 __ Bind(slow_path->GetExitLabel());
1509}
1510
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001511static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1512 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
1513 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1514 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1515
1516 LocationSummary* const locations = new (arena) LocationSummary(invoke,
1517 LocationSummary::kCall,
1518 kIntrinsified);
1519 InvokeRuntimeCallingConvention calling_convention;
1520
1521 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1522 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1523}
1524
1525static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1526 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
1527 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1528 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(1)->GetType()));
1529 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1530
1531 LocationSummary* const locations = new (arena) LocationSummary(invoke,
1532 LocationSummary::kCall,
1533 kIntrinsified);
1534 InvokeRuntimeCallingConvention calling_convention;
1535
1536 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1537 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
1538 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1539}
1540
1541static void GenFPToFPCall(HInvoke* invoke,
1542 vixl::MacroAssembler* masm,
1543 CodeGeneratorARM64* codegen,
1544 QuickEntrypointEnum entry) {
1545 __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArm64WordSize>(entry).Int32Value()));
1546 __ Blr(lr);
1547 codegen->RecordPcInfo(invoke, invoke->GetDexPc());
1548}
1549
1550void IntrinsicLocationsBuilderARM64::VisitMathCos(HInvoke* invoke) {
1551 CreateFPToFPCallLocations(arena_, invoke);
1552}
1553
1554void IntrinsicCodeGeneratorARM64::VisitMathCos(HInvoke* invoke) {
1555 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCos);
1556}
1557
1558void IntrinsicLocationsBuilderARM64::VisitMathSin(HInvoke* invoke) {
1559 CreateFPToFPCallLocations(arena_, invoke);
1560}
1561
1562void IntrinsicCodeGeneratorARM64::VisitMathSin(HInvoke* invoke) {
1563 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickSin);
1564}
1565
1566void IntrinsicLocationsBuilderARM64::VisitMathAcos(HInvoke* invoke) {
1567 CreateFPToFPCallLocations(arena_, invoke);
1568}
1569
1570void IntrinsicCodeGeneratorARM64::VisitMathAcos(HInvoke* invoke) {
1571 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAcos);
1572}
1573
1574void IntrinsicLocationsBuilderARM64::VisitMathAsin(HInvoke* invoke) {
1575 CreateFPToFPCallLocations(arena_, invoke);
1576}
1577
1578void IntrinsicCodeGeneratorARM64::VisitMathAsin(HInvoke* invoke) {
1579 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAsin);
1580}
1581
1582void IntrinsicLocationsBuilderARM64::VisitMathAtan(HInvoke* invoke) {
1583 CreateFPToFPCallLocations(arena_, invoke);
1584}
1585
1586void IntrinsicCodeGeneratorARM64::VisitMathAtan(HInvoke* invoke) {
1587 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAtan);
1588}
1589
1590void IntrinsicLocationsBuilderARM64::VisitMathCbrt(HInvoke* invoke) {
1591 CreateFPToFPCallLocations(arena_, invoke);
1592}
1593
1594void IntrinsicCodeGeneratorARM64::VisitMathCbrt(HInvoke* invoke) {
1595 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCbrt);
1596}
1597
1598void IntrinsicLocationsBuilderARM64::VisitMathCosh(HInvoke* invoke) {
1599 CreateFPToFPCallLocations(arena_, invoke);
1600}
1601
1602void IntrinsicCodeGeneratorARM64::VisitMathCosh(HInvoke* invoke) {
1603 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCosh);
1604}
1605
1606void IntrinsicLocationsBuilderARM64::VisitMathExp(HInvoke* invoke) {
1607 CreateFPToFPCallLocations(arena_, invoke);
1608}
1609
1610void IntrinsicCodeGeneratorARM64::VisitMathExp(HInvoke* invoke) {
1611 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickExp);
1612}
1613
1614void IntrinsicLocationsBuilderARM64::VisitMathExpm1(HInvoke* invoke) {
1615 CreateFPToFPCallLocations(arena_, invoke);
1616}
1617
1618void IntrinsicCodeGeneratorARM64::VisitMathExpm1(HInvoke* invoke) {
1619 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickExpm1);
1620}
1621
1622void IntrinsicLocationsBuilderARM64::VisitMathLog(HInvoke* invoke) {
1623 CreateFPToFPCallLocations(arena_, invoke);
1624}
1625
1626void IntrinsicCodeGeneratorARM64::VisitMathLog(HInvoke* invoke) {
1627 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickLog);
1628}
1629
1630void IntrinsicLocationsBuilderARM64::VisitMathLog10(HInvoke* invoke) {
1631 CreateFPToFPCallLocations(arena_, invoke);
1632}
1633
1634void IntrinsicCodeGeneratorARM64::VisitMathLog10(HInvoke* invoke) {
1635 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickLog10);
1636}
1637
1638void IntrinsicLocationsBuilderARM64::VisitMathSinh(HInvoke* invoke) {
1639 CreateFPToFPCallLocations(arena_, invoke);
1640}
1641
1642void IntrinsicCodeGeneratorARM64::VisitMathSinh(HInvoke* invoke) {
1643 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickSinh);
1644}
1645
1646void IntrinsicLocationsBuilderARM64::VisitMathTan(HInvoke* invoke) {
1647 CreateFPToFPCallLocations(arena_, invoke);
1648}
1649
1650void IntrinsicCodeGeneratorARM64::VisitMathTan(HInvoke* invoke) {
1651 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickTan);
1652}
1653
1654void IntrinsicLocationsBuilderARM64::VisitMathTanh(HInvoke* invoke) {
1655 CreateFPToFPCallLocations(arena_, invoke);
1656}
1657
1658void IntrinsicCodeGeneratorARM64::VisitMathTanh(HInvoke* invoke) {
1659 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickTanh);
1660}
1661
1662void IntrinsicLocationsBuilderARM64::VisitMathAtan2(HInvoke* invoke) {
1663 CreateFPFPToFPCallLocations(arena_, invoke);
1664}
1665
1666void IntrinsicCodeGeneratorARM64::VisitMathAtan2(HInvoke* invoke) {
1667 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAtan2);
1668}
1669
1670void IntrinsicLocationsBuilderARM64::VisitMathHypot(HInvoke* invoke) {
1671 CreateFPFPToFPCallLocations(arena_, invoke);
1672}
1673
1674void IntrinsicCodeGeneratorARM64::VisitMathHypot(HInvoke* invoke) {
1675 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickHypot);
1676}
1677
1678void IntrinsicLocationsBuilderARM64::VisitMathNextAfter(HInvoke* invoke) {
1679 CreateFPFPToFPCallLocations(arena_, invoke);
1680}
1681
1682void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) {
1683 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickNextAfter);
1684}
1685
Tim Zhang25abd6c2016-01-19 23:39:24 +08001686void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1687 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1688 LocationSummary::kNoCall,
1689 kIntrinsified);
1690 locations->SetInAt(0, Location::RequiresRegister());
1691 locations->SetInAt(1, Location::RequiresRegister());
1692 locations->SetInAt(2, Location::RequiresRegister());
1693 locations->SetInAt(3, Location::RequiresRegister());
1694 locations->SetInAt(4, Location::RequiresRegister());
1695
1696 locations->AddTemp(Location::RequiresRegister());
1697 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingdf109d92016-04-22 11:35:56 +01001698 locations->AddTemp(Location::RequiresRegister());
Tim Zhang25abd6c2016-01-19 23:39:24 +08001699}
1700
1701void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1702 vixl::MacroAssembler* masm = GetVIXLAssembler();
1703 LocationSummary* locations = invoke->GetLocations();
1704
1705 // Check assumption that sizeof(Char) is 2 (used in scaling below).
1706 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1707 DCHECK_EQ(char_size, 2u);
1708
1709 // Location of data in char array buffer.
1710 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
1711
1712 // Location of char array data in string.
1713 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1714
1715 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
1716 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
1717 Register srcObj = XRegisterFrom(locations->InAt(0));
1718 Register srcBegin = XRegisterFrom(locations->InAt(1));
1719 Register srcEnd = XRegisterFrom(locations->InAt(2));
1720 Register dstObj = XRegisterFrom(locations->InAt(3));
1721 Register dstBegin = XRegisterFrom(locations->InAt(4));
1722
1723 Register src_ptr = XRegisterFrom(locations->GetTemp(0));
Scott Wakelingdf109d92016-04-22 11:35:56 +01001724 Register num_chr = XRegisterFrom(locations->GetTemp(1));
1725 Register tmp1 = XRegisterFrom(locations->GetTemp(2));
Tim Zhang25abd6c2016-01-19 23:39:24 +08001726
1727 UseScratchRegisterScope temps(masm);
1728 Register dst_ptr = temps.AcquireX();
Scott Wakelingdf109d92016-04-22 11:35:56 +01001729 Register tmp2 = temps.AcquireX();
Tim Zhang25abd6c2016-01-19 23:39:24 +08001730
Scott Wakelingdf109d92016-04-22 11:35:56 +01001731 // src address to copy from.
Tim Zhang25abd6c2016-01-19 23:39:24 +08001732 __ Add(src_ptr, srcObj, Operand(value_offset));
Tim Zhang25abd6c2016-01-19 23:39:24 +08001733 __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
1734
Scott Wakelingdf109d92016-04-22 11:35:56 +01001735 // dst address start to copy to.
Tim Zhang25abd6c2016-01-19 23:39:24 +08001736 __ Add(dst_ptr, dstObj, Operand(data_offset));
1737 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
1738
Scott Wakelingdf109d92016-04-22 11:35:56 +01001739 __ Sub(num_chr, srcEnd, srcBegin);
1740
Tim Zhang25abd6c2016-01-19 23:39:24 +08001741 // Do the copy.
Scott Wakelingdf109d92016-04-22 11:35:56 +01001742 vixl::Label loop;
1743 vixl::Label done;
1744 vixl::Label remainder;
1745
1746 // Early out for valid zero-length retrievals.
1747 __ Cbz(num_chr, &done);
1748
1749 // Save repairing the value of num_chr on the < 8 character path.
1750 __ Subs(tmp1, num_chr, 8);
1751 __ B(lt, &remainder);
1752
1753 // Keep the result of the earlier subs, we are going to fetch at least 8 characters.
1754 __ Mov(num_chr, tmp1);
1755
1756 // Main loop used for longer fetches loads and stores 8x16-bit characters at a time.
1757 // (Unaligned addresses are acceptable here and not worth inlining extra code to rectify.)
Tim Zhang25abd6c2016-01-19 23:39:24 +08001758 __ Bind(&loop);
Scott Wakelingdf109d92016-04-22 11:35:56 +01001759 __ Ldp(tmp1, tmp2, MemOperand(src_ptr, char_size * 8, vixl::PostIndex));
1760 __ Subs(num_chr, num_chr, 8);
1761 __ Stp(tmp1, tmp2, MemOperand(dst_ptr, char_size * 8, vixl::PostIndex));
1762 __ B(ge, &loop);
1763
1764 __ Adds(num_chr, num_chr, 8);
1765 __ B(eq, &done);
1766
1767 // Main loop for < 8 character case and remainder handling. Loads and stores one
1768 // 16-bit Java character at a time.
1769 __ Bind(&remainder);
1770 __ Ldrh(tmp1, MemOperand(src_ptr, char_size, vixl::PostIndex));
1771 __ Subs(num_chr, num_chr, 1);
1772 __ Strh(tmp1, MemOperand(dst_ptr, char_size, vixl::PostIndex));
1773 __ B(gt, &remainder);
1774
Tim Zhang25abd6c2016-01-19 23:39:24 +08001775 __ Bind(&done);
1776}
1777
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001778// Mirrors ARRAYCOPY_SHORT_CHAR_ARRAY_THRESHOLD in libcore, so we can choose to use the native
1779// implementation there for longer copy lengths.
donghui.baic2ec9ad2016-03-10 14:02:55 +08001780static constexpr int32_t kSystemArrayCopyCharThreshold = 32;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001781
1782static void SetSystemArrayCopyLocationRequires(LocationSummary* locations,
1783 uint32_t at,
1784 HInstruction* input) {
1785 HIntConstant* const_input = input->AsIntConstant();
1786 if (const_input != nullptr && !vixl::Assembler::IsImmAddSub(const_input->GetValue())) {
1787 locations->SetInAt(at, Location::RequiresRegister());
1788 } else {
1789 locations->SetInAt(at, Location::RegisterOrConstant(input));
1790 }
1791}
1792
1793void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
1794 // Check to see if we have known failures that will cause us to have to bail out
1795 // to the runtime, and just generate the runtime call directly.
1796 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1797 HIntConstant* dst_pos = invoke->InputAt(3)->AsIntConstant();
1798
1799 // The positions must be non-negative.
1800 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
1801 (dst_pos != nullptr && dst_pos->GetValue() < 0)) {
1802 // We will have to fail anyways.
1803 return;
1804 }
1805
1806 // The length must be >= 0 and not so long that we would (currently) prefer libcore's
1807 // native implementation.
1808 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1809 if (length != nullptr) {
1810 int32_t len = length->GetValue();
donghui.baic2ec9ad2016-03-10 14:02:55 +08001811 if (len < 0 || len > kSystemArrayCopyCharThreshold) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001812 // Just call as normal.
1813 return;
1814 }
1815 }
1816
1817 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
1818 LocationSummary* locations = new (allocator) LocationSummary(invoke,
1819 LocationSummary::kCallOnSlowPath,
1820 kIntrinsified);
1821 // arraycopy(char[] src, int src_pos, char[] dst, int dst_pos, int length).
1822 locations->SetInAt(0, Location::RequiresRegister());
1823 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
1824 locations->SetInAt(2, Location::RequiresRegister());
1825 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
1826 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
1827
1828 locations->AddTemp(Location::RequiresRegister());
1829 locations->AddTemp(Location::RequiresRegister());
1830 locations->AddTemp(Location::RequiresRegister());
1831}
1832
1833static void CheckSystemArrayCopyPosition(vixl::MacroAssembler* masm,
1834 const Location& pos,
1835 const Register& input,
1836 const Location& length,
1837 SlowPathCodeARM64* slow_path,
1838 const Register& input_len,
1839 const Register& temp,
1840 bool length_is_input_length = false) {
1841 const int32_t length_offset = mirror::Array::LengthOffset().Int32Value();
1842 if (pos.IsConstant()) {
1843 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
1844 if (pos_const == 0) {
1845 if (!length_is_input_length) {
1846 // Check that length(input) >= length.
1847 __ Ldr(temp, MemOperand(input, length_offset));
1848 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
1849 __ B(slow_path->GetEntryLabel(), lt);
1850 }
1851 } else {
1852 // Check that length(input) >= pos.
1853 __ Ldr(input_len, MemOperand(input, length_offset));
1854 __ Subs(temp, input_len, pos_const);
1855 __ B(slow_path->GetEntryLabel(), lt);
1856
1857 // Check that (length(input) - pos) >= length.
1858 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
1859 __ B(slow_path->GetEntryLabel(), lt);
1860 }
1861 } else if (length_is_input_length) {
1862 // The only way the copy can succeed is if pos is zero.
1863 __ Cbnz(WRegisterFrom(pos), slow_path->GetEntryLabel());
1864 } else {
1865 // Check that pos >= 0.
1866 Register pos_reg = WRegisterFrom(pos);
1867 __ Tbnz(pos_reg, pos_reg.size() - 1, slow_path->GetEntryLabel());
1868
1869 // Check that pos <= length(input) && (length(input) - pos) >= length.
1870 __ Ldr(temp, MemOperand(input, length_offset));
1871 __ Subs(temp, temp, pos_reg);
1872 // Ccmp if length(input) >= pos, else definitely bail to slow path (N!=V == lt).
1873 __ Ccmp(temp, OperandFrom(length, Primitive::kPrimInt), NFlag, ge);
1874 __ B(slow_path->GetEntryLabel(), lt);
1875 }
1876}
1877
1878// Compute base source address, base destination address, and end source address
1879// for System.arraycopy* intrinsics.
1880static void GenSystemArrayCopyAddresses(vixl::MacroAssembler* masm,
1881 Primitive::Type type,
1882 const Register& src,
1883 const Location& src_pos,
1884 const Register& dst,
1885 const Location& dst_pos,
1886 const Location& copy_length,
1887 const Register& src_base,
1888 const Register& dst_base,
1889 const Register& src_end) {
1890 DCHECK(type == Primitive::kPrimNot || type == Primitive::kPrimChar)
Roland Levillainebea3d22016-04-12 15:42:57 +01001891 << "Unexpected element type: " << type;
1892 const int32_t element_size = Primitive::ComponentSize(type);
1893 const int32_t element_size_shift = Primitive::ComponentSizeShift(type);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001894
Roland Levillainebea3d22016-04-12 15:42:57 +01001895 uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001896 if (src_pos.IsConstant()) {
1897 int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01001898 __ Add(src_base, src, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001899 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01001900 __ Add(src_base, src, data_offset);
1901 __ Add(src_base, src_base, Operand(XRegisterFrom(src_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001902 }
1903
1904 if (dst_pos.IsConstant()) {
1905 int32_t constant = dst_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01001906 __ Add(dst_base, dst, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001907 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01001908 __ Add(dst_base, dst, data_offset);
1909 __ Add(dst_base, dst_base, Operand(XRegisterFrom(dst_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001910 }
1911
1912 if (copy_length.IsConstant()) {
1913 int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01001914 __ Add(src_end, src_base, element_size * constant);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001915 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01001916 __ Add(src_end, src_base, Operand(XRegisterFrom(copy_length), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001917 }
1918}
1919
1920void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
1921 vixl::MacroAssembler* masm = GetVIXLAssembler();
1922 LocationSummary* locations = invoke->GetLocations();
1923 Register src = XRegisterFrom(locations->InAt(0));
1924 Location src_pos = locations->InAt(1);
1925 Register dst = XRegisterFrom(locations->InAt(2));
1926 Location dst_pos = locations->InAt(3);
1927 Location length = locations->InAt(4);
1928
1929 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1930 codegen_->AddSlowPath(slow_path);
1931
1932 // If source and destination are the same, take the slow path. Overlapping copy regions must be
1933 // copied in reverse and we can't know in all cases if it's needed.
1934 __ Cmp(src, dst);
1935 __ B(slow_path->GetEntryLabel(), eq);
1936
1937 // Bail out if the source is null.
1938 __ Cbz(src, slow_path->GetEntryLabel());
1939
1940 // Bail out if the destination is null.
1941 __ Cbz(dst, slow_path->GetEntryLabel());
1942
1943 if (!length.IsConstant()) {
1944 // If the length is negative, bail out.
1945 __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel());
1946 // If the length > 32 then (currently) prefer libcore's native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08001947 __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001948 __ B(slow_path->GetEntryLabel(), gt);
1949 } else {
1950 // We have already checked in the LocationsBuilder for the constant case.
1951 DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0);
1952 DCHECK_LE(length.GetConstant()->AsIntConstant()->GetValue(), 32);
1953 }
1954
1955 Register src_curr_addr = WRegisterFrom(locations->GetTemp(0));
1956 Register dst_curr_addr = WRegisterFrom(locations->GetTemp(1));
1957 Register src_stop_addr = WRegisterFrom(locations->GetTemp(2));
1958
1959 CheckSystemArrayCopyPosition(masm,
1960 src_pos,
1961 src,
1962 length,
1963 slow_path,
1964 src_curr_addr,
1965 dst_curr_addr,
1966 false);
1967
1968 CheckSystemArrayCopyPosition(masm,
1969 dst_pos,
1970 dst,
1971 length,
1972 slow_path,
1973 src_curr_addr,
1974 dst_curr_addr,
1975 false);
1976
1977 src_curr_addr = src_curr_addr.X();
1978 dst_curr_addr = dst_curr_addr.X();
1979 src_stop_addr = src_stop_addr.X();
1980
1981 GenSystemArrayCopyAddresses(masm,
1982 Primitive::kPrimChar,
1983 src,
1984 src_pos,
1985 dst,
1986 dst_pos,
1987 length,
1988 src_curr_addr,
1989 dst_curr_addr,
1990 src_stop_addr);
1991
1992 // Iterate over the arrays and do a raw copy of the chars.
1993 const int32_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1994 UseScratchRegisterScope temps(masm);
1995 Register tmp = temps.AcquireW();
1996 vixl::Label loop, done;
1997 __ Bind(&loop);
1998 __ Cmp(src_curr_addr, src_stop_addr);
1999 __ B(&done, eq);
2000 __ Ldrh(tmp, MemOperand(src_curr_addr, char_size, vixl::PostIndex));
2001 __ Strh(tmp, MemOperand(dst_curr_addr, char_size, vixl::PostIndex));
2002 __ B(&loop);
2003 __ Bind(&done);
2004
2005 __ Bind(slow_path->GetExitLabel());
2006}
2007
donghui.baic2ec9ad2016-03-10 14:02:55 +08002008// We can choose to use the native implementation there for longer copy lengths.
2009static constexpr int32_t kSystemArrayCopyThreshold = 128;
2010
2011// CodeGenerator::CreateSystemArrayCopyLocationSummary use three temporary registers.
2012// We want to use two temporary registers in order to reduce the register pressure in arm64.
2013// So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary.
2014void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) {
2015 // Check to see if we have known failures that will cause us to have to bail out
2016 // to the runtime, and just generate the runtime call directly.
2017 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2018 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
2019
2020 // The positions must be non-negative.
2021 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2022 (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
2023 // We will have to fail anyways.
2024 return;
2025 }
2026
2027 // The length must be >= 0.
2028 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2029 if (length != nullptr) {
2030 int32_t len = length->GetValue();
2031 if (len < 0 || len >= kSystemArrayCopyThreshold) {
2032 // Just call as normal.
2033 return;
2034 }
2035 }
2036
2037 SystemArrayCopyOptimizations optimizations(invoke);
2038
2039 if (optimizations.GetDestinationIsSource()) {
2040 if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) {
2041 // We only support backward copying if source and destination are the same.
2042 return;
2043 }
2044 }
2045
2046 if (optimizations.GetDestinationIsPrimitiveArray() || optimizations.GetSourceIsPrimitiveArray()) {
2047 // We currently don't intrinsify primitive copying.
2048 return;
2049 }
2050
2051 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2052 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2053 LocationSummary::kCallOnSlowPath,
2054 kIntrinsified);
2055 // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length).
2056 locations->SetInAt(0, Location::RequiresRegister());
2057 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2058 locations->SetInAt(2, Location::RequiresRegister());
2059 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2060 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2061
2062 locations->AddTemp(Location::RequiresRegister());
2063 locations->AddTemp(Location::RequiresRegister());
2064}
2065
2066void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) {
2067 vixl::MacroAssembler* masm = GetVIXLAssembler();
2068 LocationSummary* locations = invoke->GetLocations();
2069
2070 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2071 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2072 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2073 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
2074
2075 Register src = XRegisterFrom(locations->InAt(0));
2076 Location src_pos = locations->InAt(1);
2077 Register dest = XRegisterFrom(locations->InAt(2));
2078 Location dest_pos = locations->InAt(3);
2079 Location length = locations->InAt(4);
2080 Register temp1 = WRegisterFrom(locations->GetTemp(0));
2081 Register temp2 = WRegisterFrom(locations->GetTemp(1));
2082
2083 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2084 codegen_->AddSlowPath(slow_path);
2085
2086 vixl::Label conditions_on_positions_validated;
2087 SystemArrayCopyOptimizations optimizations(invoke);
2088
2089 if (!optimizations.GetDestinationIsSource() &&
2090 (!src_pos.IsConstant() || !dest_pos.IsConstant())) {
2091 __ Cmp(src, dest);
2092 }
2093 // If source and destination are the same, we go to slow path if we need to do
2094 // forward copying.
2095 if (src_pos.IsConstant()) {
2096 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
2097 if (dest_pos.IsConstant()) {
2098 // Checked when building locations.
2099 DCHECK(!optimizations.GetDestinationIsSource()
2100 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
2101 } else {
2102 if (!optimizations.GetDestinationIsSource()) {
2103 __ B(&conditions_on_positions_validated, ne);
2104 }
2105 __ Cmp(WRegisterFrom(dest_pos), src_pos_constant);
2106 __ B(slow_path->GetEntryLabel(), gt);
2107 }
2108 } else {
2109 if (!optimizations.GetDestinationIsSource()) {
2110 __ B(&conditions_on_positions_validated, ne);
2111 }
2112 __ Cmp(RegisterFrom(src_pos, invoke->InputAt(1)->GetType()),
2113 OperandFrom(dest_pos, invoke->InputAt(3)->GetType()));
2114 __ B(slow_path->GetEntryLabel(), lt);
2115 }
2116
2117 __ Bind(&conditions_on_positions_validated);
2118
2119 if (!optimizations.GetSourceIsNotNull()) {
2120 // Bail out if the source is null.
2121 __ Cbz(src, slow_path->GetEntryLabel());
2122 }
2123
2124 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
2125 // Bail out if the destination is null.
2126 __ Cbz(dest, slow_path->GetEntryLabel());
2127 }
2128
2129 // We have already checked in the LocationsBuilder for the constant case.
2130 if (!length.IsConstant() &&
2131 !optimizations.GetCountIsSourceLength() &&
2132 !optimizations.GetCountIsDestinationLength()) {
2133 // If the length is negative, bail out.
2134 __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel());
2135 // If the length >= 128 then (currently) prefer native implementation.
2136 __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold);
2137 __ B(slow_path->GetEntryLabel(), ge);
2138 }
2139 // Validity checks: source.
2140 CheckSystemArrayCopyPosition(masm,
2141 src_pos,
2142 src,
2143 length,
2144 slow_path,
2145 temp1,
2146 temp2,
2147 optimizations.GetCountIsSourceLength());
2148
2149 // Validity checks: dest.
2150 CheckSystemArrayCopyPosition(masm,
2151 dest_pos,
2152 dest,
2153 length,
2154 slow_path,
2155 temp1,
2156 temp2,
2157 optimizations.GetCountIsDestinationLength());
2158 {
2159 // We use a block to end the scratch scope before the write barrier, thus
2160 // freeing the temporary registers so they can be used in `MarkGCCard`.
2161 UseScratchRegisterScope temps(masm);
2162 Register temp3 = temps.AcquireW();
2163 if (!optimizations.GetDoesNotNeedTypeCheck()) {
2164 // Check whether all elements of the source array are assignable to the component
2165 // type of the destination array. We do two checks: the classes are the same,
2166 // or the destination is Object[]. If none of these checks succeed, we go to the
2167 // slow path.
2168 __ Ldr(temp1, MemOperand(dest, class_offset));
2169 __ Ldr(temp2, MemOperand(src, class_offset));
2170 bool did_unpoison = false;
2171 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2172 !optimizations.GetSourceIsNonPrimitiveArray()) {
2173 // One or two of the references need to be unpoisoned. Unpoison them
2174 // both to make the identity check valid.
2175 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2176 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2177 did_unpoison = true;
2178 }
2179
2180 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2181 // Bail out if the destination is not a non primitive array.
2182 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2183 __ Ldr(temp3, HeapOperand(temp1, component_offset));
2184 __ Cbz(temp3, slow_path->GetEntryLabel());
2185 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2186 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2187 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2188 __ Cbnz(temp3, slow_path->GetEntryLabel());
2189 }
2190
2191 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2192 // Bail out if the source is not a non primitive array.
2193 // /* HeapReference<Class> */ temp3 = temp2->component_type_
2194 __ Ldr(temp3, HeapOperand(temp2, component_offset));
2195 __ Cbz(temp3, slow_path->GetEntryLabel());
2196 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2197 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2198 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2199 __ Cbnz(temp3, slow_path->GetEntryLabel());
2200 }
2201
2202 __ Cmp(temp1, temp2);
2203
2204 if (optimizations.GetDestinationIsTypedObjectArray()) {
2205 vixl::Label do_copy;
2206 __ B(&do_copy, eq);
2207 if (!did_unpoison) {
2208 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2209 }
2210 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2211 __ Ldr(temp1, HeapOperand(temp1, component_offset));
2212 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2213 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2214 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2215 // No need to unpoison the result, we're comparing against null.
2216 __ Cbnz(temp1, slow_path->GetEntryLabel());
2217 __ Bind(&do_copy);
2218 } else {
2219 __ B(slow_path->GetEntryLabel(), ne);
2220 }
2221 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2222 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2223 // Bail out if the source is not a non primitive array.
2224 // /* HeapReference<Class> */ temp1 = src->klass_
2225 __ Ldr(temp1, HeapOperand(src.W(), class_offset));
2226 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2227 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2228 __ Ldr(temp3, HeapOperand(temp1, component_offset));
2229 __ Cbz(temp3, slow_path->GetEntryLabel());
2230 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2231 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2232 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2233 __ Cbnz(temp3, slow_path->GetEntryLabel());
2234 }
2235
2236 Register src_curr_addr = temp1.X();
2237 Register dst_curr_addr = temp2.X();
2238 Register src_stop_addr = temp3.X();
2239
2240 GenSystemArrayCopyAddresses(masm,
2241 Primitive::kPrimNot,
2242 src,
2243 src_pos,
2244 dest,
2245 dest_pos,
2246 length,
2247 src_curr_addr,
2248 dst_curr_addr,
2249 src_stop_addr);
2250
2251 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2252 // poison/unpoison, nor do any read barrier as the next uses of the destination
2253 // array will do it.
2254 vixl::Label loop, done;
2255 const int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
2256 __ Bind(&loop);
2257 __ Cmp(src_curr_addr, src_stop_addr);
2258 __ B(&done, eq);
2259 {
2260 Register tmp = temps.AcquireW();
2261 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, vixl::PostIndex));
2262 __ Str(tmp, MemOperand(dst_curr_addr, element_size, vixl::PostIndex));
2263 }
2264 __ B(&loop);
2265 __ Bind(&done);
2266 }
2267 // We only need one card marking on the destination array.
2268 codegen_->MarkGCCard(dest.W(), Register(), /* value_can_be_null */ false);
2269
2270 __ Bind(slow_path->GetExitLabel());
2271}
2272
Anton Kirilova3ffea22016-04-07 17:02:37 +01002273static void GenIsInfinite(LocationSummary* locations,
2274 bool is64bit,
2275 vixl::MacroAssembler* masm) {
2276 Operand infinity;
2277 Register out;
2278
2279 if (is64bit) {
2280 infinity = kPositiveInfinityDouble;
2281 out = XRegisterFrom(locations->Out());
2282 } else {
2283 infinity = kPositiveInfinityFloat;
2284 out = WRegisterFrom(locations->Out());
2285 }
2286
2287 const Register zero = vixl::Assembler::AppropriateZeroRegFor(out);
2288
2289 MoveFPToInt(locations, is64bit, masm);
2290 __ Eor(out, out, infinity);
2291 // We don't care about the sign bit, so shift left.
2292 __ Cmp(zero, Operand(out, LSL, 1));
2293 __ Cset(out, eq);
2294}
2295
2296void IntrinsicLocationsBuilderARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2297 CreateFPToIntLocations(arena_, invoke);
2298}
2299
2300void IntrinsicCodeGeneratorARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2301 GenIsInfinite(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
2302}
2303
2304void IntrinsicLocationsBuilderARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2305 CreateFPToIntLocations(arena_, invoke);
2306}
2307
2308void IntrinsicCodeGeneratorARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2309 GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
2310}
2311
Aart Bik2f9fcc92016-03-01 15:16:54 -08002312UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
Aart Bik2f9fcc92016-03-01 15:16:54 -08002313UNIMPLEMENTED_INTRINSIC(ARM64, IntegerHighestOneBit)
2314UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit)
2315UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit)
2316UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit)
Andreas Gampe878d58c2015-01-15 23:24:00 -08002317
Aart Bik0e54c012016-03-04 12:08:31 -08002318// 1.8.
2319UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt)
2320UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong)
2321UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt)
2322UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong)
2323UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08002324
Aart Bik2f9fcc92016-03-01 15:16:54 -08002325UNREACHABLE_INTRINSICS(ARM64)
Roland Levillain4d027112015-07-01 15:41:14 +01002326
2327#undef __
2328
Andreas Gampe878d58c2015-01-15 23:24:00 -08002329} // namespace arm64
2330} // namespace art