blob: 807d6cf54f70ec97da7f8a000e317854bfa225ef [file] [log] [blame]
Andreas Gampe878d58c2015-01-15 23:24:00 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "intrinsics_arm64.h"
18
Serban Constantinescu579885a2015-02-22 20:51:33 +000019#include "arch/arm64/instruction_set_features_arm64.h"
Mathieu Chartiere401d142015-04-22 13:56:20 -070020#include "art_method.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080021#include "code_generator_arm64.h"
22#include "common_arm64.h"
23#include "entrypoints/quick/quick_entrypoints.h"
24#include "intrinsics.h"
25#include "mirror/array-inl.h"
Vladimir Markoe39f14f2017-02-10 15:44:25 +000026#include "mirror/string-inl.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080027#include "thread.h"
28#include "utils/arm64/assembler_arm64.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080029
Scott Wakeling97c72b72016-06-24 16:19:36 +010030using namespace vixl::aarch64; // NOLINT(build/namespaces)
Andreas Gampe878d58c2015-01-15 23:24:00 -080031
Artem Serovaf4e42a2016-08-08 15:11:24 +010032// TODO(VIXL): Make VIXL compile with -Wshadow.
Scott Wakeling97c72b72016-06-24 16:19:36 +010033#pragma GCC diagnostic push
34#pragma GCC diagnostic ignored "-Wshadow"
Artem Serovaf4e42a2016-08-08 15:11:24 +010035#include "aarch64/disasm-aarch64.h"
36#include "aarch64/macro-assembler-aarch64.h"
Scott Wakeling97c72b72016-06-24 16:19:36 +010037#pragma GCC diagnostic pop
Andreas Gampe878d58c2015-01-15 23:24:00 -080038
39namespace art {
40
41namespace arm64 {
42
43using helpers::DRegisterFrom;
44using helpers::FPRegisterFrom;
45using helpers::HeapOperand;
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +000046using helpers::LocationFrom;
Scott Wakeling9ee23f42015-07-23 10:44:35 +010047using helpers::OperandFrom;
Andreas Gampe878d58c2015-01-15 23:24:00 -080048using helpers::RegisterFrom;
49using helpers::SRegisterFrom;
50using helpers::WRegisterFrom;
51using helpers::XRegisterFrom;
xueliang.zhong49924c92016-03-03 10:52:51 +000052using helpers::InputRegisterAt;
Scott Wakeling1f36f412016-04-21 11:13:45 +010053using helpers::OutputRegister;
Andreas Gampe878d58c2015-01-15 23:24:00 -080054
Andreas Gampe878d58c2015-01-15 23:24:00 -080055namespace {
56
57ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) {
58 return MemOperand(XRegisterFrom(location), offset);
59}
60
61} // namespace
62
Scott Wakeling97c72b72016-06-24 16:19:36 +010063MacroAssembler* IntrinsicCodeGeneratorARM64::GetVIXLAssembler() {
Alexandre Rames087930f2016-08-02 13:45:28 +010064 return codegen_->GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -080065}
66
67ArenaAllocator* IntrinsicCodeGeneratorARM64::GetAllocator() {
68 return codegen_->GetGraph()->GetArena();
69}
70
Alexandre Rames087930f2016-08-02 13:45:28 +010071#define __ codegen->GetVIXLAssembler()->
Andreas Gampe878d58c2015-01-15 23:24:00 -080072
73static void MoveFromReturnRegister(Location trg,
74 Primitive::Type type,
75 CodeGeneratorARM64* codegen) {
76 if (!trg.IsValid()) {
77 DCHECK(type == Primitive::kPrimVoid);
78 return;
79 }
80
81 DCHECK_NE(type, Primitive::kPrimVoid);
82
Jeff Hao848f70a2014-01-15 13:49:50 -080083 if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) {
Andreas Gampe878d58c2015-01-15 23:24:00 -080084 Register trg_reg = RegisterFrom(trg, type);
85 Register res_reg = RegisterFrom(ARM64ReturnLocation(type), type);
86 __ Mov(trg_reg, res_reg, kDiscardForSameWReg);
87 } else {
88 FPRegister trg_reg = FPRegisterFrom(trg, type);
89 FPRegister res_reg = FPRegisterFrom(ARM64ReturnLocation(type), type);
90 __ Fmov(trg_reg, res_reg);
91 }
92}
93
Roland Levillainec525fc2015-04-28 15:50:20 +010094static void MoveArguments(HInvoke* invoke, CodeGeneratorARM64* codegen) {
Roland Levillain2d27c8e2015-04-28 15:48:45 +010095 InvokeDexCallingConventionVisitorARM64 calling_convention_visitor;
Roland Levillainec525fc2015-04-28 15:50:20 +010096 IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor);
Andreas Gampe878d58c2015-01-15 23:24:00 -080097}
98
99// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
100// call. This will copy the arguments into the positions for a regular call.
101//
102// Note: The actual parameters are required to be in the locations given by the invoke's location
103// summary. If an intrinsic modifies those locations before a slowpath call, they must be
104// restored!
105class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 {
106 public:
David Srbecky9cd6d372016-02-09 15:24:47 +0000107 explicit IntrinsicSlowPathARM64(HInvoke* invoke)
108 : SlowPathCodeARM64(invoke), invoke_(invoke) { }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800109
110 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
111 CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
112 __ Bind(GetEntryLabel());
113
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000114 SaveLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800115
Roland Levillainec525fc2015-04-28 15:50:20 +0100116 MoveArguments(invoke_, codegen);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800117
Artem Serov914d7a82017-02-07 14:33:49 +0000118 {
119 // Ensure that between the BLR (emitted by Generate*Call) and RecordPcInfo there
120 // are no pools emitted.
121 vixl::EmissionCheckScope guard(codegen->GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes);
122 if (invoke_->IsInvokeStaticOrDirect()) {
123 codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
124 LocationFrom(kArtMethodRegister));
125 } else {
126 codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister));
127 }
128 codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800129 }
130
131 // Copy the result back to the expected output.
132 Location out = invoke_->GetLocations()->Out();
133 if (out.IsValid()) {
134 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
135 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
136 MoveFromReturnRegister(out, invoke_->GetType(), codegen);
137 }
138
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000139 RestoreLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800140 __ B(GetExitLabel());
141 }
142
Alexandre Rames9931f312015-06-19 14:47:01 +0100143 const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathARM64"; }
144
Andreas Gampe878d58c2015-01-15 23:24:00 -0800145 private:
146 // The instruction where this slow path is happening.
147 HInvoke* const invoke_;
148
149 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM64);
150};
151
Roland Levillain0b671c02016-08-19 12:02:34 +0100152// Slow path implementing the SystemArrayCopy intrinsic copy loop with read barriers.
153class ReadBarrierSystemArrayCopySlowPathARM64 : public SlowPathCodeARM64 {
154 public:
155 ReadBarrierSystemArrayCopySlowPathARM64(HInstruction* instruction, Location tmp)
156 : SlowPathCodeARM64(instruction), tmp_(tmp) {
157 DCHECK(kEmitCompilerReadBarrier);
158 DCHECK(kUseBakerReadBarrier);
159 }
160
161 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
162 CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
163 LocationSummary* locations = instruction_->GetLocations();
164 DCHECK(locations->CanCall());
165 DCHECK(instruction_->IsInvokeStaticOrDirect())
166 << "Unexpected instruction in read barrier arraycopy slow path: "
167 << instruction_->DebugName();
168 DCHECK(instruction_->GetLocations()->Intrinsified());
169 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kSystemArrayCopy);
170
171 const int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
172
173 Register src_curr_addr = XRegisterFrom(locations->GetTemp(0));
174 Register dst_curr_addr = XRegisterFrom(locations->GetTemp(1));
175 Register src_stop_addr = XRegisterFrom(locations->GetTemp(2));
176 Register tmp_reg = WRegisterFrom(tmp_);
177
178 __ Bind(GetEntryLabel());
179 vixl::aarch64::Label slow_copy_loop;
180 __ Bind(&slow_copy_loop);
181 __ Ldr(tmp_reg, MemOperand(src_curr_addr, element_size, PostIndex));
182 codegen->GetAssembler()->MaybeUnpoisonHeapReference(tmp_reg);
183 // TODO: Inline the mark bit check before calling the runtime?
184 // tmp_reg = ReadBarrier::Mark(tmp_reg);
185 // No need to save live registers; it's taken care of by the
186 // entrypoint. Also, there is no need to update the stack mask,
187 // as this runtime call will not trigger a garbage collection.
188 // (See ReadBarrierMarkSlowPathARM64::EmitNativeCode for more
189 // explanations.)
190 DCHECK_NE(tmp_.reg(), LR);
191 DCHECK_NE(tmp_.reg(), WSP);
192 DCHECK_NE(tmp_.reg(), WZR);
193 // IP0 is used internally by the ReadBarrierMarkRegX entry point
194 // as a temporary (and not preserved). It thus cannot be used by
195 // any live register in this slow path.
196 DCHECK_NE(LocationFrom(src_curr_addr).reg(), IP0);
197 DCHECK_NE(LocationFrom(dst_curr_addr).reg(), IP0);
198 DCHECK_NE(LocationFrom(src_stop_addr).reg(), IP0);
199 DCHECK_NE(tmp_.reg(), IP0);
200 DCHECK(0 <= tmp_.reg() && tmp_.reg() < kNumberOfWRegisters) << tmp_.reg();
201 int32_t entry_point_offset =
202 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(tmp_.reg());
203 // This runtime call does not require a stack map.
204 codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
205 codegen->GetAssembler()->MaybePoisonHeapReference(tmp_reg);
206 __ Str(tmp_reg, MemOperand(dst_curr_addr, element_size, PostIndex));
207 __ Cmp(src_curr_addr, src_stop_addr);
208 __ B(&slow_copy_loop, ne);
209 __ B(GetExitLabel());
210 }
211
212 const char* GetDescription() const OVERRIDE { return "ReadBarrierSystemArrayCopySlowPathARM64"; }
213
214 private:
215 Location tmp_;
216
217 DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathARM64);
218};
Andreas Gampe878d58c2015-01-15 23:24:00 -0800219#undef __
220
221bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) {
222 Dispatch(invoke);
223 LocationSummary* res = invoke->GetLocations();
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000224 if (res == nullptr) {
225 return false;
226 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000227 return res->Intrinsified();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800228}
229
230#define __ masm->
231
232static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
233 LocationSummary* locations = new (arena) LocationSummary(invoke,
234 LocationSummary::kNoCall,
235 kIntrinsified);
236 locations->SetInAt(0, Location::RequiresFpuRegister());
237 locations->SetOut(Location::RequiresRegister());
238}
239
240static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
241 LocationSummary* locations = new (arena) LocationSummary(invoke,
242 LocationSummary::kNoCall,
243 kIntrinsified);
244 locations->SetInAt(0, Location::RequiresRegister());
245 locations->SetOut(Location::RequiresFpuRegister());
246}
247
Scott Wakeling97c72b72016-06-24 16:19:36 +0100248static void MoveFPToInt(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800249 Location input = locations->InAt(0);
250 Location output = locations->Out();
251 __ Fmov(is64bit ? XRegisterFrom(output) : WRegisterFrom(output),
252 is64bit ? DRegisterFrom(input) : SRegisterFrom(input));
253}
254
Scott Wakeling97c72b72016-06-24 16:19:36 +0100255static void MoveIntToFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800256 Location input = locations->InAt(0);
257 Location output = locations->Out();
258 __ Fmov(is64bit ? DRegisterFrom(output) : SRegisterFrom(output),
259 is64bit ? XRegisterFrom(input) : WRegisterFrom(input));
260}
261
262void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
263 CreateFPToIntLocations(arena_, invoke);
264}
265void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
266 CreateIntToFPLocations(arena_, invoke);
267}
268
269void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000270 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800271}
272void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000273 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800274}
275
276void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
277 CreateFPToIntLocations(arena_, invoke);
278}
279void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
280 CreateIntToFPLocations(arena_, invoke);
281}
282
283void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000284 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800285}
286void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000287 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800288}
289
290static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
291 LocationSummary* locations = new (arena) LocationSummary(invoke,
292 LocationSummary::kNoCall,
293 kIntrinsified);
294 locations->SetInAt(0, Location::RequiresRegister());
295 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
296}
297
298static void GenReverseBytes(LocationSummary* locations,
299 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100300 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800301 Location in = locations->InAt(0);
302 Location out = locations->Out();
303
304 switch (type) {
305 case Primitive::kPrimShort:
306 __ Rev16(WRegisterFrom(out), WRegisterFrom(in));
307 __ Sxth(WRegisterFrom(out), WRegisterFrom(out));
308 break;
309 case Primitive::kPrimInt:
310 case Primitive::kPrimLong:
311 __ Rev(RegisterFrom(out, type), RegisterFrom(in, type));
312 break;
313 default:
314 LOG(FATAL) << "Unexpected size for reverse-bytes: " << type;
315 UNREACHABLE();
316 }
317}
318
319void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
320 CreateIntToIntLocations(arena_, invoke);
321}
322
323void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
324 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
325}
326
327void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) {
328 CreateIntToIntLocations(arena_, invoke);
329}
330
331void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) {
332 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
333}
334
335void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) {
336 CreateIntToIntLocations(arena_, invoke);
337}
338
339void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
340 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
341}
342
Aart Bik7b565022016-01-28 14:36:22 -0800343static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
344 LocationSummary* locations = new (arena) LocationSummary(invoke,
345 LocationSummary::kNoCall,
346 kIntrinsified);
347 locations->SetInAt(0, Location::RequiresRegister());
348 locations->SetInAt(1, Location::RequiresRegister());
349 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
350}
351
Scott Wakeling611d3392015-07-10 11:42:06 +0100352static void GenNumberOfLeadingZeros(LocationSummary* locations,
353 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100354 MacroAssembler* masm) {
Scott Wakeling611d3392015-07-10 11:42:06 +0100355 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
356
357 Location in = locations->InAt(0);
358 Location out = locations->Out();
359
360 __ Clz(RegisterFrom(out, type), RegisterFrom(in, type));
361}
362
363void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
364 CreateIntToIntLocations(arena_, invoke);
365}
366
367void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
368 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
369}
370
371void IntrinsicLocationsBuilderARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
372 CreateIntToIntLocations(arena_, invoke);
373}
374
375void IntrinsicCodeGeneratorARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
376 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
377}
378
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100379static void GenNumberOfTrailingZeros(LocationSummary* locations,
380 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100381 MacroAssembler* masm) {
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100382 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
383
384 Location in = locations->InAt(0);
385 Location out = locations->Out();
386
387 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
388 __ Clz(RegisterFrom(out, type), RegisterFrom(out, type));
389}
390
391void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
392 CreateIntToIntLocations(arena_, invoke);
393}
394
395void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
396 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
397}
398
399void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
400 CreateIntToIntLocations(arena_, invoke);
401}
402
403void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
404 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
405}
406
Andreas Gampe878d58c2015-01-15 23:24:00 -0800407static void GenReverse(LocationSummary* locations,
408 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100409 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800410 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
411
412 Location in = locations->InAt(0);
413 Location out = locations->Out();
414
415 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
416}
417
418void IntrinsicLocationsBuilderARM64::VisitIntegerReverse(HInvoke* invoke) {
419 CreateIntToIntLocations(arena_, invoke);
420}
421
422void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) {
423 GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
424}
425
426void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) {
427 CreateIntToIntLocations(arena_, invoke);
428}
429
430void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
431 GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
432}
433
Scott Wakeling97c72b72016-06-24 16:19:36 +0100434static void GenBitCount(HInvoke* instr, Primitive::Type type, MacroAssembler* masm) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100435 DCHECK(Primitive::IsIntOrLongType(type)) << type;
436 DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
437 DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
xueliang.zhong49924c92016-03-03 10:52:51 +0000438
xueliang.zhong49924c92016-03-03 10:52:51 +0000439 UseScratchRegisterScope temps(masm);
440
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000441 Register src = InputRegisterAt(instr, 0);
Roland Levillainfa3912e2016-04-01 18:21:55 +0100442 Register dst = RegisterFrom(instr->GetLocations()->Out(), type);
443 FPRegister fpr = (type == Primitive::kPrimLong) ? temps.AcquireD() : temps.AcquireS();
xueliang.zhong49924c92016-03-03 10:52:51 +0000444
445 __ Fmov(fpr, src);
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000446 __ Cnt(fpr.V8B(), fpr.V8B());
447 __ Addv(fpr.B(), fpr.V8B());
xueliang.zhong49924c92016-03-03 10:52:51 +0000448 __ Fmov(dst, fpr);
449}
450
451void IntrinsicLocationsBuilderARM64::VisitLongBitCount(HInvoke* invoke) {
452 CreateIntToIntLocations(arena_, invoke);
453}
454
455void IntrinsicCodeGeneratorARM64::VisitLongBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100456 GenBitCount(invoke, Primitive::kPrimLong, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000457}
458
459void IntrinsicLocationsBuilderARM64::VisitIntegerBitCount(HInvoke* invoke) {
460 CreateIntToIntLocations(arena_, invoke);
461}
462
463void IntrinsicCodeGeneratorARM64::VisitIntegerBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100464 GenBitCount(invoke, Primitive::kPrimInt, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000465}
466
Andreas Gampe878d58c2015-01-15 23:24:00 -0800467static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800468 LocationSummary* locations = new (arena) LocationSummary(invoke,
469 LocationSummary::kNoCall,
470 kIntrinsified);
471 locations->SetInAt(0, Location::RequiresFpuRegister());
472 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
473}
474
Scott Wakeling97c72b72016-06-24 16:19:36 +0100475static void MathAbsFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800476 Location in = locations->InAt(0);
477 Location out = locations->Out();
478
479 FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in);
480 FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out);
481
482 __ Fabs(out_reg, in_reg);
483}
484
485void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) {
486 CreateFPToFPLocations(arena_, invoke);
487}
488
489void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000490 MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800491}
492
493void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) {
494 CreateFPToFPLocations(arena_, invoke);
495}
496
497void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000498 MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800499}
500
501static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
502 LocationSummary* locations = new (arena) LocationSummary(invoke,
503 LocationSummary::kNoCall,
504 kIntrinsified);
505 locations->SetInAt(0, Location::RequiresRegister());
506 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
507}
508
509static void GenAbsInteger(LocationSummary* locations,
510 bool is64bit,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100511 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800512 Location in = locations->InAt(0);
513 Location output = locations->Out();
514
515 Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in);
516 Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output);
517
518 __ Cmp(in_reg, Operand(0));
519 __ Cneg(out_reg, in_reg, lt);
520}
521
522void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
523 CreateIntToInt(arena_, invoke);
524}
525
526void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000527 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800528}
529
530void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
531 CreateIntToInt(arena_, invoke);
532}
533
534void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000535 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800536}
537
538static void GenMinMaxFP(LocationSummary* locations,
539 bool is_min,
540 bool is_double,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100541 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800542 Location op1 = locations->InAt(0);
543 Location op2 = locations->InAt(1);
544 Location out = locations->Out();
545
546 FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1);
547 FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2);
548 FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out);
549 if (is_min) {
550 __ Fmin(out_reg, op1_reg, op2_reg);
551 } else {
552 __ Fmax(out_reg, op1_reg, op2_reg);
553 }
554}
555
556static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
557 LocationSummary* locations = new (arena) LocationSummary(invoke,
558 LocationSummary::kNoCall,
559 kIntrinsified);
560 locations->SetInAt(0, Location::RequiresFpuRegister());
561 locations->SetInAt(1, Location::RequiresFpuRegister());
562 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
563}
564
565void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
566 CreateFPFPToFPLocations(arena_, invoke);
567}
568
569void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000570 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800571}
572
573void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
574 CreateFPFPToFPLocations(arena_, invoke);
575}
576
577void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000578 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800579}
580
581void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
582 CreateFPFPToFPLocations(arena_, invoke);
583}
584
585void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000586 GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800587}
588
589void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
590 CreateFPFPToFPLocations(arena_, invoke);
591}
592
593void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000594 GenMinMaxFP(
595 invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800596}
597
598static void GenMinMax(LocationSummary* locations,
599 bool is_min,
600 bool is_long,
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 Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
607 Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
608 Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
609
610 __ Cmp(op1_reg, op2_reg);
611 __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
612}
613
Andreas Gampe878d58c2015-01-15 23:24:00 -0800614void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
615 CreateIntIntToIntLocations(arena_, invoke);
616}
617
618void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000619 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800620}
621
622void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) {
623 CreateIntIntToIntLocations(arena_, invoke);
624}
625
626void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000627 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800628}
629
630void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) {
631 CreateIntIntToIntLocations(arena_, invoke);
632}
633
634void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000635 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800636}
637
638void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) {
639 CreateIntIntToIntLocations(arena_, invoke);
640}
641
642void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000643 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800644}
645
646void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) {
647 CreateFPToFPLocations(arena_, invoke);
648}
649
650void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) {
651 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100652 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800653 __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
654}
655
656void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) {
657 CreateFPToFPLocations(arena_, invoke);
658}
659
660void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) {
661 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100662 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800663 __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
664}
665
666void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) {
667 CreateFPToFPLocations(arena_, invoke);
668}
669
670void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) {
671 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100672 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800673 __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
674}
675
676void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) {
677 CreateFPToFPLocations(arena_, invoke);
678}
679
680void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) {
681 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100682 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800683 __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
684}
685
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100686static void CreateFPToIntPlusFPTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800687 LocationSummary* locations = new (arena) LocationSummary(invoke,
688 LocationSummary::kNoCall,
689 kIntrinsified);
690 locations->SetInAt(0, Location::RequiresFpuRegister());
691 locations->SetOut(Location::RequiresRegister());
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100692 locations->AddTemp(Location::RequiresFpuRegister());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800693}
694
Scott Wakeling97c72b72016-06-24 16:19:36 +0100695static void GenMathRound(HInvoke* invoke, bool is_double, vixl::aarch64::MacroAssembler* masm) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100696 // Java 8 API definition for Math.round():
697 // Return the closest long or int to the argument, with ties rounding to positive infinity.
698 //
699 // There is no single instruction in ARMv8 that can support the above definition.
700 // We choose to use FCVTAS here, because it has closest semantic.
701 // FCVTAS performs rounding to nearest integer, ties away from zero.
702 // For most inputs (positive values, zero or NaN), this instruction is enough.
703 // We only need a few handling code after FCVTAS if the input is negative half value.
704 //
705 // The reason why we didn't choose FCVTPS instruction here is that
706 // although it performs rounding toward positive infinity, it doesn't perform rounding to nearest.
707 // For example, FCVTPS(-1.9) = -1 and FCVTPS(1.1) = 2.
708 // If we were using this instruction, for most inputs, more handling code would be needed.
709 LocationSummary* l = invoke->GetLocations();
710 FPRegister in_reg = is_double ? DRegisterFrom(l->InAt(0)) : SRegisterFrom(l->InAt(0));
711 FPRegister tmp_fp = is_double ? DRegisterFrom(l->GetTemp(0)) : SRegisterFrom(l->GetTemp(0));
712 Register out_reg = is_double ? XRegisterFrom(l->Out()) : WRegisterFrom(l->Out());
Scott Wakeling97c72b72016-06-24 16:19:36 +0100713 vixl::aarch64::Label done;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800714
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100715 // Round to nearest integer, ties away from zero.
716 __ Fcvtas(out_reg, in_reg);
717
718 // For positive values, zero or NaN inputs, rounding is done.
Scott Wakeling97c72b72016-06-24 16:19:36 +0100719 __ Tbz(out_reg, out_reg.GetSizeInBits() - 1, &done);
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100720
721 // Handle input < 0 cases.
722 // If input is negative but not a tie, previous result (round to nearest) is valid.
723 // If input is a negative tie, out_reg += 1.
724 __ Frinta(tmp_fp, in_reg);
725 __ Fsub(tmp_fp, in_reg, tmp_fp);
726 __ Fcmp(tmp_fp, 0.5);
727 __ Cinc(out_reg, out_reg, eq);
728
729 __ Bind(&done);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800730}
731
732void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100733 CreateFPToIntPlusFPTempLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800734}
735
736void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100737 GenMathRound(invoke, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800738}
739
740void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100741 CreateFPToIntPlusFPTempLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800742}
743
744void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100745 GenMathRound(invoke, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800746}
747
748void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) {
749 CreateIntToIntLocations(arena_, invoke);
750}
751
752void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100753 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800754 __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()),
755 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
756}
757
758void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
759 CreateIntToIntLocations(arena_, invoke);
760}
761
762void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100763 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800764 __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()),
765 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
766}
767
768void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
769 CreateIntToIntLocations(arena_, invoke);
770}
771
772void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100773 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800774 __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()),
775 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
776}
777
778void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
779 CreateIntToIntLocations(arena_, invoke);
780}
781
782void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100783 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800784 __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()),
785 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
786}
787
788static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
789 LocationSummary* locations = new (arena) LocationSummary(invoke,
790 LocationSummary::kNoCall,
791 kIntrinsified);
792 locations->SetInAt(0, Location::RequiresRegister());
793 locations->SetInAt(1, Location::RequiresRegister());
794}
795
796void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) {
797 CreateIntIntToVoidLocations(arena_, invoke);
798}
799
800void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100801 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800802 __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)),
803 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
804}
805
806void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
807 CreateIntIntToVoidLocations(arena_, invoke);
808}
809
810void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100811 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800812 __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)),
813 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
814}
815
816void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
817 CreateIntIntToVoidLocations(arena_, invoke);
818}
819
820void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100821 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800822 __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)),
823 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
824}
825
826void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
827 CreateIntIntToVoidLocations(arena_, invoke);
828}
829
830void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100831 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800832 __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)),
833 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
834}
835
836void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) {
837 LocationSummary* locations = new (arena_) LocationSummary(invoke,
838 LocationSummary::kNoCall,
839 kIntrinsified);
840 locations->SetOut(Location::RequiresRegister());
841}
842
843void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) {
844 codegen_->Load(Primitive::kPrimNot, WRegisterFrom(invoke->GetLocations()->Out()),
Andreas Gampe542451c2016-07-26 09:02:02 -0700845 MemOperand(tr, Thread::PeerOffset<kArm64PointerSize>().Int32Value()));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800846}
847
848static void GenUnsafeGet(HInvoke* invoke,
849 Primitive::Type type,
850 bool is_volatile,
851 CodeGeneratorARM64* codegen) {
852 LocationSummary* locations = invoke->GetLocations();
853 DCHECK((type == Primitive::kPrimInt) ||
854 (type == Primitive::kPrimLong) ||
855 (type == Primitive::kPrimNot));
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000856 Location base_loc = locations->InAt(1);
857 Register base = WRegisterFrom(base_loc); // Object pointer.
858 Location offset_loc = locations->InAt(2);
859 Register offset = XRegisterFrom(offset_loc); // Long offset.
860 Location trg_loc = locations->Out();
861 Register trg = RegisterFrom(trg_loc, type);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800862
Roland Levillain44015862016-01-22 11:47:17 +0000863 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
864 // UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case.
Roland Levillain54f869e2017-03-06 13:54:11 +0000865 Register temp = WRegisterFrom(locations->GetTemp(0));
Roland Levillainbfea3352016-06-23 13:48:47 +0100866 codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke,
867 trg_loc,
868 base,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100869 /* offset */ 0u,
Roland Levillainbfea3352016-06-23 13:48:47 +0100870 /* index */ offset_loc,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100871 /* scale_factor */ 0u,
Roland Levillainbfea3352016-06-23 13:48:47 +0100872 temp,
873 /* needs_null_check */ false,
874 is_volatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800875 } else {
Roland Levillain44015862016-01-22 11:47:17 +0000876 // Other cases.
877 MemOperand mem_op(base.X(), offset);
878 if (is_volatile) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +0000879 codegen->LoadAcquire(invoke, trg, mem_op, /* needs_null_check */ true);
Roland Levillain44015862016-01-22 11:47:17 +0000880 } else {
881 codegen->Load(type, trg, mem_op);
882 }
Roland Levillain4d027112015-07-01 15:41:14 +0100883
Roland Levillain44015862016-01-22 11:47:17 +0000884 if (type == Primitive::kPrimNot) {
885 DCHECK(trg.IsW());
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100886 codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0u, offset_loc);
Roland Levillain44015862016-01-22 11:47:17 +0000887 }
Roland Levillain4d027112015-07-01 15:41:14 +0100888 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800889}
890
891static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000892 bool can_call = kEmitCompilerReadBarrier &&
893 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
894 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800895 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100896 (can_call
897 ? LocationSummary::kCallOnSlowPath
898 : LocationSummary::kNoCall),
Andreas Gampe878d58c2015-01-15 23:24:00 -0800899 kIntrinsified);
Vladimir Marko70e97462016-08-09 11:04:26 +0100900 if (can_call && kUseBakerReadBarrier) {
Vladimir Marko804b03f2016-09-14 16:26:36 +0100901 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Roland Levillain54f869e2017-03-06 13:54:11 +0000902 // We need a temporary register for the read barrier marking slow
903 // path in CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier.
904 locations->AddTemp(Location::RequiresRegister());
Vladimir Marko70e97462016-08-09 11:04:26 +0100905 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800906 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
907 locations->SetInAt(1, Location::RequiresRegister());
908 locations->SetInAt(2, Location::RequiresRegister());
Roland Levillainbfea3352016-06-23 13:48:47 +0100909 locations->SetOut(Location::RequiresRegister(),
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100910 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800911}
912
913void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
914 CreateIntIntIntToIntLocations(arena_, invoke);
915}
916void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
917 CreateIntIntIntToIntLocations(arena_, invoke);
918}
919void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
920 CreateIntIntIntToIntLocations(arena_, invoke);
921}
922void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
923 CreateIntIntIntToIntLocations(arena_, invoke);
924}
925void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
926 CreateIntIntIntToIntLocations(arena_, invoke);
927}
928void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
929 CreateIntIntIntToIntLocations(arena_, invoke);
930}
931
932void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000933 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800934}
935void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000936 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800937}
938void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000939 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800940}
941void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000942 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800943}
944void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000945 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800946}
947void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000948 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800949}
950
951static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
952 LocationSummary* locations = new (arena) LocationSummary(invoke,
953 LocationSummary::kNoCall,
954 kIntrinsified);
955 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
956 locations->SetInAt(1, Location::RequiresRegister());
957 locations->SetInAt(2, Location::RequiresRegister());
958 locations->SetInAt(3, Location::RequiresRegister());
959}
960
961void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
962 CreateIntIntIntIntToVoid(arena_, invoke);
963}
964void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
965 CreateIntIntIntIntToVoid(arena_, invoke);
966}
967void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
968 CreateIntIntIntIntToVoid(arena_, invoke);
969}
970void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
971 CreateIntIntIntIntToVoid(arena_, invoke);
972}
973void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
974 CreateIntIntIntIntToVoid(arena_, invoke);
975}
976void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
977 CreateIntIntIntIntToVoid(arena_, invoke);
978}
979void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
980 CreateIntIntIntIntToVoid(arena_, invoke);
981}
982void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
983 CreateIntIntIntIntToVoid(arena_, invoke);
984}
985void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
986 CreateIntIntIntIntToVoid(arena_, invoke);
987}
988
Artem Serov914d7a82017-02-07 14:33:49 +0000989static void GenUnsafePut(HInvoke* invoke,
Andreas Gampe878d58c2015-01-15 23:24:00 -0800990 Primitive::Type type,
991 bool is_volatile,
992 bool is_ordered,
993 CodeGeneratorARM64* codegen) {
Artem Serov914d7a82017-02-07 14:33:49 +0000994 LocationSummary* locations = invoke->GetLocations();
Alexandre Rames087930f2016-08-02 13:45:28 +0100995 MacroAssembler* masm = codegen->GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800996
997 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
998 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
999 Register value = RegisterFrom(locations->InAt(3), type);
Roland Levillain4d027112015-07-01 15:41:14 +01001000 Register source = value;
Andreas Gampe878d58c2015-01-15 23:24:00 -08001001 MemOperand mem_op(base.X(), offset);
1002
Roland Levillain4d027112015-07-01 15:41:14 +01001003 {
1004 // We use a block to end the scratch scope before the write barrier, thus
1005 // freeing the temporary registers so they can be used in `MarkGCCard`.
1006 UseScratchRegisterScope temps(masm);
1007
1008 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1009 DCHECK(value.IsW());
1010 Register temp = temps.AcquireW();
1011 __ Mov(temp.W(), value.W());
1012 codegen->GetAssembler()->PoisonHeapReference(temp.W());
1013 source = temp;
Andreas Gampe878d58c2015-01-15 23:24:00 -08001014 }
Roland Levillain4d027112015-07-01 15:41:14 +01001015
1016 if (is_volatile || is_ordered) {
Artem Serov914d7a82017-02-07 14:33:49 +00001017 codegen->StoreRelease(invoke, type, source, mem_op, /* needs_null_check */ false);
Roland Levillain4d027112015-07-01 15:41:14 +01001018 } else {
1019 codegen->Store(type, source, mem_op);
1020 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001021 }
1022
1023 if (type == Primitive::kPrimNot) {
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001024 bool value_can_be_null = true; // TODO: Worth finding out this information?
1025 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001026 }
1027}
1028
1029void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001030 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001031 Primitive::kPrimInt,
1032 /* is_volatile */ false,
1033 /* is_ordered */ false,
1034 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001035}
1036void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001037 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001038 Primitive::kPrimInt,
1039 /* is_volatile */ false,
1040 /* is_ordered */ true,
1041 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001042}
1043void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001044 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001045 Primitive::kPrimInt,
1046 /* is_volatile */ true,
1047 /* is_ordered */ false,
1048 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001049}
1050void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001051 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001052 Primitive::kPrimNot,
1053 /* is_volatile */ false,
1054 /* is_ordered */ false,
1055 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001056}
1057void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001058 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001059 Primitive::kPrimNot,
1060 /* is_volatile */ false,
1061 /* is_ordered */ true,
1062 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001063}
1064void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001065 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001066 Primitive::kPrimNot,
1067 /* is_volatile */ true,
1068 /* is_ordered */ false,
1069 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001070}
1071void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001072 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001073 Primitive::kPrimLong,
1074 /* is_volatile */ false,
1075 /* is_ordered */ false,
1076 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001077}
1078void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001079 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001080 Primitive::kPrimLong,
1081 /* is_volatile */ false,
1082 /* is_ordered */ true,
1083 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001084}
1085void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001086 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001087 Primitive::kPrimLong,
1088 /* is_volatile */ true,
1089 /* is_ordered */ false,
1090 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001091}
1092
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001093static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
1094 HInvoke* invoke,
1095 Primitive::Type type) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001096 bool can_call = kEmitCompilerReadBarrier &&
1097 kUseBakerReadBarrier &&
1098 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001099 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001100 (can_call
1101 ? LocationSummary::kCallOnSlowPath
1102 : LocationSummary::kNoCall),
Andreas Gampe878d58c2015-01-15 23:24:00 -08001103 kIntrinsified);
1104 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1105 locations->SetInAt(1, Location::RequiresRegister());
1106 locations->SetInAt(2, Location::RequiresRegister());
1107 locations->SetInAt(3, Location::RequiresRegister());
1108 locations->SetInAt(4, Location::RequiresRegister());
1109
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001110 // If heap poisoning is enabled, we don't want the unpoisoning
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001111 // operations to potentially clobber the output. Likewise when
1112 // emitting a (Baker) read barrier, which may call.
1113 Location::OutputOverlap overlaps =
1114 ((kPoisonHeapReferences && type == Primitive::kPrimNot) || can_call)
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001115 ? Location::kOutputOverlap
1116 : Location::kNoOutputOverlap;
1117 locations->SetOut(Location::RequiresRegister(), overlaps);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001118 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1119 // Temporary register for (Baker) read barrier.
1120 locations->AddTemp(Location::RequiresRegister());
1121 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001122}
1123
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001124static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARM64* codegen) {
Alexandre Rames087930f2016-08-02 13:45:28 +01001125 MacroAssembler* masm = codegen->GetVIXLAssembler();
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001126 LocationSummary* locations = invoke->GetLocations();
Andreas Gampe878d58c2015-01-15 23:24:00 -08001127
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001128 Location out_loc = locations->Out();
1129 Register out = WRegisterFrom(out_loc); // Boolean result.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001130
1131 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001132 Location offset_loc = locations->InAt(2);
1133 Register offset = XRegisterFrom(offset_loc); // Long offset.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001134 Register expected = RegisterFrom(locations->InAt(3), type); // Expected.
1135 Register value = RegisterFrom(locations->InAt(4), type); // Value.
1136
1137 // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
1138 if (type == Primitive::kPrimNot) {
1139 // Mark card for object assuming new value is stored.
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001140 bool value_can_be_null = true; // TODO: Worth finding out this information?
1141 codegen->MarkGCCard(base, value, value_can_be_null);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001142
1143 // The only read barrier implementation supporting the
1144 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1145 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1146
1147 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1148 Register temp = WRegisterFrom(locations->GetTemp(0));
1149 // Need to make sure the reference stored in the field is a to-space
1150 // one before attempting the CAS or the CAS could fail incorrectly.
1151 codegen->GenerateReferenceLoadWithBakerReadBarrier(
1152 invoke,
1153 out_loc, // Unused, used only as a "temporary" within the read barrier.
1154 base,
1155 /* offset */ 0u,
1156 /* index */ offset_loc,
1157 /* scale_factor */ 0u,
1158 temp,
1159 /* needs_null_check */ false,
1160 /* use_load_acquire */ false,
1161 /* always_update_field */ true);
1162 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001163 }
1164
1165 UseScratchRegisterScope temps(masm);
1166 Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
1167 Register tmp_value = temps.AcquireSameSizeAs(value); // Value in memory.
1168
1169 Register tmp_32 = tmp_value.W();
1170
1171 __ Add(tmp_ptr, base.X(), Operand(offset));
1172
Roland Levillain4d027112015-07-01 15:41:14 +01001173 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1174 codegen->GetAssembler()->PoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001175 if (value.Is(expected)) {
1176 // Do not poison `value`, as it is the same register as
1177 // `expected`, which has just been poisoned.
1178 } else {
1179 codegen->GetAssembler()->PoisonHeapReference(value);
1180 }
Roland Levillain4d027112015-07-01 15:41:14 +01001181 }
1182
Andreas Gampe878d58c2015-01-15 23:24:00 -08001183 // do {
1184 // tmp_value = [tmp_ptr] - expected;
1185 // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1186 // result = tmp_value != 0;
1187
Scott Wakeling97c72b72016-06-24 16:19:36 +01001188 vixl::aarch64::Label loop_head, exit_loop;
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001189 __ Bind(&loop_head);
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001190 __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
1191 __ Cmp(tmp_value, expected);
1192 __ B(&exit_loop, ne);
1193 __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
1194 __ Cbnz(tmp_32, &loop_head);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001195 __ Bind(&exit_loop);
1196 __ Cset(out, eq);
Roland Levillain4d027112015-07-01 15:41:14 +01001197
1198 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillain4d027112015-07-01 15:41:14 +01001199 codegen->GetAssembler()->UnpoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001200 if (value.Is(expected)) {
1201 // Do not unpoison `value`, as it is the same register as
1202 // `expected`, which has just been unpoisoned.
1203 } else {
1204 codegen->GetAssembler()->UnpoisonHeapReference(value);
1205 }
Roland Levillain4d027112015-07-01 15:41:14 +01001206 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001207}
1208
1209void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001210 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001211}
1212void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001213 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001214}
1215void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001216 // The only read barrier implementation supporting the
1217 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1218 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain985ff702015-10-23 13:25:35 +01001219 return;
1220 }
1221
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001222 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001223}
1224
1225void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001226 GenCas(invoke, Primitive::kPrimInt, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001227}
1228void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001229 GenCas(invoke, Primitive::kPrimLong, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001230}
1231void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001232 // The only read barrier implementation supporting the
1233 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1234 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01001235
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001236 GenCas(invoke, Primitive::kPrimNot, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001237}
1238
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001239void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001240 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Scott Wakeling1f36f412016-04-21 11:13:45 +01001241 invoke->InputAt(1)->CanBeNull()
1242 ? LocationSummary::kCallOnSlowPath
1243 : LocationSummary::kNoCall,
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001244 kIntrinsified);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001245 locations->SetInAt(0, Location::RequiresRegister());
1246 locations->SetInAt(1, Location::RequiresRegister());
1247 locations->AddTemp(Location::RequiresRegister());
1248 locations->AddTemp(Location::RequiresRegister());
1249 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001250 // Need temporary registers for String compression's feature.
1251 if (mirror::kUseStringCompression) {
1252 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001253 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001254 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001255}
1256
1257void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001258 MacroAssembler* masm = GetVIXLAssembler();
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001259 LocationSummary* locations = invoke->GetLocations();
1260
Alexandre Rames2ea91532016-08-11 17:04:14 +01001261 Register str = InputRegisterAt(invoke, 0);
1262 Register arg = InputRegisterAt(invoke, 1);
1263 DCHECK(str.IsW());
1264 DCHECK(arg.IsW());
Scott Wakeling1f36f412016-04-21 11:13:45 +01001265 Register out = OutputRegister(invoke);
1266
1267 Register temp0 = WRegisterFrom(locations->GetTemp(0));
1268 Register temp1 = WRegisterFrom(locations->GetTemp(1));
1269 Register temp2 = WRegisterFrom(locations->GetTemp(2));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001270 Register temp3;
jessicahandojo05765752016-09-09 19:01:32 -07001271 if (mirror::kUseStringCompression) {
1272 temp3 = WRegisterFrom(locations->GetTemp(3));
jessicahandojo05765752016-09-09 19:01:32 -07001273 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001274
Scott Wakeling97c72b72016-06-24 16:19:36 +01001275 vixl::aarch64::Label loop;
1276 vixl::aarch64::Label find_char_diff;
1277 vixl::aarch64::Label end;
jessicahandojo05765752016-09-09 19:01:32 -07001278 vixl::aarch64::Label different_compression;
Scott Wakeling1f36f412016-04-21 11:13:45 +01001279
1280 // Get offsets of count and value fields within a string object.
1281 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1282 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1283
Nicolas Geoffray512e04d2015-03-27 17:21:24 +00001284 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +01001285 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001286
Scott Wakeling1f36f412016-04-21 11:13:45 +01001287 // Take slow path and throw if input can be and is null.
1288 SlowPathCodeARM64* slow_path = nullptr;
1289 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1290 if (can_slow_path) {
1291 slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1292 codegen_->AddSlowPath(slow_path);
1293 __ Cbz(arg, slow_path->GetEntryLabel());
1294 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001295
Scott Wakeling1f36f412016-04-21 11:13:45 +01001296 // Reference equality check, return 0 if same reference.
1297 __ Subs(out, str, arg);
1298 __ B(&end, eq);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001299
jessicahandojo05765752016-09-09 19:01:32 -07001300 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001301 // Load `count` fields of this and argument strings.
jessicahandojo05765752016-09-09 19:01:32 -07001302 __ Ldr(temp3, HeapOperand(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001303 __ Ldr(temp2, HeapOperand(arg, count_offset));
jessicahandojo05765752016-09-09 19:01:32 -07001304 // Clean out compression flag from lengths.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001305 __ Lsr(temp0, temp3, 1u);
1306 __ Lsr(temp1, temp2, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001307 } else {
1308 // Load lengths of this and argument strings.
1309 __ Ldr(temp0, HeapOperand(str, count_offset));
1310 __ Ldr(temp1, HeapOperand(arg, count_offset));
1311 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001312 // out = length diff.
1313 __ Subs(out, temp0, temp1);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001314 // temp0 = min(len(str), len(arg)).
1315 __ Csel(temp0, temp1, temp0, ge);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001316 // Shorter string is empty?
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001317 __ Cbz(temp0, &end);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001318
jessicahandojo05765752016-09-09 19:01:32 -07001319 if (mirror::kUseStringCompression) {
1320 // Check if both strings using same compression style to use this comparison loop.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001321 __ Eor(temp2, temp2, Operand(temp3));
1322 // Interleave with compression flag extraction which is needed for both paths
1323 // and also set flags which is needed only for the different compressions path.
1324 __ Ands(temp3.W(), temp3.W(), Operand(1));
1325 __ Tbnz(temp2, 0, &different_compression); // Does not use flags.
jessicahandojo05765752016-09-09 19:01:32 -07001326 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001327 // Store offset of string value in preparation for comparison loop.
1328 __ Mov(temp1, value_offset);
jessicahandojo05765752016-09-09 19:01:32 -07001329 if (mirror::kUseStringCompression) {
1330 // For string compression, calculate the number of bytes to compare (not chars).
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001331 // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
1332 __ Lsl(temp0, temp0, temp3);
jessicahandojo05765752016-09-09 19:01:32 -07001333 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001334
1335 UseScratchRegisterScope scratch_scope(masm);
1336 Register temp4 = scratch_scope.AcquireX();
1337
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001338 // Assertions that must hold in order to compare strings 8 bytes at a time.
Scott Wakeling1f36f412016-04-21 11:13:45 +01001339 DCHECK_ALIGNED(value_offset, 8);
1340 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1341
1342 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1343 DCHECK_EQ(char_size, 2u);
1344
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001345 // Promote temp2 to an X reg, ready for LDR.
1346 temp2 = temp2.X();
Scott Wakeling1f36f412016-04-21 11:13:45 +01001347
1348 // Loop to compare 4x16-bit characters at a time (ok because of string data alignment).
1349 __ Bind(&loop);
Alexandre Rames2ea91532016-08-11 17:04:14 +01001350 __ Ldr(temp4, MemOperand(str.X(), temp1.X()));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001351 __ Ldr(temp2, MemOperand(arg.X(), temp1.X()));
1352 __ Cmp(temp4, temp2);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001353 __ B(ne, &find_char_diff);
1354 __ Add(temp1, temp1, char_size * 4);
jessicahandojo05765752016-09-09 19:01:32 -07001355 // With string compression, we have compared 8 bytes, otherwise 4 chars.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001356 __ Subs(temp0, temp0, (mirror::kUseStringCompression) ? 8 : 4);
1357 __ B(&loop, hi);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001358 __ B(&end);
1359
1360 // Promote temp1 to an X reg, ready for EOR.
1361 temp1 = temp1.X();
1362
jessicahandojo05765752016-09-09 19:01:32 -07001363 // Find the single character difference.
Scott Wakeling1f36f412016-04-21 11:13:45 +01001364 __ Bind(&find_char_diff);
1365 // Get the bit position of the first character that differs.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001366 __ Eor(temp1, temp2, temp4);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001367 __ Rbit(temp1, temp1);
1368 __ Clz(temp1, temp1);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001369
jessicahandojo05765752016-09-09 19:01:32 -07001370 // If the number of chars remaining <= the index where the difference occurs (0-3), then
Scott Wakeling1f36f412016-04-21 11:13:45 +01001371 // the difference occurs outside the remaining string data, so just return length diff (out).
jessicahandojo05765752016-09-09 19:01:32 -07001372 // Unlike ARM, we're doing the comparison in one go here, without the subtraction at the
1373 // find_char_diff_2nd_cmp path, so it doesn't matter whether the comparison is signed or
1374 // unsigned when string compression is disabled.
1375 // When it's enabled, the comparison must be unsigned.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001376 __ Cmp(temp0, Operand(temp1.W(), LSR, (mirror::kUseStringCompression) ? 3 : 4));
jessicahandojo05765752016-09-09 19:01:32 -07001377 __ B(ls, &end);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001378
Scott Wakeling1f36f412016-04-21 11:13:45 +01001379 // Extract the characters and calculate the difference.
jessicahandojo05765752016-09-09 19:01:32 -07001380 if (mirror:: kUseStringCompression) {
jessicahandojo05765752016-09-09 19:01:32 -07001381 __ Bic(temp1, temp1, 0x7);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001382 __ Bic(temp1, temp1, Operand(temp3.X(), LSL, 3u));
1383 } else {
1384 __ Bic(temp1, temp1, 0xf);
jessicahandojo05765752016-09-09 19:01:32 -07001385 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001386 __ Lsr(temp2, temp2, temp1);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001387 __ Lsr(temp4, temp4, temp1);
jessicahandojo05765752016-09-09 19:01:32 -07001388 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001389 // Prioritize the case of compressed strings and calculate such result first.
1390 __ Uxtb(temp1, temp4);
1391 __ Sub(out, temp1.W(), Operand(temp2.W(), UXTB));
1392 __ Tbz(temp3, 0u, &end); // If actually compressed, we're done.
jessicahandojo05765752016-09-09 19:01:32 -07001393 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001394 __ Uxth(temp4, temp4);
1395 __ Sub(out, temp4.W(), Operand(temp2.W(), UXTH));
jessicahandojo05765752016-09-09 19:01:32 -07001396
1397 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001398 __ B(&end);
1399 __ Bind(&different_compression);
1400
1401 // Comparison for different compression style.
jessicahandojo05765752016-09-09 19:01:32 -07001402 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
1403 DCHECK_EQ(c_char_size, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001404 temp1 = temp1.W();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001405 temp2 = temp2.W();
1406 temp4 = temp4.W();
jessicahandojo05765752016-09-09 19:01:32 -07001407
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001408 // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
1409 // Note that flags have been set by the `str` compression flag extraction to `temp3`
1410 // before branching to the `different_compression` label.
1411 __ Csel(temp1, str, arg, eq); // Pointer to the compressed string.
1412 __ Csel(temp2, str, arg, ne); // Pointer to the uncompressed string.
jessicahandojo05765752016-09-09 19:01:32 -07001413
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001414 // We want to free up the temp3, currently holding `str` compression flag, for comparison.
1415 // So, we move it to the bottom bit of the iteration count `temp0` which we then need to treat
1416 // as unsigned. Start by freeing the bit with a LSL and continue further down by a SUB which
1417 // will allow `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
1418 __ Lsl(temp0, temp0, 1u);
1419
1420 // Adjust temp1 and temp2 from string pointers to data pointers.
1421 __ Add(temp1, temp1, Operand(value_offset));
1422 __ Add(temp2, temp2, Operand(value_offset));
1423
1424 // Complete the move of the compression flag.
1425 __ Sub(temp0, temp0, Operand(temp3));
1426
1427 vixl::aarch64::Label different_compression_loop;
1428 vixl::aarch64::Label different_compression_diff;
1429
1430 __ Bind(&different_compression_loop);
1431 __ Ldrb(temp4, MemOperand(temp1.X(), c_char_size, PostIndex));
1432 __ Ldrh(temp3, MemOperand(temp2.X(), char_size, PostIndex));
1433 __ Subs(temp4, temp4, Operand(temp3));
1434 __ B(&different_compression_diff, ne);
1435 __ Subs(temp0, temp0, 2);
1436 __ B(&different_compression_loop, hi);
jessicahandojo05765752016-09-09 19:01:32 -07001437 __ B(&end);
1438
1439 // Calculate the difference.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001440 __ Bind(&different_compression_diff);
1441 __ Tst(temp0, Operand(1));
1442 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1443 "Expecting 0=compressed, 1=uncompressed");
1444 __ Cneg(out, temp4, ne);
jessicahandojo05765752016-09-09 19:01:32 -07001445 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001446
1447 __ Bind(&end);
1448
1449 if (can_slow_path) {
1450 __ Bind(slow_path->GetExitLabel());
1451 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001452}
1453
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001454// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
1455// The normal loop plus the pre-header is 9 instructions without string compression and 12
1456// instructions with string compression. We can compare up to 8 bytes in 4 instructions
1457// (LDR+LDR+CMP+BNE) and up to 16 bytes in 5 instructions (LDP+LDP+CMP+CCMP+BNE). Allow up
1458// to 10 instructions for the unrolled loop.
1459constexpr size_t kShortConstStringEqualsCutoffInBytes = 32;
1460
1461static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
1462 if (candidate->IsLoadString()) {
1463 HLoadString* load_string = candidate->AsLoadString();
1464 const DexFile& dex_file = load_string->GetDexFile();
1465 return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
1466 }
1467 return nullptr;
1468}
1469
Agi Csakiea34b402015-08-13 17:51:19 -07001470void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
1471 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1472 LocationSummary::kNoCall,
1473 kIntrinsified);
1474 locations->SetInAt(0, Location::RequiresRegister());
1475 locations->SetInAt(1, Location::RequiresRegister());
Agi Csakiea34b402015-08-13 17:51:19 -07001476
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001477 // For the generic implementation and for long const strings we need a temporary.
1478 // We do not need it for short const strings, up to 8 bytes, see code generation below.
1479 uint32_t const_string_length = 0u;
1480 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1481 if (const_string == nullptr) {
1482 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1483 }
1484 bool is_compressed =
1485 mirror::kUseStringCompression &&
1486 const_string != nullptr &&
1487 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1488 if (const_string == nullptr || const_string_length > (is_compressed ? 8u : 4u)) {
1489 locations->AddTemp(Location::RequiresRegister());
1490 }
1491
1492 // TODO: If the String.equals() is used only for an immediately following HIf, we can
1493 // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
1494 // Then we shall need an extra temporary register instead of the output register.
Agi Csakiea34b402015-08-13 17:51:19 -07001495 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1496}
1497
1498void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001499 MacroAssembler* masm = GetVIXLAssembler();
Agi Csakiea34b402015-08-13 17:51:19 -07001500 LocationSummary* locations = invoke->GetLocations();
1501
1502 Register str = WRegisterFrom(locations->InAt(0));
1503 Register arg = WRegisterFrom(locations->InAt(1));
1504 Register out = XRegisterFrom(locations->Out());
1505
1506 UseScratchRegisterScope scratch_scope(masm);
1507 Register temp = scratch_scope.AcquireW();
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001508 Register temp1 = scratch_scope.AcquireW();
Agi Csakiea34b402015-08-13 17:51:19 -07001509
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001510 vixl::aarch64::Label loop;
Scott Wakeling97c72b72016-06-24 16:19:36 +01001511 vixl::aarch64::Label end;
1512 vixl::aarch64::Label return_true;
1513 vixl::aarch64::Label return_false;
Agi Csakiea34b402015-08-13 17:51:19 -07001514
1515 // Get offsets of count, value, and class fields within a string object.
1516 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1517 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1518 const int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1519
1520 // Note that the null check must have been done earlier.
1521 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1522
Vladimir Marko53b52002016-05-24 19:30:45 +01001523 StringEqualsOptimizations optimizations(invoke);
1524 if (!optimizations.GetArgumentNotNull()) {
1525 // Check if input is null, return false if it is.
1526 __ Cbz(arg, &return_false);
1527 }
Agi Csakiea34b402015-08-13 17:51:19 -07001528
1529 // Reference equality check, return true if same reference.
1530 __ Cmp(str, arg);
1531 __ B(&return_true, eq);
1532
Vladimir Marko53b52002016-05-24 19:30:45 +01001533 if (!optimizations.GetArgumentIsString()) {
1534 // Instanceof check for the argument by comparing class fields.
1535 // All string objects must have the same type since String cannot be subclassed.
1536 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1537 // If the argument is a string object, its class field must be equal to receiver's class field.
1538 __ Ldr(temp, MemOperand(str.X(), class_offset));
1539 __ Ldr(temp1, MemOperand(arg.X(), class_offset));
1540 __ Cmp(temp, temp1);
1541 __ B(&return_false, ne);
1542 }
Agi Csakiea34b402015-08-13 17:51:19 -07001543
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001544 // Check if one of the inputs is a const string. Do not special-case both strings
1545 // being const, such cases should be handled by constant folding if needed.
1546 uint32_t const_string_length = 0u;
1547 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1548 if (const_string == nullptr) {
1549 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1550 if (const_string != nullptr) {
1551 std::swap(str, arg); // Make sure the const string is in `str`.
1552 }
1553 }
1554 bool is_compressed =
1555 mirror::kUseStringCompression &&
1556 const_string != nullptr &&
1557 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1558
1559 if (const_string != nullptr) {
1560 // Load `count` field of the argument string and check if it matches the const string.
1561 // Also compares the compression style, if differs return false.
1562 __ Ldr(temp, MemOperand(arg.X(), count_offset));
Vladimir Marko26ec3ca2017-03-14 13:37:14 +00001563 // Temporarily release temp1 as we may not be able to embed the flagged count in CMP immediate.
1564 scratch_scope.Release(temp1);
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001565 __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
Vladimir Marko26ec3ca2017-03-14 13:37:14 +00001566 temp1 = scratch_scope.AcquireW();
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001567 __ B(&return_false, ne);
1568 } else {
1569 // Load `count` fields of this and argument strings.
1570 __ Ldr(temp, MemOperand(str.X(), count_offset));
1571 __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1572 // Check if `count` fields are equal, return false if they're not.
1573 // Also compares the compression style, if differs return false.
1574 __ Cmp(temp, temp1);
1575 __ B(&return_false, ne);
1576 }
Agi Csakiea34b402015-08-13 17:51:19 -07001577
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001578 // Assertions that must hold in order to compare strings 8 bytes at a time.
Agi Csakiea34b402015-08-13 17:51:19 -07001579 DCHECK_ALIGNED(value_offset, 8);
1580 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1581
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001582 if (const_string != nullptr &&
1583 const_string_length < (is_compressed ? kShortConstStringEqualsCutoffInBytes
1584 : kShortConstStringEqualsCutoffInBytes / 2u)) {
1585 // Load and compare the contents. Though we know the contents of the short const string
1586 // at compile time, materializing constants may be more code than loading from memory.
1587 int32_t offset = value_offset;
1588 size_t remaining_bytes =
1589 RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u);
1590 temp = temp.X();
1591 temp1 = temp1.X();
1592 while (remaining_bytes > 8u) {
1593 Register temp2 = XRegisterFrom(locations->GetTemp(0));
1594 __ Ldp(temp, temp1, MemOperand(str.X(), offset));
1595 __ Ldp(temp2, out, MemOperand(arg.X(), offset));
1596 __ Cmp(temp, temp2);
1597 __ Ccmp(temp1, out, NoFlag, eq);
1598 __ B(&return_false, ne);
1599 offset += 2u * sizeof(uint64_t);
1600 remaining_bytes -= 2u * sizeof(uint64_t);
1601 }
1602 if (remaining_bytes != 0u) {
1603 __ Ldr(temp, MemOperand(str.X(), offset));
1604 __ Ldr(temp1, MemOperand(arg.X(), offset));
1605 __ Cmp(temp, temp1);
1606 __ B(&return_false, ne);
1607 }
1608 } else {
1609 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
1610 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1611 "Expecting 0=compressed, 1=uncompressed");
1612 __ Cbz(temp, &return_true);
1613
1614 if (mirror::kUseStringCompression) {
1615 // For string compression, calculate the number of bytes to compare (not chars).
1616 // This could in theory exceed INT32_MAX, so treat temp as unsigned.
1617 __ And(temp1, temp, Operand(1)); // Extract compression flag.
1618 __ Lsr(temp, temp, 1u); // Extract length.
1619 __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare.
1620 }
1621
1622 // Store offset of string value in preparation for comparison loop
1623 __ Mov(temp1, value_offset);
1624
1625 temp1 = temp1.X();
1626 Register temp2 = XRegisterFrom(locations->GetTemp(0));
1627 // Loop to compare strings 8 bytes at a time starting at the front of the string.
1628 // Ok to do this because strings are zero-padded to kObjectAlignment.
1629 __ Bind(&loop);
1630 __ Ldr(out, MemOperand(str.X(), temp1));
1631 __ Ldr(temp2, MemOperand(arg.X(), temp1));
1632 __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
1633 __ Cmp(out, temp2);
1634 __ B(&return_false, ne);
1635 // With string compression, we have compared 8 bytes, otherwise 4 chars.
1636 __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags);
1637 __ B(&loop, hi);
jessicahandojo05765752016-09-09 19:01:32 -07001638 }
1639
Agi Csakiea34b402015-08-13 17:51:19 -07001640 // Return true and exit the function.
1641 // If loop does not result in returning false, we return true.
1642 __ Bind(&return_true);
1643 __ Mov(out, 1);
1644 __ B(&end);
1645
1646 // Return false and exit the function.
1647 __ Bind(&return_false);
1648 __ Mov(out, 0);
1649 __ Bind(&end);
1650}
1651
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001652static void GenerateVisitStringIndexOf(HInvoke* invoke,
Scott Wakeling97c72b72016-06-24 16:19:36 +01001653 MacroAssembler* masm,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001654 CodeGeneratorARM64* codegen,
1655 ArenaAllocator* allocator,
1656 bool start_at_zero) {
1657 LocationSummary* locations = invoke->GetLocations();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001658
1659 // Note that the null check must have been done earlier.
1660 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1661
1662 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001663 // 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 -07001664 SlowPathCodeARM64* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001665 HInstruction* code_point = invoke->InputAt(1);
1666 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01001667 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > 0xFFFFU) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001668 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1669 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1670 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1671 codegen->AddSlowPath(slow_path);
1672 __ B(slow_path->GetEntryLabel());
1673 __ Bind(slow_path->GetExitLabel());
1674 return;
1675 }
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001676 } else if (code_point->GetType() != Primitive::kPrimChar) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001677 Register char_reg = WRegisterFrom(locations->InAt(1));
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001678 __ Tst(char_reg, 0xFFFF0000);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001679 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1680 codegen->AddSlowPath(slow_path);
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001681 __ B(ne, slow_path->GetEntryLabel());
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001682 }
1683
1684 if (start_at_zero) {
1685 // Start-index = 0.
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001686 Register tmp_reg = WRegisterFrom(locations->GetTemp(0));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001687 __ Mov(tmp_reg, 0);
1688 }
1689
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001690 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
Roland Levillain42ad2882016-02-29 18:26:54 +00001691 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001692
1693 if (slow_path != nullptr) {
1694 __ Bind(slow_path->GetExitLabel());
1695 }
1696}
1697
1698void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) {
1699 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001700 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001701 kIntrinsified);
1702 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1703 // best to align the inputs accordingly.
1704 InvokeRuntimeCallingConvention calling_convention;
1705 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1706 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1707 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1708
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001709 // Need to send start_index=0.
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001710 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1711}
1712
1713void IntrinsicCodeGeneratorARM64::VisitStringIndexOf(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001714 GenerateVisitStringIndexOf(
1715 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001716}
1717
1718void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
1719 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001720 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001721 kIntrinsified);
1722 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1723 // best to align the inputs accordingly.
1724 InvokeRuntimeCallingConvention calling_convention;
1725 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1726 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1727 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1728 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001729}
1730
1731void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001732 GenerateVisitStringIndexOf(
1733 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001734}
1735
Jeff Hao848f70a2014-01-15 13:49:50 -08001736void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1737 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001738 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001739 kIntrinsified);
1740 InvokeRuntimeCallingConvention calling_convention;
1741 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1742 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1743 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1744 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1745 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1746}
1747
1748void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001749 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001750 LocationSummary* locations = invoke->GetLocations();
1751
1752 Register byte_array = WRegisterFrom(locations->InAt(0));
1753 __ Cmp(byte_array, 0);
1754 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1755 codegen_->AddSlowPath(slow_path);
1756 __ B(eq, slow_path->GetEntryLabel());
1757
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001758 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001759 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001760 __ Bind(slow_path->GetExitLabel());
1761}
1762
1763void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
1764 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001765 LocationSummary::kCallOnMainOnly,
Jeff Hao848f70a2014-01-15 13:49:50 -08001766 kIntrinsified);
1767 InvokeRuntimeCallingConvention calling_convention;
1768 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1769 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1770 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1771 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1772}
1773
1774void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
Roland Levillaincc3839c2016-02-29 16:23:48 +00001775 // No need to emit code checking whether `locations->InAt(2)` is a null
1776 // pointer, as callers of the native method
1777 //
1778 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1779 //
1780 // all include a null check on `data` before calling that method.
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001781 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
Roland Levillainf969a202016-03-09 16:14:00 +00001782 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001783}
1784
1785void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Jeff Hao848f70a2014-01-15 13:49:50 -08001786 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001787 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001788 kIntrinsified);
1789 InvokeRuntimeCallingConvention calling_convention;
1790 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Jeff Hao848f70a2014-01-15 13:49:50 -08001791 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1792}
1793
1794void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001795 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001796 LocationSummary* locations = invoke->GetLocations();
1797
1798 Register string_to_copy = WRegisterFrom(locations->InAt(0));
1799 __ Cmp(string_to_copy, 0);
1800 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1801 codegen_->AddSlowPath(slow_path);
1802 __ B(eq, slow_path->GetEntryLabel());
1803
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001804 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001805 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001806 __ Bind(slow_path->GetExitLabel());
1807}
1808
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001809static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1810 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
1811 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1812 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1813
1814 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001815 LocationSummary::kCallOnMainOnly,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001816 kIntrinsified);
1817 InvokeRuntimeCallingConvention calling_convention;
1818
1819 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1820 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1821}
1822
1823static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1824 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
1825 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1826 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(1)->GetType()));
1827 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1828
1829 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001830 LocationSummary::kCallOnMainOnly,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001831 kIntrinsified);
1832 InvokeRuntimeCallingConvention calling_convention;
1833
1834 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1835 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
1836 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1837}
1838
1839static void GenFPToFPCall(HInvoke* invoke,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001840 CodeGeneratorARM64* codegen,
1841 QuickEntrypointEnum entry) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001842 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001843}
1844
1845void IntrinsicLocationsBuilderARM64::VisitMathCos(HInvoke* invoke) {
1846 CreateFPToFPCallLocations(arena_, invoke);
1847}
1848
1849void IntrinsicCodeGeneratorARM64::VisitMathCos(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001850 GenFPToFPCall(invoke, codegen_, kQuickCos);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001851}
1852
1853void IntrinsicLocationsBuilderARM64::VisitMathSin(HInvoke* invoke) {
1854 CreateFPToFPCallLocations(arena_, invoke);
1855}
1856
1857void IntrinsicCodeGeneratorARM64::VisitMathSin(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001858 GenFPToFPCall(invoke, codegen_, kQuickSin);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001859}
1860
1861void IntrinsicLocationsBuilderARM64::VisitMathAcos(HInvoke* invoke) {
1862 CreateFPToFPCallLocations(arena_, invoke);
1863}
1864
1865void IntrinsicCodeGeneratorARM64::VisitMathAcos(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001866 GenFPToFPCall(invoke, codegen_, kQuickAcos);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001867}
1868
1869void IntrinsicLocationsBuilderARM64::VisitMathAsin(HInvoke* invoke) {
1870 CreateFPToFPCallLocations(arena_, invoke);
1871}
1872
1873void IntrinsicCodeGeneratorARM64::VisitMathAsin(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001874 GenFPToFPCall(invoke, codegen_, kQuickAsin);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001875}
1876
1877void IntrinsicLocationsBuilderARM64::VisitMathAtan(HInvoke* invoke) {
1878 CreateFPToFPCallLocations(arena_, invoke);
1879}
1880
1881void IntrinsicCodeGeneratorARM64::VisitMathAtan(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001882 GenFPToFPCall(invoke, codegen_, kQuickAtan);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001883}
1884
1885void IntrinsicLocationsBuilderARM64::VisitMathCbrt(HInvoke* invoke) {
1886 CreateFPToFPCallLocations(arena_, invoke);
1887}
1888
1889void IntrinsicCodeGeneratorARM64::VisitMathCbrt(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001890 GenFPToFPCall(invoke, codegen_, kQuickCbrt);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001891}
1892
1893void IntrinsicLocationsBuilderARM64::VisitMathCosh(HInvoke* invoke) {
1894 CreateFPToFPCallLocations(arena_, invoke);
1895}
1896
1897void IntrinsicCodeGeneratorARM64::VisitMathCosh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001898 GenFPToFPCall(invoke, codegen_, kQuickCosh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001899}
1900
1901void IntrinsicLocationsBuilderARM64::VisitMathExp(HInvoke* invoke) {
1902 CreateFPToFPCallLocations(arena_, invoke);
1903}
1904
1905void IntrinsicCodeGeneratorARM64::VisitMathExp(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001906 GenFPToFPCall(invoke, codegen_, kQuickExp);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001907}
1908
1909void IntrinsicLocationsBuilderARM64::VisitMathExpm1(HInvoke* invoke) {
1910 CreateFPToFPCallLocations(arena_, invoke);
1911}
1912
1913void IntrinsicCodeGeneratorARM64::VisitMathExpm1(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001914 GenFPToFPCall(invoke, codegen_, kQuickExpm1);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001915}
1916
1917void IntrinsicLocationsBuilderARM64::VisitMathLog(HInvoke* invoke) {
1918 CreateFPToFPCallLocations(arena_, invoke);
1919}
1920
1921void IntrinsicCodeGeneratorARM64::VisitMathLog(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001922 GenFPToFPCall(invoke, codegen_, kQuickLog);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001923}
1924
1925void IntrinsicLocationsBuilderARM64::VisitMathLog10(HInvoke* invoke) {
1926 CreateFPToFPCallLocations(arena_, invoke);
1927}
1928
1929void IntrinsicCodeGeneratorARM64::VisitMathLog10(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001930 GenFPToFPCall(invoke, codegen_, kQuickLog10);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001931}
1932
1933void IntrinsicLocationsBuilderARM64::VisitMathSinh(HInvoke* invoke) {
1934 CreateFPToFPCallLocations(arena_, invoke);
1935}
1936
1937void IntrinsicCodeGeneratorARM64::VisitMathSinh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001938 GenFPToFPCall(invoke, codegen_, kQuickSinh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001939}
1940
1941void IntrinsicLocationsBuilderARM64::VisitMathTan(HInvoke* invoke) {
1942 CreateFPToFPCallLocations(arena_, invoke);
1943}
1944
1945void IntrinsicCodeGeneratorARM64::VisitMathTan(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001946 GenFPToFPCall(invoke, codegen_, kQuickTan);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001947}
1948
1949void IntrinsicLocationsBuilderARM64::VisitMathTanh(HInvoke* invoke) {
1950 CreateFPToFPCallLocations(arena_, invoke);
1951}
1952
1953void IntrinsicCodeGeneratorARM64::VisitMathTanh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001954 GenFPToFPCall(invoke, codegen_, kQuickTanh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001955}
1956
1957void IntrinsicLocationsBuilderARM64::VisitMathAtan2(HInvoke* invoke) {
1958 CreateFPFPToFPCallLocations(arena_, invoke);
1959}
1960
1961void IntrinsicCodeGeneratorARM64::VisitMathAtan2(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001962 GenFPToFPCall(invoke, codegen_, kQuickAtan2);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001963}
1964
1965void IntrinsicLocationsBuilderARM64::VisitMathHypot(HInvoke* invoke) {
1966 CreateFPFPToFPCallLocations(arena_, invoke);
1967}
1968
1969void IntrinsicCodeGeneratorARM64::VisitMathHypot(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001970 GenFPToFPCall(invoke, codegen_, kQuickHypot);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001971}
1972
1973void IntrinsicLocationsBuilderARM64::VisitMathNextAfter(HInvoke* invoke) {
1974 CreateFPFPToFPCallLocations(arena_, invoke);
1975}
1976
1977void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001978 GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001979}
1980
Tim Zhang25abd6c2016-01-19 23:39:24 +08001981void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1982 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1983 LocationSummary::kNoCall,
1984 kIntrinsified);
1985 locations->SetInAt(0, Location::RequiresRegister());
1986 locations->SetInAt(1, Location::RequiresRegister());
1987 locations->SetInAt(2, Location::RequiresRegister());
1988 locations->SetInAt(3, Location::RequiresRegister());
1989 locations->SetInAt(4, Location::RequiresRegister());
1990
1991 locations->AddTemp(Location::RequiresRegister());
1992 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingdf109d92016-04-22 11:35:56 +01001993 locations->AddTemp(Location::RequiresRegister());
Tim Zhang25abd6c2016-01-19 23:39:24 +08001994}
1995
1996void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001997 MacroAssembler* masm = GetVIXLAssembler();
Tim Zhang25abd6c2016-01-19 23:39:24 +08001998 LocationSummary* locations = invoke->GetLocations();
1999
2000 // Check assumption that sizeof(Char) is 2 (used in scaling below).
2001 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2002 DCHECK_EQ(char_size, 2u);
2003
2004 // Location of data in char array buffer.
2005 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2006
2007 // Location of char array data in string.
2008 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2009
2010 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2011 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2012 Register srcObj = XRegisterFrom(locations->InAt(0));
2013 Register srcBegin = XRegisterFrom(locations->InAt(1));
2014 Register srcEnd = XRegisterFrom(locations->InAt(2));
2015 Register dstObj = XRegisterFrom(locations->InAt(3));
2016 Register dstBegin = XRegisterFrom(locations->InAt(4));
2017
2018 Register src_ptr = XRegisterFrom(locations->GetTemp(0));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002019 Register num_chr = XRegisterFrom(locations->GetTemp(1));
2020 Register tmp1 = XRegisterFrom(locations->GetTemp(2));
Tim Zhang25abd6c2016-01-19 23:39:24 +08002021
2022 UseScratchRegisterScope temps(masm);
2023 Register dst_ptr = temps.AcquireX();
Scott Wakelingdf109d92016-04-22 11:35:56 +01002024 Register tmp2 = temps.AcquireX();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002025
jessicahandojo05765752016-09-09 19:01:32 -07002026 vixl::aarch64::Label done;
2027 vixl::aarch64::Label compressed_string_loop;
2028 __ Sub(num_chr, srcEnd, srcBegin);
2029 // Early out for valid zero-length retrievals.
2030 __ Cbz(num_chr, &done);
Tim Zhang25abd6c2016-01-19 23:39:24 +08002031
Scott Wakelingdf109d92016-04-22 11:35:56 +01002032 // dst address start to copy to.
Tim Zhang25abd6c2016-01-19 23:39:24 +08002033 __ Add(dst_ptr, dstObj, Operand(data_offset));
2034 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
2035
jessicahandojo05765752016-09-09 19:01:32 -07002036 // src address to copy from.
2037 __ Add(src_ptr, srcObj, Operand(value_offset));
2038 vixl::aarch64::Label compressed_string_preloop;
2039 if (mirror::kUseStringCompression) {
2040 // Location of count in string.
2041 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2042 // String's length.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002043 __ Ldr(tmp2, MemOperand(srcObj, count_offset));
2044 __ Tbz(tmp2, 0, &compressed_string_preloop);
jessicahandojo05765752016-09-09 19:01:32 -07002045 }
2046 __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002047
Tim Zhang25abd6c2016-01-19 23:39:24 +08002048 // Do the copy.
Scott Wakeling97c72b72016-06-24 16:19:36 +01002049 vixl::aarch64::Label loop;
Scott Wakeling97c72b72016-06-24 16:19:36 +01002050 vixl::aarch64::Label remainder;
Scott Wakelingdf109d92016-04-22 11:35:56 +01002051
Scott Wakelingdf109d92016-04-22 11:35:56 +01002052 // Save repairing the value of num_chr on the < 8 character path.
2053 __ Subs(tmp1, num_chr, 8);
2054 __ B(lt, &remainder);
2055
2056 // Keep the result of the earlier subs, we are going to fetch at least 8 characters.
2057 __ Mov(num_chr, tmp1);
2058
2059 // Main loop used for longer fetches loads and stores 8x16-bit characters at a time.
2060 // (Unaligned addresses are acceptable here and not worth inlining extra code to rectify.)
Tim Zhang25abd6c2016-01-19 23:39:24 +08002061 __ Bind(&loop);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002062 __ Ldp(tmp1, tmp2, MemOperand(src_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002063 __ Subs(num_chr, num_chr, 8);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002064 __ Stp(tmp1, tmp2, MemOperand(dst_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002065 __ B(ge, &loop);
2066
2067 __ Adds(num_chr, num_chr, 8);
2068 __ B(eq, &done);
2069
2070 // Main loop for < 8 character case and remainder handling. Loads and stores one
2071 // 16-bit Java character at a time.
2072 __ Bind(&remainder);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002073 __ Ldrh(tmp1, MemOperand(src_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002074 __ Subs(num_chr, num_chr, 1);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002075 __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002076 __ B(gt, &remainder);
jessicahandojo05765752016-09-09 19:01:32 -07002077 __ B(&done);
2078
2079 if (mirror::kUseStringCompression) {
2080 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
2081 DCHECK_EQ(c_char_size, 1u);
2082 __ Bind(&compressed_string_preloop);
2083 __ Add(src_ptr, src_ptr, Operand(srcBegin));
2084 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2085 __ Bind(&compressed_string_loop);
2086 __ Ldrb(tmp1, MemOperand(src_ptr, c_char_size, PostIndex));
2087 __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
2088 __ Subs(num_chr, num_chr, Operand(1));
2089 __ B(gt, &compressed_string_loop);
2090 }
Scott Wakelingdf109d92016-04-22 11:35:56 +01002091
Tim Zhang25abd6c2016-01-19 23:39:24 +08002092 __ Bind(&done);
2093}
2094
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002095// Mirrors ARRAYCOPY_SHORT_CHAR_ARRAY_THRESHOLD in libcore, so we can choose to use the native
2096// implementation there for longer copy lengths.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002097static constexpr int32_t kSystemArrayCopyCharThreshold = 32;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002098
2099static void SetSystemArrayCopyLocationRequires(LocationSummary* locations,
2100 uint32_t at,
2101 HInstruction* input) {
2102 HIntConstant* const_input = input->AsIntConstant();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002103 if (const_input != nullptr && !vixl::aarch64::Assembler::IsImmAddSub(const_input->GetValue())) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002104 locations->SetInAt(at, Location::RequiresRegister());
2105 } else {
2106 locations->SetInAt(at, Location::RegisterOrConstant(input));
2107 }
2108}
2109
2110void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
2111 // Check to see if we have known failures that will cause us to have to bail out
2112 // to the runtime, and just generate the runtime call directly.
2113 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2114 HIntConstant* dst_pos = invoke->InputAt(3)->AsIntConstant();
2115
2116 // The positions must be non-negative.
2117 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2118 (dst_pos != nullptr && dst_pos->GetValue() < 0)) {
2119 // We will have to fail anyways.
2120 return;
2121 }
2122
2123 // The length must be >= 0 and not so long that we would (currently) prefer libcore's
2124 // native implementation.
2125 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2126 if (length != nullptr) {
2127 int32_t len = length->GetValue();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002128 if (len < 0 || len > kSystemArrayCopyCharThreshold) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002129 // Just call as normal.
2130 return;
2131 }
2132 }
2133
2134 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2135 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2136 LocationSummary::kCallOnSlowPath,
2137 kIntrinsified);
2138 // arraycopy(char[] src, int src_pos, char[] dst, int dst_pos, int length).
2139 locations->SetInAt(0, Location::RequiresRegister());
2140 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2141 locations->SetInAt(2, Location::RequiresRegister());
2142 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2143 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2144
2145 locations->AddTemp(Location::RequiresRegister());
2146 locations->AddTemp(Location::RequiresRegister());
2147 locations->AddTemp(Location::RequiresRegister());
2148}
2149
Scott Wakeling97c72b72016-06-24 16:19:36 +01002150static void CheckSystemArrayCopyPosition(MacroAssembler* masm,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002151 const Location& pos,
2152 const Register& input,
2153 const Location& length,
2154 SlowPathCodeARM64* slow_path,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002155 const Register& temp,
2156 bool length_is_input_length = false) {
2157 const int32_t length_offset = mirror::Array::LengthOffset().Int32Value();
2158 if (pos.IsConstant()) {
2159 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
2160 if (pos_const == 0) {
2161 if (!length_is_input_length) {
2162 // Check that length(input) >= length.
2163 __ Ldr(temp, MemOperand(input, length_offset));
2164 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
2165 __ B(slow_path->GetEntryLabel(), lt);
2166 }
2167 } else {
2168 // Check that length(input) >= pos.
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002169 __ Ldr(temp, MemOperand(input, length_offset));
2170 __ Subs(temp, temp, pos_const);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002171 __ B(slow_path->GetEntryLabel(), lt);
2172
2173 // Check that (length(input) - pos) >= length.
2174 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
2175 __ B(slow_path->GetEntryLabel(), lt);
2176 }
2177 } else if (length_is_input_length) {
2178 // The only way the copy can succeed is if pos is zero.
2179 __ Cbnz(WRegisterFrom(pos), slow_path->GetEntryLabel());
2180 } else {
2181 // Check that pos >= 0.
2182 Register pos_reg = WRegisterFrom(pos);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002183 __ Tbnz(pos_reg, pos_reg.GetSizeInBits() - 1, slow_path->GetEntryLabel());
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002184
2185 // Check that pos <= length(input) && (length(input) - pos) >= length.
2186 __ Ldr(temp, MemOperand(input, length_offset));
2187 __ Subs(temp, temp, pos_reg);
2188 // Ccmp if length(input) >= pos, else definitely bail to slow path (N!=V == lt).
2189 __ Ccmp(temp, OperandFrom(length, Primitive::kPrimInt), NFlag, ge);
2190 __ B(slow_path->GetEntryLabel(), lt);
2191 }
2192}
2193
2194// Compute base source address, base destination address, and end source address
2195// for System.arraycopy* intrinsics.
Scott Wakeling97c72b72016-06-24 16:19:36 +01002196static void GenSystemArrayCopyAddresses(MacroAssembler* masm,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002197 Primitive::Type type,
2198 const Register& src,
2199 const Location& src_pos,
2200 const Register& dst,
2201 const Location& dst_pos,
2202 const Location& copy_length,
2203 const Register& src_base,
2204 const Register& dst_base,
2205 const Register& src_end) {
2206 DCHECK(type == Primitive::kPrimNot || type == Primitive::kPrimChar)
Roland Levillainebea3d22016-04-12 15:42:57 +01002207 << "Unexpected element type: " << type;
2208 const int32_t element_size = Primitive::ComponentSize(type);
2209 const int32_t element_size_shift = Primitive::ComponentSizeShift(type);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002210
Roland Levillainebea3d22016-04-12 15:42:57 +01002211 uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002212 if (src_pos.IsConstant()) {
2213 int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002214 __ Add(src_base, src, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002215 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002216 __ Add(src_base, src, data_offset);
2217 __ Add(src_base, src_base, Operand(XRegisterFrom(src_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002218 }
2219
2220 if (dst_pos.IsConstant()) {
2221 int32_t constant = dst_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002222 __ Add(dst_base, dst, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002223 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002224 __ Add(dst_base, dst, data_offset);
2225 __ Add(dst_base, dst_base, Operand(XRegisterFrom(dst_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002226 }
2227
2228 if (copy_length.IsConstant()) {
2229 int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002230 __ Add(src_end, src_base, element_size * constant);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002231 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002232 __ Add(src_end, src_base, Operand(XRegisterFrom(copy_length), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002233 }
2234}
2235
2236void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01002237 MacroAssembler* masm = GetVIXLAssembler();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002238 LocationSummary* locations = invoke->GetLocations();
2239 Register src = XRegisterFrom(locations->InAt(0));
2240 Location src_pos = locations->InAt(1);
2241 Register dst = XRegisterFrom(locations->InAt(2));
2242 Location dst_pos = locations->InAt(3);
2243 Location length = locations->InAt(4);
2244
2245 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2246 codegen_->AddSlowPath(slow_path);
2247
2248 // If source and destination are the same, take the slow path. Overlapping copy regions must be
2249 // copied in reverse and we can't know in all cases if it's needed.
2250 __ Cmp(src, dst);
2251 __ B(slow_path->GetEntryLabel(), eq);
2252
2253 // Bail out if the source is null.
2254 __ Cbz(src, slow_path->GetEntryLabel());
2255
2256 // Bail out if the destination is null.
2257 __ Cbz(dst, slow_path->GetEntryLabel());
2258
2259 if (!length.IsConstant()) {
Vladimir Markoc5646202016-11-28 16:03:15 +00002260 // Merge the following two comparisons into one:
2261 // If the length is negative, bail out (delegate to libcore's native implementation).
2262 // If the length > 32 then (currently) prefer libcore's native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002263 __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold);
Vladimir Markoc5646202016-11-28 16:03:15 +00002264 __ B(slow_path->GetEntryLabel(), hi);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002265 } else {
2266 // We have already checked in the LocationsBuilder for the constant case.
2267 DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0);
2268 DCHECK_LE(length.GetConstant()->AsIntConstant()->GetValue(), 32);
2269 }
2270
2271 Register src_curr_addr = WRegisterFrom(locations->GetTemp(0));
2272 Register dst_curr_addr = WRegisterFrom(locations->GetTemp(1));
2273 Register src_stop_addr = WRegisterFrom(locations->GetTemp(2));
2274
2275 CheckSystemArrayCopyPosition(masm,
2276 src_pos,
2277 src,
2278 length,
2279 slow_path,
2280 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002281 false);
2282
2283 CheckSystemArrayCopyPosition(masm,
2284 dst_pos,
2285 dst,
2286 length,
2287 slow_path,
2288 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002289 false);
2290
2291 src_curr_addr = src_curr_addr.X();
2292 dst_curr_addr = dst_curr_addr.X();
2293 src_stop_addr = src_stop_addr.X();
2294
2295 GenSystemArrayCopyAddresses(masm,
2296 Primitive::kPrimChar,
2297 src,
2298 src_pos,
2299 dst,
2300 dst_pos,
2301 length,
2302 src_curr_addr,
2303 dst_curr_addr,
2304 src_stop_addr);
2305
2306 // Iterate over the arrays and do a raw copy of the chars.
2307 const int32_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2308 UseScratchRegisterScope temps(masm);
2309 Register tmp = temps.AcquireW();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002310 vixl::aarch64::Label loop, done;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002311 __ Bind(&loop);
2312 __ Cmp(src_curr_addr, src_stop_addr);
2313 __ B(&done, eq);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002314 __ Ldrh(tmp, MemOperand(src_curr_addr, char_size, PostIndex));
2315 __ Strh(tmp, MemOperand(dst_curr_addr, char_size, PostIndex));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002316 __ B(&loop);
2317 __ Bind(&done);
2318
2319 __ Bind(slow_path->GetExitLabel());
2320}
2321
donghui.baic2ec9ad2016-03-10 14:02:55 +08002322// We can choose to use the native implementation there for longer copy lengths.
2323static constexpr int32_t kSystemArrayCopyThreshold = 128;
2324
2325// CodeGenerator::CreateSystemArrayCopyLocationSummary use three temporary registers.
2326// We want to use two temporary registers in order to reduce the register pressure in arm64.
2327// So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary.
2328void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002329 // The only read barrier implementation supporting the
2330 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2331 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain3d312422016-06-23 13:53:42 +01002332 return;
2333 }
2334
donghui.baic2ec9ad2016-03-10 14:02:55 +08002335 // Check to see if we have known failures that will cause us to have to bail out
2336 // to the runtime, and just generate the runtime call directly.
2337 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2338 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
2339
2340 // The positions must be non-negative.
2341 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2342 (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
2343 // We will have to fail anyways.
2344 return;
2345 }
2346
2347 // The length must be >= 0.
2348 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2349 if (length != nullptr) {
2350 int32_t len = length->GetValue();
2351 if (len < 0 || len >= kSystemArrayCopyThreshold) {
2352 // Just call as normal.
2353 return;
2354 }
2355 }
2356
2357 SystemArrayCopyOptimizations optimizations(invoke);
2358
2359 if (optimizations.GetDestinationIsSource()) {
2360 if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) {
2361 // We only support backward copying if source and destination are the same.
2362 return;
2363 }
2364 }
2365
2366 if (optimizations.GetDestinationIsPrimitiveArray() || optimizations.GetSourceIsPrimitiveArray()) {
2367 // We currently don't intrinsify primitive copying.
2368 return;
2369 }
2370
2371 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2372 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2373 LocationSummary::kCallOnSlowPath,
2374 kIntrinsified);
2375 // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length).
2376 locations->SetInAt(0, Location::RequiresRegister());
2377 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2378 locations->SetInAt(2, Location::RequiresRegister());
2379 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2380 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2381
2382 locations->AddTemp(Location::RequiresRegister());
2383 locations->AddTemp(Location::RequiresRegister());
Roland Levillain0b671c02016-08-19 12:02:34 +01002384 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2385 // Temporary register IP0, obtained from the VIXL scratch register
2386 // pool, cannot be used in ReadBarrierSystemArrayCopySlowPathARM64
2387 // (because that register is clobbered by ReadBarrierMarkRegX
Roland Levillain54f869e2017-03-06 13:54:11 +00002388 // entry points). It cannot be used in calls to
2389 // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier
2390 // either. For these reasons, get a third extra temporary register
2391 // from the register allocator.
Roland Levillain0b671c02016-08-19 12:02:34 +01002392 locations->AddTemp(Location::RequiresRegister());
Roland Levillain54f869e2017-03-06 13:54:11 +00002393 } else {
2394 // Cases other than Baker read barriers: the third temporary will
2395 // be acquired from the VIXL scratch register pool.
Roland Levillain0b671c02016-08-19 12:02:34 +01002396 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002397}
2398
2399void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002400 // The only read barrier implementation supporting the
2401 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2402 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01002403
Scott Wakeling97c72b72016-06-24 16:19:36 +01002404 MacroAssembler* masm = GetVIXLAssembler();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002405 LocationSummary* locations = invoke->GetLocations();
2406
2407 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2408 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2409 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2410 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Roland Levillain0b671c02016-08-19 12:02:34 +01002411 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002412
2413 Register src = XRegisterFrom(locations->InAt(0));
2414 Location src_pos = locations->InAt(1);
2415 Register dest = XRegisterFrom(locations->InAt(2));
2416 Location dest_pos = locations->InAt(3);
2417 Location length = locations->InAt(4);
2418 Register temp1 = WRegisterFrom(locations->GetTemp(0));
Roland Levillain0b671c02016-08-19 12:02:34 +01002419 Location temp1_loc = LocationFrom(temp1);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002420 Register temp2 = WRegisterFrom(locations->GetTemp(1));
Roland Levillain0b671c02016-08-19 12:02:34 +01002421 Location temp2_loc = LocationFrom(temp2);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002422
Roland Levillain0b671c02016-08-19 12:02:34 +01002423 SlowPathCodeARM64* intrinsic_slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2424 codegen_->AddSlowPath(intrinsic_slow_path);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002425
Scott Wakeling97c72b72016-06-24 16:19:36 +01002426 vixl::aarch64::Label conditions_on_positions_validated;
donghui.baic2ec9ad2016-03-10 14:02:55 +08002427 SystemArrayCopyOptimizations optimizations(invoke);
2428
donghui.baic2ec9ad2016-03-10 14:02:55 +08002429 // If source and destination are the same, we go to slow path if we need to do
2430 // forward copying.
2431 if (src_pos.IsConstant()) {
2432 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
2433 if (dest_pos.IsConstant()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002434 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
2435 if (optimizations.GetDestinationIsSource()) {
2436 // Checked when building locations.
2437 DCHECK_GE(src_pos_constant, dest_pos_constant);
2438 } else if (src_pos_constant < dest_pos_constant) {
2439 __ Cmp(src, dest);
Roland Levillain0b671c02016-08-19 12:02:34 +01002440 __ B(intrinsic_slow_path->GetEntryLabel(), eq);
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002441 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002442 // Checked when building locations.
2443 DCHECK(!optimizations.GetDestinationIsSource()
2444 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
2445 } else {
2446 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002447 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002448 __ B(&conditions_on_positions_validated, ne);
2449 }
2450 __ Cmp(WRegisterFrom(dest_pos), src_pos_constant);
Roland Levillain0b671c02016-08-19 12:02:34 +01002451 __ B(intrinsic_slow_path->GetEntryLabel(), gt);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002452 }
2453 } else {
2454 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002455 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002456 __ B(&conditions_on_positions_validated, ne);
2457 }
2458 __ Cmp(RegisterFrom(src_pos, invoke->InputAt(1)->GetType()),
2459 OperandFrom(dest_pos, invoke->InputAt(3)->GetType()));
Roland Levillain0b671c02016-08-19 12:02:34 +01002460 __ B(intrinsic_slow_path->GetEntryLabel(), lt);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002461 }
2462
2463 __ Bind(&conditions_on_positions_validated);
2464
2465 if (!optimizations.GetSourceIsNotNull()) {
2466 // Bail out if the source is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01002467 __ Cbz(src, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002468 }
2469
2470 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
2471 // Bail out if the destination is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01002472 __ Cbz(dest, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002473 }
2474
2475 // We have already checked in the LocationsBuilder for the constant case.
2476 if (!length.IsConstant() &&
2477 !optimizations.GetCountIsSourceLength() &&
2478 !optimizations.GetCountIsDestinationLength()) {
Vladimir Markoc5646202016-11-28 16:03:15 +00002479 // Merge the following two comparisons into one:
2480 // If the length is negative, bail out (delegate to libcore's native implementation).
2481 // If the length >= 128 then (currently) prefer native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002482 __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold);
Vladimir Markoc5646202016-11-28 16:03:15 +00002483 __ B(intrinsic_slow_path->GetEntryLabel(), hs);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002484 }
2485 // Validity checks: source.
2486 CheckSystemArrayCopyPosition(masm,
2487 src_pos,
2488 src,
2489 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002490 intrinsic_slow_path,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002491 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002492 optimizations.GetCountIsSourceLength());
2493
2494 // Validity checks: dest.
2495 CheckSystemArrayCopyPosition(masm,
2496 dest_pos,
2497 dest,
2498 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002499 intrinsic_slow_path,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002500 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002501 optimizations.GetCountIsDestinationLength());
2502 {
2503 // We use a block to end the scratch scope before the write barrier, thus
2504 // freeing the temporary registers so they can be used in `MarkGCCard`.
2505 UseScratchRegisterScope temps(masm);
Roland Levillain54f869e2017-03-06 13:54:11 +00002506 Register temp3;
2507 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2508 temp3 = WRegisterFrom(locations->GetTemp(2));
2509 } else {
2510 temp3 = temps.AcquireW();
2511 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002512
donghui.baic2ec9ad2016-03-10 14:02:55 +08002513 if (!optimizations.GetDoesNotNeedTypeCheck()) {
2514 // Check whether all elements of the source array are assignable to the component
2515 // type of the destination array. We do two checks: the classes are the same,
2516 // or the destination is Object[]. If none of these checks succeed, we go to the
2517 // slow path.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002518
Roland Levillain0b671c02016-08-19 12:02:34 +01002519 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2520 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2521 // /* HeapReference<Class> */ temp1 = src->klass_
2522 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2523 temp1_loc,
2524 src.W(),
2525 class_offset,
2526 temp2,
2527 /* needs_null_check */ false,
2528 /* use_load_acquire */ false);
2529 // Bail out if the source is not a non primitive array.
2530 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2531 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2532 temp1_loc,
2533 temp1,
2534 component_offset,
2535 temp2,
2536 /* needs_null_check */ false,
2537 /* use_load_acquire */ false);
2538 __ Cbz(temp1, intrinsic_slow_path->GetEntryLabel());
2539 // If heap poisoning is enabled, `temp1` has been unpoisoned
2540 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2541 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
2542 __ Ldrh(temp1, HeapOperand(temp1, primitive_offset));
2543 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2544 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002545 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002546
2547 // /* HeapReference<Class> */ temp1 = dest->klass_
2548 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2549 temp1_loc,
2550 dest.W(),
2551 class_offset,
2552 temp2,
2553 /* needs_null_check */ false,
2554 /* use_load_acquire */ false);
2555
2556 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2557 // Bail out if the destination is not a non primitive array.
2558 //
2559 // Register `temp1` is not trashed by the read barrier emitted
2560 // by GenerateFieldLoadWithBakerReadBarrier below, as that
2561 // method produces a call to a ReadBarrierMarkRegX entry point,
2562 // which saves all potentially live registers, including
2563 // temporaries such a `temp1`.
2564 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2565 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2566 temp2_loc,
2567 temp1,
2568 component_offset,
2569 temp3,
2570 /* needs_null_check */ false,
2571 /* use_load_acquire */ false);
2572 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2573 // If heap poisoning is enabled, `temp2` has been unpoisoned
2574 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2575 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2576 __ Ldrh(temp2, HeapOperand(temp2, primitive_offset));
2577 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2578 __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
2579 }
2580
2581 // For the same reason given earlier, `temp1` is not trashed by the
2582 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
2583 // /* HeapReference<Class> */ temp2 = src->klass_
2584 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2585 temp2_loc,
2586 src.W(),
2587 class_offset,
2588 temp3,
2589 /* needs_null_check */ false,
2590 /* use_load_acquire */ false);
2591 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
2592 __ Cmp(temp1, temp2);
2593
2594 if (optimizations.GetDestinationIsTypedObjectArray()) {
2595 vixl::aarch64::Label do_copy;
2596 __ B(&do_copy, eq);
2597 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2598 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2599 temp1_loc,
2600 temp1,
2601 component_offset,
2602 temp2,
2603 /* needs_null_check */ false,
2604 /* use_load_acquire */ false);
2605 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2606 // We do not need to emit a read barrier for the following
2607 // heap reference load, as `temp1` is only used in a
2608 // comparison with null below, and this reference is not
2609 // kept afterwards.
2610 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2611 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
2612 __ Bind(&do_copy);
2613 } else {
2614 __ B(intrinsic_slow_path->GetEntryLabel(), ne);
2615 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002616 } else {
Roland Levillain0b671c02016-08-19 12:02:34 +01002617 // Non read barrier code.
2618
2619 // /* HeapReference<Class> */ temp1 = dest->klass_
2620 __ Ldr(temp1, MemOperand(dest, class_offset));
2621 // /* HeapReference<Class> */ temp2 = src->klass_
2622 __ Ldr(temp2, MemOperand(src, class_offset));
2623 bool did_unpoison = false;
2624 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2625 !optimizations.GetSourceIsNonPrimitiveArray()) {
2626 // One or two of the references need to be unpoisoned. Unpoison them
2627 // both to make the identity check valid.
2628 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2629 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2630 did_unpoison = true;
2631 }
2632
2633 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2634 // Bail out if the destination is not a non primitive array.
2635 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2636 __ Ldr(temp3, HeapOperand(temp1, component_offset));
2637 __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
2638 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2639 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2640 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2641 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2642 __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
2643 }
2644
2645 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2646 // Bail out if the source is not a non primitive array.
2647 // /* HeapReference<Class> */ temp3 = temp2->component_type_
2648 __ Ldr(temp3, HeapOperand(temp2, component_offset));
2649 __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
2650 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2651 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2652 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2653 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2654 __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
2655 }
2656
2657 __ Cmp(temp1, temp2);
2658
2659 if (optimizations.GetDestinationIsTypedObjectArray()) {
2660 vixl::aarch64::Label do_copy;
2661 __ B(&do_copy, eq);
2662 if (!did_unpoison) {
2663 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2664 }
2665 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2666 __ Ldr(temp1, HeapOperand(temp1, component_offset));
2667 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2668 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2669 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2670 // No need to unpoison the result, we're comparing against null.
2671 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
2672 __ Bind(&do_copy);
2673 } else {
2674 __ B(intrinsic_slow_path->GetEntryLabel(), ne);
2675 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002676 }
2677 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2678 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2679 // Bail out if the source is not a non primitive array.
Roland Levillain0b671c02016-08-19 12:02:34 +01002680 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2681 // /* HeapReference<Class> */ temp1 = src->klass_
2682 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2683 temp1_loc,
2684 src.W(),
2685 class_offset,
2686 temp2,
2687 /* needs_null_check */ false,
2688 /* use_load_acquire */ false);
2689 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2690 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2691 temp2_loc,
2692 temp1,
2693 component_offset,
2694 temp3,
2695 /* needs_null_check */ false,
2696 /* use_load_acquire */ false);
2697 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2698 // If heap poisoning is enabled, `temp2` has been unpoisoned
2699 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2700 } else {
2701 // /* HeapReference<Class> */ temp1 = src->klass_
2702 __ Ldr(temp1, HeapOperand(src.W(), class_offset));
2703 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2704 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2705 __ Ldr(temp2, HeapOperand(temp1, component_offset));
2706 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2707 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2708 }
2709 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2710 __ Ldrh(temp2, HeapOperand(temp2, primitive_offset));
donghui.baic2ec9ad2016-03-10 14:02:55 +08002711 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Roland Levillain0b671c02016-08-19 12:02:34 +01002712 __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002713 }
2714
2715 Register src_curr_addr = temp1.X();
2716 Register dst_curr_addr = temp2.X();
Roland Levillain54f869e2017-03-06 13:54:11 +00002717 Register src_stop_addr = temp3.X();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002718
2719 GenSystemArrayCopyAddresses(masm,
2720 Primitive::kPrimNot,
2721 src,
2722 src_pos,
2723 dest,
2724 dest_pos,
2725 length,
2726 src_curr_addr,
2727 dst_curr_addr,
2728 src_stop_addr);
2729
donghui.baic2ec9ad2016-03-10 14:02:55 +08002730 const int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
Roland Levillain0b671c02016-08-19 12:02:34 +01002731
2732 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Roland Levillainba650a42017-03-06 13:52:32 +00002733 // TODO: Also convert this intrinsic to the IsGcMarking strategy?
2734
Roland Levillain0b671c02016-08-19 12:02:34 +01002735 // SystemArrayCopy implementation for Baker read barriers (see
2736 // also CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier):
2737 //
2738 // if (src_ptr != end_ptr) {
2739 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
2740 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
Hiroshi Yamauchi12b58b22016-11-01 11:55:29 -07002741 // bool is_gray = (rb_state == ReadBarrier::GrayState());
Roland Levillain0b671c02016-08-19 12:02:34 +01002742 // if (is_gray) {
2743 // // Slow-path copy.
2744 // do {
2745 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
2746 // } while (src_ptr != end_ptr)
2747 // } else {
2748 // // Fast-path copy.
2749 // do {
2750 // *dest_ptr++ = *src_ptr++;
2751 // } while (src_ptr != end_ptr)
2752 // }
2753 // }
2754
2755 vixl::aarch64::Label loop, done;
2756
2757 // Don't enter copy loop if `length == 0`.
2758 __ Cmp(src_curr_addr, src_stop_addr);
2759 __ B(&done, eq);
2760
Roland Levillain0b671c02016-08-19 12:02:34 +01002761 // Make sure `tmp` is not IP0, as it is clobbered by
2762 // ReadBarrierMarkRegX entry points in
2763 // ReadBarrierSystemArrayCopySlowPathARM64.
Roland Levillain54f869e2017-03-06 13:54:11 +00002764 temps.Exclude(ip0);
2765 Register tmp = temps.AcquireW();
Roland Levillain0b671c02016-08-19 12:02:34 +01002766 DCHECK_NE(LocationFrom(tmp).reg(), IP0);
2767
2768 // /* int32_t */ monitor = src->monitor_
2769 __ Ldr(tmp, HeapOperand(src.W(), monitor_offset));
2770 // /* LockWord */ lock_word = LockWord(monitor)
2771 static_assert(sizeof(LockWord) == sizeof(int32_t),
2772 "art::LockWord and int32_t have different sizes.");
2773
2774 // Introduce a dependency on the lock_word including rb_state,
2775 // to prevent load-load reordering, and without using
2776 // a memory barrier (which would be more expensive).
2777 // `src` is unchanged by this operation, but its value now depends
2778 // on `tmp`.
2779 __ Add(src.X(), src.X(), Operand(tmp.X(), LSR, 32));
2780
2781 // Slow path used to copy array when `src` is gray.
2782 SlowPathCodeARM64* read_barrier_slow_path =
2783 new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathARM64(invoke, LocationFrom(tmp));
2784 codegen_->AddSlowPath(read_barrier_slow_path);
2785
2786 // Given the numeric representation, it's enough to check the low bit of the rb_state.
Hiroshi Yamauchi12b58b22016-11-01 11:55:29 -07002787 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
2788 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
Roland Levillain0b671c02016-08-19 12:02:34 +01002789 __ Tbnz(tmp, LockWord::kReadBarrierStateShift, read_barrier_slow_path->GetEntryLabel());
2790
2791 // Fast-path copy.
2792 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2793 // poison/unpoison.
2794 __ Bind(&loop);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002795 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
2796 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
Roland Levillain0b671c02016-08-19 12:02:34 +01002797 __ Cmp(src_curr_addr, src_stop_addr);
2798 __ B(&loop, ne);
2799
2800 __ Bind(read_barrier_slow_path->GetExitLabel());
2801 __ Bind(&done);
2802 } else {
2803 // Non read barrier code.
2804
2805 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2806 // poison/unpoison.
2807 vixl::aarch64::Label loop, done;
2808 __ Bind(&loop);
2809 __ Cmp(src_curr_addr, src_stop_addr);
2810 __ B(&done, eq);
2811 {
2812 Register tmp = temps.AcquireW();
2813 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
2814 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
2815 }
2816 __ B(&loop);
2817 __ Bind(&done);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002818 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002819 }
2820 // We only need one card marking on the destination array.
2821 codegen_->MarkGCCard(dest.W(), Register(), /* value_can_be_null */ false);
2822
Roland Levillain0b671c02016-08-19 12:02:34 +01002823 __ Bind(intrinsic_slow_path->GetExitLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002824}
2825
Anton Kirilova3ffea22016-04-07 17:02:37 +01002826static void GenIsInfinite(LocationSummary* locations,
2827 bool is64bit,
Scott Wakeling97c72b72016-06-24 16:19:36 +01002828 MacroAssembler* masm) {
Anton Kirilova3ffea22016-04-07 17:02:37 +01002829 Operand infinity;
2830 Register out;
2831
2832 if (is64bit) {
2833 infinity = kPositiveInfinityDouble;
2834 out = XRegisterFrom(locations->Out());
2835 } else {
2836 infinity = kPositiveInfinityFloat;
2837 out = WRegisterFrom(locations->Out());
2838 }
2839
Scott Wakeling97c72b72016-06-24 16:19:36 +01002840 const Register zero = vixl::aarch64::Assembler::AppropriateZeroRegFor(out);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002841
2842 MoveFPToInt(locations, is64bit, masm);
2843 __ Eor(out, out, infinity);
2844 // We don't care about the sign bit, so shift left.
2845 __ Cmp(zero, Operand(out, LSL, 1));
2846 __ Cset(out, eq);
2847}
2848
2849void IntrinsicLocationsBuilderARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2850 CreateFPToIntLocations(arena_, invoke);
2851}
2852
2853void IntrinsicCodeGeneratorARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2854 GenIsInfinite(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
2855}
2856
2857void IntrinsicLocationsBuilderARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2858 CreateFPToIntLocations(arena_, invoke);
2859}
2860
2861void IntrinsicCodeGeneratorARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2862 GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
2863}
2864
TatWai Chongd8c052a2016-11-02 16:12:48 +08002865void IntrinsicLocationsBuilderARM64::VisitReferenceGetReferent(HInvoke* invoke) {
2866 if (kEmitCompilerReadBarrier) {
2867 // Do not intrinsify this call with the read barrier configuration.
2868 return;
2869 }
2870 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2871 LocationSummary::kCallOnSlowPath,
2872 kIntrinsified);
2873 locations->SetInAt(0, Location::RequiresRegister());
2874 locations->SetOut(Location::SameAsFirstInput());
2875 locations->AddTemp(Location::RequiresRegister());
2876}
2877
2878void IntrinsicCodeGeneratorARM64::VisitReferenceGetReferent(HInvoke* invoke) {
2879 DCHECK(!kEmitCompilerReadBarrier);
2880 MacroAssembler* masm = GetVIXLAssembler();
2881 LocationSummary* locations = invoke->GetLocations();
2882
2883 Register obj = InputRegisterAt(invoke, 0);
2884 Register out = OutputRegister(invoke);
2885
2886 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2887 codegen_->AddSlowPath(slow_path);
2888
2889 // Load ArtMethod first.
2890 HInvokeStaticOrDirect* invoke_direct = invoke->AsInvokeStaticOrDirect();
2891 DCHECK(invoke_direct != nullptr);
2892 Register temp0 = XRegisterFrom(codegen_->GenerateCalleeMethodStaticOrDirectCall(
2893 invoke_direct, locations->GetTemp(0)));
2894
2895 // Now get declaring class.
2896 __ Ldr(temp0.W(), MemOperand(temp0, ArtMethod::DeclaringClassOffset().Int32Value()));
2897
2898 uint32_t slow_path_flag_offset = codegen_->GetReferenceSlowFlagOffset();
2899 uint32_t disable_flag_offset = codegen_->GetReferenceDisableFlagOffset();
2900 DCHECK_NE(slow_path_flag_offset, 0u);
2901 DCHECK_NE(disable_flag_offset, 0u);
2902 DCHECK_NE(slow_path_flag_offset, disable_flag_offset);
2903
2904 // Check static flags that prevent using intrinsic.
2905 if (slow_path_flag_offset == disable_flag_offset + 1) {
2906 // Load two adjacent flags in one 64-bit load.
2907 __ Ldr(temp0, MemOperand(temp0, disable_flag_offset));
2908 } else {
2909 UseScratchRegisterScope temps(masm);
2910 Register temp1 = temps.AcquireW();
2911 __ Ldr(temp1.W(), MemOperand(temp0, disable_flag_offset));
2912 __ Ldr(temp0.W(), MemOperand(temp0, slow_path_flag_offset));
2913 __ Orr(temp0, temp1, temp0);
2914 }
2915 __ Cbnz(temp0, slow_path->GetEntryLabel());
2916
Artem Serov914d7a82017-02-07 14:33:49 +00002917 {
2918 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
2919 vixl::EmissionCheckScope guard(codegen_->GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
2920 // Fast path.
2921 __ Ldr(out, HeapOperand(obj, mirror::Reference::ReferentOffset().Int32Value()));
2922 codegen_->MaybeRecordImplicitNullCheck(invoke);
2923 }
TatWai Chongd8c052a2016-11-02 16:12:48 +08002924 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(out);
2925 __ Bind(slow_path->GetExitLabel());
2926}
2927
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002928void IntrinsicLocationsBuilderARM64::VisitIntegerValueOf(HInvoke* invoke) {
2929 InvokeRuntimeCallingConvention calling_convention;
2930 IntrinsicVisitor::ComputeIntegerValueOfLocations(
2931 invoke,
2932 codegen_,
2933 calling_convention.GetReturnLocation(Primitive::kPrimNot),
2934 Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
2935}
2936
2937void IntrinsicCodeGeneratorARM64::VisitIntegerValueOf(HInvoke* invoke) {
2938 IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo();
2939 LocationSummary* locations = invoke->GetLocations();
2940 MacroAssembler* masm = GetVIXLAssembler();
2941
2942 Register out = RegisterFrom(locations->Out(), Primitive::kPrimNot);
2943 UseScratchRegisterScope temps(masm);
2944 Register temp = temps.AcquireW();
2945 InvokeRuntimeCallingConvention calling_convention;
2946 Register argument = calling_convention.GetRegisterAt(0);
2947 if (invoke->InputAt(0)->IsConstant()) {
2948 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
2949 if (value >= info.low && value <= info.high) {
2950 // Just embed the j.l.Integer in the code.
2951 ScopedObjectAccess soa(Thread::Current());
2952 mirror::Object* boxed = info.cache->Get(value + (-info.low));
2953 DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed));
2954 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(boxed));
2955 __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
2956 } else {
2957 // Allocate and initialize a new j.l.Integer.
2958 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
2959 // JIT object table.
2960 uint32_t address =
2961 dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2962 __ Ldr(argument.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
2963 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2964 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2965 __ Mov(temp.W(), value);
2966 __ Str(temp.W(), HeapOperand(out.W(), info.value_offset));
2967 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2968 // one.
2969 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2970 }
2971 } else {
2972 Register in = RegisterFrom(locations->InAt(0), Primitive::kPrimInt);
2973 // Check bounds of our cache.
2974 __ Add(out.W(), in.W(), -info.low);
2975 __ Cmp(out.W(), info.high - info.low + 1);
2976 vixl::aarch64::Label allocate, done;
2977 __ B(&allocate, hs);
2978 // If the value is within the bounds, load the j.l.Integer directly from the array.
2979 uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
2980 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.cache));
2981 __ Ldr(temp.W(), codegen_->DeduplicateBootImageAddressLiteral(data_offset + address));
2982 MemOperand source = HeapOperand(
2983 temp, out.X(), LSL, Primitive::ComponentSizeShift(Primitive::kPrimNot));
2984 codegen_->Load(Primitive::kPrimNot, out, source);
2985 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(out);
2986 __ B(&done);
2987 __ Bind(&allocate);
2988 // Otherwise allocate and initialize a new j.l.Integer.
2989 address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2990 __ Ldr(argument.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
2991 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2992 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2993 __ Str(in.W(), HeapOperand(out.W(), info.value_offset));
2994 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2995 // one.
2996 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2997 __ Bind(&done);
2998 }
2999}
3000
Aart Bik2f9fcc92016-03-01 15:16:54 -08003001UNIMPLEMENTED_INTRINSIC(ARM64, IntegerHighestOneBit)
3002UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit)
3003UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit)
3004UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit)
Andreas Gampe878d58c2015-01-15 23:24:00 -08003005
Aart Bikff7d89c2016-11-07 08:49:28 -08003006UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf);
3007UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08003008UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferAppend);
3009UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferLength);
3010UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferToString);
3011UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderAppend);
3012UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderLength);
3013UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08003014
Aart Bik0e54c012016-03-04 12:08:31 -08003015// 1.8.
3016UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt)
3017UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong)
3018UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt)
3019UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong)
3020UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08003021
Aart Bik2f9fcc92016-03-01 15:16:54 -08003022UNREACHABLE_INTRINSICS(ARM64)
Roland Levillain4d027112015-07-01 15:41:14 +01003023
3024#undef __
3025
Andreas Gampe878d58c2015-01-15 23:24:00 -08003026} // namespace arm64
3027} // namespace art