blob: 788c16200d91d824e673bfc5d82f13045cae4658 [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"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080025#include "lock_word.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080026#include "mirror/array-inl.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080027#include "mirror/reference.h"
Vladimir Markoe39f14f2017-02-10 15:44:25 +000028#include "mirror/string-inl.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080029#include "scoped_thread_state_change-inl.h"
30#include "thread-inl.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080031#include "utils/arm64/assembler_arm64.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080032
Scott Wakeling97c72b72016-06-24 16:19:36 +010033using namespace vixl::aarch64; // NOLINT(build/namespaces)
Andreas Gampe878d58c2015-01-15 23:24:00 -080034
Artem Serovaf4e42a2016-08-08 15:11:24 +010035// TODO(VIXL): Make VIXL compile with -Wshadow.
Scott Wakeling97c72b72016-06-24 16:19:36 +010036#pragma GCC diagnostic push
37#pragma GCC diagnostic ignored "-Wshadow"
Artem Serovaf4e42a2016-08-08 15:11:24 +010038#include "aarch64/disasm-aarch64.h"
39#include "aarch64/macro-assembler-aarch64.h"
Scott Wakeling97c72b72016-06-24 16:19:36 +010040#pragma GCC diagnostic pop
Andreas Gampe878d58c2015-01-15 23:24:00 -080041
42namespace art {
43
44namespace arm64 {
45
46using helpers::DRegisterFrom;
47using helpers::FPRegisterFrom;
48using helpers::HeapOperand;
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +000049using helpers::LocationFrom;
Scott Wakeling9ee23f42015-07-23 10:44:35 +010050using helpers::OperandFrom;
Andreas Gampe878d58c2015-01-15 23:24:00 -080051using helpers::RegisterFrom;
52using helpers::SRegisterFrom;
53using helpers::WRegisterFrom;
54using helpers::XRegisterFrom;
xueliang.zhong49924c92016-03-03 10:52:51 +000055using helpers::InputRegisterAt;
Scott Wakeling1f36f412016-04-21 11:13:45 +010056using helpers::OutputRegister;
Andreas Gampe878d58c2015-01-15 23:24:00 -080057
Andreas Gampe878d58c2015-01-15 23:24:00 -080058namespace {
59
60ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) {
61 return MemOperand(XRegisterFrom(location), offset);
62}
63
64} // namespace
65
Scott Wakeling97c72b72016-06-24 16:19:36 +010066MacroAssembler* IntrinsicCodeGeneratorARM64::GetVIXLAssembler() {
Alexandre Rames087930f2016-08-02 13:45:28 +010067 return codegen_->GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -080068}
69
70ArenaAllocator* IntrinsicCodeGeneratorARM64::GetAllocator() {
71 return codegen_->GetGraph()->GetArena();
72}
73
Alexandre Rames087930f2016-08-02 13:45:28 +010074#define __ codegen->GetVIXLAssembler()->
Andreas Gampe878d58c2015-01-15 23:24:00 -080075
76static void MoveFromReturnRegister(Location trg,
77 Primitive::Type type,
78 CodeGeneratorARM64* codegen) {
79 if (!trg.IsValid()) {
80 DCHECK(type == Primitive::kPrimVoid);
81 return;
82 }
83
84 DCHECK_NE(type, Primitive::kPrimVoid);
85
Jeff Hao848f70a2014-01-15 13:49:50 -080086 if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) {
Andreas Gampe878d58c2015-01-15 23:24:00 -080087 Register trg_reg = RegisterFrom(trg, type);
88 Register res_reg = RegisterFrom(ARM64ReturnLocation(type), type);
89 __ Mov(trg_reg, res_reg, kDiscardForSameWReg);
90 } else {
91 FPRegister trg_reg = FPRegisterFrom(trg, type);
92 FPRegister res_reg = FPRegisterFrom(ARM64ReturnLocation(type), type);
93 __ Fmov(trg_reg, res_reg);
94 }
95}
96
Roland Levillainec525fc2015-04-28 15:50:20 +010097static void MoveArguments(HInvoke* invoke, CodeGeneratorARM64* codegen) {
Roland Levillain2d27c8e2015-04-28 15:48:45 +010098 InvokeDexCallingConventionVisitorARM64 calling_convention_visitor;
Roland Levillainec525fc2015-04-28 15:50:20 +010099 IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800100}
101
102// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
103// call. This will copy the arguments into the positions for a regular call.
104//
105// Note: The actual parameters are required to be in the locations given by the invoke's location
106// summary. If an intrinsic modifies those locations before a slowpath call, they must be
107// restored!
108class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 {
109 public:
David Srbecky9cd6d372016-02-09 15:24:47 +0000110 explicit IntrinsicSlowPathARM64(HInvoke* invoke)
111 : SlowPathCodeARM64(invoke), invoke_(invoke) { }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800112
113 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
114 CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
115 __ Bind(GetEntryLabel());
116
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000117 SaveLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800118
Roland Levillainec525fc2015-04-28 15:50:20 +0100119 MoveArguments(invoke_, codegen);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800120
Artem Serov914d7a82017-02-07 14:33:49 +0000121 {
122 // Ensure that between the BLR (emitted by Generate*Call) and RecordPcInfo there
123 // are no pools emitted.
124 vixl::EmissionCheckScope guard(codegen->GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes);
125 if (invoke_->IsInvokeStaticOrDirect()) {
126 codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
127 LocationFrom(kArtMethodRegister));
128 } else {
129 codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister));
130 }
131 codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800132 }
133
134 // Copy the result back to the expected output.
135 Location out = invoke_->GetLocations()->Out();
136 if (out.IsValid()) {
137 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
138 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
139 MoveFromReturnRegister(out, invoke_->GetType(), codegen);
140 }
141
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000142 RestoreLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800143 __ B(GetExitLabel());
144 }
145
Alexandre Rames9931f312015-06-19 14:47:01 +0100146 const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathARM64"; }
147
Andreas Gampe878d58c2015-01-15 23:24:00 -0800148 private:
149 // The instruction where this slow path is happening.
150 HInvoke* const invoke_;
151
152 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM64);
153};
154
Roland Levillain0b671c02016-08-19 12:02:34 +0100155// Slow path implementing the SystemArrayCopy intrinsic copy loop with read barriers.
156class ReadBarrierSystemArrayCopySlowPathARM64 : public SlowPathCodeARM64 {
157 public:
158 ReadBarrierSystemArrayCopySlowPathARM64(HInstruction* instruction, Location tmp)
159 : SlowPathCodeARM64(instruction), tmp_(tmp) {
160 DCHECK(kEmitCompilerReadBarrier);
161 DCHECK(kUseBakerReadBarrier);
162 }
163
164 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
165 CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
166 LocationSummary* locations = instruction_->GetLocations();
167 DCHECK(locations->CanCall());
168 DCHECK(instruction_->IsInvokeStaticOrDirect())
169 << "Unexpected instruction in read barrier arraycopy slow path: "
170 << instruction_->DebugName();
171 DCHECK(instruction_->GetLocations()->Intrinsified());
172 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kSystemArrayCopy);
173
174 const int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
175
176 Register src_curr_addr = XRegisterFrom(locations->GetTemp(0));
177 Register dst_curr_addr = XRegisterFrom(locations->GetTemp(1));
178 Register src_stop_addr = XRegisterFrom(locations->GetTemp(2));
179 Register tmp_reg = WRegisterFrom(tmp_);
180
181 __ Bind(GetEntryLabel());
182 vixl::aarch64::Label slow_copy_loop;
183 __ Bind(&slow_copy_loop);
184 __ Ldr(tmp_reg, MemOperand(src_curr_addr, element_size, PostIndex));
185 codegen->GetAssembler()->MaybeUnpoisonHeapReference(tmp_reg);
186 // TODO: Inline the mark bit check before calling the runtime?
187 // tmp_reg = ReadBarrier::Mark(tmp_reg);
188 // No need to save live registers; it's taken care of by the
189 // entrypoint. Also, there is no need to update the stack mask,
190 // as this runtime call will not trigger a garbage collection.
191 // (See ReadBarrierMarkSlowPathARM64::EmitNativeCode for more
192 // explanations.)
193 DCHECK_NE(tmp_.reg(), LR);
194 DCHECK_NE(tmp_.reg(), WSP);
195 DCHECK_NE(tmp_.reg(), WZR);
196 // IP0 is used internally by the ReadBarrierMarkRegX entry point
197 // as a temporary (and not preserved). It thus cannot be used by
198 // any live register in this slow path.
199 DCHECK_NE(LocationFrom(src_curr_addr).reg(), IP0);
200 DCHECK_NE(LocationFrom(dst_curr_addr).reg(), IP0);
201 DCHECK_NE(LocationFrom(src_stop_addr).reg(), IP0);
202 DCHECK_NE(tmp_.reg(), IP0);
203 DCHECK(0 <= tmp_.reg() && tmp_.reg() < kNumberOfWRegisters) << tmp_.reg();
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000204 // TODO: Load the entrypoint once before the loop, instead of
205 // loading it at every iteration.
Roland Levillain0b671c02016-08-19 12:02:34 +0100206 int32_t entry_point_offset =
207 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(tmp_.reg());
208 // This runtime call does not require a stack map.
209 codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
210 codegen->GetAssembler()->MaybePoisonHeapReference(tmp_reg);
211 __ Str(tmp_reg, MemOperand(dst_curr_addr, element_size, PostIndex));
212 __ Cmp(src_curr_addr, src_stop_addr);
213 __ B(&slow_copy_loop, ne);
214 __ B(GetExitLabel());
215 }
216
217 const char* GetDescription() const OVERRIDE { return "ReadBarrierSystemArrayCopySlowPathARM64"; }
218
219 private:
220 Location tmp_;
221
222 DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathARM64);
223};
Andreas Gampe878d58c2015-01-15 23:24:00 -0800224#undef __
225
226bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) {
227 Dispatch(invoke);
228 LocationSummary* res = invoke->GetLocations();
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000229 if (res == nullptr) {
230 return false;
231 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000232 return res->Intrinsified();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800233}
234
235#define __ masm->
236
237static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
238 LocationSummary* locations = new (arena) LocationSummary(invoke,
239 LocationSummary::kNoCall,
240 kIntrinsified);
241 locations->SetInAt(0, Location::RequiresFpuRegister());
242 locations->SetOut(Location::RequiresRegister());
243}
244
245static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
246 LocationSummary* locations = new (arena) LocationSummary(invoke,
247 LocationSummary::kNoCall,
248 kIntrinsified);
249 locations->SetInAt(0, Location::RequiresRegister());
250 locations->SetOut(Location::RequiresFpuRegister());
251}
252
Scott Wakeling97c72b72016-06-24 16:19:36 +0100253static void MoveFPToInt(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800254 Location input = locations->InAt(0);
255 Location output = locations->Out();
256 __ Fmov(is64bit ? XRegisterFrom(output) : WRegisterFrom(output),
257 is64bit ? DRegisterFrom(input) : SRegisterFrom(input));
258}
259
Scott Wakeling97c72b72016-06-24 16:19:36 +0100260static void MoveIntToFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800261 Location input = locations->InAt(0);
262 Location output = locations->Out();
263 __ Fmov(is64bit ? DRegisterFrom(output) : SRegisterFrom(output),
264 is64bit ? XRegisterFrom(input) : WRegisterFrom(input));
265}
266
267void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
268 CreateFPToIntLocations(arena_, invoke);
269}
270void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
271 CreateIntToFPLocations(arena_, invoke);
272}
273
274void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000275 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800276}
277void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000278 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800279}
280
281void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
282 CreateFPToIntLocations(arena_, invoke);
283}
284void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
285 CreateIntToFPLocations(arena_, invoke);
286}
287
288void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000289 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800290}
291void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000292 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800293}
294
295static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
296 LocationSummary* locations = new (arena) LocationSummary(invoke,
297 LocationSummary::kNoCall,
298 kIntrinsified);
299 locations->SetInAt(0, Location::RequiresRegister());
300 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
301}
302
303static void GenReverseBytes(LocationSummary* locations,
304 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100305 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800306 Location in = locations->InAt(0);
307 Location out = locations->Out();
308
309 switch (type) {
310 case Primitive::kPrimShort:
311 __ Rev16(WRegisterFrom(out), WRegisterFrom(in));
312 __ Sxth(WRegisterFrom(out), WRegisterFrom(out));
313 break;
314 case Primitive::kPrimInt:
315 case Primitive::kPrimLong:
316 __ Rev(RegisterFrom(out, type), RegisterFrom(in, type));
317 break;
318 default:
319 LOG(FATAL) << "Unexpected size for reverse-bytes: " << type;
320 UNREACHABLE();
321 }
322}
323
324void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
325 CreateIntToIntLocations(arena_, invoke);
326}
327
328void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
329 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
330}
331
332void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) {
333 CreateIntToIntLocations(arena_, invoke);
334}
335
336void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) {
337 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
338}
339
340void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) {
341 CreateIntToIntLocations(arena_, invoke);
342}
343
344void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
345 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
346}
347
Aart Bik7b565022016-01-28 14:36:22 -0800348static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
349 LocationSummary* locations = new (arena) LocationSummary(invoke,
350 LocationSummary::kNoCall,
351 kIntrinsified);
352 locations->SetInAt(0, Location::RequiresRegister());
353 locations->SetInAt(1, Location::RequiresRegister());
354 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
355}
356
Scott Wakeling611d3392015-07-10 11:42:06 +0100357static void GenNumberOfLeadingZeros(LocationSummary* locations,
358 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100359 MacroAssembler* masm) {
Scott Wakeling611d3392015-07-10 11:42:06 +0100360 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
361
362 Location in = locations->InAt(0);
363 Location out = locations->Out();
364
365 __ Clz(RegisterFrom(out, type), RegisterFrom(in, type));
366}
367
368void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
369 CreateIntToIntLocations(arena_, invoke);
370}
371
372void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
373 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
374}
375
376void IntrinsicLocationsBuilderARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
377 CreateIntToIntLocations(arena_, invoke);
378}
379
380void IntrinsicCodeGeneratorARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
381 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
382}
383
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100384static void GenNumberOfTrailingZeros(LocationSummary* locations,
385 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100386 MacroAssembler* masm) {
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100387 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
388
389 Location in = locations->InAt(0);
390 Location out = locations->Out();
391
392 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
393 __ Clz(RegisterFrom(out, type), RegisterFrom(out, type));
394}
395
396void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
397 CreateIntToIntLocations(arena_, invoke);
398}
399
400void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
401 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
402}
403
404void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
405 CreateIntToIntLocations(arena_, invoke);
406}
407
408void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
409 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
410}
411
Andreas Gampe878d58c2015-01-15 23:24:00 -0800412static void GenReverse(LocationSummary* locations,
413 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100414 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800415 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
416
417 Location in = locations->InAt(0);
418 Location out = locations->Out();
419
420 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
421}
422
423void IntrinsicLocationsBuilderARM64::VisitIntegerReverse(HInvoke* invoke) {
424 CreateIntToIntLocations(arena_, invoke);
425}
426
427void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) {
428 GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
429}
430
431void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) {
432 CreateIntToIntLocations(arena_, invoke);
433}
434
435void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
436 GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
437}
438
Scott Wakeling97c72b72016-06-24 16:19:36 +0100439static void GenBitCount(HInvoke* instr, Primitive::Type type, MacroAssembler* masm) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100440 DCHECK(Primitive::IsIntOrLongType(type)) << type;
441 DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
442 DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
xueliang.zhong49924c92016-03-03 10:52:51 +0000443
xueliang.zhong49924c92016-03-03 10:52:51 +0000444 UseScratchRegisterScope temps(masm);
445
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000446 Register src = InputRegisterAt(instr, 0);
Roland Levillainfa3912e2016-04-01 18:21:55 +0100447 Register dst = RegisterFrom(instr->GetLocations()->Out(), type);
448 FPRegister fpr = (type == Primitive::kPrimLong) ? temps.AcquireD() : temps.AcquireS();
xueliang.zhong49924c92016-03-03 10:52:51 +0000449
450 __ Fmov(fpr, src);
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000451 __ Cnt(fpr.V8B(), fpr.V8B());
452 __ Addv(fpr.B(), fpr.V8B());
xueliang.zhong49924c92016-03-03 10:52:51 +0000453 __ Fmov(dst, fpr);
454}
455
456void IntrinsicLocationsBuilderARM64::VisitLongBitCount(HInvoke* invoke) {
457 CreateIntToIntLocations(arena_, invoke);
458}
459
460void IntrinsicCodeGeneratorARM64::VisitLongBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100461 GenBitCount(invoke, Primitive::kPrimLong, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000462}
463
464void IntrinsicLocationsBuilderARM64::VisitIntegerBitCount(HInvoke* invoke) {
465 CreateIntToIntLocations(arena_, invoke);
466}
467
468void IntrinsicCodeGeneratorARM64::VisitIntegerBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100469 GenBitCount(invoke, Primitive::kPrimInt, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000470}
471
Andreas Gampe878d58c2015-01-15 23:24:00 -0800472static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800473 LocationSummary* locations = new (arena) LocationSummary(invoke,
474 LocationSummary::kNoCall,
475 kIntrinsified);
476 locations->SetInAt(0, Location::RequiresFpuRegister());
477 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
478}
479
Scott Wakeling97c72b72016-06-24 16:19:36 +0100480static void MathAbsFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800481 Location in = locations->InAt(0);
482 Location out = locations->Out();
483
484 FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in);
485 FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out);
486
487 __ Fabs(out_reg, in_reg);
488}
489
490void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) {
491 CreateFPToFPLocations(arena_, invoke);
492}
493
494void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000495 MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800496}
497
498void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) {
499 CreateFPToFPLocations(arena_, invoke);
500}
501
502void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000503 MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800504}
505
506static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
507 LocationSummary* locations = new (arena) LocationSummary(invoke,
508 LocationSummary::kNoCall,
509 kIntrinsified);
510 locations->SetInAt(0, Location::RequiresRegister());
511 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
512}
513
514static void GenAbsInteger(LocationSummary* locations,
515 bool is64bit,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100516 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800517 Location in = locations->InAt(0);
518 Location output = locations->Out();
519
520 Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in);
521 Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output);
522
523 __ Cmp(in_reg, Operand(0));
524 __ Cneg(out_reg, in_reg, lt);
525}
526
527void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
528 CreateIntToInt(arena_, invoke);
529}
530
531void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000532 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800533}
534
535void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
536 CreateIntToInt(arena_, invoke);
537}
538
539void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000540 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800541}
542
543static void GenMinMaxFP(LocationSummary* locations,
544 bool is_min,
545 bool is_double,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100546 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800547 Location op1 = locations->InAt(0);
548 Location op2 = locations->InAt(1);
549 Location out = locations->Out();
550
551 FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1);
552 FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2);
553 FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out);
554 if (is_min) {
555 __ Fmin(out_reg, op1_reg, op2_reg);
556 } else {
557 __ Fmax(out_reg, op1_reg, op2_reg);
558 }
559}
560
561static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
562 LocationSummary* locations = new (arena) LocationSummary(invoke,
563 LocationSummary::kNoCall,
564 kIntrinsified);
565 locations->SetInAt(0, Location::RequiresFpuRegister());
566 locations->SetInAt(1, Location::RequiresFpuRegister());
567 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
568}
569
570void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
571 CreateFPFPToFPLocations(arena_, invoke);
572}
573
574void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000575 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800576}
577
578void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
579 CreateFPFPToFPLocations(arena_, invoke);
580}
581
582void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000583 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800584}
585
586void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
587 CreateFPFPToFPLocations(arena_, invoke);
588}
589
590void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000591 GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800592}
593
594void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
595 CreateFPFPToFPLocations(arena_, invoke);
596}
597
598void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000599 GenMinMaxFP(
600 invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800601}
602
603static void GenMinMax(LocationSummary* locations,
604 bool is_min,
605 bool is_long,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100606 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800607 Location op1 = locations->InAt(0);
608 Location op2 = locations->InAt(1);
609 Location out = locations->Out();
610
611 Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
612 Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
613 Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
614
615 __ Cmp(op1_reg, op2_reg);
616 __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
617}
618
Andreas Gampe878d58c2015-01-15 23:24:00 -0800619void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
620 CreateIntIntToIntLocations(arena_, invoke);
621}
622
623void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000624 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800625}
626
627void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) {
628 CreateIntIntToIntLocations(arena_, invoke);
629}
630
631void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000632 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800633}
634
635void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) {
636 CreateIntIntToIntLocations(arena_, invoke);
637}
638
639void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000640 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800641}
642
643void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) {
644 CreateIntIntToIntLocations(arena_, invoke);
645}
646
647void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000648 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800649}
650
651void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) {
652 CreateFPToFPLocations(arena_, invoke);
653}
654
655void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) {
656 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100657 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800658 __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
659}
660
661void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) {
662 CreateFPToFPLocations(arena_, invoke);
663}
664
665void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) {
666 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100667 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800668 __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
669}
670
671void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) {
672 CreateFPToFPLocations(arena_, invoke);
673}
674
675void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) {
676 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100677 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800678 __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
679}
680
681void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) {
682 CreateFPToFPLocations(arena_, invoke);
683}
684
685void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) {
686 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100687 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800688 __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
689}
690
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100691static void CreateFPToIntPlusFPTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800692 LocationSummary* locations = new (arena) LocationSummary(invoke,
693 LocationSummary::kNoCall,
694 kIntrinsified);
695 locations->SetInAt(0, Location::RequiresFpuRegister());
696 locations->SetOut(Location::RequiresRegister());
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100697 locations->AddTemp(Location::RequiresFpuRegister());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800698}
699
Scott Wakeling97c72b72016-06-24 16:19:36 +0100700static void GenMathRound(HInvoke* invoke, bool is_double, vixl::aarch64::MacroAssembler* masm) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100701 // Java 8 API definition for Math.round():
702 // Return the closest long or int to the argument, with ties rounding to positive infinity.
703 //
704 // There is no single instruction in ARMv8 that can support the above definition.
705 // We choose to use FCVTAS here, because it has closest semantic.
706 // FCVTAS performs rounding to nearest integer, ties away from zero.
707 // For most inputs (positive values, zero or NaN), this instruction is enough.
708 // We only need a few handling code after FCVTAS if the input is negative half value.
709 //
710 // The reason why we didn't choose FCVTPS instruction here is that
711 // although it performs rounding toward positive infinity, it doesn't perform rounding to nearest.
712 // For example, FCVTPS(-1.9) = -1 and FCVTPS(1.1) = 2.
713 // If we were using this instruction, for most inputs, more handling code would be needed.
714 LocationSummary* l = invoke->GetLocations();
715 FPRegister in_reg = is_double ? DRegisterFrom(l->InAt(0)) : SRegisterFrom(l->InAt(0));
716 FPRegister tmp_fp = is_double ? DRegisterFrom(l->GetTemp(0)) : SRegisterFrom(l->GetTemp(0));
717 Register out_reg = is_double ? XRegisterFrom(l->Out()) : WRegisterFrom(l->Out());
Scott Wakeling97c72b72016-06-24 16:19:36 +0100718 vixl::aarch64::Label done;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800719
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100720 // Round to nearest integer, ties away from zero.
721 __ Fcvtas(out_reg, in_reg);
722
723 // For positive values, zero or NaN inputs, rounding is done.
Scott Wakeling97c72b72016-06-24 16:19:36 +0100724 __ Tbz(out_reg, out_reg.GetSizeInBits() - 1, &done);
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100725
726 // Handle input < 0 cases.
727 // If input is negative but not a tie, previous result (round to nearest) is valid.
728 // If input is a negative tie, out_reg += 1.
729 __ Frinta(tmp_fp, in_reg);
730 __ Fsub(tmp_fp, in_reg, tmp_fp);
731 __ Fcmp(tmp_fp, 0.5);
732 __ Cinc(out_reg, out_reg, eq);
733
734 __ Bind(&done);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800735}
736
737void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100738 CreateFPToIntPlusFPTempLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800739}
740
741void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100742 GenMathRound(invoke, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800743}
744
745void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100746 CreateFPToIntPlusFPTempLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800747}
748
749void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100750 GenMathRound(invoke, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800751}
752
753void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) {
754 CreateIntToIntLocations(arena_, invoke);
755}
756
757void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100758 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800759 __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()),
760 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
761}
762
763void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
764 CreateIntToIntLocations(arena_, invoke);
765}
766
767void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100768 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800769 __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()),
770 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
771}
772
773void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
774 CreateIntToIntLocations(arena_, invoke);
775}
776
777void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100778 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800779 __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()),
780 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
781}
782
783void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
784 CreateIntToIntLocations(arena_, invoke);
785}
786
787void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100788 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800789 __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()),
790 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
791}
792
793static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
794 LocationSummary* locations = new (arena) LocationSummary(invoke,
795 LocationSummary::kNoCall,
796 kIntrinsified);
797 locations->SetInAt(0, Location::RequiresRegister());
798 locations->SetInAt(1, Location::RequiresRegister());
799}
800
801void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) {
802 CreateIntIntToVoidLocations(arena_, invoke);
803}
804
805void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100806 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800807 __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)),
808 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
809}
810
811void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
812 CreateIntIntToVoidLocations(arena_, invoke);
813}
814
815void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100816 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800817 __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)),
818 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
819}
820
821void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
822 CreateIntIntToVoidLocations(arena_, invoke);
823}
824
825void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100826 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800827 __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)),
828 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
829}
830
831void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
832 CreateIntIntToVoidLocations(arena_, invoke);
833}
834
835void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100836 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800837 __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)),
838 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
839}
840
841void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) {
842 LocationSummary* locations = new (arena_) LocationSummary(invoke,
843 LocationSummary::kNoCall,
844 kIntrinsified);
845 locations->SetOut(Location::RequiresRegister());
846}
847
848void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) {
849 codegen_->Load(Primitive::kPrimNot, WRegisterFrom(invoke->GetLocations()->Out()),
Andreas Gampe542451c2016-07-26 09:02:02 -0700850 MemOperand(tr, Thread::PeerOffset<kArm64PointerSize>().Int32Value()));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800851}
852
853static void GenUnsafeGet(HInvoke* invoke,
854 Primitive::Type type,
855 bool is_volatile,
856 CodeGeneratorARM64* codegen) {
857 LocationSummary* locations = invoke->GetLocations();
858 DCHECK((type == Primitive::kPrimInt) ||
859 (type == Primitive::kPrimLong) ||
860 (type == Primitive::kPrimNot));
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000861 Location base_loc = locations->InAt(1);
862 Register base = WRegisterFrom(base_loc); // Object pointer.
863 Location offset_loc = locations->InAt(2);
864 Register offset = XRegisterFrom(offset_loc); // Long offset.
865 Location trg_loc = locations->Out();
866 Register trg = RegisterFrom(trg_loc, type);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800867
Roland Levillain44015862016-01-22 11:47:17 +0000868 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
869 // UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case.
Roland Levillain54f869e2017-03-06 13:54:11 +0000870 Register temp = WRegisterFrom(locations->GetTemp(0));
Roland Levillainbfea3352016-06-23 13:48:47 +0100871 codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke,
872 trg_loc,
873 base,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100874 /* offset */ 0u,
Roland Levillainbfea3352016-06-23 13:48:47 +0100875 /* index */ offset_loc,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100876 /* scale_factor */ 0u,
Roland Levillainbfea3352016-06-23 13:48:47 +0100877 temp,
878 /* needs_null_check */ false,
879 is_volatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800880 } else {
Roland Levillain44015862016-01-22 11:47:17 +0000881 // Other cases.
882 MemOperand mem_op(base.X(), offset);
883 if (is_volatile) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +0000884 codegen->LoadAcquire(invoke, trg, mem_op, /* needs_null_check */ true);
Roland Levillain44015862016-01-22 11:47:17 +0000885 } else {
886 codegen->Load(type, trg, mem_op);
887 }
Roland Levillain4d027112015-07-01 15:41:14 +0100888
Roland Levillain44015862016-01-22 11:47:17 +0000889 if (type == Primitive::kPrimNot) {
890 DCHECK(trg.IsW());
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100891 codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0u, offset_loc);
Roland Levillain44015862016-01-22 11:47:17 +0000892 }
Roland Levillain4d027112015-07-01 15:41:14 +0100893 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800894}
895
896static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000897 bool can_call = kEmitCompilerReadBarrier &&
898 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
899 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800900 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100901 (can_call
902 ? LocationSummary::kCallOnSlowPath
903 : LocationSummary::kNoCall),
Andreas Gampe878d58c2015-01-15 23:24:00 -0800904 kIntrinsified);
Vladimir Marko70e97462016-08-09 11:04:26 +0100905 if (can_call && kUseBakerReadBarrier) {
Vladimir Marko804b03f2016-09-14 16:26:36 +0100906 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Roland Levillain54f869e2017-03-06 13:54:11 +0000907 // We need a temporary register for the read barrier marking slow
908 // path in CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier.
909 locations->AddTemp(Location::RequiresRegister());
Vladimir Marko70e97462016-08-09 11:04:26 +0100910 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800911 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
912 locations->SetInAt(1, Location::RequiresRegister());
913 locations->SetInAt(2, Location::RequiresRegister());
Roland Levillainbfea3352016-06-23 13:48:47 +0100914 locations->SetOut(Location::RequiresRegister(),
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100915 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800916}
917
918void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
919 CreateIntIntIntToIntLocations(arena_, invoke);
920}
921void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
922 CreateIntIntIntToIntLocations(arena_, invoke);
923}
924void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
925 CreateIntIntIntToIntLocations(arena_, invoke);
926}
927void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
928 CreateIntIntIntToIntLocations(arena_, invoke);
929}
930void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
931 CreateIntIntIntToIntLocations(arena_, invoke);
932}
933void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
934 CreateIntIntIntToIntLocations(arena_, invoke);
935}
936
937void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000938 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800939}
940void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000941 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800942}
943void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000944 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800945}
946void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000947 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800948}
949void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000950 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800951}
952void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000953 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800954}
955
956static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
957 LocationSummary* locations = new (arena) LocationSummary(invoke,
958 LocationSummary::kNoCall,
959 kIntrinsified);
960 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
961 locations->SetInAt(1, Location::RequiresRegister());
962 locations->SetInAt(2, Location::RequiresRegister());
963 locations->SetInAt(3, Location::RequiresRegister());
964}
965
966void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
967 CreateIntIntIntIntToVoid(arena_, invoke);
968}
969void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
970 CreateIntIntIntIntToVoid(arena_, invoke);
971}
972void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
973 CreateIntIntIntIntToVoid(arena_, invoke);
974}
975void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
976 CreateIntIntIntIntToVoid(arena_, invoke);
977}
978void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
979 CreateIntIntIntIntToVoid(arena_, invoke);
980}
981void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
982 CreateIntIntIntIntToVoid(arena_, invoke);
983}
984void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
985 CreateIntIntIntIntToVoid(arena_, invoke);
986}
987void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
988 CreateIntIntIntIntToVoid(arena_, invoke);
989}
990void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
991 CreateIntIntIntIntToVoid(arena_, invoke);
992}
993
Artem Serov914d7a82017-02-07 14:33:49 +0000994static void GenUnsafePut(HInvoke* invoke,
Andreas Gampe878d58c2015-01-15 23:24:00 -0800995 Primitive::Type type,
996 bool is_volatile,
997 bool is_ordered,
998 CodeGeneratorARM64* codegen) {
Artem Serov914d7a82017-02-07 14:33:49 +0000999 LocationSummary* locations = invoke->GetLocations();
Alexandre Rames087930f2016-08-02 13:45:28 +01001000 MacroAssembler* masm = codegen->GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -08001001
1002 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
1003 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
1004 Register value = RegisterFrom(locations->InAt(3), type);
Roland Levillain4d027112015-07-01 15:41:14 +01001005 Register source = value;
Andreas Gampe878d58c2015-01-15 23:24:00 -08001006 MemOperand mem_op(base.X(), offset);
1007
Roland Levillain4d027112015-07-01 15:41:14 +01001008 {
1009 // We use a block to end the scratch scope before the write barrier, thus
1010 // freeing the temporary registers so they can be used in `MarkGCCard`.
1011 UseScratchRegisterScope temps(masm);
1012
1013 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1014 DCHECK(value.IsW());
1015 Register temp = temps.AcquireW();
1016 __ Mov(temp.W(), value.W());
1017 codegen->GetAssembler()->PoisonHeapReference(temp.W());
1018 source = temp;
Andreas Gampe878d58c2015-01-15 23:24:00 -08001019 }
Roland Levillain4d027112015-07-01 15:41:14 +01001020
1021 if (is_volatile || is_ordered) {
Artem Serov914d7a82017-02-07 14:33:49 +00001022 codegen->StoreRelease(invoke, type, source, mem_op, /* needs_null_check */ false);
Roland Levillain4d027112015-07-01 15:41:14 +01001023 } else {
1024 codegen->Store(type, source, mem_op);
1025 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001026 }
1027
1028 if (type == Primitive::kPrimNot) {
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001029 bool value_can_be_null = true; // TODO: Worth finding out this information?
1030 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001031 }
1032}
1033
1034void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001035 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001036 Primitive::kPrimInt,
1037 /* is_volatile */ false,
1038 /* is_ordered */ false,
1039 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001040}
1041void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001042 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001043 Primitive::kPrimInt,
1044 /* is_volatile */ false,
1045 /* is_ordered */ true,
1046 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001047}
1048void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001049 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001050 Primitive::kPrimInt,
1051 /* is_volatile */ true,
1052 /* is_ordered */ false,
1053 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001054}
1055void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001056 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001057 Primitive::kPrimNot,
1058 /* is_volatile */ false,
1059 /* is_ordered */ false,
1060 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001061}
1062void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001063 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001064 Primitive::kPrimNot,
1065 /* is_volatile */ false,
1066 /* is_ordered */ true,
1067 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001068}
1069void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001070 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001071 Primitive::kPrimNot,
1072 /* is_volatile */ true,
1073 /* is_ordered */ false,
1074 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001075}
1076void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001077 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001078 Primitive::kPrimLong,
1079 /* is_volatile */ false,
1080 /* is_ordered */ false,
1081 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001082}
1083void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001084 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001085 Primitive::kPrimLong,
1086 /* is_volatile */ false,
1087 /* is_ordered */ true,
1088 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001089}
1090void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001091 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001092 Primitive::kPrimLong,
1093 /* is_volatile */ true,
1094 /* is_ordered */ false,
1095 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001096}
1097
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001098static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
1099 HInvoke* invoke,
1100 Primitive::Type type) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001101 bool can_call = kEmitCompilerReadBarrier &&
1102 kUseBakerReadBarrier &&
1103 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001104 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001105 (can_call
1106 ? LocationSummary::kCallOnSlowPath
1107 : LocationSummary::kNoCall),
Andreas Gampe878d58c2015-01-15 23:24:00 -08001108 kIntrinsified);
1109 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1110 locations->SetInAt(1, Location::RequiresRegister());
1111 locations->SetInAt(2, Location::RequiresRegister());
1112 locations->SetInAt(3, Location::RequiresRegister());
1113 locations->SetInAt(4, Location::RequiresRegister());
1114
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001115 // If heap poisoning is enabled, we don't want the unpoisoning
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001116 // operations to potentially clobber the output. Likewise when
1117 // emitting a (Baker) read barrier, which may call.
1118 Location::OutputOverlap overlaps =
1119 ((kPoisonHeapReferences && type == Primitive::kPrimNot) || can_call)
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001120 ? Location::kOutputOverlap
1121 : Location::kNoOutputOverlap;
1122 locations->SetOut(Location::RequiresRegister(), overlaps);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001123 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1124 // Temporary register for (Baker) read barrier.
1125 locations->AddTemp(Location::RequiresRegister());
1126 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001127}
1128
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001129static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARM64* codegen) {
Alexandre Rames087930f2016-08-02 13:45:28 +01001130 MacroAssembler* masm = codegen->GetVIXLAssembler();
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001131 LocationSummary* locations = invoke->GetLocations();
Andreas Gampe878d58c2015-01-15 23:24:00 -08001132
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001133 Location out_loc = locations->Out();
1134 Register out = WRegisterFrom(out_loc); // Boolean result.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001135
1136 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001137 Location offset_loc = locations->InAt(2);
1138 Register offset = XRegisterFrom(offset_loc); // Long offset.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001139 Register expected = RegisterFrom(locations->InAt(3), type); // Expected.
1140 Register value = RegisterFrom(locations->InAt(4), type); // Value.
1141
1142 // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
1143 if (type == Primitive::kPrimNot) {
1144 // Mark card for object assuming new value is stored.
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001145 bool value_can_be_null = true; // TODO: Worth finding out this information?
1146 codegen->MarkGCCard(base, value, value_can_be_null);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001147
1148 // The only read barrier implementation supporting the
1149 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1150 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1151
1152 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1153 Register temp = WRegisterFrom(locations->GetTemp(0));
1154 // Need to make sure the reference stored in the field is a to-space
1155 // one before attempting the CAS or the CAS could fail incorrectly.
1156 codegen->GenerateReferenceLoadWithBakerReadBarrier(
1157 invoke,
1158 out_loc, // Unused, used only as a "temporary" within the read barrier.
1159 base,
1160 /* offset */ 0u,
1161 /* index */ offset_loc,
1162 /* scale_factor */ 0u,
1163 temp,
1164 /* needs_null_check */ false,
1165 /* use_load_acquire */ false,
1166 /* always_update_field */ true);
1167 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001168 }
1169
1170 UseScratchRegisterScope temps(masm);
1171 Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
1172 Register tmp_value = temps.AcquireSameSizeAs(value); // Value in memory.
1173
1174 Register tmp_32 = tmp_value.W();
1175
1176 __ Add(tmp_ptr, base.X(), Operand(offset));
1177
Roland Levillain4d027112015-07-01 15:41:14 +01001178 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1179 codegen->GetAssembler()->PoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001180 if (value.Is(expected)) {
1181 // Do not poison `value`, as it is the same register as
1182 // `expected`, which has just been poisoned.
1183 } else {
1184 codegen->GetAssembler()->PoisonHeapReference(value);
1185 }
Roland Levillain4d027112015-07-01 15:41:14 +01001186 }
1187
Andreas Gampe878d58c2015-01-15 23:24:00 -08001188 // do {
1189 // tmp_value = [tmp_ptr] - expected;
1190 // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1191 // result = tmp_value != 0;
1192
Scott Wakeling97c72b72016-06-24 16:19:36 +01001193 vixl::aarch64::Label loop_head, exit_loop;
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001194 __ Bind(&loop_head);
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001195 __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
1196 __ Cmp(tmp_value, expected);
1197 __ B(&exit_loop, ne);
1198 __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
1199 __ Cbnz(tmp_32, &loop_head);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001200 __ Bind(&exit_loop);
1201 __ Cset(out, eq);
Roland Levillain4d027112015-07-01 15:41:14 +01001202
1203 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillain4d027112015-07-01 15:41:14 +01001204 codegen->GetAssembler()->UnpoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001205 if (value.Is(expected)) {
1206 // Do not unpoison `value`, as it is the same register as
1207 // `expected`, which has just been unpoisoned.
1208 } else {
1209 codegen->GetAssembler()->UnpoisonHeapReference(value);
1210 }
Roland Levillain4d027112015-07-01 15:41:14 +01001211 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001212}
1213
1214void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001215 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001216}
1217void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001218 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001219}
1220void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001221 // The only read barrier implementation supporting the
1222 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1223 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain985ff702015-10-23 13:25:35 +01001224 return;
1225 }
1226
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001227 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001228}
1229
1230void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001231 GenCas(invoke, Primitive::kPrimInt, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001232}
1233void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001234 GenCas(invoke, Primitive::kPrimLong, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001235}
1236void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001237 // The only read barrier implementation supporting the
1238 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1239 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01001240
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001241 GenCas(invoke, Primitive::kPrimNot, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001242}
1243
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001244void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001245 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Scott Wakeling1f36f412016-04-21 11:13:45 +01001246 invoke->InputAt(1)->CanBeNull()
1247 ? LocationSummary::kCallOnSlowPath
1248 : LocationSummary::kNoCall,
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001249 kIntrinsified);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001250 locations->SetInAt(0, Location::RequiresRegister());
1251 locations->SetInAt(1, Location::RequiresRegister());
1252 locations->AddTemp(Location::RequiresRegister());
1253 locations->AddTemp(Location::RequiresRegister());
1254 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001255 // Need temporary registers for String compression's feature.
1256 if (mirror::kUseStringCompression) {
1257 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001258 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001259 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001260}
1261
1262void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001263 MacroAssembler* masm = GetVIXLAssembler();
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001264 LocationSummary* locations = invoke->GetLocations();
1265
Alexandre Rames2ea91532016-08-11 17:04:14 +01001266 Register str = InputRegisterAt(invoke, 0);
1267 Register arg = InputRegisterAt(invoke, 1);
1268 DCHECK(str.IsW());
1269 DCHECK(arg.IsW());
Scott Wakeling1f36f412016-04-21 11:13:45 +01001270 Register out = OutputRegister(invoke);
1271
1272 Register temp0 = WRegisterFrom(locations->GetTemp(0));
1273 Register temp1 = WRegisterFrom(locations->GetTemp(1));
1274 Register temp2 = WRegisterFrom(locations->GetTemp(2));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001275 Register temp3;
jessicahandojo05765752016-09-09 19:01:32 -07001276 if (mirror::kUseStringCompression) {
1277 temp3 = WRegisterFrom(locations->GetTemp(3));
jessicahandojo05765752016-09-09 19:01:32 -07001278 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001279
Scott Wakeling97c72b72016-06-24 16:19:36 +01001280 vixl::aarch64::Label loop;
1281 vixl::aarch64::Label find_char_diff;
1282 vixl::aarch64::Label end;
jessicahandojo05765752016-09-09 19:01:32 -07001283 vixl::aarch64::Label different_compression;
Scott Wakeling1f36f412016-04-21 11:13:45 +01001284
1285 // Get offsets of count and value fields within a string object.
1286 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1287 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1288
Nicolas Geoffray512e04d2015-03-27 17:21:24 +00001289 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +01001290 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001291
Scott Wakeling1f36f412016-04-21 11:13:45 +01001292 // Take slow path and throw if input can be and is null.
1293 SlowPathCodeARM64* slow_path = nullptr;
1294 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1295 if (can_slow_path) {
1296 slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1297 codegen_->AddSlowPath(slow_path);
1298 __ Cbz(arg, slow_path->GetEntryLabel());
1299 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001300
Scott Wakeling1f36f412016-04-21 11:13:45 +01001301 // Reference equality check, return 0 if same reference.
1302 __ Subs(out, str, arg);
1303 __ B(&end, eq);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001304
jessicahandojo05765752016-09-09 19:01:32 -07001305 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001306 // Load `count` fields of this and argument strings.
jessicahandojo05765752016-09-09 19:01:32 -07001307 __ Ldr(temp3, HeapOperand(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001308 __ Ldr(temp2, HeapOperand(arg, count_offset));
jessicahandojo05765752016-09-09 19:01:32 -07001309 // Clean out compression flag from lengths.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001310 __ Lsr(temp0, temp3, 1u);
1311 __ Lsr(temp1, temp2, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001312 } else {
1313 // Load lengths of this and argument strings.
1314 __ Ldr(temp0, HeapOperand(str, count_offset));
1315 __ Ldr(temp1, HeapOperand(arg, count_offset));
1316 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001317 // out = length diff.
1318 __ Subs(out, temp0, temp1);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001319 // temp0 = min(len(str), len(arg)).
1320 __ Csel(temp0, temp1, temp0, ge);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001321 // Shorter string is empty?
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001322 __ Cbz(temp0, &end);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001323
jessicahandojo05765752016-09-09 19:01:32 -07001324 if (mirror::kUseStringCompression) {
1325 // Check if both strings using same compression style to use this comparison loop.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001326 __ Eor(temp2, temp2, Operand(temp3));
1327 // Interleave with compression flag extraction which is needed for both paths
1328 // and also set flags which is needed only for the different compressions path.
1329 __ Ands(temp3.W(), temp3.W(), Operand(1));
1330 __ Tbnz(temp2, 0, &different_compression); // Does not use flags.
jessicahandojo05765752016-09-09 19:01:32 -07001331 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001332 // Store offset of string value in preparation for comparison loop.
1333 __ Mov(temp1, value_offset);
jessicahandojo05765752016-09-09 19:01:32 -07001334 if (mirror::kUseStringCompression) {
1335 // For string compression, calculate the number of bytes to compare (not chars).
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001336 // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
1337 __ Lsl(temp0, temp0, temp3);
jessicahandojo05765752016-09-09 19:01:32 -07001338 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001339
1340 UseScratchRegisterScope scratch_scope(masm);
1341 Register temp4 = scratch_scope.AcquireX();
1342
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001343 // Assertions that must hold in order to compare strings 8 bytes at a time.
Scott Wakeling1f36f412016-04-21 11:13:45 +01001344 DCHECK_ALIGNED(value_offset, 8);
1345 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1346
1347 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1348 DCHECK_EQ(char_size, 2u);
1349
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001350 // Promote temp2 to an X reg, ready for LDR.
1351 temp2 = temp2.X();
Scott Wakeling1f36f412016-04-21 11:13:45 +01001352
1353 // Loop to compare 4x16-bit characters at a time (ok because of string data alignment).
1354 __ Bind(&loop);
Alexandre Rames2ea91532016-08-11 17:04:14 +01001355 __ Ldr(temp4, MemOperand(str.X(), temp1.X()));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001356 __ Ldr(temp2, MemOperand(arg.X(), temp1.X()));
1357 __ Cmp(temp4, temp2);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001358 __ B(ne, &find_char_diff);
1359 __ Add(temp1, temp1, char_size * 4);
jessicahandojo05765752016-09-09 19:01:32 -07001360 // With string compression, we have compared 8 bytes, otherwise 4 chars.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001361 __ Subs(temp0, temp0, (mirror::kUseStringCompression) ? 8 : 4);
1362 __ B(&loop, hi);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001363 __ B(&end);
1364
1365 // Promote temp1 to an X reg, ready for EOR.
1366 temp1 = temp1.X();
1367
jessicahandojo05765752016-09-09 19:01:32 -07001368 // Find the single character difference.
Scott Wakeling1f36f412016-04-21 11:13:45 +01001369 __ Bind(&find_char_diff);
1370 // Get the bit position of the first character that differs.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001371 __ Eor(temp1, temp2, temp4);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001372 __ Rbit(temp1, temp1);
1373 __ Clz(temp1, temp1);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001374
jessicahandojo05765752016-09-09 19:01:32 -07001375 // If the number of chars remaining <= the index where the difference occurs (0-3), then
Scott Wakeling1f36f412016-04-21 11:13:45 +01001376 // the difference occurs outside the remaining string data, so just return length diff (out).
jessicahandojo05765752016-09-09 19:01:32 -07001377 // Unlike ARM, we're doing the comparison in one go here, without the subtraction at the
1378 // find_char_diff_2nd_cmp path, so it doesn't matter whether the comparison is signed or
1379 // unsigned when string compression is disabled.
1380 // When it's enabled, the comparison must be unsigned.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001381 __ Cmp(temp0, Operand(temp1.W(), LSR, (mirror::kUseStringCompression) ? 3 : 4));
jessicahandojo05765752016-09-09 19:01:32 -07001382 __ B(ls, &end);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001383
Scott Wakeling1f36f412016-04-21 11:13:45 +01001384 // Extract the characters and calculate the difference.
jessicahandojo05765752016-09-09 19:01:32 -07001385 if (mirror:: kUseStringCompression) {
jessicahandojo05765752016-09-09 19:01:32 -07001386 __ Bic(temp1, temp1, 0x7);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001387 __ Bic(temp1, temp1, Operand(temp3.X(), LSL, 3u));
1388 } else {
1389 __ Bic(temp1, temp1, 0xf);
jessicahandojo05765752016-09-09 19:01:32 -07001390 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001391 __ Lsr(temp2, temp2, temp1);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001392 __ Lsr(temp4, temp4, temp1);
jessicahandojo05765752016-09-09 19:01:32 -07001393 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001394 // Prioritize the case of compressed strings and calculate such result first.
1395 __ Uxtb(temp1, temp4);
1396 __ Sub(out, temp1.W(), Operand(temp2.W(), UXTB));
1397 __ Tbz(temp3, 0u, &end); // If actually compressed, we're done.
jessicahandojo05765752016-09-09 19:01:32 -07001398 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001399 __ Uxth(temp4, temp4);
1400 __ Sub(out, temp4.W(), Operand(temp2.W(), UXTH));
jessicahandojo05765752016-09-09 19:01:32 -07001401
1402 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001403 __ B(&end);
1404 __ Bind(&different_compression);
1405
1406 // Comparison for different compression style.
jessicahandojo05765752016-09-09 19:01:32 -07001407 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
1408 DCHECK_EQ(c_char_size, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001409 temp1 = temp1.W();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001410 temp2 = temp2.W();
1411 temp4 = temp4.W();
jessicahandojo05765752016-09-09 19:01:32 -07001412
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001413 // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
1414 // Note that flags have been set by the `str` compression flag extraction to `temp3`
1415 // before branching to the `different_compression` label.
1416 __ Csel(temp1, str, arg, eq); // Pointer to the compressed string.
1417 __ Csel(temp2, str, arg, ne); // Pointer to the uncompressed string.
jessicahandojo05765752016-09-09 19:01:32 -07001418
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001419 // We want to free up the temp3, currently holding `str` compression flag, for comparison.
1420 // So, we move it to the bottom bit of the iteration count `temp0` which we then need to treat
1421 // as unsigned. Start by freeing the bit with a LSL and continue further down by a SUB which
1422 // will allow `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
1423 __ Lsl(temp0, temp0, 1u);
1424
1425 // Adjust temp1 and temp2 from string pointers to data pointers.
1426 __ Add(temp1, temp1, Operand(value_offset));
1427 __ Add(temp2, temp2, Operand(value_offset));
1428
1429 // Complete the move of the compression flag.
1430 __ Sub(temp0, temp0, Operand(temp3));
1431
1432 vixl::aarch64::Label different_compression_loop;
1433 vixl::aarch64::Label different_compression_diff;
1434
1435 __ Bind(&different_compression_loop);
1436 __ Ldrb(temp4, MemOperand(temp1.X(), c_char_size, PostIndex));
1437 __ Ldrh(temp3, MemOperand(temp2.X(), char_size, PostIndex));
1438 __ Subs(temp4, temp4, Operand(temp3));
1439 __ B(&different_compression_diff, ne);
1440 __ Subs(temp0, temp0, 2);
1441 __ B(&different_compression_loop, hi);
jessicahandojo05765752016-09-09 19:01:32 -07001442 __ B(&end);
1443
1444 // Calculate the difference.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001445 __ Bind(&different_compression_diff);
1446 __ Tst(temp0, Operand(1));
1447 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1448 "Expecting 0=compressed, 1=uncompressed");
1449 __ Cneg(out, temp4, ne);
jessicahandojo05765752016-09-09 19:01:32 -07001450 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001451
1452 __ Bind(&end);
1453
1454 if (can_slow_path) {
1455 __ Bind(slow_path->GetExitLabel());
1456 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001457}
1458
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001459// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
1460// The normal loop plus the pre-header is 9 instructions without string compression and 12
1461// instructions with string compression. We can compare up to 8 bytes in 4 instructions
1462// (LDR+LDR+CMP+BNE) and up to 16 bytes in 5 instructions (LDP+LDP+CMP+CCMP+BNE). Allow up
1463// to 10 instructions for the unrolled loop.
1464constexpr size_t kShortConstStringEqualsCutoffInBytes = 32;
1465
1466static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
1467 if (candidate->IsLoadString()) {
1468 HLoadString* load_string = candidate->AsLoadString();
1469 const DexFile& dex_file = load_string->GetDexFile();
1470 return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
1471 }
1472 return nullptr;
1473}
1474
Agi Csakiea34b402015-08-13 17:51:19 -07001475void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
1476 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1477 LocationSummary::kNoCall,
1478 kIntrinsified);
1479 locations->SetInAt(0, Location::RequiresRegister());
1480 locations->SetInAt(1, Location::RequiresRegister());
Agi Csakiea34b402015-08-13 17:51:19 -07001481
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001482 // For the generic implementation and for long const strings we need a temporary.
1483 // We do not need it for short const strings, up to 8 bytes, see code generation below.
1484 uint32_t const_string_length = 0u;
1485 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1486 if (const_string == nullptr) {
1487 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1488 }
1489 bool is_compressed =
1490 mirror::kUseStringCompression &&
1491 const_string != nullptr &&
1492 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1493 if (const_string == nullptr || const_string_length > (is_compressed ? 8u : 4u)) {
1494 locations->AddTemp(Location::RequiresRegister());
1495 }
1496
1497 // TODO: If the String.equals() is used only for an immediately following HIf, we can
1498 // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
1499 // Then we shall need an extra temporary register instead of the output register.
Agi Csakiea34b402015-08-13 17:51:19 -07001500 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1501}
1502
1503void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001504 MacroAssembler* masm = GetVIXLAssembler();
Agi Csakiea34b402015-08-13 17:51:19 -07001505 LocationSummary* locations = invoke->GetLocations();
1506
1507 Register str = WRegisterFrom(locations->InAt(0));
1508 Register arg = WRegisterFrom(locations->InAt(1));
1509 Register out = XRegisterFrom(locations->Out());
1510
1511 UseScratchRegisterScope scratch_scope(masm);
1512 Register temp = scratch_scope.AcquireW();
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001513 Register temp1 = scratch_scope.AcquireW();
Agi Csakiea34b402015-08-13 17:51:19 -07001514
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001515 vixl::aarch64::Label loop;
Scott Wakeling97c72b72016-06-24 16:19:36 +01001516 vixl::aarch64::Label end;
1517 vixl::aarch64::Label return_true;
1518 vixl::aarch64::Label return_false;
Agi Csakiea34b402015-08-13 17:51:19 -07001519
1520 // Get offsets of count, value, and class fields within a string object.
1521 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1522 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1523 const int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1524
1525 // Note that the null check must have been done earlier.
1526 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1527
Vladimir Marko53b52002016-05-24 19:30:45 +01001528 StringEqualsOptimizations optimizations(invoke);
1529 if (!optimizations.GetArgumentNotNull()) {
1530 // Check if input is null, return false if it is.
1531 __ Cbz(arg, &return_false);
1532 }
Agi Csakiea34b402015-08-13 17:51:19 -07001533
1534 // Reference equality check, return true if same reference.
1535 __ Cmp(str, arg);
1536 __ B(&return_true, eq);
1537
Vladimir Marko53b52002016-05-24 19:30:45 +01001538 if (!optimizations.GetArgumentIsString()) {
1539 // Instanceof check for the argument by comparing class fields.
1540 // All string objects must have the same type since String cannot be subclassed.
1541 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1542 // If the argument is a string object, its class field must be equal to receiver's class field.
1543 __ Ldr(temp, MemOperand(str.X(), class_offset));
1544 __ Ldr(temp1, MemOperand(arg.X(), class_offset));
1545 __ Cmp(temp, temp1);
1546 __ B(&return_false, ne);
1547 }
Agi Csakiea34b402015-08-13 17:51:19 -07001548
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001549 // Check if one of the inputs is a const string. Do not special-case both strings
1550 // being const, such cases should be handled by constant folding if needed.
1551 uint32_t const_string_length = 0u;
1552 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1553 if (const_string == nullptr) {
1554 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1555 if (const_string != nullptr) {
1556 std::swap(str, arg); // Make sure the const string is in `str`.
1557 }
1558 }
1559 bool is_compressed =
1560 mirror::kUseStringCompression &&
1561 const_string != nullptr &&
1562 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1563
1564 if (const_string != nullptr) {
1565 // Load `count` field of the argument string and check if it matches the const string.
1566 // Also compares the compression style, if differs return false.
1567 __ Ldr(temp, MemOperand(arg.X(), count_offset));
Vladimir Marko26ec3ca2017-03-14 13:37:14 +00001568 // Temporarily release temp1 as we may not be able to embed the flagged count in CMP immediate.
1569 scratch_scope.Release(temp1);
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001570 __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
Vladimir Marko26ec3ca2017-03-14 13:37:14 +00001571 temp1 = scratch_scope.AcquireW();
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001572 __ B(&return_false, ne);
1573 } else {
1574 // Load `count` fields of this and argument strings.
1575 __ Ldr(temp, MemOperand(str.X(), count_offset));
1576 __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1577 // Check if `count` fields are equal, return false if they're not.
1578 // Also compares the compression style, if differs return false.
1579 __ Cmp(temp, temp1);
1580 __ B(&return_false, ne);
1581 }
Agi Csakiea34b402015-08-13 17:51:19 -07001582
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001583 // Assertions that must hold in order to compare strings 8 bytes at a time.
Agi Csakiea34b402015-08-13 17:51:19 -07001584 DCHECK_ALIGNED(value_offset, 8);
1585 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1586
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001587 if (const_string != nullptr &&
1588 const_string_length < (is_compressed ? kShortConstStringEqualsCutoffInBytes
1589 : kShortConstStringEqualsCutoffInBytes / 2u)) {
1590 // Load and compare the contents. Though we know the contents of the short const string
1591 // at compile time, materializing constants may be more code than loading from memory.
1592 int32_t offset = value_offset;
1593 size_t remaining_bytes =
1594 RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u);
1595 temp = temp.X();
1596 temp1 = temp1.X();
1597 while (remaining_bytes > 8u) {
1598 Register temp2 = XRegisterFrom(locations->GetTemp(0));
1599 __ Ldp(temp, temp1, MemOperand(str.X(), offset));
1600 __ Ldp(temp2, out, MemOperand(arg.X(), offset));
1601 __ Cmp(temp, temp2);
1602 __ Ccmp(temp1, out, NoFlag, eq);
1603 __ B(&return_false, ne);
1604 offset += 2u * sizeof(uint64_t);
1605 remaining_bytes -= 2u * sizeof(uint64_t);
1606 }
1607 if (remaining_bytes != 0u) {
1608 __ Ldr(temp, MemOperand(str.X(), offset));
1609 __ Ldr(temp1, MemOperand(arg.X(), offset));
1610 __ Cmp(temp, temp1);
1611 __ B(&return_false, ne);
1612 }
1613 } else {
1614 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
1615 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1616 "Expecting 0=compressed, 1=uncompressed");
1617 __ Cbz(temp, &return_true);
1618
1619 if (mirror::kUseStringCompression) {
1620 // For string compression, calculate the number of bytes to compare (not chars).
1621 // This could in theory exceed INT32_MAX, so treat temp as unsigned.
1622 __ And(temp1, temp, Operand(1)); // Extract compression flag.
1623 __ Lsr(temp, temp, 1u); // Extract length.
1624 __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare.
1625 }
1626
1627 // Store offset of string value in preparation for comparison loop
1628 __ Mov(temp1, value_offset);
1629
1630 temp1 = temp1.X();
1631 Register temp2 = XRegisterFrom(locations->GetTemp(0));
1632 // Loop to compare strings 8 bytes at a time starting at the front of the string.
1633 // Ok to do this because strings are zero-padded to kObjectAlignment.
1634 __ Bind(&loop);
1635 __ Ldr(out, MemOperand(str.X(), temp1));
1636 __ Ldr(temp2, MemOperand(arg.X(), temp1));
1637 __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
1638 __ Cmp(out, temp2);
1639 __ B(&return_false, ne);
1640 // With string compression, we have compared 8 bytes, otherwise 4 chars.
1641 __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags);
1642 __ B(&loop, hi);
jessicahandojo05765752016-09-09 19:01:32 -07001643 }
1644
Agi Csakiea34b402015-08-13 17:51:19 -07001645 // Return true and exit the function.
1646 // If loop does not result in returning false, we return true.
1647 __ Bind(&return_true);
1648 __ Mov(out, 1);
1649 __ B(&end);
1650
1651 // Return false and exit the function.
1652 __ Bind(&return_false);
1653 __ Mov(out, 0);
1654 __ Bind(&end);
1655}
1656
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001657static void GenerateVisitStringIndexOf(HInvoke* invoke,
Scott Wakeling97c72b72016-06-24 16:19:36 +01001658 MacroAssembler* masm,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001659 CodeGeneratorARM64* codegen,
1660 ArenaAllocator* allocator,
1661 bool start_at_zero) {
1662 LocationSummary* locations = invoke->GetLocations();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001663
1664 // Note that the null check must have been done earlier.
1665 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1666
1667 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001668 // 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 -07001669 SlowPathCodeARM64* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001670 HInstruction* code_point = invoke->InputAt(1);
1671 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01001672 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > 0xFFFFU) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001673 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1674 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1675 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1676 codegen->AddSlowPath(slow_path);
1677 __ B(slow_path->GetEntryLabel());
1678 __ Bind(slow_path->GetExitLabel());
1679 return;
1680 }
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001681 } else if (code_point->GetType() != Primitive::kPrimChar) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001682 Register char_reg = WRegisterFrom(locations->InAt(1));
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001683 __ Tst(char_reg, 0xFFFF0000);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001684 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1685 codegen->AddSlowPath(slow_path);
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001686 __ B(ne, slow_path->GetEntryLabel());
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001687 }
1688
1689 if (start_at_zero) {
1690 // Start-index = 0.
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001691 Register tmp_reg = WRegisterFrom(locations->GetTemp(0));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001692 __ Mov(tmp_reg, 0);
1693 }
1694
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001695 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
Roland Levillain42ad2882016-02-29 18:26:54 +00001696 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001697
1698 if (slow_path != nullptr) {
1699 __ Bind(slow_path->GetExitLabel());
1700 }
1701}
1702
1703void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) {
1704 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001705 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001706 kIntrinsified);
1707 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1708 // best to align the inputs accordingly.
1709 InvokeRuntimeCallingConvention calling_convention;
1710 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1711 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1712 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1713
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001714 // Need to send start_index=0.
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001715 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1716}
1717
1718void IntrinsicCodeGeneratorARM64::VisitStringIndexOf(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001719 GenerateVisitStringIndexOf(
1720 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001721}
1722
1723void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
1724 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001725 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001726 kIntrinsified);
1727 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1728 // best to align the inputs accordingly.
1729 InvokeRuntimeCallingConvention calling_convention;
1730 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1731 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1732 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1733 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001734}
1735
1736void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001737 GenerateVisitStringIndexOf(
1738 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001739}
1740
Jeff Hao848f70a2014-01-15 13:49:50 -08001741void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1742 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001743 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001744 kIntrinsified);
1745 InvokeRuntimeCallingConvention calling_convention;
1746 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1747 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1748 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1749 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1750 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1751}
1752
1753void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001754 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001755 LocationSummary* locations = invoke->GetLocations();
1756
1757 Register byte_array = WRegisterFrom(locations->InAt(0));
1758 __ Cmp(byte_array, 0);
1759 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1760 codegen_->AddSlowPath(slow_path);
1761 __ B(eq, slow_path->GetEntryLabel());
1762
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001763 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001764 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001765 __ Bind(slow_path->GetExitLabel());
1766}
1767
1768void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
1769 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001770 LocationSummary::kCallOnMainOnly,
Jeff Hao848f70a2014-01-15 13:49:50 -08001771 kIntrinsified);
1772 InvokeRuntimeCallingConvention calling_convention;
1773 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1774 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1775 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1776 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1777}
1778
1779void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
Roland Levillaincc3839c2016-02-29 16:23:48 +00001780 // No need to emit code checking whether `locations->InAt(2)` is a null
1781 // pointer, as callers of the native method
1782 //
1783 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1784 //
1785 // all include a null check on `data` before calling that method.
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001786 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
Roland Levillainf969a202016-03-09 16:14:00 +00001787 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001788}
1789
1790void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Jeff Hao848f70a2014-01-15 13:49:50 -08001791 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001792 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001793 kIntrinsified);
1794 InvokeRuntimeCallingConvention calling_convention;
1795 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Jeff Hao848f70a2014-01-15 13:49:50 -08001796 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1797}
1798
1799void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001800 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001801 LocationSummary* locations = invoke->GetLocations();
1802
1803 Register string_to_copy = WRegisterFrom(locations->InAt(0));
1804 __ Cmp(string_to_copy, 0);
1805 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1806 codegen_->AddSlowPath(slow_path);
1807 __ B(eq, slow_path->GetEntryLabel());
1808
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001809 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001810 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001811 __ Bind(slow_path->GetExitLabel());
1812}
1813
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001814static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1815 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
1816 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1817 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1818
1819 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001820 LocationSummary::kCallOnMainOnly,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001821 kIntrinsified);
1822 InvokeRuntimeCallingConvention calling_convention;
1823
1824 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1825 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1826}
1827
1828static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1829 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
1830 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1831 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(1)->GetType()));
1832 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1833
1834 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001835 LocationSummary::kCallOnMainOnly,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001836 kIntrinsified);
1837 InvokeRuntimeCallingConvention calling_convention;
1838
1839 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1840 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
1841 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1842}
1843
1844static void GenFPToFPCall(HInvoke* invoke,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001845 CodeGeneratorARM64* codegen,
1846 QuickEntrypointEnum entry) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001847 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001848}
1849
1850void IntrinsicLocationsBuilderARM64::VisitMathCos(HInvoke* invoke) {
1851 CreateFPToFPCallLocations(arena_, invoke);
1852}
1853
1854void IntrinsicCodeGeneratorARM64::VisitMathCos(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001855 GenFPToFPCall(invoke, codegen_, kQuickCos);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001856}
1857
1858void IntrinsicLocationsBuilderARM64::VisitMathSin(HInvoke* invoke) {
1859 CreateFPToFPCallLocations(arena_, invoke);
1860}
1861
1862void IntrinsicCodeGeneratorARM64::VisitMathSin(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001863 GenFPToFPCall(invoke, codegen_, kQuickSin);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001864}
1865
1866void IntrinsicLocationsBuilderARM64::VisitMathAcos(HInvoke* invoke) {
1867 CreateFPToFPCallLocations(arena_, invoke);
1868}
1869
1870void IntrinsicCodeGeneratorARM64::VisitMathAcos(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001871 GenFPToFPCall(invoke, codegen_, kQuickAcos);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001872}
1873
1874void IntrinsicLocationsBuilderARM64::VisitMathAsin(HInvoke* invoke) {
1875 CreateFPToFPCallLocations(arena_, invoke);
1876}
1877
1878void IntrinsicCodeGeneratorARM64::VisitMathAsin(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001879 GenFPToFPCall(invoke, codegen_, kQuickAsin);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001880}
1881
1882void IntrinsicLocationsBuilderARM64::VisitMathAtan(HInvoke* invoke) {
1883 CreateFPToFPCallLocations(arena_, invoke);
1884}
1885
1886void IntrinsicCodeGeneratorARM64::VisitMathAtan(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001887 GenFPToFPCall(invoke, codegen_, kQuickAtan);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001888}
1889
1890void IntrinsicLocationsBuilderARM64::VisitMathCbrt(HInvoke* invoke) {
1891 CreateFPToFPCallLocations(arena_, invoke);
1892}
1893
1894void IntrinsicCodeGeneratorARM64::VisitMathCbrt(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001895 GenFPToFPCall(invoke, codegen_, kQuickCbrt);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001896}
1897
1898void IntrinsicLocationsBuilderARM64::VisitMathCosh(HInvoke* invoke) {
1899 CreateFPToFPCallLocations(arena_, invoke);
1900}
1901
1902void IntrinsicCodeGeneratorARM64::VisitMathCosh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001903 GenFPToFPCall(invoke, codegen_, kQuickCosh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001904}
1905
1906void IntrinsicLocationsBuilderARM64::VisitMathExp(HInvoke* invoke) {
1907 CreateFPToFPCallLocations(arena_, invoke);
1908}
1909
1910void IntrinsicCodeGeneratorARM64::VisitMathExp(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001911 GenFPToFPCall(invoke, codegen_, kQuickExp);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001912}
1913
1914void IntrinsicLocationsBuilderARM64::VisitMathExpm1(HInvoke* invoke) {
1915 CreateFPToFPCallLocations(arena_, invoke);
1916}
1917
1918void IntrinsicCodeGeneratorARM64::VisitMathExpm1(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001919 GenFPToFPCall(invoke, codegen_, kQuickExpm1);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001920}
1921
1922void IntrinsicLocationsBuilderARM64::VisitMathLog(HInvoke* invoke) {
1923 CreateFPToFPCallLocations(arena_, invoke);
1924}
1925
1926void IntrinsicCodeGeneratorARM64::VisitMathLog(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001927 GenFPToFPCall(invoke, codegen_, kQuickLog);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001928}
1929
1930void IntrinsicLocationsBuilderARM64::VisitMathLog10(HInvoke* invoke) {
1931 CreateFPToFPCallLocations(arena_, invoke);
1932}
1933
1934void IntrinsicCodeGeneratorARM64::VisitMathLog10(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001935 GenFPToFPCall(invoke, codegen_, kQuickLog10);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001936}
1937
1938void IntrinsicLocationsBuilderARM64::VisitMathSinh(HInvoke* invoke) {
1939 CreateFPToFPCallLocations(arena_, invoke);
1940}
1941
1942void IntrinsicCodeGeneratorARM64::VisitMathSinh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001943 GenFPToFPCall(invoke, codegen_, kQuickSinh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001944}
1945
1946void IntrinsicLocationsBuilderARM64::VisitMathTan(HInvoke* invoke) {
1947 CreateFPToFPCallLocations(arena_, invoke);
1948}
1949
1950void IntrinsicCodeGeneratorARM64::VisitMathTan(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001951 GenFPToFPCall(invoke, codegen_, kQuickTan);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001952}
1953
1954void IntrinsicLocationsBuilderARM64::VisitMathTanh(HInvoke* invoke) {
1955 CreateFPToFPCallLocations(arena_, invoke);
1956}
1957
1958void IntrinsicCodeGeneratorARM64::VisitMathTanh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001959 GenFPToFPCall(invoke, codegen_, kQuickTanh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001960}
1961
1962void IntrinsicLocationsBuilderARM64::VisitMathAtan2(HInvoke* invoke) {
1963 CreateFPFPToFPCallLocations(arena_, invoke);
1964}
1965
1966void IntrinsicCodeGeneratorARM64::VisitMathAtan2(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001967 GenFPToFPCall(invoke, codegen_, kQuickAtan2);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001968}
1969
1970void IntrinsicLocationsBuilderARM64::VisitMathHypot(HInvoke* invoke) {
1971 CreateFPFPToFPCallLocations(arena_, invoke);
1972}
1973
1974void IntrinsicCodeGeneratorARM64::VisitMathHypot(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001975 GenFPToFPCall(invoke, codegen_, kQuickHypot);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001976}
1977
1978void IntrinsicLocationsBuilderARM64::VisitMathNextAfter(HInvoke* invoke) {
1979 CreateFPFPToFPCallLocations(arena_, invoke);
1980}
1981
1982void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001983 GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001984}
1985
Tim Zhang25abd6c2016-01-19 23:39:24 +08001986void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1987 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1988 LocationSummary::kNoCall,
1989 kIntrinsified);
1990 locations->SetInAt(0, Location::RequiresRegister());
1991 locations->SetInAt(1, Location::RequiresRegister());
1992 locations->SetInAt(2, Location::RequiresRegister());
1993 locations->SetInAt(3, Location::RequiresRegister());
1994 locations->SetInAt(4, Location::RequiresRegister());
1995
1996 locations->AddTemp(Location::RequiresRegister());
1997 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingdf109d92016-04-22 11:35:56 +01001998 locations->AddTemp(Location::RequiresRegister());
Tim Zhang25abd6c2016-01-19 23:39:24 +08001999}
2000
2001void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01002002 MacroAssembler* masm = GetVIXLAssembler();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002003 LocationSummary* locations = invoke->GetLocations();
2004
2005 // Check assumption that sizeof(Char) is 2 (used in scaling below).
2006 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2007 DCHECK_EQ(char_size, 2u);
2008
2009 // Location of data in char array buffer.
2010 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2011
2012 // Location of char array data in string.
2013 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2014
2015 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2016 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2017 Register srcObj = XRegisterFrom(locations->InAt(0));
2018 Register srcBegin = XRegisterFrom(locations->InAt(1));
2019 Register srcEnd = XRegisterFrom(locations->InAt(2));
2020 Register dstObj = XRegisterFrom(locations->InAt(3));
2021 Register dstBegin = XRegisterFrom(locations->InAt(4));
2022
2023 Register src_ptr = XRegisterFrom(locations->GetTemp(0));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002024 Register num_chr = XRegisterFrom(locations->GetTemp(1));
2025 Register tmp1 = XRegisterFrom(locations->GetTemp(2));
Tim Zhang25abd6c2016-01-19 23:39:24 +08002026
2027 UseScratchRegisterScope temps(masm);
2028 Register dst_ptr = temps.AcquireX();
Scott Wakelingdf109d92016-04-22 11:35:56 +01002029 Register tmp2 = temps.AcquireX();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002030
jessicahandojo05765752016-09-09 19:01:32 -07002031 vixl::aarch64::Label done;
2032 vixl::aarch64::Label compressed_string_loop;
2033 __ Sub(num_chr, srcEnd, srcBegin);
2034 // Early out for valid zero-length retrievals.
2035 __ Cbz(num_chr, &done);
Tim Zhang25abd6c2016-01-19 23:39:24 +08002036
Scott Wakelingdf109d92016-04-22 11:35:56 +01002037 // dst address start to copy to.
Tim Zhang25abd6c2016-01-19 23:39:24 +08002038 __ Add(dst_ptr, dstObj, Operand(data_offset));
2039 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
2040
jessicahandojo05765752016-09-09 19:01:32 -07002041 // src address to copy from.
2042 __ Add(src_ptr, srcObj, Operand(value_offset));
2043 vixl::aarch64::Label compressed_string_preloop;
2044 if (mirror::kUseStringCompression) {
2045 // Location of count in string.
2046 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2047 // String's length.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002048 __ Ldr(tmp2, MemOperand(srcObj, count_offset));
2049 __ Tbz(tmp2, 0, &compressed_string_preloop);
jessicahandojo05765752016-09-09 19:01:32 -07002050 }
2051 __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002052
Tim Zhang25abd6c2016-01-19 23:39:24 +08002053 // Do the copy.
Scott Wakeling97c72b72016-06-24 16:19:36 +01002054 vixl::aarch64::Label loop;
Scott Wakeling97c72b72016-06-24 16:19:36 +01002055 vixl::aarch64::Label remainder;
Scott Wakelingdf109d92016-04-22 11:35:56 +01002056
Scott Wakelingdf109d92016-04-22 11:35:56 +01002057 // Save repairing the value of num_chr on the < 8 character path.
2058 __ Subs(tmp1, num_chr, 8);
2059 __ B(lt, &remainder);
2060
2061 // Keep the result of the earlier subs, we are going to fetch at least 8 characters.
2062 __ Mov(num_chr, tmp1);
2063
2064 // Main loop used for longer fetches loads and stores 8x16-bit characters at a time.
2065 // (Unaligned addresses are acceptable here and not worth inlining extra code to rectify.)
Tim Zhang25abd6c2016-01-19 23:39:24 +08002066 __ Bind(&loop);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002067 __ Ldp(tmp1, tmp2, MemOperand(src_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002068 __ Subs(num_chr, num_chr, 8);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002069 __ Stp(tmp1, tmp2, MemOperand(dst_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002070 __ B(ge, &loop);
2071
2072 __ Adds(num_chr, num_chr, 8);
2073 __ B(eq, &done);
2074
2075 // Main loop for < 8 character case and remainder handling. Loads and stores one
2076 // 16-bit Java character at a time.
2077 __ Bind(&remainder);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002078 __ Ldrh(tmp1, MemOperand(src_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002079 __ Subs(num_chr, num_chr, 1);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002080 __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002081 __ B(gt, &remainder);
jessicahandojo05765752016-09-09 19:01:32 -07002082 __ B(&done);
2083
2084 if (mirror::kUseStringCompression) {
2085 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
2086 DCHECK_EQ(c_char_size, 1u);
2087 __ Bind(&compressed_string_preloop);
2088 __ Add(src_ptr, src_ptr, Operand(srcBegin));
2089 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2090 __ Bind(&compressed_string_loop);
2091 __ Ldrb(tmp1, MemOperand(src_ptr, c_char_size, PostIndex));
2092 __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
2093 __ Subs(num_chr, num_chr, Operand(1));
2094 __ B(gt, &compressed_string_loop);
2095 }
Scott Wakelingdf109d92016-04-22 11:35:56 +01002096
Tim Zhang25abd6c2016-01-19 23:39:24 +08002097 __ Bind(&done);
2098}
2099
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002100// Mirrors ARRAYCOPY_SHORT_CHAR_ARRAY_THRESHOLD in libcore, so we can choose to use the native
2101// implementation there for longer copy lengths.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002102static constexpr int32_t kSystemArrayCopyCharThreshold = 32;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002103
2104static void SetSystemArrayCopyLocationRequires(LocationSummary* locations,
2105 uint32_t at,
2106 HInstruction* input) {
2107 HIntConstant* const_input = input->AsIntConstant();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002108 if (const_input != nullptr && !vixl::aarch64::Assembler::IsImmAddSub(const_input->GetValue())) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002109 locations->SetInAt(at, Location::RequiresRegister());
2110 } else {
2111 locations->SetInAt(at, Location::RegisterOrConstant(input));
2112 }
2113}
2114
2115void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
2116 // Check to see if we have known failures that will cause us to have to bail out
2117 // to the runtime, and just generate the runtime call directly.
2118 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2119 HIntConstant* dst_pos = invoke->InputAt(3)->AsIntConstant();
2120
2121 // The positions must be non-negative.
2122 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2123 (dst_pos != nullptr && dst_pos->GetValue() < 0)) {
2124 // We will have to fail anyways.
2125 return;
2126 }
2127
2128 // The length must be >= 0 and not so long that we would (currently) prefer libcore's
2129 // native implementation.
2130 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2131 if (length != nullptr) {
2132 int32_t len = length->GetValue();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002133 if (len < 0 || len > kSystemArrayCopyCharThreshold) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002134 // Just call as normal.
2135 return;
2136 }
2137 }
2138
2139 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2140 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2141 LocationSummary::kCallOnSlowPath,
2142 kIntrinsified);
2143 // arraycopy(char[] src, int src_pos, char[] dst, int dst_pos, int length).
2144 locations->SetInAt(0, Location::RequiresRegister());
2145 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2146 locations->SetInAt(2, Location::RequiresRegister());
2147 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2148 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2149
2150 locations->AddTemp(Location::RequiresRegister());
2151 locations->AddTemp(Location::RequiresRegister());
2152 locations->AddTemp(Location::RequiresRegister());
2153}
2154
Scott Wakeling97c72b72016-06-24 16:19:36 +01002155static void CheckSystemArrayCopyPosition(MacroAssembler* masm,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002156 const Location& pos,
2157 const Register& input,
2158 const Location& length,
2159 SlowPathCodeARM64* slow_path,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002160 const Register& temp,
2161 bool length_is_input_length = false) {
2162 const int32_t length_offset = mirror::Array::LengthOffset().Int32Value();
2163 if (pos.IsConstant()) {
2164 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
2165 if (pos_const == 0) {
2166 if (!length_is_input_length) {
2167 // Check that length(input) >= length.
2168 __ Ldr(temp, MemOperand(input, length_offset));
2169 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
2170 __ B(slow_path->GetEntryLabel(), lt);
2171 }
2172 } else {
2173 // Check that length(input) >= pos.
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002174 __ Ldr(temp, MemOperand(input, length_offset));
2175 __ Subs(temp, temp, pos_const);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002176 __ B(slow_path->GetEntryLabel(), lt);
2177
2178 // Check that (length(input) - pos) >= length.
2179 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
2180 __ B(slow_path->GetEntryLabel(), lt);
2181 }
2182 } else if (length_is_input_length) {
2183 // The only way the copy can succeed is if pos is zero.
2184 __ Cbnz(WRegisterFrom(pos), slow_path->GetEntryLabel());
2185 } else {
2186 // Check that pos >= 0.
2187 Register pos_reg = WRegisterFrom(pos);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002188 __ Tbnz(pos_reg, pos_reg.GetSizeInBits() - 1, slow_path->GetEntryLabel());
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002189
2190 // Check that pos <= length(input) && (length(input) - pos) >= length.
2191 __ Ldr(temp, MemOperand(input, length_offset));
2192 __ Subs(temp, temp, pos_reg);
2193 // Ccmp if length(input) >= pos, else definitely bail to slow path (N!=V == lt).
2194 __ Ccmp(temp, OperandFrom(length, Primitive::kPrimInt), NFlag, ge);
2195 __ B(slow_path->GetEntryLabel(), lt);
2196 }
2197}
2198
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002199// Compute base source address, base destination address, and end
2200// source address for System.arraycopy* intrinsics in `src_base`,
2201// `dst_base` and `src_end` respectively.
Scott Wakeling97c72b72016-06-24 16:19:36 +01002202static void GenSystemArrayCopyAddresses(MacroAssembler* masm,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002203 Primitive::Type type,
2204 const Register& src,
2205 const Location& src_pos,
2206 const Register& dst,
2207 const Location& dst_pos,
2208 const Location& copy_length,
2209 const Register& src_base,
2210 const Register& dst_base,
2211 const Register& src_end) {
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002212 // This routine is used by the SystemArrayCopy and the SystemArrayCopyChar intrinsics.
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002213 DCHECK(type == Primitive::kPrimNot || type == Primitive::kPrimChar)
Roland Levillainebea3d22016-04-12 15:42:57 +01002214 << "Unexpected element type: " << type;
2215 const int32_t element_size = Primitive::ComponentSize(type);
2216 const int32_t element_size_shift = Primitive::ComponentSizeShift(type);
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002217 const uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002218
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002219 if (src_pos.IsConstant()) {
2220 int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002221 __ Add(src_base, src, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002222 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002223 __ Add(src_base, src, data_offset);
2224 __ Add(src_base, src_base, Operand(XRegisterFrom(src_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002225 }
2226
2227 if (dst_pos.IsConstant()) {
2228 int32_t constant = dst_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002229 __ Add(dst_base, dst, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002230 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002231 __ Add(dst_base, dst, data_offset);
2232 __ Add(dst_base, dst_base, Operand(XRegisterFrom(dst_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002233 }
2234
2235 if (copy_length.IsConstant()) {
2236 int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002237 __ Add(src_end, src_base, element_size * constant);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002238 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002239 __ Add(src_end, src_base, Operand(XRegisterFrom(copy_length), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002240 }
2241}
2242
2243void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01002244 MacroAssembler* masm = GetVIXLAssembler();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002245 LocationSummary* locations = invoke->GetLocations();
2246 Register src = XRegisterFrom(locations->InAt(0));
2247 Location src_pos = locations->InAt(1);
2248 Register dst = XRegisterFrom(locations->InAt(2));
2249 Location dst_pos = locations->InAt(3);
2250 Location length = locations->InAt(4);
2251
2252 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2253 codegen_->AddSlowPath(slow_path);
2254
2255 // If source and destination are the same, take the slow path. Overlapping copy regions must be
2256 // copied in reverse and we can't know in all cases if it's needed.
2257 __ Cmp(src, dst);
2258 __ B(slow_path->GetEntryLabel(), eq);
2259
2260 // Bail out if the source is null.
2261 __ Cbz(src, slow_path->GetEntryLabel());
2262
2263 // Bail out if the destination is null.
2264 __ Cbz(dst, slow_path->GetEntryLabel());
2265
2266 if (!length.IsConstant()) {
Vladimir Markoc5646202016-11-28 16:03:15 +00002267 // Merge the following two comparisons into one:
2268 // If the length is negative, bail out (delegate to libcore's native implementation).
2269 // If the length > 32 then (currently) prefer libcore's native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002270 __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold);
Vladimir Markoc5646202016-11-28 16:03:15 +00002271 __ B(slow_path->GetEntryLabel(), hi);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002272 } else {
2273 // We have already checked in the LocationsBuilder for the constant case.
2274 DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0);
2275 DCHECK_LE(length.GetConstant()->AsIntConstant()->GetValue(), 32);
2276 }
2277
2278 Register src_curr_addr = WRegisterFrom(locations->GetTemp(0));
2279 Register dst_curr_addr = WRegisterFrom(locations->GetTemp(1));
2280 Register src_stop_addr = WRegisterFrom(locations->GetTemp(2));
2281
2282 CheckSystemArrayCopyPosition(masm,
2283 src_pos,
2284 src,
2285 length,
2286 slow_path,
2287 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002288 false);
2289
2290 CheckSystemArrayCopyPosition(masm,
2291 dst_pos,
2292 dst,
2293 length,
2294 slow_path,
2295 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002296 false);
2297
2298 src_curr_addr = src_curr_addr.X();
2299 dst_curr_addr = dst_curr_addr.X();
2300 src_stop_addr = src_stop_addr.X();
2301
2302 GenSystemArrayCopyAddresses(masm,
2303 Primitive::kPrimChar,
2304 src,
2305 src_pos,
2306 dst,
2307 dst_pos,
2308 length,
2309 src_curr_addr,
2310 dst_curr_addr,
2311 src_stop_addr);
2312
2313 // Iterate over the arrays and do a raw copy of the chars.
2314 const int32_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2315 UseScratchRegisterScope temps(masm);
2316 Register tmp = temps.AcquireW();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002317 vixl::aarch64::Label loop, done;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002318 __ Bind(&loop);
2319 __ Cmp(src_curr_addr, src_stop_addr);
2320 __ B(&done, eq);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002321 __ Ldrh(tmp, MemOperand(src_curr_addr, char_size, PostIndex));
2322 __ Strh(tmp, MemOperand(dst_curr_addr, char_size, PostIndex));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002323 __ B(&loop);
2324 __ Bind(&done);
2325
2326 __ Bind(slow_path->GetExitLabel());
2327}
2328
donghui.baic2ec9ad2016-03-10 14:02:55 +08002329// We can choose to use the native implementation there for longer copy lengths.
2330static constexpr int32_t kSystemArrayCopyThreshold = 128;
2331
2332// CodeGenerator::CreateSystemArrayCopyLocationSummary use three temporary registers.
2333// We want to use two temporary registers in order to reduce the register pressure in arm64.
2334// So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary.
2335void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002336 // The only read barrier implementation supporting the
2337 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2338 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain3d312422016-06-23 13:53:42 +01002339 return;
2340 }
2341
donghui.baic2ec9ad2016-03-10 14:02:55 +08002342 // Check to see if we have known failures that will cause us to have to bail out
2343 // to the runtime, and just generate the runtime call directly.
2344 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2345 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
2346
2347 // The positions must be non-negative.
2348 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2349 (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
2350 // We will have to fail anyways.
2351 return;
2352 }
2353
2354 // The length must be >= 0.
2355 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2356 if (length != nullptr) {
2357 int32_t len = length->GetValue();
2358 if (len < 0 || len >= kSystemArrayCopyThreshold) {
2359 // Just call as normal.
2360 return;
2361 }
2362 }
2363
2364 SystemArrayCopyOptimizations optimizations(invoke);
2365
2366 if (optimizations.GetDestinationIsSource()) {
2367 if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) {
2368 // We only support backward copying if source and destination are the same.
2369 return;
2370 }
2371 }
2372
2373 if (optimizations.GetDestinationIsPrimitiveArray() || optimizations.GetSourceIsPrimitiveArray()) {
2374 // We currently don't intrinsify primitive copying.
2375 return;
2376 }
2377
2378 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2379 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2380 LocationSummary::kCallOnSlowPath,
2381 kIntrinsified);
2382 // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length).
2383 locations->SetInAt(0, Location::RequiresRegister());
2384 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2385 locations->SetInAt(2, Location::RequiresRegister());
2386 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2387 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2388
2389 locations->AddTemp(Location::RequiresRegister());
2390 locations->AddTemp(Location::RequiresRegister());
Roland Levillain0b671c02016-08-19 12:02:34 +01002391 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2392 // Temporary register IP0, obtained from the VIXL scratch register
2393 // pool, cannot be used in ReadBarrierSystemArrayCopySlowPathARM64
2394 // (because that register is clobbered by ReadBarrierMarkRegX
Roland Levillain54f869e2017-03-06 13:54:11 +00002395 // entry points). It cannot be used in calls to
2396 // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier
2397 // either. For these reasons, get a third extra temporary register
2398 // from the register allocator.
Roland Levillain0b671c02016-08-19 12:02:34 +01002399 locations->AddTemp(Location::RequiresRegister());
Roland Levillain54f869e2017-03-06 13:54:11 +00002400 } else {
2401 // Cases other than Baker read barriers: the third temporary will
2402 // be acquired from the VIXL scratch register pool.
Roland Levillain0b671c02016-08-19 12:02:34 +01002403 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002404}
2405
2406void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002407 // The only read barrier implementation supporting the
2408 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2409 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01002410
Scott Wakeling97c72b72016-06-24 16:19:36 +01002411 MacroAssembler* masm = GetVIXLAssembler();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002412 LocationSummary* locations = invoke->GetLocations();
2413
2414 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2415 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2416 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2417 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Roland Levillain0b671c02016-08-19 12:02:34 +01002418 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002419
2420 Register src = XRegisterFrom(locations->InAt(0));
2421 Location src_pos = locations->InAt(1);
2422 Register dest = XRegisterFrom(locations->InAt(2));
2423 Location dest_pos = locations->InAt(3);
2424 Location length = locations->InAt(4);
2425 Register temp1 = WRegisterFrom(locations->GetTemp(0));
Roland Levillain0b671c02016-08-19 12:02:34 +01002426 Location temp1_loc = LocationFrom(temp1);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002427 Register temp2 = WRegisterFrom(locations->GetTemp(1));
Roland Levillain0b671c02016-08-19 12:02:34 +01002428 Location temp2_loc = LocationFrom(temp2);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002429
Roland Levillain0b671c02016-08-19 12:02:34 +01002430 SlowPathCodeARM64* intrinsic_slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2431 codegen_->AddSlowPath(intrinsic_slow_path);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002432
Scott Wakeling97c72b72016-06-24 16:19:36 +01002433 vixl::aarch64::Label conditions_on_positions_validated;
donghui.baic2ec9ad2016-03-10 14:02:55 +08002434 SystemArrayCopyOptimizations optimizations(invoke);
2435
donghui.baic2ec9ad2016-03-10 14:02:55 +08002436 // If source and destination are the same, we go to slow path if we need to do
2437 // forward copying.
2438 if (src_pos.IsConstant()) {
2439 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
2440 if (dest_pos.IsConstant()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002441 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
2442 if (optimizations.GetDestinationIsSource()) {
2443 // Checked when building locations.
2444 DCHECK_GE(src_pos_constant, dest_pos_constant);
2445 } else if (src_pos_constant < dest_pos_constant) {
2446 __ Cmp(src, dest);
Roland Levillain0b671c02016-08-19 12:02:34 +01002447 __ B(intrinsic_slow_path->GetEntryLabel(), eq);
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002448 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002449 // Checked when building locations.
2450 DCHECK(!optimizations.GetDestinationIsSource()
2451 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
2452 } else {
2453 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002454 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002455 __ B(&conditions_on_positions_validated, ne);
2456 }
2457 __ Cmp(WRegisterFrom(dest_pos), src_pos_constant);
Roland Levillain0b671c02016-08-19 12:02:34 +01002458 __ B(intrinsic_slow_path->GetEntryLabel(), gt);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002459 }
2460 } else {
2461 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002462 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002463 __ B(&conditions_on_positions_validated, ne);
2464 }
2465 __ Cmp(RegisterFrom(src_pos, invoke->InputAt(1)->GetType()),
2466 OperandFrom(dest_pos, invoke->InputAt(3)->GetType()));
Roland Levillain0b671c02016-08-19 12:02:34 +01002467 __ B(intrinsic_slow_path->GetEntryLabel(), lt);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002468 }
2469
2470 __ Bind(&conditions_on_positions_validated);
2471
2472 if (!optimizations.GetSourceIsNotNull()) {
2473 // Bail out if the source is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01002474 __ Cbz(src, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002475 }
2476
2477 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
2478 // Bail out if the destination is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01002479 __ Cbz(dest, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002480 }
2481
2482 // We have already checked in the LocationsBuilder for the constant case.
2483 if (!length.IsConstant() &&
2484 !optimizations.GetCountIsSourceLength() &&
2485 !optimizations.GetCountIsDestinationLength()) {
Vladimir Markoc5646202016-11-28 16:03:15 +00002486 // Merge the following two comparisons into one:
2487 // If the length is negative, bail out (delegate to libcore's native implementation).
2488 // If the length >= 128 then (currently) prefer native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002489 __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold);
Vladimir Markoc5646202016-11-28 16:03:15 +00002490 __ B(intrinsic_slow_path->GetEntryLabel(), hs);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002491 }
2492 // Validity checks: source.
2493 CheckSystemArrayCopyPosition(masm,
2494 src_pos,
2495 src,
2496 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002497 intrinsic_slow_path,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002498 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002499 optimizations.GetCountIsSourceLength());
2500
2501 // Validity checks: dest.
2502 CheckSystemArrayCopyPosition(masm,
2503 dest_pos,
2504 dest,
2505 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002506 intrinsic_slow_path,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002507 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002508 optimizations.GetCountIsDestinationLength());
2509 {
2510 // We use a block to end the scratch scope before the write barrier, thus
2511 // freeing the temporary registers so they can be used in `MarkGCCard`.
2512 UseScratchRegisterScope temps(masm);
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002513 Location temp3_loc; // Used only for Baker read barrier.
Roland Levillain54f869e2017-03-06 13:54:11 +00002514 Register temp3;
2515 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002516 temp3_loc = locations->GetTemp(2);
2517 temp3 = WRegisterFrom(temp3_loc);
Roland Levillain54f869e2017-03-06 13:54:11 +00002518 } else {
2519 temp3 = temps.AcquireW();
2520 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002521
donghui.baic2ec9ad2016-03-10 14:02:55 +08002522 if (!optimizations.GetDoesNotNeedTypeCheck()) {
2523 // Check whether all elements of the source array are assignable to the component
2524 // type of the destination array. We do two checks: the classes are the same,
2525 // or the destination is Object[]. If none of these checks succeed, we go to the
2526 // slow path.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002527
Roland Levillain0b671c02016-08-19 12:02:34 +01002528 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2529 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2530 // /* HeapReference<Class> */ temp1 = src->klass_
2531 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2532 temp1_loc,
2533 src.W(),
2534 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002535 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002536 /* needs_null_check */ false,
2537 /* use_load_acquire */ false);
2538 // Bail out if the source is not a non primitive array.
2539 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2540 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2541 temp1_loc,
2542 temp1,
2543 component_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002544 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002545 /* needs_null_check */ false,
2546 /* use_load_acquire */ false);
2547 __ Cbz(temp1, intrinsic_slow_path->GetEntryLabel());
2548 // If heap poisoning is enabled, `temp1` has been unpoisoned
2549 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2550 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
2551 __ Ldrh(temp1, HeapOperand(temp1, primitive_offset));
2552 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2553 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002554 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002555
2556 // /* HeapReference<Class> */ temp1 = dest->klass_
2557 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2558 temp1_loc,
2559 dest.W(),
2560 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002561 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002562 /* needs_null_check */ false,
2563 /* use_load_acquire */ false);
2564
2565 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2566 // Bail out if the destination is not a non primitive array.
2567 //
2568 // Register `temp1` is not trashed by the read barrier emitted
2569 // by GenerateFieldLoadWithBakerReadBarrier below, as that
2570 // method produces a call to a ReadBarrierMarkRegX entry point,
2571 // which saves all potentially live registers, including
2572 // temporaries such a `temp1`.
2573 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2574 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2575 temp2_loc,
2576 temp1,
2577 component_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002578 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002579 /* needs_null_check */ false,
2580 /* use_load_acquire */ false);
2581 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2582 // If heap poisoning is enabled, `temp2` has been unpoisoned
2583 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2584 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2585 __ Ldrh(temp2, HeapOperand(temp2, primitive_offset));
2586 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2587 __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
2588 }
2589
2590 // For the same reason given earlier, `temp1` is not trashed by the
2591 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
2592 // /* HeapReference<Class> */ temp2 = src->klass_
2593 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2594 temp2_loc,
2595 src.W(),
2596 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002597 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002598 /* needs_null_check */ false,
2599 /* use_load_acquire */ false);
2600 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
2601 __ Cmp(temp1, temp2);
2602
2603 if (optimizations.GetDestinationIsTypedObjectArray()) {
2604 vixl::aarch64::Label do_copy;
2605 __ B(&do_copy, eq);
2606 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2607 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2608 temp1_loc,
2609 temp1,
2610 component_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002611 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002612 /* needs_null_check */ false,
2613 /* use_load_acquire */ false);
2614 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2615 // We do not need to emit a read barrier for the following
2616 // heap reference load, as `temp1` is only used in a
2617 // comparison with null below, and this reference is not
2618 // kept afterwards.
2619 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2620 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
2621 __ Bind(&do_copy);
2622 } else {
2623 __ B(intrinsic_slow_path->GetEntryLabel(), ne);
2624 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002625 } else {
Roland Levillain0b671c02016-08-19 12:02:34 +01002626 // Non read barrier code.
2627
2628 // /* HeapReference<Class> */ temp1 = dest->klass_
2629 __ Ldr(temp1, MemOperand(dest, class_offset));
2630 // /* HeapReference<Class> */ temp2 = src->klass_
2631 __ Ldr(temp2, MemOperand(src, class_offset));
2632 bool did_unpoison = false;
2633 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2634 !optimizations.GetSourceIsNonPrimitiveArray()) {
2635 // One or two of the references need to be unpoisoned. Unpoison them
2636 // both to make the identity check valid.
2637 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2638 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2639 did_unpoison = true;
2640 }
2641
2642 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2643 // Bail out if the destination is not a non primitive array.
2644 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2645 __ Ldr(temp3, HeapOperand(temp1, component_offset));
2646 __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
2647 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2648 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2649 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2650 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2651 __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
2652 }
2653
2654 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2655 // Bail out if the source is not a non primitive array.
2656 // /* HeapReference<Class> */ temp3 = temp2->component_type_
2657 __ Ldr(temp3, HeapOperand(temp2, component_offset));
2658 __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
2659 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2660 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2661 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2662 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2663 __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
2664 }
2665
2666 __ Cmp(temp1, temp2);
2667
2668 if (optimizations.GetDestinationIsTypedObjectArray()) {
2669 vixl::aarch64::Label do_copy;
2670 __ B(&do_copy, eq);
2671 if (!did_unpoison) {
2672 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2673 }
2674 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2675 __ Ldr(temp1, HeapOperand(temp1, component_offset));
2676 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2677 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2678 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2679 // No need to unpoison the result, we're comparing against null.
2680 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
2681 __ Bind(&do_copy);
2682 } else {
2683 __ B(intrinsic_slow_path->GetEntryLabel(), ne);
2684 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002685 }
2686 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2687 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2688 // Bail out if the source is not a non primitive array.
Roland Levillain0b671c02016-08-19 12:02:34 +01002689 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2690 // /* HeapReference<Class> */ temp1 = src->klass_
2691 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2692 temp1_loc,
2693 src.W(),
2694 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002695 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002696 /* needs_null_check */ false,
2697 /* use_load_acquire */ false);
2698 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2699 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2700 temp2_loc,
2701 temp1,
2702 component_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002703 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002704 /* needs_null_check */ false,
2705 /* use_load_acquire */ false);
2706 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2707 // If heap poisoning is enabled, `temp2` has been unpoisoned
2708 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2709 } else {
2710 // /* HeapReference<Class> */ temp1 = src->klass_
2711 __ Ldr(temp1, HeapOperand(src.W(), class_offset));
2712 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2713 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2714 __ Ldr(temp2, HeapOperand(temp1, component_offset));
2715 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2716 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2717 }
2718 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2719 __ Ldrh(temp2, HeapOperand(temp2, primitive_offset));
donghui.baic2ec9ad2016-03-10 14:02:55 +08002720 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Roland Levillain0b671c02016-08-19 12:02:34 +01002721 __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002722 }
2723
Roland Levillain1663d162017-03-17 15:15:21 +00002724 if (length.IsConstant() && length.GetConstant()->AsIntConstant()->GetValue() == 0) {
2725 // Null constant length: not need to emit the loop code at all.
Roland Levillain0b671c02016-08-19 12:02:34 +01002726 } else {
Roland Levillain1663d162017-03-17 15:15:21 +00002727 Register src_curr_addr = temp1.X();
2728 Register dst_curr_addr = temp2.X();
2729 Register src_stop_addr = temp3.X();
2730 vixl::aarch64::Label done;
2731 const Primitive::Type type = Primitive::kPrimNot;
2732 const int32_t element_size = Primitive::ComponentSize(type);
2733
2734 if (length.IsRegister()) {
2735 // Don't enter the copy loop if the length is null.
2736 __ Cbz(WRegisterFrom(length), &done);
2737 }
2738
2739 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2740 // TODO: Also convert this intrinsic to the IsGcMarking strategy?
2741
2742 // SystemArrayCopy implementation for Baker read barriers (see
2743 // also CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier):
2744 //
2745 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
2746 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
2747 // bool is_gray = (rb_state == ReadBarrier::GrayState());
2748 // if (is_gray) {
2749 // // Slow-path copy.
2750 // do {
2751 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
2752 // } while (src_ptr != end_ptr)
2753 // } else {
2754 // // Fast-path copy.
2755 // do {
2756 // *dest_ptr++ = *src_ptr++;
2757 // } while (src_ptr != end_ptr)
2758 // }
2759
2760 // Make sure `tmp` is not IP0, as it is clobbered by
2761 // ReadBarrierMarkRegX entry points in
2762 // ReadBarrierSystemArrayCopySlowPathARM64.
Roland Levillain1ca955d2017-04-13 19:34:30 +01002763 DCHECK(temps.IsAvailable(ip0));
Roland Levillain1663d162017-03-17 15:15:21 +00002764 temps.Exclude(ip0);
Roland Levillain0b671c02016-08-19 12:02:34 +01002765 Register tmp = temps.AcquireW();
Roland Levillain1663d162017-03-17 15:15:21 +00002766 DCHECK_NE(LocationFrom(tmp).reg(), IP0);
Roland Levillain1ca955d2017-04-13 19:34:30 +01002767 // Put IP0 back in the pool so that VIXL has at least one
2768 // scratch register available to emit macro-instructions (note
2769 // that IP1 is already used for `tmp`). Indeed some
2770 // macro-instructions used in GenSystemArrayCopyAddresses
2771 // (invoked hereunder) may require a scratch register (for
2772 // instance to emit a load with a large constant offset).
2773 temps.Include(ip0);
Roland Levillain1663d162017-03-17 15:15:21 +00002774
2775 // /* int32_t */ monitor = src->monitor_
2776 __ Ldr(tmp, HeapOperand(src.W(), monitor_offset));
2777 // /* LockWord */ lock_word = LockWord(monitor)
2778 static_assert(sizeof(LockWord) == sizeof(int32_t),
2779 "art::LockWord and int32_t have different sizes.");
2780
2781 // Introduce a dependency on the lock_word including rb_state,
2782 // to prevent load-load reordering, and without using
2783 // a memory barrier (which would be more expensive).
2784 // `src` is unchanged by this operation, but its value now depends
2785 // on `tmp`.
2786 __ Add(src.X(), src.X(), Operand(tmp.X(), LSR, 32));
2787
2788 // Compute base source address, base destination address, and end
2789 // source address for System.arraycopy* intrinsics in `src_base`,
2790 // `dst_base` and `src_end` respectively.
2791 // Note that `src_curr_addr` is computed from from `src` (and
2792 // `src_pos`) here, and thus honors the artificial dependency
2793 // of `src` on `tmp`.
2794 GenSystemArrayCopyAddresses(masm,
2795 type,
2796 src,
2797 src_pos,
2798 dest,
2799 dest_pos,
2800 length,
2801 src_curr_addr,
2802 dst_curr_addr,
2803 src_stop_addr);
2804
2805 // Slow path used to copy array when `src` is gray.
2806 SlowPathCodeARM64* read_barrier_slow_path =
2807 new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathARM64(invoke, LocationFrom(tmp));
2808 codegen_->AddSlowPath(read_barrier_slow_path);
2809
2810 // Given the numeric representation, it's enough to check the low bit of the rb_state.
2811 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
2812 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
2813 __ Tbnz(tmp, LockWord::kReadBarrierStateShift, read_barrier_slow_path->GetEntryLabel());
2814
2815 // Fast-path copy.
2816 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2817 // poison/unpoison.
2818 vixl::aarch64::Label loop;
2819 __ Bind(&loop);
Roland Levillain0b671c02016-08-19 12:02:34 +01002820 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
2821 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
Roland Levillain1663d162017-03-17 15:15:21 +00002822 __ Cmp(src_curr_addr, src_stop_addr);
2823 __ B(&loop, ne);
2824
2825 __ Bind(read_barrier_slow_path->GetExitLabel());
2826 } else {
2827 // Non read barrier code.
2828 // Compute base source address, base destination address, and end
2829 // source address for System.arraycopy* intrinsics in `src_base`,
2830 // `dst_base` and `src_end` respectively.
2831 GenSystemArrayCopyAddresses(masm,
2832 type,
2833 src,
2834 src_pos,
2835 dest,
2836 dest_pos,
2837 length,
2838 src_curr_addr,
2839 dst_curr_addr,
2840 src_stop_addr);
2841 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2842 // poison/unpoison.
2843 vixl::aarch64::Label loop;
2844 __ Bind(&loop);
2845 {
2846 Register tmp = temps.AcquireW();
2847 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
2848 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
2849 }
2850 __ Cmp(src_curr_addr, src_stop_addr);
2851 __ B(&loop, ne);
Roland Levillain0b671c02016-08-19 12:02:34 +01002852 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002853 __ Bind(&done);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002854 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002855 }
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002856
donghui.baic2ec9ad2016-03-10 14:02:55 +08002857 // We only need one card marking on the destination array.
2858 codegen_->MarkGCCard(dest.W(), Register(), /* value_can_be_null */ false);
2859
Roland Levillain0b671c02016-08-19 12:02:34 +01002860 __ Bind(intrinsic_slow_path->GetExitLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002861}
2862
Anton Kirilova3ffea22016-04-07 17:02:37 +01002863static void GenIsInfinite(LocationSummary* locations,
2864 bool is64bit,
Scott Wakeling97c72b72016-06-24 16:19:36 +01002865 MacroAssembler* masm) {
Anton Kirilova3ffea22016-04-07 17:02:37 +01002866 Operand infinity;
2867 Register out;
2868
2869 if (is64bit) {
2870 infinity = kPositiveInfinityDouble;
2871 out = XRegisterFrom(locations->Out());
2872 } else {
2873 infinity = kPositiveInfinityFloat;
2874 out = WRegisterFrom(locations->Out());
2875 }
2876
Scott Wakeling97c72b72016-06-24 16:19:36 +01002877 const Register zero = vixl::aarch64::Assembler::AppropriateZeroRegFor(out);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002878
2879 MoveFPToInt(locations, is64bit, masm);
2880 __ Eor(out, out, infinity);
2881 // We don't care about the sign bit, so shift left.
2882 __ Cmp(zero, Operand(out, LSL, 1));
2883 __ Cset(out, eq);
2884}
2885
2886void IntrinsicLocationsBuilderARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2887 CreateFPToIntLocations(arena_, invoke);
2888}
2889
2890void IntrinsicCodeGeneratorARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2891 GenIsInfinite(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
2892}
2893
2894void IntrinsicLocationsBuilderARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2895 CreateFPToIntLocations(arena_, invoke);
2896}
2897
2898void IntrinsicCodeGeneratorARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2899 GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
2900}
2901
TatWai Chongd8c052a2016-11-02 16:12:48 +08002902void IntrinsicLocationsBuilderARM64::VisitReferenceGetReferent(HInvoke* invoke) {
2903 if (kEmitCompilerReadBarrier) {
2904 // Do not intrinsify this call with the read barrier configuration.
2905 return;
2906 }
2907 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2908 LocationSummary::kCallOnSlowPath,
2909 kIntrinsified);
2910 locations->SetInAt(0, Location::RequiresRegister());
2911 locations->SetOut(Location::SameAsFirstInput());
2912 locations->AddTemp(Location::RequiresRegister());
2913}
2914
2915void IntrinsicCodeGeneratorARM64::VisitReferenceGetReferent(HInvoke* invoke) {
2916 DCHECK(!kEmitCompilerReadBarrier);
2917 MacroAssembler* masm = GetVIXLAssembler();
2918 LocationSummary* locations = invoke->GetLocations();
2919
2920 Register obj = InputRegisterAt(invoke, 0);
2921 Register out = OutputRegister(invoke);
2922
2923 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2924 codegen_->AddSlowPath(slow_path);
2925
2926 // Load ArtMethod first.
2927 HInvokeStaticOrDirect* invoke_direct = invoke->AsInvokeStaticOrDirect();
2928 DCHECK(invoke_direct != nullptr);
2929 Register temp0 = XRegisterFrom(codegen_->GenerateCalleeMethodStaticOrDirectCall(
2930 invoke_direct, locations->GetTemp(0)));
2931
2932 // Now get declaring class.
2933 __ Ldr(temp0.W(), MemOperand(temp0, ArtMethod::DeclaringClassOffset().Int32Value()));
2934
2935 uint32_t slow_path_flag_offset = codegen_->GetReferenceSlowFlagOffset();
2936 uint32_t disable_flag_offset = codegen_->GetReferenceDisableFlagOffset();
2937 DCHECK_NE(slow_path_flag_offset, 0u);
2938 DCHECK_NE(disable_flag_offset, 0u);
2939 DCHECK_NE(slow_path_flag_offset, disable_flag_offset);
2940
2941 // Check static flags that prevent using intrinsic.
2942 if (slow_path_flag_offset == disable_flag_offset + 1) {
2943 // Load two adjacent flags in one 64-bit load.
2944 __ Ldr(temp0, MemOperand(temp0, disable_flag_offset));
2945 } else {
2946 UseScratchRegisterScope temps(masm);
2947 Register temp1 = temps.AcquireW();
2948 __ Ldr(temp1.W(), MemOperand(temp0, disable_flag_offset));
2949 __ Ldr(temp0.W(), MemOperand(temp0, slow_path_flag_offset));
2950 __ Orr(temp0, temp1, temp0);
2951 }
2952 __ Cbnz(temp0, slow_path->GetEntryLabel());
2953
Artem Serov914d7a82017-02-07 14:33:49 +00002954 {
2955 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
2956 vixl::EmissionCheckScope guard(codegen_->GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
2957 // Fast path.
2958 __ Ldr(out, HeapOperand(obj, mirror::Reference::ReferentOffset().Int32Value()));
2959 codegen_->MaybeRecordImplicitNullCheck(invoke);
2960 }
TatWai Chongd8c052a2016-11-02 16:12:48 +08002961 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(out);
2962 __ Bind(slow_path->GetExitLabel());
2963}
2964
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002965void IntrinsicLocationsBuilderARM64::VisitIntegerValueOf(HInvoke* invoke) {
2966 InvokeRuntimeCallingConvention calling_convention;
2967 IntrinsicVisitor::ComputeIntegerValueOfLocations(
2968 invoke,
2969 codegen_,
2970 calling_convention.GetReturnLocation(Primitive::kPrimNot),
2971 Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
2972}
2973
2974void IntrinsicCodeGeneratorARM64::VisitIntegerValueOf(HInvoke* invoke) {
2975 IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo();
2976 LocationSummary* locations = invoke->GetLocations();
2977 MacroAssembler* masm = GetVIXLAssembler();
2978
2979 Register out = RegisterFrom(locations->Out(), Primitive::kPrimNot);
2980 UseScratchRegisterScope temps(masm);
2981 Register temp = temps.AcquireW();
2982 InvokeRuntimeCallingConvention calling_convention;
2983 Register argument = calling_convention.GetRegisterAt(0);
2984 if (invoke->InputAt(0)->IsConstant()) {
2985 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
2986 if (value >= info.low && value <= info.high) {
2987 // Just embed the j.l.Integer in the code.
2988 ScopedObjectAccess soa(Thread::Current());
2989 mirror::Object* boxed = info.cache->Get(value + (-info.low));
2990 DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed));
2991 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(boxed));
2992 __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
2993 } else {
2994 // Allocate and initialize a new j.l.Integer.
2995 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
2996 // JIT object table.
2997 uint32_t address =
2998 dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2999 __ Ldr(argument.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
3000 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
3001 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
3002 __ Mov(temp.W(), value);
3003 __ Str(temp.W(), HeapOperand(out.W(), info.value_offset));
3004 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
3005 // one.
3006 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3007 }
3008 } else {
3009 Register in = RegisterFrom(locations->InAt(0), Primitive::kPrimInt);
3010 // Check bounds of our cache.
3011 __ Add(out.W(), in.W(), -info.low);
3012 __ Cmp(out.W(), info.high - info.low + 1);
3013 vixl::aarch64::Label allocate, done;
3014 __ B(&allocate, hs);
3015 // If the value is within the bounds, load the j.l.Integer directly from the array.
3016 uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
3017 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.cache));
3018 __ Ldr(temp.W(), codegen_->DeduplicateBootImageAddressLiteral(data_offset + address));
3019 MemOperand source = HeapOperand(
3020 temp, out.X(), LSL, Primitive::ComponentSizeShift(Primitive::kPrimNot));
3021 codegen_->Load(Primitive::kPrimNot, out, source);
3022 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(out);
3023 __ B(&done);
3024 __ Bind(&allocate);
3025 // Otherwise allocate and initialize a new j.l.Integer.
3026 address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
3027 __ Ldr(argument.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
3028 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
3029 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
3030 __ Str(in.W(), HeapOperand(out.W(), info.value_offset));
3031 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
3032 // one.
3033 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3034 __ Bind(&done);
3035 }
3036}
3037
Aart Bik2f9fcc92016-03-01 15:16:54 -08003038UNIMPLEMENTED_INTRINSIC(ARM64, IntegerHighestOneBit)
3039UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit)
3040UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit)
3041UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit)
Andreas Gampe878d58c2015-01-15 23:24:00 -08003042
Aart Bikff7d89c2016-11-07 08:49:28 -08003043UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf);
3044UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08003045UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferAppend);
3046UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferLength);
3047UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferToString);
3048UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderAppend);
3049UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderLength);
3050UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08003051
Aart Bik0e54c012016-03-04 12:08:31 -08003052// 1.8.
3053UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt)
3054UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong)
3055UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt)
3056UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong)
3057UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08003058
Aart Bik2f9fcc92016-03-01 15:16:54 -08003059UNREACHABLE_INTRINSICS(ARM64)
Roland Levillain4d027112015-07-01 15:41:14 +01003060
3061#undef __
3062
Andreas Gampe878d58c2015-01-15 23:24:00 -08003063} // namespace arm64
3064} // namespace art