blob: 12d65beab17b62d095d755af22267d8b761daa91 [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();
Roland Levillainbfea3352016-06-23 13:48:47 +0100794 codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke,
795 trg_loc,
796 base,
797 /* offset */ 0U,
798 /* index */ offset_loc,
799 /* scale_factor */ 0U,
800 temp,
801 /* needs_null_check */ false,
802 is_volatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800803 } else {
Roland Levillain44015862016-01-22 11:47:17 +0000804 // Other cases.
805 MemOperand mem_op(base.X(), offset);
806 if (is_volatile) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +0000807 codegen->LoadAcquire(invoke, trg, mem_op, /* needs_null_check */ true);
Roland Levillain44015862016-01-22 11:47:17 +0000808 } else {
809 codegen->Load(type, trg, mem_op);
810 }
Roland Levillain4d027112015-07-01 15:41:14 +0100811
Roland Levillain44015862016-01-22 11:47:17 +0000812 if (type == Primitive::kPrimNot) {
813 DCHECK(trg.IsW());
814 codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
815 }
Roland Levillain4d027112015-07-01 15:41:14 +0100816 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800817}
818
819static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000820 bool can_call = kEmitCompilerReadBarrier &&
821 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
822 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800823 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000824 can_call ?
825 LocationSummary::kCallOnSlowPath :
826 LocationSummary::kNoCall,
Andreas Gampe878d58c2015-01-15 23:24:00 -0800827 kIntrinsified);
828 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
829 locations->SetInAt(1, Location::RequiresRegister());
830 locations->SetInAt(2, Location::RequiresRegister());
Roland Levillainbfea3352016-06-23 13:48:47 +0100831 locations->SetOut(Location::RequiresRegister(),
832 can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800833}
834
835void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
836 CreateIntIntIntToIntLocations(arena_, invoke);
837}
838void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
839 CreateIntIntIntToIntLocations(arena_, invoke);
840}
841void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
842 CreateIntIntIntToIntLocations(arena_, invoke);
843}
844void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
845 CreateIntIntIntToIntLocations(arena_, invoke);
846}
847void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
848 CreateIntIntIntToIntLocations(arena_, invoke);
849}
850void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
851 CreateIntIntIntToIntLocations(arena_, invoke);
852}
853
854void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000855 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800856}
857void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000858 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800859}
860void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000861 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800862}
863void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000864 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800865}
866void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000867 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800868}
869void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000870 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800871}
872
873static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
874 LocationSummary* locations = new (arena) LocationSummary(invoke,
875 LocationSummary::kNoCall,
876 kIntrinsified);
877 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
878 locations->SetInAt(1, Location::RequiresRegister());
879 locations->SetInAt(2, Location::RequiresRegister());
880 locations->SetInAt(3, Location::RequiresRegister());
881}
882
883void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
884 CreateIntIntIntIntToVoid(arena_, invoke);
885}
886void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
887 CreateIntIntIntIntToVoid(arena_, invoke);
888}
889void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
890 CreateIntIntIntIntToVoid(arena_, invoke);
891}
892void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
893 CreateIntIntIntIntToVoid(arena_, invoke);
894}
895void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
896 CreateIntIntIntIntToVoid(arena_, invoke);
897}
898void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
899 CreateIntIntIntIntToVoid(arena_, invoke);
900}
901void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
902 CreateIntIntIntIntToVoid(arena_, invoke);
903}
904void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
905 CreateIntIntIntIntToVoid(arena_, invoke);
906}
907void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
908 CreateIntIntIntIntToVoid(arena_, invoke);
909}
910
911static void GenUnsafePut(LocationSummary* locations,
912 Primitive::Type type,
913 bool is_volatile,
914 bool is_ordered,
915 CodeGeneratorARM64* codegen) {
916 vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
917
918 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
919 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
920 Register value = RegisterFrom(locations->InAt(3), type);
Roland Levillain4d027112015-07-01 15:41:14 +0100921 Register source = value;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800922 MemOperand mem_op(base.X(), offset);
923
Roland Levillain4d027112015-07-01 15:41:14 +0100924 {
925 // We use a block to end the scratch scope before the write barrier, thus
926 // freeing the temporary registers so they can be used in `MarkGCCard`.
927 UseScratchRegisterScope temps(masm);
928
929 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
930 DCHECK(value.IsW());
931 Register temp = temps.AcquireW();
932 __ Mov(temp.W(), value.W());
933 codegen->GetAssembler()->PoisonHeapReference(temp.W());
934 source = temp;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800935 }
Roland Levillain4d027112015-07-01 15:41:14 +0100936
937 if (is_volatile || is_ordered) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +0000938 codegen->StoreRelease(type, source, mem_op);
Roland Levillain4d027112015-07-01 15:41:14 +0100939 } else {
940 codegen->Store(type, source, mem_op);
941 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800942 }
943
944 if (type == Primitive::kPrimNot) {
Nicolas Geoffray07276db2015-05-18 14:22:09 +0100945 bool value_can_be_null = true; // TODO: Worth finding out this information?
946 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800947 }
948}
949
950void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000951 GenUnsafePut(invoke->GetLocations(),
952 Primitive::kPrimInt,
953 /* is_volatile */ false,
954 /* is_ordered */ false,
955 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800956}
957void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000958 GenUnsafePut(invoke->GetLocations(),
959 Primitive::kPrimInt,
960 /* is_volatile */ false,
961 /* is_ordered */ true,
962 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800963}
964void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000965 GenUnsafePut(invoke->GetLocations(),
966 Primitive::kPrimInt,
967 /* is_volatile */ true,
968 /* is_ordered */ false,
969 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800970}
971void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000972 GenUnsafePut(invoke->GetLocations(),
973 Primitive::kPrimNot,
974 /* is_volatile */ false,
975 /* is_ordered */ false,
976 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800977}
978void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000979 GenUnsafePut(invoke->GetLocations(),
980 Primitive::kPrimNot,
981 /* is_volatile */ false,
982 /* is_ordered */ true,
983 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800984}
985void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000986 GenUnsafePut(invoke->GetLocations(),
987 Primitive::kPrimNot,
988 /* is_volatile */ true,
989 /* is_ordered */ false,
990 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800991}
992void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000993 GenUnsafePut(invoke->GetLocations(),
994 Primitive::kPrimLong,
995 /* is_volatile */ false,
996 /* is_ordered */ false,
997 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800998}
999void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001000 GenUnsafePut(invoke->GetLocations(),
1001 Primitive::kPrimLong,
1002 /* is_volatile */ false,
1003 /* is_ordered */ true,
1004 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001005}
1006void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001007 GenUnsafePut(invoke->GetLocations(),
1008 Primitive::kPrimLong,
1009 /* is_volatile */ true,
1010 /* is_ordered */ false,
1011 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001012}
1013
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001014static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
1015 HInvoke* invoke,
1016 Primitive::Type type) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08001017 LocationSummary* locations = new (arena) LocationSummary(invoke,
1018 LocationSummary::kNoCall,
1019 kIntrinsified);
1020 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1021 locations->SetInAt(1, Location::RequiresRegister());
1022 locations->SetInAt(2, Location::RequiresRegister());
1023 locations->SetInAt(3, Location::RequiresRegister());
1024 locations->SetInAt(4, Location::RequiresRegister());
1025
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001026 // If heap poisoning is enabled, we don't want the unpoisoning
1027 // operations to potentially clobber the output.
1028 Location::OutputOverlap overlaps = (kPoisonHeapReferences && type == Primitive::kPrimNot)
1029 ? Location::kOutputOverlap
1030 : Location::kNoOutputOverlap;
1031 locations->SetOut(Location::RequiresRegister(), overlaps);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001032}
1033
1034static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM64* codegen) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08001035 vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
1036
1037 Register out = WRegisterFrom(locations->Out()); // Boolean result.
1038
1039 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
1040 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
1041 Register expected = RegisterFrom(locations->InAt(3), type); // Expected.
1042 Register value = RegisterFrom(locations->InAt(4), type); // Value.
1043
1044 // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
1045 if (type == Primitive::kPrimNot) {
1046 // Mark card for object assuming new value is stored.
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001047 bool value_can_be_null = true; // TODO: Worth finding out this information?
1048 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001049 }
1050
1051 UseScratchRegisterScope temps(masm);
1052 Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
1053 Register tmp_value = temps.AcquireSameSizeAs(value); // Value in memory.
1054
1055 Register tmp_32 = tmp_value.W();
1056
1057 __ Add(tmp_ptr, base.X(), Operand(offset));
1058
Roland Levillain4d027112015-07-01 15:41:14 +01001059 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1060 codegen->GetAssembler()->PoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001061 if (value.Is(expected)) {
1062 // Do not poison `value`, as it is the same register as
1063 // `expected`, which has just been poisoned.
1064 } else {
1065 codegen->GetAssembler()->PoisonHeapReference(value);
1066 }
Roland Levillain4d027112015-07-01 15:41:14 +01001067 }
1068
Andreas Gampe878d58c2015-01-15 23:24:00 -08001069 // do {
1070 // tmp_value = [tmp_ptr] - expected;
1071 // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1072 // result = tmp_value != 0;
1073
1074 vixl::Label loop_head, exit_loop;
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001075 __ Bind(&loop_head);
1076 // TODO: When `type == Primitive::kPrimNot`, add a read barrier for
1077 // the reference stored in the object before attempting the CAS,
1078 // similar to the one in the art::Unsafe_compareAndSwapObject JNI
1079 // implementation.
1080 //
1081 // Note that this code is not (yet) used when read barriers are
1082 // enabled (see IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject).
1083 DCHECK(!(type == Primitive::kPrimNot && kEmitCompilerReadBarrier));
1084 __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
1085 __ Cmp(tmp_value, expected);
1086 __ B(&exit_loop, ne);
1087 __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
1088 __ Cbnz(tmp_32, &loop_head);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001089 __ Bind(&exit_loop);
1090 __ Cset(out, eq);
Roland Levillain4d027112015-07-01 15:41:14 +01001091
1092 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillain4d027112015-07-01 15:41:14 +01001093 codegen->GetAssembler()->UnpoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001094 if (value.Is(expected)) {
1095 // Do not unpoison `value`, as it is the same register as
1096 // `expected`, which has just been unpoisoned.
1097 } else {
1098 codegen->GetAssembler()->UnpoisonHeapReference(value);
1099 }
Roland Levillain4d027112015-07-01 15:41:14 +01001100 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001101}
1102
1103void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001104 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001105}
1106void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001107 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001108}
1109void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillain391b8662015-12-18 11:43:38 +00001110 // The UnsafeCASObject intrinsic is missing a read barrier, and
1111 // therefore sometimes does not work as expected (b/25883050).
1112 // Turn it off temporarily as a quick fix, until the read barrier is
1113 // implemented (see TODO in GenCAS below).
1114 //
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001115 // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers.
1116 if (kEmitCompilerReadBarrier) {
Roland Levillain985ff702015-10-23 13:25:35 +01001117 return;
1118 }
1119
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001120 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001121}
1122
1123void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
1124 GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
1125}
1126void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
1127 GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_);
1128}
1129void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
1130 GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
1131}
1132
1133void IntrinsicLocationsBuilderARM64::VisitStringCharAt(HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08001134 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1135 LocationSummary::kCallOnSlowPath,
1136 kIntrinsified);
1137 locations->SetInAt(0, Location::RequiresRegister());
1138 locations->SetInAt(1, Location::RequiresRegister());
Nicolas Geoffray82f34492015-02-04 10:44:23 +00001139 // In case we need to go in the slow path, we can't have the output be the same
1140 // as the input: the current liveness analysis considers the input to be live
1141 // at the point of the call.
1142 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001143}
1144
1145void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) {
1146 vixl::MacroAssembler* masm = GetVIXLAssembler();
1147 LocationSummary* locations = invoke->GetLocations();
1148
1149 // Location of reference to data array
1150 const MemberOffset value_offset = mirror::String::ValueOffset();
1151 // Location of count
1152 const MemberOffset count_offset = mirror::String::CountOffset();
Andreas Gampe878d58c2015-01-15 23:24:00 -08001153
1154 Register obj = WRegisterFrom(locations->InAt(0)); // String object pointer.
1155 Register idx = WRegisterFrom(locations->InAt(1)); // Index of character.
1156 Register out = WRegisterFrom(locations->Out()); // Result character.
1157
1158 UseScratchRegisterScope temps(masm);
1159 Register temp = temps.AcquireW();
1160 Register array_temp = temps.AcquireW(); // We can trade this for worse scheduling.
1161
1162 // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
1163 // the cost.
1164 // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
1165 // we will not optimize the code for constants (which would save a register).
1166
1167 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1168 codegen_->AddSlowPath(slow_path);
1169
1170 __ Ldr(temp, HeapOperand(obj, count_offset)); // temp = str.length.
1171 codegen_->MaybeRecordImplicitNullCheck(invoke);
1172 __ Cmp(idx, temp);
1173 __ B(hs, slow_path->GetEntryLabel());
1174
Jeff Hao848f70a2014-01-15 13:49:50 -08001175 __ Add(array_temp, obj, Operand(value_offset.Int32Value())); // array_temp := str.value.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001176
1177 // Load the value.
Jeff Hao848f70a2014-01-15 13:49:50 -08001178 __ Ldrh(out, MemOperand(array_temp.X(), idx, UXTW, 1)); // out := array_temp[idx].
Andreas Gampe878d58c2015-01-15 23:24:00 -08001179
1180 __ Bind(slow_path->GetExitLabel());
1181}
1182
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001183void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001184 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Scott Wakeling1f36f412016-04-21 11:13:45 +01001185 invoke->InputAt(1)->CanBeNull()
1186 ? LocationSummary::kCallOnSlowPath
1187 : LocationSummary::kNoCall,
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001188 kIntrinsified);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001189 locations->SetInAt(0, Location::RequiresRegister());
1190 locations->SetInAt(1, Location::RequiresRegister());
1191 locations->AddTemp(Location::RequiresRegister());
1192 locations->AddTemp(Location::RequiresRegister());
1193 locations->AddTemp(Location::RequiresRegister());
1194 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001195}
1196
1197void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
1198 vixl::MacroAssembler* masm = GetVIXLAssembler();
1199 LocationSummary* locations = invoke->GetLocations();
1200
Scott Wakeling1f36f412016-04-21 11:13:45 +01001201 Register str = XRegisterFrom(locations->InAt(0));
1202 Register arg = XRegisterFrom(locations->InAt(1));
1203 Register out = OutputRegister(invoke);
1204
1205 Register temp0 = WRegisterFrom(locations->GetTemp(0));
1206 Register temp1 = WRegisterFrom(locations->GetTemp(1));
1207 Register temp2 = WRegisterFrom(locations->GetTemp(2));
1208
1209 vixl::Label loop;
1210 vixl::Label find_char_diff;
1211 vixl::Label end;
1212
1213 // Get offsets of count and value fields within a string object.
1214 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1215 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1216
Nicolas Geoffray512e04d2015-03-27 17:21:24 +00001217 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +01001218 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001219
Scott Wakeling1f36f412016-04-21 11:13:45 +01001220 // Take slow path and throw if input can be and is null.
1221 SlowPathCodeARM64* slow_path = nullptr;
1222 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1223 if (can_slow_path) {
1224 slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1225 codegen_->AddSlowPath(slow_path);
1226 __ Cbz(arg, slow_path->GetEntryLabel());
1227 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001228
Scott Wakeling1f36f412016-04-21 11:13:45 +01001229 // Reference equality check, return 0 if same reference.
1230 __ Subs(out, str, arg);
1231 __ B(&end, eq);
1232 // Load lengths of this and argument strings.
1233 __ Ldr(temp0, MemOperand(str.X(), count_offset));
1234 __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1235 // Return zero if both strings are empty.
1236 __ Orr(out, temp0, temp1);
1237 __ Cbz(out, &end);
1238 // out = length diff.
1239 __ Subs(out, temp0, temp1);
1240 // temp2 = min(len(str), len(arg)).
1241 __ Csel(temp2, temp1, temp0, ge);
1242 // Shorter string is empty?
1243 __ Cbz(temp2, &end);
1244
1245 // Store offset of string value in preparation for comparison loop.
1246 __ Mov(temp1, value_offset);
1247
1248 UseScratchRegisterScope scratch_scope(masm);
1249 Register temp4 = scratch_scope.AcquireX();
1250
1251 // Assertions that must hold in order to compare strings 4 characters at a time.
1252 DCHECK_ALIGNED(value_offset, 8);
1253 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1254
1255 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1256 DCHECK_EQ(char_size, 2u);
1257
1258 // Promote temp0 to an X reg, ready for LDR.
1259 temp0 = temp0.X();
1260
1261 // Loop to compare 4x16-bit characters at a time (ok because of string data alignment).
1262 __ Bind(&loop);
1263 __ Ldr(temp4, MemOperand(str.X(), temp1));
1264 __ Ldr(temp0, MemOperand(arg.X(), temp1));
1265 __ Cmp(temp4, temp0);
1266 __ B(ne, &find_char_diff);
1267 __ Add(temp1, temp1, char_size * 4);
1268 __ Subs(temp2, temp2, 4);
1269 __ B(gt, &loop);
1270 __ B(&end);
1271
1272 // Promote temp1 to an X reg, ready for EOR.
1273 temp1 = temp1.X();
1274
1275 // Find the single 16-bit character difference.
1276 __ Bind(&find_char_diff);
1277 // Get the bit position of the first character that differs.
1278 __ Eor(temp1, temp0, temp4);
1279 __ Rbit(temp1, temp1);
1280 __ Clz(temp1, temp1);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001281 // If the number of 16-bit chars remaining <= the index where the difference occurs (0-3), then
1282 // the difference occurs outside the remaining string data, so just return length diff (out).
1283 __ Cmp(temp2, Operand(temp1, LSR, 4));
1284 __ B(le, &end);
1285 // Extract the characters and calculate the difference.
Scott Wakelinge5ed20b2016-05-20 10:41:38 +01001286 __ Bic(temp1, temp1, 0xf);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001287 __ Lsr(temp0, temp0, temp1);
1288 __ Lsr(temp4, temp4, temp1);
1289 __ And(temp4, temp4, 0xffff);
1290 __ Sub(out, temp4, Operand(temp0, UXTH));
1291
1292 __ Bind(&end);
1293
1294 if (can_slow_path) {
1295 __ Bind(slow_path->GetExitLabel());
1296 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001297}
1298
Agi Csakiea34b402015-08-13 17:51:19 -07001299void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
1300 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1301 LocationSummary::kNoCall,
1302 kIntrinsified);
1303 locations->SetInAt(0, Location::RequiresRegister());
1304 locations->SetInAt(1, Location::RequiresRegister());
1305 // Temporary registers to store lengths of strings and for calculations.
1306 locations->AddTemp(Location::RequiresRegister());
1307 locations->AddTemp(Location::RequiresRegister());
1308
1309 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1310}
1311
1312void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
1313 vixl::MacroAssembler* masm = GetVIXLAssembler();
1314 LocationSummary* locations = invoke->GetLocations();
1315
1316 Register str = WRegisterFrom(locations->InAt(0));
1317 Register arg = WRegisterFrom(locations->InAt(1));
1318 Register out = XRegisterFrom(locations->Out());
1319
1320 UseScratchRegisterScope scratch_scope(masm);
1321 Register temp = scratch_scope.AcquireW();
1322 Register temp1 = WRegisterFrom(locations->GetTemp(0));
1323 Register temp2 = WRegisterFrom(locations->GetTemp(1));
1324
1325 vixl::Label loop;
1326 vixl::Label end;
1327 vixl::Label return_true;
1328 vixl::Label return_false;
1329
1330 // Get offsets of count, value, and class fields within a string object.
1331 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1332 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1333 const int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1334
1335 // Note that the null check must have been done earlier.
1336 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1337
Vladimir Marko53b52002016-05-24 19:30:45 +01001338 StringEqualsOptimizations optimizations(invoke);
1339 if (!optimizations.GetArgumentNotNull()) {
1340 // Check if input is null, return false if it is.
1341 __ Cbz(arg, &return_false);
1342 }
Agi Csakiea34b402015-08-13 17:51:19 -07001343
1344 // Reference equality check, return true if same reference.
1345 __ Cmp(str, arg);
1346 __ B(&return_true, eq);
1347
Vladimir Marko53b52002016-05-24 19:30:45 +01001348 if (!optimizations.GetArgumentIsString()) {
1349 // Instanceof check for the argument by comparing class fields.
1350 // All string objects must have the same type since String cannot be subclassed.
1351 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1352 // If the argument is a string object, its class field must be equal to receiver's class field.
1353 __ Ldr(temp, MemOperand(str.X(), class_offset));
1354 __ Ldr(temp1, MemOperand(arg.X(), class_offset));
1355 __ Cmp(temp, temp1);
1356 __ B(&return_false, ne);
1357 }
Agi Csakiea34b402015-08-13 17:51:19 -07001358
1359 // Load lengths of this and argument strings.
1360 __ Ldr(temp, MemOperand(str.X(), count_offset));
1361 __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1362 // Check if lengths are equal, return false if they're not.
1363 __ Cmp(temp, temp1);
1364 __ B(&return_false, ne);
1365 // Store offset of string value in preparation for comparison loop
1366 __ Mov(temp1, value_offset);
1367 // Return true if both strings are empty.
1368 __ Cbz(temp, &return_true);
1369
1370 // Assertions that must hold in order to compare strings 4 characters at a time.
1371 DCHECK_ALIGNED(value_offset, 8);
1372 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1373
1374 temp1 = temp1.X();
1375 temp2 = temp2.X();
1376
1377 // Loop to compare strings 4 characters at a time starting at the beginning of the string.
1378 // Ok to do this because strings are zero-padded to be 8-byte aligned.
1379 __ Bind(&loop);
1380 __ Ldr(out, MemOperand(str.X(), temp1));
1381 __ Ldr(temp2, MemOperand(arg.X(), temp1));
1382 __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
1383 __ Cmp(out, temp2);
1384 __ B(&return_false, ne);
1385 __ Sub(temp, temp, Operand(4), SetFlags);
1386 __ B(&loop, gt);
1387
1388 // Return true and exit the function.
1389 // If loop does not result in returning false, we return true.
1390 __ Bind(&return_true);
1391 __ Mov(out, 1);
1392 __ B(&end);
1393
1394 // Return false and exit the function.
1395 __ Bind(&return_false);
1396 __ Mov(out, 0);
1397 __ Bind(&end);
1398}
1399
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001400static void GenerateVisitStringIndexOf(HInvoke* invoke,
1401 vixl::MacroAssembler* masm,
1402 CodeGeneratorARM64* codegen,
1403 ArenaAllocator* allocator,
1404 bool start_at_zero) {
1405 LocationSummary* locations = invoke->GetLocations();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001406
1407 // Note that the null check must have been done earlier.
1408 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1409
1410 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001411 // 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 -07001412 SlowPathCodeARM64* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001413 HInstruction* code_point = invoke->InputAt(1);
1414 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01001415 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > 0xFFFFU) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001416 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1417 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1418 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1419 codegen->AddSlowPath(slow_path);
1420 __ B(slow_path->GetEntryLabel());
1421 __ Bind(slow_path->GetExitLabel());
1422 return;
1423 }
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001424 } else if (code_point->GetType() != Primitive::kPrimChar) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001425 Register char_reg = WRegisterFrom(locations->InAt(1));
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001426 __ Tst(char_reg, 0xFFFF0000);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001427 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1428 codegen->AddSlowPath(slow_path);
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001429 __ B(ne, slow_path->GetEntryLabel());
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001430 }
1431
1432 if (start_at_zero) {
1433 // Start-index = 0.
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001434 Register tmp_reg = WRegisterFrom(locations->GetTemp(0));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001435 __ Mov(tmp_reg, 0);
1436 }
1437
1438 __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pIndexOf).Int32Value()));
Roland Levillain42ad2882016-02-29 18:26:54 +00001439 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001440 __ Blr(lr);
1441
1442 if (slow_path != nullptr) {
1443 __ Bind(slow_path->GetExitLabel());
1444 }
1445}
1446
1447void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) {
1448 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1449 LocationSummary::kCall,
1450 kIntrinsified);
1451 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1452 // best to align the inputs accordingly.
1453 InvokeRuntimeCallingConvention calling_convention;
1454 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1455 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1456 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1457
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001458 // Need to send start_index=0.
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001459 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1460}
1461
1462void IntrinsicCodeGeneratorARM64::VisitStringIndexOf(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001463 GenerateVisitStringIndexOf(
1464 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001465}
1466
1467void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
1468 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1469 LocationSummary::kCall,
1470 kIntrinsified);
1471 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1472 // best to align the inputs accordingly.
1473 InvokeRuntimeCallingConvention calling_convention;
1474 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1475 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1476 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1477 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001478}
1479
1480void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001481 GenerateVisitStringIndexOf(
1482 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001483}
1484
Jeff Hao848f70a2014-01-15 13:49:50 -08001485void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1486 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1487 LocationSummary::kCall,
1488 kIntrinsified);
1489 InvokeRuntimeCallingConvention calling_convention;
1490 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1491 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1492 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1493 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1494 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1495}
1496
1497void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1498 vixl::MacroAssembler* masm = GetVIXLAssembler();
1499 LocationSummary* locations = invoke->GetLocations();
1500
1501 Register byte_array = WRegisterFrom(locations->InAt(0));
1502 __ Cmp(byte_array, 0);
1503 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1504 codegen_->AddSlowPath(slow_path);
1505 __ B(eq, slow_path->GetEntryLabel());
1506
1507 __ Ldr(lr,
1508 MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromBytes).Int32Value()));
Roland Levillainf969a202016-03-09 16:14:00 +00001509 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001510 __ Blr(lr);
Roland Levillainf969a202016-03-09 16:14:00 +00001511 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Jeff Hao848f70a2014-01-15 13:49:50 -08001512 __ Bind(slow_path->GetExitLabel());
1513}
1514
1515void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
1516 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1517 LocationSummary::kCall,
1518 kIntrinsified);
1519 InvokeRuntimeCallingConvention calling_convention;
1520 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1521 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1522 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1523 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1524}
1525
1526void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
1527 vixl::MacroAssembler* masm = GetVIXLAssembler();
1528
Roland Levillaincc3839c2016-02-29 16:23:48 +00001529 // No need to emit code checking whether `locations->InAt(2)` is a null
1530 // pointer, as callers of the native method
1531 //
1532 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1533 //
1534 // all include a null check on `data` before calling that method.
Jeff Hao848f70a2014-01-15 13:49:50 -08001535 __ Ldr(lr,
1536 MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromChars).Int32Value()));
Roland Levillainf969a202016-03-09 16:14:00 +00001537 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001538 __ Blr(lr);
Roland Levillainf969a202016-03-09 16:14:00 +00001539 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Jeff Hao848f70a2014-01-15 13:49:50 -08001540}
1541
1542void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Jeff Hao848f70a2014-01-15 13:49:50 -08001543 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1544 LocationSummary::kCall,
1545 kIntrinsified);
1546 InvokeRuntimeCallingConvention calling_convention;
1547 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Jeff Hao848f70a2014-01-15 13:49:50 -08001548 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1549}
1550
1551void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) {
1552 vixl::MacroAssembler* masm = GetVIXLAssembler();
1553 LocationSummary* locations = invoke->GetLocations();
1554
1555 Register string_to_copy = WRegisterFrom(locations->InAt(0));
1556 __ Cmp(string_to_copy, 0);
1557 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1558 codegen_->AddSlowPath(slow_path);
1559 __ B(eq, slow_path->GetEntryLabel());
1560
1561 __ Ldr(lr,
1562 MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromString).Int32Value()));
Roland Levillainf969a202016-03-09 16:14:00 +00001563 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001564 __ Blr(lr);
Roland Levillainf969a202016-03-09 16:14:00 +00001565 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Jeff Hao848f70a2014-01-15 13:49:50 -08001566 __ Bind(slow_path->GetExitLabel());
1567}
1568
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001569static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1570 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
1571 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1572 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1573
1574 LocationSummary* const locations = new (arena) LocationSummary(invoke,
1575 LocationSummary::kCall,
1576 kIntrinsified);
1577 InvokeRuntimeCallingConvention calling_convention;
1578
1579 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1580 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1581}
1582
1583static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1584 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
1585 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1586 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(1)->GetType()));
1587 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1588
1589 LocationSummary* const locations = new (arena) LocationSummary(invoke,
1590 LocationSummary::kCall,
1591 kIntrinsified);
1592 InvokeRuntimeCallingConvention calling_convention;
1593
1594 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1595 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
1596 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1597}
1598
1599static void GenFPToFPCall(HInvoke* invoke,
1600 vixl::MacroAssembler* masm,
1601 CodeGeneratorARM64* codegen,
1602 QuickEntrypointEnum entry) {
1603 __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArm64WordSize>(entry).Int32Value()));
1604 __ Blr(lr);
1605 codegen->RecordPcInfo(invoke, invoke->GetDexPc());
1606}
1607
1608void IntrinsicLocationsBuilderARM64::VisitMathCos(HInvoke* invoke) {
1609 CreateFPToFPCallLocations(arena_, invoke);
1610}
1611
1612void IntrinsicCodeGeneratorARM64::VisitMathCos(HInvoke* invoke) {
1613 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCos);
1614}
1615
1616void IntrinsicLocationsBuilderARM64::VisitMathSin(HInvoke* invoke) {
1617 CreateFPToFPCallLocations(arena_, invoke);
1618}
1619
1620void IntrinsicCodeGeneratorARM64::VisitMathSin(HInvoke* invoke) {
1621 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickSin);
1622}
1623
1624void IntrinsicLocationsBuilderARM64::VisitMathAcos(HInvoke* invoke) {
1625 CreateFPToFPCallLocations(arena_, invoke);
1626}
1627
1628void IntrinsicCodeGeneratorARM64::VisitMathAcos(HInvoke* invoke) {
1629 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAcos);
1630}
1631
1632void IntrinsicLocationsBuilderARM64::VisitMathAsin(HInvoke* invoke) {
1633 CreateFPToFPCallLocations(arena_, invoke);
1634}
1635
1636void IntrinsicCodeGeneratorARM64::VisitMathAsin(HInvoke* invoke) {
1637 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAsin);
1638}
1639
1640void IntrinsicLocationsBuilderARM64::VisitMathAtan(HInvoke* invoke) {
1641 CreateFPToFPCallLocations(arena_, invoke);
1642}
1643
1644void IntrinsicCodeGeneratorARM64::VisitMathAtan(HInvoke* invoke) {
1645 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAtan);
1646}
1647
1648void IntrinsicLocationsBuilderARM64::VisitMathCbrt(HInvoke* invoke) {
1649 CreateFPToFPCallLocations(arena_, invoke);
1650}
1651
1652void IntrinsicCodeGeneratorARM64::VisitMathCbrt(HInvoke* invoke) {
1653 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCbrt);
1654}
1655
1656void IntrinsicLocationsBuilderARM64::VisitMathCosh(HInvoke* invoke) {
1657 CreateFPToFPCallLocations(arena_, invoke);
1658}
1659
1660void IntrinsicCodeGeneratorARM64::VisitMathCosh(HInvoke* invoke) {
1661 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCosh);
1662}
1663
1664void IntrinsicLocationsBuilderARM64::VisitMathExp(HInvoke* invoke) {
1665 CreateFPToFPCallLocations(arena_, invoke);
1666}
1667
1668void IntrinsicCodeGeneratorARM64::VisitMathExp(HInvoke* invoke) {
1669 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickExp);
1670}
1671
1672void IntrinsicLocationsBuilderARM64::VisitMathExpm1(HInvoke* invoke) {
1673 CreateFPToFPCallLocations(arena_, invoke);
1674}
1675
1676void IntrinsicCodeGeneratorARM64::VisitMathExpm1(HInvoke* invoke) {
1677 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickExpm1);
1678}
1679
1680void IntrinsicLocationsBuilderARM64::VisitMathLog(HInvoke* invoke) {
1681 CreateFPToFPCallLocations(arena_, invoke);
1682}
1683
1684void IntrinsicCodeGeneratorARM64::VisitMathLog(HInvoke* invoke) {
1685 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickLog);
1686}
1687
1688void IntrinsicLocationsBuilderARM64::VisitMathLog10(HInvoke* invoke) {
1689 CreateFPToFPCallLocations(arena_, invoke);
1690}
1691
1692void IntrinsicCodeGeneratorARM64::VisitMathLog10(HInvoke* invoke) {
1693 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickLog10);
1694}
1695
1696void IntrinsicLocationsBuilderARM64::VisitMathSinh(HInvoke* invoke) {
1697 CreateFPToFPCallLocations(arena_, invoke);
1698}
1699
1700void IntrinsicCodeGeneratorARM64::VisitMathSinh(HInvoke* invoke) {
1701 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickSinh);
1702}
1703
1704void IntrinsicLocationsBuilderARM64::VisitMathTan(HInvoke* invoke) {
1705 CreateFPToFPCallLocations(arena_, invoke);
1706}
1707
1708void IntrinsicCodeGeneratorARM64::VisitMathTan(HInvoke* invoke) {
1709 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickTan);
1710}
1711
1712void IntrinsicLocationsBuilderARM64::VisitMathTanh(HInvoke* invoke) {
1713 CreateFPToFPCallLocations(arena_, invoke);
1714}
1715
1716void IntrinsicCodeGeneratorARM64::VisitMathTanh(HInvoke* invoke) {
1717 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickTanh);
1718}
1719
1720void IntrinsicLocationsBuilderARM64::VisitMathAtan2(HInvoke* invoke) {
1721 CreateFPFPToFPCallLocations(arena_, invoke);
1722}
1723
1724void IntrinsicCodeGeneratorARM64::VisitMathAtan2(HInvoke* invoke) {
1725 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAtan2);
1726}
1727
1728void IntrinsicLocationsBuilderARM64::VisitMathHypot(HInvoke* invoke) {
1729 CreateFPFPToFPCallLocations(arena_, invoke);
1730}
1731
1732void IntrinsicCodeGeneratorARM64::VisitMathHypot(HInvoke* invoke) {
1733 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickHypot);
1734}
1735
1736void IntrinsicLocationsBuilderARM64::VisitMathNextAfter(HInvoke* invoke) {
1737 CreateFPFPToFPCallLocations(arena_, invoke);
1738}
1739
1740void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) {
1741 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickNextAfter);
1742}
1743
Tim Zhang25abd6c2016-01-19 23:39:24 +08001744void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1745 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1746 LocationSummary::kNoCall,
1747 kIntrinsified);
1748 locations->SetInAt(0, Location::RequiresRegister());
1749 locations->SetInAt(1, Location::RequiresRegister());
1750 locations->SetInAt(2, Location::RequiresRegister());
1751 locations->SetInAt(3, Location::RequiresRegister());
1752 locations->SetInAt(4, Location::RequiresRegister());
1753
1754 locations->AddTemp(Location::RequiresRegister());
1755 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingdf109d92016-04-22 11:35:56 +01001756 locations->AddTemp(Location::RequiresRegister());
Tim Zhang25abd6c2016-01-19 23:39:24 +08001757}
1758
1759void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1760 vixl::MacroAssembler* masm = GetVIXLAssembler();
1761 LocationSummary* locations = invoke->GetLocations();
1762
1763 // Check assumption that sizeof(Char) is 2 (used in scaling below).
1764 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1765 DCHECK_EQ(char_size, 2u);
1766
1767 // Location of data in char array buffer.
1768 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
1769
1770 // Location of char array data in string.
1771 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1772
1773 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
1774 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
1775 Register srcObj = XRegisterFrom(locations->InAt(0));
1776 Register srcBegin = XRegisterFrom(locations->InAt(1));
1777 Register srcEnd = XRegisterFrom(locations->InAt(2));
1778 Register dstObj = XRegisterFrom(locations->InAt(3));
1779 Register dstBegin = XRegisterFrom(locations->InAt(4));
1780
1781 Register src_ptr = XRegisterFrom(locations->GetTemp(0));
Scott Wakelingdf109d92016-04-22 11:35:56 +01001782 Register num_chr = XRegisterFrom(locations->GetTemp(1));
1783 Register tmp1 = XRegisterFrom(locations->GetTemp(2));
Tim Zhang25abd6c2016-01-19 23:39:24 +08001784
1785 UseScratchRegisterScope temps(masm);
1786 Register dst_ptr = temps.AcquireX();
Scott Wakelingdf109d92016-04-22 11:35:56 +01001787 Register tmp2 = temps.AcquireX();
Tim Zhang25abd6c2016-01-19 23:39:24 +08001788
Scott Wakelingdf109d92016-04-22 11:35:56 +01001789 // src address to copy from.
Tim Zhang25abd6c2016-01-19 23:39:24 +08001790 __ Add(src_ptr, srcObj, Operand(value_offset));
Tim Zhang25abd6c2016-01-19 23:39:24 +08001791 __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
1792
Scott Wakelingdf109d92016-04-22 11:35:56 +01001793 // dst address start to copy to.
Tim Zhang25abd6c2016-01-19 23:39:24 +08001794 __ Add(dst_ptr, dstObj, Operand(data_offset));
1795 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
1796
Scott Wakelingdf109d92016-04-22 11:35:56 +01001797 __ Sub(num_chr, srcEnd, srcBegin);
1798
Tim Zhang25abd6c2016-01-19 23:39:24 +08001799 // Do the copy.
Scott Wakelingdf109d92016-04-22 11:35:56 +01001800 vixl::Label loop;
1801 vixl::Label done;
1802 vixl::Label remainder;
1803
1804 // Early out for valid zero-length retrievals.
1805 __ Cbz(num_chr, &done);
1806
1807 // Save repairing the value of num_chr on the < 8 character path.
1808 __ Subs(tmp1, num_chr, 8);
1809 __ B(lt, &remainder);
1810
1811 // Keep the result of the earlier subs, we are going to fetch at least 8 characters.
1812 __ Mov(num_chr, tmp1);
1813
1814 // Main loop used for longer fetches loads and stores 8x16-bit characters at a time.
1815 // (Unaligned addresses are acceptable here and not worth inlining extra code to rectify.)
Tim Zhang25abd6c2016-01-19 23:39:24 +08001816 __ Bind(&loop);
Scott Wakelingdf109d92016-04-22 11:35:56 +01001817 __ Ldp(tmp1, tmp2, MemOperand(src_ptr, char_size * 8, vixl::PostIndex));
1818 __ Subs(num_chr, num_chr, 8);
1819 __ Stp(tmp1, tmp2, MemOperand(dst_ptr, char_size * 8, vixl::PostIndex));
1820 __ B(ge, &loop);
1821
1822 __ Adds(num_chr, num_chr, 8);
1823 __ B(eq, &done);
1824
1825 // Main loop for < 8 character case and remainder handling. Loads and stores one
1826 // 16-bit Java character at a time.
1827 __ Bind(&remainder);
1828 __ Ldrh(tmp1, MemOperand(src_ptr, char_size, vixl::PostIndex));
1829 __ Subs(num_chr, num_chr, 1);
1830 __ Strh(tmp1, MemOperand(dst_ptr, char_size, vixl::PostIndex));
1831 __ B(gt, &remainder);
1832
Tim Zhang25abd6c2016-01-19 23:39:24 +08001833 __ Bind(&done);
1834}
1835
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001836// Mirrors ARRAYCOPY_SHORT_CHAR_ARRAY_THRESHOLD in libcore, so we can choose to use the native
1837// implementation there for longer copy lengths.
donghui.baic2ec9ad2016-03-10 14:02:55 +08001838static constexpr int32_t kSystemArrayCopyCharThreshold = 32;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001839
1840static void SetSystemArrayCopyLocationRequires(LocationSummary* locations,
1841 uint32_t at,
1842 HInstruction* input) {
1843 HIntConstant* const_input = input->AsIntConstant();
1844 if (const_input != nullptr && !vixl::Assembler::IsImmAddSub(const_input->GetValue())) {
1845 locations->SetInAt(at, Location::RequiresRegister());
1846 } else {
1847 locations->SetInAt(at, Location::RegisterOrConstant(input));
1848 }
1849}
1850
1851void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
1852 // Check to see if we have known failures that will cause us to have to bail out
1853 // to the runtime, and just generate the runtime call directly.
1854 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1855 HIntConstant* dst_pos = invoke->InputAt(3)->AsIntConstant();
1856
1857 // The positions must be non-negative.
1858 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
1859 (dst_pos != nullptr && dst_pos->GetValue() < 0)) {
1860 // We will have to fail anyways.
1861 return;
1862 }
1863
1864 // The length must be >= 0 and not so long that we would (currently) prefer libcore's
1865 // native implementation.
1866 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1867 if (length != nullptr) {
1868 int32_t len = length->GetValue();
donghui.baic2ec9ad2016-03-10 14:02:55 +08001869 if (len < 0 || len > kSystemArrayCopyCharThreshold) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001870 // Just call as normal.
1871 return;
1872 }
1873 }
1874
1875 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
1876 LocationSummary* locations = new (allocator) LocationSummary(invoke,
1877 LocationSummary::kCallOnSlowPath,
1878 kIntrinsified);
1879 // arraycopy(char[] src, int src_pos, char[] dst, int dst_pos, int length).
1880 locations->SetInAt(0, Location::RequiresRegister());
1881 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
1882 locations->SetInAt(2, Location::RequiresRegister());
1883 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
1884 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
1885
1886 locations->AddTemp(Location::RequiresRegister());
1887 locations->AddTemp(Location::RequiresRegister());
1888 locations->AddTemp(Location::RequiresRegister());
1889}
1890
1891static void CheckSystemArrayCopyPosition(vixl::MacroAssembler* masm,
1892 const Location& pos,
1893 const Register& input,
1894 const Location& length,
1895 SlowPathCodeARM64* slow_path,
1896 const Register& input_len,
1897 const Register& temp,
1898 bool length_is_input_length = false) {
1899 const int32_t length_offset = mirror::Array::LengthOffset().Int32Value();
1900 if (pos.IsConstant()) {
1901 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
1902 if (pos_const == 0) {
1903 if (!length_is_input_length) {
1904 // Check that length(input) >= length.
1905 __ Ldr(temp, MemOperand(input, length_offset));
1906 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
1907 __ B(slow_path->GetEntryLabel(), lt);
1908 }
1909 } else {
1910 // Check that length(input) >= pos.
1911 __ Ldr(input_len, MemOperand(input, length_offset));
1912 __ Subs(temp, input_len, pos_const);
1913 __ B(slow_path->GetEntryLabel(), lt);
1914
1915 // Check that (length(input) - pos) >= length.
1916 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
1917 __ B(slow_path->GetEntryLabel(), lt);
1918 }
1919 } else if (length_is_input_length) {
1920 // The only way the copy can succeed is if pos is zero.
1921 __ Cbnz(WRegisterFrom(pos), slow_path->GetEntryLabel());
1922 } else {
1923 // Check that pos >= 0.
1924 Register pos_reg = WRegisterFrom(pos);
1925 __ Tbnz(pos_reg, pos_reg.size() - 1, slow_path->GetEntryLabel());
1926
1927 // Check that pos <= length(input) && (length(input) - pos) >= length.
1928 __ Ldr(temp, MemOperand(input, length_offset));
1929 __ Subs(temp, temp, pos_reg);
1930 // Ccmp if length(input) >= pos, else definitely bail to slow path (N!=V == lt).
1931 __ Ccmp(temp, OperandFrom(length, Primitive::kPrimInt), NFlag, ge);
1932 __ B(slow_path->GetEntryLabel(), lt);
1933 }
1934}
1935
1936// Compute base source address, base destination address, and end source address
1937// for System.arraycopy* intrinsics.
1938static void GenSystemArrayCopyAddresses(vixl::MacroAssembler* masm,
1939 Primitive::Type type,
1940 const Register& src,
1941 const Location& src_pos,
1942 const Register& dst,
1943 const Location& dst_pos,
1944 const Location& copy_length,
1945 const Register& src_base,
1946 const Register& dst_base,
1947 const Register& src_end) {
1948 DCHECK(type == Primitive::kPrimNot || type == Primitive::kPrimChar)
Roland Levillainebea3d22016-04-12 15:42:57 +01001949 << "Unexpected element type: " << type;
1950 const int32_t element_size = Primitive::ComponentSize(type);
1951 const int32_t element_size_shift = Primitive::ComponentSizeShift(type);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001952
Roland Levillainebea3d22016-04-12 15:42:57 +01001953 uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001954 if (src_pos.IsConstant()) {
1955 int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01001956 __ Add(src_base, src, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001957 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01001958 __ Add(src_base, src, data_offset);
1959 __ Add(src_base, src_base, Operand(XRegisterFrom(src_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001960 }
1961
1962 if (dst_pos.IsConstant()) {
1963 int32_t constant = dst_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01001964 __ Add(dst_base, dst, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001965 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01001966 __ Add(dst_base, dst, data_offset);
1967 __ Add(dst_base, dst_base, Operand(XRegisterFrom(dst_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001968 }
1969
1970 if (copy_length.IsConstant()) {
1971 int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01001972 __ Add(src_end, src_base, element_size * constant);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001973 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01001974 __ Add(src_end, src_base, Operand(XRegisterFrom(copy_length), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001975 }
1976}
1977
1978void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
1979 vixl::MacroAssembler* masm = GetVIXLAssembler();
1980 LocationSummary* locations = invoke->GetLocations();
1981 Register src = XRegisterFrom(locations->InAt(0));
1982 Location src_pos = locations->InAt(1);
1983 Register dst = XRegisterFrom(locations->InAt(2));
1984 Location dst_pos = locations->InAt(3);
1985 Location length = locations->InAt(4);
1986
1987 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1988 codegen_->AddSlowPath(slow_path);
1989
1990 // If source and destination are the same, take the slow path. Overlapping copy regions must be
1991 // copied in reverse and we can't know in all cases if it's needed.
1992 __ Cmp(src, dst);
1993 __ B(slow_path->GetEntryLabel(), eq);
1994
1995 // Bail out if the source is null.
1996 __ Cbz(src, slow_path->GetEntryLabel());
1997
1998 // Bail out if the destination is null.
1999 __ Cbz(dst, slow_path->GetEntryLabel());
2000
2001 if (!length.IsConstant()) {
2002 // If the length is negative, bail out.
2003 __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel());
2004 // If the length > 32 then (currently) prefer libcore's native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002005 __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002006 __ B(slow_path->GetEntryLabel(), gt);
2007 } else {
2008 // We have already checked in the LocationsBuilder for the constant case.
2009 DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0);
2010 DCHECK_LE(length.GetConstant()->AsIntConstant()->GetValue(), 32);
2011 }
2012
2013 Register src_curr_addr = WRegisterFrom(locations->GetTemp(0));
2014 Register dst_curr_addr = WRegisterFrom(locations->GetTemp(1));
2015 Register src_stop_addr = WRegisterFrom(locations->GetTemp(2));
2016
2017 CheckSystemArrayCopyPosition(masm,
2018 src_pos,
2019 src,
2020 length,
2021 slow_path,
2022 src_curr_addr,
2023 dst_curr_addr,
2024 false);
2025
2026 CheckSystemArrayCopyPosition(masm,
2027 dst_pos,
2028 dst,
2029 length,
2030 slow_path,
2031 src_curr_addr,
2032 dst_curr_addr,
2033 false);
2034
2035 src_curr_addr = src_curr_addr.X();
2036 dst_curr_addr = dst_curr_addr.X();
2037 src_stop_addr = src_stop_addr.X();
2038
2039 GenSystemArrayCopyAddresses(masm,
2040 Primitive::kPrimChar,
2041 src,
2042 src_pos,
2043 dst,
2044 dst_pos,
2045 length,
2046 src_curr_addr,
2047 dst_curr_addr,
2048 src_stop_addr);
2049
2050 // Iterate over the arrays and do a raw copy of the chars.
2051 const int32_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2052 UseScratchRegisterScope temps(masm);
2053 Register tmp = temps.AcquireW();
2054 vixl::Label loop, done;
2055 __ Bind(&loop);
2056 __ Cmp(src_curr_addr, src_stop_addr);
2057 __ B(&done, eq);
2058 __ Ldrh(tmp, MemOperand(src_curr_addr, char_size, vixl::PostIndex));
2059 __ Strh(tmp, MemOperand(dst_curr_addr, char_size, vixl::PostIndex));
2060 __ B(&loop);
2061 __ Bind(&done);
2062
2063 __ Bind(slow_path->GetExitLabel());
2064}
2065
donghui.baic2ec9ad2016-03-10 14:02:55 +08002066// We can choose to use the native implementation there for longer copy lengths.
2067static constexpr int32_t kSystemArrayCopyThreshold = 128;
2068
2069// CodeGenerator::CreateSystemArrayCopyLocationSummary use three temporary registers.
2070// We want to use two temporary registers in order to reduce the register pressure in arm64.
2071// So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary.
2072void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) {
2073 // Check to see if we have known failures that will cause us to have to bail out
2074 // to the runtime, and just generate the runtime call directly.
2075 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2076 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
2077
2078 // The positions must be non-negative.
2079 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2080 (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
2081 // We will have to fail anyways.
2082 return;
2083 }
2084
2085 // The length must be >= 0.
2086 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2087 if (length != nullptr) {
2088 int32_t len = length->GetValue();
2089 if (len < 0 || len >= kSystemArrayCopyThreshold) {
2090 // Just call as normal.
2091 return;
2092 }
2093 }
2094
2095 SystemArrayCopyOptimizations optimizations(invoke);
2096
2097 if (optimizations.GetDestinationIsSource()) {
2098 if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) {
2099 // We only support backward copying if source and destination are the same.
2100 return;
2101 }
2102 }
2103
2104 if (optimizations.GetDestinationIsPrimitiveArray() || optimizations.GetSourceIsPrimitiveArray()) {
2105 // We currently don't intrinsify primitive copying.
2106 return;
2107 }
2108
2109 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2110 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2111 LocationSummary::kCallOnSlowPath,
2112 kIntrinsified);
2113 // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length).
2114 locations->SetInAt(0, Location::RequiresRegister());
2115 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2116 locations->SetInAt(2, Location::RequiresRegister());
2117 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2118 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2119
2120 locations->AddTemp(Location::RequiresRegister());
2121 locations->AddTemp(Location::RequiresRegister());
2122}
2123
2124void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) {
2125 vixl::MacroAssembler* masm = GetVIXLAssembler();
2126 LocationSummary* locations = invoke->GetLocations();
2127
2128 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2129 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2130 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2131 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
2132
2133 Register src = XRegisterFrom(locations->InAt(0));
2134 Location src_pos = locations->InAt(1);
2135 Register dest = XRegisterFrom(locations->InAt(2));
2136 Location dest_pos = locations->InAt(3);
2137 Location length = locations->InAt(4);
2138 Register temp1 = WRegisterFrom(locations->GetTemp(0));
2139 Register temp2 = WRegisterFrom(locations->GetTemp(1));
2140
2141 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2142 codegen_->AddSlowPath(slow_path);
2143
2144 vixl::Label conditions_on_positions_validated;
2145 SystemArrayCopyOptimizations optimizations(invoke);
2146
2147 if (!optimizations.GetDestinationIsSource() &&
2148 (!src_pos.IsConstant() || !dest_pos.IsConstant())) {
2149 __ Cmp(src, dest);
2150 }
2151 // If source and destination are the same, we go to slow path if we need to do
2152 // forward copying.
2153 if (src_pos.IsConstant()) {
2154 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
2155 if (dest_pos.IsConstant()) {
2156 // Checked when building locations.
2157 DCHECK(!optimizations.GetDestinationIsSource()
2158 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
2159 } else {
2160 if (!optimizations.GetDestinationIsSource()) {
2161 __ B(&conditions_on_positions_validated, ne);
2162 }
2163 __ Cmp(WRegisterFrom(dest_pos), src_pos_constant);
2164 __ B(slow_path->GetEntryLabel(), gt);
2165 }
2166 } else {
2167 if (!optimizations.GetDestinationIsSource()) {
2168 __ B(&conditions_on_positions_validated, ne);
2169 }
2170 __ Cmp(RegisterFrom(src_pos, invoke->InputAt(1)->GetType()),
2171 OperandFrom(dest_pos, invoke->InputAt(3)->GetType()));
2172 __ B(slow_path->GetEntryLabel(), lt);
2173 }
2174
2175 __ Bind(&conditions_on_positions_validated);
2176
2177 if (!optimizations.GetSourceIsNotNull()) {
2178 // Bail out if the source is null.
2179 __ Cbz(src, slow_path->GetEntryLabel());
2180 }
2181
2182 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
2183 // Bail out if the destination is null.
2184 __ Cbz(dest, slow_path->GetEntryLabel());
2185 }
2186
2187 // We have already checked in the LocationsBuilder for the constant case.
2188 if (!length.IsConstant() &&
2189 !optimizations.GetCountIsSourceLength() &&
2190 !optimizations.GetCountIsDestinationLength()) {
2191 // If the length is negative, bail out.
2192 __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel());
2193 // If the length >= 128 then (currently) prefer native implementation.
2194 __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold);
2195 __ B(slow_path->GetEntryLabel(), ge);
2196 }
2197 // Validity checks: source.
2198 CheckSystemArrayCopyPosition(masm,
2199 src_pos,
2200 src,
2201 length,
2202 slow_path,
2203 temp1,
2204 temp2,
2205 optimizations.GetCountIsSourceLength());
2206
2207 // Validity checks: dest.
2208 CheckSystemArrayCopyPosition(masm,
2209 dest_pos,
2210 dest,
2211 length,
2212 slow_path,
2213 temp1,
2214 temp2,
2215 optimizations.GetCountIsDestinationLength());
2216 {
2217 // We use a block to end the scratch scope before the write barrier, thus
2218 // freeing the temporary registers so they can be used in `MarkGCCard`.
2219 UseScratchRegisterScope temps(masm);
2220 Register temp3 = temps.AcquireW();
2221 if (!optimizations.GetDoesNotNeedTypeCheck()) {
2222 // Check whether all elements of the source array are assignable to the component
2223 // type of the destination array. We do two checks: the classes are the same,
2224 // or the destination is Object[]. If none of these checks succeed, we go to the
2225 // slow path.
2226 __ Ldr(temp1, MemOperand(dest, class_offset));
2227 __ Ldr(temp2, MemOperand(src, class_offset));
2228 bool did_unpoison = false;
2229 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2230 !optimizations.GetSourceIsNonPrimitiveArray()) {
2231 // One or two of the references need to be unpoisoned. Unpoison them
2232 // both to make the identity check valid.
2233 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2234 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2235 did_unpoison = true;
2236 }
2237
2238 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2239 // Bail out if the destination is not a non primitive array.
2240 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2241 __ Ldr(temp3, HeapOperand(temp1, component_offset));
2242 __ Cbz(temp3, slow_path->GetEntryLabel());
2243 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2244 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2245 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2246 __ Cbnz(temp3, slow_path->GetEntryLabel());
2247 }
2248
2249 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2250 // Bail out if the source is not a non primitive array.
2251 // /* HeapReference<Class> */ temp3 = temp2->component_type_
2252 __ Ldr(temp3, HeapOperand(temp2, component_offset));
2253 __ Cbz(temp3, slow_path->GetEntryLabel());
2254 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2255 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2256 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2257 __ Cbnz(temp3, slow_path->GetEntryLabel());
2258 }
2259
2260 __ Cmp(temp1, temp2);
2261
2262 if (optimizations.GetDestinationIsTypedObjectArray()) {
2263 vixl::Label do_copy;
2264 __ B(&do_copy, eq);
2265 if (!did_unpoison) {
2266 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2267 }
2268 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2269 __ Ldr(temp1, HeapOperand(temp1, component_offset));
2270 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2271 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2272 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2273 // No need to unpoison the result, we're comparing against null.
2274 __ Cbnz(temp1, slow_path->GetEntryLabel());
2275 __ Bind(&do_copy);
2276 } else {
2277 __ B(slow_path->GetEntryLabel(), ne);
2278 }
2279 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2280 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2281 // Bail out if the source is not a non primitive array.
2282 // /* HeapReference<Class> */ temp1 = src->klass_
2283 __ Ldr(temp1, HeapOperand(src.W(), class_offset));
2284 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2285 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2286 __ Ldr(temp3, HeapOperand(temp1, component_offset));
2287 __ Cbz(temp3, slow_path->GetEntryLabel());
2288 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2289 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2290 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2291 __ Cbnz(temp3, slow_path->GetEntryLabel());
2292 }
2293
2294 Register src_curr_addr = temp1.X();
2295 Register dst_curr_addr = temp2.X();
2296 Register src_stop_addr = temp3.X();
2297
2298 GenSystemArrayCopyAddresses(masm,
2299 Primitive::kPrimNot,
2300 src,
2301 src_pos,
2302 dest,
2303 dest_pos,
2304 length,
2305 src_curr_addr,
2306 dst_curr_addr,
2307 src_stop_addr);
2308
2309 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2310 // poison/unpoison, nor do any read barrier as the next uses of the destination
2311 // array will do it.
2312 vixl::Label loop, done;
2313 const int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
2314 __ Bind(&loop);
2315 __ Cmp(src_curr_addr, src_stop_addr);
2316 __ B(&done, eq);
2317 {
2318 Register tmp = temps.AcquireW();
2319 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, vixl::PostIndex));
2320 __ Str(tmp, MemOperand(dst_curr_addr, element_size, vixl::PostIndex));
2321 }
2322 __ B(&loop);
2323 __ Bind(&done);
2324 }
2325 // We only need one card marking on the destination array.
2326 codegen_->MarkGCCard(dest.W(), Register(), /* value_can_be_null */ false);
2327
2328 __ Bind(slow_path->GetExitLabel());
2329}
2330
Anton Kirilova3ffea22016-04-07 17:02:37 +01002331static void GenIsInfinite(LocationSummary* locations,
2332 bool is64bit,
2333 vixl::MacroAssembler* masm) {
2334 Operand infinity;
2335 Register out;
2336
2337 if (is64bit) {
2338 infinity = kPositiveInfinityDouble;
2339 out = XRegisterFrom(locations->Out());
2340 } else {
2341 infinity = kPositiveInfinityFloat;
2342 out = WRegisterFrom(locations->Out());
2343 }
2344
2345 const Register zero = vixl::Assembler::AppropriateZeroRegFor(out);
2346
2347 MoveFPToInt(locations, is64bit, masm);
2348 __ Eor(out, out, infinity);
2349 // We don't care about the sign bit, so shift left.
2350 __ Cmp(zero, Operand(out, LSL, 1));
2351 __ Cset(out, eq);
2352}
2353
2354void IntrinsicLocationsBuilderARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2355 CreateFPToIntLocations(arena_, invoke);
2356}
2357
2358void IntrinsicCodeGeneratorARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2359 GenIsInfinite(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
2360}
2361
2362void IntrinsicLocationsBuilderARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2363 CreateFPToIntLocations(arena_, invoke);
2364}
2365
2366void IntrinsicCodeGeneratorARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2367 GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
2368}
2369
Aart Bik2f9fcc92016-03-01 15:16:54 -08002370UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
Aart Bik2f9fcc92016-03-01 15:16:54 -08002371UNIMPLEMENTED_INTRINSIC(ARM64, IntegerHighestOneBit)
2372UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit)
2373UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit)
2374UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit)
Andreas Gampe878d58c2015-01-15 23:24:00 -08002375
Aart Bik0e54c012016-03-04 12:08:31 -08002376// 1.8.
2377UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt)
2378UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong)
2379UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt)
2380UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong)
2381UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08002382
Aart Bik2f9fcc92016-03-01 15:16:54 -08002383UNREACHABLE_INTRINSICS(ARM64)
Roland Levillain4d027112015-07-01 15:41:14 +01002384
2385#undef __
2386
Andreas Gampe878d58c2015-01-15 23:24:00 -08002387} // namespace arm64
2388} // namespace art