blob: 96efe7f3b1635d4aa0e58ed5d33996d2b6f64fd7 [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"
Andreas Gampe09659c22017-09-18 18:23:32 -070024#include "heap_poisoning.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080025#include "intrinsics.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080026#include "lock_word.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080027#include "mirror/array-inl.h"
Andreas Gampec15a2f42017-04-21 12:09:39 -070028#include "mirror/object_array-inl.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080029#include "mirror/reference.h"
Vladimir Markoe39f14f2017-02-10 15:44:25 +000030#include "mirror/string-inl.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080031#include "scoped_thread_state_change-inl.h"
Andreas Gampeb486a982017-06-01 13:45:54 -070032#include "thread-current-inl.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080033#include "utils/arm64/assembler_arm64.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080034
Scott Wakeling97c72b72016-06-24 16:19:36 +010035using namespace vixl::aarch64; // NOLINT(build/namespaces)
Andreas Gampe878d58c2015-01-15 23:24:00 -080036
Artem Serovaf4e42a2016-08-08 15:11:24 +010037// TODO(VIXL): Make VIXL compile with -Wshadow.
Scott Wakeling97c72b72016-06-24 16:19:36 +010038#pragma GCC diagnostic push
39#pragma GCC diagnostic ignored "-Wshadow"
Artem Serovaf4e42a2016-08-08 15:11:24 +010040#include "aarch64/disasm-aarch64.h"
41#include "aarch64/macro-assembler-aarch64.h"
Scott Wakeling97c72b72016-06-24 16:19:36 +010042#pragma GCC diagnostic pop
Andreas Gampe878d58c2015-01-15 23:24:00 -080043
44namespace art {
45
46namespace arm64 {
47
48using helpers::DRegisterFrom;
49using helpers::FPRegisterFrom;
50using helpers::HeapOperand;
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +000051using helpers::LocationFrom;
Scott Wakeling9ee23f42015-07-23 10:44:35 +010052using helpers::OperandFrom;
Andreas Gampe878d58c2015-01-15 23:24:00 -080053using helpers::RegisterFrom;
54using helpers::SRegisterFrom;
55using helpers::WRegisterFrom;
56using helpers::XRegisterFrom;
xueliang.zhong49924c92016-03-03 10:52:51 +000057using helpers::InputRegisterAt;
Scott Wakeling1f36f412016-04-21 11:13:45 +010058using helpers::OutputRegister;
Andreas Gampe878d58c2015-01-15 23:24:00 -080059
Andreas Gampe878d58c2015-01-15 23:24:00 -080060namespace {
61
62ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) {
63 return MemOperand(XRegisterFrom(location), offset);
64}
65
66} // namespace
67
Scott Wakeling97c72b72016-06-24 16:19:36 +010068MacroAssembler* IntrinsicCodeGeneratorARM64::GetVIXLAssembler() {
Alexandre Rames087930f2016-08-02 13:45:28 +010069 return codegen_->GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -080070}
71
72ArenaAllocator* IntrinsicCodeGeneratorARM64::GetAllocator() {
73 return codegen_->GetGraph()->GetArena();
74}
75
Alexandre Rames087930f2016-08-02 13:45:28 +010076#define __ codegen->GetVIXLAssembler()->
Andreas Gampe878d58c2015-01-15 23:24:00 -080077
78static void MoveFromReturnRegister(Location trg,
79 Primitive::Type type,
80 CodeGeneratorARM64* codegen) {
81 if (!trg.IsValid()) {
82 DCHECK(type == Primitive::kPrimVoid);
83 return;
84 }
85
86 DCHECK_NE(type, Primitive::kPrimVoid);
87
Jeff Hao848f70a2014-01-15 13:49:50 -080088 if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) {
Andreas Gampe878d58c2015-01-15 23:24:00 -080089 Register trg_reg = RegisterFrom(trg, type);
90 Register res_reg = RegisterFrom(ARM64ReturnLocation(type), type);
91 __ Mov(trg_reg, res_reg, kDiscardForSameWReg);
92 } else {
93 FPRegister trg_reg = FPRegisterFrom(trg, type);
94 FPRegister res_reg = FPRegisterFrom(ARM64ReturnLocation(type), type);
95 __ Fmov(trg_reg, res_reg);
96 }
97}
98
Roland Levillainec525fc2015-04-28 15:50:20 +010099static void MoveArguments(HInvoke* invoke, CodeGeneratorARM64* codegen) {
Roland Levillain2d27c8e2015-04-28 15:48:45 +0100100 InvokeDexCallingConventionVisitorARM64 calling_convention_visitor;
Roland Levillainec525fc2015-04-28 15:50:20 +0100101 IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800102}
103
104// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
105// call. This will copy the arguments into the positions for a regular call.
106//
107// Note: The actual parameters are required to be in the locations given by the invoke's location
108// summary. If an intrinsic modifies those locations before a slowpath call, they must be
109// restored!
110class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 {
111 public:
David Srbecky9cd6d372016-02-09 15:24:47 +0000112 explicit IntrinsicSlowPathARM64(HInvoke* invoke)
113 : SlowPathCodeARM64(invoke), invoke_(invoke) { }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800114
115 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
116 CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
117 __ Bind(GetEntryLabel());
118
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000119 SaveLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800120
Roland Levillainec525fc2015-04-28 15:50:20 +0100121 MoveArguments(invoke_, codegen);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800122
Artem Serov914d7a82017-02-07 14:33:49 +0000123 {
124 // Ensure that between the BLR (emitted by Generate*Call) and RecordPcInfo there
125 // are no pools emitted.
126 vixl::EmissionCheckScope guard(codegen->GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes);
127 if (invoke_->IsInvokeStaticOrDirect()) {
Vladimir Markoe7197bf2017-06-02 17:00:23 +0100128 codegen->GenerateStaticOrDirectCall(
129 invoke_->AsInvokeStaticOrDirect(), LocationFrom(kArtMethodRegister), this);
Artem Serov914d7a82017-02-07 14:33:49 +0000130 } else {
Vladimir Markoe7197bf2017-06-02 17:00:23 +0100131 codegen->GenerateVirtualCall(
132 invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister), this);
Artem Serov914d7a82017-02-07 14:33:49 +0000133 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800134 }
135
136 // Copy the result back to the expected output.
137 Location out = invoke_->GetLocations()->Out();
138 if (out.IsValid()) {
139 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
140 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
141 MoveFromReturnRegister(out, invoke_->GetType(), codegen);
142 }
143
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000144 RestoreLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800145 __ B(GetExitLabel());
146 }
147
Alexandre Rames9931f312015-06-19 14:47:01 +0100148 const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathARM64"; }
149
Andreas Gampe878d58c2015-01-15 23:24:00 -0800150 private:
151 // The instruction where this slow path is happening.
152 HInvoke* const invoke_;
153
154 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM64);
155};
156
Roland Levillain0b671c02016-08-19 12:02:34 +0100157// Slow path implementing the SystemArrayCopy intrinsic copy loop with read barriers.
158class ReadBarrierSystemArrayCopySlowPathARM64 : public SlowPathCodeARM64 {
159 public:
160 ReadBarrierSystemArrayCopySlowPathARM64(HInstruction* instruction, Location tmp)
161 : SlowPathCodeARM64(instruction), tmp_(tmp) {
162 DCHECK(kEmitCompilerReadBarrier);
163 DCHECK(kUseBakerReadBarrier);
164 }
165
166 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
167 CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
168 LocationSummary* locations = instruction_->GetLocations();
169 DCHECK(locations->CanCall());
170 DCHECK(instruction_->IsInvokeStaticOrDirect())
171 << "Unexpected instruction in read barrier arraycopy slow path: "
172 << instruction_->DebugName();
173 DCHECK(instruction_->GetLocations()->Intrinsified());
174 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kSystemArrayCopy);
175
176 const int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
177
178 Register src_curr_addr = XRegisterFrom(locations->GetTemp(0));
179 Register dst_curr_addr = XRegisterFrom(locations->GetTemp(1));
180 Register src_stop_addr = XRegisterFrom(locations->GetTemp(2));
181 Register tmp_reg = WRegisterFrom(tmp_);
182
183 __ Bind(GetEntryLabel());
184 vixl::aarch64::Label slow_copy_loop;
185 __ Bind(&slow_copy_loop);
186 __ Ldr(tmp_reg, MemOperand(src_curr_addr, element_size, PostIndex));
187 codegen->GetAssembler()->MaybeUnpoisonHeapReference(tmp_reg);
188 // TODO: Inline the mark bit check before calling the runtime?
189 // tmp_reg = ReadBarrier::Mark(tmp_reg);
190 // No need to save live registers; it's taken care of by the
191 // entrypoint. Also, there is no need to update the stack mask,
192 // as this runtime call will not trigger a garbage collection.
193 // (See ReadBarrierMarkSlowPathARM64::EmitNativeCode for more
194 // explanations.)
195 DCHECK_NE(tmp_.reg(), LR);
196 DCHECK_NE(tmp_.reg(), WSP);
197 DCHECK_NE(tmp_.reg(), WZR);
198 // IP0 is used internally by the ReadBarrierMarkRegX entry point
199 // as a temporary (and not preserved). It thus cannot be used by
200 // any live register in this slow path.
201 DCHECK_NE(LocationFrom(src_curr_addr).reg(), IP0);
202 DCHECK_NE(LocationFrom(dst_curr_addr).reg(), IP0);
203 DCHECK_NE(LocationFrom(src_stop_addr).reg(), IP0);
204 DCHECK_NE(tmp_.reg(), IP0);
205 DCHECK(0 <= tmp_.reg() && tmp_.reg() < kNumberOfWRegisters) << tmp_.reg();
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000206 // TODO: Load the entrypoint once before the loop, instead of
207 // loading it at every iteration.
Roland Levillain0b671c02016-08-19 12:02:34 +0100208 int32_t entry_point_offset =
Roland Levillain97c46462017-05-11 14:04:03 +0100209 Thread::ReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(tmp_.reg());
Roland Levillain0b671c02016-08-19 12:02:34 +0100210 // This runtime call does not require a stack map.
211 codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
212 codegen->GetAssembler()->MaybePoisonHeapReference(tmp_reg);
213 __ Str(tmp_reg, MemOperand(dst_curr_addr, element_size, PostIndex));
214 __ Cmp(src_curr_addr, src_stop_addr);
215 __ B(&slow_copy_loop, ne);
216 __ B(GetExitLabel());
217 }
218
219 const char* GetDescription() const OVERRIDE { return "ReadBarrierSystemArrayCopySlowPathARM64"; }
220
221 private:
222 Location tmp_;
223
224 DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathARM64);
225};
Andreas Gampe878d58c2015-01-15 23:24:00 -0800226#undef __
227
228bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) {
229 Dispatch(invoke);
230 LocationSummary* res = invoke->GetLocations();
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000231 if (res == nullptr) {
232 return false;
233 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000234 return res->Intrinsified();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800235}
236
237#define __ masm->
238
239static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
240 LocationSummary* locations = new (arena) LocationSummary(invoke,
241 LocationSummary::kNoCall,
242 kIntrinsified);
243 locations->SetInAt(0, Location::RequiresFpuRegister());
244 locations->SetOut(Location::RequiresRegister());
245}
246
247static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
248 LocationSummary* locations = new (arena) LocationSummary(invoke,
249 LocationSummary::kNoCall,
250 kIntrinsified);
251 locations->SetInAt(0, Location::RequiresRegister());
252 locations->SetOut(Location::RequiresFpuRegister());
253}
254
Scott Wakeling97c72b72016-06-24 16:19:36 +0100255static void MoveFPToInt(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800256 Location input = locations->InAt(0);
257 Location output = locations->Out();
258 __ Fmov(is64bit ? XRegisterFrom(output) : WRegisterFrom(output),
259 is64bit ? DRegisterFrom(input) : SRegisterFrom(input));
260}
261
Scott Wakeling97c72b72016-06-24 16:19:36 +0100262static void MoveIntToFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800263 Location input = locations->InAt(0);
264 Location output = locations->Out();
265 __ Fmov(is64bit ? DRegisterFrom(output) : SRegisterFrom(output),
266 is64bit ? XRegisterFrom(input) : WRegisterFrom(input));
267}
268
269void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
270 CreateFPToIntLocations(arena_, invoke);
271}
272void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
273 CreateIntToFPLocations(arena_, invoke);
274}
275
276void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000277 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800278}
279void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000280 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800281}
282
283void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
284 CreateFPToIntLocations(arena_, invoke);
285}
286void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
287 CreateIntToFPLocations(arena_, invoke);
288}
289
290void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000291 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800292}
293void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000294 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800295}
296
297static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
298 LocationSummary* locations = new (arena) LocationSummary(invoke,
299 LocationSummary::kNoCall,
300 kIntrinsified);
301 locations->SetInAt(0, Location::RequiresRegister());
302 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
303}
304
305static void GenReverseBytes(LocationSummary* locations,
306 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100307 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800308 Location in = locations->InAt(0);
309 Location out = locations->Out();
310
311 switch (type) {
312 case Primitive::kPrimShort:
313 __ Rev16(WRegisterFrom(out), WRegisterFrom(in));
314 __ Sxth(WRegisterFrom(out), WRegisterFrom(out));
315 break;
316 case Primitive::kPrimInt:
317 case Primitive::kPrimLong:
318 __ Rev(RegisterFrom(out, type), RegisterFrom(in, type));
319 break;
320 default:
321 LOG(FATAL) << "Unexpected size for reverse-bytes: " << type;
322 UNREACHABLE();
323 }
324}
325
326void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
327 CreateIntToIntLocations(arena_, invoke);
328}
329
330void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
331 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
332}
333
334void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) {
335 CreateIntToIntLocations(arena_, invoke);
336}
337
338void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) {
339 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
340}
341
342void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) {
343 CreateIntToIntLocations(arena_, invoke);
344}
345
346void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
347 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
348}
349
Aart Bik7b565022016-01-28 14:36:22 -0800350static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
351 LocationSummary* locations = new (arena) LocationSummary(invoke,
352 LocationSummary::kNoCall,
353 kIntrinsified);
354 locations->SetInAt(0, Location::RequiresRegister());
355 locations->SetInAt(1, Location::RequiresRegister());
356 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
357}
358
Scott Wakeling611d3392015-07-10 11:42:06 +0100359static void GenNumberOfLeadingZeros(LocationSummary* locations,
360 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100361 MacroAssembler* masm) {
Scott Wakeling611d3392015-07-10 11:42:06 +0100362 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
363
364 Location in = locations->InAt(0);
365 Location out = locations->Out();
366
367 __ Clz(RegisterFrom(out, type), RegisterFrom(in, type));
368}
369
370void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
371 CreateIntToIntLocations(arena_, invoke);
372}
373
374void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
375 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
376}
377
378void IntrinsicLocationsBuilderARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
379 CreateIntToIntLocations(arena_, invoke);
380}
381
382void IntrinsicCodeGeneratorARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
383 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
384}
385
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100386static void GenNumberOfTrailingZeros(LocationSummary* locations,
387 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100388 MacroAssembler* masm) {
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100389 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
390
391 Location in = locations->InAt(0);
392 Location out = locations->Out();
393
394 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
395 __ Clz(RegisterFrom(out, type), RegisterFrom(out, type));
396}
397
398void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
399 CreateIntToIntLocations(arena_, invoke);
400}
401
402void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
403 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
404}
405
406void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
407 CreateIntToIntLocations(arena_, invoke);
408}
409
410void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
411 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
412}
413
Andreas Gampe878d58c2015-01-15 23:24:00 -0800414static void GenReverse(LocationSummary* locations,
415 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100416 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800417 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
418
419 Location in = locations->InAt(0);
420 Location out = locations->Out();
421
422 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
423}
424
425void IntrinsicLocationsBuilderARM64::VisitIntegerReverse(HInvoke* invoke) {
426 CreateIntToIntLocations(arena_, invoke);
427}
428
429void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) {
430 GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
431}
432
433void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) {
434 CreateIntToIntLocations(arena_, invoke);
435}
436
437void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
438 GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
439}
440
Scott Wakeling97c72b72016-06-24 16:19:36 +0100441static void GenBitCount(HInvoke* instr, Primitive::Type type, MacroAssembler* masm) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100442 DCHECK(Primitive::IsIntOrLongType(type)) << type;
443 DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
444 DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
xueliang.zhong49924c92016-03-03 10:52:51 +0000445
xueliang.zhong49924c92016-03-03 10:52:51 +0000446 UseScratchRegisterScope temps(masm);
447
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000448 Register src = InputRegisterAt(instr, 0);
Roland Levillainfa3912e2016-04-01 18:21:55 +0100449 Register dst = RegisterFrom(instr->GetLocations()->Out(), type);
450 FPRegister fpr = (type == Primitive::kPrimLong) ? temps.AcquireD() : temps.AcquireS();
xueliang.zhong49924c92016-03-03 10:52:51 +0000451
452 __ Fmov(fpr, src);
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000453 __ Cnt(fpr.V8B(), fpr.V8B());
454 __ Addv(fpr.B(), fpr.V8B());
xueliang.zhong49924c92016-03-03 10:52:51 +0000455 __ Fmov(dst, fpr);
456}
457
458void IntrinsicLocationsBuilderARM64::VisitLongBitCount(HInvoke* invoke) {
459 CreateIntToIntLocations(arena_, invoke);
460}
461
462void IntrinsicCodeGeneratorARM64::VisitLongBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100463 GenBitCount(invoke, Primitive::kPrimLong, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000464}
465
466void IntrinsicLocationsBuilderARM64::VisitIntegerBitCount(HInvoke* invoke) {
467 CreateIntToIntLocations(arena_, invoke);
468}
469
470void IntrinsicCodeGeneratorARM64::VisitIntegerBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100471 GenBitCount(invoke, Primitive::kPrimInt, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000472}
473
Petre-Ionut Tudorda483162017-08-14 13:54:31 +0100474static void GenHighestOneBit(HInvoke* invoke, Primitive::Type type, MacroAssembler* masm) {
475 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
476
477 UseScratchRegisterScope temps(masm);
478
479 Register src = InputRegisterAt(invoke, 0);
480 Register dst = RegisterFrom(invoke->GetLocations()->Out(), type);
481 Register temp = (type == Primitive::kPrimLong) ? temps.AcquireX() : temps.AcquireW();
482 size_t high_bit = (type == Primitive::kPrimLong) ? 63u : 31u;
483 size_t clz_high_bit = (type == Primitive::kPrimLong) ? 6u : 5u;
484
485 __ Clz(temp, src);
486 __ Mov(dst, UINT64_C(1) << high_bit); // MOV (bitmask immediate)
487 __ Bic(dst, dst, Operand(temp, LSL, high_bit - clz_high_bit)); // Clear dst if src was 0.
488 __ Lsr(dst, dst, temp);
489}
490
491void IntrinsicLocationsBuilderARM64::VisitIntegerHighestOneBit(HInvoke* invoke) {
492 CreateIntToIntLocations(arena_, invoke);
493}
494
495void IntrinsicCodeGeneratorARM64::VisitIntegerHighestOneBit(HInvoke* invoke) {
496 GenHighestOneBit(invoke, Primitive::kPrimInt, GetVIXLAssembler());
497}
498
499void IntrinsicLocationsBuilderARM64::VisitLongHighestOneBit(HInvoke* invoke) {
500 CreateIntToIntLocations(arena_, invoke);
501}
502
503void IntrinsicCodeGeneratorARM64::VisitLongHighestOneBit(HInvoke* invoke) {
504 GenHighestOneBit(invoke, Primitive::kPrimLong, GetVIXLAssembler());
505}
506
507static void GenLowestOneBit(HInvoke* invoke, Primitive::Type type, MacroAssembler* masm) {
508 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
509
510 UseScratchRegisterScope temps(masm);
511
512 Register src = InputRegisterAt(invoke, 0);
513 Register dst = RegisterFrom(invoke->GetLocations()->Out(), type);
514 Register temp = (type == Primitive::kPrimLong) ? temps.AcquireX() : temps.AcquireW();
515
516 __ Neg(temp, src);
517 __ And(dst, temp, src);
518}
519
520void IntrinsicLocationsBuilderARM64::VisitIntegerLowestOneBit(HInvoke* invoke) {
521 CreateIntToIntLocations(arena_, invoke);
522}
523
524void IntrinsicCodeGeneratorARM64::VisitIntegerLowestOneBit(HInvoke* invoke) {
525 GenLowestOneBit(invoke, Primitive::kPrimInt, GetVIXLAssembler());
526}
527
528void IntrinsicLocationsBuilderARM64::VisitLongLowestOneBit(HInvoke* invoke) {
529 CreateIntToIntLocations(arena_, invoke);
530}
531
532void IntrinsicCodeGeneratorARM64::VisitLongLowestOneBit(HInvoke* invoke) {
533 GenLowestOneBit(invoke, Primitive::kPrimLong, GetVIXLAssembler());
534}
535
Andreas Gampe878d58c2015-01-15 23:24:00 -0800536static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800537 LocationSummary* locations = new (arena) LocationSummary(invoke,
538 LocationSummary::kNoCall,
539 kIntrinsified);
540 locations->SetInAt(0, Location::RequiresFpuRegister());
541 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
542}
543
Scott Wakeling97c72b72016-06-24 16:19:36 +0100544static void MathAbsFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800545 Location in = locations->InAt(0);
546 Location out = locations->Out();
547
548 FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in);
549 FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out);
550
551 __ Fabs(out_reg, in_reg);
552}
553
554void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) {
555 CreateFPToFPLocations(arena_, invoke);
556}
557
558void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000559 MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800560}
561
562void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) {
563 CreateFPToFPLocations(arena_, invoke);
564}
565
566void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000567 MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800568}
569
Andreas Gampe878d58c2015-01-15 23:24:00 -0800570static void GenAbsInteger(LocationSummary* locations,
571 bool is64bit,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100572 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800573 Location in = locations->InAt(0);
574 Location output = locations->Out();
575
576 Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in);
577 Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output);
578
579 __ Cmp(in_reg, Operand(0));
580 __ Cneg(out_reg, in_reg, lt);
581}
582
583void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
Vladimir Marko4fae4fb2017-08-29 12:00:09 +0100584 CreateIntToIntLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800585}
586
587void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000588 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800589}
590
591void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
Vladimir Marko4fae4fb2017-08-29 12:00:09 +0100592 CreateIntToIntLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800593}
594
595void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000596 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800597}
598
599static void GenMinMaxFP(LocationSummary* locations,
600 bool is_min,
601 bool is_double,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100602 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800603 Location op1 = locations->InAt(0);
604 Location op2 = locations->InAt(1);
605 Location out = locations->Out();
606
607 FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1);
608 FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2);
609 FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out);
610 if (is_min) {
611 __ Fmin(out_reg, op1_reg, op2_reg);
612 } else {
613 __ Fmax(out_reg, op1_reg, op2_reg);
614 }
615}
616
617static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
618 LocationSummary* locations = new (arena) LocationSummary(invoke,
619 LocationSummary::kNoCall,
620 kIntrinsified);
621 locations->SetInAt(0, Location::RequiresFpuRegister());
622 locations->SetInAt(1, Location::RequiresFpuRegister());
623 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
624}
625
626void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
627 CreateFPFPToFPLocations(arena_, invoke);
628}
629
630void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000631 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800632}
633
634void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
635 CreateFPFPToFPLocations(arena_, invoke);
636}
637
638void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000639 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800640}
641
642void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
643 CreateFPFPToFPLocations(arena_, invoke);
644}
645
646void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000647 GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800648}
649
650void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
651 CreateFPFPToFPLocations(arena_, invoke);
652}
653
654void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000655 GenMinMaxFP(
656 invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800657}
658
659static void GenMinMax(LocationSummary* locations,
660 bool is_min,
661 bool is_long,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100662 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800663 Location op1 = locations->InAt(0);
664 Location op2 = locations->InAt(1);
665 Location out = locations->Out();
666
667 Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
668 Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
669 Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
670
671 __ Cmp(op1_reg, op2_reg);
672 __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
673}
674
Andreas Gampe878d58c2015-01-15 23:24:00 -0800675void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
676 CreateIntIntToIntLocations(arena_, invoke);
677}
678
679void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000680 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800681}
682
683void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) {
684 CreateIntIntToIntLocations(arena_, invoke);
685}
686
687void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000688 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800689}
690
691void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) {
692 CreateIntIntToIntLocations(arena_, invoke);
693}
694
695void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000696 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800697}
698
699void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) {
700 CreateIntIntToIntLocations(arena_, invoke);
701}
702
703void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000704 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800705}
706
707void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) {
708 CreateFPToFPLocations(arena_, invoke);
709}
710
711void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) {
712 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100713 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800714 __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
715}
716
717void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) {
718 CreateFPToFPLocations(arena_, invoke);
719}
720
721void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) {
722 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100723 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800724 __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
725}
726
727void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) {
728 CreateFPToFPLocations(arena_, invoke);
729}
730
731void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) {
732 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100733 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800734 __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
735}
736
737void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) {
738 CreateFPToFPLocations(arena_, invoke);
739}
740
741void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) {
742 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100743 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800744 __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
745}
746
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100747static void CreateFPToIntPlusFPTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800748 LocationSummary* locations = new (arena) LocationSummary(invoke,
749 LocationSummary::kNoCall,
750 kIntrinsified);
751 locations->SetInAt(0, Location::RequiresFpuRegister());
752 locations->SetOut(Location::RequiresRegister());
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100753 locations->AddTemp(Location::RequiresFpuRegister());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800754}
755
Scott Wakeling97c72b72016-06-24 16:19:36 +0100756static void GenMathRound(HInvoke* invoke, bool is_double, vixl::aarch64::MacroAssembler* masm) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100757 // Java 8 API definition for Math.round():
758 // Return the closest long or int to the argument, with ties rounding to positive infinity.
759 //
760 // There is no single instruction in ARMv8 that can support the above definition.
761 // We choose to use FCVTAS here, because it has closest semantic.
762 // FCVTAS performs rounding to nearest integer, ties away from zero.
763 // For most inputs (positive values, zero or NaN), this instruction is enough.
764 // We only need a few handling code after FCVTAS if the input is negative half value.
765 //
766 // The reason why we didn't choose FCVTPS instruction here is that
767 // although it performs rounding toward positive infinity, it doesn't perform rounding to nearest.
768 // For example, FCVTPS(-1.9) = -1 and FCVTPS(1.1) = 2.
769 // If we were using this instruction, for most inputs, more handling code would be needed.
770 LocationSummary* l = invoke->GetLocations();
771 FPRegister in_reg = is_double ? DRegisterFrom(l->InAt(0)) : SRegisterFrom(l->InAt(0));
772 FPRegister tmp_fp = is_double ? DRegisterFrom(l->GetTemp(0)) : SRegisterFrom(l->GetTemp(0));
773 Register out_reg = is_double ? XRegisterFrom(l->Out()) : WRegisterFrom(l->Out());
Scott Wakeling97c72b72016-06-24 16:19:36 +0100774 vixl::aarch64::Label done;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800775
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100776 // Round to nearest integer, ties away from zero.
777 __ Fcvtas(out_reg, in_reg);
778
779 // For positive values, zero or NaN inputs, rounding is done.
Scott Wakeling97c72b72016-06-24 16:19:36 +0100780 __ Tbz(out_reg, out_reg.GetSizeInBits() - 1, &done);
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100781
782 // Handle input < 0 cases.
783 // If input is negative but not a tie, previous result (round to nearest) is valid.
784 // If input is a negative tie, out_reg += 1.
785 __ Frinta(tmp_fp, in_reg);
786 __ Fsub(tmp_fp, in_reg, tmp_fp);
787 __ Fcmp(tmp_fp, 0.5);
788 __ Cinc(out_reg, out_reg, eq);
789
790 __ Bind(&done);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800791}
792
793void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100794 CreateFPToIntPlusFPTempLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800795}
796
797void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100798 GenMathRound(invoke, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800799}
800
801void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100802 CreateFPToIntPlusFPTempLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800803}
804
805void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100806 GenMathRound(invoke, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800807}
808
809void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) {
810 CreateIntToIntLocations(arena_, invoke);
811}
812
813void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100814 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800815 __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()),
816 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
817}
818
819void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
820 CreateIntToIntLocations(arena_, invoke);
821}
822
823void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100824 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800825 __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()),
826 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
827}
828
829void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
830 CreateIntToIntLocations(arena_, invoke);
831}
832
833void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100834 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800835 __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()),
836 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
837}
838
839void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
840 CreateIntToIntLocations(arena_, invoke);
841}
842
843void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100844 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800845 __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()),
846 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
847}
848
849static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
850 LocationSummary* locations = new (arena) LocationSummary(invoke,
851 LocationSummary::kNoCall,
852 kIntrinsified);
853 locations->SetInAt(0, Location::RequiresRegister());
854 locations->SetInAt(1, Location::RequiresRegister());
855}
856
857void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) {
858 CreateIntIntToVoidLocations(arena_, invoke);
859}
860
861void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100862 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800863 __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)),
864 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
865}
866
867void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
868 CreateIntIntToVoidLocations(arena_, invoke);
869}
870
871void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100872 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800873 __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)),
874 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
875}
876
877void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
878 CreateIntIntToVoidLocations(arena_, invoke);
879}
880
881void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100882 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800883 __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)),
884 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
885}
886
887void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
888 CreateIntIntToVoidLocations(arena_, invoke);
889}
890
891void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100892 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800893 __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)),
894 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
895}
896
897void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) {
898 LocationSummary* locations = new (arena_) LocationSummary(invoke,
899 LocationSummary::kNoCall,
900 kIntrinsified);
901 locations->SetOut(Location::RequiresRegister());
902}
903
904void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) {
905 codegen_->Load(Primitive::kPrimNot, WRegisterFrom(invoke->GetLocations()->Out()),
Andreas Gampe542451c2016-07-26 09:02:02 -0700906 MemOperand(tr, Thread::PeerOffset<kArm64PointerSize>().Int32Value()));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800907}
908
909static void GenUnsafeGet(HInvoke* invoke,
910 Primitive::Type type,
911 bool is_volatile,
912 CodeGeneratorARM64* codegen) {
913 LocationSummary* locations = invoke->GetLocations();
914 DCHECK((type == Primitive::kPrimInt) ||
915 (type == Primitive::kPrimLong) ||
916 (type == Primitive::kPrimNot));
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000917 Location base_loc = locations->InAt(1);
918 Register base = WRegisterFrom(base_loc); // Object pointer.
919 Location offset_loc = locations->InAt(2);
920 Register offset = XRegisterFrom(offset_loc); // Long offset.
921 Location trg_loc = locations->Out();
922 Register trg = RegisterFrom(trg_loc, type);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800923
Roland Levillain44015862016-01-22 11:47:17 +0000924 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
925 // UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case.
Roland Levillain54f869e2017-03-06 13:54:11 +0000926 Register temp = WRegisterFrom(locations->GetTemp(0));
Roland Levillainbfea3352016-06-23 13:48:47 +0100927 codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke,
928 trg_loc,
929 base,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100930 /* offset */ 0u,
Roland Levillainbfea3352016-06-23 13:48:47 +0100931 /* index */ offset_loc,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100932 /* scale_factor */ 0u,
Roland Levillainbfea3352016-06-23 13:48:47 +0100933 temp,
934 /* needs_null_check */ false,
935 is_volatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800936 } else {
Roland Levillain44015862016-01-22 11:47:17 +0000937 // Other cases.
938 MemOperand mem_op(base.X(), offset);
939 if (is_volatile) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +0000940 codegen->LoadAcquire(invoke, trg, mem_op, /* needs_null_check */ true);
Roland Levillain44015862016-01-22 11:47:17 +0000941 } else {
942 codegen->Load(type, trg, mem_op);
943 }
Roland Levillain4d027112015-07-01 15:41:14 +0100944
Roland Levillain44015862016-01-22 11:47:17 +0000945 if (type == Primitive::kPrimNot) {
946 DCHECK(trg.IsW());
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100947 codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0u, offset_loc);
Roland Levillain44015862016-01-22 11:47:17 +0000948 }
Roland Levillain4d027112015-07-01 15:41:14 +0100949 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800950}
951
952static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000953 bool can_call = kEmitCompilerReadBarrier &&
954 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
955 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800956 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100957 (can_call
958 ? LocationSummary::kCallOnSlowPath
959 : LocationSummary::kNoCall),
Andreas Gampe878d58c2015-01-15 23:24:00 -0800960 kIntrinsified);
Vladimir Marko70e97462016-08-09 11:04:26 +0100961 if (can_call && kUseBakerReadBarrier) {
Vladimir Marko804b03f2016-09-14 16:26:36 +0100962 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Roland Levillain54f869e2017-03-06 13:54:11 +0000963 // We need a temporary register for the read barrier marking slow
964 // path in CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier.
965 locations->AddTemp(Location::RequiresRegister());
Vladimir Marko70e97462016-08-09 11:04:26 +0100966 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800967 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
968 locations->SetInAt(1, Location::RequiresRegister());
969 locations->SetInAt(2, Location::RequiresRegister());
Roland Levillainbfea3352016-06-23 13:48:47 +0100970 locations->SetOut(Location::RequiresRegister(),
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100971 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800972}
973
974void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
975 CreateIntIntIntToIntLocations(arena_, invoke);
976}
977void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
978 CreateIntIntIntToIntLocations(arena_, invoke);
979}
980void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
981 CreateIntIntIntToIntLocations(arena_, invoke);
982}
983void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
984 CreateIntIntIntToIntLocations(arena_, invoke);
985}
986void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
987 CreateIntIntIntToIntLocations(arena_, invoke);
988}
989void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
990 CreateIntIntIntToIntLocations(arena_, invoke);
991}
992
993void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000994 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800995}
996void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000997 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800998}
999void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001000 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001001}
1002void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001003 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001004}
1005void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001006 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001007}
1008void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001009 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001010}
1011
1012static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
1013 LocationSummary* locations = new (arena) LocationSummary(invoke,
1014 LocationSummary::kNoCall,
1015 kIntrinsified);
1016 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1017 locations->SetInAt(1, Location::RequiresRegister());
1018 locations->SetInAt(2, Location::RequiresRegister());
1019 locations->SetInAt(3, Location::RequiresRegister());
1020}
1021
1022void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
1023 CreateIntIntIntIntToVoid(arena_, invoke);
1024}
1025void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
1026 CreateIntIntIntIntToVoid(arena_, invoke);
1027}
1028void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
1029 CreateIntIntIntIntToVoid(arena_, invoke);
1030}
1031void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
1032 CreateIntIntIntIntToVoid(arena_, invoke);
1033}
1034void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
1035 CreateIntIntIntIntToVoid(arena_, invoke);
1036}
1037void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
1038 CreateIntIntIntIntToVoid(arena_, invoke);
1039}
1040void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
1041 CreateIntIntIntIntToVoid(arena_, invoke);
1042}
1043void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
1044 CreateIntIntIntIntToVoid(arena_, invoke);
1045}
1046void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
1047 CreateIntIntIntIntToVoid(arena_, invoke);
1048}
1049
Artem Serov914d7a82017-02-07 14:33:49 +00001050static void GenUnsafePut(HInvoke* invoke,
Andreas Gampe878d58c2015-01-15 23:24:00 -08001051 Primitive::Type type,
1052 bool is_volatile,
1053 bool is_ordered,
1054 CodeGeneratorARM64* codegen) {
Artem Serov914d7a82017-02-07 14:33:49 +00001055 LocationSummary* locations = invoke->GetLocations();
Alexandre Rames087930f2016-08-02 13:45:28 +01001056 MacroAssembler* masm = codegen->GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -08001057
1058 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
1059 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
1060 Register value = RegisterFrom(locations->InAt(3), type);
Roland Levillain4d027112015-07-01 15:41:14 +01001061 Register source = value;
Andreas Gampe878d58c2015-01-15 23:24:00 -08001062 MemOperand mem_op(base.X(), offset);
1063
Roland Levillain4d027112015-07-01 15:41:14 +01001064 {
1065 // We use a block to end the scratch scope before the write barrier, thus
1066 // freeing the temporary registers so they can be used in `MarkGCCard`.
1067 UseScratchRegisterScope temps(masm);
1068
1069 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1070 DCHECK(value.IsW());
1071 Register temp = temps.AcquireW();
1072 __ Mov(temp.W(), value.W());
1073 codegen->GetAssembler()->PoisonHeapReference(temp.W());
1074 source = temp;
Andreas Gampe878d58c2015-01-15 23:24:00 -08001075 }
Roland Levillain4d027112015-07-01 15:41:14 +01001076
1077 if (is_volatile || is_ordered) {
Artem Serov914d7a82017-02-07 14:33:49 +00001078 codegen->StoreRelease(invoke, type, source, mem_op, /* needs_null_check */ false);
Roland Levillain4d027112015-07-01 15:41:14 +01001079 } else {
1080 codegen->Store(type, source, mem_op);
1081 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001082 }
1083
1084 if (type == Primitive::kPrimNot) {
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001085 bool value_can_be_null = true; // TODO: Worth finding out this information?
1086 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001087 }
1088}
1089
1090void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001091 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001092 Primitive::kPrimInt,
1093 /* is_volatile */ false,
1094 /* is_ordered */ false,
1095 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001096}
1097void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001098 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001099 Primitive::kPrimInt,
1100 /* is_volatile */ false,
1101 /* is_ordered */ true,
1102 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001103}
1104void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001105 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001106 Primitive::kPrimInt,
1107 /* is_volatile */ true,
1108 /* is_ordered */ false,
1109 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001110}
1111void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001112 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001113 Primitive::kPrimNot,
1114 /* is_volatile */ false,
1115 /* is_ordered */ false,
1116 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001117}
1118void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001119 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001120 Primitive::kPrimNot,
1121 /* is_volatile */ false,
1122 /* is_ordered */ true,
1123 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001124}
1125void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001126 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001127 Primitive::kPrimNot,
1128 /* is_volatile */ true,
1129 /* is_ordered */ false,
1130 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001131}
1132void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001133 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001134 Primitive::kPrimLong,
1135 /* is_volatile */ false,
1136 /* is_ordered */ false,
1137 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001138}
1139void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001140 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001141 Primitive::kPrimLong,
1142 /* is_volatile */ false,
1143 /* is_ordered */ true,
1144 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001145}
1146void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001147 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001148 Primitive::kPrimLong,
1149 /* is_volatile */ true,
1150 /* is_ordered */ false,
1151 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001152}
1153
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001154static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
1155 HInvoke* invoke,
1156 Primitive::Type type) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001157 bool can_call = kEmitCompilerReadBarrier &&
1158 kUseBakerReadBarrier &&
1159 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001160 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001161 (can_call
1162 ? LocationSummary::kCallOnSlowPath
1163 : LocationSummary::kNoCall),
Andreas Gampe878d58c2015-01-15 23:24:00 -08001164 kIntrinsified);
1165 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1166 locations->SetInAt(1, Location::RequiresRegister());
1167 locations->SetInAt(2, Location::RequiresRegister());
1168 locations->SetInAt(3, Location::RequiresRegister());
1169 locations->SetInAt(4, Location::RequiresRegister());
1170
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001171 // If heap poisoning is enabled, we don't want the unpoisoning
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001172 // operations to potentially clobber the output. Likewise when
1173 // emitting a (Baker) read barrier, which may call.
1174 Location::OutputOverlap overlaps =
1175 ((kPoisonHeapReferences && type == Primitive::kPrimNot) || can_call)
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001176 ? Location::kOutputOverlap
1177 : Location::kNoOutputOverlap;
1178 locations->SetOut(Location::RequiresRegister(), overlaps);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001179 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1180 // Temporary register for (Baker) read barrier.
1181 locations->AddTemp(Location::RequiresRegister());
1182 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001183}
1184
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001185static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARM64* codegen) {
Alexandre Rames087930f2016-08-02 13:45:28 +01001186 MacroAssembler* masm = codegen->GetVIXLAssembler();
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001187 LocationSummary* locations = invoke->GetLocations();
Andreas Gampe878d58c2015-01-15 23:24:00 -08001188
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001189 Location out_loc = locations->Out();
1190 Register out = WRegisterFrom(out_loc); // Boolean result.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001191
1192 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001193 Location offset_loc = locations->InAt(2);
1194 Register offset = XRegisterFrom(offset_loc); // Long offset.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001195 Register expected = RegisterFrom(locations->InAt(3), type); // Expected.
1196 Register value = RegisterFrom(locations->InAt(4), type); // Value.
1197
1198 // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
1199 if (type == Primitive::kPrimNot) {
1200 // Mark card for object assuming new value is stored.
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001201 bool value_can_be_null = true; // TODO: Worth finding out this information?
1202 codegen->MarkGCCard(base, value, value_can_be_null);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001203
1204 // The only read barrier implementation supporting the
1205 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1206 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1207
1208 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1209 Register temp = WRegisterFrom(locations->GetTemp(0));
1210 // Need to make sure the reference stored in the field is a to-space
1211 // one before attempting the CAS or the CAS could fail incorrectly.
Roland Levillainff487002017-03-07 16:50:01 +00001212 codegen->UpdateReferenceFieldWithBakerReadBarrier(
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001213 invoke,
1214 out_loc, // Unused, used only as a "temporary" within the read barrier.
1215 base,
Roland Levillainff487002017-03-07 16:50:01 +00001216 /* field_offset */ offset_loc,
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001217 temp,
1218 /* needs_null_check */ false,
Roland Levillainff487002017-03-07 16:50:01 +00001219 /* use_load_acquire */ false);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001220 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001221 }
1222
1223 UseScratchRegisterScope temps(masm);
1224 Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
1225 Register tmp_value = temps.AcquireSameSizeAs(value); // Value in memory.
1226
1227 Register tmp_32 = tmp_value.W();
1228
1229 __ Add(tmp_ptr, base.X(), Operand(offset));
1230
Roland Levillain4d027112015-07-01 15:41:14 +01001231 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1232 codegen->GetAssembler()->PoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001233 if (value.Is(expected)) {
1234 // Do not poison `value`, as it is the same register as
1235 // `expected`, which has just been poisoned.
1236 } else {
1237 codegen->GetAssembler()->PoisonHeapReference(value);
1238 }
Roland Levillain4d027112015-07-01 15:41:14 +01001239 }
1240
Andreas Gampe878d58c2015-01-15 23:24:00 -08001241 // do {
1242 // tmp_value = [tmp_ptr] - expected;
1243 // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1244 // result = tmp_value != 0;
1245
Scott Wakeling97c72b72016-06-24 16:19:36 +01001246 vixl::aarch64::Label loop_head, exit_loop;
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001247 __ Bind(&loop_head);
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001248 __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
1249 __ Cmp(tmp_value, expected);
1250 __ B(&exit_loop, ne);
1251 __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
1252 __ Cbnz(tmp_32, &loop_head);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001253 __ Bind(&exit_loop);
1254 __ Cset(out, eq);
Roland Levillain4d027112015-07-01 15:41:14 +01001255
1256 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillain4d027112015-07-01 15:41:14 +01001257 codegen->GetAssembler()->UnpoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001258 if (value.Is(expected)) {
1259 // Do not unpoison `value`, as it is the same register as
1260 // `expected`, which has just been unpoisoned.
1261 } else {
1262 codegen->GetAssembler()->UnpoisonHeapReference(value);
1263 }
Roland Levillain4d027112015-07-01 15:41:14 +01001264 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001265}
1266
1267void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001268 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001269}
1270void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001271 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001272}
1273void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001274 // The only read barrier implementation supporting the
1275 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1276 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain985ff702015-10-23 13:25:35 +01001277 return;
1278 }
1279
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001280 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001281}
1282
1283void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001284 GenCas(invoke, Primitive::kPrimInt, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001285}
1286void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001287 GenCas(invoke, Primitive::kPrimLong, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001288}
1289void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001290 // The only read barrier implementation supporting the
1291 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1292 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01001293
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001294 GenCas(invoke, Primitive::kPrimNot, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001295}
1296
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001297void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001298 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Scott Wakeling1f36f412016-04-21 11:13:45 +01001299 invoke->InputAt(1)->CanBeNull()
1300 ? LocationSummary::kCallOnSlowPath
1301 : LocationSummary::kNoCall,
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001302 kIntrinsified);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001303 locations->SetInAt(0, Location::RequiresRegister());
1304 locations->SetInAt(1, Location::RequiresRegister());
1305 locations->AddTemp(Location::RequiresRegister());
1306 locations->AddTemp(Location::RequiresRegister());
1307 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001308 // Need temporary registers for String compression's feature.
1309 if (mirror::kUseStringCompression) {
1310 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001311 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001312 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001313}
1314
1315void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001316 MacroAssembler* masm = GetVIXLAssembler();
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001317 LocationSummary* locations = invoke->GetLocations();
1318
Alexandre Rames2ea91532016-08-11 17:04:14 +01001319 Register str = InputRegisterAt(invoke, 0);
1320 Register arg = InputRegisterAt(invoke, 1);
1321 DCHECK(str.IsW());
1322 DCHECK(arg.IsW());
Scott Wakeling1f36f412016-04-21 11:13:45 +01001323 Register out = OutputRegister(invoke);
1324
1325 Register temp0 = WRegisterFrom(locations->GetTemp(0));
1326 Register temp1 = WRegisterFrom(locations->GetTemp(1));
1327 Register temp2 = WRegisterFrom(locations->GetTemp(2));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001328 Register temp3;
jessicahandojo05765752016-09-09 19:01:32 -07001329 if (mirror::kUseStringCompression) {
1330 temp3 = WRegisterFrom(locations->GetTemp(3));
jessicahandojo05765752016-09-09 19:01:32 -07001331 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001332
Scott Wakeling97c72b72016-06-24 16:19:36 +01001333 vixl::aarch64::Label loop;
1334 vixl::aarch64::Label find_char_diff;
1335 vixl::aarch64::Label end;
jessicahandojo05765752016-09-09 19:01:32 -07001336 vixl::aarch64::Label different_compression;
Scott Wakeling1f36f412016-04-21 11:13:45 +01001337
1338 // Get offsets of count and value fields within a string object.
1339 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1340 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1341
Nicolas Geoffray512e04d2015-03-27 17:21:24 +00001342 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +01001343 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001344
Scott Wakeling1f36f412016-04-21 11:13:45 +01001345 // Take slow path and throw if input can be and is null.
1346 SlowPathCodeARM64* slow_path = nullptr;
1347 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1348 if (can_slow_path) {
1349 slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1350 codegen_->AddSlowPath(slow_path);
1351 __ Cbz(arg, slow_path->GetEntryLabel());
1352 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001353
Scott Wakeling1f36f412016-04-21 11:13:45 +01001354 // Reference equality check, return 0 if same reference.
1355 __ Subs(out, str, arg);
1356 __ B(&end, eq);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001357
jessicahandojo05765752016-09-09 19:01:32 -07001358 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001359 // Load `count` fields of this and argument strings.
jessicahandojo05765752016-09-09 19:01:32 -07001360 __ Ldr(temp3, HeapOperand(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001361 __ Ldr(temp2, HeapOperand(arg, count_offset));
jessicahandojo05765752016-09-09 19:01:32 -07001362 // Clean out compression flag from lengths.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001363 __ Lsr(temp0, temp3, 1u);
1364 __ Lsr(temp1, temp2, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001365 } else {
1366 // Load lengths of this and argument strings.
1367 __ Ldr(temp0, HeapOperand(str, count_offset));
1368 __ Ldr(temp1, HeapOperand(arg, count_offset));
1369 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001370 // out = length diff.
1371 __ Subs(out, temp0, temp1);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001372 // temp0 = min(len(str), len(arg)).
1373 __ Csel(temp0, temp1, temp0, ge);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001374 // Shorter string is empty?
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001375 __ Cbz(temp0, &end);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001376
jessicahandojo05765752016-09-09 19:01:32 -07001377 if (mirror::kUseStringCompression) {
1378 // Check if both strings using same compression style to use this comparison loop.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001379 __ Eor(temp2, temp2, Operand(temp3));
1380 // Interleave with compression flag extraction which is needed for both paths
1381 // and also set flags which is needed only for the different compressions path.
1382 __ Ands(temp3.W(), temp3.W(), Operand(1));
1383 __ Tbnz(temp2, 0, &different_compression); // Does not use flags.
jessicahandojo05765752016-09-09 19:01:32 -07001384 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001385 // Store offset of string value in preparation for comparison loop.
1386 __ Mov(temp1, value_offset);
jessicahandojo05765752016-09-09 19:01:32 -07001387 if (mirror::kUseStringCompression) {
1388 // For string compression, calculate the number of bytes to compare (not chars).
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001389 // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
1390 __ Lsl(temp0, temp0, temp3);
jessicahandojo05765752016-09-09 19:01:32 -07001391 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001392
1393 UseScratchRegisterScope scratch_scope(masm);
1394 Register temp4 = scratch_scope.AcquireX();
1395
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001396 // Assertions that must hold in order to compare strings 8 bytes at a time.
Scott Wakeling1f36f412016-04-21 11:13:45 +01001397 DCHECK_ALIGNED(value_offset, 8);
1398 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1399
1400 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1401 DCHECK_EQ(char_size, 2u);
1402
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001403 // Promote temp2 to an X reg, ready for LDR.
1404 temp2 = temp2.X();
Scott Wakeling1f36f412016-04-21 11:13:45 +01001405
1406 // Loop to compare 4x16-bit characters at a time (ok because of string data alignment).
1407 __ Bind(&loop);
Alexandre Rames2ea91532016-08-11 17:04:14 +01001408 __ Ldr(temp4, MemOperand(str.X(), temp1.X()));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001409 __ Ldr(temp2, MemOperand(arg.X(), temp1.X()));
1410 __ Cmp(temp4, temp2);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001411 __ B(ne, &find_char_diff);
1412 __ Add(temp1, temp1, char_size * 4);
jessicahandojo05765752016-09-09 19:01:32 -07001413 // With string compression, we have compared 8 bytes, otherwise 4 chars.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001414 __ Subs(temp0, temp0, (mirror::kUseStringCompression) ? 8 : 4);
1415 __ B(&loop, hi);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001416 __ B(&end);
1417
1418 // Promote temp1 to an X reg, ready for EOR.
1419 temp1 = temp1.X();
1420
jessicahandojo05765752016-09-09 19:01:32 -07001421 // Find the single character difference.
Scott Wakeling1f36f412016-04-21 11:13:45 +01001422 __ Bind(&find_char_diff);
1423 // Get the bit position of the first character that differs.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001424 __ Eor(temp1, temp2, temp4);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001425 __ Rbit(temp1, temp1);
1426 __ Clz(temp1, temp1);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001427
jessicahandojo05765752016-09-09 19:01:32 -07001428 // If the number of chars remaining <= the index where the difference occurs (0-3), then
Scott Wakeling1f36f412016-04-21 11:13:45 +01001429 // the difference occurs outside the remaining string data, so just return length diff (out).
jessicahandojo05765752016-09-09 19:01:32 -07001430 // Unlike ARM, we're doing the comparison in one go here, without the subtraction at the
1431 // find_char_diff_2nd_cmp path, so it doesn't matter whether the comparison is signed or
1432 // unsigned when string compression is disabled.
1433 // When it's enabled, the comparison must be unsigned.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001434 __ Cmp(temp0, Operand(temp1.W(), LSR, (mirror::kUseStringCompression) ? 3 : 4));
jessicahandojo05765752016-09-09 19:01:32 -07001435 __ B(ls, &end);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001436
Scott Wakeling1f36f412016-04-21 11:13:45 +01001437 // Extract the characters and calculate the difference.
jessicahandojo05765752016-09-09 19:01:32 -07001438 if (mirror:: kUseStringCompression) {
jessicahandojo05765752016-09-09 19:01:32 -07001439 __ Bic(temp1, temp1, 0x7);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001440 __ Bic(temp1, temp1, Operand(temp3.X(), LSL, 3u));
1441 } else {
1442 __ Bic(temp1, temp1, 0xf);
jessicahandojo05765752016-09-09 19:01:32 -07001443 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001444 __ Lsr(temp2, temp2, temp1);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001445 __ Lsr(temp4, temp4, temp1);
jessicahandojo05765752016-09-09 19:01:32 -07001446 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001447 // Prioritize the case of compressed strings and calculate such result first.
1448 __ Uxtb(temp1, temp4);
1449 __ Sub(out, temp1.W(), Operand(temp2.W(), UXTB));
1450 __ Tbz(temp3, 0u, &end); // If actually compressed, we're done.
jessicahandojo05765752016-09-09 19:01:32 -07001451 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001452 __ Uxth(temp4, temp4);
1453 __ Sub(out, temp4.W(), Operand(temp2.W(), UXTH));
jessicahandojo05765752016-09-09 19:01:32 -07001454
1455 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001456 __ B(&end);
1457 __ Bind(&different_compression);
1458
1459 // Comparison for different compression style.
jessicahandojo05765752016-09-09 19:01:32 -07001460 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
1461 DCHECK_EQ(c_char_size, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001462 temp1 = temp1.W();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001463 temp2 = temp2.W();
1464 temp4 = temp4.W();
jessicahandojo05765752016-09-09 19:01:32 -07001465
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001466 // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
1467 // Note that flags have been set by the `str` compression flag extraction to `temp3`
1468 // before branching to the `different_compression` label.
1469 __ Csel(temp1, str, arg, eq); // Pointer to the compressed string.
1470 __ Csel(temp2, str, arg, ne); // Pointer to the uncompressed string.
jessicahandojo05765752016-09-09 19:01:32 -07001471
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001472 // We want to free up the temp3, currently holding `str` compression flag, for comparison.
1473 // So, we move it to the bottom bit of the iteration count `temp0` which we then need to treat
1474 // as unsigned. Start by freeing the bit with a LSL and continue further down by a SUB which
1475 // will allow `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
1476 __ Lsl(temp0, temp0, 1u);
1477
1478 // Adjust temp1 and temp2 from string pointers to data pointers.
1479 __ Add(temp1, temp1, Operand(value_offset));
1480 __ Add(temp2, temp2, Operand(value_offset));
1481
1482 // Complete the move of the compression flag.
1483 __ Sub(temp0, temp0, Operand(temp3));
1484
1485 vixl::aarch64::Label different_compression_loop;
1486 vixl::aarch64::Label different_compression_diff;
1487
1488 __ Bind(&different_compression_loop);
1489 __ Ldrb(temp4, MemOperand(temp1.X(), c_char_size, PostIndex));
1490 __ Ldrh(temp3, MemOperand(temp2.X(), char_size, PostIndex));
1491 __ Subs(temp4, temp4, Operand(temp3));
1492 __ B(&different_compression_diff, ne);
1493 __ Subs(temp0, temp0, 2);
1494 __ B(&different_compression_loop, hi);
jessicahandojo05765752016-09-09 19:01:32 -07001495 __ B(&end);
1496
1497 // Calculate the difference.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001498 __ Bind(&different_compression_diff);
1499 __ Tst(temp0, Operand(1));
1500 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1501 "Expecting 0=compressed, 1=uncompressed");
1502 __ Cneg(out, temp4, ne);
jessicahandojo05765752016-09-09 19:01:32 -07001503 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001504
1505 __ Bind(&end);
1506
1507 if (can_slow_path) {
1508 __ Bind(slow_path->GetExitLabel());
1509 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001510}
1511
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001512// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
1513// The normal loop plus the pre-header is 9 instructions without string compression and 12
1514// instructions with string compression. We can compare up to 8 bytes in 4 instructions
1515// (LDR+LDR+CMP+BNE) and up to 16 bytes in 5 instructions (LDP+LDP+CMP+CCMP+BNE). Allow up
1516// to 10 instructions for the unrolled loop.
1517constexpr size_t kShortConstStringEqualsCutoffInBytes = 32;
1518
1519static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
1520 if (candidate->IsLoadString()) {
1521 HLoadString* load_string = candidate->AsLoadString();
1522 const DexFile& dex_file = load_string->GetDexFile();
1523 return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
1524 }
1525 return nullptr;
1526}
1527
Agi Csakiea34b402015-08-13 17:51:19 -07001528void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
1529 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1530 LocationSummary::kNoCall,
1531 kIntrinsified);
1532 locations->SetInAt(0, Location::RequiresRegister());
1533 locations->SetInAt(1, Location::RequiresRegister());
Agi Csakiea34b402015-08-13 17:51:19 -07001534
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001535 // For the generic implementation and for long const strings we need a temporary.
1536 // We do not need it for short const strings, up to 8 bytes, see code generation below.
1537 uint32_t const_string_length = 0u;
1538 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1539 if (const_string == nullptr) {
1540 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1541 }
1542 bool is_compressed =
1543 mirror::kUseStringCompression &&
1544 const_string != nullptr &&
1545 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1546 if (const_string == nullptr || const_string_length > (is_compressed ? 8u : 4u)) {
1547 locations->AddTemp(Location::RequiresRegister());
1548 }
1549
1550 // TODO: If the String.equals() is used only for an immediately following HIf, we can
1551 // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
1552 // Then we shall need an extra temporary register instead of the output register.
Agi Csakiea34b402015-08-13 17:51:19 -07001553 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1554}
1555
1556void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001557 MacroAssembler* masm = GetVIXLAssembler();
Agi Csakiea34b402015-08-13 17:51:19 -07001558 LocationSummary* locations = invoke->GetLocations();
1559
1560 Register str = WRegisterFrom(locations->InAt(0));
1561 Register arg = WRegisterFrom(locations->InAt(1));
1562 Register out = XRegisterFrom(locations->Out());
1563
1564 UseScratchRegisterScope scratch_scope(masm);
1565 Register temp = scratch_scope.AcquireW();
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001566 Register temp1 = scratch_scope.AcquireW();
Agi Csakiea34b402015-08-13 17:51:19 -07001567
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001568 vixl::aarch64::Label loop;
Scott Wakeling97c72b72016-06-24 16:19:36 +01001569 vixl::aarch64::Label end;
1570 vixl::aarch64::Label return_true;
1571 vixl::aarch64::Label return_false;
Agi Csakiea34b402015-08-13 17:51:19 -07001572
1573 // Get offsets of count, value, and class fields within a string object.
1574 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1575 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1576 const int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1577
1578 // Note that the null check must have been done earlier.
1579 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1580
Vladimir Marko53b52002016-05-24 19:30:45 +01001581 StringEqualsOptimizations optimizations(invoke);
1582 if (!optimizations.GetArgumentNotNull()) {
1583 // Check if input is null, return false if it is.
1584 __ Cbz(arg, &return_false);
1585 }
Agi Csakiea34b402015-08-13 17:51:19 -07001586
1587 // Reference equality check, return true if same reference.
1588 __ Cmp(str, arg);
1589 __ B(&return_true, eq);
1590
Vladimir Marko53b52002016-05-24 19:30:45 +01001591 if (!optimizations.GetArgumentIsString()) {
1592 // Instanceof check for the argument by comparing class fields.
1593 // All string objects must have the same type since String cannot be subclassed.
1594 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1595 // If the argument is a string object, its class field must be equal to receiver's class field.
1596 __ Ldr(temp, MemOperand(str.X(), class_offset));
1597 __ Ldr(temp1, MemOperand(arg.X(), class_offset));
1598 __ Cmp(temp, temp1);
1599 __ B(&return_false, ne);
1600 }
Agi Csakiea34b402015-08-13 17:51:19 -07001601
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001602 // Check if one of the inputs is a const string. Do not special-case both strings
1603 // being const, such cases should be handled by constant folding if needed.
1604 uint32_t const_string_length = 0u;
1605 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1606 if (const_string == nullptr) {
1607 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1608 if (const_string != nullptr) {
1609 std::swap(str, arg); // Make sure the const string is in `str`.
1610 }
1611 }
1612 bool is_compressed =
1613 mirror::kUseStringCompression &&
1614 const_string != nullptr &&
1615 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1616
1617 if (const_string != nullptr) {
1618 // Load `count` field of the argument string and check if it matches the const string.
1619 // Also compares the compression style, if differs return false.
1620 __ Ldr(temp, MemOperand(arg.X(), count_offset));
Vladimir Marko26ec3ca2017-03-14 13:37:14 +00001621 // Temporarily release temp1 as we may not be able to embed the flagged count in CMP immediate.
1622 scratch_scope.Release(temp1);
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001623 __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
Vladimir Marko26ec3ca2017-03-14 13:37:14 +00001624 temp1 = scratch_scope.AcquireW();
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001625 __ B(&return_false, ne);
1626 } else {
1627 // Load `count` fields of this and argument strings.
1628 __ Ldr(temp, MemOperand(str.X(), count_offset));
1629 __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1630 // Check if `count` fields are equal, return false if they're not.
1631 // Also compares the compression style, if differs return false.
1632 __ Cmp(temp, temp1);
1633 __ B(&return_false, ne);
1634 }
Agi Csakiea34b402015-08-13 17:51:19 -07001635
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001636 // Assertions that must hold in order to compare strings 8 bytes at a time.
Vladimir Marko984519c2017-08-23 10:45:29 +01001637 // Ok to do this because strings are zero-padded to kObjectAlignment.
Agi Csakiea34b402015-08-13 17:51:19 -07001638 DCHECK_ALIGNED(value_offset, 8);
1639 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1640
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001641 if (const_string != nullptr &&
Vladimir Marko984519c2017-08-23 10:45:29 +01001642 const_string_length <= (is_compressed ? kShortConstStringEqualsCutoffInBytes
1643 : kShortConstStringEqualsCutoffInBytes / 2u)) {
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001644 // Load and compare the contents. Though we know the contents of the short const string
1645 // at compile time, materializing constants may be more code than loading from memory.
1646 int32_t offset = value_offset;
1647 size_t remaining_bytes =
1648 RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u);
1649 temp = temp.X();
1650 temp1 = temp1.X();
Vladimir Marko984519c2017-08-23 10:45:29 +01001651 while (remaining_bytes > sizeof(uint64_t)) {
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001652 Register temp2 = XRegisterFrom(locations->GetTemp(0));
1653 __ Ldp(temp, temp1, MemOperand(str.X(), offset));
1654 __ Ldp(temp2, out, MemOperand(arg.X(), offset));
1655 __ Cmp(temp, temp2);
1656 __ Ccmp(temp1, out, NoFlag, eq);
1657 __ B(&return_false, ne);
1658 offset += 2u * sizeof(uint64_t);
1659 remaining_bytes -= 2u * sizeof(uint64_t);
1660 }
1661 if (remaining_bytes != 0u) {
1662 __ Ldr(temp, MemOperand(str.X(), offset));
1663 __ Ldr(temp1, MemOperand(arg.X(), offset));
1664 __ Cmp(temp, temp1);
1665 __ B(&return_false, ne);
1666 }
1667 } else {
1668 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
1669 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1670 "Expecting 0=compressed, 1=uncompressed");
1671 __ Cbz(temp, &return_true);
1672
1673 if (mirror::kUseStringCompression) {
1674 // For string compression, calculate the number of bytes to compare (not chars).
1675 // This could in theory exceed INT32_MAX, so treat temp as unsigned.
1676 __ And(temp1, temp, Operand(1)); // Extract compression flag.
1677 __ Lsr(temp, temp, 1u); // Extract length.
1678 __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare.
1679 }
1680
1681 // Store offset of string value in preparation for comparison loop
1682 __ Mov(temp1, value_offset);
1683
1684 temp1 = temp1.X();
1685 Register temp2 = XRegisterFrom(locations->GetTemp(0));
1686 // Loop to compare strings 8 bytes at a time starting at the front of the string.
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001687 __ Bind(&loop);
1688 __ Ldr(out, MemOperand(str.X(), temp1));
1689 __ Ldr(temp2, MemOperand(arg.X(), temp1));
1690 __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
1691 __ Cmp(out, temp2);
1692 __ B(&return_false, ne);
1693 // With string compression, we have compared 8 bytes, otherwise 4 chars.
1694 __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags);
1695 __ B(&loop, hi);
jessicahandojo05765752016-09-09 19:01:32 -07001696 }
1697
Agi Csakiea34b402015-08-13 17:51:19 -07001698 // Return true and exit the function.
1699 // If loop does not result in returning false, we return true.
1700 __ Bind(&return_true);
1701 __ Mov(out, 1);
1702 __ B(&end);
1703
1704 // Return false and exit the function.
1705 __ Bind(&return_false);
1706 __ Mov(out, 0);
1707 __ Bind(&end);
1708}
1709
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001710static void GenerateVisitStringIndexOf(HInvoke* invoke,
Scott Wakeling97c72b72016-06-24 16:19:36 +01001711 MacroAssembler* masm,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001712 CodeGeneratorARM64* codegen,
1713 ArenaAllocator* allocator,
1714 bool start_at_zero) {
1715 LocationSummary* locations = invoke->GetLocations();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001716
1717 // Note that the null check must have been done earlier.
1718 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1719
1720 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001721 // 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 -07001722 SlowPathCodeARM64* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001723 HInstruction* code_point = invoke->InputAt(1);
1724 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01001725 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > 0xFFFFU) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001726 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1727 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1728 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1729 codegen->AddSlowPath(slow_path);
1730 __ B(slow_path->GetEntryLabel());
1731 __ Bind(slow_path->GetExitLabel());
1732 return;
1733 }
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001734 } else if (code_point->GetType() != Primitive::kPrimChar) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001735 Register char_reg = WRegisterFrom(locations->InAt(1));
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001736 __ Tst(char_reg, 0xFFFF0000);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001737 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1738 codegen->AddSlowPath(slow_path);
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001739 __ B(ne, slow_path->GetEntryLabel());
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001740 }
1741
1742 if (start_at_zero) {
1743 // Start-index = 0.
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001744 Register tmp_reg = WRegisterFrom(locations->GetTemp(0));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001745 __ Mov(tmp_reg, 0);
1746 }
1747
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001748 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
Roland Levillain42ad2882016-02-29 18:26:54 +00001749 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001750
1751 if (slow_path != nullptr) {
1752 __ Bind(slow_path->GetExitLabel());
1753 }
1754}
1755
1756void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) {
1757 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001758 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001759 kIntrinsified);
1760 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1761 // best to align the inputs accordingly.
1762 InvokeRuntimeCallingConvention calling_convention;
1763 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1764 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1765 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1766
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001767 // Need to send start_index=0.
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001768 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1769}
1770
1771void IntrinsicCodeGeneratorARM64::VisitStringIndexOf(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001772 GenerateVisitStringIndexOf(
1773 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001774}
1775
1776void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
1777 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001778 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001779 kIntrinsified);
1780 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1781 // best to align the inputs accordingly.
1782 InvokeRuntimeCallingConvention calling_convention;
1783 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1784 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1785 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1786 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001787}
1788
1789void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001790 GenerateVisitStringIndexOf(
1791 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001792}
1793
Jeff Hao848f70a2014-01-15 13:49:50 -08001794void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1795 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001796 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001797 kIntrinsified);
1798 InvokeRuntimeCallingConvention calling_convention;
1799 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1800 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1801 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1802 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1803 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1804}
1805
1806void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001807 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001808 LocationSummary* locations = invoke->GetLocations();
1809
1810 Register byte_array = WRegisterFrom(locations->InAt(0));
1811 __ Cmp(byte_array, 0);
1812 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1813 codegen_->AddSlowPath(slow_path);
1814 __ B(eq, slow_path->GetEntryLabel());
1815
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001816 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001817 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001818 __ Bind(slow_path->GetExitLabel());
1819}
1820
1821void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
1822 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001823 LocationSummary::kCallOnMainOnly,
Jeff Hao848f70a2014-01-15 13:49:50 -08001824 kIntrinsified);
1825 InvokeRuntimeCallingConvention calling_convention;
1826 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1827 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1828 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1829 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1830}
1831
1832void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
Roland Levillaincc3839c2016-02-29 16:23:48 +00001833 // No need to emit code checking whether `locations->InAt(2)` is a null
1834 // pointer, as callers of the native method
1835 //
1836 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1837 //
1838 // all include a null check on `data` before calling that method.
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001839 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
Roland Levillainf969a202016-03-09 16:14:00 +00001840 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001841}
1842
1843void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Jeff Hao848f70a2014-01-15 13:49:50 -08001844 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001845 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001846 kIntrinsified);
1847 InvokeRuntimeCallingConvention calling_convention;
1848 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Jeff Hao848f70a2014-01-15 13:49:50 -08001849 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1850}
1851
1852void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001853 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001854 LocationSummary* locations = invoke->GetLocations();
1855
1856 Register string_to_copy = WRegisterFrom(locations->InAt(0));
1857 __ Cmp(string_to_copy, 0);
1858 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1859 codegen_->AddSlowPath(slow_path);
1860 __ B(eq, slow_path->GetEntryLabel());
1861
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001862 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001863 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001864 __ Bind(slow_path->GetExitLabel());
1865}
1866
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001867static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1868 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
1869 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1870 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1871
1872 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001873 LocationSummary::kCallOnMainOnly,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001874 kIntrinsified);
1875 InvokeRuntimeCallingConvention calling_convention;
1876
1877 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1878 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1879}
1880
1881static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1882 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
1883 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1884 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(1)->GetType()));
1885 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1886
1887 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001888 LocationSummary::kCallOnMainOnly,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001889 kIntrinsified);
1890 InvokeRuntimeCallingConvention calling_convention;
1891
1892 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1893 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
1894 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1895}
1896
1897static void GenFPToFPCall(HInvoke* invoke,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001898 CodeGeneratorARM64* codegen,
1899 QuickEntrypointEnum entry) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001900 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001901}
1902
1903void IntrinsicLocationsBuilderARM64::VisitMathCos(HInvoke* invoke) {
1904 CreateFPToFPCallLocations(arena_, invoke);
1905}
1906
1907void IntrinsicCodeGeneratorARM64::VisitMathCos(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001908 GenFPToFPCall(invoke, codegen_, kQuickCos);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001909}
1910
1911void IntrinsicLocationsBuilderARM64::VisitMathSin(HInvoke* invoke) {
1912 CreateFPToFPCallLocations(arena_, invoke);
1913}
1914
1915void IntrinsicCodeGeneratorARM64::VisitMathSin(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001916 GenFPToFPCall(invoke, codegen_, kQuickSin);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001917}
1918
1919void IntrinsicLocationsBuilderARM64::VisitMathAcos(HInvoke* invoke) {
1920 CreateFPToFPCallLocations(arena_, invoke);
1921}
1922
1923void IntrinsicCodeGeneratorARM64::VisitMathAcos(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001924 GenFPToFPCall(invoke, codegen_, kQuickAcos);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001925}
1926
1927void IntrinsicLocationsBuilderARM64::VisitMathAsin(HInvoke* invoke) {
1928 CreateFPToFPCallLocations(arena_, invoke);
1929}
1930
1931void IntrinsicCodeGeneratorARM64::VisitMathAsin(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001932 GenFPToFPCall(invoke, codegen_, kQuickAsin);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001933}
1934
1935void IntrinsicLocationsBuilderARM64::VisitMathAtan(HInvoke* invoke) {
1936 CreateFPToFPCallLocations(arena_, invoke);
1937}
1938
1939void IntrinsicCodeGeneratorARM64::VisitMathAtan(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001940 GenFPToFPCall(invoke, codegen_, kQuickAtan);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001941}
1942
1943void IntrinsicLocationsBuilderARM64::VisitMathCbrt(HInvoke* invoke) {
1944 CreateFPToFPCallLocations(arena_, invoke);
1945}
1946
1947void IntrinsicCodeGeneratorARM64::VisitMathCbrt(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001948 GenFPToFPCall(invoke, codegen_, kQuickCbrt);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001949}
1950
1951void IntrinsicLocationsBuilderARM64::VisitMathCosh(HInvoke* invoke) {
1952 CreateFPToFPCallLocations(arena_, invoke);
1953}
1954
1955void IntrinsicCodeGeneratorARM64::VisitMathCosh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001956 GenFPToFPCall(invoke, codegen_, kQuickCosh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001957}
1958
1959void IntrinsicLocationsBuilderARM64::VisitMathExp(HInvoke* invoke) {
1960 CreateFPToFPCallLocations(arena_, invoke);
1961}
1962
1963void IntrinsicCodeGeneratorARM64::VisitMathExp(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001964 GenFPToFPCall(invoke, codegen_, kQuickExp);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001965}
1966
1967void IntrinsicLocationsBuilderARM64::VisitMathExpm1(HInvoke* invoke) {
1968 CreateFPToFPCallLocations(arena_, invoke);
1969}
1970
1971void IntrinsicCodeGeneratorARM64::VisitMathExpm1(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001972 GenFPToFPCall(invoke, codegen_, kQuickExpm1);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001973}
1974
1975void IntrinsicLocationsBuilderARM64::VisitMathLog(HInvoke* invoke) {
1976 CreateFPToFPCallLocations(arena_, invoke);
1977}
1978
1979void IntrinsicCodeGeneratorARM64::VisitMathLog(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001980 GenFPToFPCall(invoke, codegen_, kQuickLog);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001981}
1982
1983void IntrinsicLocationsBuilderARM64::VisitMathLog10(HInvoke* invoke) {
1984 CreateFPToFPCallLocations(arena_, invoke);
1985}
1986
1987void IntrinsicCodeGeneratorARM64::VisitMathLog10(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001988 GenFPToFPCall(invoke, codegen_, kQuickLog10);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001989}
1990
1991void IntrinsicLocationsBuilderARM64::VisitMathSinh(HInvoke* invoke) {
1992 CreateFPToFPCallLocations(arena_, invoke);
1993}
1994
1995void IntrinsicCodeGeneratorARM64::VisitMathSinh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001996 GenFPToFPCall(invoke, codegen_, kQuickSinh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001997}
1998
1999void IntrinsicLocationsBuilderARM64::VisitMathTan(HInvoke* invoke) {
2000 CreateFPToFPCallLocations(arena_, invoke);
2001}
2002
2003void IntrinsicCodeGeneratorARM64::VisitMathTan(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00002004 GenFPToFPCall(invoke, codegen_, kQuickTan);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00002005}
2006
2007void IntrinsicLocationsBuilderARM64::VisitMathTanh(HInvoke* invoke) {
2008 CreateFPToFPCallLocations(arena_, invoke);
2009}
2010
2011void IntrinsicCodeGeneratorARM64::VisitMathTanh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00002012 GenFPToFPCall(invoke, codegen_, kQuickTanh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00002013}
2014
2015void IntrinsicLocationsBuilderARM64::VisitMathAtan2(HInvoke* invoke) {
2016 CreateFPFPToFPCallLocations(arena_, invoke);
2017}
2018
2019void IntrinsicCodeGeneratorARM64::VisitMathAtan2(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00002020 GenFPToFPCall(invoke, codegen_, kQuickAtan2);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00002021}
2022
2023void IntrinsicLocationsBuilderARM64::VisitMathHypot(HInvoke* invoke) {
2024 CreateFPFPToFPCallLocations(arena_, invoke);
2025}
2026
2027void IntrinsicCodeGeneratorARM64::VisitMathHypot(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00002028 GenFPToFPCall(invoke, codegen_, kQuickHypot);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00002029}
2030
2031void IntrinsicLocationsBuilderARM64::VisitMathNextAfter(HInvoke* invoke) {
2032 CreateFPFPToFPCallLocations(arena_, invoke);
2033}
2034
2035void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00002036 GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00002037}
2038
Tim Zhang25abd6c2016-01-19 23:39:24 +08002039void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2040 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2041 LocationSummary::kNoCall,
2042 kIntrinsified);
2043 locations->SetInAt(0, Location::RequiresRegister());
2044 locations->SetInAt(1, Location::RequiresRegister());
2045 locations->SetInAt(2, Location::RequiresRegister());
2046 locations->SetInAt(3, Location::RequiresRegister());
2047 locations->SetInAt(4, Location::RequiresRegister());
2048
2049 locations->AddTemp(Location::RequiresRegister());
2050 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingdf109d92016-04-22 11:35:56 +01002051 locations->AddTemp(Location::RequiresRegister());
Tim Zhang25abd6c2016-01-19 23:39:24 +08002052}
2053
2054void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01002055 MacroAssembler* masm = GetVIXLAssembler();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002056 LocationSummary* locations = invoke->GetLocations();
2057
2058 // Check assumption that sizeof(Char) is 2 (used in scaling below).
2059 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2060 DCHECK_EQ(char_size, 2u);
2061
2062 // Location of data in char array buffer.
2063 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2064
2065 // Location of char array data in string.
2066 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2067
2068 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2069 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2070 Register srcObj = XRegisterFrom(locations->InAt(0));
2071 Register srcBegin = XRegisterFrom(locations->InAt(1));
2072 Register srcEnd = XRegisterFrom(locations->InAt(2));
2073 Register dstObj = XRegisterFrom(locations->InAt(3));
2074 Register dstBegin = XRegisterFrom(locations->InAt(4));
2075
2076 Register src_ptr = XRegisterFrom(locations->GetTemp(0));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002077 Register num_chr = XRegisterFrom(locations->GetTemp(1));
2078 Register tmp1 = XRegisterFrom(locations->GetTemp(2));
Tim Zhang25abd6c2016-01-19 23:39:24 +08002079
2080 UseScratchRegisterScope temps(masm);
2081 Register dst_ptr = temps.AcquireX();
Scott Wakelingdf109d92016-04-22 11:35:56 +01002082 Register tmp2 = temps.AcquireX();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002083
jessicahandojo05765752016-09-09 19:01:32 -07002084 vixl::aarch64::Label done;
2085 vixl::aarch64::Label compressed_string_loop;
2086 __ Sub(num_chr, srcEnd, srcBegin);
2087 // Early out for valid zero-length retrievals.
2088 __ Cbz(num_chr, &done);
Tim Zhang25abd6c2016-01-19 23:39:24 +08002089
Scott Wakelingdf109d92016-04-22 11:35:56 +01002090 // dst address start to copy to.
Tim Zhang25abd6c2016-01-19 23:39:24 +08002091 __ Add(dst_ptr, dstObj, Operand(data_offset));
2092 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
2093
jessicahandojo05765752016-09-09 19:01:32 -07002094 // src address to copy from.
2095 __ Add(src_ptr, srcObj, Operand(value_offset));
2096 vixl::aarch64::Label compressed_string_preloop;
2097 if (mirror::kUseStringCompression) {
2098 // Location of count in string.
2099 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2100 // String's length.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002101 __ Ldr(tmp2, MemOperand(srcObj, count_offset));
2102 __ Tbz(tmp2, 0, &compressed_string_preloop);
jessicahandojo05765752016-09-09 19:01:32 -07002103 }
2104 __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002105
Tim Zhang25abd6c2016-01-19 23:39:24 +08002106 // Do the copy.
Scott Wakeling97c72b72016-06-24 16:19:36 +01002107 vixl::aarch64::Label loop;
Scott Wakeling97c72b72016-06-24 16:19:36 +01002108 vixl::aarch64::Label remainder;
Scott Wakelingdf109d92016-04-22 11:35:56 +01002109
Scott Wakelingdf109d92016-04-22 11:35:56 +01002110 // Save repairing the value of num_chr on the < 8 character path.
2111 __ Subs(tmp1, num_chr, 8);
2112 __ B(lt, &remainder);
2113
2114 // Keep the result of the earlier subs, we are going to fetch at least 8 characters.
2115 __ Mov(num_chr, tmp1);
2116
2117 // Main loop used for longer fetches loads and stores 8x16-bit characters at a time.
2118 // (Unaligned addresses are acceptable here and not worth inlining extra code to rectify.)
Tim Zhang25abd6c2016-01-19 23:39:24 +08002119 __ Bind(&loop);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002120 __ Ldp(tmp1, tmp2, MemOperand(src_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002121 __ Subs(num_chr, num_chr, 8);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002122 __ Stp(tmp1, tmp2, MemOperand(dst_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002123 __ B(ge, &loop);
2124
2125 __ Adds(num_chr, num_chr, 8);
2126 __ B(eq, &done);
2127
2128 // Main loop for < 8 character case and remainder handling. Loads and stores one
2129 // 16-bit Java character at a time.
2130 __ Bind(&remainder);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002131 __ Ldrh(tmp1, MemOperand(src_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002132 __ Subs(num_chr, num_chr, 1);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002133 __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002134 __ B(gt, &remainder);
jessicahandojo05765752016-09-09 19:01:32 -07002135 __ B(&done);
2136
2137 if (mirror::kUseStringCompression) {
2138 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
2139 DCHECK_EQ(c_char_size, 1u);
2140 __ Bind(&compressed_string_preloop);
2141 __ Add(src_ptr, src_ptr, Operand(srcBegin));
2142 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2143 __ Bind(&compressed_string_loop);
2144 __ Ldrb(tmp1, MemOperand(src_ptr, c_char_size, PostIndex));
2145 __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
2146 __ Subs(num_chr, num_chr, Operand(1));
2147 __ B(gt, &compressed_string_loop);
2148 }
Scott Wakelingdf109d92016-04-22 11:35:56 +01002149
Tim Zhang25abd6c2016-01-19 23:39:24 +08002150 __ Bind(&done);
2151}
2152
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002153// Mirrors ARRAYCOPY_SHORT_CHAR_ARRAY_THRESHOLD in libcore, so we can choose to use the native
2154// implementation there for longer copy lengths.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002155static constexpr int32_t kSystemArrayCopyCharThreshold = 32;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002156
2157static void SetSystemArrayCopyLocationRequires(LocationSummary* locations,
2158 uint32_t at,
2159 HInstruction* input) {
2160 HIntConstant* const_input = input->AsIntConstant();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002161 if (const_input != nullptr && !vixl::aarch64::Assembler::IsImmAddSub(const_input->GetValue())) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002162 locations->SetInAt(at, Location::RequiresRegister());
2163 } else {
2164 locations->SetInAt(at, Location::RegisterOrConstant(input));
2165 }
2166}
2167
2168void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
2169 // Check to see if we have known failures that will cause us to have to bail out
2170 // to the runtime, and just generate the runtime call directly.
2171 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2172 HIntConstant* dst_pos = invoke->InputAt(3)->AsIntConstant();
2173
2174 // The positions must be non-negative.
2175 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2176 (dst_pos != nullptr && dst_pos->GetValue() < 0)) {
2177 // We will have to fail anyways.
2178 return;
2179 }
2180
2181 // The length must be >= 0 and not so long that we would (currently) prefer libcore's
2182 // native implementation.
2183 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2184 if (length != nullptr) {
2185 int32_t len = length->GetValue();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002186 if (len < 0 || len > kSystemArrayCopyCharThreshold) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002187 // Just call as normal.
2188 return;
2189 }
2190 }
2191
2192 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2193 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2194 LocationSummary::kCallOnSlowPath,
2195 kIntrinsified);
2196 // arraycopy(char[] src, int src_pos, char[] dst, int dst_pos, int length).
2197 locations->SetInAt(0, Location::RequiresRegister());
2198 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2199 locations->SetInAt(2, Location::RequiresRegister());
2200 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2201 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2202
2203 locations->AddTemp(Location::RequiresRegister());
2204 locations->AddTemp(Location::RequiresRegister());
2205 locations->AddTemp(Location::RequiresRegister());
2206}
2207
Scott Wakeling97c72b72016-06-24 16:19:36 +01002208static void CheckSystemArrayCopyPosition(MacroAssembler* masm,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002209 const Location& pos,
2210 const Register& input,
2211 const Location& length,
2212 SlowPathCodeARM64* slow_path,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002213 const Register& temp,
2214 bool length_is_input_length = false) {
2215 const int32_t length_offset = mirror::Array::LengthOffset().Int32Value();
2216 if (pos.IsConstant()) {
2217 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
2218 if (pos_const == 0) {
2219 if (!length_is_input_length) {
2220 // Check that length(input) >= length.
2221 __ Ldr(temp, MemOperand(input, length_offset));
2222 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
2223 __ B(slow_path->GetEntryLabel(), lt);
2224 }
2225 } else {
2226 // Check that length(input) >= pos.
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002227 __ Ldr(temp, MemOperand(input, length_offset));
2228 __ Subs(temp, temp, pos_const);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002229 __ B(slow_path->GetEntryLabel(), lt);
2230
2231 // Check that (length(input) - pos) >= length.
2232 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
2233 __ B(slow_path->GetEntryLabel(), lt);
2234 }
2235 } else if (length_is_input_length) {
2236 // The only way the copy can succeed is if pos is zero.
2237 __ Cbnz(WRegisterFrom(pos), slow_path->GetEntryLabel());
2238 } else {
2239 // Check that pos >= 0.
2240 Register pos_reg = WRegisterFrom(pos);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002241 __ Tbnz(pos_reg, pos_reg.GetSizeInBits() - 1, slow_path->GetEntryLabel());
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002242
2243 // Check that pos <= length(input) && (length(input) - pos) >= length.
2244 __ Ldr(temp, MemOperand(input, length_offset));
2245 __ Subs(temp, temp, pos_reg);
2246 // Ccmp if length(input) >= pos, else definitely bail to slow path (N!=V == lt).
2247 __ Ccmp(temp, OperandFrom(length, Primitive::kPrimInt), NFlag, ge);
2248 __ B(slow_path->GetEntryLabel(), lt);
2249 }
2250}
2251
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002252// Compute base source address, base destination address, and end
2253// source address for System.arraycopy* intrinsics in `src_base`,
2254// `dst_base` and `src_end` respectively.
Scott Wakeling97c72b72016-06-24 16:19:36 +01002255static void GenSystemArrayCopyAddresses(MacroAssembler* masm,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002256 Primitive::Type type,
2257 const Register& src,
2258 const Location& src_pos,
2259 const Register& dst,
2260 const Location& dst_pos,
2261 const Location& copy_length,
2262 const Register& src_base,
2263 const Register& dst_base,
2264 const Register& src_end) {
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002265 // This routine is used by the SystemArrayCopy and the SystemArrayCopyChar intrinsics.
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002266 DCHECK(type == Primitive::kPrimNot || type == Primitive::kPrimChar)
Roland Levillainebea3d22016-04-12 15:42:57 +01002267 << "Unexpected element type: " << type;
2268 const int32_t element_size = Primitive::ComponentSize(type);
2269 const int32_t element_size_shift = Primitive::ComponentSizeShift(type);
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002270 const uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002271
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002272 if (src_pos.IsConstant()) {
2273 int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002274 __ Add(src_base, src, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002275 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002276 __ Add(src_base, src, data_offset);
2277 __ Add(src_base, src_base, Operand(XRegisterFrom(src_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002278 }
2279
2280 if (dst_pos.IsConstant()) {
2281 int32_t constant = dst_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002282 __ Add(dst_base, dst, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002283 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002284 __ Add(dst_base, dst, data_offset);
2285 __ Add(dst_base, dst_base, Operand(XRegisterFrom(dst_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002286 }
2287
2288 if (copy_length.IsConstant()) {
2289 int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002290 __ Add(src_end, src_base, element_size * constant);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002291 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002292 __ Add(src_end, src_base, Operand(XRegisterFrom(copy_length), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002293 }
2294}
2295
2296void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01002297 MacroAssembler* masm = GetVIXLAssembler();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002298 LocationSummary* locations = invoke->GetLocations();
2299 Register src = XRegisterFrom(locations->InAt(0));
2300 Location src_pos = locations->InAt(1);
2301 Register dst = XRegisterFrom(locations->InAt(2));
2302 Location dst_pos = locations->InAt(3);
2303 Location length = locations->InAt(4);
2304
2305 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2306 codegen_->AddSlowPath(slow_path);
2307
2308 // If source and destination are the same, take the slow path. Overlapping copy regions must be
2309 // copied in reverse and we can't know in all cases if it's needed.
2310 __ Cmp(src, dst);
2311 __ B(slow_path->GetEntryLabel(), eq);
2312
2313 // Bail out if the source is null.
2314 __ Cbz(src, slow_path->GetEntryLabel());
2315
2316 // Bail out if the destination is null.
2317 __ Cbz(dst, slow_path->GetEntryLabel());
2318
2319 if (!length.IsConstant()) {
Vladimir Markoc5646202016-11-28 16:03:15 +00002320 // Merge the following two comparisons into one:
2321 // If the length is negative, bail out (delegate to libcore's native implementation).
2322 // If the length > 32 then (currently) prefer libcore's native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002323 __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold);
Vladimir Markoc5646202016-11-28 16:03:15 +00002324 __ B(slow_path->GetEntryLabel(), hi);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002325 } else {
2326 // We have already checked in the LocationsBuilder for the constant case.
2327 DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0);
2328 DCHECK_LE(length.GetConstant()->AsIntConstant()->GetValue(), 32);
2329 }
2330
2331 Register src_curr_addr = WRegisterFrom(locations->GetTemp(0));
2332 Register dst_curr_addr = WRegisterFrom(locations->GetTemp(1));
2333 Register src_stop_addr = WRegisterFrom(locations->GetTemp(2));
2334
2335 CheckSystemArrayCopyPosition(masm,
2336 src_pos,
2337 src,
2338 length,
2339 slow_path,
2340 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002341 false);
2342
2343 CheckSystemArrayCopyPosition(masm,
2344 dst_pos,
2345 dst,
2346 length,
2347 slow_path,
2348 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002349 false);
2350
2351 src_curr_addr = src_curr_addr.X();
2352 dst_curr_addr = dst_curr_addr.X();
2353 src_stop_addr = src_stop_addr.X();
2354
2355 GenSystemArrayCopyAddresses(masm,
2356 Primitive::kPrimChar,
2357 src,
2358 src_pos,
2359 dst,
2360 dst_pos,
2361 length,
2362 src_curr_addr,
2363 dst_curr_addr,
2364 src_stop_addr);
2365
2366 // Iterate over the arrays and do a raw copy of the chars.
2367 const int32_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2368 UseScratchRegisterScope temps(masm);
2369 Register tmp = temps.AcquireW();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002370 vixl::aarch64::Label loop, done;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002371 __ Bind(&loop);
2372 __ Cmp(src_curr_addr, src_stop_addr);
2373 __ B(&done, eq);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002374 __ Ldrh(tmp, MemOperand(src_curr_addr, char_size, PostIndex));
2375 __ Strh(tmp, MemOperand(dst_curr_addr, char_size, PostIndex));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002376 __ B(&loop);
2377 __ Bind(&done);
2378
2379 __ Bind(slow_path->GetExitLabel());
2380}
2381
donghui.baic2ec9ad2016-03-10 14:02:55 +08002382// We can choose to use the native implementation there for longer copy lengths.
2383static constexpr int32_t kSystemArrayCopyThreshold = 128;
2384
2385// CodeGenerator::CreateSystemArrayCopyLocationSummary use three temporary registers.
2386// We want to use two temporary registers in order to reduce the register pressure in arm64.
2387// So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary.
2388void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002389 // The only read barrier implementation supporting the
2390 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2391 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain3d312422016-06-23 13:53:42 +01002392 return;
2393 }
2394
donghui.baic2ec9ad2016-03-10 14:02:55 +08002395 // Check to see if we have known failures that will cause us to have to bail out
2396 // to the runtime, and just generate the runtime call directly.
2397 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2398 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
2399
2400 // The positions must be non-negative.
2401 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2402 (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
2403 // We will have to fail anyways.
2404 return;
2405 }
2406
2407 // The length must be >= 0.
2408 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2409 if (length != nullptr) {
2410 int32_t len = length->GetValue();
2411 if (len < 0 || len >= kSystemArrayCopyThreshold) {
2412 // Just call as normal.
2413 return;
2414 }
2415 }
2416
2417 SystemArrayCopyOptimizations optimizations(invoke);
2418
2419 if (optimizations.GetDestinationIsSource()) {
2420 if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) {
2421 // We only support backward copying if source and destination are the same.
2422 return;
2423 }
2424 }
2425
2426 if (optimizations.GetDestinationIsPrimitiveArray() || optimizations.GetSourceIsPrimitiveArray()) {
2427 // We currently don't intrinsify primitive copying.
2428 return;
2429 }
2430
2431 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2432 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2433 LocationSummary::kCallOnSlowPath,
2434 kIntrinsified);
2435 // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length).
2436 locations->SetInAt(0, Location::RequiresRegister());
2437 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2438 locations->SetInAt(2, Location::RequiresRegister());
2439 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2440 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2441
2442 locations->AddTemp(Location::RequiresRegister());
2443 locations->AddTemp(Location::RequiresRegister());
Roland Levillain0b671c02016-08-19 12:02:34 +01002444 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2445 // Temporary register IP0, obtained from the VIXL scratch register
2446 // pool, cannot be used in ReadBarrierSystemArrayCopySlowPathARM64
2447 // (because that register is clobbered by ReadBarrierMarkRegX
Roland Levillain54f869e2017-03-06 13:54:11 +00002448 // entry points). It cannot be used in calls to
2449 // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier
2450 // either. For these reasons, get a third extra temporary register
2451 // from the register allocator.
Roland Levillain0b671c02016-08-19 12:02:34 +01002452 locations->AddTemp(Location::RequiresRegister());
Roland Levillain54f869e2017-03-06 13:54:11 +00002453 } else {
2454 // Cases other than Baker read barriers: the third temporary will
2455 // be acquired from the VIXL scratch register pool.
Roland Levillain0b671c02016-08-19 12:02:34 +01002456 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002457}
2458
2459void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002460 // The only read barrier implementation supporting the
2461 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2462 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01002463
Scott Wakeling97c72b72016-06-24 16:19:36 +01002464 MacroAssembler* masm = GetVIXLAssembler();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002465 LocationSummary* locations = invoke->GetLocations();
2466
2467 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2468 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2469 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2470 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Roland Levillain0b671c02016-08-19 12:02:34 +01002471 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002472
2473 Register src = XRegisterFrom(locations->InAt(0));
2474 Location src_pos = locations->InAt(1);
2475 Register dest = XRegisterFrom(locations->InAt(2));
2476 Location dest_pos = locations->InAt(3);
2477 Location length = locations->InAt(4);
2478 Register temp1 = WRegisterFrom(locations->GetTemp(0));
Roland Levillain0b671c02016-08-19 12:02:34 +01002479 Location temp1_loc = LocationFrom(temp1);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002480 Register temp2 = WRegisterFrom(locations->GetTemp(1));
Roland Levillain0b671c02016-08-19 12:02:34 +01002481 Location temp2_loc = LocationFrom(temp2);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002482
Roland Levillain0b671c02016-08-19 12:02:34 +01002483 SlowPathCodeARM64* intrinsic_slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2484 codegen_->AddSlowPath(intrinsic_slow_path);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002485
Scott Wakeling97c72b72016-06-24 16:19:36 +01002486 vixl::aarch64::Label conditions_on_positions_validated;
donghui.baic2ec9ad2016-03-10 14:02:55 +08002487 SystemArrayCopyOptimizations optimizations(invoke);
2488
donghui.baic2ec9ad2016-03-10 14:02:55 +08002489 // If source and destination are the same, we go to slow path if we need to do
2490 // forward copying.
2491 if (src_pos.IsConstant()) {
2492 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
2493 if (dest_pos.IsConstant()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002494 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
2495 if (optimizations.GetDestinationIsSource()) {
2496 // Checked when building locations.
2497 DCHECK_GE(src_pos_constant, dest_pos_constant);
2498 } else if (src_pos_constant < dest_pos_constant) {
2499 __ Cmp(src, dest);
Roland Levillain0b671c02016-08-19 12:02:34 +01002500 __ B(intrinsic_slow_path->GetEntryLabel(), eq);
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002501 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002502 // Checked when building locations.
2503 DCHECK(!optimizations.GetDestinationIsSource()
2504 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
2505 } else {
2506 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002507 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002508 __ B(&conditions_on_positions_validated, ne);
2509 }
2510 __ Cmp(WRegisterFrom(dest_pos), src_pos_constant);
Roland Levillain0b671c02016-08-19 12:02:34 +01002511 __ B(intrinsic_slow_path->GetEntryLabel(), gt);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002512 }
2513 } else {
2514 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002515 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002516 __ B(&conditions_on_positions_validated, ne);
2517 }
2518 __ Cmp(RegisterFrom(src_pos, invoke->InputAt(1)->GetType()),
2519 OperandFrom(dest_pos, invoke->InputAt(3)->GetType()));
Roland Levillain0b671c02016-08-19 12:02:34 +01002520 __ B(intrinsic_slow_path->GetEntryLabel(), lt);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002521 }
2522
2523 __ Bind(&conditions_on_positions_validated);
2524
2525 if (!optimizations.GetSourceIsNotNull()) {
2526 // Bail out if the source is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01002527 __ Cbz(src, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002528 }
2529
2530 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
2531 // Bail out if the destination is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01002532 __ Cbz(dest, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002533 }
2534
2535 // We have already checked in the LocationsBuilder for the constant case.
2536 if (!length.IsConstant() &&
2537 !optimizations.GetCountIsSourceLength() &&
2538 !optimizations.GetCountIsDestinationLength()) {
Vladimir Markoc5646202016-11-28 16:03:15 +00002539 // Merge the following two comparisons into one:
2540 // If the length is negative, bail out (delegate to libcore's native implementation).
2541 // If the length >= 128 then (currently) prefer native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002542 __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold);
Vladimir Markoc5646202016-11-28 16:03:15 +00002543 __ B(intrinsic_slow_path->GetEntryLabel(), hs);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002544 }
2545 // Validity checks: source.
2546 CheckSystemArrayCopyPosition(masm,
2547 src_pos,
2548 src,
2549 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002550 intrinsic_slow_path,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002551 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002552 optimizations.GetCountIsSourceLength());
2553
2554 // Validity checks: dest.
2555 CheckSystemArrayCopyPosition(masm,
2556 dest_pos,
2557 dest,
2558 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002559 intrinsic_slow_path,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002560 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002561 optimizations.GetCountIsDestinationLength());
2562 {
2563 // We use a block to end the scratch scope before the write barrier, thus
2564 // freeing the temporary registers so they can be used in `MarkGCCard`.
2565 UseScratchRegisterScope temps(masm);
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002566 Location temp3_loc; // Used only for Baker read barrier.
Roland Levillain54f869e2017-03-06 13:54:11 +00002567 Register temp3;
2568 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002569 temp3_loc = locations->GetTemp(2);
2570 temp3 = WRegisterFrom(temp3_loc);
Roland Levillain54f869e2017-03-06 13:54:11 +00002571 } else {
2572 temp3 = temps.AcquireW();
2573 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002574
donghui.baic2ec9ad2016-03-10 14:02:55 +08002575 if (!optimizations.GetDoesNotNeedTypeCheck()) {
2576 // Check whether all elements of the source array are assignable to the component
2577 // type of the destination array. We do two checks: the classes are the same,
2578 // or the destination is Object[]. If none of these checks succeed, we go to the
2579 // slow path.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002580
Roland Levillain0b671c02016-08-19 12:02:34 +01002581 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2582 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2583 // /* HeapReference<Class> */ temp1 = src->klass_
2584 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2585 temp1_loc,
2586 src.W(),
2587 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002588 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002589 /* needs_null_check */ false,
2590 /* use_load_acquire */ false);
2591 // Bail out if the source is not a non primitive array.
2592 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2593 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2594 temp1_loc,
2595 temp1,
2596 component_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 __ Cbz(temp1, intrinsic_slow_path->GetEntryLabel());
2601 // If heap poisoning is enabled, `temp1` has been unpoisoned
2602 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2603 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
2604 __ Ldrh(temp1, HeapOperand(temp1, primitive_offset));
2605 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2606 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002607 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002608
2609 // /* HeapReference<Class> */ temp1 = dest->klass_
2610 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2611 temp1_loc,
2612 dest.W(),
2613 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002614 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002615 /* needs_null_check */ false,
2616 /* use_load_acquire */ false);
2617
2618 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2619 // Bail out if the destination is not a non primitive array.
2620 //
2621 // Register `temp1` is not trashed by the read barrier emitted
2622 // by GenerateFieldLoadWithBakerReadBarrier below, as that
2623 // method produces a call to a ReadBarrierMarkRegX entry point,
2624 // which saves all potentially live registers, including
2625 // temporaries such a `temp1`.
2626 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2627 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2628 temp2_loc,
2629 temp1,
2630 component_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002631 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002632 /* needs_null_check */ false,
2633 /* use_load_acquire */ false);
2634 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2635 // If heap poisoning is enabled, `temp2` has been unpoisoned
2636 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2637 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2638 __ Ldrh(temp2, HeapOperand(temp2, primitive_offset));
2639 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2640 __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
2641 }
2642
2643 // For the same reason given earlier, `temp1` is not trashed by the
2644 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
2645 // /* HeapReference<Class> */ temp2 = src->klass_
2646 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2647 temp2_loc,
2648 src.W(),
2649 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002650 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002651 /* needs_null_check */ false,
2652 /* use_load_acquire */ false);
2653 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
2654 __ Cmp(temp1, temp2);
2655
2656 if (optimizations.GetDestinationIsTypedObjectArray()) {
2657 vixl::aarch64::Label do_copy;
2658 __ B(&do_copy, eq);
2659 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2660 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2661 temp1_loc,
2662 temp1,
2663 component_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002664 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002665 /* needs_null_check */ false,
2666 /* use_load_acquire */ false);
2667 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2668 // We do not need to emit a read barrier for the following
2669 // heap reference load, as `temp1` is only used in a
2670 // comparison with null below, and this reference is not
2671 // kept afterwards.
2672 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2673 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
2674 __ Bind(&do_copy);
2675 } else {
2676 __ B(intrinsic_slow_path->GetEntryLabel(), ne);
2677 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002678 } else {
Roland Levillain0b671c02016-08-19 12:02:34 +01002679 // Non read barrier code.
2680
2681 // /* HeapReference<Class> */ temp1 = dest->klass_
2682 __ Ldr(temp1, MemOperand(dest, class_offset));
2683 // /* HeapReference<Class> */ temp2 = src->klass_
2684 __ Ldr(temp2, MemOperand(src, class_offset));
2685 bool did_unpoison = false;
2686 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2687 !optimizations.GetSourceIsNonPrimitiveArray()) {
2688 // One or two of the references need to be unpoisoned. Unpoison them
2689 // both to make the identity check valid.
2690 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2691 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2692 did_unpoison = true;
2693 }
2694
2695 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2696 // Bail out if the destination is not a non primitive array.
2697 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2698 __ Ldr(temp3, HeapOperand(temp1, component_offset));
2699 __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
2700 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2701 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2702 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2703 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2704 __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
2705 }
2706
2707 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2708 // Bail out if the source is not a non primitive array.
2709 // /* HeapReference<Class> */ temp3 = temp2->component_type_
2710 __ Ldr(temp3, HeapOperand(temp2, component_offset));
2711 __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
2712 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2713 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2714 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2715 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2716 __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
2717 }
2718
2719 __ Cmp(temp1, temp2);
2720
2721 if (optimizations.GetDestinationIsTypedObjectArray()) {
2722 vixl::aarch64::Label do_copy;
2723 __ B(&do_copy, eq);
2724 if (!did_unpoison) {
2725 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2726 }
2727 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2728 __ Ldr(temp1, HeapOperand(temp1, component_offset));
2729 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2730 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2731 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2732 // No need to unpoison the result, we're comparing against null.
2733 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
2734 __ Bind(&do_copy);
2735 } else {
2736 __ B(intrinsic_slow_path->GetEntryLabel(), ne);
2737 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002738 }
2739 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2740 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2741 // Bail out if the source is not a non primitive array.
Roland Levillain0b671c02016-08-19 12:02:34 +01002742 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2743 // /* HeapReference<Class> */ temp1 = src->klass_
2744 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2745 temp1_loc,
2746 src.W(),
2747 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002748 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002749 /* needs_null_check */ false,
2750 /* use_load_acquire */ false);
2751 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2752 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2753 temp2_loc,
2754 temp1,
2755 component_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002756 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002757 /* needs_null_check */ false,
2758 /* use_load_acquire */ false);
2759 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2760 // If heap poisoning is enabled, `temp2` has been unpoisoned
2761 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2762 } else {
2763 // /* HeapReference<Class> */ temp1 = src->klass_
2764 __ Ldr(temp1, HeapOperand(src.W(), class_offset));
2765 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2766 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2767 __ Ldr(temp2, HeapOperand(temp1, component_offset));
2768 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2769 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2770 }
2771 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2772 __ Ldrh(temp2, HeapOperand(temp2, primitive_offset));
donghui.baic2ec9ad2016-03-10 14:02:55 +08002773 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Roland Levillain0b671c02016-08-19 12:02:34 +01002774 __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002775 }
2776
Roland Levillain1663d162017-03-17 15:15:21 +00002777 if (length.IsConstant() && length.GetConstant()->AsIntConstant()->GetValue() == 0) {
2778 // Null constant length: not need to emit the loop code at all.
Roland Levillain0b671c02016-08-19 12:02:34 +01002779 } else {
Roland Levillain1663d162017-03-17 15:15:21 +00002780 Register src_curr_addr = temp1.X();
2781 Register dst_curr_addr = temp2.X();
2782 Register src_stop_addr = temp3.X();
2783 vixl::aarch64::Label done;
2784 const Primitive::Type type = Primitive::kPrimNot;
2785 const int32_t element_size = Primitive::ComponentSize(type);
2786
2787 if (length.IsRegister()) {
2788 // Don't enter the copy loop if the length is null.
2789 __ Cbz(WRegisterFrom(length), &done);
2790 }
2791
2792 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2793 // TODO: Also convert this intrinsic to the IsGcMarking strategy?
2794
2795 // SystemArrayCopy implementation for Baker read barriers (see
Roland Levillain9983e302017-07-14 14:34:22 +01002796 // also CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier):
Roland Levillain1663d162017-03-17 15:15:21 +00002797 //
2798 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
2799 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
2800 // bool is_gray = (rb_state == ReadBarrier::GrayState());
2801 // if (is_gray) {
2802 // // Slow-path copy.
2803 // do {
2804 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
2805 // } while (src_ptr != end_ptr)
2806 // } else {
2807 // // Fast-path copy.
2808 // do {
2809 // *dest_ptr++ = *src_ptr++;
2810 // } while (src_ptr != end_ptr)
2811 // }
2812
2813 // Make sure `tmp` is not IP0, as it is clobbered by
2814 // ReadBarrierMarkRegX entry points in
2815 // ReadBarrierSystemArrayCopySlowPathARM64.
Roland Levillain1ca955d2017-04-13 19:34:30 +01002816 DCHECK(temps.IsAvailable(ip0));
Roland Levillain1663d162017-03-17 15:15:21 +00002817 temps.Exclude(ip0);
Roland Levillain0b671c02016-08-19 12:02:34 +01002818 Register tmp = temps.AcquireW();
Roland Levillain1663d162017-03-17 15:15:21 +00002819 DCHECK_NE(LocationFrom(tmp).reg(), IP0);
Roland Levillain1ca955d2017-04-13 19:34:30 +01002820 // Put IP0 back in the pool so that VIXL has at least one
2821 // scratch register available to emit macro-instructions (note
2822 // that IP1 is already used for `tmp`). Indeed some
2823 // macro-instructions used in GenSystemArrayCopyAddresses
2824 // (invoked hereunder) may require a scratch register (for
2825 // instance to emit a load with a large constant offset).
2826 temps.Include(ip0);
Roland Levillain1663d162017-03-17 15:15:21 +00002827
2828 // /* int32_t */ monitor = src->monitor_
2829 __ Ldr(tmp, HeapOperand(src.W(), monitor_offset));
2830 // /* LockWord */ lock_word = LockWord(monitor)
2831 static_assert(sizeof(LockWord) == sizeof(int32_t),
2832 "art::LockWord and int32_t have different sizes.");
2833
2834 // Introduce a dependency on the lock_word including rb_state,
2835 // to prevent load-load reordering, and without using
2836 // a memory barrier (which would be more expensive).
2837 // `src` is unchanged by this operation, but its value now depends
2838 // on `tmp`.
2839 __ Add(src.X(), src.X(), Operand(tmp.X(), LSR, 32));
2840
2841 // Compute base source address, base destination address, and end
2842 // source address for System.arraycopy* intrinsics in `src_base`,
2843 // `dst_base` and `src_end` respectively.
2844 // Note that `src_curr_addr` is computed from from `src` (and
2845 // `src_pos`) here, and thus honors the artificial dependency
2846 // of `src` on `tmp`.
2847 GenSystemArrayCopyAddresses(masm,
2848 type,
2849 src,
2850 src_pos,
2851 dest,
2852 dest_pos,
2853 length,
2854 src_curr_addr,
2855 dst_curr_addr,
2856 src_stop_addr);
2857
2858 // Slow path used to copy array when `src` is gray.
2859 SlowPathCodeARM64* read_barrier_slow_path =
2860 new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathARM64(invoke, LocationFrom(tmp));
2861 codegen_->AddSlowPath(read_barrier_slow_path);
2862
2863 // Given the numeric representation, it's enough to check the low bit of the rb_state.
2864 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
2865 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
2866 __ Tbnz(tmp, LockWord::kReadBarrierStateShift, read_barrier_slow_path->GetEntryLabel());
2867
2868 // Fast-path copy.
2869 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2870 // poison/unpoison.
2871 vixl::aarch64::Label loop;
2872 __ Bind(&loop);
Roland Levillain0b671c02016-08-19 12:02:34 +01002873 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
2874 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
Roland Levillain1663d162017-03-17 15:15:21 +00002875 __ Cmp(src_curr_addr, src_stop_addr);
2876 __ B(&loop, ne);
2877
2878 __ Bind(read_barrier_slow_path->GetExitLabel());
2879 } else {
2880 // Non read barrier code.
2881 // Compute base source address, base destination address, and end
2882 // source address for System.arraycopy* intrinsics in `src_base`,
2883 // `dst_base` and `src_end` respectively.
2884 GenSystemArrayCopyAddresses(masm,
2885 type,
2886 src,
2887 src_pos,
2888 dest,
2889 dest_pos,
2890 length,
2891 src_curr_addr,
2892 dst_curr_addr,
2893 src_stop_addr);
2894 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2895 // poison/unpoison.
2896 vixl::aarch64::Label loop;
2897 __ Bind(&loop);
2898 {
2899 Register tmp = temps.AcquireW();
2900 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
2901 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
2902 }
2903 __ Cmp(src_curr_addr, src_stop_addr);
2904 __ B(&loop, ne);
Roland Levillain0b671c02016-08-19 12:02:34 +01002905 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002906 __ Bind(&done);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002907 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002908 }
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002909
donghui.baic2ec9ad2016-03-10 14:02:55 +08002910 // We only need one card marking on the destination array.
2911 codegen_->MarkGCCard(dest.W(), Register(), /* value_can_be_null */ false);
2912
Roland Levillain0b671c02016-08-19 12:02:34 +01002913 __ Bind(intrinsic_slow_path->GetExitLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002914}
2915
Anton Kirilova3ffea22016-04-07 17:02:37 +01002916static void GenIsInfinite(LocationSummary* locations,
2917 bool is64bit,
Scott Wakeling97c72b72016-06-24 16:19:36 +01002918 MacroAssembler* masm) {
Anton Kirilova3ffea22016-04-07 17:02:37 +01002919 Operand infinity;
2920 Register out;
2921
2922 if (is64bit) {
2923 infinity = kPositiveInfinityDouble;
2924 out = XRegisterFrom(locations->Out());
2925 } else {
2926 infinity = kPositiveInfinityFloat;
2927 out = WRegisterFrom(locations->Out());
2928 }
2929
Scott Wakeling97c72b72016-06-24 16:19:36 +01002930 const Register zero = vixl::aarch64::Assembler::AppropriateZeroRegFor(out);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002931
2932 MoveFPToInt(locations, is64bit, masm);
2933 __ Eor(out, out, infinity);
2934 // We don't care about the sign bit, so shift left.
2935 __ Cmp(zero, Operand(out, LSL, 1));
2936 __ Cset(out, eq);
2937}
2938
2939void IntrinsicLocationsBuilderARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2940 CreateFPToIntLocations(arena_, invoke);
2941}
2942
2943void IntrinsicCodeGeneratorARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2944 GenIsInfinite(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
2945}
2946
2947void IntrinsicLocationsBuilderARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2948 CreateFPToIntLocations(arena_, invoke);
2949}
2950
2951void IntrinsicCodeGeneratorARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2952 GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
2953}
2954
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002955void IntrinsicLocationsBuilderARM64::VisitIntegerValueOf(HInvoke* invoke) {
2956 InvokeRuntimeCallingConvention calling_convention;
2957 IntrinsicVisitor::ComputeIntegerValueOfLocations(
2958 invoke,
2959 codegen_,
2960 calling_convention.GetReturnLocation(Primitive::kPrimNot),
2961 Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
2962}
2963
2964void IntrinsicCodeGeneratorARM64::VisitIntegerValueOf(HInvoke* invoke) {
2965 IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo();
2966 LocationSummary* locations = invoke->GetLocations();
2967 MacroAssembler* masm = GetVIXLAssembler();
2968
2969 Register out = RegisterFrom(locations->Out(), Primitive::kPrimNot);
2970 UseScratchRegisterScope temps(masm);
2971 Register temp = temps.AcquireW();
2972 InvokeRuntimeCallingConvention calling_convention;
2973 Register argument = calling_convention.GetRegisterAt(0);
2974 if (invoke->InputAt(0)->IsConstant()) {
2975 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
2976 if (value >= info.low && value <= info.high) {
2977 // Just embed the j.l.Integer in the code.
2978 ScopedObjectAccess soa(Thread::Current());
2979 mirror::Object* boxed = info.cache->Get(value + (-info.low));
2980 DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed));
2981 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(boxed));
2982 __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
2983 } else {
2984 // Allocate and initialize a new j.l.Integer.
2985 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
2986 // JIT object table.
2987 uint32_t address =
2988 dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2989 __ Ldr(argument.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
2990 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2991 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2992 __ Mov(temp.W(), value);
2993 __ Str(temp.W(), HeapOperand(out.W(), info.value_offset));
2994 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2995 // one.
2996 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2997 }
2998 } else {
2999 Register in = RegisterFrom(locations->InAt(0), Primitive::kPrimInt);
3000 // Check bounds of our cache.
3001 __ Add(out.W(), in.W(), -info.low);
3002 __ Cmp(out.W(), info.high - info.low + 1);
3003 vixl::aarch64::Label allocate, done;
3004 __ B(&allocate, hs);
3005 // If the value is within the bounds, load the j.l.Integer directly from the array.
3006 uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
3007 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.cache));
3008 __ Ldr(temp.W(), codegen_->DeduplicateBootImageAddressLiteral(data_offset + address));
3009 MemOperand source = HeapOperand(
3010 temp, out.X(), LSL, Primitive::ComponentSizeShift(Primitive::kPrimNot));
3011 codegen_->Load(Primitive::kPrimNot, out, source);
3012 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(out);
3013 __ B(&done);
3014 __ Bind(&allocate);
3015 // Otherwise allocate and initialize a new j.l.Integer.
3016 address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
3017 __ Ldr(argument.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
3018 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
3019 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
3020 __ Str(in.W(), HeapOperand(out.W(), info.value_offset));
3021 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
3022 // one.
3023 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3024 __ Bind(&done);
3025 }
3026}
3027
Nicolas Geoffray365719c2017-03-08 13:11:50 +00003028void IntrinsicLocationsBuilderARM64::VisitThreadInterrupted(HInvoke* invoke) {
3029 LocationSummary* locations = new (arena_) LocationSummary(invoke,
3030 LocationSummary::kNoCall,
3031 kIntrinsified);
3032 locations->SetOut(Location::RequiresRegister());
3033}
3034
3035void IntrinsicCodeGeneratorARM64::VisitThreadInterrupted(HInvoke* invoke) {
3036 MacroAssembler* masm = GetVIXLAssembler();
3037 Register out = RegisterFrom(invoke->GetLocations()->Out(), Primitive::kPrimInt);
3038 UseScratchRegisterScope temps(masm);
3039 Register temp = temps.AcquireX();
3040
3041 __ Add(temp, tr, Thread::InterruptedOffset<kArm64PointerSize>().Int32Value());
3042 __ Ldar(out.W(), MemOperand(temp));
3043
3044 vixl::aarch64::Label done;
3045 __ Cbz(out.W(), &done);
3046 __ Stlr(wzr, MemOperand(temp));
3047 __ Bind(&done);
3048}
3049
Vladimir Markod254f5c2017-06-02 15:18:36 +00003050UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
Andreas Gampe878d58c2015-01-15 23:24:00 -08003051
Aart Bikff7d89c2016-11-07 08:49:28 -08003052UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf);
3053UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08003054UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferAppend);
3055UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferLength);
3056UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferToString);
3057UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderAppend);
3058UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderLength);
3059UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08003060
Aart Bik0e54c012016-03-04 12:08:31 -08003061// 1.8.
3062UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt)
3063UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong)
3064UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt)
3065UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong)
3066UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08003067
Aart Bik2f9fcc92016-03-01 15:16:54 -08003068UNREACHABLE_INTRINSICS(ARM64)
Roland Levillain4d027112015-07-01 15:41:14 +01003069
3070#undef __
3071
Andreas Gampe878d58c2015-01-15 23:24:00 -08003072} // namespace arm64
3073} // namespace art