blob: ee10506219e183779d21142e00604719327e6709 [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 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000152 return res->Intrinsified();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800153}
154
155#define __ masm->
156
157static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
158 LocationSummary* locations = new (arena) LocationSummary(invoke,
159 LocationSummary::kNoCall,
160 kIntrinsified);
161 locations->SetInAt(0, Location::RequiresFpuRegister());
162 locations->SetOut(Location::RequiresRegister());
163}
164
165static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
166 LocationSummary* locations = new (arena) LocationSummary(invoke,
167 LocationSummary::kNoCall,
168 kIntrinsified);
169 locations->SetInAt(0, Location::RequiresRegister());
170 locations->SetOut(Location::RequiresFpuRegister());
171}
172
173static void MoveFPToInt(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
174 Location input = locations->InAt(0);
175 Location output = locations->Out();
176 __ Fmov(is64bit ? XRegisterFrom(output) : WRegisterFrom(output),
177 is64bit ? DRegisterFrom(input) : SRegisterFrom(input));
178}
179
180static void MoveIntToFP(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
181 Location input = locations->InAt(0);
182 Location output = locations->Out();
183 __ Fmov(is64bit ? DRegisterFrom(output) : SRegisterFrom(output),
184 is64bit ? XRegisterFrom(input) : WRegisterFrom(input));
185}
186
187void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
188 CreateFPToIntLocations(arena_, invoke);
189}
190void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
191 CreateIntToFPLocations(arena_, invoke);
192}
193
194void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000195 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800196}
197void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000198 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800199}
200
201void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
202 CreateFPToIntLocations(arena_, invoke);
203}
204void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
205 CreateIntToFPLocations(arena_, invoke);
206}
207
208void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000209 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800210}
211void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000212 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800213}
214
215static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
216 LocationSummary* locations = new (arena) LocationSummary(invoke,
217 LocationSummary::kNoCall,
218 kIntrinsified);
219 locations->SetInAt(0, Location::RequiresRegister());
220 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
221}
222
223static void GenReverseBytes(LocationSummary* locations,
224 Primitive::Type type,
225 vixl::MacroAssembler* masm) {
226 Location in = locations->InAt(0);
227 Location out = locations->Out();
228
229 switch (type) {
230 case Primitive::kPrimShort:
231 __ Rev16(WRegisterFrom(out), WRegisterFrom(in));
232 __ Sxth(WRegisterFrom(out), WRegisterFrom(out));
233 break;
234 case Primitive::kPrimInt:
235 case Primitive::kPrimLong:
236 __ Rev(RegisterFrom(out, type), RegisterFrom(in, type));
237 break;
238 default:
239 LOG(FATAL) << "Unexpected size for reverse-bytes: " << type;
240 UNREACHABLE();
241 }
242}
243
244void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
245 CreateIntToIntLocations(arena_, invoke);
246}
247
248void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
249 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
250}
251
252void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) {
253 CreateIntToIntLocations(arena_, invoke);
254}
255
256void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) {
257 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
258}
259
260void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) {
261 CreateIntToIntLocations(arena_, invoke);
262}
263
264void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
265 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
266}
267
Aart Bik7b565022016-01-28 14:36:22 -0800268static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
269 LocationSummary* locations = new (arena) LocationSummary(invoke,
270 LocationSummary::kNoCall,
271 kIntrinsified);
272 locations->SetInAt(0, Location::RequiresRegister());
273 locations->SetInAt(1, Location::RequiresRegister());
274 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
275}
276
Scott Wakeling611d3392015-07-10 11:42:06 +0100277static void GenNumberOfLeadingZeros(LocationSummary* locations,
278 Primitive::Type type,
279 vixl::MacroAssembler* masm) {
280 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
281
282 Location in = locations->InAt(0);
283 Location out = locations->Out();
284
285 __ Clz(RegisterFrom(out, type), RegisterFrom(in, type));
286}
287
288void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
289 CreateIntToIntLocations(arena_, invoke);
290}
291
292void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
293 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
294}
295
296void IntrinsicLocationsBuilderARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
297 CreateIntToIntLocations(arena_, invoke);
298}
299
300void IntrinsicCodeGeneratorARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
301 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
302}
303
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100304static void GenNumberOfTrailingZeros(LocationSummary* locations,
305 Primitive::Type type,
306 vixl::MacroAssembler* masm) {
307 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
308
309 Location in = locations->InAt(0);
310 Location out = locations->Out();
311
312 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
313 __ Clz(RegisterFrom(out, type), RegisterFrom(out, type));
314}
315
316void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
317 CreateIntToIntLocations(arena_, invoke);
318}
319
320void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
321 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
322}
323
324void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
325 CreateIntToIntLocations(arena_, invoke);
326}
327
328void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
329 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
330}
331
Andreas Gampe878d58c2015-01-15 23:24:00 -0800332static void GenReverse(LocationSummary* locations,
333 Primitive::Type type,
334 vixl::MacroAssembler* masm) {
335 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
336
337 Location in = locations->InAt(0);
338 Location out = locations->Out();
339
340 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
341}
342
343void IntrinsicLocationsBuilderARM64::VisitIntegerReverse(HInvoke* invoke) {
344 CreateIntToIntLocations(arena_, invoke);
345}
346
347void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) {
348 GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
349}
350
351void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) {
352 CreateIntToIntLocations(arena_, invoke);
353}
354
355void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
356 GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
357}
358
Roland Levillainfa3912e2016-04-01 18:21:55 +0100359static void GenBitCount(HInvoke* instr, Primitive::Type type, vixl::MacroAssembler* masm) {
360 DCHECK(Primitive::IsIntOrLongType(type)) << type;
361 DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
362 DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
xueliang.zhong49924c92016-03-03 10:52:51 +0000363
xueliang.zhong49924c92016-03-03 10:52:51 +0000364 UseScratchRegisterScope temps(masm);
365
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000366 Register src = InputRegisterAt(instr, 0);
Roland Levillainfa3912e2016-04-01 18:21:55 +0100367 Register dst = RegisterFrom(instr->GetLocations()->Out(), type);
368 FPRegister fpr = (type == Primitive::kPrimLong) ? temps.AcquireD() : temps.AcquireS();
xueliang.zhong49924c92016-03-03 10:52:51 +0000369
370 __ Fmov(fpr, src);
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000371 __ Cnt(fpr.V8B(), fpr.V8B());
372 __ Addv(fpr.B(), fpr.V8B());
xueliang.zhong49924c92016-03-03 10:52:51 +0000373 __ Fmov(dst, fpr);
374}
375
376void IntrinsicLocationsBuilderARM64::VisitLongBitCount(HInvoke* invoke) {
377 CreateIntToIntLocations(arena_, invoke);
378}
379
380void IntrinsicCodeGeneratorARM64::VisitLongBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100381 GenBitCount(invoke, Primitive::kPrimLong, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000382}
383
384void IntrinsicLocationsBuilderARM64::VisitIntegerBitCount(HInvoke* invoke) {
385 CreateIntToIntLocations(arena_, invoke);
386}
387
388void IntrinsicCodeGeneratorARM64::VisitIntegerBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100389 GenBitCount(invoke, Primitive::kPrimInt, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000390}
391
Andreas Gampe878d58c2015-01-15 23:24:00 -0800392static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800393 LocationSummary* locations = new (arena) LocationSummary(invoke,
394 LocationSummary::kNoCall,
395 kIntrinsified);
396 locations->SetInAt(0, Location::RequiresFpuRegister());
397 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
398}
399
400static void MathAbsFP(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
401 Location in = locations->InAt(0);
402 Location out = locations->Out();
403
404 FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in);
405 FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out);
406
407 __ Fabs(out_reg, in_reg);
408}
409
410void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) {
411 CreateFPToFPLocations(arena_, invoke);
412}
413
414void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000415 MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800416}
417
418void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) {
419 CreateFPToFPLocations(arena_, invoke);
420}
421
422void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000423 MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800424}
425
426static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
427 LocationSummary* locations = new (arena) LocationSummary(invoke,
428 LocationSummary::kNoCall,
429 kIntrinsified);
430 locations->SetInAt(0, Location::RequiresRegister());
431 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
432}
433
434static void GenAbsInteger(LocationSummary* locations,
435 bool is64bit,
436 vixl::MacroAssembler* masm) {
437 Location in = locations->InAt(0);
438 Location output = locations->Out();
439
440 Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in);
441 Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output);
442
443 __ Cmp(in_reg, Operand(0));
444 __ Cneg(out_reg, in_reg, lt);
445}
446
447void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
448 CreateIntToInt(arena_, invoke);
449}
450
451void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000452 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800453}
454
455void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
456 CreateIntToInt(arena_, invoke);
457}
458
459void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000460 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800461}
462
463static void GenMinMaxFP(LocationSummary* locations,
464 bool is_min,
465 bool is_double,
466 vixl::MacroAssembler* masm) {
467 Location op1 = locations->InAt(0);
468 Location op2 = locations->InAt(1);
469 Location out = locations->Out();
470
471 FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1);
472 FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2);
473 FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out);
474 if (is_min) {
475 __ Fmin(out_reg, op1_reg, op2_reg);
476 } else {
477 __ Fmax(out_reg, op1_reg, op2_reg);
478 }
479}
480
481static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
482 LocationSummary* locations = new (arena) LocationSummary(invoke,
483 LocationSummary::kNoCall,
484 kIntrinsified);
485 locations->SetInAt(0, Location::RequiresFpuRegister());
486 locations->SetInAt(1, Location::RequiresFpuRegister());
487 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
488}
489
490void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
491 CreateFPFPToFPLocations(arena_, invoke);
492}
493
494void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000495 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800496}
497
498void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
499 CreateFPFPToFPLocations(arena_, invoke);
500}
501
502void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000503 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800504}
505
506void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
507 CreateFPFPToFPLocations(arena_, invoke);
508}
509
510void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000511 GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800512}
513
514void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
515 CreateFPFPToFPLocations(arena_, invoke);
516}
517
518void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000519 GenMinMaxFP(
520 invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800521}
522
523static void GenMinMax(LocationSummary* locations,
524 bool is_min,
525 bool is_long,
526 vixl::MacroAssembler* masm) {
527 Location op1 = locations->InAt(0);
528 Location op2 = locations->InAt(1);
529 Location out = locations->Out();
530
531 Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
532 Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
533 Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
534
535 __ Cmp(op1_reg, op2_reg);
536 __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
537}
538
Andreas Gampe878d58c2015-01-15 23:24:00 -0800539void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
540 CreateIntIntToIntLocations(arena_, invoke);
541}
542
543void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000544 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800545}
546
547void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) {
548 CreateIntIntToIntLocations(arena_, invoke);
549}
550
551void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000552 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800553}
554
555void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) {
556 CreateIntIntToIntLocations(arena_, invoke);
557}
558
559void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000560 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800561}
562
563void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) {
564 CreateIntIntToIntLocations(arena_, invoke);
565}
566
567void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000568 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800569}
570
571void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) {
572 CreateFPToFPLocations(arena_, invoke);
573}
574
575void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) {
576 LocationSummary* locations = invoke->GetLocations();
577 vixl::MacroAssembler* masm = GetVIXLAssembler();
578 __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
579}
580
581void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) {
582 CreateFPToFPLocations(arena_, invoke);
583}
584
585void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) {
586 LocationSummary* locations = invoke->GetLocations();
587 vixl::MacroAssembler* masm = GetVIXLAssembler();
588 __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
589}
590
591void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) {
592 CreateFPToFPLocations(arena_, invoke);
593}
594
595void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) {
596 LocationSummary* locations = invoke->GetLocations();
597 vixl::MacroAssembler* masm = GetVIXLAssembler();
598 __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
599}
600
601void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) {
602 CreateFPToFPLocations(arena_, invoke);
603}
604
605void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) {
606 LocationSummary* locations = invoke->GetLocations();
607 vixl::MacroAssembler* masm = GetVIXLAssembler();
608 __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
609}
610
611static void CreateFPToIntPlusTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
612 LocationSummary* locations = new (arena) LocationSummary(invoke,
613 LocationSummary::kNoCall,
614 kIntrinsified);
615 locations->SetInAt(0, Location::RequiresFpuRegister());
616 locations->SetOut(Location::RequiresRegister());
617}
618
619static void GenMathRound(LocationSummary* locations,
620 bool is_double,
621 vixl::MacroAssembler* masm) {
622 FPRegister in_reg = is_double ?
623 DRegisterFrom(locations->InAt(0)) : SRegisterFrom(locations->InAt(0));
624 Register out_reg = is_double ?
625 XRegisterFrom(locations->Out()) : WRegisterFrom(locations->Out());
626 UseScratchRegisterScope temps(masm);
627 FPRegister temp1_reg = temps.AcquireSameSizeAs(in_reg);
628
629 // 0.5 can be encoded as an immediate, so use fmov.
630 if (is_double) {
631 __ Fmov(temp1_reg, static_cast<double>(0.5));
632 } else {
633 __ Fmov(temp1_reg, static_cast<float>(0.5));
634 }
635 __ Fadd(temp1_reg, in_reg, temp1_reg);
636 __ Fcvtms(out_reg, temp1_reg);
637}
638
639void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) {
Andreas Gampee6d0d8d2015-12-28 09:54:29 -0800640 // See intrinsics.h.
641 if (kRoundIsPlusPointFive) {
642 CreateFPToIntPlusTempLocations(arena_, invoke);
643 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800644}
645
646void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000647 GenMathRound(invoke->GetLocations(), /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800648}
649
650void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) {
Andreas Gampee6d0d8d2015-12-28 09:54:29 -0800651 // See intrinsics.h.
652 if (kRoundIsPlusPointFive) {
653 CreateFPToIntPlusTempLocations(arena_, invoke);
654 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800655}
656
657void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000658 GenMathRound(invoke->GetLocations(), /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800659}
660
661void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) {
662 CreateIntToIntLocations(arena_, invoke);
663}
664
665void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) {
666 vixl::MacroAssembler* masm = GetVIXLAssembler();
667 __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()),
668 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
669}
670
671void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
672 CreateIntToIntLocations(arena_, invoke);
673}
674
675void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
676 vixl::MacroAssembler* masm = GetVIXLAssembler();
677 __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()),
678 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
679}
680
681void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
682 CreateIntToIntLocations(arena_, invoke);
683}
684
685void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
686 vixl::MacroAssembler* masm = GetVIXLAssembler();
687 __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()),
688 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
689}
690
691void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
692 CreateIntToIntLocations(arena_, invoke);
693}
694
695void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
696 vixl::MacroAssembler* masm = GetVIXLAssembler();
697 __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()),
698 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
699}
700
701static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
702 LocationSummary* locations = new (arena) LocationSummary(invoke,
703 LocationSummary::kNoCall,
704 kIntrinsified);
705 locations->SetInAt(0, Location::RequiresRegister());
706 locations->SetInAt(1, Location::RequiresRegister());
707}
708
709void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) {
710 CreateIntIntToVoidLocations(arena_, invoke);
711}
712
713void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) {
714 vixl::MacroAssembler* masm = GetVIXLAssembler();
715 __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)),
716 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
717}
718
719void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
720 CreateIntIntToVoidLocations(arena_, invoke);
721}
722
723void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
724 vixl::MacroAssembler* masm = GetVIXLAssembler();
725 __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)),
726 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
727}
728
729void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
730 CreateIntIntToVoidLocations(arena_, invoke);
731}
732
733void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
734 vixl::MacroAssembler* masm = GetVIXLAssembler();
735 __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)),
736 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
737}
738
739void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
740 CreateIntIntToVoidLocations(arena_, invoke);
741}
742
743void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
744 vixl::MacroAssembler* masm = GetVIXLAssembler();
745 __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)),
746 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
747}
748
749void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) {
750 LocationSummary* locations = new (arena_) LocationSummary(invoke,
751 LocationSummary::kNoCall,
752 kIntrinsified);
753 locations->SetOut(Location::RequiresRegister());
754}
755
756void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) {
757 codegen_->Load(Primitive::kPrimNot, WRegisterFrom(invoke->GetLocations()->Out()),
758 MemOperand(tr, Thread::PeerOffset<8>().Int32Value()));
759}
760
761static void GenUnsafeGet(HInvoke* invoke,
762 Primitive::Type type,
763 bool is_volatile,
764 CodeGeneratorARM64* codegen) {
765 LocationSummary* locations = invoke->GetLocations();
766 DCHECK((type == Primitive::kPrimInt) ||
767 (type == Primitive::kPrimLong) ||
768 (type == Primitive::kPrimNot));
769 vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000770 Location base_loc = locations->InAt(1);
771 Register base = WRegisterFrom(base_loc); // Object pointer.
772 Location offset_loc = locations->InAt(2);
773 Register offset = XRegisterFrom(offset_loc); // Long offset.
774 Location trg_loc = locations->Out();
775 Register trg = RegisterFrom(trg_loc, type);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800776
Roland Levillain44015862016-01-22 11:47:17 +0000777 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
778 // UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case.
779 UseScratchRegisterScope temps(masm);
780 Register temp = temps.AcquireW();
Roland Levillainbfea3352016-06-23 13:48:47 +0100781 codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke,
782 trg_loc,
783 base,
784 /* offset */ 0U,
785 /* index */ offset_loc,
786 /* scale_factor */ 0U,
787 temp,
788 /* needs_null_check */ false,
789 is_volatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800790 } else {
Roland Levillain44015862016-01-22 11:47:17 +0000791 // Other cases.
792 MemOperand mem_op(base.X(), offset);
793 if (is_volatile) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +0000794 codegen->LoadAcquire(invoke, trg, mem_op, /* needs_null_check */ true);
Roland Levillain44015862016-01-22 11:47:17 +0000795 } else {
796 codegen->Load(type, trg, mem_op);
797 }
Roland Levillain4d027112015-07-01 15:41:14 +0100798
Roland Levillain44015862016-01-22 11:47:17 +0000799 if (type == Primitive::kPrimNot) {
800 DCHECK(trg.IsW());
801 codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
802 }
Roland Levillain4d027112015-07-01 15:41:14 +0100803 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800804}
805
806static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000807 bool can_call = kEmitCompilerReadBarrier &&
808 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
809 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800810 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000811 can_call ?
812 LocationSummary::kCallOnSlowPath :
813 LocationSummary::kNoCall,
Andreas Gampe878d58c2015-01-15 23:24:00 -0800814 kIntrinsified);
815 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
816 locations->SetInAt(1, Location::RequiresRegister());
817 locations->SetInAt(2, Location::RequiresRegister());
Roland Levillainbfea3352016-06-23 13:48:47 +0100818 locations->SetOut(Location::RequiresRegister(),
819 can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800820}
821
822void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
823 CreateIntIntIntToIntLocations(arena_, invoke);
824}
825void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
826 CreateIntIntIntToIntLocations(arena_, invoke);
827}
828void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
829 CreateIntIntIntToIntLocations(arena_, invoke);
830}
831void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
832 CreateIntIntIntToIntLocations(arena_, invoke);
833}
834void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
835 CreateIntIntIntToIntLocations(arena_, invoke);
836}
837void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
838 CreateIntIntIntToIntLocations(arena_, invoke);
839}
840
841void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000842 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800843}
844void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000845 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800846}
847void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000848 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800849}
850void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000851 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800852}
853void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000854 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800855}
856void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000857 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800858}
859
860static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
861 LocationSummary* locations = new (arena) LocationSummary(invoke,
862 LocationSummary::kNoCall,
863 kIntrinsified);
864 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
865 locations->SetInAt(1, Location::RequiresRegister());
866 locations->SetInAt(2, Location::RequiresRegister());
867 locations->SetInAt(3, Location::RequiresRegister());
868}
869
870void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
871 CreateIntIntIntIntToVoid(arena_, invoke);
872}
873void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
874 CreateIntIntIntIntToVoid(arena_, invoke);
875}
876void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
877 CreateIntIntIntIntToVoid(arena_, invoke);
878}
879void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
880 CreateIntIntIntIntToVoid(arena_, invoke);
881}
882void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
883 CreateIntIntIntIntToVoid(arena_, invoke);
884}
885void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
886 CreateIntIntIntIntToVoid(arena_, invoke);
887}
888void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
889 CreateIntIntIntIntToVoid(arena_, invoke);
890}
891void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
892 CreateIntIntIntIntToVoid(arena_, invoke);
893}
894void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
895 CreateIntIntIntIntToVoid(arena_, invoke);
896}
897
898static void GenUnsafePut(LocationSummary* locations,
899 Primitive::Type type,
900 bool is_volatile,
901 bool is_ordered,
902 CodeGeneratorARM64* codegen) {
903 vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
904
905 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
906 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
907 Register value = RegisterFrom(locations->InAt(3), type);
Roland Levillain4d027112015-07-01 15:41:14 +0100908 Register source = value;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800909 MemOperand mem_op(base.X(), offset);
910
Roland Levillain4d027112015-07-01 15:41:14 +0100911 {
912 // We use a block to end the scratch scope before the write barrier, thus
913 // freeing the temporary registers so they can be used in `MarkGCCard`.
914 UseScratchRegisterScope temps(masm);
915
916 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
917 DCHECK(value.IsW());
918 Register temp = temps.AcquireW();
919 __ Mov(temp.W(), value.W());
920 codegen->GetAssembler()->PoisonHeapReference(temp.W());
921 source = temp;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800922 }
Roland Levillain4d027112015-07-01 15:41:14 +0100923
924 if (is_volatile || is_ordered) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +0000925 codegen->StoreRelease(type, source, mem_op);
Roland Levillain4d027112015-07-01 15:41:14 +0100926 } else {
927 codegen->Store(type, source, mem_op);
928 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800929 }
930
931 if (type == Primitive::kPrimNot) {
Nicolas Geoffray07276db2015-05-18 14:22:09 +0100932 bool value_can_be_null = true; // TODO: Worth finding out this information?
933 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800934 }
935}
936
937void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000938 GenUnsafePut(invoke->GetLocations(),
939 Primitive::kPrimInt,
940 /* is_volatile */ false,
941 /* is_ordered */ false,
942 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800943}
944void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000945 GenUnsafePut(invoke->GetLocations(),
946 Primitive::kPrimInt,
947 /* is_volatile */ false,
948 /* is_ordered */ true,
949 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800950}
951void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000952 GenUnsafePut(invoke->GetLocations(),
953 Primitive::kPrimInt,
954 /* is_volatile */ true,
955 /* is_ordered */ false,
956 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800957}
958void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000959 GenUnsafePut(invoke->GetLocations(),
960 Primitive::kPrimNot,
961 /* is_volatile */ false,
962 /* is_ordered */ false,
963 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800964}
965void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000966 GenUnsafePut(invoke->GetLocations(),
967 Primitive::kPrimNot,
968 /* is_volatile */ false,
969 /* is_ordered */ true,
970 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800971}
972void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000973 GenUnsafePut(invoke->GetLocations(),
974 Primitive::kPrimNot,
975 /* is_volatile */ true,
976 /* is_ordered */ false,
977 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800978}
979void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000980 GenUnsafePut(invoke->GetLocations(),
981 Primitive::kPrimLong,
982 /* is_volatile */ false,
983 /* is_ordered */ false,
984 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800985}
986void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000987 GenUnsafePut(invoke->GetLocations(),
988 Primitive::kPrimLong,
989 /* is_volatile */ false,
990 /* is_ordered */ true,
991 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800992}
993void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000994 GenUnsafePut(invoke->GetLocations(),
995 Primitive::kPrimLong,
996 /* is_volatile */ true,
997 /* is_ordered */ false,
998 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800999}
1000
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001001static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
1002 HInvoke* invoke,
1003 Primitive::Type type) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08001004 LocationSummary* locations = new (arena) LocationSummary(invoke,
1005 LocationSummary::kNoCall,
1006 kIntrinsified);
1007 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1008 locations->SetInAt(1, Location::RequiresRegister());
1009 locations->SetInAt(2, Location::RequiresRegister());
1010 locations->SetInAt(3, Location::RequiresRegister());
1011 locations->SetInAt(4, Location::RequiresRegister());
1012
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001013 // If heap poisoning is enabled, we don't want the unpoisoning
1014 // operations to potentially clobber the output.
1015 Location::OutputOverlap overlaps = (kPoisonHeapReferences && type == Primitive::kPrimNot)
1016 ? Location::kOutputOverlap
1017 : Location::kNoOutputOverlap;
1018 locations->SetOut(Location::RequiresRegister(), overlaps);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001019}
1020
1021static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM64* codegen) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08001022 vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
1023
1024 Register out = WRegisterFrom(locations->Out()); // Boolean result.
1025
1026 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
1027 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
1028 Register expected = RegisterFrom(locations->InAt(3), type); // Expected.
1029 Register value = RegisterFrom(locations->InAt(4), type); // Value.
1030
1031 // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
1032 if (type == Primitive::kPrimNot) {
1033 // Mark card for object assuming new value is stored.
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001034 bool value_can_be_null = true; // TODO: Worth finding out this information?
1035 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001036 }
1037
1038 UseScratchRegisterScope temps(masm);
1039 Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
1040 Register tmp_value = temps.AcquireSameSizeAs(value); // Value in memory.
1041
1042 Register tmp_32 = tmp_value.W();
1043
1044 __ Add(tmp_ptr, base.X(), Operand(offset));
1045
Roland Levillain4d027112015-07-01 15:41:14 +01001046 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1047 codegen->GetAssembler()->PoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001048 if (value.Is(expected)) {
1049 // Do not poison `value`, as it is the same register as
1050 // `expected`, which has just been poisoned.
1051 } else {
1052 codegen->GetAssembler()->PoisonHeapReference(value);
1053 }
Roland Levillain4d027112015-07-01 15:41:14 +01001054 }
1055
Andreas Gampe878d58c2015-01-15 23:24:00 -08001056 // do {
1057 // tmp_value = [tmp_ptr] - expected;
1058 // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1059 // result = tmp_value != 0;
1060
1061 vixl::Label loop_head, exit_loop;
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001062 __ Bind(&loop_head);
1063 // TODO: When `type == Primitive::kPrimNot`, add a read barrier for
1064 // the reference stored in the object before attempting the CAS,
1065 // similar to the one in the art::Unsafe_compareAndSwapObject JNI
1066 // implementation.
1067 //
1068 // Note that this code is not (yet) used when read barriers are
1069 // enabled (see IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject).
1070 DCHECK(!(type == Primitive::kPrimNot && kEmitCompilerReadBarrier));
1071 __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
1072 __ Cmp(tmp_value, expected);
1073 __ B(&exit_loop, ne);
1074 __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
1075 __ Cbnz(tmp_32, &loop_head);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001076 __ Bind(&exit_loop);
1077 __ Cset(out, eq);
Roland Levillain4d027112015-07-01 15:41:14 +01001078
1079 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillain4d027112015-07-01 15:41:14 +01001080 codegen->GetAssembler()->UnpoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001081 if (value.Is(expected)) {
1082 // Do not unpoison `value`, as it is the same register as
1083 // `expected`, which has just been unpoisoned.
1084 } else {
1085 codegen->GetAssembler()->UnpoisonHeapReference(value);
1086 }
Roland Levillain4d027112015-07-01 15:41:14 +01001087 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001088}
1089
1090void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001091 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001092}
1093void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001094 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001095}
1096void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillain391b8662015-12-18 11:43:38 +00001097 // The UnsafeCASObject intrinsic is missing a read barrier, and
1098 // therefore sometimes does not work as expected (b/25883050).
1099 // Turn it off temporarily as a quick fix, until the read barrier is
Roland Levillain3d312422016-06-23 13:53:42 +01001100 // implemented (see TODO in GenCAS).
Roland Levillain391b8662015-12-18 11:43:38 +00001101 //
Roland Levillain3d312422016-06-23 13:53:42 +01001102 // TODO(rpl): Implement read barrier support in GenCAS and re-enable
1103 // this intrinsic.
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001104 if (kEmitCompilerReadBarrier) {
Roland Levillain985ff702015-10-23 13:25:35 +01001105 return;
1106 }
1107
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001108 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001109}
1110
1111void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
1112 GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
1113}
1114void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
1115 GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_);
1116}
1117void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillain3d312422016-06-23 13:53:42 +01001118 // The UnsafeCASObject intrinsic is missing a read barrier, and
1119 // therefore sometimes does not work as expected (b/25883050).
1120 // Turn it off temporarily as a quick fix, until the read barrier is
1121 // implemented (see TODO in GenCAS).
1122 //
1123 // TODO(rpl): Implement read barrier support in GenCAS and re-enable
1124 // this intrinsic.
1125 DCHECK(!kEmitCompilerReadBarrier);
1126
Andreas Gampe878d58c2015-01-15 23:24:00 -08001127 GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
1128}
1129
1130void IntrinsicLocationsBuilderARM64::VisitStringCharAt(HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08001131 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1132 LocationSummary::kCallOnSlowPath,
1133 kIntrinsified);
1134 locations->SetInAt(0, Location::RequiresRegister());
1135 locations->SetInAt(1, Location::RequiresRegister());
Nicolas Geoffray82f34492015-02-04 10:44:23 +00001136 // In case we need to go in the slow path, we can't have the output be the same
1137 // as the input: the current liveness analysis considers the input to be live
1138 // at the point of the call.
1139 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001140}
1141
1142void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) {
1143 vixl::MacroAssembler* masm = GetVIXLAssembler();
1144 LocationSummary* locations = invoke->GetLocations();
1145
1146 // Location of reference to data array
1147 const MemberOffset value_offset = mirror::String::ValueOffset();
1148 // Location of count
1149 const MemberOffset count_offset = mirror::String::CountOffset();
Andreas Gampe878d58c2015-01-15 23:24:00 -08001150
1151 Register obj = WRegisterFrom(locations->InAt(0)); // String object pointer.
1152 Register idx = WRegisterFrom(locations->InAt(1)); // Index of character.
1153 Register out = WRegisterFrom(locations->Out()); // Result character.
1154
1155 UseScratchRegisterScope temps(masm);
1156 Register temp = temps.AcquireW();
1157 Register array_temp = temps.AcquireW(); // We can trade this for worse scheduling.
1158
1159 // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
1160 // the cost.
1161 // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
1162 // we will not optimize the code for constants (which would save a register).
1163
1164 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1165 codegen_->AddSlowPath(slow_path);
1166
1167 __ Ldr(temp, HeapOperand(obj, count_offset)); // temp = str.length.
1168 codegen_->MaybeRecordImplicitNullCheck(invoke);
1169 __ Cmp(idx, temp);
1170 __ B(hs, slow_path->GetEntryLabel());
1171
Jeff Hao848f70a2014-01-15 13:49:50 -08001172 __ Add(array_temp, obj, Operand(value_offset.Int32Value())); // array_temp := str.value.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001173
1174 // Load the value.
Jeff Hao848f70a2014-01-15 13:49:50 -08001175 __ Ldrh(out, MemOperand(array_temp.X(), idx, UXTW, 1)); // out := array_temp[idx].
Andreas Gampe878d58c2015-01-15 23:24:00 -08001176
1177 __ Bind(slow_path->GetExitLabel());
1178}
1179
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001180void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001181 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Scott Wakeling1f36f412016-04-21 11:13:45 +01001182 invoke->InputAt(1)->CanBeNull()
1183 ? LocationSummary::kCallOnSlowPath
1184 : LocationSummary::kNoCall,
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001185 kIntrinsified);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001186 locations->SetInAt(0, Location::RequiresRegister());
1187 locations->SetInAt(1, Location::RequiresRegister());
1188 locations->AddTemp(Location::RequiresRegister());
1189 locations->AddTemp(Location::RequiresRegister());
1190 locations->AddTemp(Location::RequiresRegister());
1191 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001192}
1193
1194void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
1195 vixl::MacroAssembler* masm = GetVIXLAssembler();
1196 LocationSummary* locations = invoke->GetLocations();
1197
Scott Wakeling1f36f412016-04-21 11:13:45 +01001198 Register str = XRegisterFrom(locations->InAt(0));
1199 Register arg = XRegisterFrom(locations->InAt(1));
1200 Register out = OutputRegister(invoke);
1201
1202 Register temp0 = WRegisterFrom(locations->GetTemp(0));
1203 Register temp1 = WRegisterFrom(locations->GetTemp(1));
1204 Register temp2 = WRegisterFrom(locations->GetTemp(2));
1205
1206 vixl::Label loop;
1207 vixl::Label find_char_diff;
1208 vixl::Label end;
1209
1210 // Get offsets of count and value fields within a string object.
1211 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1212 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1213
Nicolas Geoffray512e04d2015-03-27 17:21:24 +00001214 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +01001215 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001216
Scott Wakeling1f36f412016-04-21 11:13:45 +01001217 // Take slow path and throw if input can be and is null.
1218 SlowPathCodeARM64* slow_path = nullptr;
1219 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1220 if (can_slow_path) {
1221 slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1222 codegen_->AddSlowPath(slow_path);
1223 __ Cbz(arg, slow_path->GetEntryLabel());
1224 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001225
Scott Wakeling1f36f412016-04-21 11:13:45 +01001226 // Reference equality check, return 0 if same reference.
1227 __ Subs(out, str, arg);
1228 __ B(&end, eq);
1229 // Load lengths of this and argument strings.
1230 __ Ldr(temp0, MemOperand(str.X(), count_offset));
1231 __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1232 // Return zero if both strings are empty.
1233 __ Orr(out, temp0, temp1);
1234 __ Cbz(out, &end);
1235 // out = length diff.
1236 __ Subs(out, temp0, temp1);
1237 // temp2 = min(len(str), len(arg)).
1238 __ Csel(temp2, temp1, temp0, ge);
1239 // Shorter string is empty?
1240 __ Cbz(temp2, &end);
1241
1242 // Store offset of string value in preparation for comparison loop.
1243 __ Mov(temp1, value_offset);
1244
1245 UseScratchRegisterScope scratch_scope(masm);
1246 Register temp4 = scratch_scope.AcquireX();
1247
1248 // Assertions that must hold in order to compare strings 4 characters at a time.
1249 DCHECK_ALIGNED(value_offset, 8);
1250 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1251
1252 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1253 DCHECK_EQ(char_size, 2u);
1254
1255 // Promote temp0 to an X reg, ready for LDR.
1256 temp0 = temp0.X();
1257
1258 // Loop to compare 4x16-bit characters at a time (ok because of string data alignment).
1259 __ Bind(&loop);
1260 __ Ldr(temp4, MemOperand(str.X(), temp1));
1261 __ Ldr(temp0, MemOperand(arg.X(), temp1));
1262 __ Cmp(temp4, temp0);
1263 __ B(ne, &find_char_diff);
1264 __ Add(temp1, temp1, char_size * 4);
1265 __ Subs(temp2, temp2, 4);
1266 __ B(gt, &loop);
1267 __ B(&end);
1268
1269 // Promote temp1 to an X reg, ready for EOR.
1270 temp1 = temp1.X();
1271
1272 // Find the single 16-bit character difference.
1273 __ Bind(&find_char_diff);
1274 // Get the bit position of the first character that differs.
1275 __ Eor(temp1, temp0, temp4);
1276 __ Rbit(temp1, temp1);
1277 __ Clz(temp1, temp1);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001278 // If the number of 16-bit chars remaining <= the index where the difference occurs (0-3), then
1279 // the difference occurs outside the remaining string data, so just return length diff (out).
1280 __ Cmp(temp2, Operand(temp1, LSR, 4));
1281 __ B(le, &end);
1282 // Extract the characters and calculate the difference.
Scott Wakelinge5ed20b2016-05-20 10:41:38 +01001283 __ Bic(temp1, temp1, 0xf);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001284 __ Lsr(temp0, temp0, temp1);
1285 __ Lsr(temp4, temp4, temp1);
1286 __ And(temp4, temp4, 0xffff);
1287 __ Sub(out, temp4, Operand(temp0, UXTH));
1288
1289 __ Bind(&end);
1290
1291 if (can_slow_path) {
1292 __ Bind(slow_path->GetExitLabel());
1293 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001294}
1295
Agi Csakiea34b402015-08-13 17:51:19 -07001296void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
1297 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1298 LocationSummary::kNoCall,
1299 kIntrinsified);
1300 locations->SetInAt(0, Location::RequiresRegister());
1301 locations->SetInAt(1, Location::RequiresRegister());
1302 // Temporary registers to store lengths of strings and for calculations.
1303 locations->AddTemp(Location::RequiresRegister());
1304 locations->AddTemp(Location::RequiresRegister());
1305
1306 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1307}
1308
1309void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
1310 vixl::MacroAssembler* masm = GetVIXLAssembler();
1311 LocationSummary* locations = invoke->GetLocations();
1312
1313 Register str = WRegisterFrom(locations->InAt(0));
1314 Register arg = WRegisterFrom(locations->InAt(1));
1315 Register out = XRegisterFrom(locations->Out());
1316
1317 UseScratchRegisterScope scratch_scope(masm);
1318 Register temp = scratch_scope.AcquireW();
1319 Register temp1 = WRegisterFrom(locations->GetTemp(0));
1320 Register temp2 = WRegisterFrom(locations->GetTemp(1));
1321
1322 vixl::Label loop;
1323 vixl::Label end;
1324 vixl::Label return_true;
1325 vixl::Label return_false;
1326
1327 // Get offsets of count, value, and class fields within a string object.
1328 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1329 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1330 const int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1331
1332 // Note that the null check must have been done earlier.
1333 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1334
Vladimir Marko53b52002016-05-24 19:30:45 +01001335 StringEqualsOptimizations optimizations(invoke);
1336 if (!optimizations.GetArgumentNotNull()) {
1337 // Check if input is null, return false if it is.
1338 __ Cbz(arg, &return_false);
1339 }
Agi Csakiea34b402015-08-13 17:51:19 -07001340
1341 // Reference equality check, return true if same reference.
1342 __ Cmp(str, arg);
1343 __ B(&return_true, eq);
1344
Vladimir Marko53b52002016-05-24 19:30:45 +01001345 if (!optimizations.GetArgumentIsString()) {
1346 // Instanceof check for the argument by comparing class fields.
1347 // All string objects must have the same type since String cannot be subclassed.
1348 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1349 // If the argument is a string object, its class field must be equal to receiver's class field.
1350 __ Ldr(temp, MemOperand(str.X(), class_offset));
1351 __ Ldr(temp1, MemOperand(arg.X(), class_offset));
1352 __ Cmp(temp, temp1);
1353 __ B(&return_false, ne);
1354 }
Agi Csakiea34b402015-08-13 17:51:19 -07001355
1356 // Load lengths of this and argument strings.
1357 __ Ldr(temp, MemOperand(str.X(), count_offset));
1358 __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1359 // Check if lengths are equal, return false if they're not.
1360 __ Cmp(temp, temp1);
1361 __ B(&return_false, ne);
1362 // Store offset of string value in preparation for comparison loop
1363 __ Mov(temp1, value_offset);
1364 // Return true if both strings are empty.
1365 __ Cbz(temp, &return_true);
1366
1367 // Assertions that must hold in order to compare strings 4 characters at a time.
1368 DCHECK_ALIGNED(value_offset, 8);
1369 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1370
1371 temp1 = temp1.X();
1372 temp2 = temp2.X();
1373
1374 // Loop to compare strings 4 characters at a time starting at the beginning of the string.
1375 // Ok to do this because strings are zero-padded to be 8-byte aligned.
1376 __ Bind(&loop);
1377 __ Ldr(out, MemOperand(str.X(), temp1));
1378 __ Ldr(temp2, MemOperand(arg.X(), temp1));
1379 __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
1380 __ Cmp(out, temp2);
1381 __ B(&return_false, ne);
1382 __ Sub(temp, temp, Operand(4), SetFlags);
1383 __ B(&loop, gt);
1384
1385 // Return true and exit the function.
1386 // If loop does not result in returning false, we return true.
1387 __ Bind(&return_true);
1388 __ Mov(out, 1);
1389 __ B(&end);
1390
1391 // Return false and exit the function.
1392 __ Bind(&return_false);
1393 __ Mov(out, 0);
1394 __ Bind(&end);
1395}
1396
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001397static void GenerateVisitStringIndexOf(HInvoke* invoke,
1398 vixl::MacroAssembler* masm,
1399 CodeGeneratorARM64* codegen,
1400 ArenaAllocator* allocator,
1401 bool start_at_zero) {
1402 LocationSummary* locations = invoke->GetLocations();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001403
1404 // Note that the null check must have been done earlier.
1405 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1406
1407 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001408 // 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 -07001409 SlowPathCodeARM64* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001410 HInstruction* code_point = invoke->InputAt(1);
1411 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01001412 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > 0xFFFFU) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001413 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1414 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1415 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1416 codegen->AddSlowPath(slow_path);
1417 __ B(slow_path->GetEntryLabel());
1418 __ Bind(slow_path->GetExitLabel());
1419 return;
1420 }
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001421 } else if (code_point->GetType() != Primitive::kPrimChar) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001422 Register char_reg = WRegisterFrom(locations->InAt(1));
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001423 __ Tst(char_reg, 0xFFFF0000);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001424 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1425 codegen->AddSlowPath(slow_path);
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001426 __ B(ne, slow_path->GetEntryLabel());
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001427 }
1428
1429 if (start_at_zero) {
1430 // Start-index = 0.
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001431 Register tmp_reg = WRegisterFrom(locations->GetTemp(0));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001432 __ Mov(tmp_reg, 0);
1433 }
1434
1435 __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pIndexOf).Int32Value()));
Roland Levillain42ad2882016-02-29 18:26:54 +00001436 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001437 __ Blr(lr);
1438
1439 if (slow_path != nullptr) {
1440 __ Bind(slow_path->GetExitLabel());
1441 }
1442}
1443
1444void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) {
1445 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1446 LocationSummary::kCall,
1447 kIntrinsified);
1448 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1449 // best to align the inputs accordingly.
1450 InvokeRuntimeCallingConvention calling_convention;
1451 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1452 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1453 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1454
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001455 // Need to send start_index=0.
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001456 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1457}
1458
1459void IntrinsicCodeGeneratorARM64::VisitStringIndexOf(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001460 GenerateVisitStringIndexOf(
1461 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001462}
1463
1464void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
1465 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1466 LocationSummary::kCall,
1467 kIntrinsified);
1468 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1469 // best to align the inputs accordingly.
1470 InvokeRuntimeCallingConvention calling_convention;
1471 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1472 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1473 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1474 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001475}
1476
1477void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001478 GenerateVisitStringIndexOf(
1479 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001480}
1481
Jeff Hao848f70a2014-01-15 13:49:50 -08001482void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1483 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1484 LocationSummary::kCall,
1485 kIntrinsified);
1486 InvokeRuntimeCallingConvention calling_convention;
1487 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1488 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1489 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1490 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1491 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1492}
1493
1494void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1495 vixl::MacroAssembler* masm = GetVIXLAssembler();
1496 LocationSummary* locations = invoke->GetLocations();
1497
1498 Register byte_array = WRegisterFrom(locations->InAt(0));
1499 __ Cmp(byte_array, 0);
1500 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1501 codegen_->AddSlowPath(slow_path);
1502 __ B(eq, slow_path->GetEntryLabel());
1503
1504 __ Ldr(lr,
1505 MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromBytes).Int32Value()));
Roland Levillainf969a202016-03-09 16:14:00 +00001506 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001507 __ Blr(lr);
Roland Levillainf969a202016-03-09 16:14:00 +00001508 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Jeff Hao848f70a2014-01-15 13:49:50 -08001509 __ Bind(slow_path->GetExitLabel());
1510}
1511
1512void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
1513 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1514 LocationSummary::kCall,
1515 kIntrinsified);
1516 InvokeRuntimeCallingConvention calling_convention;
1517 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1518 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1519 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1520 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1521}
1522
1523void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
1524 vixl::MacroAssembler* masm = GetVIXLAssembler();
1525
Roland Levillaincc3839c2016-02-29 16:23:48 +00001526 // No need to emit code checking whether `locations->InAt(2)` is a null
1527 // pointer, as callers of the native method
1528 //
1529 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1530 //
1531 // all include a null check on `data` before calling that method.
Jeff Hao848f70a2014-01-15 13:49:50 -08001532 __ Ldr(lr,
1533 MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromChars).Int32Value()));
Roland Levillainf969a202016-03-09 16:14:00 +00001534 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001535 __ Blr(lr);
Roland Levillainf969a202016-03-09 16:14:00 +00001536 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Jeff Hao848f70a2014-01-15 13:49:50 -08001537}
1538
1539void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Jeff Hao848f70a2014-01-15 13:49:50 -08001540 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1541 LocationSummary::kCall,
1542 kIntrinsified);
1543 InvokeRuntimeCallingConvention calling_convention;
1544 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Jeff Hao848f70a2014-01-15 13:49:50 -08001545 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1546}
1547
1548void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) {
1549 vixl::MacroAssembler* masm = GetVIXLAssembler();
1550 LocationSummary* locations = invoke->GetLocations();
1551
1552 Register string_to_copy = WRegisterFrom(locations->InAt(0));
1553 __ Cmp(string_to_copy, 0);
1554 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1555 codegen_->AddSlowPath(slow_path);
1556 __ B(eq, slow_path->GetEntryLabel());
1557
1558 __ Ldr(lr,
1559 MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromString).Int32Value()));
Roland Levillainf969a202016-03-09 16:14:00 +00001560 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001561 __ Blr(lr);
Roland Levillainf969a202016-03-09 16:14:00 +00001562 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Jeff Hao848f70a2014-01-15 13:49:50 -08001563 __ Bind(slow_path->GetExitLabel());
1564}
1565
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001566static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1567 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
1568 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1569 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1570
1571 LocationSummary* const locations = new (arena) LocationSummary(invoke,
1572 LocationSummary::kCall,
1573 kIntrinsified);
1574 InvokeRuntimeCallingConvention calling_convention;
1575
1576 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1577 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1578}
1579
1580static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1581 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
1582 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1583 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(1)->GetType()));
1584 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1585
1586 LocationSummary* const locations = new (arena) LocationSummary(invoke,
1587 LocationSummary::kCall,
1588 kIntrinsified);
1589 InvokeRuntimeCallingConvention calling_convention;
1590
1591 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1592 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
1593 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1594}
1595
1596static void GenFPToFPCall(HInvoke* invoke,
1597 vixl::MacroAssembler* masm,
1598 CodeGeneratorARM64* codegen,
1599 QuickEntrypointEnum entry) {
1600 __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArm64WordSize>(entry).Int32Value()));
1601 __ Blr(lr);
1602 codegen->RecordPcInfo(invoke, invoke->GetDexPc());
1603}
1604
1605void IntrinsicLocationsBuilderARM64::VisitMathCos(HInvoke* invoke) {
1606 CreateFPToFPCallLocations(arena_, invoke);
1607}
1608
1609void IntrinsicCodeGeneratorARM64::VisitMathCos(HInvoke* invoke) {
1610 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCos);
1611}
1612
1613void IntrinsicLocationsBuilderARM64::VisitMathSin(HInvoke* invoke) {
1614 CreateFPToFPCallLocations(arena_, invoke);
1615}
1616
1617void IntrinsicCodeGeneratorARM64::VisitMathSin(HInvoke* invoke) {
1618 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickSin);
1619}
1620
1621void IntrinsicLocationsBuilderARM64::VisitMathAcos(HInvoke* invoke) {
1622 CreateFPToFPCallLocations(arena_, invoke);
1623}
1624
1625void IntrinsicCodeGeneratorARM64::VisitMathAcos(HInvoke* invoke) {
1626 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAcos);
1627}
1628
1629void IntrinsicLocationsBuilderARM64::VisitMathAsin(HInvoke* invoke) {
1630 CreateFPToFPCallLocations(arena_, invoke);
1631}
1632
1633void IntrinsicCodeGeneratorARM64::VisitMathAsin(HInvoke* invoke) {
1634 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAsin);
1635}
1636
1637void IntrinsicLocationsBuilderARM64::VisitMathAtan(HInvoke* invoke) {
1638 CreateFPToFPCallLocations(arena_, invoke);
1639}
1640
1641void IntrinsicCodeGeneratorARM64::VisitMathAtan(HInvoke* invoke) {
1642 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAtan);
1643}
1644
1645void IntrinsicLocationsBuilderARM64::VisitMathCbrt(HInvoke* invoke) {
1646 CreateFPToFPCallLocations(arena_, invoke);
1647}
1648
1649void IntrinsicCodeGeneratorARM64::VisitMathCbrt(HInvoke* invoke) {
1650 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCbrt);
1651}
1652
1653void IntrinsicLocationsBuilderARM64::VisitMathCosh(HInvoke* invoke) {
1654 CreateFPToFPCallLocations(arena_, invoke);
1655}
1656
1657void IntrinsicCodeGeneratorARM64::VisitMathCosh(HInvoke* invoke) {
1658 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCosh);
1659}
1660
1661void IntrinsicLocationsBuilderARM64::VisitMathExp(HInvoke* invoke) {
1662 CreateFPToFPCallLocations(arena_, invoke);
1663}
1664
1665void IntrinsicCodeGeneratorARM64::VisitMathExp(HInvoke* invoke) {
1666 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickExp);
1667}
1668
1669void IntrinsicLocationsBuilderARM64::VisitMathExpm1(HInvoke* invoke) {
1670 CreateFPToFPCallLocations(arena_, invoke);
1671}
1672
1673void IntrinsicCodeGeneratorARM64::VisitMathExpm1(HInvoke* invoke) {
1674 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickExpm1);
1675}
1676
1677void IntrinsicLocationsBuilderARM64::VisitMathLog(HInvoke* invoke) {
1678 CreateFPToFPCallLocations(arena_, invoke);
1679}
1680
1681void IntrinsicCodeGeneratorARM64::VisitMathLog(HInvoke* invoke) {
1682 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickLog);
1683}
1684
1685void IntrinsicLocationsBuilderARM64::VisitMathLog10(HInvoke* invoke) {
1686 CreateFPToFPCallLocations(arena_, invoke);
1687}
1688
1689void IntrinsicCodeGeneratorARM64::VisitMathLog10(HInvoke* invoke) {
1690 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickLog10);
1691}
1692
1693void IntrinsicLocationsBuilderARM64::VisitMathSinh(HInvoke* invoke) {
1694 CreateFPToFPCallLocations(arena_, invoke);
1695}
1696
1697void IntrinsicCodeGeneratorARM64::VisitMathSinh(HInvoke* invoke) {
1698 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickSinh);
1699}
1700
1701void IntrinsicLocationsBuilderARM64::VisitMathTan(HInvoke* invoke) {
1702 CreateFPToFPCallLocations(arena_, invoke);
1703}
1704
1705void IntrinsicCodeGeneratorARM64::VisitMathTan(HInvoke* invoke) {
1706 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickTan);
1707}
1708
1709void IntrinsicLocationsBuilderARM64::VisitMathTanh(HInvoke* invoke) {
1710 CreateFPToFPCallLocations(arena_, invoke);
1711}
1712
1713void IntrinsicCodeGeneratorARM64::VisitMathTanh(HInvoke* invoke) {
1714 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickTanh);
1715}
1716
1717void IntrinsicLocationsBuilderARM64::VisitMathAtan2(HInvoke* invoke) {
1718 CreateFPFPToFPCallLocations(arena_, invoke);
1719}
1720
1721void IntrinsicCodeGeneratorARM64::VisitMathAtan2(HInvoke* invoke) {
1722 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAtan2);
1723}
1724
1725void IntrinsicLocationsBuilderARM64::VisitMathHypot(HInvoke* invoke) {
1726 CreateFPFPToFPCallLocations(arena_, invoke);
1727}
1728
1729void IntrinsicCodeGeneratorARM64::VisitMathHypot(HInvoke* invoke) {
1730 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickHypot);
1731}
1732
1733void IntrinsicLocationsBuilderARM64::VisitMathNextAfter(HInvoke* invoke) {
1734 CreateFPFPToFPCallLocations(arena_, invoke);
1735}
1736
1737void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) {
1738 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickNextAfter);
1739}
1740
Tim Zhang25abd6c2016-01-19 23:39:24 +08001741void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1742 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1743 LocationSummary::kNoCall,
1744 kIntrinsified);
1745 locations->SetInAt(0, Location::RequiresRegister());
1746 locations->SetInAt(1, Location::RequiresRegister());
1747 locations->SetInAt(2, Location::RequiresRegister());
1748 locations->SetInAt(3, Location::RequiresRegister());
1749 locations->SetInAt(4, Location::RequiresRegister());
1750
1751 locations->AddTemp(Location::RequiresRegister());
1752 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingdf109d92016-04-22 11:35:56 +01001753 locations->AddTemp(Location::RequiresRegister());
Tim Zhang25abd6c2016-01-19 23:39:24 +08001754}
1755
1756void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1757 vixl::MacroAssembler* masm = GetVIXLAssembler();
1758 LocationSummary* locations = invoke->GetLocations();
1759
1760 // Check assumption that sizeof(Char) is 2 (used in scaling below).
1761 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1762 DCHECK_EQ(char_size, 2u);
1763
1764 // Location of data in char array buffer.
1765 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
1766
1767 // Location of char array data in string.
1768 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1769
1770 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
1771 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
1772 Register srcObj = XRegisterFrom(locations->InAt(0));
1773 Register srcBegin = XRegisterFrom(locations->InAt(1));
1774 Register srcEnd = XRegisterFrom(locations->InAt(2));
1775 Register dstObj = XRegisterFrom(locations->InAt(3));
1776 Register dstBegin = XRegisterFrom(locations->InAt(4));
1777
1778 Register src_ptr = XRegisterFrom(locations->GetTemp(0));
Scott Wakelingdf109d92016-04-22 11:35:56 +01001779 Register num_chr = XRegisterFrom(locations->GetTemp(1));
1780 Register tmp1 = XRegisterFrom(locations->GetTemp(2));
Tim Zhang25abd6c2016-01-19 23:39:24 +08001781
1782 UseScratchRegisterScope temps(masm);
1783 Register dst_ptr = temps.AcquireX();
Scott Wakelingdf109d92016-04-22 11:35:56 +01001784 Register tmp2 = temps.AcquireX();
Tim Zhang25abd6c2016-01-19 23:39:24 +08001785
Scott Wakelingdf109d92016-04-22 11:35:56 +01001786 // src address to copy from.
Tim Zhang25abd6c2016-01-19 23:39:24 +08001787 __ Add(src_ptr, srcObj, Operand(value_offset));
Tim Zhang25abd6c2016-01-19 23:39:24 +08001788 __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
1789
Scott Wakelingdf109d92016-04-22 11:35:56 +01001790 // dst address start to copy to.
Tim Zhang25abd6c2016-01-19 23:39:24 +08001791 __ Add(dst_ptr, dstObj, Operand(data_offset));
1792 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
1793
Scott Wakelingdf109d92016-04-22 11:35:56 +01001794 __ Sub(num_chr, srcEnd, srcBegin);
1795
Tim Zhang25abd6c2016-01-19 23:39:24 +08001796 // Do the copy.
Scott Wakelingdf109d92016-04-22 11:35:56 +01001797 vixl::Label loop;
1798 vixl::Label done;
1799 vixl::Label remainder;
1800
1801 // Early out for valid zero-length retrievals.
1802 __ Cbz(num_chr, &done);
1803
1804 // Save repairing the value of num_chr on the < 8 character path.
1805 __ Subs(tmp1, num_chr, 8);
1806 __ B(lt, &remainder);
1807
1808 // Keep the result of the earlier subs, we are going to fetch at least 8 characters.
1809 __ Mov(num_chr, tmp1);
1810
1811 // Main loop used for longer fetches loads and stores 8x16-bit characters at a time.
1812 // (Unaligned addresses are acceptable here and not worth inlining extra code to rectify.)
Tim Zhang25abd6c2016-01-19 23:39:24 +08001813 __ Bind(&loop);
Scott Wakelingdf109d92016-04-22 11:35:56 +01001814 __ Ldp(tmp1, tmp2, MemOperand(src_ptr, char_size * 8, vixl::PostIndex));
1815 __ Subs(num_chr, num_chr, 8);
1816 __ Stp(tmp1, tmp2, MemOperand(dst_ptr, char_size * 8, vixl::PostIndex));
1817 __ B(ge, &loop);
1818
1819 __ Adds(num_chr, num_chr, 8);
1820 __ B(eq, &done);
1821
1822 // Main loop for < 8 character case and remainder handling. Loads and stores one
1823 // 16-bit Java character at a time.
1824 __ Bind(&remainder);
1825 __ Ldrh(tmp1, MemOperand(src_ptr, char_size, vixl::PostIndex));
1826 __ Subs(num_chr, num_chr, 1);
1827 __ Strh(tmp1, MemOperand(dst_ptr, char_size, vixl::PostIndex));
1828 __ B(gt, &remainder);
1829
Tim Zhang25abd6c2016-01-19 23:39:24 +08001830 __ Bind(&done);
1831}
1832
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001833// Mirrors ARRAYCOPY_SHORT_CHAR_ARRAY_THRESHOLD in libcore, so we can choose to use the native
1834// implementation there for longer copy lengths.
donghui.baic2ec9ad2016-03-10 14:02:55 +08001835static constexpr int32_t kSystemArrayCopyCharThreshold = 32;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001836
1837static void SetSystemArrayCopyLocationRequires(LocationSummary* locations,
1838 uint32_t at,
1839 HInstruction* input) {
1840 HIntConstant* const_input = input->AsIntConstant();
1841 if (const_input != nullptr && !vixl::Assembler::IsImmAddSub(const_input->GetValue())) {
1842 locations->SetInAt(at, Location::RequiresRegister());
1843 } else {
1844 locations->SetInAt(at, Location::RegisterOrConstant(input));
1845 }
1846}
1847
1848void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
1849 // Check to see if we have known failures that will cause us to have to bail out
1850 // to the runtime, and just generate the runtime call directly.
1851 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1852 HIntConstant* dst_pos = invoke->InputAt(3)->AsIntConstant();
1853
1854 // The positions must be non-negative.
1855 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
1856 (dst_pos != nullptr && dst_pos->GetValue() < 0)) {
1857 // We will have to fail anyways.
1858 return;
1859 }
1860
1861 // The length must be >= 0 and not so long that we would (currently) prefer libcore's
1862 // native implementation.
1863 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1864 if (length != nullptr) {
1865 int32_t len = length->GetValue();
donghui.baic2ec9ad2016-03-10 14:02:55 +08001866 if (len < 0 || len > kSystemArrayCopyCharThreshold) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001867 // Just call as normal.
1868 return;
1869 }
1870 }
1871
1872 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
1873 LocationSummary* locations = new (allocator) LocationSummary(invoke,
1874 LocationSummary::kCallOnSlowPath,
1875 kIntrinsified);
1876 // arraycopy(char[] src, int src_pos, char[] dst, int dst_pos, int length).
1877 locations->SetInAt(0, Location::RequiresRegister());
1878 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
1879 locations->SetInAt(2, Location::RequiresRegister());
1880 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
1881 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
1882
1883 locations->AddTemp(Location::RequiresRegister());
1884 locations->AddTemp(Location::RequiresRegister());
1885 locations->AddTemp(Location::RequiresRegister());
1886}
1887
1888static void CheckSystemArrayCopyPosition(vixl::MacroAssembler* masm,
1889 const Location& pos,
1890 const Register& input,
1891 const Location& length,
1892 SlowPathCodeARM64* slow_path,
1893 const Register& input_len,
1894 const Register& temp,
1895 bool length_is_input_length = false) {
1896 const int32_t length_offset = mirror::Array::LengthOffset().Int32Value();
1897 if (pos.IsConstant()) {
1898 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
1899 if (pos_const == 0) {
1900 if (!length_is_input_length) {
1901 // Check that length(input) >= length.
1902 __ Ldr(temp, MemOperand(input, length_offset));
1903 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
1904 __ B(slow_path->GetEntryLabel(), lt);
1905 }
1906 } else {
1907 // Check that length(input) >= pos.
1908 __ Ldr(input_len, MemOperand(input, length_offset));
1909 __ Subs(temp, input_len, pos_const);
1910 __ B(slow_path->GetEntryLabel(), lt);
1911
1912 // Check that (length(input) - pos) >= length.
1913 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
1914 __ B(slow_path->GetEntryLabel(), lt);
1915 }
1916 } else if (length_is_input_length) {
1917 // The only way the copy can succeed is if pos is zero.
1918 __ Cbnz(WRegisterFrom(pos), slow_path->GetEntryLabel());
1919 } else {
1920 // Check that pos >= 0.
1921 Register pos_reg = WRegisterFrom(pos);
1922 __ Tbnz(pos_reg, pos_reg.size() - 1, slow_path->GetEntryLabel());
1923
1924 // Check that pos <= length(input) && (length(input) - pos) >= length.
1925 __ Ldr(temp, MemOperand(input, length_offset));
1926 __ Subs(temp, temp, pos_reg);
1927 // Ccmp if length(input) >= pos, else definitely bail to slow path (N!=V == lt).
1928 __ Ccmp(temp, OperandFrom(length, Primitive::kPrimInt), NFlag, ge);
1929 __ B(slow_path->GetEntryLabel(), lt);
1930 }
1931}
1932
1933// Compute base source address, base destination address, and end source address
1934// for System.arraycopy* intrinsics.
1935static void GenSystemArrayCopyAddresses(vixl::MacroAssembler* masm,
1936 Primitive::Type type,
1937 const Register& src,
1938 const Location& src_pos,
1939 const Register& dst,
1940 const Location& dst_pos,
1941 const Location& copy_length,
1942 const Register& src_base,
1943 const Register& dst_base,
1944 const Register& src_end) {
1945 DCHECK(type == Primitive::kPrimNot || type == Primitive::kPrimChar)
Roland Levillainebea3d22016-04-12 15:42:57 +01001946 << "Unexpected element type: " << type;
1947 const int32_t element_size = Primitive::ComponentSize(type);
1948 const int32_t element_size_shift = Primitive::ComponentSizeShift(type);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001949
Roland Levillainebea3d22016-04-12 15:42:57 +01001950 uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001951 if (src_pos.IsConstant()) {
1952 int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01001953 __ Add(src_base, src, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001954 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01001955 __ Add(src_base, src, data_offset);
1956 __ Add(src_base, src_base, Operand(XRegisterFrom(src_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001957 }
1958
1959 if (dst_pos.IsConstant()) {
1960 int32_t constant = dst_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01001961 __ Add(dst_base, dst, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001962 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01001963 __ Add(dst_base, dst, data_offset);
1964 __ Add(dst_base, dst_base, Operand(XRegisterFrom(dst_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001965 }
1966
1967 if (copy_length.IsConstant()) {
1968 int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01001969 __ Add(src_end, src_base, element_size * constant);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001970 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01001971 __ Add(src_end, src_base, Operand(XRegisterFrom(copy_length), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001972 }
1973}
1974
1975void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
1976 vixl::MacroAssembler* masm = GetVIXLAssembler();
1977 LocationSummary* locations = invoke->GetLocations();
1978 Register src = XRegisterFrom(locations->InAt(0));
1979 Location src_pos = locations->InAt(1);
1980 Register dst = XRegisterFrom(locations->InAt(2));
1981 Location dst_pos = locations->InAt(3);
1982 Location length = locations->InAt(4);
1983
1984 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1985 codegen_->AddSlowPath(slow_path);
1986
1987 // If source and destination are the same, take the slow path. Overlapping copy regions must be
1988 // copied in reverse and we can't know in all cases if it's needed.
1989 __ Cmp(src, dst);
1990 __ B(slow_path->GetEntryLabel(), eq);
1991
1992 // Bail out if the source is null.
1993 __ Cbz(src, slow_path->GetEntryLabel());
1994
1995 // Bail out if the destination is null.
1996 __ Cbz(dst, slow_path->GetEntryLabel());
1997
1998 if (!length.IsConstant()) {
1999 // If the length is negative, bail out.
2000 __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel());
2001 // If the length > 32 then (currently) prefer libcore's native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002002 __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002003 __ B(slow_path->GetEntryLabel(), gt);
2004 } else {
2005 // We have already checked in the LocationsBuilder for the constant case.
2006 DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0);
2007 DCHECK_LE(length.GetConstant()->AsIntConstant()->GetValue(), 32);
2008 }
2009
2010 Register src_curr_addr = WRegisterFrom(locations->GetTemp(0));
2011 Register dst_curr_addr = WRegisterFrom(locations->GetTemp(1));
2012 Register src_stop_addr = WRegisterFrom(locations->GetTemp(2));
2013
2014 CheckSystemArrayCopyPosition(masm,
2015 src_pos,
2016 src,
2017 length,
2018 slow_path,
2019 src_curr_addr,
2020 dst_curr_addr,
2021 false);
2022
2023 CheckSystemArrayCopyPosition(masm,
2024 dst_pos,
2025 dst,
2026 length,
2027 slow_path,
2028 src_curr_addr,
2029 dst_curr_addr,
2030 false);
2031
2032 src_curr_addr = src_curr_addr.X();
2033 dst_curr_addr = dst_curr_addr.X();
2034 src_stop_addr = src_stop_addr.X();
2035
2036 GenSystemArrayCopyAddresses(masm,
2037 Primitive::kPrimChar,
2038 src,
2039 src_pos,
2040 dst,
2041 dst_pos,
2042 length,
2043 src_curr_addr,
2044 dst_curr_addr,
2045 src_stop_addr);
2046
2047 // Iterate over the arrays and do a raw copy of the chars.
2048 const int32_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2049 UseScratchRegisterScope temps(masm);
2050 Register tmp = temps.AcquireW();
2051 vixl::Label loop, done;
2052 __ Bind(&loop);
2053 __ Cmp(src_curr_addr, src_stop_addr);
2054 __ B(&done, eq);
2055 __ Ldrh(tmp, MemOperand(src_curr_addr, char_size, vixl::PostIndex));
2056 __ Strh(tmp, MemOperand(dst_curr_addr, char_size, vixl::PostIndex));
2057 __ B(&loop);
2058 __ Bind(&done);
2059
2060 __ Bind(slow_path->GetExitLabel());
2061}
2062
donghui.baic2ec9ad2016-03-10 14:02:55 +08002063// We can choose to use the native implementation there for longer copy lengths.
2064static constexpr int32_t kSystemArrayCopyThreshold = 128;
2065
2066// CodeGenerator::CreateSystemArrayCopyLocationSummary use three temporary registers.
2067// We want to use two temporary registers in order to reduce the register pressure in arm64.
2068// So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary.
2069void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain3d312422016-06-23 13:53:42 +01002070 // TODO(rpl): Implement read barriers in the SystemArrayCopy
2071 // intrinsic and re-enable it (b/29516905).
2072 if (kEmitCompilerReadBarrier) {
2073 return;
2074 }
2075
donghui.baic2ec9ad2016-03-10 14:02:55 +08002076 // Check to see if we have known failures that will cause us to have to bail out
2077 // to the runtime, and just generate the runtime call directly.
2078 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2079 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
2080
2081 // The positions must be non-negative.
2082 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2083 (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
2084 // We will have to fail anyways.
2085 return;
2086 }
2087
2088 // The length must be >= 0.
2089 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2090 if (length != nullptr) {
2091 int32_t len = length->GetValue();
2092 if (len < 0 || len >= kSystemArrayCopyThreshold) {
2093 // Just call as normal.
2094 return;
2095 }
2096 }
2097
2098 SystemArrayCopyOptimizations optimizations(invoke);
2099
2100 if (optimizations.GetDestinationIsSource()) {
2101 if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) {
2102 // We only support backward copying if source and destination are the same.
2103 return;
2104 }
2105 }
2106
2107 if (optimizations.GetDestinationIsPrimitiveArray() || optimizations.GetSourceIsPrimitiveArray()) {
2108 // We currently don't intrinsify primitive copying.
2109 return;
2110 }
2111
2112 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2113 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2114 LocationSummary::kCallOnSlowPath,
2115 kIntrinsified);
2116 // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length).
2117 locations->SetInAt(0, Location::RequiresRegister());
2118 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2119 locations->SetInAt(2, Location::RequiresRegister());
2120 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2121 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2122
2123 locations->AddTemp(Location::RequiresRegister());
2124 locations->AddTemp(Location::RequiresRegister());
2125}
2126
2127void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain3d312422016-06-23 13:53:42 +01002128 // TODO(rpl): Implement read barriers in the SystemArrayCopy
2129 // intrinsic and re-enable it (b/29516905).
2130 DCHECK(!kEmitCompilerReadBarrier);
2131
donghui.baic2ec9ad2016-03-10 14:02:55 +08002132 vixl::MacroAssembler* masm = GetVIXLAssembler();
2133 LocationSummary* locations = invoke->GetLocations();
2134
2135 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2136 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2137 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2138 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
2139
2140 Register src = XRegisterFrom(locations->InAt(0));
2141 Location src_pos = locations->InAt(1);
2142 Register dest = XRegisterFrom(locations->InAt(2));
2143 Location dest_pos = locations->InAt(3);
2144 Location length = locations->InAt(4);
2145 Register temp1 = WRegisterFrom(locations->GetTemp(0));
2146 Register temp2 = WRegisterFrom(locations->GetTemp(1));
2147
2148 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2149 codegen_->AddSlowPath(slow_path);
2150
2151 vixl::Label conditions_on_positions_validated;
2152 SystemArrayCopyOptimizations optimizations(invoke);
2153
2154 if (!optimizations.GetDestinationIsSource() &&
2155 (!src_pos.IsConstant() || !dest_pos.IsConstant())) {
2156 __ Cmp(src, dest);
2157 }
2158 // If source and destination are the same, we go to slow path if we need to do
2159 // forward copying.
2160 if (src_pos.IsConstant()) {
2161 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
2162 if (dest_pos.IsConstant()) {
2163 // Checked when building locations.
2164 DCHECK(!optimizations.GetDestinationIsSource()
2165 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
2166 } else {
2167 if (!optimizations.GetDestinationIsSource()) {
2168 __ B(&conditions_on_positions_validated, ne);
2169 }
2170 __ Cmp(WRegisterFrom(dest_pos), src_pos_constant);
2171 __ B(slow_path->GetEntryLabel(), gt);
2172 }
2173 } else {
2174 if (!optimizations.GetDestinationIsSource()) {
2175 __ B(&conditions_on_positions_validated, ne);
2176 }
2177 __ Cmp(RegisterFrom(src_pos, invoke->InputAt(1)->GetType()),
2178 OperandFrom(dest_pos, invoke->InputAt(3)->GetType()));
2179 __ B(slow_path->GetEntryLabel(), lt);
2180 }
2181
2182 __ Bind(&conditions_on_positions_validated);
2183
2184 if (!optimizations.GetSourceIsNotNull()) {
2185 // Bail out if the source is null.
2186 __ Cbz(src, slow_path->GetEntryLabel());
2187 }
2188
2189 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
2190 // Bail out if the destination is null.
2191 __ Cbz(dest, slow_path->GetEntryLabel());
2192 }
2193
2194 // We have already checked in the LocationsBuilder for the constant case.
2195 if (!length.IsConstant() &&
2196 !optimizations.GetCountIsSourceLength() &&
2197 !optimizations.GetCountIsDestinationLength()) {
2198 // If the length is negative, bail out.
2199 __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel());
2200 // If the length >= 128 then (currently) prefer native implementation.
2201 __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold);
2202 __ B(slow_path->GetEntryLabel(), ge);
2203 }
2204 // Validity checks: source.
2205 CheckSystemArrayCopyPosition(masm,
2206 src_pos,
2207 src,
2208 length,
2209 slow_path,
2210 temp1,
2211 temp2,
2212 optimizations.GetCountIsSourceLength());
2213
2214 // Validity checks: dest.
2215 CheckSystemArrayCopyPosition(masm,
2216 dest_pos,
2217 dest,
2218 length,
2219 slow_path,
2220 temp1,
2221 temp2,
2222 optimizations.GetCountIsDestinationLength());
2223 {
2224 // We use a block to end the scratch scope before the write barrier, thus
2225 // freeing the temporary registers so they can be used in `MarkGCCard`.
2226 UseScratchRegisterScope temps(masm);
2227 Register temp3 = temps.AcquireW();
2228 if (!optimizations.GetDoesNotNeedTypeCheck()) {
2229 // Check whether all elements of the source array are assignable to the component
2230 // type of the destination array. We do two checks: the classes are the same,
2231 // or the destination is Object[]. If none of these checks succeed, we go to the
2232 // slow path.
2233 __ Ldr(temp1, MemOperand(dest, class_offset));
2234 __ Ldr(temp2, MemOperand(src, class_offset));
2235 bool did_unpoison = false;
2236 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2237 !optimizations.GetSourceIsNonPrimitiveArray()) {
2238 // One or two of the references need to be unpoisoned. Unpoison them
2239 // both to make the identity check valid.
2240 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2241 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2242 did_unpoison = true;
2243 }
2244
2245 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2246 // Bail out if the destination is not a non primitive array.
2247 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2248 __ Ldr(temp3, HeapOperand(temp1, component_offset));
2249 __ Cbz(temp3, slow_path->GetEntryLabel());
2250 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2251 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2252 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2253 __ Cbnz(temp3, slow_path->GetEntryLabel());
2254 }
2255
2256 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2257 // Bail out if the source is not a non primitive array.
2258 // /* HeapReference<Class> */ temp3 = temp2->component_type_
2259 __ Ldr(temp3, HeapOperand(temp2, component_offset));
2260 __ Cbz(temp3, slow_path->GetEntryLabel());
2261 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2262 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2263 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2264 __ Cbnz(temp3, slow_path->GetEntryLabel());
2265 }
2266
2267 __ Cmp(temp1, temp2);
2268
2269 if (optimizations.GetDestinationIsTypedObjectArray()) {
2270 vixl::Label do_copy;
2271 __ B(&do_copy, eq);
2272 if (!did_unpoison) {
2273 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2274 }
2275 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2276 __ Ldr(temp1, HeapOperand(temp1, component_offset));
2277 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2278 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2279 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2280 // No need to unpoison the result, we're comparing against null.
2281 __ Cbnz(temp1, slow_path->GetEntryLabel());
2282 __ Bind(&do_copy);
2283 } else {
2284 __ B(slow_path->GetEntryLabel(), ne);
2285 }
2286 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2287 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2288 // Bail out if the source is not a non primitive array.
2289 // /* HeapReference<Class> */ temp1 = src->klass_
2290 __ Ldr(temp1, HeapOperand(src.W(), class_offset));
2291 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2292 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2293 __ Ldr(temp3, HeapOperand(temp1, component_offset));
2294 __ Cbz(temp3, slow_path->GetEntryLabel());
2295 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2296 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2297 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2298 __ Cbnz(temp3, slow_path->GetEntryLabel());
2299 }
2300
2301 Register src_curr_addr = temp1.X();
2302 Register dst_curr_addr = temp2.X();
2303 Register src_stop_addr = temp3.X();
2304
2305 GenSystemArrayCopyAddresses(masm,
2306 Primitive::kPrimNot,
2307 src,
2308 src_pos,
2309 dest,
2310 dest_pos,
2311 length,
2312 src_curr_addr,
2313 dst_curr_addr,
2314 src_stop_addr);
2315
2316 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2317 // poison/unpoison, nor do any read barrier as the next uses of the destination
2318 // array will do it.
2319 vixl::Label loop, done;
2320 const int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
2321 __ Bind(&loop);
2322 __ Cmp(src_curr_addr, src_stop_addr);
2323 __ B(&done, eq);
2324 {
2325 Register tmp = temps.AcquireW();
2326 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, vixl::PostIndex));
2327 __ Str(tmp, MemOperand(dst_curr_addr, element_size, vixl::PostIndex));
2328 }
2329 __ B(&loop);
2330 __ Bind(&done);
2331 }
2332 // We only need one card marking on the destination array.
2333 codegen_->MarkGCCard(dest.W(), Register(), /* value_can_be_null */ false);
2334
2335 __ Bind(slow_path->GetExitLabel());
2336}
2337
Anton Kirilova3ffea22016-04-07 17:02:37 +01002338static void GenIsInfinite(LocationSummary* locations,
2339 bool is64bit,
2340 vixl::MacroAssembler* masm) {
2341 Operand infinity;
2342 Register out;
2343
2344 if (is64bit) {
2345 infinity = kPositiveInfinityDouble;
2346 out = XRegisterFrom(locations->Out());
2347 } else {
2348 infinity = kPositiveInfinityFloat;
2349 out = WRegisterFrom(locations->Out());
2350 }
2351
2352 const Register zero = vixl::Assembler::AppropriateZeroRegFor(out);
2353
2354 MoveFPToInt(locations, is64bit, masm);
2355 __ Eor(out, out, infinity);
2356 // We don't care about the sign bit, so shift left.
2357 __ Cmp(zero, Operand(out, LSL, 1));
2358 __ Cset(out, eq);
2359}
2360
2361void IntrinsicLocationsBuilderARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2362 CreateFPToIntLocations(arena_, invoke);
2363}
2364
2365void IntrinsicCodeGeneratorARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2366 GenIsInfinite(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
2367}
2368
2369void IntrinsicLocationsBuilderARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2370 CreateFPToIntLocations(arena_, invoke);
2371}
2372
2373void IntrinsicCodeGeneratorARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2374 GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
2375}
2376
Aart Bik2f9fcc92016-03-01 15:16:54 -08002377UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
Aart Bik2f9fcc92016-03-01 15:16:54 -08002378UNIMPLEMENTED_INTRINSIC(ARM64, IntegerHighestOneBit)
2379UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit)
2380UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit)
2381UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit)
Andreas Gampe878d58c2015-01-15 23:24:00 -08002382
Aart Bik0e54c012016-03-04 12:08:31 -08002383// 1.8.
2384UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt)
2385UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong)
2386UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt)
2387UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong)
2388UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08002389
Aart Bik2f9fcc92016-03-01 15:16:54 -08002390UNREACHABLE_INTRINSICS(ARM64)
Roland Levillain4d027112015-07-01 15:41:14 +01002391
2392#undef __
2393
Andreas Gampe878d58c2015-01-15 23:24:00 -08002394} // namespace arm64
2395} // namespace art