blob: 06d11486523d0cb5397eb5a60b30e60a47655925 [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
Scott Wakeling97c72b72016-06-24 16:19:36 +010031using namespace vixl::aarch64; // NOLINT(build/namespaces)
Andreas Gampe878d58c2015-01-15 23:24:00 -080032
Scott Wakeling97c72b72016-06-24 16:19:36 +010033// TODO: make vixl clean wrt -Wshadow.
34#pragma GCC diagnostic push
35#pragma GCC diagnostic ignored "-Wshadow"
36#include "a64/disasm-a64.h"
37#include "a64/macro-assembler-a64.h"
38#pragma GCC diagnostic pop
Andreas Gampe878d58c2015-01-15 23:24:00 -080039
40namespace art {
41
42namespace arm64 {
43
44using helpers::DRegisterFrom;
45using helpers::FPRegisterFrom;
46using helpers::HeapOperand;
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +000047using helpers::LocationFrom;
Scott Wakeling9ee23f42015-07-23 10:44:35 +010048using helpers::OperandFrom;
Andreas Gampe878d58c2015-01-15 23:24:00 -080049using helpers::RegisterFrom;
50using helpers::SRegisterFrom;
51using helpers::WRegisterFrom;
52using helpers::XRegisterFrom;
xueliang.zhong49924c92016-03-03 10:52:51 +000053using helpers::InputRegisterAt;
Scott Wakeling1f36f412016-04-21 11:13:45 +010054using helpers::OutputRegister;
Andreas Gampe878d58c2015-01-15 23:24:00 -080055
Andreas Gampe878d58c2015-01-15 23:24:00 -080056namespace {
57
58ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) {
59 return MemOperand(XRegisterFrom(location), offset);
60}
61
62} // namespace
63
Scott Wakeling97c72b72016-06-24 16:19:36 +010064MacroAssembler* IntrinsicCodeGeneratorARM64::GetVIXLAssembler() {
Andreas Gampe878d58c2015-01-15 23:24:00 -080065 return codegen_->GetAssembler()->vixl_masm_;
66}
67
68ArenaAllocator* IntrinsicCodeGeneratorARM64::GetAllocator() {
69 return codegen_->GetGraph()->GetArena();
70}
71
72#define __ codegen->GetAssembler()->vixl_masm_->
73
74static void MoveFromReturnRegister(Location trg,
75 Primitive::Type type,
76 CodeGeneratorARM64* codegen) {
77 if (!trg.IsValid()) {
78 DCHECK(type == Primitive::kPrimVoid);
79 return;
80 }
81
82 DCHECK_NE(type, Primitive::kPrimVoid);
83
Jeff Hao848f70a2014-01-15 13:49:50 -080084 if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) {
Andreas Gampe878d58c2015-01-15 23:24:00 -080085 Register trg_reg = RegisterFrom(trg, type);
86 Register res_reg = RegisterFrom(ARM64ReturnLocation(type), type);
87 __ Mov(trg_reg, res_reg, kDiscardForSameWReg);
88 } else {
89 FPRegister trg_reg = FPRegisterFrom(trg, type);
90 FPRegister res_reg = FPRegisterFrom(ARM64ReturnLocation(type), type);
91 __ Fmov(trg_reg, res_reg);
92 }
93}
94
Roland Levillainec525fc2015-04-28 15:50:20 +010095static void MoveArguments(HInvoke* invoke, CodeGeneratorARM64* codegen) {
Roland Levillain2d27c8e2015-04-28 15:48:45 +010096 InvokeDexCallingConventionVisitorARM64 calling_convention_visitor;
Roland Levillainec525fc2015-04-28 15:50:20 +010097 IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor);
Andreas Gampe878d58c2015-01-15 23:24:00 -080098}
99
100// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
101// call. This will copy the arguments into the positions for a regular call.
102//
103// Note: The actual parameters are required to be in the locations given by the invoke's location
104// summary. If an intrinsic modifies those locations before a slowpath call, they must be
105// restored!
106class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 {
107 public:
David Srbecky9cd6d372016-02-09 15:24:47 +0000108 explicit IntrinsicSlowPathARM64(HInvoke* invoke)
109 : SlowPathCodeARM64(invoke), invoke_(invoke) { }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800110
111 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
112 CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
113 __ Bind(GetEntryLabel());
114
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000115 SaveLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800116
Roland Levillainec525fc2015-04-28 15:50:20 +0100117 MoveArguments(invoke_, codegen);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800118
119 if (invoke_->IsInvokeStaticOrDirect()) {
Nicolas Geoffray94015b92015-06-04 18:21:04 +0100120 codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
121 LocationFrom(kArtMethodRegister));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800122 } else {
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000123 codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800124 }
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000125 codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800126
127 // Copy the result back to the expected output.
128 Location out = invoke_->GetLocations()->Out();
129 if (out.IsValid()) {
130 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
131 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
132 MoveFromReturnRegister(out, invoke_->GetType(), codegen);
133 }
134
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000135 RestoreLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800136 __ B(GetExitLabel());
137 }
138
Alexandre Rames9931f312015-06-19 14:47:01 +0100139 const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathARM64"; }
140
Andreas Gampe878d58c2015-01-15 23:24:00 -0800141 private:
142 // The instruction where this slow path is happening.
143 HInvoke* const invoke_;
144
145 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM64);
146};
147
148#undef __
149
150bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) {
151 Dispatch(invoke);
152 LocationSummary* res = invoke->GetLocations();
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000153 if (res == nullptr) {
154 return false;
155 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000156 return res->Intrinsified();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800157}
158
159#define __ masm->
160
161static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
162 LocationSummary* locations = new (arena) LocationSummary(invoke,
163 LocationSummary::kNoCall,
164 kIntrinsified);
165 locations->SetInAt(0, Location::RequiresFpuRegister());
166 locations->SetOut(Location::RequiresRegister());
167}
168
169static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
170 LocationSummary* locations = new (arena) LocationSummary(invoke,
171 LocationSummary::kNoCall,
172 kIntrinsified);
173 locations->SetInAt(0, Location::RequiresRegister());
174 locations->SetOut(Location::RequiresFpuRegister());
175}
176
Scott Wakeling97c72b72016-06-24 16:19:36 +0100177static void MoveFPToInt(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800178 Location input = locations->InAt(0);
179 Location output = locations->Out();
180 __ Fmov(is64bit ? XRegisterFrom(output) : WRegisterFrom(output),
181 is64bit ? DRegisterFrom(input) : SRegisterFrom(input));
182}
183
Scott Wakeling97c72b72016-06-24 16:19:36 +0100184static void MoveIntToFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800185 Location input = locations->InAt(0);
186 Location output = locations->Out();
187 __ Fmov(is64bit ? DRegisterFrom(output) : SRegisterFrom(output),
188 is64bit ? XRegisterFrom(input) : WRegisterFrom(input));
189}
190
191void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
192 CreateFPToIntLocations(arena_, invoke);
193}
194void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
195 CreateIntToFPLocations(arena_, invoke);
196}
197
198void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000199 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800200}
201void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000202 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800203}
204
205void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
206 CreateFPToIntLocations(arena_, invoke);
207}
208void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
209 CreateIntToFPLocations(arena_, invoke);
210}
211
212void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000213 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800214}
215void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000216 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800217}
218
219static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
220 LocationSummary* locations = new (arena) LocationSummary(invoke,
221 LocationSummary::kNoCall,
222 kIntrinsified);
223 locations->SetInAt(0, Location::RequiresRegister());
224 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
225}
226
227static void GenReverseBytes(LocationSummary* locations,
228 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100229 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800230 Location in = locations->InAt(0);
231 Location out = locations->Out();
232
233 switch (type) {
234 case Primitive::kPrimShort:
235 __ Rev16(WRegisterFrom(out), WRegisterFrom(in));
236 __ Sxth(WRegisterFrom(out), WRegisterFrom(out));
237 break;
238 case Primitive::kPrimInt:
239 case Primitive::kPrimLong:
240 __ Rev(RegisterFrom(out, type), RegisterFrom(in, type));
241 break;
242 default:
243 LOG(FATAL) << "Unexpected size for reverse-bytes: " << type;
244 UNREACHABLE();
245 }
246}
247
248void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
249 CreateIntToIntLocations(arena_, invoke);
250}
251
252void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
253 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
254}
255
256void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) {
257 CreateIntToIntLocations(arena_, invoke);
258}
259
260void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) {
261 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
262}
263
264void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) {
265 CreateIntToIntLocations(arena_, invoke);
266}
267
268void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
269 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
270}
271
Aart Bik7b565022016-01-28 14:36:22 -0800272static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
273 LocationSummary* locations = new (arena) LocationSummary(invoke,
274 LocationSummary::kNoCall,
275 kIntrinsified);
276 locations->SetInAt(0, Location::RequiresRegister());
277 locations->SetInAt(1, Location::RequiresRegister());
278 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
279}
280
Scott Wakeling611d3392015-07-10 11:42:06 +0100281static void GenNumberOfLeadingZeros(LocationSummary* locations,
282 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100283 MacroAssembler* masm) {
Scott Wakeling611d3392015-07-10 11:42:06 +0100284 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
285
286 Location in = locations->InAt(0);
287 Location out = locations->Out();
288
289 __ Clz(RegisterFrom(out, type), RegisterFrom(in, type));
290}
291
292void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
293 CreateIntToIntLocations(arena_, invoke);
294}
295
296void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
297 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
298}
299
300void IntrinsicLocationsBuilderARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
301 CreateIntToIntLocations(arena_, invoke);
302}
303
304void IntrinsicCodeGeneratorARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
305 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
306}
307
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100308static void GenNumberOfTrailingZeros(LocationSummary* locations,
309 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100310 MacroAssembler* masm) {
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100311 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
312
313 Location in = locations->InAt(0);
314 Location out = locations->Out();
315
316 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
317 __ Clz(RegisterFrom(out, type), RegisterFrom(out, type));
318}
319
320void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
321 CreateIntToIntLocations(arena_, invoke);
322}
323
324void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
325 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
326}
327
328void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
329 CreateIntToIntLocations(arena_, invoke);
330}
331
332void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
333 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
334}
335
Andreas Gampe878d58c2015-01-15 23:24:00 -0800336static void GenReverse(LocationSummary* locations,
337 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100338 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800339 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
340
341 Location in = locations->InAt(0);
342 Location out = locations->Out();
343
344 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
345}
346
347void IntrinsicLocationsBuilderARM64::VisitIntegerReverse(HInvoke* invoke) {
348 CreateIntToIntLocations(arena_, invoke);
349}
350
351void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) {
352 GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
353}
354
355void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) {
356 CreateIntToIntLocations(arena_, invoke);
357}
358
359void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
360 GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
361}
362
Scott Wakeling97c72b72016-06-24 16:19:36 +0100363static void GenBitCount(HInvoke* instr, Primitive::Type type, MacroAssembler* masm) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100364 DCHECK(Primitive::IsIntOrLongType(type)) << type;
365 DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
366 DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
xueliang.zhong49924c92016-03-03 10:52:51 +0000367
xueliang.zhong49924c92016-03-03 10:52:51 +0000368 UseScratchRegisterScope temps(masm);
369
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000370 Register src = InputRegisterAt(instr, 0);
Roland Levillainfa3912e2016-04-01 18:21:55 +0100371 Register dst = RegisterFrom(instr->GetLocations()->Out(), type);
372 FPRegister fpr = (type == Primitive::kPrimLong) ? temps.AcquireD() : temps.AcquireS();
xueliang.zhong49924c92016-03-03 10:52:51 +0000373
374 __ Fmov(fpr, src);
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000375 __ Cnt(fpr.V8B(), fpr.V8B());
376 __ Addv(fpr.B(), fpr.V8B());
xueliang.zhong49924c92016-03-03 10:52:51 +0000377 __ Fmov(dst, fpr);
378}
379
380void IntrinsicLocationsBuilderARM64::VisitLongBitCount(HInvoke* invoke) {
381 CreateIntToIntLocations(arena_, invoke);
382}
383
384void IntrinsicCodeGeneratorARM64::VisitLongBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100385 GenBitCount(invoke, Primitive::kPrimLong, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000386}
387
388void IntrinsicLocationsBuilderARM64::VisitIntegerBitCount(HInvoke* invoke) {
389 CreateIntToIntLocations(arena_, invoke);
390}
391
392void IntrinsicCodeGeneratorARM64::VisitIntegerBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100393 GenBitCount(invoke, Primitive::kPrimInt, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000394}
395
Andreas Gampe878d58c2015-01-15 23:24:00 -0800396static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800397 LocationSummary* locations = new (arena) LocationSummary(invoke,
398 LocationSummary::kNoCall,
399 kIntrinsified);
400 locations->SetInAt(0, Location::RequiresFpuRegister());
401 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
402}
403
Scott Wakeling97c72b72016-06-24 16:19:36 +0100404static void MathAbsFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800405 Location in = locations->InAt(0);
406 Location out = locations->Out();
407
408 FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in);
409 FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out);
410
411 __ Fabs(out_reg, in_reg);
412}
413
414void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) {
415 CreateFPToFPLocations(arena_, invoke);
416}
417
418void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000419 MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800420}
421
422void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) {
423 CreateFPToFPLocations(arena_, invoke);
424}
425
426void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000427 MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800428}
429
430static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
431 LocationSummary* locations = new (arena) LocationSummary(invoke,
432 LocationSummary::kNoCall,
433 kIntrinsified);
434 locations->SetInAt(0, Location::RequiresRegister());
435 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
436}
437
438static void GenAbsInteger(LocationSummary* locations,
439 bool is64bit,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100440 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800441 Location in = locations->InAt(0);
442 Location output = locations->Out();
443
444 Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in);
445 Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output);
446
447 __ Cmp(in_reg, Operand(0));
448 __ Cneg(out_reg, in_reg, lt);
449}
450
451void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
452 CreateIntToInt(arena_, invoke);
453}
454
455void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000456 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800457}
458
459void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
460 CreateIntToInt(arena_, invoke);
461}
462
463void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000464 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800465}
466
467static void GenMinMaxFP(LocationSummary* locations,
468 bool is_min,
469 bool is_double,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100470 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800471 Location op1 = locations->InAt(0);
472 Location op2 = locations->InAt(1);
473 Location out = locations->Out();
474
475 FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1);
476 FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2);
477 FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out);
478 if (is_min) {
479 __ Fmin(out_reg, op1_reg, op2_reg);
480 } else {
481 __ Fmax(out_reg, op1_reg, op2_reg);
482 }
483}
484
485static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
486 LocationSummary* locations = new (arena) LocationSummary(invoke,
487 LocationSummary::kNoCall,
488 kIntrinsified);
489 locations->SetInAt(0, Location::RequiresFpuRegister());
490 locations->SetInAt(1, Location::RequiresFpuRegister());
491 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
492}
493
494void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
495 CreateFPFPToFPLocations(arena_, invoke);
496}
497
498void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000499 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800500}
501
502void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
503 CreateFPFPToFPLocations(arena_, invoke);
504}
505
506void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000507 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800508}
509
510void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
511 CreateFPFPToFPLocations(arena_, invoke);
512}
513
514void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000515 GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800516}
517
518void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
519 CreateFPFPToFPLocations(arena_, invoke);
520}
521
522void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000523 GenMinMaxFP(
524 invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800525}
526
527static void GenMinMax(LocationSummary* locations,
528 bool is_min,
529 bool is_long,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100530 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800531 Location op1 = locations->InAt(0);
532 Location op2 = locations->InAt(1);
533 Location out = locations->Out();
534
535 Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
536 Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
537 Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
538
539 __ Cmp(op1_reg, op2_reg);
540 __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
541}
542
Andreas Gampe878d58c2015-01-15 23:24:00 -0800543void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
544 CreateIntIntToIntLocations(arena_, invoke);
545}
546
547void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000548 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800549}
550
551void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) {
552 CreateIntIntToIntLocations(arena_, invoke);
553}
554
555void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000556 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800557}
558
559void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) {
560 CreateIntIntToIntLocations(arena_, invoke);
561}
562
563void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000564 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800565}
566
567void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) {
568 CreateIntIntToIntLocations(arena_, invoke);
569}
570
571void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000572 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800573}
574
575void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) {
576 CreateFPToFPLocations(arena_, invoke);
577}
578
579void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) {
580 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100581 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800582 __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
583}
584
585void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) {
586 CreateFPToFPLocations(arena_, invoke);
587}
588
589void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) {
590 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100591 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800592 __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
593}
594
595void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) {
596 CreateFPToFPLocations(arena_, invoke);
597}
598
599void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) {
600 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100601 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800602 __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
603}
604
605void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) {
606 CreateFPToFPLocations(arena_, invoke);
607}
608
609void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) {
610 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100611 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800612 __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
613}
614
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100615static void CreateFPToIntPlusFPTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800616 LocationSummary* locations = new (arena) LocationSummary(invoke,
617 LocationSummary::kNoCall,
618 kIntrinsified);
619 locations->SetInAt(0, Location::RequiresFpuRegister());
620 locations->SetOut(Location::RequiresRegister());
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100621 locations->AddTemp(Location::RequiresFpuRegister());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800622}
623
Scott Wakeling97c72b72016-06-24 16:19:36 +0100624static void GenMathRound(HInvoke* invoke, bool is_double, vixl::aarch64::MacroAssembler* masm) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100625 // Java 8 API definition for Math.round():
626 // Return the closest long or int to the argument, with ties rounding to positive infinity.
627 //
628 // There is no single instruction in ARMv8 that can support the above definition.
629 // We choose to use FCVTAS here, because it has closest semantic.
630 // FCVTAS performs rounding to nearest integer, ties away from zero.
631 // For most inputs (positive values, zero or NaN), this instruction is enough.
632 // We only need a few handling code after FCVTAS if the input is negative half value.
633 //
634 // The reason why we didn't choose FCVTPS instruction here is that
635 // although it performs rounding toward positive infinity, it doesn't perform rounding to nearest.
636 // For example, FCVTPS(-1.9) = -1 and FCVTPS(1.1) = 2.
637 // If we were using this instruction, for most inputs, more handling code would be needed.
638 LocationSummary* l = invoke->GetLocations();
639 FPRegister in_reg = is_double ? DRegisterFrom(l->InAt(0)) : SRegisterFrom(l->InAt(0));
640 FPRegister tmp_fp = is_double ? DRegisterFrom(l->GetTemp(0)) : SRegisterFrom(l->GetTemp(0));
641 Register out_reg = is_double ? XRegisterFrom(l->Out()) : WRegisterFrom(l->Out());
Scott Wakeling97c72b72016-06-24 16:19:36 +0100642 vixl::aarch64::Label done;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800643
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100644 // Round to nearest integer, ties away from zero.
645 __ Fcvtas(out_reg, in_reg);
646
647 // For positive values, zero or NaN inputs, rounding is done.
Scott Wakeling97c72b72016-06-24 16:19:36 +0100648 __ Tbz(out_reg, out_reg.GetSizeInBits() - 1, &done);
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100649
650 // Handle input < 0 cases.
651 // If input is negative but not a tie, previous result (round to nearest) is valid.
652 // If input is a negative tie, out_reg += 1.
653 __ Frinta(tmp_fp, in_reg);
654 __ Fsub(tmp_fp, in_reg, tmp_fp);
655 __ Fcmp(tmp_fp, 0.5);
656 __ Cinc(out_reg, out_reg, eq);
657
658 __ Bind(&done);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800659}
660
661void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100662 CreateFPToIntPlusFPTempLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800663}
664
665void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100666 GenMathRound(invoke, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800667}
668
669void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100670 CreateFPToIntPlusFPTempLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800671}
672
673void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100674 GenMathRound(invoke, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800675}
676
677void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) {
678 CreateIntToIntLocations(arena_, invoke);
679}
680
681void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100682 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800683 __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()),
684 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
685}
686
687void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
688 CreateIntToIntLocations(arena_, invoke);
689}
690
691void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100692 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800693 __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()),
694 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
695}
696
697void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
698 CreateIntToIntLocations(arena_, invoke);
699}
700
701void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100702 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800703 __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()),
704 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
705}
706
707void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
708 CreateIntToIntLocations(arena_, invoke);
709}
710
711void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100712 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800713 __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()),
714 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
715}
716
717static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
718 LocationSummary* locations = new (arena) LocationSummary(invoke,
719 LocationSummary::kNoCall,
720 kIntrinsified);
721 locations->SetInAt(0, Location::RequiresRegister());
722 locations->SetInAt(1, Location::RequiresRegister());
723}
724
725void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) {
726 CreateIntIntToVoidLocations(arena_, invoke);
727}
728
729void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100730 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800731 __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)),
732 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
733}
734
735void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
736 CreateIntIntToVoidLocations(arena_, invoke);
737}
738
739void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100740 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800741 __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)),
742 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
743}
744
745void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
746 CreateIntIntToVoidLocations(arena_, invoke);
747}
748
749void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100750 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800751 __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)),
752 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
753}
754
755void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
756 CreateIntIntToVoidLocations(arena_, invoke);
757}
758
759void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100760 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800761 __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)),
762 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
763}
764
765void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) {
766 LocationSummary* locations = new (arena_) LocationSummary(invoke,
767 LocationSummary::kNoCall,
768 kIntrinsified);
769 locations->SetOut(Location::RequiresRegister());
770}
771
772void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) {
773 codegen_->Load(Primitive::kPrimNot, WRegisterFrom(invoke->GetLocations()->Out()),
Andreas Gampe542451c2016-07-26 09:02:02 -0700774 MemOperand(tr, Thread::PeerOffset<kArm64PointerSize>().Int32Value()));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800775}
776
777static void GenUnsafeGet(HInvoke* invoke,
778 Primitive::Type type,
779 bool is_volatile,
780 CodeGeneratorARM64* codegen) {
781 LocationSummary* locations = invoke->GetLocations();
782 DCHECK((type == Primitive::kPrimInt) ||
783 (type == Primitive::kPrimLong) ||
784 (type == Primitive::kPrimNot));
Scott Wakeling97c72b72016-06-24 16:19:36 +0100785 MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000786 Location base_loc = locations->InAt(1);
787 Register base = WRegisterFrom(base_loc); // Object pointer.
788 Location offset_loc = locations->InAt(2);
789 Register offset = XRegisterFrom(offset_loc); // Long offset.
790 Location trg_loc = locations->Out();
791 Register trg = RegisterFrom(trg_loc, type);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800792
Roland Levillain44015862016-01-22 11:47:17 +0000793 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
794 // UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case.
795 UseScratchRegisterScope temps(masm);
796 Register temp = temps.AcquireW();
Roland Levillainbfea3352016-06-23 13:48:47 +0100797 codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke,
798 trg_loc,
799 base,
800 /* offset */ 0U,
801 /* index */ offset_loc,
802 /* scale_factor */ 0U,
803 temp,
804 /* needs_null_check */ false,
805 is_volatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800806 } else {
Roland Levillain44015862016-01-22 11:47:17 +0000807 // Other cases.
808 MemOperand mem_op(base.X(), offset);
809 if (is_volatile) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +0000810 codegen->LoadAcquire(invoke, trg, mem_op, /* needs_null_check */ true);
Roland Levillain44015862016-01-22 11:47:17 +0000811 } else {
812 codegen->Load(type, trg, mem_op);
813 }
Roland Levillain4d027112015-07-01 15:41:14 +0100814
Roland Levillain44015862016-01-22 11:47:17 +0000815 if (type == Primitive::kPrimNot) {
816 DCHECK(trg.IsW());
817 codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
818 }
Roland Levillain4d027112015-07-01 15:41:14 +0100819 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800820}
821
822static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000823 bool can_call = kEmitCompilerReadBarrier &&
824 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
825 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800826 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000827 can_call ?
828 LocationSummary::kCallOnSlowPath :
829 LocationSummary::kNoCall,
Andreas Gampe878d58c2015-01-15 23:24:00 -0800830 kIntrinsified);
831 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
832 locations->SetInAt(1, Location::RequiresRegister());
833 locations->SetInAt(2, Location::RequiresRegister());
Roland Levillainbfea3352016-06-23 13:48:47 +0100834 locations->SetOut(Location::RequiresRegister(),
835 can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800836}
837
838void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
839 CreateIntIntIntToIntLocations(arena_, invoke);
840}
841void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
842 CreateIntIntIntToIntLocations(arena_, invoke);
843}
844void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
845 CreateIntIntIntToIntLocations(arena_, invoke);
846}
847void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
848 CreateIntIntIntToIntLocations(arena_, invoke);
849}
850void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
851 CreateIntIntIntToIntLocations(arena_, invoke);
852}
853void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
854 CreateIntIntIntToIntLocations(arena_, invoke);
855}
856
857void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000858 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800859}
860void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000861 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800862}
863void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000864 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800865}
866void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000867 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800868}
869void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000870 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800871}
872void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000873 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800874}
875
876static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
877 LocationSummary* locations = new (arena) LocationSummary(invoke,
878 LocationSummary::kNoCall,
879 kIntrinsified);
880 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
881 locations->SetInAt(1, Location::RequiresRegister());
882 locations->SetInAt(2, Location::RequiresRegister());
883 locations->SetInAt(3, Location::RequiresRegister());
884}
885
886void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
887 CreateIntIntIntIntToVoid(arena_, invoke);
888}
889void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
890 CreateIntIntIntIntToVoid(arena_, invoke);
891}
892void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
893 CreateIntIntIntIntToVoid(arena_, invoke);
894}
895void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
896 CreateIntIntIntIntToVoid(arena_, invoke);
897}
898void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
899 CreateIntIntIntIntToVoid(arena_, invoke);
900}
901void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
902 CreateIntIntIntIntToVoid(arena_, invoke);
903}
904void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
905 CreateIntIntIntIntToVoid(arena_, invoke);
906}
907void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
908 CreateIntIntIntIntToVoid(arena_, invoke);
909}
910void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
911 CreateIntIntIntIntToVoid(arena_, invoke);
912}
913
914static void GenUnsafePut(LocationSummary* locations,
915 Primitive::Type type,
916 bool is_volatile,
917 bool is_ordered,
918 CodeGeneratorARM64* codegen) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100919 MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800920
921 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
922 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
923 Register value = RegisterFrom(locations->InAt(3), type);
Roland Levillain4d027112015-07-01 15:41:14 +0100924 Register source = value;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800925 MemOperand mem_op(base.X(), offset);
926
Roland Levillain4d027112015-07-01 15:41:14 +0100927 {
928 // We use a block to end the scratch scope before the write barrier, thus
929 // freeing the temporary registers so they can be used in `MarkGCCard`.
930 UseScratchRegisterScope temps(masm);
931
932 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
933 DCHECK(value.IsW());
934 Register temp = temps.AcquireW();
935 __ Mov(temp.W(), value.W());
936 codegen->GetAssembler()->PoisonHeapReference(temp.W());
937 source = temp;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800938 }
Roland Levillain4d027112015-07-01 15:41:14 +0100939
940 if (is_volatile || is_ordered) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +0000941 codegen->StoreRelease(type, source, mem_op);
Roland Levillain4d027112015-07-01 15:41:14 +0100942 } else {
943 codegen->Store(type, source, mem_op);
944 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800945 }
946
947 if (type == Primitive::kPrimNot) {
Nicolas Geoffray07276db2015-05-18 14:22:09 +0100948 bool value_can_be_null = true; // TODO: Worth finding out this information?
949 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800950 }
951}
952
953void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000954 GenUnsafePut(invoke->GetLocations(),
955 Primitive::kPrimInt,
956 /* is_volatile */ false,
957 /* is_ordered */ false,
958 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800959}
960void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000961 GenUnsafePut(invoke->GetLocations(),
962 Primitive::kPrimInt,
963 /* is_volatile */ false,
964 /* is_ordered */ true,
965 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800966}
967void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000968 GenUnsafePut(invoke->GetLocations(),
969 Primitive::kPrimInt,
970 /* is_volatile */ true,
971 /* is_ordered */ false,
972 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800973}
974void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000975 GenUnsafePut(invoke->GetLocations(),
976 Primitive::kPrimNot,
977 /* is_volatile */ false,
978 /* is_ordered */ false,
979 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800980}
981void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000982 GenUnsafePut(invoke->GetLocations(),
983 Primitive::kPrimNot,
984 /* is_volatile */ false,
985 /* is_ordered */ true,
986 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800987}
988void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000989 GenUnsafePut(invoke->GetLocations(),
990 Primitive::kPrimNot,
991 /* is_volatile */ true,
992 /* is_ordered */ false,
993 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800994}
995void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000996 GenUnsafePut(invoke->GetLocations(),
997 Primitive::kPrimLong,
998 /* is_volatile */ false,
999 /* is_ordered */ false,
1000 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001001}
1002void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001003 GenUnsafePut(invoke->GetLocations(),
1004 Primitive::kPrimLong,
1005 /* is_volatile */ false,
1006 /* is_ordered */ true,
1007 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001008}
1009void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001010 GenUnsafePut(invoke->GetLocations(),
1011 Primitive::kPrimLong,
1012 /* is_volatile */ true,
1013 /* is_ordered */ false,
1014 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001015}
1016
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001017static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
1018 HInvoke* invoke,
1019 Primitive::Type type) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08001020 LocationSummary* locations = new (arena) LocationSummary(invoke,
1021 LocationSummary::kNoCall,
1022 kIntrinsified);
1023 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1024 locations->SetInAt(1, Location::RequiresRegister());
1025 locations->SetInAt(2, Location::RequiresRegister());
1026 locations->SetInAt(3, Location::RequiresRegister());
1027 locations->SetInAt(4, Location::RequiresRegister());
1028
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001029 // If heap poisoning is enabled, we don't want the unpoisoning
1030 // operations to potentially clobber the output.
1031 Location::OutputOverlap overlaps = (kPoisonHeapReferences && type == Primitive::kPrimNot)
1032 ? Location::kOutputOverlap
1033 : Location::kNoOutputOverlap;
1034 locations->SetOut(Location::RequiresRegister(), overlaps);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001035}
1036
1037static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM64* codegen) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001038 MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
Andreas Gampe878d58c2015-01-15 23:24:00 -08001039
1040 Register out = WRegisterFrom(locations->Out()); // Boolean result.
1041
1042 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
1043 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
1044 Register expected = RegisterFrom(locations->InAt(3), type); // Expected.
1045 Register value = RegisterFrom(locations->InAt(4), type); // Value.
1046
1047 // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
1048 if (type == Primitive::kPrimNot) {
1049 // Mark card for object assuming new value is stored.
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001050 bool value_can_be_null = true; // TODO: Worth finding out this information?
1051 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001052 }
1053
1054 UseScratchRegisterScope temps(masm);
1055 Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
1056 Register tmp_value = temps.AcquireSameSizeAs(value); // Value in memory.
1057
1058 Register tmp_32 = tmp_value.W();
1059
1060 __ Add(tmp_ptr, base.X(), Operand(offset));
1061
Roland Levillain4d027112015-07-01 15:41:14 +01001062 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1063 codegen->GetAssembler()->PoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001064 if (value.Is(expected)) {
1065 // Do not poison `value`, as it is the same register as
1066 // `expected`, which has just been poisoned.
1067 } else {
1068 codegen->GetAssembler()->PoisonHeapReference(value);
1069 }
Roland Levillain4d027112015-07-01 15:41:14 +01001070 }
1071
Andreas Gampe878d58c2015-01-15 23:24:00 -08001072 // do {
1073 // tmp_value = [tmp_ptr] - expected;
1074 // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1075 // result = tmp_value != 0;
1076
Scott Wakeling97c72b72016-06-24 16:19:36 +01001077 vixl::aarch64::Label loop_head, exit_loop;
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001078 __ Bind(&loop_head);
1079 // TODO: When `type == Primitive::kPrimNot`, add a read barrier for
1080 // the reference stored in the object before attempting the CAS,
1081 // similar to the one in the art::Unsafe_compareAndSwapObject JNI
1082 // implementation.
1083 //
1084 // Note that this code is not (yet) used when read barriers are
1085 // enabled (see IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject).
1086 DCHECK(!(type == Primitive::kPrimNot && kEmitCompilerReadBarrier));
1087 __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
1088 __ Cmp(tmp_value, expected);
1089 __ B(&exit_loop, ne);
1090 __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
1091 __ Cbnz(tmp_32, &loop_head);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001092 __ Bind(&exit_loop);
1093 __ Cset(out, eq);
Roland Levillain4d027112015-07-01 15:41:14 +01001094
1095 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillain4d027112015-07-01 15:41:14 +01001096 codegen->GetAssembler()->UnpoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001097 if (value.Is(expected)) {
1098 // Do not unpoison `value`, as it is the same register as
1099 // `expected`, which has just been unpoisoned.
1100 } else {
1101 codegen->GetAssembler()->UnpoisonHeapReference(value);
1102 }
Roland Levillain4d027112015-07-01 15:41:14 +01001103 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001104}
1105
1106void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001107 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001108}
1109void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001110 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001111}
1112void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillain391b8662015-12-18 11:43:38 +00001113 // The UnsafeCASObject intrinsic is missing a read barrier, and
1114 // therefore sometimes does not work as expected (b/25883050).
1115 // Turn it off temporarily as a quick fix, until the read barrier is
Roland Levillain3d312422016-06-23 13:53:42 +01001116 // implemented (see TODO in GenCAS).
Roland Levillain391b8662015-12-18 11:43:38 +00001117 //
Roland Levillain3d312422016-06-23 13:53:42 +01001118 // TODO(rpl): Implement read barrier support in GenCAS and re-enable
1119 // this intrinsic.
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001120 if (kEmitCompilerReadBarrier) {
Roland Levillain985ff702015-10-23 13:25:35 +01001121 return;
1122 }
1123
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001124 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001125}
1126
1127void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
1128 GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
1129}
1130void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
1131 GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_);
1132}
1133void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillain3d312422016-06-23 13:53:42 +01001134 // The UnsafeCASObject intrinsic is missing a read barrier, and
1135 // therefore sometimes does not work as expected (b/25883050).
1136 // Turn it off temporarily as a quick fix, until the read barrier is
1137 // implemented (see TODO in GenCAS).
1138 //
1139 // TODO(rpl): Implement read barrier support in GenCAS and re-enable
1140 // this intrinsic.
1141 DCHECK(!kEmitCompilerReadBarrier);
1142
Andreas Gampe878d58c2015-01-15 23:24:00 -08001143 GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
1144}
1145
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001146void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001147 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Scott Wakeling1f36f412016-04-21 11:13:45 +01001148 invoke->InputAt(1)->CanBeNull()
1149 ? LocationSummary::kCallOnSlowPath
1150 : LocationSummary::kNoCall,
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001151 kIntrinsified);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001152 locations->SetInAt(0, Location::RequiresRegister());
1153 locations->SetInAt(1, Location::RequiresRegister());
1154 locations->AddTemp(Location::RequiresRegister());
1155 locations->AddTemp(Location::RequiresRegister());
1156 locations->AddTemp(Location::RequiresRegister());
1157 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001158}
1159
1160void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001161 MacroAssembler* masm = GetVIXLAssembler();
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001162 LocationSummary* locations = invoke->GetLocations();
1163
Scott Wakeling1f36f412016-04-21 11:13:45 +01001164 Register str = XRegisterFrom(locations->InAt(0));
1165 Register arg = XRegisterFrom(locations->InAt(1));
1166 Register out = OutputRegister(invoke);
1167
1168 Register temp0 = WRegisterFrom(locations->GetTemp(0));
1169 Register temp1 = WRegisterFrom(locations->GetTemp(1));
1170 Register temp2 = WRegisterFrom(locations->GetTemp(2));
1171
Scott Wakeling97c72b72016-06-24 16:19:36 +01001172 vixl::aarch64::Label loop;
1173 vixl::aarch64::Label find_char_diff;
1174 vixl::aarch64::Label end;
Scott Wakeling1f36f412016-04-21 11:13:45 +01001175
1176 // Get offsets of count and value fields within a string object.
1177 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1178 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1179
Nicolas Geoffray512e04d2015-03-27 17:21:24 +00001180 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +01001181 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001182
Scott Wakeling1f36f412016-04-21 11:13:45 +01001183 // Take slow path and throw if input can be and is null.
1184 SlowPathCodeARM64* slow_path = nullptr;
1185 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1186 if (can_slow_path) {
1187 slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1188 codegen_->AddSlowPath(slow_path);
1189 __ Cbz(arg, slow_path->GetEntryLabel());
1190 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001191
Scott Wakeling1f36f412016-04-21 11:13:45 +01001192 // Reference equality check, return 0 if same reference.
1193 __ Subs(out, str, arg);
1194 __ B(&end, eq);
1195 // Load lengths of this and argument strings.
1196 __ Ldr(temp0, MemOperand(str.X(), count_offset));
1197 __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1198 // Return zero if both strings are empty.
1199 __ Orr(out, temp0, temp1);
1200 __ Cbz(out, &end);
1201 // out = length diff.
1202 __ Subs(out, temp0, temp1);
1203 // temp2 = min(len(str), len(arg)).
1204 __ Csel(temp2, temp1, temp0, ge);
1205 // Shorter string is empty?
1206 __ Cbz(temp2, &end);
1207
1208 // Store offset of string value in preparation for comparison loop.
1209 __ Mov(temp1, value_offset);
1210
1211 UseScratchRegisterScope scratch_scope(masm);
1212 Register temp4 = scratch_scope.AcquireX();
1213
1214 // Assertions that must hold in order to compare strings 4 characters at a time.
1215 DCHECK_ALIGNED(value_offset, 8);
1216 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1217
1218 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1219 DCHECK_EQ(char_size, 2u);
1220
1221 // Promote temp0 to an X reg, ready for LDR.
1222 temp0 = temp0.X();
1223
1224 // Loop to compare 4x16-bit characters at a time (ok because of string data alignment).
1225 __ Bind(&loop);
1226 __ Ldr(temp4, MemOperand(str.X(), temp1));
1227 __ Ldr(temp0, MemOperand(arg.X(), temp1));
1228 __ Cmp(temp4, temp0);
1229 __ B(ne, &find_char_diff);
1230 __ Add(temp1, temp1, char_size * 4);
1231 __ Subs(temp2, temp2, 4);
1232 __ B(gt, &loop);
1233 __ B(&end);
1234
1235 // Promote temp1 to an X reg, ready for EOR.
1236 temp1 = temp1.X();
1237
1238 // Find the single 16-bit character difference.
1239 __ Bind(&find_char_diff);
1240 // Get the bit position of the first character that differs.
1241 __ Eor(temp1, temp0, temp4);
1242 __ Rbit(temp1, temp1);
1243 __ Clz(temp1, temp1);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001244 // If the number of 16-bit chars remaining <= the index where the difference occurs (0-3), then
1245 // the difference occurs outside the remaining string data, so just return length diff (out).
1246 __ Cmp(temp2, Operand(temp1, LSR, 4));
1247 __ B(le, &end);
1248 // Extract the characters and calculate the difference.
Scott Wakelinge5ed20b2016-05-20 10:41:38 +01001249 __ Bic(temp1, temp1, 0xf);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001250 __ Lsr(temp0, temp0, temp1);
1251 __ Lsr(temp4, temp4, temp1);
1252 __ And(temp4, temp4, 0xffff);
1253 __ Sub(out, temp4, Operand(temp0, UXTH));
1254
1255 __ Bind(&end);
1256
1257 if (can_slow_path) {
1258 __ Bind(slow_path->GetExitLabel());
1259 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001260}
1261
Agi Csakiea34b402015-08-13 17:51:19 -07001262void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
1263 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1264 LocationSummary::kNoCall,
1265 kIntrinsified);
1266 locations->SetInAt(0, Location::RequiresRegister());
1267 locations->SetInAt(1, Location::RequiresRegister());
1268 // Temporary registers to store lengths of strings and for calculations.
1269 locations->AddTemp(Location::RequiresRegister());
1270 locations->AddTemp(Location::RequiresRegister());
1271
1272 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1273}
1274
1275void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001276 MacroAssembler* masm = GetVIXLAssembler();
Agi Csakiea34b402015-08-13 17:51:19 -07001277 LocationSummary* locations = invoke->GetLocations();
1278
1279 Register str = WRegisterFrom(locations->InAt(0));
1280 Register arg = WRegisterFrom(locations->InAt(1));
1281 Register out = XRegisterFrom(locations->Out());
1282
1283 UseScratchRegisterScope scratch_scope(masm);
1284 Register temp = scratch_scope.AcquireW();
1285 Register temp1 = WRegisterFrom(locations->GetTemp(0));
1286 Register temp2 = WRegisterFrom(locations->GetTemp(1));
1287
Scott Wakeling97c72b72016-06-24 16:19:36 +01001288 vixl::aarch64::Label loop;
1289 vixl::aarch64::Label end;
1290 vixl::aarch64::Label return_true;
1291 vixl::aarch64::Label return_false;
Agi Csakiea34b402015-08-13 17:51:19 -07001292
1293 // Get offsets of count, value, and class fields within a string object.
1294 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1295 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1296 const int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1297
1298 // Note that the null check must have been done earlier.
1299 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1300
Vladimir Marko53b52002016-05-24 19:30:45 +01001301 StringEqualsOptimizations optimizations(invoke);
1302 if (!optimizations.GetArgumentNotNull()) {
1303 // Check if input is null, return false if it is.
1304 __ Cbz(arg, &return_false);
1305 }
Agi Csakiea34b402015-08-13 17:51:19 -07001306
1307 // Reference equality check, return true if same reference.
1308 __ Cmp(str, arg);
1309 __ B(&return_true, eq);
1310
Vladimir Marko53b52002016-05-24 19:30:45 +01001311 if (!optimizations.GetArgumentIsString()) {
1312 // Instanceof check for the argument by comparing class fields.
1313 // All string objects must have the same type since String cannot be subclassed.
1314 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1315 // If the argument is a string object, its class field must be equal to receiver's class field.
1316 __ Ldr(temp, MemOperand(str.X(), class_offset));
1317 __ Ldr(temp1, MemOperand(arg.X(), class_offset));
1318 __ Cmp(temp, temp1);
1319 __ B(&return_false, ne);
1320 }
Agi Csakiea34b402015-08-13 17:51:19 -07001321
1322 // Load lengths of this and argument strings.
1323 __ Ldr(temp, MemOperand(str.X(), count_offset));
1324 __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1325 // Check if lengths are equal, return false if they're not.
1326 __ Cmp(temp, temp1);
1327 __ B(&return_false, ne);
1328 // Store offset of string value in preparation for comparison loop
1329 __ Mov(temp1, value_offset);
1330 // Return true if both strings are empty.
1331 __ Cbz(temp, &return_true);
1332
1333 // Assertions that must hold in order to compare strings 4 characters at a time.
1334 DCHECK_ALIGNED(value_offset, 8);
1335 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1336
1337 temp1 = temp1.X();
1338 temp2 = temp2.X();
1339
1340 // Loop to compare strings 4 characters at a time starting at the beginning of the string.
1341 // Ok to do this because strings are zero-padded to be 8-byte aligned.
1342 __ Bind(&loop);
1343 __ Ldr(out, MemOperand(str.X(), temp1));
1344 __ Ldr(temp2, MemOperand(arg.X(), temp1));
1345 __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
1346 __ Cmp(out, temp2);
1347 __ B(&return_false, ne);
1348 __ Sub(temp, temp, Operand(4), SetFlags);
1349 __ B(&loop, gt);
1350
1351 // Return true and exit the function.
1352 // If loop does not result in returning false, we return true.
1353 __ Bind(&return_true);
1354 __ Mov(out, 1);
1355 __ B(&end);
1356
1357 // Return false and exit the function.
1358 __ Bind(&return_false);
1359 __ Mov(out, 0);
1360 __ Bind(&end);
1361}
1362
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001363static void GenerateVisitStringIndexOf(HInvoke* invoke,
Scott Wakeling97c72b72016-06-24 16:19:36 +01001364 MacroAssembler* masm,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001365 CodeGeneratorARM64* codegen,
1366 ArenaAllocator* allocator,
1367 bool start_at_zero) {
1368 LocationSummary* locations = invoke->GetLocations();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001369
1370 // Note that the null check must have been done earlier.
1371 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1372
1373 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001374 // 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 -07001375 SlowPathCodeARM64* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001376 HInstruction* code_point = invoke->InputAt(1);
1377 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01001378 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > 0xFFFFU) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001379 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1380 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1381 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1382 codegen->AddSlowPath(slow_path);
1383 __ B(slow_path->GetEntryLabel());
1384 __ Bind(slow_path->GetExitLabel());
1385 return;
1386 }
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001387 } else if (code_point->GetType() != Primitive::kPrimChar) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001388 Register char_reg = WRegisterFrom(locations->InAt(1));
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001389 __ Tst(char_reg, 0xFFFF0000);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001390 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1391 codegen->AddSlowPath(slow_path);
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001392 __ B(ne, slow_path->GetEntryLabel());
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001393 }
1394
1395 if (start_at_zero) {
1396 // Start-index = 0.
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001397 Register tmp_reg = WRegisterFrom(locations->GetTemp(0));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001398 __ Mov(tmp_reg, 0);
1399 }
1400
Andreas Gampe542451c2016-07-26 09:02:02 -07001401 __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64PointerSize, pIndexOf).Int32Value()));
Roland Levillain42ad2882016-02-29 18:26:54 +00001402 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001403 __ Blr(lr);
1404
1405 if (slow_path != nullptr) {
1406 __ Bind(slow_path->GetExitLabel());
1407 }
1408}
1409
1410void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) {
1411 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001412 LocationSummary::kCallOnMainOnly,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001413 kIntrinsified);
1414 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1415 // best to align the inputs accordingly.
1416 InvokeRuntimeCallingConvention calling_convention;
1417 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1418 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1419 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1420
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001421 // Need to send start_index=0.
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001422 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1423}
1424
1425void IntrinsicCodeGeneratorARM64::VisitStringIndexOf(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001426 GenerateVisitStringIndexOf(
1427 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001428}
1429
1430void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
1431 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001432 LocationSummary::kCallOnMainOnly,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001433 kIntrinsified);
1434 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1435 // best to align the inputs accordingly.
1436 InvokeRuntimeCallingConvention calling_convention;
1437 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1438 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1439 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1440 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001441}
1442
1443void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001444 GenerateVisitStringIndexOf(
1445 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001446}
1447
Jeff Hao848f70a2014-01-15 13:49:50 -08001448void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1449 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001450 LocationSummary::kCallOnMainOnly,
Jeff Hao848f70a2014-01-15 13:49:50 -08001451 kIntrinsified);
1452 InvokeRuntimeCallingConvention calling_convention;
1453 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1454 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1455 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1456 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1457 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1458}
1459
1460void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001461 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001462 LocationSummary* locations = invoke->GetLocations();
1463
1464 Register byte_array = WRegisterFrom(locations->InAt(0));
1465 __ Cmp(byte_array, 0);
1466 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1467 codegen_->AddSlowPath(slow_path);
1468 __ B(eq, slow_path->GetEntryLabel());
1469
1470 __ Ldr(lr,
Andreas Gampe542451c2016-07-26 09:02:02 -07001471 MemOperand(tr,
1472 QUICK_ENTRYPOINT_OFFSET(kArm64PointerSize, pAllocStringFromBytes).Int32Value()));
Roland Levillainf969a202016-03-09 16:14:00 +00001473 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001474 __ Blr(lr);
Roland Levillainf969a202016-03-09 16:14:00 +00001475 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Jeff Hao848f70a2014-01-15 13:49:50 -08001476 __ Bind(slow_path->GetExitLabel());
1477}
1478
1479void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
1480 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001481 LocationSummary::kCallOnMainOnly,
Jeff Hao848f70a2014-01-15 13:49:50 -08001482 kIntrinsified);
1483 InvokeRuntimeCallingConvention calling_convention;
1484 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1485 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1486 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1487 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1488}
1489
1490void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001491 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001492
Roland Levillaincc3839c2016-02-29 16:23:48 +00001493 // No need to emit code checking whether `locations->InAt(2)` is a null
1494 // pointer, as callers of the native method
1495 //
1496 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1497 //
1498 // all include a null check on `data` before calling that method.
Jeff Hao848f70a2014-01-15 13:49:50 -08001499 __ Ldr(lr,
Andreas Gampe542451c2016-07-26 09:02:02 -07001500 MemOperand(tr,
1501 QUICK_ENTRYPOINT_OFFSET(kArm64PointerSize, pAllocStringFromChars).Int32Value()));
Roland Levillainf969a202016-03-09 16:14:00 +00001502 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001503 __ Blr(lr);
Roland Levillainf969a202016-03-09 16:14:00 +00001504 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Jeff Hao848f70a2014-01-15 13:49:50 -08001505}
1506
1507void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Jeff Hao848f70a2014-01-15 13:49:50 -08001508 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001509 LocationSummary::kCallOnMainOnly,
Jeff Hao848f70a2014-01-15 13:49:50 -08001510 kIntrinsified);
1511 InvokeRuntimeCallingConvention calling_convention;
1512 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Jeff Hao848f70a2014-01-15 13:49:50 -08001513 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1514}
1515
1516void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001517 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001518 LocationSummary* locations = invoke->GetLocations();
1519
1520 Register string_to_copy = WRegisterFrom(locations->InAt(0));
1521 __ Cmp(string_to_copy, 0);
1522 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1523 codegen_->AddSlowPath(slow_path);
1524 __ B(eq, slow_path->GetEntryLabel());
1525
1526 __ Ldr(lr,
Andreas Gampe542451c2016-07-26 09:02:02 -07001527 MemOperand(tr,
1528 QUICK_ENTRYPOINT_OFFSET(kArm64PointerSize, pAllocStringFromString).Int32Value()));
Roland Levillainf969a202016-03-09 16:14:00 +00001529 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001530 __ Blr(lr);
Roland Levillainf969a202016-03-09 16:14:00 +00001531 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Jeff Hao848f70a2014-01-15 13:49:50 -08001532 __ Bind(slow_path->GetExitLabel());
1533}
1534
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001535static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1536 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
1537 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1538 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1539
1540 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001541 LocationSummary::kCallOnMainOnly,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001542 kIntrinsified);
1543 InvokeRuntimeCallingConvention calling_convention;
1544
1545 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1546 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1547}
1548
1549static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1550 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
1551 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1552 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(1)->GetType()));
1553 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1554
1555 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001556 LocationSummary::kCallOnMainOnly,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001557 kIntrinsified);
1558 InvokeRuntimeCallingConvention calling_convention;
1559
1560 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1561 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
1562 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1563}
1564
1565static void GenFPToFPCall(HInvoke* invoke,
Scott Wakeling97c72b72016-06-24 16:19:36 +01001566 MacroAssembler* masm,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001567 CodeGeneratorARM64* codegen,
1568 QuickEntrypointEnum entry) {
Andreas Gampe542451c2016-07-26 09:02:02 -07001569 __ Ldr(lr, MemOperand(tr,
1570 GetThreadOffset<kArm64PointerSize>(entry).Int32Value()));
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001571 __ Blr(lr);
1572 codegen->RecordPcInfo(invoke, invoke->GetDexPc());
1573}
1574
1575void IntrinsicLocationsBuilderARM64::VisitMathCos(HInvoke* invoke) {
1576 CreateFPToFPCallLocations(arena_, invoke);
1577}
1578
1579void IntrinsicCodeGeneratorARM64::VisitMathCos(HInvoke* invoke) {
1580 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCos);
1581}
1582
1583void IntrinsicLocationsBuilderARM64::VisitMathSin(HInvoke* invoke) {
1584 CreateFPToFPCallLocations(arena_, invoke);
1585}
1586
1587void IntrinsicCodeGeneratorARM64::VisitMathSin(HInvoke* invoke) {
1588 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickSin);
1589}
1590
1591void IntrinsicLocationsBuilderARM64::VisitMathAcos(HInvoke* invoke) {
1592 CreateFPToFPCallLocations(arena_, invoke);
1593}
1594
1595void IntrinsicCodeGeneratorARM64::VisitMathAcos(HInvoke* invoke) {
1596 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAcos);
1597}
1598
1599void IntrinsicLocationsBuilderARM64::VisitMathAsin(HInvoke* invoke) {
1600 CreateFPToFPCallLocations(arena_, invoke);
1601}
1602
1603void IntrinsicCodeGeneratorARM64::VisitMathAsin(HInvoke* invoke) {
1604 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAsin);
1605}
1606
1607void IntrinsicLocationsBuilderARM64::VisitMathAtan(HInvoke* invoke) {
1608 CreateFPToFPCallLocations(arena_, invoke);
1609}
1610
1611void IntrinsicCodeGeneratorARM64::VisitMathAtan(HInvoke* invoke) {
1612 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAtan);
1613}
1614
1615void IntrinsicLocationsBuilderARM64::VisitMathCbrt(HInvoke* invoke) {
1616 CreateFPToFPCallLocations(arena_, invoke);
1617}
1618
1619void IntrinsicCodeGeneratorARM64::VisitMathCbrt(HInvoke* invoke) {
1620 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCbrt);
1621}
1622
1623void IntrinsicLocationsBuilderARM64::VisitMathCosh(HInvoke* invoke) {
1624 CreateFPToFPCallLocations(arena_, invoke);
1625}
1626
1627void IntrinsicCodeGeneratorARM64::VisitMathCosh(HInvoke* invoke) {
1628 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCosh);
1629}
1630
1631void IntrinsicLocationsBuilderARM64::VisitMathExp(HInvoke* invoke) {
1632 CreateFPToFPCallLocations(arena_, invoke);
1633}
1634
1635void IntrinsicCodeGeneratorARM64::VisitMathExp(HInvoke* invoke) {
1636 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickExp);
1637}
1638
1639void IntrinsicLocationsBuilderARM64::VisitMathExpm1(HInvoke* invoke) {
1640 CreateFPToFPCallLocations(arena_, invoke);
1641}
1642
1643void IntrinsicCodeGeneratorARM64::VisitMathExpm1(HInvoke* invoke) {
1644 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickExpm1);
1645}
1646
1647void IntrinsicLocationsBuilderARM64::VisitMathLog(HInvoke* invoke) {
1648 CreateFPToFPCallLocations(arena_, invoke);
1649}
1650
1651void IntrinsicCodeGeneratorARM64::VisitMathLog(HInvoke* invoke) {
1652 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickLog);
1653}
1654
1655void IntrinsicLocationsBuilderARM64::VisitMathLog10(HInvoke* invoke) {
1656 CreateFPToFPCallLocations(arena_, invoke);
1657}
1658
1659void IntrinsicCodeGeneratorARM64::VisitMathLog10(HInvoke* invoke) {
1660 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickLog10);
1661}
1662
1663void IntrinsicLocationsBuilderARM64::VisitMathSinh(HInvoke* invoke) {
1664 CreateFPToFPCallLocations(arena_, invoke);
1665}
1666
1667void IntrinsicCodeGeneratorARM64::VisitMathSinh(HInvoke* invoke) {
1668 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickSinh);
1669}
1670
1671void IntrinsicLocationsBuilderARM64::VisitMathTan(HInvoke* invoke) {
1672 CreateFPToFPCallLocations(arena_, invoke);
1673}
1674
1675void IntrinsicCodeGeneratorARM64::VisitMathTan(HInvoke* invoke) {
1676 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickTan);
1677}
1678
1679void IntrinsicLocationsBuilderARM64::VisitMathTanh(HInvoke* invoke) {
1680 CreateFPToFPCallLocations(arena_, invoke);
1681}
1682
1683void IntrinsicCodeGeneratorARM64::VisitMathTanh(HInvoke* invoke) {
1684 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickTanh);
1685}
1686
1687void IntrinsicLocationsBuilderARM64::VisitMathAtan2(HInvoke* invoke) {
1688 CreateFPFPToFPCallLocations(arena_, invoke);
1689}
1690
1691void IntrinsicCodeGeneratorARM64::VisitMathAtan2(HInvoke* invoke) {
1692 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAtan2);
1693}
1694
1695void IntrinsicLocationsBuilderARM64::VisitMathHypot(HInvoke* invoke) {
1696 CreateFPFPToFPCallLocations(arena_, invoke);
1697}
1698
1699void IntrinsicCodeGeneratorARM64::VisitMathHypot(HInvoke* invoke) {
1700 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickHypot);
1701}
1702
1703void IntrinsicLocationsBuilderARM64::VisitMathNextAfter(HInvoke* invoke) {
1704 CreateFPFPToFPCallLocations(arena_, invoke);
1705}
1706
1707void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) {
1708 GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickNextAfter);
1709}
1710
Tim Zhang25abd6c2016-01-19 23:39:24 +08001711void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1712 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1713 LocationSummary::kNoCall,
1714 kIntrinsified);
1715 locations->SetInAt(0, Location::RequiresRegister());
1716 locations->SetInAt(1, Location::RequiresRegister());
1717 locations->SetInAt(2, Location::RequiresRegister());
1718 locations->SetInAt(3, Location::RequiresRegister());
1719 locations->SetInAt(4, Location::RequiresRegister());
1720
1721 locations->AddTemp(Location::RequiresRegister());
1722 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingdf109d92016-04-22 11:35:56 +01001723 locations->AddTemp(Location::RequiresRegister());
Tim Zhang25abd6c2016-01-19 23:39:24 +08001724}
1725
1726void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001727 MacroAssembler* masm = GetVIXLAssembler();
Tim Zhang25abd6c2016-01-19 23:39:24 +08001728 LocationSummary* locations = invoke->GetLocations();
1729
1730 // Check assumption that sizeof(Char) is 2 (used in scaling below).
1731 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1732 DCHECK_EQ(char_size, 2u);
1733
1734 // Location of data in char array buffer.
1735 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
1736
1737 // Location of char array data in string.
1738 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1739
1740 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
1741 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
1742 Register srcObj = XRegisterFrom(locations->InAt(0));
1743 Register srcBegin = XRegisterFrom(locations->InAt(1));
1744 Register srcEnd = XRegisterFrom(locations->InAt(2));
1745 Register dstObj = XRegisterFrom(locations->InAt(3));
1746 Register dstBegin = XRegisterFrom(locations->InAt(4));
1747
1748 Register src_ptr = XRegisterFrom(locations->GetTemp(0));
Scott Wakelingdf109d92016-04-22 11:35:56 +01001749 Register num_chr = XRegisterFrom(locations->GetTemp(1));
1750 Register tmp1 = XRegisterFrom(locations->GetTemp(2));
Tim Zhang25abd6c2016-01-19 23:39:24 +08001751
1752 UseScratchRegisterScope temps(masm);
1753 Register dst_ptr = temps.AcquireX();
Scott Wakelingdf109d92016-04-22 11:35:56 +01001754 Register tmp2 = temps.AcquireX();
Tim Zhang25abd6c2016-01-19 23:39:24 +08001755
Scott Wakelingdf109d92016-04-22 11:35:56 +01001756 // src address to copy from.
Tim Zhang25abd6c2016-01-19 23:39:24 +08001757 __ Add(src_ptr, srcObj, Operand(value_offset));
Tim Zhang25abd6c2016-01-19 23:39:24 +08001758 __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
1759
Scott Wakelingdf109d92016-04-22 11:35:56 +01001760 // dst address start to copy to.
Tim Zhang25abd6c2016-01-19 23:39:24 +08001761 __ Add(dst_ptr, dstObj, Operand(data_offset));
1762 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
1763
Scott Wakelingdf109d92016-04-22 11:35:56 +01001764 __ Sub(num_chr, srcEnd, srcBegin);
1765
Tim Zhang25abd6c2016-01-19 23:39:24 +08001766 // Do the copy.
Scott Wakeling97c72b72016-06-24 16:19:36 +01001767 vixl::aarch64::Label loop;
1768 vixl::aarch64::Label done;
1769 vixl::aarch64::Label remainder;
Scott Wakelingdf109d92016-04-22 11:35:56 +01001770
1771 // Early out for valid zero-length retrievals.
1772 __ Cbz(num_chr, &done);
1773
1774 // Save repairing the value of num_chr on the < 8 character path.
1775 __ Subs(tmp1, num_chr, 8);
1776 __ B(lt, &remainder);
1777
1778 // Keep the result of the earlier subs, we are going to fetch at least 8 characters.
1779 __ Mov(num_chr, tmp1);
1780
1781 // Main loop used for longer fetches loads and stores 8x16-bit characters at a time.
1782 // (Unaligned addresses are acceptable here and not worth inlining extra code to rectify.)
Tim Zhang25abd6c2016-01-19 23:39:24 +08001783 __ Bind(&loop);
Scott Wakeling97c72b72016-06-24 16:19:36 +01001784 __ Ldp(tmp1, tmp2, MemOperand(src_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01001785 __ Subs(num_chr, num_chr, 8);
Scott Wakeling97c72b72016-06-24 16:19:36 +01001786 __ Stp(tmp1, tmp2, MemOperand(dst_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01001787 __ B(ge, &loop);
1788
1789 __ Adds(num_chr, num_chr, 8);
1790 __ B(eq, &done);
1791
1792 // Main loop for < 8 character case and remainder handling. Loads and stores one
1793 // 16-bit Java character at a time.
1794 __ Bind(&remainder);
Scott Wakeling97c72b72016-06-24 16:19:36 +01001795 __ Ldrh(tmp1, MemOperand(src_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01001796 __ Subs(num_chr, num_chr, 1);
Scott Wakeling97c72b72016-06-24 16:19:36 +01001797 __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01001798 __ B(gt, &remainder);
1799
Tim Zhang25abd6c2016-01-19 23:39:24 +08001800 __ Bind(&done);
1801}
1802
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001803// Mirrors ARRAYCOPY_SHORT_CHAR_ARRAY_THRESHOLD in libcore, so we can choose to use the native
1804// implementation there for longer copy lengths.
donghui.baic2ec9ad2016-03-10 14:02:55 +08001805static constexpr int32_t kSystemArrayCopyCharThreshold = 32;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001806
1807static void SetSystemArrayCopyLocationRequires(LocationSummary* locations,
1808 uint32_t at,
1809 HInstruction* input) {
1810 HIntConstant* const_input = input->AsIntConstant();
Scott Wakeling97c72b72016-06-24 16:19:36 +01001811 if (const_input != nullptr && !vixl::aarch64::Assembler::IsImmAddSub(const_input->GetValue())) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001812 locations->SetInAt(at, Location::RequiresRegister());
1813 } else {
1814 locations->SetInAt(at, Location::RegisterOrConstant(input));
1815 }
1816}
1817
1818void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
1819 // Check to see if we have known failures that will cause us to have to bail out
1820 // to the runtime, and just generate the runtime call directly.
1821 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1822 HIntConstant* dst_pos = invoke->InputAt(3)->AsIntConstant();
1823
1824 // The positions must be non-negative.
1825 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
1826 (dst_pos != nullptr && dst_pos->GetValue() < 0)) {
1827 // We will have to fail anyways.
1828 return;
1829 }
1830
1831 // The length must be >= 0 and not so long that we would (currently) prefer libcore's
1832 // native implementation.
1833 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1834 if (length != nullptr) {
1835 int32_t len = length->GetValue();
donghui.baic2ec9ad2016-03-10 14:02:55 +08001836 if (len < 0 || len > kSystemArrayCopyCharThreshold) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001837 // Just call as normal.
1838 return;
1839 }
1840 }
1841
1842 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
1843 LocationSummary* locations = new (allocator) LocationSummary(invoke,
1844 LocationSummary::kCallOnSlowPath,
1845 kIntrinsified);
1846 // arraycopy(char[] src, int src_pos, char[] dst, int dst_pos, int length).
1847 locations->SetInAt(0, Location::RequiresRegister());
1848 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
1849 locations->SetInAt(2, Location::RequiresRegister());
1850 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
1851 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
1852
1853 locations->AddTemp(Location::RequiresRegister());
1854 locations->AddTemp(Location::RequiresRegister());
1855 locations->AddTemp(Location::RequiresRegister());
1856}
1857
Scott Wakeling97c72b72016-06-24 16:19:36 +01001858static void CheckSystemArrayCopyPosition(MacroAssembler* masm,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001859 const Location& pos,
1860 const Register& input,
1861 const Location& length,
1862 SlowPathCodeARM64* slow_path,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001863 const Register& temp,
1864 bool length_is_input_length = false) {
1865 const int32_t length_offset = mirror::Array::LengthOffset().Int32Value();
1866 if (pos.IsConstant()) {
1867 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
1868 if (pos_const == 0) {
1869 if (!length_is_input_length) {
1870 // Check that length(input) >= length.
1871 __ Ldr(temp, MemOperand(input, length_offset));
1872 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
1873 __ B(slow_path->GetEntryLabel(), lt);
1874 }
1875 } else {
1876 // Check that length(input) >= pos.
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01001877 __ Ldr(temp, MemOperand(input, length_offset));
1878 __ Subs(temp, temp, pos_const);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001879 __ B(slow_path->GetEntryLabel(), lt);
1880
1881 // Check that (length(input) - pos) >= length.
1882 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
1883 __ B(slow_path->GetEntryLabel(), lt);
1884 }
1885 } else if (length_is_input_length) {
1886 // The only way the copy can succeed is if pos is zero.
1887 __ Cbnz(WRegisterFrom(pos), slow_path->GetEntryLabel());
1888 } else {
1889 // Check that pos >= 0.
1890 Register pos_reg = WRegisterFrom(pos);
Scott Wakeling97c72b72016-06-24 16:19:36 +01001891 __ Tbnz(pos_reg, pos_reg.GetSizeInBits() - 1, slow_path->GetEntryLabel());
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001892
1893 // Check that pos <= length(input) && (length(input) - pos) >= length.
1894 __ Ldr(temp, MemOperand(input, length_offset));
1895 __ Subs(temp, temp, pos_reg);
1896 // Ccmp if length(input) >= pos, else definitely bail to slow path (N!=V == lt).
1897 __ Ccmp(temp, OperandFrom(length, Primitive::kPrimInt), NFlag, ge);
1898 __ B(slow_path->GetEntryLabel(), lt);
1899 }
1900}
1901
1902// Compute base source address, base destination address, and end source address
1903// for System.arraycopy* intrinsics.
Scott Wakeling97c72b72016-06-24 16:19:36 +01001904static void GenSystemArrayCopyAddresses(MacroAssembler* masm,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001905 Primitive::Type type,
1906 const Register& src,
1907 const Location& src_pos,
1908 const Register& dst,
1909 const Location& dst_pos,
1910 const Location& copy_length,
1911 const Register& src_base,
1912 const Register& dst_base,
1913 const Register& src_end) {
1914 DCHECK(type == Primitive::kPrimNot || type == Primitive::kPrimChar)
Roland Levillainebea3d22016-04-12 15:42:57 +01001915 << "Unexpected element type: " << type;
1916 const int32_t element_size = Primitive::ComponentSize(type);
1917 const int32_t element_size_shift = Primitive::ComponentSizeShift(type);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001918
Roland Levillainebea3d22016-04-12 15:42:57 +01001919 uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001920 if (src_pos.IsConstant()) {
1921 int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01001922 __ Add(src_base, src, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001923 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01001924 __ Add(src_base, src, data_offset);
1925 __ Add(src_base, src_base, Operand(XRegisterFrom(src_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001926 }
1927
1928 if (dst_pos.IsConstant()) {
1929 int32_t constant = dst_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01001930 __ Add(dst_base, dst, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001931 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01001932 __ Add(dst_base, dst, data_offset);
1933 __ Add(dst_base, dst_base, Operand(XRegisterFrom(dst_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001934 }
1935
1936 if (copy_length.IsConstant()) {
1937 int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01001938 __ Add(src_end, src_base, element_size * constant);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001939 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01001940 __ Add(src_end, src_base, Operand(XRegisterFrom(copy_length), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001941 }
1942}
1943
1944void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001945 MacroAssembler* masm = GetVIXLAssembler();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001946 LocationSummary* locations = invoke->GetLocations();
1947 Register src = XRegisterFrom(locations->InAt(0));
1948 Location src_pos = locations->InAt(1);
1949 Register dst = XRegisterFrom(locations->InAt(2));
1950 Location dst_pos = locations->InAt(3);
1951 Location length = locations->InAt(4);
1952
1953 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1954 codegen_->AddSlowPath(slow_path);
1955
1956 // If source and destination are the same, take the slow path. Overlapping copy regions must be
1957 // copied in reverse and we can't know in all cases if it's needed.
1958 __ Cmp(src, dst);
1959 __ B(slow_path->GetEntryLabel(), eq);
1960
1961 // Bail out if the source is null.
1962 __ Cbz(src, slow_path->GetEntryLabel());
1963
1964 // Bail out if the destination is null.
1965 __ Cbz(dst, slow_path->GetEntryLabel());
1966
1967 if (!length.IsConstant()) {
1968 // If the length is negative, bail out.
1969 __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel());
1970 // If the length > 32 then (currently) prefer libcore's native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08001971 __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001972 __ B(slow_path->GetEntryLabel(), gt);
1973 } else {
1974 // We have already checked in the LocationsBuilder for the constant case.
1975 DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0);
1976 DCHECK_LE(length.GetConstant()->AsIntConstant()->GetValue(), 32);
1977 }
1978
1979 Register src_curr_addr = WRegisterFrom(locations->GetTemp(0));
1980 Register dst_curr_addr = WRegisterFrom(locations->GetTemp(1));
1981 Register src_stop_addr = WRegisterFrom(locations->GetTemp(2));
1982
1983 CheckSystemArrayCopyPosition(masm,
1984 src_pos,
1985 src,
1986 length,
1987 slow_path,
1988 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001989 false);
1990
1991 CheckSystemArrayCopyPosition(masm,
1992 dst_pos,
1993 dst,
1994 length,
1995 slow_path,
1996 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00001997 false);
1998
1999 src_curr_addr = src_curr_addr.X();
2000 dst_curr_addr = dst_curr_addr.X();
2001 src_stop_addr = src_stop_addr.X();
2002
2003 GenSystemArrayCopyAddresses(masm,
2004 Primitive::kPrimChar,
2005 src,
2006 src_pos,
2007 dst,
2008 dst_pos,
2009 length,
2010 src_curr_addr,
2011 dst_curr_addr,
2012 src_stop_addr);
2013
2014 // Iterate over the arrays and do a raw copy of the chars.
2015 const int32_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2016 UseScratchRegisterScope temps(masm);
2017 Register tmp = temps.AcquireW();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002018 vixl::aarch64::Label loop, done;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002019 __ Bind(&loop);
2020 __ Cmp(src_curr_addr, src_stop_addr);
2021 __ B(&done, eq);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002022 __ Ldrh(tmp, MemOperand(src_curr_addr, char_size, PostIndex));
2023 __ Strh(tmp, MemOperand(dst_curr_addr, char_size, PostIndex));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002024 __ B(&loop);
2025 __ Bind(&done);
2026
2027 __ Bind(slow_path->GetExitLabel());
2028}
2029
donghui.baic2ec9ad2016-03-10 14:02:55 +08002030// We can choose to use the native implementation there for longer copy lengths.
2031static constexpr int32_t kSystemArrayCopyThreshold = 128;
2032
2033// CodeGenerator::CreateSystemArrayCopyLocationSummary use three temporary registers.
2034// We want to use two temporary registers in order to reduce the register pressure in arm64.
2035// So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary.
2036void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain3d312422016-06-23 13:53:42 +01002037 // TODO(rpl): Implement read barriers in the SystemArrayCopy
2038 // intrinsic and re-enable it (b/29516905).
2039 if (kEmitCompilerReadBarrier) {
2040 return;
2041 }
2042
donghui.baic2ec9ad2016-03-10 14:02:55 +08002043 // Check to see if we have known failures that will cause us to have to bail out
2044 // to the runtime, and just generate the runtime call directly.
2045 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2046 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
2047
2048 // The positions must be non-negative.
2049 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2050 (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
2051 // We will have to fail anyways.
2052 return;
2053 }
2054
2055 // The length must be >= 0.
2056 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2057 if (length != nullptr) {
2058 int32_t len = length->GetValue();
2059 if (len < 0 || len >= kSystemArrayCopyThreshold) {
2060 // Just call as normal.
2061 return;
2062 }
2063 }
2064
2065 SystemArrayCopyOptimizations optimizations(invoke);
2066
2067 if (optimizations.GetDestinationIsSource()) {
2068 if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) {
2069 // We only support backward copying if source and destination are the same.
2070 return;
2071 }
2072 }
2073
2074 if (optimizations.GetDestinationIsPrimitiveArray() || optimizations.GetSourceIsPrimitiveArray()) {
2075 // We currently don't intrinsify primitive copying.
2076 return;
2077 }
2078
2079 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2080 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2081 LocationSummary::kCallOnSlowPath,
2082 kIntrinsified);
2083 // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length).
2084 locations->SetInAt(0, Location::RequiresRegister());
2085 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2086 locations->SetInAt(2, Location::RequiresRegister());
2087 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2088 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2089
2090 locations->AddTemp(Location::RequiresRegister());
2091 locations->AddTemp(Location::RequiresRegister());
2092}
2093
2094void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain3d312422016-06-23 13:53:42 +01002095 // TODO(rpl): Implement read barriers in the SystemArrayCopy
2096 // intrinsic and re-enable it (b/29516905).
2097 DCHECK(!kEmitCompilerReadBarrier);
2098
Scott Wakeling97c72b72016-06-24 16:19:36 +01002099 MacroAssembler* masm = GetVIXLAssembler();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002100 LocationSummary* locations = invoke->GetLocations();
2101
2102 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2103 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2104 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2105 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
2106
2107 Register src = XRegisterFrom(locations->InAt(0));
2108 Location src_pos = locations->InAt(1);
2109 Register dest = XRegisterFrom(locations->InAt(2));
2110 Location dest_pos = locations->InAt(3);
2111 Location length = locations->InAt(4);
2112 Register temp1 = WRegisterFrom(locations->GetTemp(0));
2113 Register temp2 = WRegisterFrom(locations->GetTemp(1));
2114
2115 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2116 codegen_->AddSlowPath(slow_path);
2117
Scott Wakeling97c72b72016-06-24 16:19:36 +01002118 vixl::aarch64::Label conditions_on_positions_validated;
donghui.baic2ec9ad2016-03-10 14:02:55 +08002119 SystemArrayCopyOptimizations optimizations(invoke);
2120
donghui.baic2ec9ad2016-03-10 14:02:55 +08002121 // If source and destination are the same, we go to slow path if we need to do
2122 // forward copying.
2123 if (src_pos.IsConstant()) {
2124 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
2125 if (dest_pos.IsConstant()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002126 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
2127 if (optimizations.GetDestinationIsSource()) {
2128 // Checked when building locations.
2129 DCHECK_GE(src_pos_constant, dest_pos_constant);
2130 } else if (src_pos_constant < dest_pos_constant) {
2131 __ Cmp(src, dest);
2132 __ B(slow_path->GetEntryLabel(), eq);
2133 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002134 // Checked when building locations.
2135 DCHECK(!optimizations.GetDestinationIsSource()
2136 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
2137 } else {
2138 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002139 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002140 __ B(&conditions_on_positions_validated, ne);
2141 }
2142 __ Cmp(WRegisterFrom(dest_pos), src_pos_constant);
2143 __ B(slow_path->GetEntryLabel(), gt);
2144 }
2145 } else {
2146 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002147 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002148 __ B(&conditions_on_positions_validated, ne);
2149 }
2150 __ Cmp(RegisterFrom(src_pos, invoke->InputAt(1)->GetType()),
2151 OperandFrom(dest_pos, invoke->InputAt(3)->GetType()));
2152 __ B(slow_path->GetEntryLabel(), lt);
2153 }
2154
2155 __ Bind(&conditions_on_positions_validated);
2156
2157 if (!optimizations.GetSourceIsNotNull()) {
2158 // Bail out if the source is null.
2159 __ Cbz(src, slow_path->GetEntryLabel());
2160 }
2161
2162 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
2163 // Bail out if the destination is null.
2164 __ Cbz(dest, slow_path->GetEntryLabel());
2165 }
2166
2167 // We have already checked in the LocationsBuilder for the constant case.
2168 if (!length.IsConstant() &&
2169 !optimizations.GetCountIsSourceLength() &&
2170 !optimizations.GetCountIsDestinationLength()) {
2171 // If the length is negative, bail out.
2172 __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel());
2173 // If the length >= 128 then (currently) prefer native implementation.
2174 __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold);
2175 __ B(slow_path->GetEntryLabel(), ge);
2176 }
2177 // Validity checks: source.
2178 CheckSystemArrayCopyPosition(masm,
2179 src_pos,
2180 src,
2181 length,
2182 slow_path,
2183 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002184 optimizations.GetCountIsSourceLength());
2185
2186 // Validity checks: dest.
2187 CheckSystemArrayCopyPosition(masm,
2188 dest_pos,
2189 dest,
2190 length,
2191 slow_path,
2192 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002193 optimizations.GetCountIsDestinationLength());
2194 {
2195 // We use a block to end the scratch scope before the write barrier, thus
2196 // freeing the temporary registers so they can be used in `MarkGCCard`.
2197 UseScratchRegisterScope temps(masm);
2198 Register temp3 = temps.AcquireW();
2199 if (!optimizations.GetDoesNotNeedTypeCheck()) {
2200 // Check whether all elements of the source array are assignable to the component
2201 // type of the destination array. We do two checks: the classes are the same,
2202 // or the destination is Object[]. If none of these checks succeed, we go to the
2203 // slow path.
2204 __ Ldr(temp1, MemOperand(dest, class_offset));
2205 __ Ldr(temp2, MemOperand(src, class_offset));
2206 bool did_unpoison = false;
2207 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2208 !optimizations.GetSourceIsNonPrimitiveArray()) {
2209 // One or two of the references need to be unpoisoned. Unpoison them
2210 // both to make the identity check valid.
2211 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2212 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2213 did_unpoison = true;
2214 }
2215
2216 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2217 // Bail out if the destination is not a non primitive array.
2218 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2219 __ Ldr(temp3, HeapOperand(temp1, component_offset));
2220 __ Cbz(temp3, slow_path->GetEntryLabel());
2221 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2222 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2223 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2224 __ Cbnz(temp3, slow_path->GetEntryLabel());
2225 }
2226
2227 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2228 // Bail out if the source is not a non primitive array.
2229 // /* HeapReference<Class> */ temp3 = temp2->component_type_
2230 __ Ldr(temp3, HeapOperand(temp2, component_offset));
2231 __ Cbz(temp3, slow_path->GetEntryLabel());
2232 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2233 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2234 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2235 __ Cbnz(temp3, slow_path->GetEntryLabel());
2236 }
2237
2238 __ Cmp(temp1, temp2);
2239
2240 if (optimizations.GetDestinationIsTypedObjectArray()) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01002241 vixl::aarch64::Label do_copy;
donghui.baic2ec9ad2016-03-10 14:02:55 +08002242 __ B(&do_copy, eq);
2243 if (!did_unpoison) {
2244 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2245 }
2246 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2247 __ Ldr(temp1, HeapOperand(temp1, component_offset));
2248 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2249 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2250 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2251 // No need to unpoison the result, we're comparing against null.
2252 __ Cbnz(temp1, slow_path->GetEntryLabel());
2253 __ Bind(&do_copy);
2254 } else {
2255 __ B(slow_path->GetEntryLabel(), ne);
2256 }
2257 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2258 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2259 // Bail out if the source is not a non primitive array.
2260 // /* HeapReference<Class> */ temp1 = src->klass_
2261 __ Ldr(temp1, HeapOperand(src.W(), class_offset));
2262 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2263 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2264 __ Ldr(temp3, HeapOperand(temp1, component_offset));
2265 __ Cbz(temp3, slow_path->GetEntryLabel());
2266 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2267 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2268 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2269 __ Cbnz(temp3, slow_path->GetEntryLabel());
2270 }
2271
2272 Register src_curr_addr = temp1.X();
2273 Register dst_curr_addr = temp2.X();
2274 Register src_stop_addr = temp3.X();
2275
2276 GenSystemArrayCopyAddresses(masm,
2277 Primitive::kPrimNot,
2278 src,
2279 src_pos,
2280 dest,
2281 dest_pos,
2282 length,
2283 src_curr_addr,
2284 dst_curr_addr,
2285 src_stop_addr);
2286
2287 // Iterate over the arrays and do a raw copy of the objects. We don't need to
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002288 // poison/unpoison.
Scott Wakeling97c72b72016-06-24 16:19:36 +01002289 vixl::aarch64::Label loop, done;
donghui.baic2ec9ad2016-03-10 14:02:55 +08002290 const int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
2291 __ Bind(&loop);
2292 __ Cmp(src_curr_addr, src_stop_addr);
2293 __ B(&done, eq);
2294 {
2295 Register tmp = temps.AcquireW();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002296 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
2297 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
donghui.baic2ec9ad2016-03-10 14:02:55 +08002298 }
2299 __ B(&loop);
2300 __ Bind(&done);
2301 }
2302 // We only need one card marking on the destination array.
2303 codegen_->MarkGCCard(dest.W(), Register(), /* value_can_be_null */ false);
2304
2305 __ Bind(slow_path->GetExitLabel());
2306}
2307
Anton Kirilova3ffea22016-04-07 17:02:37 +01002308static void GenIsInfinite(LocationSummary* locations,
2309 bool is64bit,
Scott Wakeling97c72b72016-06-24 16:19:36 +01002310 MacroAssembler* masm) {
Anton Kirilova3ffea22016-04-07 17:02:37 +01002311 Operand infinity;
2312 Register out;
2313
2314 if (is64bit) {
2315 infinity = kPositiveInfinityDouble;
2316 out = XRegisterFrom(locations->Out());
2317 } else {
2318 infinity = kPositiveInfinityFloat;
2319 out = WRegisterFrom(locations->Out());
2320 }
2321
Scott Wakeling97c72b72016-06-24 16:19:36 +01002322 const Register zero = vixl::aarch64::Assembler::AppropriateZeroRegFor(out);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002323
2324 MoveFPToInt(locations, is64bit, masm);
2325 __ Eor(out, out, infinity);
2326 // We don't care about the sign bit, so shift left.
2327 __ Cmp(zero, Operand(out, LSL, 1));
2328 __ Cset(out, eq);
2329}
2330
2331void IntrinsicLocationsBuilderARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2332 CreateFPToIntLocations(arena_, invoke);
2333}
2334
2335void IntrinsicCodeGeneratorARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2336 GenIsInfinite(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
2337}
2338
2339void IntrinsicLocationsBuilderARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2340 CreateFPToIntLocations(arena_, invoke);
2341}
2342
2343void IntrinsicCodeGeneratorARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2344 GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
2345}
2346
Aart Bik2f9fcc92016-03-01 15:16:54 -08002347UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
Aart Bik2f9fcc92016-03-01 15:16:54 -08002348UNIMPLEMENTED_INTRINSIC(ARM64, IntegerHighestOneBit)
2349UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit)
2350UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit)
2351UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit)
Andreas Gampe878d58c2015-01-15 23:24:00 -08002352
Aart Bik0e54c012016-03-04 12:08:31 -08002353// 1.8.
2354UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt)
2355UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong)
2356UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt)
2357UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong)
2358UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08002359
Aart Bik2f9fcc92016-03-01 15:16:54 -08002360UNREACHABLE_INTRINSICS(ARM64)
Roland Levillain4d027112015-07-01 15:41:14 +01002361
2362#undef __
2363
Andreas Gampe878d58c2015-01-15 23:24:00 -08002364} // namespace arm64
2365} // namespace art