blob: 1ed1b7537e7f5490d38c60fe69e33106c8df6a8f [file] [log] [blame]
Andreas Gampe878d58c2015-01-15 23:24:00 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "intrinsics_arm64.h"
18
Serban Constantinescu579885a2015-02-22 20:51:33 +000019#include "arch/arm64/instruction_set_features_arm64.h"
Mathieu Chartiere401d142015-04-22 13:56:20 -070020#include "art_method.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080021#include "code_generator_arm64.h"
22#include "common_arm64.h"
23#include "entrypoints/quick/quick_entrypoints.h"
24#include "intrinsics.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080025#include "lock_word.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080026#include "mirror/array-inl.h"
Andreas Gampec15a2f42017-04-21 12:09:39 -070027#include "mirror/object_array-inl.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080028#include "mirror/reference.h"
Vladimir Markoe39f14f2017-02-10 15:44:25 +000029#include "mirror/string-inl.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080030#include "scoped_thread_state_change-inl.h"
Andreas Gampeb486a982017-06-01 13:45:54 -070031#include "thread-current-inl.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080032#include "utils/arm64/assembler_arm64.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080033
Scott Wakeling97c72b72016-06-24 16:19:36 +010034using namespace vixl::aarch64; // NOLINT(build/namespaces)
Andreas Gampe878d58c2015-01-15 23:24:00 -080035
Artem Serovaf4e42a2016-08-08 15:11:24 +010036// TODO(VIXL): Make VIXL compile with -Wshadow.
Scott Wakeling97c72b72016-06-24 16:19:36 +010037#pragma GCC diagnostic push
38#pragma GCC diagnostic ignored "-Wshadow"
Artem Serovaf4e42a2016-08-08 15:11:24 +010039#include "aarch64/disasm-aarch64.h"
40#include "aarch64/macro-assembler-aarch64.h"
Scott Wakeling97c72b72016-06-24 16:19:36 +010041#pragma GCC diagnostic pop
Andreas Gampe878d58c2015-01-15 23:24:00 -080042
43namespace art {
44
45namespace arm64 {
46
47using helpers::DRegisterFrom;
48using helpers::FPRegisterFrom;
49using helpers::HeapOperand;
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +000050using helpers::LocationFrom;
Scott Wakeling9ee23f42015-07-23 10:44:35 +010051using helpers::OperandFrom;
Andreas Gampe878d58c2015-01-15 23:24:00 -080052using helpers::RegisterFrom;
53using helpers::SRegisterFrom;
54using helpers::WRegisterFrom;
55using helpers::XRegisterFrom;
xueliang.zhong49924c92016-03-03 10:52:51 +000056using helpers::InputRegisterAt;
Scott Wakeling1f36f412016-04-21 11:13:45 +010057using helpers::OutputRegister;
Andreas Gampe878d58c2015-01-15 23:24:00 -080058
Andreas Gampe878d58c2015-01-15 23:24:00 -080059namespace {
60
61ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) {
62 return MemOperand(XRegisterFrom(location), offset);
63}
64
65} // namespace
66
Scott Wakeling97c72b72016-06-24 16:19:36 +010067MacroAssembler* IntrinsicCodeGeneratorARM64::GetVIXLAssembler() {
Alexandre Rames087930f2016-08-02 13:45:28 +010068 return codegen_->GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -080069}
70
71ArenaAllocator* IntrinsicCodeGeneratorARM64::GetAllocator() {
72 return codegen_->GetGraph()->GetArena();
73}
74
Alexandre Rames087930f2016-08-02 13:45:28 +010075#define __ codegen->GetVIXLAssembler()->
Andreas Gampe878d58c2015-01-15 23:24:00 -080076
77static void MoveFromReturnRegister(Location trg,
78 Primitive::Type type,
79 CodeGeneratorARM64* codegen) {
80 if (!trg.IsValid()) {
81 DCHECK(type == Primitive::kPrimVoid);
82 return;
83 }
84
85 DCHECK_NE(type, Primitive::kPrimVoid);
86
Jeff Hao848f70a2014-01-15 13:49:50 -080087 if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) {
Andreas Gampe878d58c2015-01-15 23:24:00 -080088 Register trg_reg = RegisterFrom(trg, type);
89 Register res_reg = RegisterFrom(ARM64ReturnLocation(type), type);
90 __ Mov(trg_reg, res_reg, kDiscardForSameWReg);
91 } else {
92 FPRegister trg_reg = FPRegisterFrom(trg, type);
93 FPRegister res_reg = FPRegisterFrom(ARM64ReturnLocation(type), type);
94 __ Fmov(trg_reg, res_reg);
95 }
96}
97
Roland Levillainec525fc2015-04-28 15:50:20 +010098static void MoveArguments(HInvoke* invoke, CodeGeneratorARM64* codegen) {
Roland Levillain2d27c8e2015-04-28 15:48:45 +010099 InvokeDexCallingConventionVisitorARM64 calling_convention_visitor;
Roland Levillainec525fc2015-04-28 15:50:20 +0100100 IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800101}
102
103// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
104// call. This will copy the arguments into the positions for a regular call.
105//
106// Note: The actual parameters are required to be in the locations given by the invoke's location
107// summary. If an intrinsic modifies those locations before a slowpath call, they must be
108// restored!
109class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 {
110 public:
David Srbecky9cd6d372016-02-09 15:24:47 +0000111 explicit IntrinsicSlowPathARM64(HInvoke* invoke)
112 : SlowPathCodeARM64(invoke), invoke_(invoke) { }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800113
114 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
115 CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
116 __ Bind(GetEntryLabel());
117
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000118 SaveLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800119
Roland Levillainec525fc2015-04-28 15:50:20 +0100120 MoveArguments(invoke_, codegen);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800121
Artem Serov914d7a82017-02-07 14:33:49 +0000122 {
123 // Ensure that between the BLR (emitted by Generate*Call) and RecordPcInfo there
124 // are no pools emitted.
125 vixl::EmissionCheckScope guard(codegen->GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes);
126 if (invoke_->IsInvokeStaticOrDirect()) {
Vladimir Markoe7197bf2017-06-02 17:00:23 +0100127 codegen->GenerateStaticOrDirectCall(
128 invoke_->AsInvokeStaticOrDirect(), LocationFrom(kArtMethodRegister), this);
Artem Serov914d7a82017-02-07 14:33:49 +0000129 } else {
Vladimir Markoe7197bf2017-06-02 17:00:23 +0100130 codegen->GenerateVirtualCall(
131 invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister), this);
Artem Serov914d7a82017-02-07 14:33:49 +0000132 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800133 }
134
135 // Copy the result back to the expected output.
136 Location out = invoke_->GetLocations()->Out();
137 if (out.IsValid()) {
138 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
139 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
140 MoveFromReturnRegister(out, invoke_->GetType(), codegen);
141 }
142
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000143 RestoreLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800144 __ B(GetExitLabel());
145 }
146
Alexandre Rames9931f312015-06-19 14:47:01 +0100147 const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathARM64"; }
148
Andreas Gampe878d58c2015-01-15 23:24:00 -0800149 private:
150 // The instruction where this slow path is happening.
151 HInvoke* const invoke_;
152
153 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM64);
154};
155
Roland Levillain0b671c02016-08-19 12:02:34 +0100156// Slow path implementing the SystemArrayCopy intrinsic copy loop with read barriers.
157class ReadBarrierSystemArrayCopySlowPathARM64 : public SlowPathCodeARM64 {
158 public:
159 ReadBarrierSystemArrayCopySlowPathARM64(HInstruction* instruction, Location tmp)
160 : SlowPathCodeARM64(instruction), tmp_(tmp) {
161 DCHECK(kEmitCompilerReadBarrier);
162 DCHECK(kUseBakerReadBarrier);
163 }
164
165 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
166 CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
167 LocationSummary* locations = instruction_->GetLocations();
168 DCHECK(locations->CanCall());
169 DCHECK(instruction_->IsInvokeStaticOrDirect())
170 << "Unexpected instruction in read barrier arraycopy slow path: "
171 << instruction_->DebugName();
172 DCHECK(instruction_->GetLocations()->Intrinsified());
173 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kSystemArrayCopy);
174
175 const int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
176
177 Register src_curr_addr = XRegisterFrom(locations->GetTemp(0));
178 Register dst_curr_addr = XRegisterFrom(locations->GetTemp(1));
179 Register src_stop_addr = XRegisterFrom(locations->GetTemp(2));
180 Register tmp_reg = WRegisterFrom(tmp_);
181
182 __ Bind(GetEntryLabel());
183 vixl::aarch64::Label slow_copy_loop;
184 __ Bind(&slow_copy_loop);
185 __ Ldr(tmp_reg, MemOperand(src_curr_addr, element_size, PostIndex));
186 codegen->GetAssembler()->MaybeUnpoisonHeapReference(tmp_reg);
187 // TODO: Inline the mark bit check before calling the runtime?
188 // tmp_reg = ReadBarrier::Mark(tmp_reg);
189 // No need to save live registers; it's taken care of by the
190 // entrypoint. Also, there is no need to update the stack mask,
191 // as this runtime call will not trigger a garbage collection.
192 // (See ReadBarrierMarkSlowPathARM64::EmitNativeCode for more
193 // explanations.)
194 DCHECK_NE(tmp_.reg(), LR);
195 DCHECK_NE(tmp_.reg(), WSP);
196 DCHECK_NE(tmp_.reg(), WZR);
197 // IP0 is used internally by the ReadBarrierMarkRegX entry point
198 // as a temporary (and not preserved). It thus cannot be used by
199 // any live register in this slow path.
200 DCHECK_NE(LocationFrom(src_curr_addr).reg(), IP0);
201 DCHECK_NE(LocationFrom(dst_curr_addr).reg(), IP0);
202 DCHECK_NE(LocationFrom(src_stop_addr).reg(), IP0);
203 DCHECK_NE(tmp_.reg(), IP0);
204 DCHECK(0 <= tmp_.reg() && tmp_.reg() < kNumberOfWRegisters) << tmp_.reg();
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000205 // TODO: Load the entrypoint once before the loop, instead of
206 // loading it at every iteration.
Roland Levillain0b671c02016-08-19 12:02:34 +0100207 int32_t entry_point_offset =
Roland Levillain97c46462017-05-11 14:04:03 +0100208 Thread::ReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(tmp_.reg());
Roland Levillain0b671c02016-08-19 12:02:34 +0100209 // This runtime call does not require a stack map.
210 codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
211 codegen->GetAssembler()->MaybePoisonHeapReference(tmp_reg);
212 __ Str(tmp_reg, MemOperand(dst_curr_addr, element_size, PostIndex));
213 __ Cmp(src_curr_addr, src_stop_addr);
214 __ B(&slow_copy_loop, ne);
215 __ B(GetExitLabel());
216 }
217
218 const char* GetDescription() const OVERRIDE { return "ReadBarrierSystemArrayCopySlowPathARM64"; }
219
220 private:
221 Location tmp_;
222
223 DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathARM64);
224};
Andreas Gampe878d58c2015-01-15 23:24:00 -0800225#undef __
226
227bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) {
228 Dispatch(invoke);
229 LocationSummary* res = invoke->GetLocations();
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000230 if (res == nullptr) {
231 return false;
232 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000233 return res->Intrinsified();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800234}
235
236#define __ masm->
237
238static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
239 LocationSummary* locations = new (arena) LocationSummary(invoke,
240 LocationSummary::kNoCall,
241 kIntrinsified);
242 locations->SetInAt(0, Location::RequiresFpuRegister());
243 locations->SetOut(Location::RequiresRegister());
244}
245
246static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
247 LocationSummary* locations = new (arena) LocationSummary(invoke,
248 LocationSummary::kNoCall,
249 kIntrinsified);
250 locations->SetInAt(0, Location::RequiresRegister());
251 locations->SetOut(Location::RequiresFpuRegister());
252}
253
Scott Wakeling97c72b72016-06-24 16:19:36 +0100254static void MoveFPToInt(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800255 Location input = locations->InAt(0);
256 Location output = locations->Out();
257 __ Fmov(is64bit ? XRegisterFrom(output) : WRegisterFrom(output),
258 is64bit ? DRegisterFrom(input) : SRegisterFrom(input));
259}
260
Scott Wakeling97c72b72016-06-24 16:19:36 +0100261static void MoveIntToFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800262 Location input = locations->InAt(0);
263 Location output = locations->Out();
264 __ Fmov(is64bit ? DRegisterFrom(output) : SRegisterFrom(output),
265 is64bit ? XRegisterFrom(input) : WRegisterFrom(input));
266}
267
268void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
269 CreateFPToIntLocations(arena_, invoke);
270}
271void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
272 CreateIntToFPLocations(arena_, invoke);
273}
274
275void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000276 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800277}
278void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000279 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800280}
281
282void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
283 CreateFPToIntLocations(arena_, invoke);
284}
285void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
286 CreateIntToFPLocations(arena_, invoke);
287}
288
289void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000290 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800291}
292void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000293 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800294}
295
296static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
297 LocationSummary* locations = new (arena) LocationSummary(invoke,
298 LocationSummary::kNoCall,
299 kIntrinsified);
300 locations->SetInAt(0, Location::RequiresRegister());
301 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
302}
303
304static void GenReverseBytes(LocationSummary* locations,
305 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100306 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800307 Location in = locations->InAt(0);
308 Location out = locations->Out();
309
310 switch (type) {
311 case Primitive::kPrimShort:
312 __ Rev16(WRegisterFrom(out), WRegisterFrom(in));
313 __ Sxth(WRegisterFrom(out), WRegisterFrom(out));
314 break;
315 case Primitive::kPrimInt:
316 case Primitive::kPrimLong:
317 __ Rev(RegisterFrom(out, type), RegisterFrom(in, type));
318 break;
319 default:
320 LOG(FATAL) << "Unexpected size for reverse-bytes: " << type;
321 UNREACHABLE();
322 }
323}
324
325void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
326 CreateIntToIntLocations(arena_, invoke);
327}
328
329void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
330 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
331}
332
333void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) {
334 CreateIntToIntLocations(arena_, invoke);
335}
336
337void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) {
338 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
339}
340
341void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) {
342 CreateIntToIntLocations(arena_, invoke);
343}
344
345void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
346 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
347}
348
Aart Bik7b565022016-01-28 14:36:22 -0800349static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
350 LocationSummary* locations = new (arena) LocationSummary(invoke,
351 LocationSummary::kNoCall,
352 kIntrinsified);
353 locations->SetInAt(0, Location::RequiresRegister());
354 locations->SetInAt(1, Location::RequiresRegister());
355 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
356}
357
Scott Wakeling611d3392015-07-10 11:42:06 +0100358static void GenNumberOfLeadingZeros(LocationSummary* locations,
359 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100360 MacroAssembler* masm) {
Scott Wakeling611d3392015-07-10 11:42:06 +0100361 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
362
363 Location in = locations->InAt(0);
364 Location out = locations->Out();
365
366 __ Clz(RegisterFrom(out, type), RegisterFrom(in, type));
367}
368
369void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
370 CreateIntToIntLocations(arena_, invoke);
371}
372
373void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
374 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
375}
376
377void IntrinsicLocationsBuilderARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
378 CreateIntToIntLocations(arena_, invoke);
379}
380
381void IntrinsicCodeGeneratorARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
382 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
383}
384
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100385static void GenNumberOfTrailingZeros(LocationSummary* locations,
386 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100387 MacroAssembler* masm) {
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100388 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
389
390 Location in = locations->InAt(0);
391 Location out = locations->Out();
392
393 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
394 __ Clz(RegisterFrom(out, type), RegisterFrom(out, type));
395}
396
397void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
398 CreateIntToIntLocations(arena_, invoke);
399}
400
401void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
402 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
403}
404
405void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
406 CreateIntToIntLocations(arena_, invoke);
407}
408
409void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
410 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
411}
412
Andreas Gampe878d58c2015-01-15 23:24:00 -0800413static void GenReverse(LocationSummary* locations,
414 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100415 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800416 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
417
418 Location in = locations->InAt(0);
419 Location out = locations->Out();
420
421 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
422}
423
424void IntrinsicLocationsBuilderARM64::VisitIntegerReverse(HInvoke* invoke) {
425 CreateIntToIntLocations(arena_, invoke);
426}
427
428void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) {
429 GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
430}
431
432void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) {
433 CreateIntToIntLocations(arena_, invoke);
434}
435
436void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
437 GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
438}
439
Scott Wakeling97c72b72016-06-24 16:19:36 +0100440static void GenBitCount(HInvoke* instr, Primitive::Type type, MacroAssembler* masm) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100441 DCHECK(Primitive::IsIntOrLongType(type)) << type;
442 DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
443 DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
xueliang.zhong49924c92016-03-03 10:52:51 +0000444
xueliang.zhong49924c92016-03-03 10:52:51 +0000445 UseScratchRegisterScope temps(masm);
446
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000447 Register src = InputRegisterAt(instr, 0);
Roland Levillainfa3912e2016-04-01 18:21:55 +0100448 Register dst = RegisterFrom(instr->GetLocations()->Out(), type);
449 FPRegister fpr = (type == Primitive::kPrimLong) ? temps.AcquireD() : temps.AcquireS();
xueliang.zhong49924c92016-03-03 10:52:51 +0000450
451 __ Fmov(fpr, src);
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000452 __ Cnt(fpr.V8B(), fpr.V8B());
453 __ Addv(fpr.B(), fpr.V8B());
xueliang.zhong49924c92016-03-03 10:52:51 +0000454 __ Fmov(dst, fpr);
455}
456
457void IntrinsicLocationsBuilderARM64::VisitLongBitCount(HInvoke* invoke) {
458 CreateIntToIntLocations(arena_, invoke);
459}
460
461void IntrinsicCodeGeneratorARM64::VisitLongBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100462 GenBitCount(invoke, Primitive::kPrimLong, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000463}
464
465void IntrinsicLocationsBuilderARM64::VisitIntegerBitCount(HInvoke* invoke) {
466 CreateIntToIntLocations(arena_, invoke);
467}
468
469void IntrinsicCodeGeneratorARM64::VisitIntegerBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100470 GenBitCount(invoke, Primitive::kPrimInt, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000471}
472
Petre-Ionut Tudorda483162017-08-14 13:54:31 +0100473static void GenHighestOneBit(HInvoke* invoke, Primitive::Type type, MacroAssembler* masm) {
474 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
475
476 UseScratchRegisterScope temps(masm);
477
478 Register src = InputRegisterAt(invoke, 0);
479 Register dst = RegisterFrom(invoke->GetLocations()->Out(), type);
480 Register temp = (type == Primitive::kPrimLong) ? temps.AcquireX() : temps.AcquireW();
481 size_t high_bit = (type == Primitive::kPrimLong) ? 63u : 31u;
482 size_t clz_high_bit = (type == Primitive::kPrimLong) ? 6u : 5u;
483
484 __ Clz(temp, src);
485 __ Mov(dst, UINT64_C(1) << high_bit); // MOV (bitmask immediate)
486 __ Bic(dst, dst, Operand(temp, LSL, high_bit - clz_high_bit)); // Clear dst if src was 0.
487 __ Lsr(dst, dst, temp);
488}
489
490void IntrinsicLocationsBuilderARM64::VisitIntegerHighestOneBit(HInvoke* invoke) {
491 CreateIntToIntLocations(arena_, invoke);
492}
493
494void IntrinsicCodeGeneratorARM64::VisitIntegerHighestOneBit(HInvoke* invoke) {
495 GenHighestOneBit(invoke, Primitive::kPrimInt, GetVIXLAssembler());
496}
497
498void IntrinsicLocationsBuilderARM64::VisitLongHighestOneBit(HInvoke* invoke) {
499 CreateIntToIntLocations(arena_, invoke);
500}
501
502void IntrinsicCodeGeneratorARM64::VisitLongHighestOneBit(HInvoke* invoke) {
503 GenHighestOneBit(invoke, Primitive::kPrimLong, GetVIXLAssembler());
504}
505
506static void GenLowestOneBit(HInvoke* invoke, Primitive::Type type, MacroAssembler* masm) {
507 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
508
509 UseScratchRegisterScope temps(masm);
510
511 Register src = InputRegisterAt(invoke, 0);
512 Register dst = RegisterFrom(invoke->GetLocations()->Out(), type);
513 Register temp = (type == Primitive::kPrimLong) ? temps.AcquireX() : temps.AcquireW();
514
515 __ Neg(temp, src);
516 __ And(dst, temp, src);
517}
518
519void IntrinsicLocationsBuilderARM64::VisitIntegerLowestOneBit(HInvoke* invoke) {
520 CreateIntToIntLocations(arena_, invoke);
521}
522
523void IntrinsicCodeGeneratorARM64::VisitIntegerLowestOneBit(HInvoke* invoke) {
524 GenLowestOneBit(invoke, Primitive::kPrimInt, GetVIXLAssembler());
525}
526
527void IntrinsicLocationsBuilderARM64::VisitLongLowestOneBit(HInvoke* invoke) {
528 CreateIntToIntLocations(arena_, invoke);
529}
530
531void IntrinsicCodeGeneratorARM64::VisitLongLowestOneBit(HInvoke* invoke) {
532 GenLowestOneBit(invoke, Primitive::kPrimLong, GetVIXLAssembler());
533}
534
Andreas Gampe878d58c2015-01-15 23:24:00 -0800535static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800536 LocationSummary* locations = new (arena) LocationSummary(invoke,
537 LocationSummary::kNoCall,
538 kIntrinsified);
539 locations->SetInAt(0, Location::RequiresFpuRegister());
540 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
541}
542
Scott Wakeling97c72b72016-06-24 16:19:36 +0100543static void MathAbsFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800544 Location in = locations->InAt(0);
545 Location out = locations->Out();
546
547 FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in);
548 FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out);
549
550 __ Fabs(out_reg, in_reg);
551}
552
553void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) {
554 CreateFPToFPLocations(arena_, invoke);
555}
556
557void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000558 MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800559}
560
561void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) {
562 CreateFPToFPLocations(arena_, invoke);
563}
564
565void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000566 MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800567}
568
Andreas Gampe878d58c2015-01-15 23:24:00 -0800569static void GenAbsInteger(LocationSummary* locations,
570 bool is64bit,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100571 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800572 Location in = locations->InAt(0);
573 Location output = locations->Out();
574
575 Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in);
576 Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output);
577
578 __ Cmp(in_reg, Operand(0));
579 __ Cneg(out_reg, in_reg, lt);
580}
581
582void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
Vladimir Marko4fae4fb2017-08-29 12:00:09 +0100583 CreateIntToIntLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800584}
585
586void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000587 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800588}
589
590void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
Vladimir Marko4fae4fb2017-08-29 12:00:09 +0100591 CreateIntToIntLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800592}
593
594void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000595 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800596}
597
598static void GenMinMaxFP(LocationSummary* locations,
599 bool is_min,
600 bool is_double,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100601 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800602 Location op1 = locations->InAt(0);
603 Location op2 = locations->InAt(1);
604 Location out = locations->Out();
605
606 FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1);
607 FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2);
608 FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out);
609 if (is_min) {
610 __ Fmin(out_reg, op1_reg, op2_reg);
611 } else {
612 __ Fmax(out_reg, op1_reg, op2_reg);
613 }
614}
615
616static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
617 LocationSummary* locations = new (arena) LocationSummary(invoke,
618 LocationSummary::kNoCall,
619 kIntrinsified);
620 locations->SetInAt(0, Location::RequiresFpuRegister());
621 locations->SetInAt(1, Location::RequiresFpuRegister());
622 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
623}
624
625void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
626 CreateFPFPToFPLocations(arena_, invoke);
627}
628
629void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000630 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800631}
632
633void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
634 CreateFPFPToFPLocations(arena_, invoke);
635}
636
637void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000638 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800639}
640
641void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
642 CreateFPFPToFPLocations(arena_, invoke);
643}
644
645void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000646 GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800647}
648
649void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
650 CreateFPFPToFPLocations(arena_, invoke);
651}
652
653void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000654 GenMinMaxFP(
655 invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800656}
657
658static void GenMinMax(LocationSummary* locations,
659 bool is_min,
660 bool is_long,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100661 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800662 Location op1 = locations->InAt(0);
663 Location op2 = locations->InAt(1);
664 Location out = locations->Out();
665
666 Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
667 Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
668 Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
669
670 __ Cmp(op1_reg, op2_reg);
671 __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
672}
673
Andreas Gampe878d58c2015-01-15 23:24:00 -0800674void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
675 CreateIntIntToIntLocations(arena_, invoke);
676}
677
678void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000679 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800680}
681
682void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) {
683 CreateIntIntToIntLocations(arena_, invoke);
684}
685
686void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000687 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800688}
689
690void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) {
691 CreateIntIntToIntLocations(arena_, invoke);
692}
693
694void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000695 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800696}
697
698void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) {
699 CreateIntIntToIntLocations(arena_, invoke);
700}
701
702void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000703 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800704}
705
706void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) {
707 CreateFPToFPLocations(arena_, invoke);
708}
709
710void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) {
711 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100712 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800713 __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
714}
715
716void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) {
717 CreateFPToFPLocations(arena_, invoke);
718}
719
720void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) {
721 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100722 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800723 __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
724}
725
726void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) {
727 CreateFPToFPLocations(arena_, invoke);
728}
729
730void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) {
731 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100732 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800733 __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
734}
735
736void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) {
737 CreateFPToFPLocations(arena_, invoke);
738}
739
740void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) {
741 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100742 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800743 __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
744}
745
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100746static void CreateFPToIntPlusFPTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800747 LocationSummary* locations = new (arena) LocationSummary(invoke,
748 LocationSummary::kNoCall,
749 kIntrinsified);
750 locations->SetInAt(0, Location::RequiresFpuRegister());
751 locations->SetOut(Location::RequiresRegister());
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100752 locations->AddTemp(Location::RequiresFpuRegister());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800753}
754
Scott Wakeling97c72b72016-06-24 16:19:36 +0100755static void GenMathRound(HInvoke* invoke, bool is_double, vixl::aarch64::MacroAssembler* masm) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100756 // Java 8 API definition for Math.round():
757 // Return the closest long or int to the argument, with ties rounding to positive infinity.
758 //
759 // There is no single instruction in ARMv8 that can support the above definition.
760 // We choose to use FCVTAS here, because it has closest semantic.
761 // FCVTAS performs rounding to nearest integer, ties away from zero.
762 // For most inputs (positive values, zero or NaN), this instruction is enough.
763 // We only need a few handling code after FCVTAS if the input is negative half value.
764 //
765 // The reason why we didn't choose FCVTPS instruction here is that
766 // although it performs rounding toward positive infinity, it doesn't perform rounding to nearest.
767 // For example, FCVTPS(-1.9) = -1 and FCVTPS(1.1) = 2.
768 // If we were using this instruction, for most inputs, more handling code would be needed.
769 LocationSummary* l = invoke->GetLocations();
770 FPRegister in_reg = is_double ? DRegisterFrom(l->InAt(0)) : SRegisterFrom(l->InAt(0));
771 FPRegister tmp_fp = is_double ? DRegisterFrom(l->GetTemp(0)) : SRegisterFrom(l->GetTemp(0));
772 Register out_reg = is_double ? XRegisterFrom(l->Out()) : WRegisterFrom(l->Out());
Scott Wakeling97c72b72016-06-24 16:19:36 +0100773 vixl::aarch64::Label done;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800774
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100775 // Round to nearest integer, ties away from zero.
776 __ Fcvtas(out_reg, in_reg);
777
778 // For positive values, zero or NaN inputs, rounding is done.
Scott Wakeling97c72b72016-06-24 16:19:36 +0100779 __ Tbz(out_reg, out_reg.GetSizeInBits() - 1, &done);
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100780
781 // Handle input < 0 cases.
782 // If input is negative but not a tie, previous result (round to nearest) is valid.
783 // If input is a negative tie, out_reg += 1.
784 __ Frinta(tmp_fp, in_reg);
785 __ Fsub(tmp_fp, in_reg, tmp_fp);
786 __ Fcmp(tmp_fp, 0.5);
787 __ Cinc(out_reg, out_reg, eq);
788
789 __ Bind(&done);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800790}
791
792void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100793 CreateFPToIntPlusFPTempLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800794}
795
796void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100797 GenMathRound(invoke, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800798}
799
800void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100801 CreateFPToIntPlusFPTempLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800802}
803
804void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100805 GenMathRound(invoke, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800806}
807
808void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) {
809 CreateIntToIntLocations(arena_, invoke);
810}
811
812void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100813 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800814 __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()),
815 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
816}
817
818void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
819 CreateIntToIntLocations(arena_, invoke);
820}
821
822void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100823 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800824 __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()),
825 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
826}
827
828void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
829 CreateIntToIntLocations(arena_, invoke);
830}
831
832void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100833 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800834 __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()),
835 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
836}
837
838void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
839 CreateIntToIntLocations(arena_, invoke);
840}
841
842void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100843 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800844 __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()),
845 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
846}
847
848static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
849 LocationSummary* locations = new (arena) LocationSummary(invoke,
850 LocationSummary::kNoCall,
851 kIntrinsified);
852 locations->SetInAt(0, Location::RequiresRegister());
853 locations->SetInAt(1, Location::RequiresRegister());
854}
855
856void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) {
857 CreateIntIntToVoidLocations(arena_, invoke);
858}
859
860void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100861 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800862 __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)),
863 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
864}
865
866void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
867 CreateIntIntToVoidLocations(arena_, invoke);
868}
869
870void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100871 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800872 __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)),
873 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
874}
875
876void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
877 CreateIntIntToVoidLocations(arena_, invoke);
878}
879
880void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100881 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800882 __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)),
883 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
884}
885
886void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
887 CreateIntIntToVoidLocations(arena_, invoke);
888}
889
890void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100891 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800892 __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)),
893 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
894}
895
896void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) {
897 LocationSummary* locations = new (arena_) LocationSummary(invoke,
898 LocationSummary::kNoCall,
899 kIntrinsified);
900 locations->SetOut(Location::RequiresRegister());
901}
902
903void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) {
904 codegen_->Load(Primitive::kPrimNot, WRegisterFrom(invoke->GetLocations()->Out()),
Andreas Gampe542451c2016-07-26 09:02:02 -0700905 MemOperand(tr, Thread::PeerOffset<kArm64PointerSize>().Int32Value()));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800906}
907
908static void GenUnsafeGet(HInvoke* invoke,
909 Primitive::Type type,
910 bool is_volatile,
911 CodeGeneratorARM64* codegen) {
912 LocationSummary* locations = invoke->GetLocations();
913 DCHECK((type == Primitive::kPrimInt) ||
914 (type == Primitive::kPrimLong) ||
915 (type == Primitive::kPrimNot));
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000916 Location base_loc = locations->InAt(1);
917 Register base = WRegisterFrom(base_loc); // Object pointer.
918 Location offset_loc = locations->InAt(2);
919 Register offset = XRegisterFrom(offset_loc); // Long offset.
920 Location trg_loc = locations->Out();
921 Register trg = RegisterFrom(trg_loc, type);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800922
Roland Levillain44015862016-01-22 11:47:17 +0000923 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
924 // UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case.
Roland Levillain54f869e2017-03-06 13:54:11 +0000925 Register temp = WRegisterFrom(locations->GetTemp(0));
Roland Levillainbfea3352016-06-23 13:48:47 +0100926 codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke,
927 trg_loc,
928 base,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100929 /* offset */ 0u,
Roland Levillainbfea3352016-06-23 13:48:47 +0100930 /* index */ offset_loc,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100931 /* scale_factor */ 0u,
Roland Levillainbfea3352016-06-23 13:48:47 +0100932 temp,
933 /* needs_null_check */ false,
934 is_volatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800935 } else {
Roland Levillain44015862016-01-22 11:47:17 +0000936 // Other cases.
937 MemOperand mem_op(base.X(), offset);
938 if (is_volatile) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +0000939 codegen->LoadAcquire(invoke, trg, mem_op, /* needs_null_check */ true);
Roland Levillain44015862016-01-22 11:47:17 +0000940 } else {
941 codegen->Load(type, trg, mem_op);
942 }
Roland Levillain4d027112015-07-01 15:41:14 +0100943
Roland Levillain44015862016-01-22 11:47:17 +0000944 if (type == Primitive::kPrimNot) {
945 DCHECK(trg.IsW());
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100946 codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0u, offset_loc);
Roland Levillain44015862016-01-22 11:47:17 +0000947 }
Roland Levillain4d027112015-07-01 15:41:14 +0100948 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800949}
950
951static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000952 bool can_call = kEmitCompilerReadBarrier &&
953 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
954 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800955 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100956 (can_call
957 ? LocationSummary::kCallOnSlowPath
958 : LocationSummary::kNoCall),
Andreas Gampe878d58c2015-01-15 23:24:00 -0800959 kIntrinsified);
Vladimir Marko70e97462016-08-09 11:04:26 +0100960 if (can_call && kUseBakerReadBarrier) {
Vladimir Marko804b03f2016-09-14 16:26:36 +0100961 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Roland Levillain54f869e2017-03-06 13:54:11 +0000962 // We need a temporary register for the read barrier marking slow
963 // path in CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier.
964 locations->AddTemp(Location::RequiresRegister());
Vladimir Marko70e97462016-08-09 11:04:26 +0100965 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800966 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
967 locations->SetInAt(1, Location::RequiresRegister());
968 locations->SetInAt(2, Location::RequiresRegister());
Roland Levillainbfea3352016-06-23 13:48:47 +0100969 locations->SetOut(Location::RequiresRegister(),
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100970 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800971}
972
973void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
974 CreateIntIntIntToIntLocations(arena_, invoke);
975}
976void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
977 CreateIntIntIntToIntLocations(arena_, invoke);
978}
979void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
980 CreateIntIntIntToIntLocations(arena_, invoke);
981}
982void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
983 CreateIntIntIntToIntLocations(arena_, invoke);
984}
985void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
986 CreateIntIntIntToIntLocations(arena_, invoke);
987}
988void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
989 CreateIntIntIntToIntLocations(arena_, invoke);
990}
991
992void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000993 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800994}
995void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000996 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800997}
998void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000999 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001000}
1001void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001002 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001003}
1004void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001005 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001006}
1007void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001008 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001009}
1010
1011static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
1012 LocationSummary* locations = new (arena) LocationSummary(invoke,
1013 LocationSummary::kNoCall,
1014 kIntrinsified);
1015 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1016 locations->SetInAt(1, Location::RequiresRegister());
1017 locations->SetInAt(2, Location::RequiresRegister());
1018 locations->SetInAt(3, Location::RequiresRegister());
1019}
1020
1021void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
1022 CreateIntIntIntIntToVoid(arena_, invoke);
1023}
1024void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
1025 CreateIntIntIntIntToVoid(arena_, invoke);
1026}
1027void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
1028 CreateIntIntIntIntToVoid(arena_, invoke);
1029}
1030void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
1031 CreateIntIntIntIntToVoid(arena_, invoke);
1032}
1033void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
1034 CreateIntIntIntIntToVoid(arena_, invoke);
1035}
1036void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
1037 CreateIntIntIntIntToVoid(arena_, invoke);
1038}
1039void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
1040 CreateIntIntIntIntToVoid(arena_, invoke);
1041}
1042void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
1043 CreateIntIntIntIntToVoid(arena_, invoke);
1044}
1045void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
1046 CreateIntIntIntIntToVoid(arena_, invoke);
1047}
1048
Artem Serov914d7a82017-02-07 14:33:49 +00001049static void GenUnsafePut(HInvoke* invoke,
Andreas Gampe878d58c2015-01-15 23:24:00 -08001050 Primitive::Type type,
1051 bool is_volatile,
1052 bool is_ordered,
1053 CodeGeneratorARM64* codegen) {
Artem Serov914d7a82017-02-07 14:33:49 +00001054 LocationSummary* locations = invoke->GetLocations();
Alexandre Rames087930f2016-08-02 13:45:28 +01001055 MacroAssembler* masm = codegen->GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -08001056
1057 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
1058 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
1059 Register value = RegisterFrom(locations->InAt(3), type);
Roland Levillain4d027112015-07-01 15:41:14 +01001060 Register source = value;
Andreas Gampe878d58c2015-01-15 23:24:00 -08001061 MemOperand mem_op(base.X(), offset);
1062
Roland Levillain4d027112015-07-01 15:41:14 +01001063 {
1064 // We use a block to end the scratch scope before the write barrier, thus
1065 // freeing the temporary registers so they can be used in `MarkGCCard`.
1066 UseScratchRegisterScope temps(masm);
1067
1068 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1069 DCHECK(value.IsW());
1070 Register temp = temps.AcquireW();
1071 __ Mov(temp.W(), value.W());
1072 codegen->GetAssembler()->PoisonHeapReference(temp.W());
1073 source = temp;
Andreas Gampe878d58c2015-01-15 23:24:00 -08001074 }
Roland Levillain4d027112015-07-01 15:41:14 +01001075
1076 if (is_volatile || is_ordered) {
Artem Serov914d7a82017-02-07 14:33:49 +00001077 codegen->StoreRelease(invoke, type, source, mem_op, /* needs_null_check */ false);
Roland Levillain4d027112015-07-01 15:41:14 +01001078 } else {
1079 codegen->Store(type, source, mem_op);
1080 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001081 }
1082
1083 if (type == Primitive::kPrimNot) {
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001084 bool value_can_be_null = true; // TODO: Worth finding out this information?
1085 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001086 }
1087}
1088
1089void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001090 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001091 Primitive::kPrimInt,
1092 /* is_volatile */ false,
1093 /* is_ordered */ false,
1094 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001095}
1096void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001097 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001098 Primitive::kPrimInt,
1099 /* is_volatile */ false,
1100 /* is_ordered */ true,
1101 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001102}
1103void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001104 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001105 Primitive::kPrimInt,
1106 /* is_volatile */ true,
1107 /* is_ordered */ false,
1108 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001109}
1110void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001111 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001112 Primitive::kPrimNot,
1113 /* is_volatile */ false,
1114 /* is_ordered */ false,
1115 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001116}
1117void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001118 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001119 Primitive::kPrimNot,
1120 /* is_volatile */ false,
1121 /* is_ordered */ true,
1122 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001123}
1124void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001125 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001126 Primitive::kPrimNot,
1127 /* is_volatile */ true,
1128 /* is_ordered */ false,
1129 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001130}
1131void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001132 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001133 Primitive::kPrimLong,
1134 /* is_volatile */ false,
1135 /* is_ordered */ false,
1136 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001137}
1138void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001139 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001140 Primitive::kPrimLong,
1141 /* is_volatile */ false,
1142 /* is_ordered */ true,
1143 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001144}
1145void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001146 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001147 Primitive::kPrimLong,
1148 /* is_volatile */ true,
1149 /* is_ordered */ false,
1150 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001151}
1152
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001153static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
1154 HInvoke* invoke,
1155 Primitive::Type type) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001156 bool can_call = kEmitCompilerReadBarrier &&
1157 kUseBakerReadBarrier &&
1158 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001159 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001160 (can_call
1161 ? LocationSummary::kCallOnSlowPath
1162 : LocationSummary::kNoCall),
Andreas Gampe878d58c2015-01-15 23:24:00 -08001163 kIntrinsified);
1164 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1165 locations->SetInAt(1, Location::RequiresRegister());
1166 locations->SetInAt(2, Location::RequiresRegister());
1167 locations->SetInAt(3, Location::RequiresRegister());
1168 locations->SetInAt(4, Location::RequiresRegister());
1169
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001170 // If heap poisoning is enabled, we don't want the unpoisoning
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001171 // operations to potentially clobber the output. Likewise when
1172 // emitting a (Baker) read barrier, which may call.
1173 Location::OutputOverlap overlaps =
1174 ((kPoisonHeapReferences && type == Primitive::kPrimNot) || can_call)
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001175 ? Location::kOutputOverlap
1176 : Location::kNoOutputOverlap;
1177 locations->SetOut(Location::RequiresRegister(), overlaps);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001178 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1179 // Temporary register for (Baker) read barrier.
1180 locations->AddTemp(Location::RequiresRegister());
1181 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001182}
1183
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001184static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARM64* codegen) {
Alexandre Rames087930f2016-08-02 13:45:28 +01001185 MacroAssembler* masm = codegen->GetVIXLAssembler();
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001186 LocationSummary* locations = invoke->GetLocations();
Andreas Gampe878d58c2015-01-15 23:24:00 -08001187
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001188 Location out_loc = locations->Out();
1189 Register out = WRegisterFrom(out_loc); // Boolean result.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001190
1191 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001192 Location offset_loc = locations->InAt(2);
1193 Register offset = XRegisterFrom(offset_loc); // Long offset.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001194 Register expected = RegisterFrom(locations->InAt(3), type); // Expected.
1195 Register value = RegisterFrom(locations->InAt(4), type); // Value.
1196
1197 // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
1198 if (type == Primitive::kPrimNot) {
1199 // Mark card for object assuming new value is stored.
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001200 bool value_can_be_null = true; // TODO: Worth finding out this information?
1201 codegen->MarkGCCard(base, value, value_can_be_null);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001202
1203 // The only read barrier implementation supporting the
1204 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1205 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1206
1207 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1208 Register temp = WRegisterFrom(locations->GetTemp(0));
1209 // Need to make sure the reference stored in the field is a to-space
1210 // one before attempting the CAS or the CAS could fail incorrectly.
Roland Levillainff487002017-03-07 16:50:01 +00001211 codegen->UpdateReferenceFieldWithBakerReadBarrier(
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001212 invoke,
1213 out_loc, // Unused, used only as a "temporary" within the read barrier.
1214 base,
Roland Levillainff487002017-03-07 16:50:01 +00001215 /* field_offset */ offset_loc,
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001216 temp,
1217 /* needs_null_check */ false,
Roland Levillainff487002017-03-07 16:50:01 +00001218 /* use_load_acquire */ false);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001219 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001220 }
1221
1222 UseScratchRegisterScope temps(masm);
1223 Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
1224 Register tmp_value = temps.AcquireSameSizeAs(value); // Value in memory.
1225
1226 Register tmp_32 = tmp_value.W();
1227
1228 __ Add(tmp_ptr, base.X(), Operand(offset));
1229
Roland Levillain4d027112015-07-01 15:41:14 +01001230 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1231 codegen->GetAssembler()->PoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001232 if (value.Is(expected)) {
1233 // Do not poison `value`, as it is the same register as
1234 // `expected`, which has just been poisoned.
1235 } else {
1236 codegen->GetAssembler()->PoisonHeapReference(value);
1237 }
Roland Levillain4d027112015-07-01 15:41:14 +01001238 }
1239
Andreas Gampe878d58c2015-01-15 23:24:00 -08001240 // do {
1241 // tmp_value = [tmp_ptr] - expected;
1242 // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1243 // result = tmp_value != 0;
1244
Scott Wakeling97c72b72016-06-24 16:19:36 +01001245 vixl::aarch64::Label loop_head, exit_loop;
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001246 __ Bind(&loop_head);
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001247 __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
1248 __ Cmp(tmp_value, expected);
1249 __ B(&exit_loop, ne);
1250 __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
1251 __ Cbnz(tmp_32, &loop_head);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001252 __ Bind(&exit_loop);
1253 __ Cset(out, eq);
Roland Levillain4d027112015-07-01 15:41:14 +01001254
1255 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillain4d027112015-07-01 15:41:14 +01001256 codegen->GetAssembler()->UnpoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001257 if (value.Is(expected)) {
1258 // Do not unpoison `value`, as it is the same register as
1259 // `expected`, which has just been unpoisoned.
1260 } else {
1261 codegen->GetAssembler()->UnpoisonHeapReference(value);
1262 }
Roland Levillain4d027112015-07-01 15:41:14 +01001263 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001264}
1265
1266void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001267 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001268}
1269void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001270 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001271}
1272void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001273 // The only read barrier implementation supporting the
1274 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1275 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain985ff702015-10-23 13:25:35 +01001276 return;
1277 }
1278
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001279 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001280}
1281
1282void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001283 GenCas(invoke, Primitive::kPrimInt, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001284}
1285void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001286 GenCas(invoke, Primitive::kPrimLong, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001287}
1288void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001289 // The only read barrier implementation supporting the
1290 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1291 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01001292
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001293 GenCas(invoke, Primitive::kPrimNot, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001294}
1295
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001296void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001297 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Scott Wakeling1f36f412016-04-21 11:13:45 +01001298 invoke->InputAt(1)->CanBeNull()
1299 ? LocationSummary::kCallOnSlowPath
1300 : LocationSummary::kNoCall,
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001301 kIntrinsified);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001302 locations->SetInAt(0, Location::RequiresRegister());
1303 locations->SetInAt(1, Location::RequiresRegister());
1304 locations->AddTemp(Location::RequiresRegister());
1305 locations->AddTemp(Location::RequiresRegister());
1306 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001307 // Need temporary registers for String compression's feature.
1308 if (mirror::kUseStringCompression) {
1309 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001310 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001311 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001312}
1313
1314void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001315 MacroAssembler* masm = GetVIXLAssembler();
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001316 LocationSummary* locations = invoke->GetLocations();
1317
Alexandre Rames2ea91532016-08-11 17:04:14 +01001318 Register str = InputRegisterAt(invoke, 0);
1319 Register arg = InputRegisterAt(invoke, 1);
1320 DCHECK(str.IsW());
1321 DCHECK(arg.IsW());
Scott Wakeling1f36f412016-04-21 11:13:45 +01001322 Register out = OutputRegister(invoke);
1323
1324 Register temp0 = WRegisterFrom(locations->GetTemp(0));
1325 Register temp1 = WRegisterFrom(locations->GetTemp(1));
1326 Register temp2 = WRegisterFrom(locations->GetTemp(2));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001327 Register temp3;
jessicahandojo05765752016-09-09 19:01:32 -07001328 if (mirror::kUseStringCompression) {
1329 temp3 = WRegisterFrom(locations->GetTemp(3));
jessicahandojo05765752016-09-09 19:01:32 -07001330 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001331
Scott Wakeling97c72b72016-06-24 16:19:36 +01001332 vixl::aarch64::Label loop;
1333 vixl::aarch64::Label find_char_diff;
1334 vixl::aarch64::Label end;
jessicahandojo05765752016-09-09 19:01:32 -07001335 vixl::aarch64::Label different_compression;
Scott Wakeling1f36f412016-04-21 11:13:45 +01001336
1337 // Get offsets of count and value fields within a string object.
1338 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1339 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1340
Nicolas Geoffray512e04d2015-03-27 17:21:24 +00001341 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +01001342 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001343
Scott Wakeling1f36f412016-04-21 11:13:45 +01001344 // Take slow path and throw if input can be and is null.
1345 SlowPathCodeARM64* slow_path = nullptr;
1346 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1347 if (can_slow_path) {
1348 slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1349 codegen_->AddSlowPath(slow_path);
1350 __ Cbz(arg, slow_path->GetEntryLabel());
1351 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001352
Scott Wakeling1f36f412016-04-21 11:13:45 +01001353 // Reference equality check, return 0 if same reference.
1354 __ Subs(out, str, arg);
1355 __ B(&end, eq);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001356
jessicahandojo05765752016-09-09 19:01:32 -07001357 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001358 // Load `count` fields of this and argument strings.
jessicahandojo05765752016-09-09 19:01:32 -07001359 __ Ldr(temp3, HeapOperand(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001360 __ Ldr(temp2, HeapOperand(arg, count_offset));
jessicahandojo05765752016-09-09 19:01:32 -07001361 // Clean out compression flag from lengths.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001362 __ Lsr(temp0, temp3, 1u);
1363 __ Lsr(temp1, temp2, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001364 } else {
1365 // Load lengths of this and argument strings.
1366 __ Ldr(temp0, HeapOperand(str, count_offset));
1367 __ Ldr(temp1, HeapOperand(arg, count_offset));
1368 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001369 // out = length diff.
1370 __ Subs(out, temp0, temp1);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001371 // temp0 = min(len(str), len(arg)).
1372 __ Csel(temp0, temp1, temp0, ge);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001373 // Shorter string is empty?
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001374 __ Cbz(temp0, &end);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001375
jessicahandojo05765752016-09-09 19:01:32 -07001376 if (mirror::kUseStringCompression) {
1377 // Check if both strings using same compression style to use this comparison loop.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001378 __ Eor(temp2, temp2, Operand(temp3));
1379 // Interleave with compression flag extraction which is needed for both paths
1380 // and also set flags which is needed only for the different compressions path.
1381 __ Ands(temp3.W(), temp3.W(), Operand(1));
1382 __ Tbnz(temp2, 0, &different_compression); // Does not use flags.
jessicahandojo05765752016-09-09 19:01:32 -07001383 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001384 // Store offset of string value in preparation for comparison loop.
1385 __ Mov(temp1, value_offset);
jessicahandojo05765752016-09-09 19:01:32 -07001386 if (mirror::kUseStringCompression) {
1387 // For string compression, calculate the number of bytes to compare (not chars).
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001388 // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
1389 __ Lsl(temp0, temp0, temp3);
jessicahandojo05765752016-09-09 19:01:32 -07001390 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001391
1392 UseScratchRegisterScope scratch_scope(masm);
1393 Register temp4 = scratch_scope.AcquireX();
1394
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001395 // Assertions that must hold in order to compare strings 8 bytes at a time.
Scott Wakeling1f36f412016-04-21 11:13:45 +01001396 DCHECK_ALIGNED(value_offset, 8);
1397 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1398
1399 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1400 DCHECK_EQ(char_size, 2u);
1401
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001402 // Promote temp2 to an X reg, ready for LDR.
1403 temp2 = temp2.X();
Scott Wakeling1f36f412016-04-21 11:13:45 +01001404
1405 // Loop to compare 4x16-bit characters at a time (ok because of string data alignment).
1406 __ Bind(&loop);
Alexandre Rames2ea91532016-08-11 17:04:14 +01001407 __ Ldr(temp4, MemOperand(str.X(), temp1.X()));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001408 __ Ldr(temp2, MemOperand(arg.X(), temp1.X()));
1409 __ Cmp(temp4, temp2);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001410 __ B(ne, &find_char_diff);
1411 __ Add(temp1, temp1, char_size * 4);
jessicahandojo05765752016-09-09 19:01:32 -07001412 // With string compression, we have compared 8 bytes, otherwise 4 chars.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001413 __ Subs(temp0, temp0, (mirror::kUseStringCompression) ? 8 : 4);
1414 __ B(&loop, hi);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001415 __ B(&end);
1416
1417 // Promote temp1 to an X reg, ready for EOR.
1418 temp1 = temp1.X();
1419
jessicahandojo05765752016-09-09 19:01:32 -07001420 // Find the single character difference.
Scott Wakeling1f36f412016-04-21 11:13:45 +01001421 __ Bind(&find_char_diff);
1422 // Get the bit position of the first character that differs.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001423 __ Eor(temp1, temp2, temp4);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001424 __ Rbit(temp1, temp1);
1425 __ Clz(temp1, temp1);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001426
jessicahandojo05765752016-09-09 19:01:32 -07001427 // If the number of chars remaining <= the index where the difference occurs (0-3), then
Scott Wakeling1f36f412016-04-21 11:13:45 +01001428 // the difference occurs outside the remaining string data, so just return length diff (out).
jessicahandojo05765752016-09-09 19:01:32 -07001429 // Unlike ARM, we're doing the comparison in one go here, without the subtraction at the
1430 // find_char_diff_2nd_cmp path, so it doesn't matter whether the comparison is signed or
1431 // unsigned when string compression is disabled.
1432 // When it's enabled, the comparison must be unsigned.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001433 __ Cmp(temp0, Operand(temp1.W(), LSR, (mirror::kUseStringCompression) ? 3 : 4));
jessicahandojo05765752016-09-09 19:01:32 -07001434 __ B(ls, &end);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001435
Scott Wakeling1f36f412016-04-21 11:13:45 +01001436 // Extract the characters and calculate the difference.
jessicahandojo05765752016-09-09 19:01:32 -07001437 if (mirror:: kUseStringCompression) {
jessicahandojo05765752016-09-09 19:01:32 -07001438 __ Bic(temp1, temp1, 0x7);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001439 __ Bic(temp1, temp1, Operand(temp3.X(), LSL, 3u));
1440 } else {
1441 __ Bic(temp1, temp1, 0xf);
jessicahandojo05765752016-09-09 19:01:32 -07001442 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001443 __ Lsr(temp2, temp2, temp1);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001444 __ Lsr(temp4, temp4, temp1);
jessicahandojo05765752016-09-09 19:01:32 -07001445 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001446 // Prioritize the case of compressed strings and calculate such result first.
1447 __ Uxtb(temp1, temp4);
1448 __ Sub(out, temp1.W(), Operand(temp2.W(), UXTB));
1449 __ Tbz(temp3, 0u, &end); // If actually compressed, we're done.
jessicahandojo05765752016-09-09 19:01:32 -07001450 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001451 __ Uxth(temp4, temp4);
1452 __ Sub(out, temp4.W(), Operand(temp2.W(), UXTH));
jessicahandojo05765752016-09-09 19:01:32 -07001453
1454 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001455 __ B(&end);
1456 __ Bind(&different_compression);
1457
1458 // Comparison for different compression style.
jessicahandojo05765752016-09-09 19:01:32 -07001459 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
1460 DCHECK_EQ(c_char_size, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001461 temp1 = temp1.W();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001462 temp2 = temp2.W();
1463 temp4 = temp4.W();
jessicahandojo05765752016-09-09 19:01:32 -07001464
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001465 // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
1466 // Note that flags have been set by the `str` compression flag extraction to `temp3`
1467 // before branching to the `different_compression` label.
1468 __ Csel(temp1, str, arg, eq); // Pointer to the compressed string.
1469 __ Csel(temp2, str, arg, ne); // Pointer to the uncompressed string.
jessicahandojo05765752016-09-09 19:01:32 -07001470
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001471 // We want to free up the temp3, currently holding `str` compression flag, for comparison.
1472 // So, we move it to the bottom bit of the iteration count `temp0` which we then need to treat
1473 // as unsigned. Start by freeing the bit with a LSL and continue further down by a SUB which
1474 // will allow `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
1475 __ Lsl(temp0, temp0, 1u);
1476
1477 // Adjust temp1 and temp2 from string pointers to data pointers.
1478 __ Add(temp1, temp1, Operand(value_offset));
1479 __ Add(temp2, temp2, Operand(value_offset));
1480
1481 // Complete the move of the compression flag.
1482 __ Sub(temp0, temp0, Operand(temp3));
1483
1484 vixl::aarch64::Label different_compression_loop;
1485 vixl::aarch64::Label different_compression_diff;
1486
1487 __ Bind(&different_compression_loop);
1488 __ Ldrb(temp4, MemOperand(temp1.X(), c_char_size, PostIndex));
1489 __ Ldrh(temp3, MemOperand(temp2.X(), char_size, PostIndex));
1490 __ Subs(temp4, temp4, Operand(temp3));
1491 __ B(&different_compression_diff, ne);
1492 __ Subs(temp0, temp0, 2);
1493 __ B(&different_compression_loop, hi);
jessicahandojo05765752016-09-09 19:01:32 -07001494 __ B(&end);
1495
1496 // Calculate the difference.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001497 __ Bind(&different_compression_diff);
1498 __ Tst(temp0, Operand(1));
1499 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1500 "Expecting 0=compressed, 1=uncompressed");
1501 __ Cneg(out, temp4, ne);
jessicahandojo05765752016-09-09 19:01:32 -07001502 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001503
1504 __ Bind(&end);
1505
1506 if (can_slow_path) {
1507 __ Bind(slow_path->GetExitLabel());
1508 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001509}
1510
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001511// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
1512// The normal loop plus the pre-header is 9 instructions without string compression and 12
1513// instructions with string compression. We can compare up to 8 bytes in 4 instructions
1514// (LDR+LDR+CMP+BNE) and up to 16 bytes in 5 instructions (LDP+LDP+CMP+CCMP+BNE). Allow up
1515// to 10 instructions for the unrolled loop.
1516constexpr size_t kShortConstStringEqualsCutoffInBytes = 32;
1517
1518static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
1519 if (candidate->IsLoadString()) {
1520 HLoadString* load_string = candidate->AsLoadString();
1521 const DexFile& dex_file = load_string->GetDexFile();
1522 return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
1523 }
1524 return nullptr;
1525}
1526
Agi Csakiea34b402015-08-13 17:51:19 -07001527void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
1528 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1529 LocationSummary::kNoCall,
1530 kIntrinsified);
1531 locations->SetInAt(0, Location::RequiresRegister());
1532 locations->SetInAt(1, Location::RequiresRegister());
Agi Csakiea34b402015-08-13 17:51:19 -07001533
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001534 // For the generic implementation and for long const strings we need a temporary.
1535 // We do not need it for short const strings, up to 8 bytes, see code generation below.
1536 uint32_t const_string_length = 0u;
1537 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1538 if (const_string == nullptr) {
1539 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1540 }
1541 bool is_compressed =
1542 mirror::kUseStringCompression &&
1543 const_string != nullptr &&
1544 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1545 if (const_string == nullptr || const_string_length > (is_compressed ? 8u : 4u)) {
1546 locations->AddTemp(Location::RequiresRegister());
1547 }
1548
1549 // TODO: If the String.equals() is used only for an immediately following HIf, we can
1550 // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
1551 // Then we shall need an extra temporary register instead of the output register.
Agi Csakiea34b402015-08-13 17:51:19 -07001552 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1553}
1554
1555void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001556 MacroAssembler* masm = GetVIXLAssembler();
Agi Csakiea34b402015-08-13 17:51:19 -07001557 LocationSummary* locations = invoke->GetLocations();
1558
1559 Register str = WRegisterFrom(locations->InAt(0));
1560 Register arg = WRegisterFrom(locations->InAt(1));
1561 Register out = XRegisterFrom(locations->Out());
1562
1563 UseScratchRegisterScope scratch_scope(masm);
1564 Register temp = scratch_scope.AcquireW();
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001565 Register temp1 = scratch_scope.AcquireW();
Agi Csakiea34b402015-08-13 17:51:19 -07001566
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001567 vixl::aarch64::Label loop;
Scott Wakeling97c72b72016-06-24 16:19:36 +01001568 vixl::aarch64::Label end;
1569 vixl::aarch64::Label return_true;
1570 vixl::aarch64::Label return_false;
Agi Csakiea34b402015-08-13 17:51:19 -07001571
1572 // Get offsets of count, value, and class fields within a string object.
1573 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1574 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1575 const int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1576
1577 // Note that the null check must have been done earlier.
1578 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1579
Vladimir Marko53b52002016-05-24 19:30:45 +01001580 StringEqualsOptimizations optimizations(invoke);
1581 if (!optimizations.GetArgumentNotNull()) {
1582 // Check if input is null, return false if it is.
1583 __ Cbz(arg, &return_false);
1584 }
Agi Csakiea34b402015-08-13 17:51:19 -07001585
1586 // Reference equality check, return true if same reference.
1587 __ Cmp(str, arg);
1588 __ B(&return_true, eq);
1589
Vladimir Marko53b52002016-05-24 19:30:45 +01001590 if (!optimizations.GetArgumentIsString()) {
1591 // Instanceof check for the argument by comparing class fields.
1592 // All string objects must have the same type since String cannot be subclassed.
1593 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1594 // If the argument is a string object, its class field must be equal to receiver's class field.
1595 __ Ldr(temp, MemOperand(str.X(), class_offset));
1596 __ Ldr(temp1, MemOperand(arg.X(), class_offset));
1597 __ Cmp(temp, temp1);
1598 __ B(&return_false, ne);
1599 }
Agi Csakiea34b402015-08-13 17:51:19 -07001600
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001601 // Check if one of the inputs is a const string. Do not special-case both strings
1602 // being const, such cases should be handled by constant folding if needed.
1603 uint32_t const_string_length = 0u;
1604 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1605 if (const_string == nullptr) {
1606 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1607 if (const_string != nullptr) {
1608 std::swap(str, arg); // Make sure the const string is in `str`.
1609 }
1610 }
1611 bool is_compressed =
1612 mirror::kUseStringCompression &&
1613 const_string != nullptr &&
1614 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1615
1616 if (const_string != nullptr) {
1617 // Load `count` field of the argument string and check if it matches the const string.
1618 // Also compares the compression style, if differs return false.
1619 __ Ldr(temp, MemOperand(arg.X(), count_offset));
Vladimir Marko26ec3ca2017-03-14 13:37:14 +00001620 // Temporarily release temp1 as we may not be able to embed the flagged count in CMP immediate.
1621 scratch_scope.Release(temp1);
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001622 __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
Vladimir Marko26ec3ca2017-03-14 13:37:14 +00001623 temp1 = scratch_scope.AcquireW();
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001624 __ B(&return_false, ne);
1625 } else {
1626 // Load `count` fields of this and argument strings.
1627 __ Ldr(temp, MemOperand(str.X(), count_offset));
1628 __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1629 // Check if `count` fields are equal, return false if they're not.
1630 // Also compares the compression style, if differs return false.
1631 __ Cmp(temp, temp1);
1632 __ B(&return_false, ne);
1633 }
Agi Csakiea34b402015-08-13 17:51:19 -07001634
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001635 // Assertions that must hold in order to compare strings 8 bytes at a time.
Vladimir Marko984519c2017-08-23 10:45:29 +01001636 // Ok to do this because strings are zero-padded to kObjectAlignment.
Agi Csakiea34b402015-08-13 17:51:19 -07001637 DCHECK_ALIGNED(value_offset, 8);
1638 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1639
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001640 if (const_string != nullptr &&
Vladimir Marko984519c2017-08-23 10:45:29 +01001641 const_string_length <= (is_compressed ? kShortConstStringEqualsCutoffInBytes
1642 : kShortConstStringEqualsCutoffInBytes / 2u)) {
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001643 // Load and compare the contents. Though we know the contents of the short const string
1644 // at compile time, materializing constants may be more code than loading from memory.
1645 int32_t offset = value_offset;
1646 size_t remaining_bytes =
1647 RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u);
1648 temp = temp.X();
1649 temp1 = temp1.X();
Vladimir Marko984519c2017-08-23 10:45:29 +01001650 while (remaining_bytes > sizeof(uint64_t)) {
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001651 Register temp2 = XRegisterFrom(locations->GetTemp(0));
1652 __ Ldp(temp, temp1, MemOperand(str.X(), offset));
1653 __ Ldp(temp2, out, MemOperand(arg.X(), offset));
1654 __ Cmp(temp, temp2);
1655 __ Ccmp(temp1, out, NoFlag, eq);
1656 __ B(&return_false, ne);
1657 offset += 2u * sizeof(uint64_t);
1658 remaining_bytes -= 2u * sizeof(uint64_t);
1659 }
1660 if (remaining_bytes != 0u) {
1661 __ Ldr(temp, MemOperand(str.X(), offset));
1662 __ Ldr(temp1, MemOperand(arg.X(), offset));
1663 __ Cmp(temp, temp1);
1664 __ B(&return_false, ne);
1665 }
1666 } else {
1667 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
1668 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1669 "Expecting 0=compressed, 1=uncompressed");
1670 __ Cbz(temp, &return_true);
1671
1672 if (mirror::kUseStringCompression) {
1673 // For string compression, calculate the number of bytes to compare (not chars).
1674 // This could in theory exceed INT32_MAX, so treat temp as unsigned.
1675 __ And(temp1, temp, Operand(1)); // Extract compression flag.
1676 __ Lsr(temp, temp, 1u); // Extract length.
1677 __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare.
1678 }
1679
1680 // Store offset of string value in preparation for comparison loop
1681 __ Mov(temp1, value_offset);
1682
1683 temp1 = temp1.X();
1684 Register temp2 = XRegisterFrom(locations->GetTemp(0));
1685 // Loop to compare strings 8 bytes at a time starting at the front of the string.
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001686 __ Bind(&loop);
1687 __ Ldr(out, MemOperand(str.X(), temp1));
1688 __ Ldr(temp2, MemOperand(arg.X(), temp1));
1689 __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
1690 __ Cmp(out, temp2);
1691 __ B(&return_false, ne);
1692 // With string compression, we have compared 8 bytes, otherwise 4 chars.
1693 __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags);
1694 __ B(&loop, hi);
jessicahandojo05765752016-09-09 19:01:32 -07001695 }
1696
Agi Csakiea34b402015-08-13 17:51:19 -07001697 // Return true and exit the function.
1698 // If loop does not result in returning false, we return true.
1699 __ Bind(&return_true);
1700 __ Mov(out, 1);
1701 __ B(&end);
1702
1703 // Return false and exit the function.
1704 __ Bind(&return_false);
1705 __ Mov(out, 0);
1706 __ Bind(&end);
1707}
1708
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001709static void GenerateVisitStringIndexOf(HInvoke* invoke,
Scott Wakeling97c72b72016-06-24 16:19:36 +01001710 MacroAssembler* masm,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001711 CodeGeneratorARM64* codegen,
1712 ArenaAllocator* allocator,
1713 bool start_at_zero) {
1714 LocationSummary* locations = invoke->GetLocations();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001715
1716 // Note that the null check must have been done earlier.
1717 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1718
1719 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001720 // 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 -07001721 SlowPathCodeARM64* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001722 HInstruction* code_point = invoke->InputAt(1);
1723 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01001724 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > 0xFFFFU) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001725 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1726 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1727 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1728 codegen->AddSlowPath(slow_path);
1729 __ B(slow_path->GetEntryLabel());
1730 __ Bind(slow_path->GetExitLabel());
1731 return;
1732 }
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001733 } else if (code_point->GetType() != Primitive::kPrimChar) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001734 Register char_reg = WRegisterFrom(locations->InAt(1));
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001735 __ Tst(char_reg, 0xFFFF0000);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001736 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1737 codegen->AddSlowPath(slow_path);
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001738 __ B(ne, slow_path->GetEntryLabel());
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001739 }
1740
1741 if (start_at_zero) {
1742 // Start-index = 0.
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001743 Register tmp_reg = WRegisterFrom(locations->GetTemp(0));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001744 __ Mov(tmp_reg, 0);
1745 }
1746
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001747 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
Roland Levillain42ad2882016-02-29 18:26:54 +00001748 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001749
1750 if (slow_path != nullptr) {
1751 __ Bind(slow_path->GetExitLabel());
1752 }
1753}
1754
1755void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) {
1756 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001757 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001758 kIntrinsified);
1759 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1760 // best to align the inputs accordingly.
1761 InvokeRuntimeCallingConvention calling_convention;
1762 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1763 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1764 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1765
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001766 // Need to send start_index=0.
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001767 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1768}
1769
1770void IntrinsicCodeGeneratorARM64::VisitStringIndexOf(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001771 GenerateVisitStringIndexOf(
1772 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001773}
1774
1775void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
1776 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001777 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001778 kIntrinsified);
1779 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1780 // best to align the inputs accordingly.
1781 InvokeRuntimeCallingConvention calling_convention;
1782 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1783 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1784 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1785 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001786}
1787
1788void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001789 GenerateVisitStringIndexOf(
1790 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001791}
1792
Jeff Hao848f70a2014-01-15 13:49:50 -08001793void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1794 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001795 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001796 kIntrinsified);
1797 InvokeRuntimeCallingConvention calling_convention;
1798 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1799 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1800 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1801 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1802 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1803}
1804
1805void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001806 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001807 LocationSummary* locations = invoke->GetLocations();
1808
1809 Register byte_array = WRegisterFrom(locations->InAt(0));
1810 __ Cmp(byte_array, 0);
1811 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1812 codegen_->AddSlowPath(slow_path);
1813 __ B(eq, slow_path->GetEntryLabel());
1814
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001815 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001816 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001817 __ Bind(slow_path->GetExitLabel());
1818}
1819
1820void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
1821 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001822 LocationSummary::kCallOnMainOnly,
Jeff Hao848f70a2014-01-15 13:49:50 -08001823 kIntrinsified);
1824 InvokeRuntimeCallingConvention calling_convention;
1825 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1826 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1827 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1828 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1829}
1830
1831void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
Roland Levillaincc3839c2016-02-29 16:23:48 +00001832 // No need to emit code checking whether `locations->InAt(2)` is a null
1833 // pointer, as callers of the native method
1834 //
1835 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1836 //
1837 // all include a null check on `data` before calling that method.
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001838 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
Roland Levillainf969a202016-03-09 16:14:00 +00001839 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001840}
1841
1842void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Jeff Hao848f70a2014-01-15 13:49:50 -08001843 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001844 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001845 kIntrinsified);
1846 InvokeRuntimeCallingConvention calling_convention;
1847 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Jeff Hao848f70a2014-01-15 13:49:50 -08001848 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1849}
1850
1851void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001852 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001853 LocationSummary* locations = invoke->GetLocations();
1854
1855 Register string_to_copy = WRegisterFrom(locations->InAt(0));
1856 __ Cmp(string_to_copy, 0);
1857 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1858 codegen_->AddSlowPath(slow_path);
1859 __ B(eq, slow_path->GetEntryLabel());
1860
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001861 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001862 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001863 __ Bind(slow_path->GetExitLabel());
1864}
1865
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001866static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1867 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
1868 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1869 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1870
1871 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001872 LocationSummary::kCallOnMainOnly,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001873 kIntrinsified);
1874 InvokeRuntimeCallingConvention calling_convention;
1875
1876 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1877 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1878}
1879
1880static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1881 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
1882 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1883 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(1)->GetType()));
1884 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1885
1886 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001887 LocationSummary::kCallOnMainOnly,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001888 kIntrinsified);
1889 InvokeRuntimeCallingConvention calling_convention;
1890
1891 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1892 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
1893 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1894}
1895
1896static void GenFPToFPCall(HInvoke* invoke,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001897 CodeGeneratorARM64* codegen,
1898 QuickEntrypointEnum entry) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001899 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001900}
1901
1902void IntrinsicLocationsBuilderARM64::VisitMathCos(HInvoke* invoke) {
1903 CreateFPToFPCallLocations(arena_, invoke);
1904}
1905
1906void IntrinsicCodeGeneratorARM64::VisitMathCos(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001907 GenFPToFPCall(invoke, codegen_, kQuickCos);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001908}
1909
1910void IntrinsicLocationsBuilderARM64::VisitMathSin(HInvoke* invoke) {
1911 CreateFPToFPCallLocations(arena_, invoke);
1912}
1913
1914void IntrinsicCodeGeneratorARM64::VisitMathSin(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001915 GenFPToFPCall(invoke, codegen_, kQuickSin);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001916}
1917
1918void IntrinsicLocationsBuilderARM64::VisitMathAcos(HInvoke* invoke) {
1919 CreateFPToFPCallLocations(arena_, invoke);
1920}
1921
1922void IntrinsicCodeGeneratorARM64::VisitMathAcos(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001923 GenFPToFPCall(invoke, codegen_, kQuickAcos);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001924}
1925
1926void IntrinsicLocationsBuilderARM64::VisitMathAsin(HInvoke* invoke) {
1927 CreateFPToFPCallLocations(arena_, invoke);
1928}
1929
1930void IntrinsicCodeGeneratorARM64::VisitMathAsin(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001931 GenFPToFPCall(invoke, codegen_, kQuickAsin);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001932}
1933
1934void IntrinsicLocationsBuilderARM64::VisitMathAtan(HInvoke* invoke) {
1935 CreateFPToFPCallLocations(arena_, invoke);
1936}
1937
1938void IntrinsicCodeGeneratorARM64::VisitMathAtan(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001939 GenFPToFPCall(invoke, codegen_, kQuickAtan);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001940}
1941
1942void IntrinsicLocationsBuilderARM64::VisitMathCbrt(HInvoke* invoke) {
1943 CreateFPToFPCallLocations(arena_, invoke);
1944}
1945
1946void IntrinsicCodeGeneratorARM64::VisitMathCbrt(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001947 GenFPToFPCall(invoke, codegen_, kQuickCbrt);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001948}
1949
1950void IntrinsicLocationsBuilderARM64::VisitMathCosh(HInvoke* invoke) {
1951 CreateFPToFPCallLocations(arena_, invoke);
1952}
1953
1954void IntrinsicCodeGeneratorARM64::VisitMathCosh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001955 GenFPToFPCall(invoke, codegen_, kQuickCosh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001956}
1957
1958void IntrinsicLocationsBuilderARM64::VisitMathExp(HInvoke* invoke) {
1959 CreateFPToFPCallLocations(arena_, invoke);
1960}
1961
1962void IntrinsicCodeGeneratorARM64::VisitMathExp(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001963 GenFPToFPCall(invoke, codegen_, kQuickExp);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001964}
1965
1966void IntrinsicLocationsBuilderARM64::VisitMathExpm1(HInvoke* invoke) {
1967 CreateFPToFPCallLocations(arena_, invoke);
1968}
1969
1970void IntrinsicCodeGeneratorARM64::VisitMathExpm1(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001971 GenFPToFPCall(invoke, codegen_, kQuickExpm1);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001972}
1973
1974void IntrinsicLocationsBuilderARM64::VisitMathLog(HInvoke* invoke) {
1975 CreateFPToFPCallLocations(arena_, invoke);
1976}
1977
1978void IntrinsicCodeGeneratorARM64::VisitMathLog(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001979 GenFPToFPCall(invoke, codegen_, kQuickLog);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001980}
1981
1982void IntrinsicLocationsBuilderARM64::VisitMathLog10(HInvoke* invoke) {
1983 CreateFPToFPCallLocations(arena_, invoke);
1984}
1985
1986void IntrinsicCodeGeneratorARM64::VisitMathLog10(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001987 GenFPToFPCall(invoke, codegen_, kQuickLog10);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001988}
1989
1990void IntrinsicLocationsBuilderARM64::VisitMathSinh(HInvoke* invoke) {
1991 CreateFPToFPCallLocations(arena_, invoke);
1992}
1993
1994void IntrinsicCodeGeneratorARM64::VisitMathSinh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001995 GenFPToFPCall(invoke, codegen_, kQuickSinh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001996}
1997
1998void IntrinsicLocationsBuilderARM64::VisitMathTan(HInvoke* invoke) {
1999 CreateFPToFPCallLocations(arena_, invoke);
2000}
2001
2002void IntrinsicCodeGeneratorARM64::VisitMathTan(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00002003 GenFPToFPCall(invoke, codegen_, kQuickTan);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00002004}
2005
2006void IntrinsicLocationsBuilderARM64::VisitMathTanh(HInvoke* invoke) {
2007 CreateFPToFPCallLocations(arena_, invoke);
2008}
2009
2010void IntrinsicCodeGeneratorARM64::VisitMathTanh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00002011 GenFPToFPCall(invoke, codegen_, kQuickTanh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00002012}
2013
2014void IntrinsicLocationsBuilderARM64::VisitMathAtan2(HInvoke* invoke) {
2015 CreateFPFPToFPCallLocations(arena_, invoke);
2016}
2017
2018void IntrinsicCodeGeneratorARM64::VisitMathAtan2(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00002019 GenFPToFPCall(invoke, codegen_, kQuickAtan2);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00002020}
2021
2022void IntrinsicLocationsBuilderARM64::VisitMathHypot(HInvoke* invoke) {
2023 CreateFPFPToFPCallLocations(arena_, invoke);
2024}
2025
2026void IntrinsicCodeGeneratorARM64::VisitMathHypot(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00002027 GenFPToFPCall(invoke, codegen_, kQuickHypot);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00002028}
2029
2030void IntrinsicLocationsBuilderARM64::VisitMathNextAfter(HInvoke* invoke) {
2031 CreateFPFPToFPCallLocations(arena_, invoke);
2032}
2033
2034void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00002035 GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00002036}
2037
Tim Zhang25abd6c2016-01-19 23:39:24 +08002038void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2039 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2040 LocationSummary::kNoCall,
2041 kIntrinsified);
2042 locations->SetInAt(0, Location::RequiresRegister());
2043 locations->SetInAt(1, Location::RequiresRegister());
2044 locations->SetInAt(2, Location::RequiresRegister());
2045 locations->SetInAt(3, Location::RequiresRegister());
2046 locations->SetInAt(4, Location::RequiresRegister());
2047
2048 locations->AddTemp(Location::RequiresRegister());
2049 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingdf109d92016-04-22 11:35:56 +01002050 locations->AddTemp(Location::RequiresRegister());
Tim Zhang25abd6c2016-01-19 23:39:24 +08002051}
2052
2053void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01002054 MacroAssembler* masm = GetVIXLAssembler();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002055 LocationSummary* locations = invoke->GetLocations();
2056
2057 // Check assumption that sizeof(Char) is 2 (used in scaling below).
2058 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2059 DCHECK_EQ(char_size, 2u);
2060
2061 // Location of data in char array buffer.
2062 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2063
2064 // Location of char array data in string.
2065 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2066
2067 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2068 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2069 Register srcObj = XRegisterFrom(locations->InAt(0));
2070 Register srcBegin = XRegisterFrom(locations->InAt(1));
2071 Register srcEnd = XRegisterFrom(locations->InAt(2));
2072 Register dstObj = XRegisterFrom(locations->InAt(3));
2073 Register dstBegin = XRegisterFrom(locations->InAt(4));
2074
2075 Register src_ptr = XRegisterFrom(locations->GetTemp(0));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002076 Register num_chr = XRegisterFrom(locations->GetTemp(1));
2077 Register tmp1 = XRegisterFrom(locations->GetTemp(2));
Tim Zhang25abd6c2016-01-19 23:39:24 +08002078
2079 UseScratchRegisterScope temps(masm);
2080 Register dst_ptr = temps.AcquireX();
Scott Wakelingdf109d92016-04-22 11:35:56 +01002081 Register tmp2 = temps.AcquireX();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002082
jessicahandojo05765752016-09-09 19:01:32 -07002083 vixl::aarch64::Label done;
2084 vixl::aarch64::Label compressed_string_loop;
2085 __ Sub(num_chr, srcEnd, srcBegin);
2086 // Early out for valid zero-length retrievals.
2087 __ Cbz(num_chr, &done);
Tim Zhang25abd6c2016-01-19 23:39:24 +08002088
Scott Wakelingdf109d92016-04-22 11:35:56 +01002089 // dst address start to copy to.
Tim Zhang25abd6c2016-01-19 23:39:24 +08002090 __ Add(dst_ptr, dstObj, Operand(data_offset));
2091 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
2092
jessicahandojo05765752016-09-09 19:01:32 -07002093 // src address to copy from.
2094 __ Add(src_ptr, srcObj, Operand(value_offset));
2095 vixl::aarch64::Label compressed_string_preloop;
2096 if (mirror::kUseStringCompression) {
2097 // Location of count in string.
2098 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2099 // String's length.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002100 __ Ldr(tmp2, MemOperand(srcObj, count_offset));
2101 __ Tbz(tmp2, 0, &compressed_string_preloop);
jessicahandojo05765752016-09-09 19:01:32 -07002102 }
2103 __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002104
Tim Zhang25abd6c2016-01-19 23:39:24 +08002105 // Do the copy.
Scott Wakeling97c72b72016-06-24 16:19:36 +01002106 vixl::aarch64::Label loop;
Scott Wakeling97c72b72016-06-24 16:19:36 +01002107 vixl::aarch64::Label remainder;
Scott Wakelingdf109d92016-04-22 11:35:56 +01002108
Scott Wakelingdf109d92016-04-22 11:35:56 +01002109 // Save repairing the value of num_chr on the < 8 character path.
2110 __ Subs(tmp1, num_chr, 8);
2111 __ B(lt, &remainder);
2112
2113 // Keep the result of the earlier subs, we are going to fetch at least 8 characters.
2114 __ Mov(num_chr, tmp1);
2115
2116 // Main loop used for longer fetches loads and stores 8x16-bit characters at a time.
2117 // (Unaligned addresses are acceptable here and not worth inlining extra code to rectify.)
Tim Zhang25abd6c2016-01-19 23:39:24 +08002118 __ Bind(&loop);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002119 __ Ldp(tmp1, tmp2, MemOperand(src_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002120 __ Subs(num_chr, num_chr, 8);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002121 __ Stp(tmp1, tmp2, MemOperand(dst_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002122 __ B(ge, &loop);
2123
2124 __ Adds(num_chr, num_chr, 8);
2125 __ B(eq, &done);
2126
2127 // Main loop for < 8 character case and remainder handling. Loads and stores one
2128 // 16-bit Java character at a time.
2129 __ Bind(&remainder);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002130 __ Ldrh(tmp1, MemOperand(src_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002131 __ Subs(num_chr, num_chr, 1);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002132 __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002133 __ B(gt, &remainder);
jessicahandojo05765752016-09-09 19:01:32 -07002134 __ B(&done);
2135
2136 if (mirror::kUseStringCompression) {
2137 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
2138 DCHECK_EQ(c_char_size, 1u);
2139 __ Bind(&compressed_string_preloop);
2140 __ Add(src_ptr, src_ptr, Operand(srcBegin));
2141 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2142 __ Bind(&compressed_string_loop);
2143 __ Ldrb(tmp1, MemOperand(src_ptr, c_char_size, PostIndex));
2144 __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
2145 __ Subs(num_chr, num_chr, Operand(1));
2146 __ B(gt, &compressed_string_loop);
2147 }
Scott Wakelingdf109d92016-04-22 11:35:56 +01002148
Tim Zhang25abd6c2016-01-19 23:39:24 +08002149 __ Bind(&done);
2150}
2151
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002152// Mirrors ARRAYCOPY_SHORT_CHAR_ARRAY_THRESHOLD in libcore, so we can choose to use the native
2153// implementation there for longer copy lengths.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002154static constexpr int32_t kSystemArrayCopyCharThreshold = 32;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002155
2156static void SetSystemArrayCopyLocationRequires(LocationSummary* locations,
2157 uint32_t at,
2158 HInstruction* input) {
2159 HIntConstant* const_input = input->AsIntConstant();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002160 if (const_input != nullptr && !vixl::aarch64::Assembler::IsImmAddSub(const_input->GetValue())) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002161 locations->SetInAt(at, Location::RequiresRegister());
2162 } else {
2163 locations->SetInAt(at, Location::RegisterOrConstant(input));
2164 }
2165}
2166
2167void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
2168 // Check to see if we have known failures that will cause us to have to bail out
2169 // to the runtime, and just generate the runtime call directly.
2170 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2171 HIntConstant* dst_pos = invoke->InputAt(3)->AsIntConstant();
2172
2173 // The positions must be non-negative.
2174 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2175 (dst_pos != nullptr && dst_pos->GetValue() < 0)) {
2176 // We will have to fail anyways.
2177 return;
2178 }
2179
2180 // The length must be >= 0 and not so long that we would (currently) prefer libcore's
2181 // native implementation.
2182 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2183 if (length != nullptr) {
2184 int32_t len = length->GetValue();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002185 if (len < 0 || len > kSystemArrayCopyCharThreshold) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002186 // Just call as normal.
2187 return;
2188 }
2189 }
2190
2191 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2192 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2193 LocationSummary::kCallOnSlowPath,
2194 kIntrinsified);
2195 // arraycopy(char[] src, int src_pos, char[] dst, int dst_pos, int length).
2196 locations->SetInAt(0, Location::RequiresRegister());
2197 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2198 locations->SetInAt(2, Location::RequiresRegister());
2199 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2200 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2201
2202 locations->AddTemp(Location::RequiresRegister());
2203 locations->AddTemp(Location::RequiresRegister());
2204 locations->AddTemp(Location::RequiresRegister());
2205}
2206
Scott Wakeling97c72b72016-06-24 16:19:36 +01002207static void CheckSystemArrayCopyPosition(MacroAssembler* masm,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002208 const Location& pos,
2209 const Register& input,
2210 const Location& length,
2211 SlowPathCodeARM64* slow_path,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002212 const Register& temp,
2213 bool length_is_input_length = false) {
2214 const int32_t length_offset = mirror::Array::LengthOffset().Int32Value();
2215 if (pos.IsConstant()) {
2216 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
2217 if (pos_const == 0) {
2218 if (!length_is_input_length) {
2219 // Check that length(input) >= length.
2220 __ Ldr(temp, MemOperand(input, length_offset));
2221 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
2222 __ B(slow_path->GetEntryLabel(), lt);
2223 }
2224 } else {
2225 // Check that length(input) >= pos.
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002226 __ Ldr(temp, MemOperand(input, length_offset));
2227 __ Subs(temp, temp, pos_const);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002228 __ B(slow_path->GetEntryLabel(), lt);
2229
2230 // Check that (length(input) - pos) >= length.
2231 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
2232 __ B(slow_path->GetEntryLabel(), lt);
2233 }
2234 } else if (length_is_input_length) {
2235 // The only way the copy can succeed is if pos is zero.
2236 __ Cbnz(WRegisterFrom(pos), slow_path->GetEntryLabel());
2237 } else {
2238 // Check that pos >= 0.
2239 Register pos_reg = WRegisterFrom(pos);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002240 __ Tbnz(pos_reg, pos_reg.GetSizeInBits() - 1, slow_path->GetEntryLabel());
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002241
2242 // Check that pos <= length(input) && (length(input) - pos) >= length.
2243 __ Ldr(temp, MemOperand(input, length_offset));
2244 __ Subs(temp, temp, pos_reg);
2245 // Ccmp if length(input) >= pos, else definitely bail to slow path (N!=V == lt).
2246 __ Ccmp(temp, OperandFrom(length, Primitive::kPrimInt), NFlag, ge);
2247 __ B(slow_path->GetEntryLabel(), lt);
2248 }
2249}
2250
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002251// Compute base source address, base destination address, and end
2252// source address for System.arraycopy* intrinsics in `src_base`,
2253// `dst_base` and `src_end` respectively.
Scott Wakeling97c72b72016-06-24 16:19:36 +01002254static void GenSystemArrayCopyAddresses(MacroAssembler* masm,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002255 Primitive::Type type,
2256 const Register& src,
2257 const Location& src_pos,
2258 const Register& dst,
2259 const Location& dst_pos,
2260 const Location& copy_length,
2261 const Register& src_base,
2262 const Register& dst_base,
2263 const Register& src_end) {
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002264 // This routine is used by the SystemArrayCopy and the SystemArrayCopyChar intrinsics.
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002265 DCHECK(type == Primitive::kPrimNot || type == Primitive::kPrimChar)
Roland Levillainebea3d22016-04-12 15:42:57 +01002266 << "Unexpected element type: " << type;
2267 const int32_t element_size = Primitive::ComponentSize(type);
2268 const int32_t element_size_shift = Primitive::ComponentSizeShift(type);
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002269 const uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002270
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002271 if (src_pos.IsConstant()) {
2272 int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002273 __ Add(src_base, src, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002274 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002275 __ Add(src_base, src, data_offset);
2276 __ Add(src_base, src_base, Operand(XRegisterFrom(src_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002277 }
2278
2279 if (dst_pos.IsConstant()) {
2280 int32_t constant = dst_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002281 __ Add(dst_base, dst, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002282 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002283 __ Add(dst_base, dst, data_offset);
2284 __ Add(dst_base, dst_base, Operand(XRegisterFrom(dst_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002285 }
2286
2287 if (copy_length.IsConstant()) {
2288 int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002289 __ Add(src_end, src_base, element_size * constant);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002290 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002291 __ Add(src_end, src_base, Operand(XRegisterFrom(copy_length), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002292 }
2293}
2294
2295void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01002296 MacroAssembler* masm = GetVIXLAssembler();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002297 LocationSummary* locations = invoke->GetLocations();
2298 Register src = XRegisterFrom(locations->InAt(0));
2299 Location src_pos = locations->InAt(1);
2300 Register dst = XRegisterFrom(locations->InAt(2));
2301 Location dst_pos = locations->InAt(3);
2302 Location length = locations->InAt(4);
2303
2304 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2305 codegen_->AddSlowPath(slow_path);
2306
2307 // If source and destination are the same, take the slow path. Overlapping copy regions must be
2308 // copied in reverse and we can't know in all cases if it's needed.
2309 __ Cmp(src, dst);
2310 __ B(slow_path->GetEntryLabel(), eq);
2311
2312 // Bail out if the source is null.
2313 __ Cbz(src, slow_path->GetEntryLabel());
2314
2315 // Bail out if the destination is null.
2316 __ Cbz(dst, slow_path->GetEntryLabel());
2317
2318 if (!length.IsConstant()) {
Vladimir Markoc5646202016-11-28 16:03:15 +00002319 // Merge the following two comparisons into one:
2320 // If the length is negative, bail out (delegate to libcore's native implementation).
2321 // If the length > 32 then (currently) prefer libcore's native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002322 __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold);
Vladimir Markoc5646202016-11-28 16:03:15 +00002323 __ B(slow_path->GetEntryLabel(), hi);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002324 } else {
2325 // We have already checked in the LocationsBuilder for the constant case.
2326 DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0);
2327 DCHECK_LE(length.GetConstant()->AsIntConstant()->GetValue(), 32);
2328 }
2329
2330 Register src_curr_addr = WRegisterFrom(locations->GetTemp(0));
2331 Register dst_curr_addr = WRegisterFrom(locations->GetTemp(1));
2332 Register src_stop_addr = WRegisterFrom(locations->GetTemp(2));
2333
2334 CheckSystemArrayCopyPosition(masm,
2335 src_pos,
2336 src,
2337 length,
2338 slow_path,
2339 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002340 false);
2341
2342 CheckSystemArrayCopyPosition(masm,
2343 dst_pos,
2344 dst,
2345 length,
2346 slow_path,
2347 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002348 false);
2349
2350 src_curr_addr = src_curr_addr.X();
2351 dst_curr_addr = dst_curr_addr.X();
2352 src_stop_addr = src_stop_addr.X();
2353
2354 GenSystemArrayCopyAddresses(masm,
2355 Primitive::kPrimChar,
2356 src,
2357 src_pos,
2358 dst,
2359 dst_pos,
2360 length,
2361 src_curr_addr,
2362 dst_curr_addr,
2363 src_stop_addr);
2364
2365 // Iterate over the arrays and do a raw copy of the chars.
2366 const int32_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2367 UseScratchRegisterScope temps(masm);
2368 Register tmp = temps.AcquireW();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002369 vixl::aarch64::Label loop, done;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002370 __ Bind(&loop);
2371 __ Cmp(src_curr_addr, src_stop_addr);
2372 __ B(&done, eq);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002373 __ Ldrh(tmp, MemOperand(src_curr_addr, char_size, PostIndex));
2374 __ Strh(tmp, MemOperand(dst_curr_addr, char_size, PostIndex));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002375 __ B(&loop);
2376 __ Bind(&done);
2377
2378 __ Bind(slow_path->GetExitLabel());
2379}
2380
donghui.baic2ec9ad2016-03-10 14:02:55 +08002381// We can choose to use the native implementation there for longer copy lengths.
2382static constexpr int32_t kSystemArrayCopyThreshold = 128;
2383
2384// CodeGenerator::CreateSystemArrayCopyLocationSummary use three temporary registers.
2385// We want to use two temporary registers in order to reduce the register pressure in arm64.
2386// So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary.
2387void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002388 // The only read barrier implementation supporting the
2389 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2390 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain3d312422016-06-23 13:53:42 +01002391 return;
2392 }
2393
donghui.baic2ec9ad2016-03-10 14:02:55 +08002394 // Check to see if we have known failures that will cause us to have to bail out
2395 // to the runtime, and just generate the runtime call directly.
2396 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2397 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
2398
2399 // The positions must be non-negative.
2400 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2401 (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
2402 // We will have to fail anyways.
2403 return;
2404 }
2405
2406 // The length must be >= 0.
2407 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2408 if (length != nullptr) {
2409 int32_t len = length->GetValue();
2410 if (len < 0 || len >= kSystemArrayCopyThreshold) {
2411 // Just call as normal.
2412 return;
2413 }
2414 }
2415
2416 SystemArrayCopyOptimizations optimizations(invoke);
2417
2418 if (optimizations.GetDestinationIsSource()) {
2419 if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) {
2420 // We only support backward copying if source and destination are the same.
2421 return;
2422 }
2423 }
2424
2425 if (optimizations.GetDestinationIsPrimitiveArray() || optimizations.GetSourceIsPrimitiveArray()) {
2426 // We currently don't intrinsify primitive copying.
2427 return;
2428 }
2429
2430 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2431 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2432 LocationSummary::kCallOnSlowPath,
2433 kIntrinsified);
2434 // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length).
2435 locations->SetInAt(0, Location::RequiresRegister());
2436 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2437 locations->SetInAt(2, Location::RequiresRegister());
2438 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2439 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2440
2441 locations->AddTemp(Location::RequiresRegister());
2442 locations->AddTemp(Location::RequiresRegister());
Roland Levillain0b671c02016-08-19 12:02:34 +01002443 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2444 // Temporary register IP0, obtained from the VIXL scratch register
2445 // pool, cannot be used in ReadBarrierSystemArrayCopySlowPathARM64
2446 // (because that register is clobbered by ReadBarrierMarkRegX
Roland Levillain54f869e2017-03-06 13:54:11 +00002447 // entry points). It cannot be used in calls to
2448 // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier
2449 // either. For these reasons, get a third extra temporary register
2450 // from the register allocator.
Roland Levillain0b671c02016-08-19 12:02:34 +01002451 locations->AddTemp(Location::RequiresRegister());
Roland Levillain54f869e2017-03-06 13:54:11 +00002452 } else {
2453 // Cases other than Baker read barriers: the third temporary will
2454 // be acquired from the VIXL scratch register pool.
Roland Levillain0b671c02016-08-19 12:02:34 +01002455 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002456}
2457
2458void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002459 // The only read barrier implementation supporting the
2460 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2461 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01002462
Scott Wakeling97c72b72016-06-24 16:19:36 +01002463 MacroAssembler* masm = GetVIXLAssembler();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002464 LocationSummary* locations = invoke->GetLocations();
2465
2466 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2467 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2468 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2469 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Roland Levillain0b671c02016-08-19 12:02:34 +01002470 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002471
2472 Register src = XRegisterFrom(locations->InAt(0));
2473 Location src_pos = locations->InAt(1);
2474 Register dest = XRegisterFrom(locations->InAt(2));
2475 Location dest_pos = locations->InAt(3);
2476 Location length = locations->InAt(4);
2477 Register temp1 = WRegisterFrom(locations->GetTemp(0));
Roland Levillain0b671c02016-08-19 12:02:34 +01002478 Location temp1_loc = LocationFrom(temp1);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002479 Register temp2 = WRegisterFrom(locations->GetTemp(1));
Roland Levillain0b671c02016-08-19 12:02:34 +01002480 Location temp2_loc = LocationFrom(temp2);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002481
Roland Levillain0b671c02016-08-19 12:02:34 +01002482 SlowPathCodeARM64* intrinsic_slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2483 codegen_->AddSlowPath(intrinsic_slow_path);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002484
Scott Wakeling97c72b72016-06-24 16:19:36 +01002485 vixl::aarch64::Label conditions_on_positions_validated;
donghui.baic2ec9ad2016-03-10 14:02:55 +08002486 SystemArrayCopyOptimizations optimizations(invoke);
2487
donghui.baic2ec9ad2016-03-10 14:02:55 +08002488 // If source and destination are the same, we go to slow path if we need to do
2489 // forward copying.
2490 if (src_pos.IsConstant()) {
2491 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
2492 if (dest_pos.IsConstant()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002493 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
2494 if (optimizations.GetDestinationIsSource()) {
2495 // Checked when building locations.
2496 DCHECK_GE(src_pos_constant, dest_pos_constant);
2497 } else if (src_pos_constant < dest_pos_constant) {
2498 __ Cmp(src, dest);
Roland Levillain0b671c02016-08-19 12:02:34 +01002499 __ B(intrinsic_slow_path->GetEntryLabel(), eq);
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002500 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002501 // Checked when building locations.
2502 DCHECK(!optimizations.GetDestinationIsSource()
2503 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
2504 } else {
2505 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002506 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002507 __ B(&conditions_on_positions_validated, ne);
2508 }
2509 __ Cmp(WRegisterFrom(dest_pos), src_pos_constant);
Roland Levillain0b671c02016-08-19 12:02:34 +01002510 __ B(intrinsic_slow_path->GetEntryLabel(), gt);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002511 }
2512 } else {
2513 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002514 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002515 __ B(&conditions_on_positions_validated, ne);
2516 }
2517 __ Cmp(RegisterFrom(src_pos, invoke->InputAt(1)->GetType()),
2518 OperandFrom(dest_pos, invoke->InputAt(3)->GetType()));
Roland Levillain0b671c02016-08-19 12:02:34 +01002519 __ B(intrinsic_slow_path->GetEntryLabel(), lt);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002520 }
2521
2522 __ Bind(&conditions_on_positions_validated);
2523
2524 if (!optimizations.GetSourceIsNotNull()) {
2525 // Bail out if the source is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01002526 __ Cbz(src, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002527 }
2528
2529 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
2530 // Bail out if the destination is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01002531 __ Cbz(dest, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002532 }
2533
2534 // We have already checked in the LocationsBuilder for the constant case.
2535 if (!length.IsConstant() &&
2536 !optimizations.GetCountIsSourceLength() &&
2537 !optimizations.GetCountIsDestinationLength()) {
Vladimir Markoc5646202016-11-28 16:03:15 +00002538 // Merge the following two comparisons into one:
2539 // If the length is negative, bail out (delegate to libcore's native implementation).
2540 // If the length >= 128 then (currently) prefer native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002541 __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold);
Vladimir Markoc5646202016-11-28 16:03:15 +00002542 __ B(intrinsic_slow_path->GetEntryLabel(), hs);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002543 }
2544 // Validity checks: source.
2545 CheckSystemArrayCopyPosition(masm,
2546 src_pos,
2547 src,
2548 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002549 intrinsic_slow_path,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002550 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002551 optimizations.GetCountIsSourceLength());
2552
2553 // Validity checks: dest.
2554 CheckSystemArrayCopyPosition(masm,
2555 dest_pos,
2556 dest,
2557 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002558 intrinsic_slow_path,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002559 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002560 optimizations.GetCountIsDestinationLength());
2561 {
2562 // We use a block to end the scratch scope before the write barrier, thus
2563 // freeing the temporary registers so they can be used in `MarkGCCard`.
2564 UseScratchRegisterScope temps(masm);
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002565 Location temp3_loc; // Used only for Baker read barrier.
Roland Levillain54f869e2017-03-06 13:54:11 +00002566 Register temp3;
2567 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002568 temp3_loc = locations->GetTemp(2);
2569 temp3 = WRegisterFrom(temp3_loc);
Roland Levillain54f869e2017-03-06 13:54:11 +00002570 } else {
2571 temp3 = temps.AcquireW();
2572 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002573
donghui.baic2ec9ad2016-03-10 14:02:55 +08002574 if (!optimizations.GetDoesNotNeedTypeCheck()) {
2575 // Check whether all elements of the source array are assignable to the component
2576 // type of the destination array. We do two checks: the classes are the same,
2577 // or the destination is Object[]. If none of these checks succeed, we go to the
2578 // slow path.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002579
Roland Levillain0b671c02016-08-19 12:02:34 +01002580 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2581 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2582 // /* HeapReference<Class> */ temp1 = src->klass_
2583 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2584 temp1_loc,
2585 src.W(),
2586 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002587 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002588 /* needs_null_check */ false,
2589 /* use_load_acquire */ false);
2590 // Bail out if the source is not a non primitive array.
2591 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2592 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2593 temp1_loc,
2594 temp1,
2595 component_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002596 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002597 /* needs_null_check */ false,
2598 /* use_load_acquire */ false);
2599 __ Cbz(temp1, intrinsic_slow_path->GetEntryLabel());
2600 // If heap poisoning is enabled, `temp1` has been unpoisoned
2601 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2602 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
2603 __ Ldrh(temp1, HeapOperand(temp1, primitive_offset));
2604 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2605 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002606 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002607
2608 // /* HeapReference<Class> */ temp1 = dest->klass_
2609 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2610 temp1_loc,
2611 dest.W(),
2612 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002613 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002614 /* needs_null_check */ false,
2615 /* use_load_acquire */ false);
2616
2617 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2618 // Bail out if the destination is not a non primitive array.
2619 //
2620 // Register `temp1` is not trashed by the read barrier emitted
2621 // by GenerateFieldLoadWithBakerReadBarrier below, as that
2622 // method produces a call to a ReadBarrierMarkRegX entry point,
2623 // which saves all potentially live registers, including
2624 // temporaries such a `temp1`.
2625 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2626 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2627 temp2_loc,
2628 temp1,
2629 component_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002630 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002631 /* needs_null_check */ false,
2632 /* use_load_acquire */ false);
2633 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2634 // If heap poisoning is enabled, `temp2` has been unpoisoned
2635 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2636 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2637 __ Ldrh(temp2, HeapOperand(temp2, primitive_offset));
2638 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2639 __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
2640 }
2641
2642 // For the same reason given earlier, `temp1` is not trashed by the
2643 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
2644 // /* HeapReference<Class> */ temp2 = src->klass_
2645 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2646 temp2_loc,
2647 src.W(),
2648 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002649 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002650 /* needs_null_check */ false,
2651 /* use_load_acquire */ false);
2652 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
2653 __ Cmp(temp1, temp2);
2654
2655 if (optimizations.GetDestinationIsTypedObjectArray()) {
2656 vixl::aarch64::Label do_copy;
2657 __ B(&do_copy, eq);
2658 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2659 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2660 temp1_loc,
2661 temp1,
2662 component_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002663 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002664 /* needs_null_check */ false,
2665 /* use_load_acquire */ false);
2666 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2667 // We do not need to emit a read barrier for the following
2668 // heap reference load, as `temp1` is only used in a
2669 // comparison with null below, and this reference is not
2670 // kept afterwards.
2671 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2672 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
2673 __ Bind(&do_copy);
2674 } else {
2675 __ B(intrinsic_slow_path->GetEntryLabel(), ne);
2676 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002677 } else {
Roland Levillain0b671c02016-08-19 12:02:34 +01002678 // Non read barrier code.
2679
2680 // /* HeapReference<Class> */ temp1 = dest->klass_
2681 __ Ldr(temp1, MemOperand(dest, class_offset));
2682 // /* HeapReference<Class> */ temp2 = src->klass_
2683 __ Ldr(temp2, MemOperand(src, class_offset));
2684 bool did_unpoison = false;
2685 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2686 !optimizations.GetSourceIsNonPrimitiveArray()) {
2687 // One or two of the references need to be unpoisoned. Unpoison them
2688 // both to make the identity check valid.
2689 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2690 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2691 did_unpoison = true;
2692 }
2693
2694 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2695 // Bail out if the destination is not a non primitive array.
2696 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2697 __ Ldr(temp3, HeapOperand(temp1, component_offset));
2698 __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
2699 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2700 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2701 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2702 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2703 __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
2704 }
2705
2706 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2707 // Bail out if the source is not a non primitive array.
2708 // /* HeapReference<Class> */ temp3 = temp2->component_type_
2709 __ Ldr(temp3, HeapOperand(temp2, component_offset));
2710 __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
2711 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2712 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2713 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2714 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2715 __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
2716 }
2717
2718 __ Cmp(temp1, temp2);
2719
2720 if (optimizations.GetDestinationIsTypedObjectArray()) {
2721 vixl::aarch64::Label do_copy;
2722 __ B(&do_copy, eq);
2723 if (!did_unpoison) {
2724 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2725 }
2726 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2727 __ Ldr(temp1, HeapOperand(temp1, component_offset));
2728 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2729 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2730 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2731 // No need to unpoison the result, we're comparing against null.
2732 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
2733 __ Bind(&do_copy);
2734 } else {
2735 __ B(intrinsic_slow_path->GetEntryLabel(), ne);
2736 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002737 }
2738 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2739 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2740 // Bail out if the source is not a non primitive array.
Roland Levillain0b671c02016-08-19 12:02:34 +01002741 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2742 // /* HeapReference<Class> */ temp1 = src->klass_
2743 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2744 temp1_loc,
2745 src.W(),
2746 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002747 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002748 /* needs_null_check */ false,
2749 /* use_load_acquire */ false);
2750 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2751 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2752 temp2_loc,
2753 temp1,
2754 component_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002755 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002756 /* needs_null_check */ false,
2757 /* use_load_acquire */ false);
2758 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2759 // If heap poisoning is enabled, `temp2` has been unpoisoned
2760 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2761 } else {
2762 // /* HeapReference<Class> */ temp1 = src->klass_
2763 __ Ldr(temp1, HeapOperand(src.W(), class_offset));
2764 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2765 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2766 __ Ldr(temp2, HeapOperand(temp1, component_offset));
2767 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2768 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2769 }
2770 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2771 __ Ldrh(temp2, HeapOperand(temp2, primitive_offset));
donghui.baic2ec9ad2016-03-10 14:02:55 +08002772 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Roland Levillain0b671c02016-08-19 12:02:34 +01002773 __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002774 }
2775
Roland Levillain1663d162017-03-17 15:15:21 +00002776 if (length.IsConstant() && length.GetConstant()->AsIntConstant()->GetValue() == 0) {
2777 // Null constant length: not need to emit the loop code at all.
Roland Levillain0b671c02016-08-19 12:02:34 +01002778 } else {
Roland Levillain1663d162017-03-17 15:15:21 +00002779 Register src_curr_addr = temp1.X();
2780 Register dst_curr_addr = temp2.X();
2781 Register src_stop_addr = temp3.X();
2782 vixl::aarch64::Label done;
2783 const Primitive::Type type = Primitive::kPrimNot;
2784 const int32_t element_size = Primitive::ComponentSize(type);
2785
2786 if (length.IsRegister()) {
2787 // Don't enter the copy loop if the length is null.
2788 __ Cbz(WRegisterFrom(length), &done);
2789 }
2790
2791 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2792 // TODO: Also convert this intrinsic to the IsGcMarking strategy?
2793
2794 // SystemArrayCopy implementation for Baker read barriers (see
Roland Levillain9983e302017-07-14 14:34:22 +01002795 // also CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier):
Roland Levillain1663d162017-03-17 15:15:21 +00002796 //
2797 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
2798 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
2799 // bool is_gray = (rb_state == ReadBarrier::GrayState());
2800 // if (is_gray) {
2801 // // Slow-path copy.
2802 // do {
2803 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
2804 // } while (src_ptr != end_ptr)
2805 // } else {
2806 // // Fast-path copy.
2807 // do {
2808 // *dest_ptr++ = *src_ptr++;
2809 // } while (src_ptr != end_ptr)
2810 // }
2811
2812 // Make sure `tmp` is not IP0, as it is clobbered by
2813 // ReadBarrierMarkRegX entry points in
2814 // ReadBarrierSystemArrayCopySlowPathARM64.
Roland Levillain1ca955d2017-04-13 19:34:30 +01002815 DCHECK(temps.IsAvailable(ip0));
Roland Levillain1663d162017-03-17 15:15:21 +00002816 temps.Exclude(ip0);
Roland Levillain0b671c02016-08-19 12:02:34 +01002817 Register tmp = temps.AcquireW();
Roland Levillain1663d162017-03-17 15:15:21 +00002818 DCHECK_NE(LocationFrom(tmp).reg(), IP0);
Roland Levillain1ca955d2017-04-13 19:34:30 +01002819 // Put IP0 back in the pool so that VIXL has at least one
2820 // scratch register available to emit macro-instructions (note
2821 // that IP1 is already used for `tmp`). Indeed some
2822 // macro-instructions used in GenSystemArrayCopyAddresses
2823 // (invoked hereunder) may require a scratch register (for
2824 // instance to emit a load with a large constant offset).
2825 temps.Include(ip0);
Roland Levillain1663d162017-03-17 15:15:21 +00002826
2827 // /* int32_t */ monitor = src->monitor_
2828 __ Ldr(tmp, HeapOperand(src.W(), monitor_offset));
2829 // /* LockWord */ lock_word = LockWord(monitor)
2830 static_assert(sizeof(LockWord) == sizeof(int32_t),
2831 "art::LockWord and int32_t have different sizes.");
2832
2833 // Introduce a dependency on the lock_word including rb_state,
2834 // to prevent load-load reordering, and without using
2835 // a memory barrier (which would be more expensive).
2836 // `src` is unchanged by this operation, but its value now depends
2837 // on `tmp`.
2838 __ Add(src.X(), src.X(), Operand(tmp.X(), LSR, 32));
2839
2840 // Compute base source address, base destination address, and end
2841 // source address for System.arraycopy* intrinsics in `src_base`,
2842 // `dst_base` and `src_end` respectively.
2843 // Note that `src_curr_addr` is computed from from `src` (and
2844 // `src_pos`) here, and thus honors the artificial dependency
2845 // of `src` on `tmp`.
2846 GenSystemArrayCopyAddresses(masm,
2847 type,
2848 src,
2849 src_pos,
2850 dest,
2851 dest_pos,
2852 length,
2853 src_curr_addr,
2854 dst_curr_addr,
2855 src_stop_addr);
2856
2857 // Slow path used to copy array when `src` is gray.
2858 SlowPathCodeARM64* read_barrier_slow_path =
2859 new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathARM64(invoke, LocationFrom(tmp));
2860 codegen_->AddSlowPath(read_barrier_slow_path);
2861
2862 // Given the numeric representation, it's enough to check the low bit of the rb_state.
2863 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
2864 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
2865 __ Tbnz(tmp, LockWord::kReadBarrierStateShift, read_barrier_slow_path->GetEntryLabel());
2866
2867 // Fast-path copy.
2868 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2869 // poison/unpoison.
2870 vixl::aarch64::Label loop;
2871 __ Bind(&loop);
Roland Levillain0b671c02016-08-19 12:02:34 +01002872 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
2873 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
Roland Levillain1663d162017-03-17 15:15:21 +00002874 __ Cmp(src_curr_addr, src_stop_addr);
2875 __ B(&loop, ne);
2876
2877 __ Bind(read_barrier_slow_path->GetExitLabel());
2878 } else {
2879 // Non read barrier code.
2880 // Compute base source address, base destination address, and end
2881 // source address for System.arraycopy* intrinsics in `src_base`,
2882 // `dst_base` and `src_end` respectively.
2883 GenSystemArrayCopyAddresses(masm,
2884 type,
2885 src,
2886 src_pos,
2887 dest,
2888 dest_pos,
2889 length,
2890 src_curr_addr,
2891 dst_curr_addr,
2892 src_stop_addr);
2893 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2894 // poison/unpoison.
2895 vixl::aarch64::Label loop;
2896 __ Bind(&loop);
2897 {
2898 Register tmp = temps.AcquireW();
2899 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
2900 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
2901 }
2902 __ Cmp(src_curr_addr, src_stop_addr);
2903 __ B(&loop, ne);
Roland Levillain0b671c02016-08-19 12:02:34 +01002904 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002905 __ Bind(&done);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002906 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002907 }
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002908
donghui.baic2ec9ad2016-03-10 14:02:55 +08002909 // We only need one card marking on the destination array.
2910 codegen_->MarkGCCard(dest.W(), Register(), /* value_can_be_null */ false);
2911
Roland Levillain0b671c02016-08-19 12:02:34 +01002912 __ Bind(intrinsic_slow_path->GetExitLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002913}
2914
Anton Kirilova3ffea22016-04-07 17:02:37 +01002915static void GenIsInfinite(LocationSummary* locations,
2916 bool is64bit,
Scott Wakeling97c72b72016-06-24 16:19:36 +01002917 MacroAssembler* masm) {
Anton Kirilova3ffea22016-04-07 17:02:37 +01002918 Operand infinity;
2919 Register out;
2920
2921 if (is64bit) {
2922 infinity = kPositiveInfinityDouble;
2923 out = XRegisterFrom(locations->Out());
2924 } else {
2925 infinity = kPositiveInfinityFloat;
2926 out = WRegisterFrom(locations->Out());
2927 }
2928
Scott Wakeling97c72b72016-06-24 16:19:36 +01002929 const Register zero = vixl::aarch64::Assembler::AppropriateZeroRegFor(out);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002930
2931 MoveFPToInt(locations, is64bit, masm);
2932 __ Eor(out, out, infinity);
2933 // We don't care about the sign bit, so shift left.
2934 __ Cmp(zero, Operand(out, LSL, 1));
2935 __ Cset(out, eq);
2936}
2937
2938void IntrinsicLocationsBuilderARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2939 CreateFPToIntLocations(arena_, invoke);
2940}
2941
2942void IntrinsicCodeGeneratorARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2943 GenIsInfinite(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
2944}
2945
2946void IntrinsicLocationsBuilderARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2947 CreateFPToIntLocations(arena_, invoke);
2948}
2949
2950void IntrinsicCodeGeneratorARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2951 GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
2952}
2953
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002954void IntrinsicLocationsBuilderARM64::VisitIntegerValueOf(HInvoke* invoke) {
2955 InvokeRuntimeCallingConvention calling_convention;
2956 IntrinsicVisitor::ComputeIntegerValueOfLocations(
2957 invoke,
2958 codegen_,
2959 calling_convention.GetReturnLocation(Primitive::kPrimNot),
2960 Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
2961}
2962
2963void IntrinsicCodeGeneratorARM64::VisitIntegerValueOf(HInvoke* invoke) {
2964 IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo();
2965 LocationSummary* locations = invoke->GetLocations();
2966 MacroAssembler* masm = GetVIXLAssembler();
2967
2968 Register out = RegisterFrom(locations->Out(), Primitive::kPrimNot);
2969 UseScratchRegisterScope temps(masm);
2970 Register temp = temps.AcquireW();
2971 InvokeRuntimeCallingConvention calling_convention;
2972 Register argument = calling_convention.GetRegisterAt(0);
2973 if (invoke->InputAt(0)->IsConstant()) {
2974 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
2975 if (value >= info.low && value <= info.high) {
2976 // Just embed the j.l.Integer in the code.
2977 ScopedObjectAccess soa(Thread::Current());
2978 mirror::Object* boxed = info.cache->Get(value + (-info.low));
2979 DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed));
2980 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(boxed));
2981 __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
2982 } else {
2983 // Allocate and initialize a new j.l.Integer.
2984 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
2985 // JIT object table.
2986 uint32_t address =
2987 dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2988 __ Ldr(argument.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
2989 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2990 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2991 __ Mov(temp.W(), value);
2992 __ Str(temp.W(), HeapOperand(out.W(), info.value_offset));
2993 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2994 // one.
2995 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2996 }
2997 } else {
2998 Register in = RegisterFrom(locations->InAt(0), Primitive::kPrimInt);
2999 // Check bounds of our cache.
3000 __ Add(out.W(), in.W(), -info.low);
3001 __ Cmp(out.W(), info.high - info.low + 1);
3002 vixl::aarch64::Label allocate, done;
3003 __ B(&allocate, hs);
3004 // If the value is within the bounds, load the j.l.Integer directly from the array.
3005 uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
3006 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.cache));
3007 __ Ldr(temp.W(), codegen_->DeduplicateBootImageAddressLiteral(data_offset + address));
3008 MemOperand source = HeapOperand(
3009 temp, out.X(), LSL, Primitive::ComponentSizeShift(Primitive::kPrimNot));
3010 codegen_->Load(Primitive::kPrimNot, out, source);
3011 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(out);
3012 __ B(&done);
3013 __ Bind(&allocate);
3014 // Otherwise allocate and initialize a new j.l.Integer.
3015 address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
3016 __ Ldr(argument.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
3017 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
3018 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
3019 __ Str(in.W(), HeapOperand(out.W(), info.value_offset));
3020 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
3021 // one.
3022 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3023 __ Bind(&done);
3024 }
3025}
3026
Nicolas Geoffray365719c2017-03-08 13:11:50 +00003027void IntrinsicLocationsBuilderARM64::VisitThreadInterrupted(HInvoke* invoke) {
3028 LocationSummary* locations = new (arena_) LocationSummary(invoke,
3029 LocationSummary::kNoCall,
3030 kIntrinsified);
3031 locations->SetOut(Location::RequiresRegister());
3032}
3033
3034void IntrinsicCodeGeneratorARM64::VisitThreadInterrupted(HInvoke* invoke) {
3035 MacroAssembler* masm = GetVIXLAssembler();
3036 Register out = RegisterFrom(invoke->GetLocations()->Out(), Primitive::kPrimInt);
3037 UseScratchRegisterScope temps(masm);
3038 Register temp = temps.AcquireX();
3039
3040 __ Add(temp, tr, Thread::InterruptedOffset<kArm64PointerSize>().Int32Value());
3041 __ Ldar(out.W(), MemOperand(temp));
3042
3043 vixl::aarch64::Label done;
3044 __ Cbz(out.W(), &done);
3045 __ Stlr(wzr, MemOperand(temp));
3046 __ Bind(&done);
3047}
3048
Vladimir Markod254f5c2017-06-02 15:18:36 +00003049UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
Andreas Gampe878d58c2015-01-15 23:24:00 -08003050
Aart Bikff7d89c2016-11-07 08:49:28 -08003051UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf);
3052UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08003053UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferAppend);
3054UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferLength);
3055UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferToString);
3056UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderAppend);
3057UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderLength);
3058UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08003059
Aart Bik0e54c012016-03-04 12:08:31 -08003060// 1.8.
3061UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt)
3062UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong)
3063UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt)
3064UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong)
3065UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08003066
Aart Bik2f9fcc92016-03-01 15:16:54 -08003067UNREACHABLE_INTRINSICS(ARM64)
Roland Levillain4d027112015-07-01 15:41:14 +01003068
3069#undef __
3070
Andreas Gampe878d58c2015-01-15 23:24:00 -08003071} // namespace arm64
3072} // namespace art