blob: 423fd3c6ae182a550385ffde058bce51d2ea8798 [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();
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000201 // TODO: Load the entrypoint once before the loop, instead of
202 // loading it at every iteration.
Roland Levillain0b671c02016-08-19 12:02:34 +0100203 int32_t entry_point_offset =
204 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(tmp_.reg());
205 // This runtime call does not require a stack map.
206 codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
207 codegen->GetAssembler()->MaybePoisonHeapReference(tmp_reg);
208 __ Str(tmp_reg, MemOperand(dst_curr_addr, element_size, PostIndex));
209 __ Cmp(src_curr_addr, src_stop_addr);
210 __ B(&slow_copy_loop, ne);
211 __ B(GetExitLabel());
212 }
213
214 const char* GetDescription() const OVERRIDE { return "ReadBarrierSystemArrayCopySlowPathARM64"; }
215
216 private:
217 Location tmp_;
218
219 DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathARM64);
220};
Andreas Gampe878d58c2015-01-15 23:24:00 -0800221#undef __
222
223bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) {
224 Dispatch(invoke);
225 LocationSummary* res = invoke->GetLocations();
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000226 if (res == nullptr) {
227 return false;
228 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000229 return res->Intrinsified();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800230}
231
232#define __ masm->
233
234static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
235 LocationSummary* locations = new (arena) LocationSummary(invoke,
236 LocationSummary::kNoCall,
237 kIntrinsified);
238 locations->SetInAt(0, Location::RequiresFpuRegister());
239 locations->SetOut(Location::RequiresRegister());
240}
241
242static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
243 LocationSummary* locations = new (arena) LocationSummary(invoke,
244 LocationSummary::kNoCall,
245 kIntrinsified);
246 locations->SetInAt(0, Location::RequiresRegister());
247 locations->SetOut(Location::RequiresFpuRegister());
248}
249
Scott Wakeling97c72b72016-06-24 16:19:36 +0100250static void MoveFPToInt(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800251 Location input = locations->InAt(0);
252 Location output = locations->Out();
253 __ Fmov(is64bit ? XRegisterFrom(output) : WRegisterFrom(output),
254 is64bit ? DRegisterFrom(input) : SRegisterFrom(input));
255}
256
Scott Wakeling97c72b72016-06-24 16:19:36 +0100257static void MoveIntToFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800258 Location input = locations->InAt(0);
259 Location output = locations->Out();
260 __ Fmov(is64bit ? DRegisterFrom(output) : SRegisterFrom(output),
261 is64bit ? XRegisterFrom(input) : WRegisterFrom(input));
262}
263
264void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
265 CreateFPToIntLocations(arena_, invoke);
266}
267void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
268 CreateIntToFPLocations(arena_, invoke);
269}
270
271void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000272 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800273}
274void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000275 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800276}
277
278void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
279 CreateFPToIntLocations(arena_, invoke);
280}
281void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
282 CreateIntToFPLocations(arena_, invoke);
283}
284
285void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000286 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800287}
288void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000289 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800290}
291
292static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
293 LocationSummary* locations = new (arena) LocationSummary(invoke,
294 LocationSummary::kNoCall,
295 kIntrinsified);
296 locations->SetInAt(0, Location::RequiresRegister());
297 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
298}
299
300static void GenReverseBytes(LocationSummary* locations,
301 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100302 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800303 Location in = locations->InAt(0);
304 Location out = locations->Out();
305
306 switch (type) {
307 case Primitive::kPrimShort:
308 __ Rev16(WRegisterFrom(out), WRegisterFrom(in));
309 __ Sxth(WRegisterFrom(out), WRegisterFrom(out));
310 break;
311 case Primitive::kPrimInt:
312 case Primitive::kPrimLong:
313 __ Rev(RegisterFrom(out, type), RegisterFrom(in, type));
314 break;
315 default:
316 LOG(FATAL) << "Unexpected size for reverse-bytes: " << type;
317 UNREACHABLE();
318 }
319}
320
321void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
322 CreateIntToIntLocations(arena_, invoke);
323}
324
325void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
326 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
327}
328
329void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) {
330 CreateIntToIntLocations(arena_, invoke);
331}
332
333void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) {
334 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
335}
336
337void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) {
338 CreateIntToIntLocations(arena_, invoke);
339}
340
341void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
342 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
343}
344
Aart Bik7b565022016-01-28 14:36:22 -0800345static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
346 LocationSummary* locations = new (arena) LocationSummary(invoke,
347 LocationSummary::kNoCall,
348 kIntrinsified);
349 locations->SetInAt(0, Location::RequiresRegister());
350 locations->SetInAt(1, Location::RequiresRegister());
351 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
352}
353
Scott Wakeling611d3392015-07-10 11:42:06 +0100354static void GenNumberOfLeadingZeros(LocationSummary* locations,
355 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100356 MacroAssembler* masm) {
Scott Wakeling611d3392015-07-10 11:42:06 +0100357 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
358
359 Location in = locations->InAt(0);
360 Location out = locations->Out();
361
362 __ Clz(RegisterFrom(out, type), RegisterFrom(in, type));
363}
364
365void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
366 CreateIntToIntLocations(arena_, invoke);
367}
368
369void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
370 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
371}
372
373void IntrinsicLocationsBuilderARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
374 CreateIntToIntLocations(arena_, invoke);
375}
376
377void IntrinsicCodeGeneratorARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
378 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
379}
380
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100381static void GenNumberOfTrailingZeros(LocationSummary* locations,
382 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100383 MacroAssembler* masm) {
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100384 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
385
386 Location in = locations->InAt(0);
387 Location out = locations->Out();
388
389 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
390 __ Clz(RegisterFrom(out, type), RegisterFrom(out, type));
391}
392
393void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
394 CreateIntToIntLocations(arena_, invoke);
395}
396
397void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
398 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
399}
400
401void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
402 CreateIntToIntLocations(arena_, invoke);
403}
404
405void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
406 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
407}
408
Andreas Gampe878d58c2015-01-15 23:24:00 -0800409static void GenReverse(LocationSummary* locations,
410 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100411 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800412 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
413
414 Location in = locations->InAt(0);
415 Location out = locations->Out();
416
417 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
418}
419
420void IntrinsicLocationsBuilderARM64::VisitIntegerReverse(HInvoke* invoke) {
421 CreateIntToIntLocations(arena_, invoke);
422}
423
424void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) {
425 GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
426}
427
428void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) {
429 CreateIntToIntLocations(arena_, invoke);
430}
431
432void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
433 GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
434}
435
Scott Wakeling97c72b72016-06-24 16:19:36 +0100436static void GenBitCount(HInvoke* instr, Primitive::Type type, MacroAssembler* masm) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100437 DCHECK(Primitive::IsIntOrLongType(type)) << type;
438 DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
439 DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
xueliang.zhong49924c92016-03-03 10:52:51 +0000440
xueliang.zhong49924c92016-03-03 10:52:51 +0000441 UseScratchRegisterScope temps(masm);
442
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000443 Register src = InputRegisterAt(instr, 0);
Roland Levillainfa3912e2016-04-01 18:21:55 +0100444 Register dst = RegisterFrom(instr->GetLocations()->Out(), type);
445 FPRegister fpr = (type == Primitive::kPrimLong) ? temps.AcquireD() : temps.AcquireS();
xueliang.zhong49924c92016-03-03 10:52:51 +0000446
447 __ Fmov(fpr, src);
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000448 __ Cnt(fpr.V8B(), fpr.V8B());
449 __ Addv(fpr.B(), fpr.V8B());
xueliang.zhong49924c92016-03-03 10:52:51 +0000450 __ Fmov(dst, fpr);
451}
452
453void IntrinsicLocationsBuilderARM64::VisitLongBitCount(HInvoke* invoke) {
454 CreateIntToIntLocations(arena_, invoke);
455}
456
457void IntrinsicCodeGeneratorARM64::VisitLongBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100458 GenBitCount(invoke, Primitive::kPrimLong, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000459}
460
461void IntrinsicLocationsBuilderARM64::VisitIntegerBitCount(HInvoke* invoke) {
462 CreateIntToIntLocations(arena_, invoke);
463}
464
465void IntrinsicCodeGeneratorARM64::VisitIntegerBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100466 GenBitCount(invoke, Primitive::kPrimInt, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000467}
468
Andreas Gampe878d58c2015-01-15 23:24:00 -0800469static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800470 LocationSummary* locations = new (arena) LocationSummary(invoke,
471 LocationSummary::kNoCall,
472 kIntrinsified);
473 locations->SetInAt(0, Location::RequiresFpuRegister());
474 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
475}
476
Scott Wakeling97c72b72016-06-24 16:19:36 +0100477static void MathAbsFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800478 Location in = locations->InAt(0);
479 Location out = locations->Out();
480
481 FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in);
482 FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out);
483
484 __ Fabs(out_reg, in_reg);
485}
486
487void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) {
488 CreateFPToFPLocations(arena_, invoke);
489}
490
491void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000492 MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800493}
494
495void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) {
496 CreateFPToFPLocations(arena_, invoke);
497}
498
499void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000500 MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800501}
502
503static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
504 LocationSummary* locations = new (arena) LocationSummary(invoke,
505 LocationSummary::kNoCall,
506 kIntrinsified);
507 locations->SetInAt(0, Location::RequiresRegister());
508 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
509}
510
511static void GenAbsInteger(LocationSummary* locations,
512 bool is64bit,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100513 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800514 Location in = locations->InAt(0);
515 Location output = locations->Out();
516
517 Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in);
518 Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output);
519
520 __ Cmp(in_reg, Operand(0));
521 __ Cneg(out_reg, in_reg, lt);
522}
523
524void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
525 CreateIntToInt(arena_, invoke);
526}
527
528void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000529 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800530}
531
532void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
533 CreateIntToInt(arena_, invoke);
534}
535
536void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000537 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800538}
539
540static void GenMinMaxFP(LocationSummary* locations,
541 bool is_min,
542 bool is_double,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100543 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800544 Location op1 = locations->InAt(0);
545 Location op2 = locations->InAt(1);
546 Location out = locations->Out();
547
548 FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1);
549 FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2);
550 FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out);
551 if (is_min) {
552 __ Fmin(out_reg, op1_reg, op2_reg);
553 } else {
554 __ Fmax(out_reg, op1_reg, op2_reg);
555 }
556}
557
558static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
559 LocationSummary* locations = new (arena) LocationSummary(invoke,
560 LocationSummary::kNoCall,
561 kIntrinsified);
562 locations->SetInAt(0, Location::RequiresFpuRegister());
563 locations->SetInAt(1, Location::RequiresFpuRegister());
564 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
565}
566
567void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
568 CreateFPFPToFPLocations(arena_, invoke);
569}
570
571void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000572 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800573}
574
575void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
576 CreateFPFPToFPLocations(arena_, invoke);
577}
578
579void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000580 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800581}
582
583void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
584 CreateFPFPToFPLocations(arena_, invoke);
585}
586
587void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000588 GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800589}
590
591void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
592 CreateFPFPToFPLocations(arena_, invoke);
593}
594
595void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000596 GenMinMaxFP(
597 invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800598}
599
600static void GenMinMax(LocationSummary* locations,
601 bool is_min,
602 bool is_long,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100603 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800604 Location op1 = locations->InAt(0);
605 Location op2 = locations->InAt(1);
606 Location out = locations->Out();
607
608 Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
609 Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
610 Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
611
612 __ Cmp(op1_reg, op2_reg);
613 __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
614}
615
Andreas Gampe878d58c2015-01-15 23:24:00 -0800616void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
617 CreateIntIntToIntLocations(arena_, invoke);
618}
619
620void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000621 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800622}
623
624void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) {
625 CreateIntIntToIntLocations(arena_, invoke);
626}
627
628void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000629 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800630}
631
632void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) {
633 CreateIntIntToIntLocations(arena_, invoke);
634}
635
636void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000637 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800638}
639
640void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) {
641 CreateIntIntToIntLocations(arena_, invoke);
642}
643
644void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000645 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800646}
647
648void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) {
649 CreateFPToFPLocations(arena_, invoke);
650}
651
652void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) {
653 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100654 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800655 __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
656}
657
658void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) {
659 CreateFPToFPLocations(arena_, invoke);
660}
661
662void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) {
663 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100664 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800665 __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
666}
667
668void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) {
669 CreateFPToFPLocations(arena_, invoke);
670}
671
672void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) {
673 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100674 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800675 __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
676}
677
678void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) {
679 CreateFPToFPLocations(arena_, invoke);
680}
681
682void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) {
683 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100684 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800685 __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
686}
687
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100688static void CreateFPToIntPlusFPTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800689 LocationSummary* locations = new (arena) LocationSummary(invoke,
690 LocationSummary::kNoCall,
691 kIntrinsified);
692 locations->SetInAt(0, Location::RequiresFpuRegister());
693 locations->SetOut(Location::RequiresRegister());
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100694 locations->AddTemp(Location::RequiresFpuRegister());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800695}
696
Scott Wakeling97c72b72016-06-24 16:19:36 +0100697static void GenMathRound(HInvoke* invoke, bool is_double, vixl::aarch64::MacroAssembler* masm) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100698 // Java 8 API definition for Math.round():
699 // Return the closest long or int to the argument, with ties rounding to positive infinity.
700 //
701 // There is no single instruction in ARMv8 that can support the above definition.
702 // We choose to use FCVTAS here, because it has closest semantic.
703 // FCVTAS performs rounding to nearest integer, ties away from zero.
704 // For most inputs (positive values, zero or NaN), this instruction is enough.
705 // We only need a few handling code after FCVTAS if the input is negative half value.
706 //
707 // The reason why we didn't choose FCVTPS instruction here is that
708 // although it performs rounding toward positive infinity, it doesn't perform rounding to nearest.
709 // For example, FCVTPS(-1.9) = -1 and FCVTPS(1.1) = 2.
710 // If we were using this instruction, for most inputs, more handling code would be needed.
711 LocationSummary* l = invoke->GetLocations();
712 FPRegister in_reg = is_double ? DRegisterFrom(l->InAt(0)) : SRegisterFrom(l->InAt(0));
713 FPRegister tmp_fp = is_double ? DRegisterFrom(l->GetTemp(0)) : SRegisterFrom(l->GetTemp(0));
714 Register out_reg = is_double ? XRegisterFrom(l->Out()) : WRegisterFrom(l->Out());
Scott Wakeling97c72b72016-06-24 16:19:36 +0100715 vixl::aarch64::Label done;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800716
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100717 // Round to nearest integer, ties away from zero.
718 __ Fcvtas(out_reg, in_reg);
719
720 // For positive values, zero or NaN inputs, rounding is done.
Scott Wakeling97c72b72016-06-24 16:19:36 +0100721 __ Tbz(out_reg, out_reg.GetSizeInBits() - 1, &done);
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100722
723 // Handle input < 0 cases.
724 // If input is negative but not a tie, previous result (round to nearest) is valid.
725 // If input is a negative tie, out_reg += 1.
726 __ Frinta(tmp_fp, in_reg);
727 __ Fsub(tmp_fp, in_reg, tmp_fp);
728 __ Fcmp(tmp_fp, 0.5);
729 __ Cinc(out_reg, out_reg, eq);
730
731 __ Bind(&done);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800732}
733
734void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100735 CreateFPToIntPlusFPTempLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800736}
737
738void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100739 GenMathRound(invoke, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800740}
741
742void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100743 CreateFPToIntPlusFPTempLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800744}
745
746void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100747 GenMathRound(invoke, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800748}
749
750void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) {
751 CreateIntToIntLocations(arena_, invoke);
752}
753
754void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100755 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800756 __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()),
757 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
758}
759
760void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
761 CreateIntToIntLocations(arena_, invoke);
762}
763
764void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100765 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800766 __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()),
767 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
768}
769
770void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
771 CreateIntToIntLocations(arena_, invoke);
772}
773
774void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100775 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800776 __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()),
777 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
778}
779
780void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
781 CreateIntToIntLocations(arena_, invoke);
782}
783
784void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100785 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800786 __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()),
787 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
788}
789
790static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
791 LocationSummary* locations = new (arena) LocationSummary(invoke,
792 LocationSummary::kNoCall,
793 kIntrinsified);
794 locations->SetInAt(0, Location::RequiresRegister());
795 locations->SetInAt(1, Location::RequiresRegister());
796}
797
798void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) {
799 CreateIntIntToVoidLocations(arena_, invoke);
800}
801
802void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100803 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800804 __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)),
805 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
806}
807
808void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
809 CreateIntIntToVoidLocations(arena_, invoke);
810}
811
812void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100813 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800814 __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)),
815 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
816}
817
818void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
819 CreateIntIntToVoidLocations(arena_, invoke);
820}
821
822void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100823 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800824 __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)),
825 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
826}
827
828void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
829 CreateIntIntToVoidLocations(arena_, invoke);
830}
831
832void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100833 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800834 __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)),
835 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
836}
837
838void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) {
839 LocationSummary* locations = new (arena_) LocationSummary(invoke,
840 LocationSummary::kNoCall,
841 kIntrinsified);
842 locations->SetOut(Location::RequiresRegister());
843}
844
845void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) {
846 codegen_->Load(Primitive::kPrimNot, WRegisterFrom(invoke->GetLocations()->Out()),
Andreas Gampe542451c2016-07-26 09:02:02 -0700847 MemOperand(tr, Thread::PeerOffset<kArm64PointerSize>().Int32Value()));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800848}
849
850static void GenUnsafeGet(HInvoke* invoke,
851 Primitive::Type type,
852 bool is_volatile,
853 CodeGeneratorARM64* codegen) {
854 LocationSummary* locations = invoke->GetLocations();
855 DCHECK((type == Primitive::kPrimInt) ||
856 (type == Primitive::kPrimLong) ||
857 (type == Primitive::kPrimNot));
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000858 Location base_loc = locations->InAt(1);
859 Register base = WRegisterFrom(base_loc); // Object pointer.
860 Location offset_loc = locations->InAt(2);
861 Register offset = XRegisterFrom(offset_loc); // Long offset.
862 Location trg_loc = locations->Out();
863 Register trg = RegisterFrom(trg_loc, type);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800864
Roland Levillain44015862016-01-22 11:47:17 +0000865 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
866 // UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case.
Roland Levillain54f869e2017-03-06 13:54:11 +0000867 Register temp = WRegisterFrom(locations->GetTemp(0));
Roland Levillainbfea3352016-06-23 13:48:47 +0100868 codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke,
869 trg_loc,
870 base,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100871 /* offset */ 0u,
Roland Levillainbfea3352016-06-23 13:48:47 +0100872 /* index */ offset_loc,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100873 /* scale_factor */ 0u,
Roland Levillainbfea3352016-06-23 13:48:47 +0100874 temp,
875 /* needs_null_check */ false,
876 is_volatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800877 } else {
Roland Levillain44015862016-01-22 11:47:17 +0000878 // Other cases.
879 MemOperand mem_op(base.X(), offset);
880 if (is_volatile) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +0000881 codegen->LoadAcquire(invoke, trg, mem_op, /* needs_null_check */ true);
Roland Levillain44015862016-01-22 11:47:17 +0000882 } else {
883 codegen->Load(type, trg, mem_op);
884 }
Roland Levillain4d027112015-07-01 15:41:14 +0100885
Roland Levillain44015862016-01-22 11:47:17 +0000886 if (type == Primitive::kPrimNot) {
887 DCHECK(trg.IsW());
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100888 codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0u, offset_loc);
Roland Levillain44015862016-01-22 11:47:17 +0000889 }
Roland Levillain4d027112015-07-01 15:41:14 +0100890 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800891}
892
893static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000894 bool can_call = kEmitCompilerReadBarrier &&
895 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
896 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800897 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100898 (can_call
899 ? LocationSummary::kCallOnSlowPath
900 : LocationSummary::kNoCall),
Andreas Gampe878d58c2015-01-15 23:24:00 -0800901 kIntrinsified);
Vladimir Marko70e97462016-08-09 11:04:26 +0100902 if (can_call && kUseBakerReadBarrier) {
Vladimir Marko804b03f2016-09-14 16:26:36 +0100903 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Roland Levillain54f869e2017-03-06 13:54:11 +0000904 // We need a temporary register for the read barrier marking slow
905 // path in CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier.
906 locations->AddTemp(Location::RequiresRegister());
Vladimir Marko70e97462016-08-09 11:04:26 +0100907 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800908 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
909 locations->SetInAt(1, Location::RequiresRegister());
910 locations->SetInAt(2, Location::RequiresRegister());
Roland Levillainbfea3352016-06-23 13:48:47 +0100911 locations->SetOut(Location::RequiresRegister(),
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100912 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800913}
914
915void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
916 CreateIntIntIntToIntLocations(arena_, invoke);
917}
918void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
919 CreateIntIntIntToIntLocations(arena_, invoke);
920}
921void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
922 CreateIntIntIntToIntLocations(arena_, invoke);
923}
924void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
925 CreateIntIntIntToIntLocations(arena_, invoke);
926}
927void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
928 CreateIntIntIntToIntLocations(arena_, invoke);
929}
930void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
931 CreateIntIntIntToIntLocations(arena_, invoke);
932}
933
934void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000935 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800936}
937void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000938 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800939}
940void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000941 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800942}
943void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000944 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800945}
946void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000947 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800948}
949void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000950 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800951}
952
953static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
954 LocationSummary* locations = new (arena) LocationSummary(invoke,
955 LocationSummary::kNoCall,
956 kIntrinsified);
957 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
958 locations->SetInAt(1, Location::RequiresRegister());
959 locations->SetInAt(2, Location::RequiresRegister());
960 locations->SetInAt(3, Location::RequiresRegister());
961}
962
963void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
964 CreateIntIntIntIntToVoid(arena_, invoke);
965}
966void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
967 CreateIntIntIntIntToVoid(arena_, invoke);
968}
969void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
970 CreateIntIntIntIntToVoid(arena_, invoke);
971}
972void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
973 CreateIntIntIntIntToVoid(arena_, invoke);
974}
975void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
976 CreateIntIntIntIntToVoid(arena_, invoke);
977}
978void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
979 CreateIntIntIntIntToVoid(arena_, invoke);
980}
981void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
982 CreateIntIntIntIntToVoid(arena_, invoke);
983}
984void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
985 CreateIntIntIntIntToVoid(arena_, invoke);
986}
987void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
988 CreateIntIntIntIntToVoid(arena_, invoke);
989}
990
Artem Serov914d7a82017-02-07 14:33:49 +0000991static void GenUnsafePut(HInvoke* invoke,
Andreas Gampe878d58c2015-01-15 23:24:00 -0800992 Primitive::Type type,
993 bool is_volatile,
994 bool is_ordered,
995 CodeGeneratorARM64* codegen) {
Artem Serov914d7a82017-02-07 14:33:49 +0000996 LocationSummary* locations = invoke->GetLocations();
Alexandre Rames087930f2016-08-02 13:45:28 +0100997 MacroAssembler* masm = codegen->GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800998
999 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
1000 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
1001 Register value = RegisterFrom(locations->InAt(3), type);
Roland Levillain4d027112015-07-01 15:41:14 +01001002 Register source = value;
Andreas Gampe878d58c2015-01-15 23:24:00 -08001003 MemOperand mem_op(base.X(), offset);
1004
Roland Levillain4d027112015-07-01 15:41:14 +01001005 {
1006 // We use a block to end the scratch scope before the write barrier, thus
1007 // freeing the temporary registers so they can be used in `MarkGCCard`.
1008 UseScratchRegisterScope temps(masm);
1009
1010 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1011 DCHECK(value.IsW());
1012 Register temp = temps.AcquireW();
1013 __ Mov(temp.W(), value.W());
1014 codegen->GetAssembler()->PoisonHeapReference(temp.W());
1015 source = temp;
Andreas Gampe878d58c2015-01-15 23:24:00 -08001016 }
Roland Levillain4d027112015-07-01 15:41:14 +01001017
1018 if (is_volatile || is_ordered) {
Artem Serov914d7a82017-02-07 14:33:49 +00001019 codegen->StoreRelease(invoke, type, source, mem_op, /* needs_null_check */ false);
Roland Levillain4d027112015-07-01 15:41:14 +01001020 } else {
1021 codegen->Store(type, source, mem_op);
1022 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001023 }
1024
1025 if (type == Primitive::kPrimNot) {
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001026 bool value_can_be_null = true; // TODO: Worth finding out this information?
1027 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001028 }
1029}
1030
1031void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001032 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001033 Primitive::kPrimInt,
1034 /* is_volatile */ false,
1035 /* is_ordered */ false,
1036 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001037}
1038void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001039 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001040 Primitive::kPrimInt,
1041 /* is_volatile */ false,
1042 /* is_ordered */ true,
1043 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001044}
1045void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001046 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001047 Primitive::kPrimInt,
1048 /* is_volatile */ true,
1049 /* is_ordered */ false,
1050 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001051}
1052void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001053 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001054 Primitive::kPrimNot,
1055 /* is_volatile */ false,
1056 /* is_ordered */ false,
1057 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001058}
1059void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001060 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001061 Primitive::kPrimNot,
1062 /* is_volatile */ false,
1063 /* is_ordered */ true,
1064 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001065}
1066void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001067 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001068 Primitive::kPrimNot,
1069 /* is_volatile */ true,
1070 /* is_ordered */ false,
1071 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001072}
1073void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001074 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001075 Primitive::kPrimLong,
1076 /* is_volatile */ false,
1077 /* is_ordered */ false,
1078 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001079}
1080void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001081 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001082 Primitive::kPrimLong,
1083 /* is_volatile */ false,
1084 /* is_ordered */ true,
1085 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001086}
1087void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +00001088 GenUnsafePut(invoke,
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001089 Primitive::kPrimLong,
1090 /* is_volatile */ true,
1091 /* is_ordered */ false,
1092 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001093}
1094
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001095static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
1096 HInvoke* invoke,
1097 Primitive::Type type) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001098 bool can_call = kEmitCompilerReadBarrier &&
1099 kUseBakerReadBarrier &&
1100 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001101 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001102 (can_call
1103 ? LocationSummary::kCallOnSlowPath
1104 : LocationSummary::kNoCall),
Andreas Gampe878d58c2015-01-15 23:24:00 -08001105 kIntrinsified);
1106 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1107 locations->SetInAt(1, Location::RequiresRegister());
1108 locations->SetInAt(2, Location::RequiresRegister());
1109 locations->SetInAt(3, Location::RequiresRegister());
1110 locations->SetInAt(4, Location::RequiresRegister());
1111
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001112 // If heap poisoning is enabled, we don't want the unpoisoning
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001113 // operations to potentially clobber the output. Likewise when
1114 // emitting a (Baker) read barrier, which may call.
1115 Location::OutputOverlap overlaps =
1116 ((kPoisonHeapReferences && type == Primitive::kPrimNot) || can_call)
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001117 ? Location::kOutputOverlap
1118 : Location::kNoOutputOverlap;
1119 locations->SetOut(Location::RequiresRegister(), overlaps);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001120 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1121 // Temporary register for (Baker) read barrier.
1122 locations->AddTemp(Location::RequiresRegister());
1123 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001124}
1125
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001126static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARM64* codegen) {
Alexandre Rames087930f2016-08-02 13:45:28 +01001127 MacroAssembler* masm = codegen->GetVIXLAssembler();
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001128 LocationSummary* locations = invoke->GetLocations();
Andreas Gampe878d58c2015-01-15 23:24:00 -08001129
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001130 Location out_loc = locations->Out();
1131 Register out = WRegisterFrom(out_loc); // Boolean result.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001132
1133 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001134 Location offset_loc = locations->InAt(2);
1135 Register offset = XRegisterFrom(offset_loc); // Long offset.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001136 Register expected = RegisterFrom(locations->InAt(3), type); // Expected.
1137 Register value = RegisterFrom(locations->InAt(4), type); // Value.
1138
1139 // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
1140 if (type == Primitive::kPrimNot) {
1141 // Mark card for object assuming new value is stored.
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001142 bool value_can_be_null = true; // TODO: Worth finding out this information?
1143 codegen->MarkGCCard(base, value, value_can_be_null);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001144
1145 // The only read barrier implementation supporting the
1146 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1147 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1148
1149 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1150 Register temp = WRegisterFrom(locations->GetTemp(0));
1151 // Need to make sure the reference stored in the field is a to-space
1152 // one before attempting the CAS or the CAS could fail incorrectly.
1153 codegen->GenerateReferenceLoadWithBakerReadBarrier(
1154 invoke,
1155 out_loc, // Unused, used only as a "temporary" within the read barrier.
1156 base,
1157 /* offset */ 0u,
1158 /* index */ offset_loc,
1159 /* scale_factor */ 0u,
1160 temp,
1161 /* needs_null_check */ false,
1162 /* use_load_acquire */ false,
1163 /* always_update_field */ true);
1164 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001165 }
1166
1167 UseScratchRegisterScope temps(masm);
1168 Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
1169 Register tmp_value = temps.AcquireSameSizeAs(value); // Value in memory.
1170
1171 Register tmp_32 = tmp_value.W();
1172
1173 __ Add(tmp_ptr, base.X(), Operand(offset));
1174
Roland Levillain4d027112015-07-01 15:41:14 +01001175 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1176 codegen->GetAssembler()->PoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001177 if (value.Is(expected)) {
1178 // Do not poison `value`, as it is the same register as
1179 // `expected`, which has just been poisoned.
1180 } else {
1181 codegen->GetAssembler()->PoisonHeapReference(value);
1182 }
Roland Levillain4d027112015-07-01 15:41:14 +01001183 }
1184
Andreas Gampe878d58c2015-01-15 23:24:00 -08001185 // do {
1186 // tmp_value = [tmp_ptr] - expected;
1187 // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1188 // result = tmp_value != 0;
1189
Scott Wakeling97c72b72016-06-24 16:19:36 +01001190 vixl::aarch64::Label loop_head, exit_loop;
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001191 __ Bind(&loop_head);
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001192 __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
1193 __ Cmp(tmp_value, expected);
1194 __ B(&exit_loop, ne);
1195 __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
1196 __ Cbnz(tmp_32, &loop_head);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001197 __ Bind(&exit_loop);
1198 __ Cset(out, eq);
Roland Levillain4d027112015-07-01 15:41:14 +01001199
1200 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillain4d027112015-07-01 15:41:14 +01001201 codegen->GetAssembler()->UnpoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001202 if (value.Is(expected)) {
1203 // Do not unpoison `value`, as it is the same register as
1204 // `expected`, which has just been unpoisoned.
1205 } else {
1206 codegen->GetAssembler()->UnpoisonHeapReference(value);
1207 }
Roland Levillain4d027112015-07-01 15:41:14 +01001208 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001209}
1210
1211void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001212 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001213}
1214void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001215 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001216}
1217void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001218 // The only read barrier implementation supporting the
1219 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1220 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain985ff702015-10-23 13:25:35 +01001221 return;
1222 }
1223
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001224 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001225}
1226
1227void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001228 GenCas(invoke, Primitive::kPrimInt, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001229}
1230void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001231 GenCas(invoke, Primitive::kPrimLong, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001232}
1233void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001234 // The only read barrier implementation supporting the
1235 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1236 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01001237
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001238 GenCas(invoke, Primitive::kPrimNot, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001239}
1240
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001241void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001242 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Scott Wakeling1f36f412016-04-21 11:13:45 +01001243 invoke->InputAt(1)->CanBeNull()
1244 ? LocationSummary::kCallOnSlowPath
1245 : LocationSummary::kNoCall,
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001246 kIntrinsified);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001247 locations->SetInAt(0, Location::RequiresRegister());
1248 locations->SetInAt(1, Location::RequiresRegister());
1249 locations->AddTemp(Location::RequiresRegister());
1250 locations->AddTemp(Location::RequiresRegister());
1251 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001252 // Need temporary registers for String compression's feature.
1253 if (mirror::kUseStringCompression) {
1254 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001255 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001256 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001257}
1258
1259void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001260 MacroAssembler* masm = GetVIXLAssembler();
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001261 LocationSummary* locations = invoke->GetLocations();
1262
Alexandre Rames2ea91532016-08-11 17:04:14 +01001263 Register str = InputRegisterAt(invoke, 0);
1264 Register arg = InputRegisterAt(invoke, 1);
1265 DCHECK(str.IsW());
1266 DCHECK(arg.IsW());
Scott Wakeling1f36f412016-04-21 11:13:45 +01001267 Register out = OutputRegister(invoke);
1268
1269 Register temp0 = WRegisterFrom(locations->GetTemp(0));
1270 Register temp1 = WRegisterFrom(locations->GetTemp(1));
1271 Register temp2 = WRegisterFrom(locations->GetTemp(2));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001272 Register temp3;
jessicahandojo05765752016-09-09 19:01:32 -07001273 if (mirror::kUseStringCompression) {
1274 temp3 = WRegisterFrom(locations->GetTemp(3));
jessicahandojo05765752016-09-09 19:01:32 -07001275 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001276
Scott Wakeling97c72b72016-06-24 16:19:36 +01001277 vixl::aarch64::Label loop;
1278 vixl::aarch64::Label find_char_diff;
1279 vixl::aarch64::Label end;
jessicahandojo05765752016-09-09 19:01:32 -07001280 vixl::aarch64::Label different_compression;
Scott Wakeling1f36f412016-04-21 11:13:45 +01001281
1282 // Get offsets of count and value fields within a string object.
1283 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1284 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1285
Nicolas Geoffray512e04d2015-03-27 17:21:24 +00001286 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +01001287 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001288
Scott Wakeling1f36f412016-04-21 11:13:45 +01001289 // Take slow path and throw if input can be and is null.
1290 SlowPathCodeARM64* slow_path = nullptr;
1291 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1292 if (can_slow_path) {
1293 slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1294 codegen_->AddSlowPath(slow_path);
1295 __ Cbz(arg, slow_path->GetEntryLabel());
1296 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001297
Scott Wakeling1f36f412016-04-21 11:13:45 +01001298 // Reference equality check, return 0 if same reference.
1299 __ Subs(out, str, arg);
1300 __ B(&end, eq);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001301
jessicahandojo05765752016-09-09 19:01:32 -07001302 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001303 // Load `count` fields of this and argument strings.
jessicahandojo05765752016-09-09 19:01:32 -07001304 __ Ldr(temp3, HeapOperand(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001305 __ Ldr(temp2, HeapOperand(arg, count_offset));
jessicahandojo05765752016-09-09 19:01:32 -07001306 // Clean out compression flag from lengths.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001307 __ Lsr(temp0, temp3, 1u);
1308 __ Lsr(temp1, temp2, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001309 } else {
1310 // Load lengths of this and argument strings.
1311 __ Ldr(temp0, HeapOperand(str, count_offset));
1312 __ Ldr(temp1, HeapOperand(arg, count_offset));
1313 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001314 // out = length diff.
1315 __ Subs(out, temp0, temp1);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001316 // temp0 = min(len(str), len(arg)).
1317 __ Csel(temp0, temp1, temp0, ge);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001318 // Shorter string is empty?
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001319 __ Cbz(temp0, &end);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001320
jessicahandojo05765752016-09-09 19:01:32 -07001321 if (mirror::kUseStringCompression) {
1322 // Check if both strings using same compression style to use this comparison loop.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001323 __ Eor(temp2, temp2, Operand(temp3));
1324 // Interleave with compression flag extraction which is needed for both paths
1325 // and also set flags which is needed only for the different compressions path.
1326 __ Ands(temp3.W(), temp3.W(), Operand(1));
1327 __ Tbnz(temp2, 0, &different_compression); // Does not use flags.
jessicahandojo05765752016-09-09 19:01:32 -07001328 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001329 // Store offset of string value in preparation for comparison loop.
1330 __ Mov(temp1, value_offset);
jessicahandojo05765752016-09-09 19:01:32 -07001331 if (mirror::kUseStringCompression) {
1332 // For string compression, calculate the number of bytes to compare (not chars).
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001333 // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
1334 __ Lsl(temp0, temp0, temp3);
jessicahandojo05765752016-09-09 19:01:32 -07001335 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001336
1337 UseScratchRegisterScope scratch_scope(masm);
1338 Register temp4 = scratch_scope.AcquireX();
1339
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001340 // Assertions that must hold in order to compare strings 8 bytes at a time.
Scott Wakeling1f36f412016-04-21 11:13:45 +01001341 DCHECK_ALIGNED(value_offset, 8);
1342 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1343
1344 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1345 DCHECK_EQ(char_size, 2u);
1346
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001347 // Promote temp2 to an X reg, ready for LDR.
1348 temp2 = temp2.X();
Scott Wakeling1f36f412016-04-21 11:13:45 +01001349
1350 // Loop to compare 4x16-bit characters at a time (ok because of string data alignment).
1351 __ Bind(&loop);
Alexandre Rames2ea91532016-08-11 17:04:14 +01001352 __ Ldr(temp4, MemOperand(str.X(), temp1.X()));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001353 __ Ldr(temp2, MemOperand(arg.X(), temp1.X()));
1354 __ Cmp(temp4, temp2);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001355 __ B(ne, &find_char_diff);
1356 __ Add(temp1, temp1, char_size * 4);
jessicahandojo05765752016-09-09 19:01:32 -07001357 // With string compression, we have compared 8 bytes, otherwise 4 chars.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001358 __ Subs(temp0, temp0, (mirror::kUseStringCompression) ? 8 : 4);
1359 __ B(&loop, hi);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001360 __ B(&end);
1361
1362 // Promote temp1 to an X reg, ready for EOR.
1363 temp1 = temp1.X();
1364
jessicahandojo05765752016-09-09 19:01:32 -07001365 // Find the single character difference.
Scott Wakeling1f36f412016-04-21 11:13:45 +01001366 __ Bind(&find_char_diff);
1367 // Get the bit position of the first character that differs.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001368 __ Eor(temp1, temp2, temp4);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001369 __ Rbit(temp1, temp1);
1370 __ Clz(temp1, temp1);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001371
jessicahandojo05765752016-09-09 19:01:32 -07001372 // If the number of chars remaining <= the index where the difference occurs (0-3), then
Scott Wakeling1f36f412016-04-21 11:13:45 +01001373 // the difference occurs outside the remaining string data, so just return length diff (out).
jessicahandojo05765752016-09-09 19:01:32 -07001374 // Unlike ARM, we're doing the comparison in one go here, without the subtraction at the
1375 // find_char_diff_2nd_cmp path, so it doesn't matter whether the comparison is signed or
1376 // unsigned when string compression is disabled.
1377 // When it's enabled, the comparison must be unsigned.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001378 __ Cmp(temp0, Operand(temp1.W(), LSR, (mirror::kUseStringCompression) ? 3 : 4));
jessicahandojo05765752016-09-09 19:01:32 -07001379 __ B(ls, &end);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001380
Scott Wakeling1f36f412016-04-21 11:13:45 +01001381 // Extract the characters and calculate the difference.
jessicahandojo05765752016-09-09 19:01:32 -07001382 if (mirror:: kUseStringCompression) {
jessicahandojo05765752016-09-09 19:01:32 -07001383 __ Bic(temp1, temp1, 0x7);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001384 __ Bic(temp1, temp1, Operand(temp3.X(), LSL, 3u));
1385 } else {
1386 __ Bic(temp1, temp1, 0xf);
jessicahandojo05765752016-09-09 19:01:32 -07001387 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001388 __ Lsr(temp2, temp2, temp1);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001389 __ Lsr(temp4, temp4, temp1);
jessicahandojo05765752016-09-09 19:01:32 -07001390 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001391 // Prioritize the case of compressed strings and calculate such result first.
1392 __ Uxtb(temp1, temp4);
1393 __ Sub(out, temp1.W(), Operand(temp2.W(), UXTB));
1394 __ Tbz(temp3, 0u, &end); // If actually compressed, we're done.
jessicahandojo05765752016-09-09 19:01:32 -07001395 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001396 __ Uxth(temp4, temp4);
1397 __ Sub(out, temp4.W(), Operand(temp2.W(), UXTH));
jessicahandojo05765752016-09-09 19:01:32 -07001398
1399 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001400 __ B(&end);
1401 __ Bind(&different_compression);
1402
1403 // Comparison for different compression style.
jessicahandojo05765752016-09-09 19:01:32 -07001404 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
1405 DCHECK_EQ(c_char_size, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001406 temp1 = temp1.W();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001407 temp2 = temp2.W();
1408 temp4 = temp4.W();
jessicahandojo05765752016-09-09 19:01:32 -07001409
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001410 // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
1411 // Note that flags have been set by the `str` compression flag extraction to `temp3`
1412 // before branching to the `different_compression` label.
1413 __ Csel(temp1, str, arg, eq); // Pointer to the compressed string.
1414 __ Csel(temp2, str, arg, ne); // Pointer to the uncompressed string.
jessicahandojo05765752016-09-09 19:01:32 -07001415
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001416 // We want to free up the temp3, currently holding `str` compression flag, for comparison.
1417 // So, we move it to the bottom bit of the iteration count `temp0` which we then need to treat
1418 // as unsigned. Start by freeing the bit with a LSL and continue further down by a SUB which
1419 // will allow `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
1420 __ Lsl(temp0, temp0, 1u);
1421
1422 // Adjust temp1 and temp2 from string pointers to data pointers.
1423 __ Add(temp1, temp1, Operand(value_offset));
1424 __ Add(temp2, temp2, Operand(value_offset));
1425
1426 // Complete the move of the compression flag.
1427 __ Sub(temp0, temp0, Operand(temp3));
1428
1429 vixl::aarch64::Label different_compression_loop;
1430 vixl::aarch64::Label different_compression_diff;
1431
1432 __ Bind(&different_compression_loop);
1433 __ Ldrb(temp4, MemOperand(temp1.X(), c_char_size, PostIndex));
1434 __ Ldrh(temp3, MemOperand(temp2.X(), char_size, PostIndex));
1435 __ Subs(temp4, temp4, Operand(temp3));
1436 __ B(&different_compression_diff, ne);
1437 __ Subs(temp0, temp0, 2);
1438 __ B(&different_compression_loop, hi);
jessicahandojo05765752016-09-09 19:01:32 -07001439 __ B(&end);
1440
1441 // Calculate the difference.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001442 __ Bind(&different_compression_diff);
1443 __ Tst(temp0, Operand(1));
1444 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1445 "Expecting 0=compressed, 1=uncompressed");
1446 __ Cneg(out, temp4, ne);
jessicahandojo05765752016-09-09 19:01:32 -07001447 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001448
1449 __ Bind(&end);
1450
1451 if (can_slow_path) {
1452 __ Bind(slow_path->GetExitLabel());
1453 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001454}
1455
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001456// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
1457// The normal loop plus the pre-header is 9 instructions without string compression and 12
1458// instructions with string compression. We can compare up to 8 bytes in 4 instructions
1459// (LDR+LDR+CMP+BNE) and up to 16 bytes in 5 instructions (LDP+LDP+CMP+CCMP+BNE). Allow up
1460// to 10 instructions for the unrolled loop.
1461constexpr size_t kShortConstStringEqualsCutoffInBytes = 32;
1462
1463static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
1464 if (candidate->IsLoadString()) {
1465 HLoadString* load_string = candidate->AsLoadString();
1466 const DexFile& dex_file = load_string->GetDexFile();
1467 return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
1468 }
1469 return nullptr;
1470}
1471
Agi Csakiea34b402015-08-13 17:51:19 -07001472void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
1473 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1474 LocationSummary::kNoCall,
1475 kIntrinsified);
1476 locations->SetInAt(0, Location::RequiresRegister());
1477 locations->SetInAt(1, Location::RequiresRegister());
Agi Csakiea34b402015-08-13 17:51:19 -07001478
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001479 // For the generic implementation and for long const strings we need a temporary.
1480 // We do not need it for short const strings, up to 8 bytes, see code generation below.
1481 uint32_t const_string_length = 0u;
1482 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1483 if (const_string == nullptr) {
1484 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1485 }
1486 bool is_compressed =
1487 mirror::kUseStringCompression &&
1488 const_string != nullptr &&
1489 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1490 if (const_string == nullptr || const_string_length > (is_compressed ? 8u : 4u)) {
1491 locations->AddTemp(Location::RequiresRegister());
1492 }
1493
1494 // TODO: If the String.equals() is used only for an immediately following HIf, we can
1495 // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
1496 // Then we shall need an extra temporary register instead of the output register.
Agi Csakiea34b402015-08-13 17:51:19 -07001497 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1498}
1499
1500void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001501 MacroAssembler* masm = GetVIXLAssembler();
Agi Csakiea34b402015-08-13 17:51:19 -07001502 LocationSummary* locations = invoke->GetLocations();
1503
1504 Register str = WRegisterFrom(locations->InAt(0));
1505 Register arg = WRegisterFrom(locations->InAt(1));
1506 Register out = XRegisterFrom(locations->Out());
1507
1508 UseScratchRegisterScope scratch_scope(masm);
1509 Register temp = scratch_scope.AcquireW();
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001510 Register temp1 = scratch_scope.AcquireW();
Agi Csakiea34b402015-08-13 17:51:19 -07001511
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001512 vixl::aarch64::Label loop;
Scott Wakeling97c72b72016-06-24 16:19:36 +01001513 vixl::aarch64::Label end;
1514 vixl::aarch64::Label return_true;
1515 vixl::aarch64::Label return_false;
Agi Csakiea34b402015-08-13 17:51:19 -07001516
1517 // Get offsets of count, value, and class fields within a string object.
1518 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1519 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1520 const int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1521
1522 // Note that the null check must have been done earlier.
1523 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1524
Vladimir Marko53b52002016-05-24 19:30:45 +01001525 StringEqualsOptimizations optimizations(invoke);
1526 if (!optimizations.GetArgumentNotNull()) {
1527 // Check if input is null, return false if it is.
1528 __ Cbz(arg, &return_false);
1529 }
Agi Csakiea34b402015-08-13 17:51:19 -07001530
1531 // Reference equality check, return true if same reference.
1532 __ Cmp(str, arg);
1533 __ B(&return_true, eq);
1534
Vladimir Marko53b52002016-05-24 19:30:45 +01001535 if (!optimizations.GetArgumentIsString()) {
1536 // Instanceof check for the argument by comparing class fields.
1537 // All string objects must have the same type since String cannot be subclassed.
1538 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1539 // If the argument is a string object, its class field must be equal to receiver's class field.
1540 __ Ldr(temp, MemOperand(str.X(), class_offset));
1541 __ Ldr(temp1, MemOperand(arg.X(), class_offset));
1542 __ Cmp(temp, temp1);
1543 __ B(&return_false, ne);
1544 }
Agi Csakiea34b402015-08-13 17:51:19 -07001545
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001546 // Check if one of the inputs is a const string. Do not special-case both strings
1547 // being const, such cases should be handled by constant folding if needed.
1548 uint32_t const_string_length = 0u;
1549 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1550 if (const_string == nullptr) {
1551 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1552 if (const_string != nullptr) {
1553 std::swap(str, arg); // Make sure the const string is in `str`.
1554 }
1555 }
1556 bool is_compressed =
1557 mirror::kUseStringCompression &&
1558 const_string != nullptr &&
1559 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1560
1561 if (const_string != nullptr) {
1562 // Load `count` field of the argument string and check if it matches the const string.
1563 // Also compares the compression style, if differs return false.
1564 __ Ldr(temp, MemOperand(arg.X(), count_offset));
Vladimir Marko26ec3ca2017-03-14 13:37:14 +00001565 // Temporarily release temp1 as we may not be able to embed the flagged count in CMP immediate.
1566 scratch_scope.Release(temp1);
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001567 __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
Vladimir Marko26ec3ca2017-03-14 13:37:14 +00001568 temp1 = scratch_scope.AcquireW();
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001569 __ B(&return_false, ne);
1570 } else {
1571 // Load `count` fields of this and argument strings.
1572 __ Ldr(temp, MemOperand(str.X(), count_offset));
1573 __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1574 // Check if `count` fields are equal, return false if they're not.
1575 // Also compares the compression style, if differs return false.
1576 __ Cmp(temp, temp1);
1577 __ B(&return_false, ne);
1578 }
Agi Csakiea34b402015-08-13 17:51:19 -07001579
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001580 // Assertions that must hold in order to compare strings 8 bytes at a time.
Agi Csakiea34b402015-08-13 17:51:19 -07001581 DCHECK_ALIGNED(value_offset, 8);
1582 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1583
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001584 if (const_string != nullptr &&
1585 const_string_length < (is_compressed ? kShortConstStringEqualsCutoffInBytes
1586 : kShortConstStringEqualsCutoffInBytes / 2u)) {
1587 // Load and compare the contents. Though we know the contents of the short const string
1588 // at compile time, materializing constants may be more code than loading from memory.
1589 int32_t offset = value_offset;
1590 size_t remaining_bytes =
1591 RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u);
1592 temp = temp.X();
1593 temp1 = temp1.X();
1594 while (remaining_bytes > 8u) {
1595 Register temp2 = XRegisterFrom(locations->GetTemp(0));
1596 __ Ldp(temp, temp1, MemOperand(str.X(), offset));
1597 __ Ldp(temp2, out, MemOperand(arg.X(), offset));
1598 __ Cmp(temp, temp2);
1599 __ Ccmp(temp1, out, NoFlag, eq);
1600 __ B(&return_false, ne);
1601 offset += 2u * sizeof(uint64_t);
1602 remaining_bytes -= 2u * sizeof(uint64_t);
1603 }
1604 if (remaining_bytes != 0u) {
1605 __ Ldr(temp, MemOperand(str.X(), offset));
1606 __ Ldr(temp1, MemOperand(arg.X(), offset));
1607 __ Cmp(temp, temp1);
1608 __ B(&return_false, ne);
1609 }
1610 } else {
1611 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
1612 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1613 "Expecting 0=compressed, 1=uncompressed");
1614 __ Cbz(temp, &return_true);
1615
1616 if (mirror::kUseStringCompression) {
1617 // For string compression, calculate the number of bytes to compare (not chars).
1618 // This could in theory exceed INT32_MAX, so treat temp as unsigned.
1619 __ And(temp1, temp, Operand(1)); // Extract compression flag.
1620 __ Lsr(temp, temp, 1u); // Extract length.
1621 __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare.
1622 }
1623
1624 // Store offset of string value in preparation for comparison loop
1625 __ Mov(temp1, value_offset);
1626
1627 temp1 = temp1.X();
1628 Register temp2 = XRegisterFrom(locations->GetTemp(0));
1629 // Loop to compare strings 8 bytes at a time starting at the front of the string.
1630 // Ok to do this because strings are zero-padded to kObjectAlignment.
1631 __ Bind(&loop);
1632 __ Ldr(out, MemOperand(str.X(), temp1));
1633 __ Ldr(temp2, MemOperand(arg.X(), temp1));
1634 __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
1635 __ Cmp(out, temp2);
1636 __ B(&return_false, ne);
1637 // With string compression, we have compared 8 bytes, otherwise 4 chars.
1638 __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags);
1639 __ B(&loop, hi);
jessicahandojo05765752016-09-09 19:01:32 -07001640 }
1641
Agi Csakiea34b402015-08-13 17:51:19 -07001642 // Return true and exit the function.
1643 // If loop does not result in returning false, we return true.
1644 __ Bind(&return_true);
1645 __ Mov(out, 1);
1646 __ B(&end);
1647
1648 // Return false and exit the function.
1649 __ Bind(&return_false);
1650 __ Mov(out, 0);
1651 __ Bind(&end);
1652}
1653
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001654static void GenerateVisitStringIndexOf(HInvoke* invoke,
Scott Wakeling97c72b72016-06-24 16:19:36 +01001655 MacroAssembler* masm,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001656 CodeGeneratorARM64* codegen,
1657 ArenaAllocator* allocator,
1658 bool start_at_zero) {
1659 LocationSummary* locations = invoke->GetLocations();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001660
1661 // Note that the null check must have been done earlier.
1662 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1663
1664 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001665 // 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 -07001666 SlowPathCodeARM64* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001667 HInstruction* code_point = invoke->InputAt(1);
1668 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01001669 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > 0xFFFFU) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001670 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1671 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1672 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1673 codegen->AddSlowPath(slow_path);
1674 __ B(slow_path->GetEntryLabel());
1675 __ Bind(slow_path->GetExitLabel());
1676 return;
1677 }
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001678 } else if (code_point->GetType() != Primitive::kPrimChar) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001679 Register char_reg = WRegisterFrom(locations->InAt(1));
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001680 __ Tst(char_reg, 0xFFFF0000);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001681 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1682 codegen->AddSlowPath(slow_path);
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001683 __ B(ne, slow_path->GetEntryLabel());
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001684 }
1685
1686 if (start_at_zero) {
1687 // Start-index = 0.
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001688 Register tmp_reg = WRegisterFrom(locations->GetTemp(0));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001689 __ Mov(tmp_reg, 0);
1690 }
1691
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001692 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
Roland Levillain42ad2882016-02-29 18:26:54 +00001693 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001694
1695 if (slow_path != nullptr) {
1696 __ Bind(slow_path->GetExitLabel());
1697 }
1698}
1699
1700void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) {
1701 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001702 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001703 kIntrinsified);
1704 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1705 // best to align the inputs accordingly.
1706 InvokeRuntimeCallingConvention calling_convention;
1707 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1708 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1709 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1710
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001711 // Need to send start_index=0.
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001712 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1713}
1714
1715void IntrinsicCodeGeneratorARM64::VisitStringIndexOf(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001716 GenerateVisitStringIndexOf(
1717 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001718}
1719
1720void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
1721 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001722 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001723 kIntrinsified);
1724 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1725 // best to align the inputs accordingly.
1726 InvokeRuntimeCallingConvention calling_convention;
1727 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1728 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1729 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1730 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001731}
1732
1733void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001734 GenerateVisitStringIndexOf(
1735 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001736}
1737
Jeff Hao848f70a2014-01-15 13:49:50 -08001738void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1739 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001740 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001741 kIntrinsified);
1742 InvokeRuntimeCallingConvention calling_convention;
1743 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1744 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1745 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1746 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1747 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1748}
1749
1750void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001751 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001752 LocationSummary* locations = invoke->GetLocations();
1753
1754 Register byte_array = WRegisterFrom(locations->InAt(0));
1755 __ Cmp(byte_array, 0);
1756 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1757 codegen_->AddSlowPath(slow_path);
1758 __ B(eq, slow_path->GetEntryLabel());
1759
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001760 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001761 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001762 __ Bind(slow_path->GetExitLabel());
1763}
1764
1765void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
1766 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001767 LocationSummary::kCallOnMainOnly,
Jeff Hao848f70a2014-01-15 13:49:50 -08001768 kIntrinsified);
1769 InvokeRuntimeCallingConvention calling_convention;
1770 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1771 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1772 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1773 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1774}
1775
1776void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
Roland Levillaincc3839c2016-02-29 16:23:48 +00001777 // No need to emit code checking whether `locations->InAt(2)` is a null
1778 // pointer, as callers of the native method
1779 //
1780 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1781 //
1782 // all include a null check on `data` before calling that method.
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001783 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
Roland Levillainf969a202016-03-09 16:14:00 +00001784 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001785}
1786
1787void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Jeff Hao848f70a2014-01-15 13:49:50 -08001788 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001789 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001790 kIntrinsified);
1791 InvokeRuntimeCallingConvention calling_convention;
1792 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Jeff Hao848f70a2014-01-15 13:49:50 -08001793 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1794}
1795
1796void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001797 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001798 LocationSummary* locations = invoke->GetLocations();
1799
1800 Register string_to_copy = WRegisterFrom(locations->InAt(0));
1801 __ Cmp(string_to_copy, 0);
1802 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1803 codegen_->AddSlowPath(slow_path);
1804 __ B(eq, slow_path->GetEntryLabel());
1805
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001806 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001807 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001808 __ Bind(slow_path->GetExitLabel());
1809}
1810
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001811static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1812 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
1813 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1814 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1815
1816 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001817 LocationSummary::kCallOnMainOnly,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001818 kIntrinsified);
1819 InvokeRuntimeCallingConvention calling_convention;
1820
1821 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1822 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1823}
1824
1825static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1826 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
1827 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1828 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(1)->GetType()));
1829 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1830
1831 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001832 LocationSummary::kCallOnMainOnly,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001833 kIntrinsified);
1834 InvokeRuntimeCallingConvention calling_convention;
1835
1836 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1837 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
1838 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1839}
1840
1841static void GenFPToFPCall(HInvoke* invoke,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001842 CodeGeneratorARM64* codegen,
1843 QuickEntrypointEnum entry) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001844 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001845}
1846
1847void IntrinsicLocationsBuilderARM64::VisitMathCos(HInvoke* invoke) {
1848 CreateFPToFPCallLocations(arena_, invoke);
1849}
1850
1851void IntrinsicCodeGeneratorARM64::VisitMathCos(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001852 GenFPToFPCall(invoke, codegen_, kQuickCos);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001853}
1854
1855void IntrinsicLocationsBuilderARM64::VisitMathSin(HInvoke* invoke) {
1856 CreateFPToFPCallLocations(arena_, invoke);
1857}
1858
1859void IntrinsicCodeGeneratorARM64::VisitMathSin(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001860 GenFPToFPCall(invoke, codegen_, kQuickSin);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001861}
1862
1863void IntrinsicLocationsBuilderARM64::VisitMathAcos(HInvoke* invoke) {
1864 CreateFPToFPCallLocations(arena_, invoke);
1865}
1866
1867void IntrinsicCodeGeneratorARM64::VisitMathAcos(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001868 GenFPToFPCall(invoke, codegen_, kQuickAcos);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001869}
1870
1871void IntrinsicLocationsBuilderARM64::VisitMathAsin(HInvoke* invoke) {
1872 CreateFPToFPCallLocations(arena_, invoke);
1873}
1874
1875void IntrinsicCodeGeneratorARM64::VisitMathAsin(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001876 GenFPToFPCall(invoke, codegen_, kQuickAsin);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001877}
1878
1879void IntrinsicLocationsBuilderARM64::VisitMathAtan(HInvoke* invoke) {
1880 CreateFPToFPCallLocations(arena_, invoke);
1881}
1882
1883void IntrinsicCodeGeneratorARM64::VisitMathAtan(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001884 GenFPToFPCall(invoke, codegen_, kQuickAtan);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001885}
1886
1887void IntrinsicLocationsBuilderARM64::VisitMathCbrt(HInvoke* invoke) {
1888 CreateFPToFPCallLocations(arena_, invoke);
1889}
1890
1891void IntrinsicCodeGeneratorARM64::VisitMathCbrt(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001892 GenFPToFPCall(invoke, codegen_, kQuickCbrt);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001893}
1894
1895void IntrinsicLocationsBuilderARM64::VisitMathCosh(HInvoke* invoke) {
1896 CreateFPToFPCallLocations(arena_, invoke);
1897}
1898
1899void IntrinsicCodeGeneratorARM64::VisitMathCosh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001900 GenFPToFPCall(invoke, codegen_, kQuickCosh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001901}
1902
1903void IntrinsicLocationsBuilderARM64::VisitMathExp(HInvoke* invoke) {
1904 CreateFPToFPCallLocations(arena_, invoke);
1905}
1906
1907void IntrinsicCodeGeneratorARM64::VisitMathExp(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001908 GenFPToFPCall(invoke, codegen_, kQuickExp);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001909}
1910
1911void IntrinsicLocationsBuilderARM64::VisitMathExpm1(HInvoke* invoke) {
1912 CreateFPToFPCallLocations(arena_, invoke);
1913}
1914
1915void IntrinsicCodeGeneratorARM64::VisitMathExpm1(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001916 GenFPToFPCall(invoke, codegen_, kQuickExpm1);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001917}
1918
1919void IntrinsicLocationsBuilderARM64::VisitMathLog(HInvoke* invoke) {
1920 CreateFPToFPCallLocations(arena_, invoke);
1921}
1922
1923void IntrinsicCodeGeneratorARM64::VisitMathLog(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001924 GenFPToFPCall(invoke, codegen_, kQuickLog);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001925}
1926
1927void IntrinsicLocationsBuilderARM64::VisitMathLog10(HInvoke* invoke) {
1928 CreateFPToFPCallLocations(arena_, invoke);
1929}
1930
1931void IntrinsicCodeGeneratorARM64::VisitMathLog10(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001932 GenFPToFPCall(invoke, codegen_, kQuickLog10);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001933}
1934
1935void IntrinsicLocationsBuilderARM64::VisitMathSinh(HInvoke* invoke) {
1936 CreateFPToFPCallLocations(arena_, invoke);
1937}
1938
1939void IntrinsicCodeGeneratorARM64::VisitMathSinh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001940 GenFPToFPCall(invoke, codegen_, kQuickSinh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001941}
1942
1943void IntrinsicLocationsBuilderARM64::VisitMathTan(HInvoke* invoke) {
1944 CreateFPToFPCallLocations(arena_, invoke);
1945}
1946
1947void IntrinsicCodeGeneratorARM64::VisitMathTan(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001948 GenFPToFPCall(invoke, codegen_, kQuickTan);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001949}
1950
1951void IntrinsicLocationsBuilderARM64::VisitMathTanh(HInvoke* invoke) {
1952 CreateFPToFPCallLocations(arena_, invoke);
1953}
1954
1955void IntrinsicCodeGeneratorARM64::VisitMathTanh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001956 GenFPToFPCall(invoke, codegen_, kQuickTanh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001957}
1958
1959void IntrinsicLocationsBuilderARM64::VisitMathAtan2(HInvoke* invoke) {
1960 CreateFPFPToFPCallLocations(arena_, invoke);
1961}
1962
1963void IntrinsicCodeGeneratorARM64::VisitMathAtan2(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001964 GenFPToFPCall(invoke, codegen_, kQuickAtan2);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001965}
1966
1967void IntrinsicLocationsBuilderARM64::VisitMathHypot(HInvoke* invoke) {
1968 CreateFPFPToFPCallLocations(arena_, invoke);
1969}
1970
1971void IntrinsicCodeGeneratorARM64::VisitMathHypot(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001972 GenFPToFPCall(invoke, codegen_, kQuickHypot);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001973}
1974
1975void IntrinsicLocationsBuilderARM64::VisitMathNextAfter(HInvoke* invoke) {
1976 CreateFPFPToFPCallLocations(arena_, invoke);
1977}
1978
1979void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001980 GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001981}
1982
Tim Zhang25abd6c2016-01-19 23:39:24 +08001983void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1984 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1985 LocationSummary::kNoCall,
1986 kIntrinsified);
1987 locations->SetInAt(0, Location::RequiresRegister());
1988 locations->SetInAt(1, Location::RequiresRegister());
1989 locations->SetInAt(2, Location::RequiresRegister());
1990 locations->SetInAt(3, Location::RequiresRegister());
1991 locations->SetInAt(4, Location::RequiresRegister());
1992
1993 locations->AddTemp(Location::RequiresRegister());
1994 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingdf109d92016-04-22 11:35:56 +01001995 locations->AddTemp(Location::RequiresRegister());
Tim Zhang25abd6c2016-01-19 23:39:24 +08001996}
1997
1998void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001999 MacroAssembler* masm = GetVIXLAssembler();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002000 LocationSummary* locations = invoke->GetLocations();
2001
2002 // Check assumption that sizeof(Char) is 2 (used in scaling below).
2003 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2004 DCHECK_EQ(char_size, 2u);
2005
2006 // Location of data in char array buffer.
2007 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2008
2009 // Location of char array data in string.
2010 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2011
2012 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2013 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2014 Register srcObj = XRegisterFrom(locations->InAt(0));
2015 Register srcBegin = XRegisterFrom(locations->InAt(1));
2016 Register srcEnd = XRegisterFrom(locations->InAt(2));
2017 Register dstObj = XRegisterFrom(locations->InAt(3));
2018 Register dstBegin = XRegisterFrom(locations->InAt(4));
2019
2020 Register src_ptr = XRegisterFrom(locations->GetTemp(0));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002021 Register num_chr = XRegisterFrom(locations->GetTemp(1));
2022 Register tmp1 = XRegisterFrom(locations->GetTemp(2));
Tim Zhang25abd6c2016-01-19 23:39:24 +08002023
2024 UseScratchRegisterScope temps(masm);
2025 Register dst_ptr = temps.AcquireX();
Scott Wakelingdf109d92016-04-22 11:35:56 +01002026 Register tmp2 = temps.AcquireX();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002027
jessicahandojo05765752016-09-09 19:01:32 -07002028 vixl::aarch64::Label done;
2029 vixl::aarch64::Label compressed_string_loop;
2030 __ Sub(num_chr, srcEnd, srcBegin);
2031 // Early out for valid zero-length retrievals.
2032 __ Cbz(num_chr, &done);
Tim Zhang25abd6c2016-01-19 23:39:24 +08002033
Scott Wakelingdf109d92016-04-22 11:35:56 +01002034 // dst address start to copy to.
Tim Zhang25abd6c2016-01-19 23:39:24 +08002035 __ Add(dst_ptr, dstObj, Operand(data_offset));
2036 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
2037
jessicahandojo05765752016-09-09 19:01:32 -07002038 // src address to copy from.
2039 __ Add(src_ptr, srcObj, Operand(value_offset));
2040 vixl::aarch64::Label compressed_string_preloop;
2041 if (mirror::kUseStringCompression) {
2042 // Location of count in string.
2043 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2044 // String's length.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002045 __ Ldr(tmp2, MemOperand(srcObj, count_offset));
2046 __ Tbz(tmp2, 0, &compressed_string_preloop);
jessicahandojo05765752016-09-09 19:01:32 -07002047 }
2048 __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002049
Tim Zhang25abd6c2016-01-19 23:39:24 +08002050 // Do the copy.
Scott Wakeling97c72b72016-06-24 16:19:36 +01002051 vixl::aarch64::Label loop;
Scott Wakeling97c72b72016-06-24 16:19:36 +01002052 vixl::aarch64::Label remainder;
Scott Wakelingdf109d92016-04-22 11:35:56 +01002053
Scott Wakelingdf109d92016-04-22 11:35:56 +01002054 // Save repairing the value of num_chr on the < 8 character path.
2055 __ Subs(tmp1, num_chr, 8);
2056 __ B(lt, &remainder);
2057
2058 // Keep the result of the earlier subs, we are going to fetch at least 8 characters.
2059 __ Mov(num_chr, tmp1);
2060
2061 // Main loop used for longer fetches loads and stores 8x16-bit characters at a time.
2062 // (Unaligned addresses are acceptable here and not worth inlining extra code to rectify.)
Tim Zhang25abd6c2016-01-19 23:39:24 +08002063 __ Bind(&loop);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002064 __ Ldp(tmp1, tmp2, MemOperand(src_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002065 __ Subs(num_chr, num_chr, 8);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002066 __ Stp(tmp1, tmp2, MemOperand(dst_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002067 __ B(ge, &loop);
2068
2069 __ Adds(num_chr, num_chr, 8);
2070 __ B(eq, &done);
2071
2072 // Main loop for < 8 character case and remainder handling. Loads and stores one
2073 // 16-bit Java character at a time.
2074 __ Bind(&remainder);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002075 __ Ldrh(tmp1, MemOperand(src_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002076 __ Subs(num_chr, num_chr, 1);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002077 __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002078 __ B(gt, &remainder);
jessicahandojo05765752016-09-09 19:01:32 -07002079 __ B(&done);
2080
2081 if (mirror::kUseStringCompression) {
2082 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
2083 DCHECK_EQ(c_char_size, 1u);
2084 __ Bind(&compressed_string_preloop);
2085 __ Add(src_ptr, src_ptr, Operand(srcBegin));
2086 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2087 __ Bind(&compressed_string_loop);
2088 __ Ldrb(tmp1, MemOperand(src_ptr, c_char_size, PostIndex));
2089 __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
2090 __ Subs(num_chr, num_chr, Operand(1));
2091 __ B(gt, &compressed_string_loop);
2092 }
Scott Wakelingdf109d92016-04-22 11:35:56 +01002093
Tim Zhang25abd6c2016-01-19 23:39:24 +08002094 __ Bind(&done);
2095}
2096
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002097// Mirrors ARRAYCOPY_SHORT_CHAR_ARRAY_THRESHOLD in libcore, so we can choose to use the native
2098// implementation there for longer copy lengths.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002099static constexpr int32_t kSystemArrayCopyCharThreshold = 32;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002100
2101static void SetSystemArrayCopyLocationRequires(LocationSummary* locations,
2102 uint32_t at,
2103 HInstruction* input) {
2104 HIntConstant* const_input = input->AsIntConstant();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002105 if (const_input != nullptr && !vixl::aarch64::Assembler::IsImmAddSub(const_input->GetValue())) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002106 locations->SetInAt(at, Location::RequiresRegister());
2107 } else {
2108 locations->SetInAt(at, Location::RegisterOrConstant(input));
2109 }
2110}
2111
2112void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
2113 // Check to see if we have known failures that will cause us to have to bail out
2114 // to the runtime, and just generate the runtime call directly.
2115 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2116 HIntConstant* dst_pos = invoke->InputAt(3)->AsIntConstant();
2117
2118 // The positions must be non-negative.
2119 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2120 (dst_pos != nullptr && dst_pos->GetValue() < 0)) {
2121 // We will have to fail anyways.
2122 return;
2123 }
2124
2125 // The length must be >= 0 and not so long that we would (currently) prefer libcore's
2126 // native implementation.
2127 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2128 if (length != nullptr) {
2129 int32_t len = length->GetValue();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002130 if (len < 0 || len > kSystemArrayCopyCharThreshold) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002131 // Just call as normal.
2132 return;
2133 }
2134 }
2135
2136 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2137 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2138 LocationSummary::kCallOnSlowPath,
2139 kIntrinsified);
2140 // arraycopy(char[] src, int src_pos, char[] dst, int dst_pos, int length).
2141 locations->SetInAt(0, Location::RequiresRegister());
2142 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2143 locations->SetInAt(2, Location::RequiresRegister());
2144 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2145 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2146
2147 locations->AddTemp(Location::RequiresRegister());
2148 locations->AddTemp(Location::RequiresRegister());
2149 locations->AddTemp(Location::RequiresRegister());
2150}
2151
Scott Wakeling97c72b72016-06-24 16:19:36 +01002152static void CheckSystemArrayCopyPosition(MacroAssembler* masm,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002153 const Location& pos,
2154 const Register& input,
2155 const Location& length,
2156 SlowPathCodeARM64* slow_path,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002157 const Register& temp,
2158 bool length_is_input_length = false) {
2159 const int32_t length_offset = mirror::Array::LengthOffset().Int32Value();
2160 if (pos.IsConstant()) {
2161 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
2162 if (pos_const == 0) {
2163 if (!length_is_input_length) {
2164 // Check that length(input) >= length.
2165 __ Ldr(temp, MemOperand(input, length_offset));
2166 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
2167 __ B(slow_path->GetEntryLabel(), lt);
2168 }
2169 } else {
2170 // Check that length(input) >= pos.
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002171 __ Ldr(temp, MemOperand(input, length_offset));
2172 __ Subs(temp, temp, pos_const);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002173 __ B(slow_path->GetEntryLabel(), lt);
2174
2175 // Check that (length(input) - pos) >= length.
2176 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
2177 __ B(slow_path->GetEntryLabel(), lt);
2178 }
2179 } else if (length_is_input_length) {
2180 // The only way the copy can succeed is if pos is zero.
2181 __ Cbnz(WRegisterFrom(pos), slow_path->GetEntryLabel());
2182 } else {
2183 // Check that pos >= 0.
2184 Register pos_reg = WRegisterFrom(pos);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002185 __ Tbnz(pos_reg, pos_reg.GetSizeInBits() - 1, slow_path->GetEntryLabel());
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002186
2187 // Check that pos <= length(input) && (length(input) - pos) >= length.
2188 __ Ldr(temp, MemOperand(input, length_offset));
2189 __ Subs(temp, temp, pos_reg);
2190 // Ccmp if length(input) >= pos, else definitely bail to slow path (N!=V == lt).
2191 __ Ccmp(temp, OperandFrom(length, Primitive::kPrimInt), NFlag, ge);
2192 __ B(slow_path->GetEntryLabel(), lt);
2193 }
2194}
2195
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002196// Compute base source address, base destination address, and end
2197// source address for System.arraycopy* intrinsics in `src_base`,
2198// `dst_base` and `src_end` respectively.
Scott Wakeling97c72b72016-06-24 16:19:36 +01002199static void GenSystemArrayCopyAddresses(MacroAssembler* masm,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002200 Primitive::Type type,
2201 const Register& src,
2202 const Location& src_pos,
2203 const Register& dst,
2204 const Location& dst_pos,
2205 const Location& copy_length,
2206 const Register& src_base,
2207 const Register& dst_base,
2208 const Register& src_end) {
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002209 // This routine is used by the SystemArrayCopy and the SystemArrayCopyChar intrinsics.
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002210 DCHECK(type == Primitive::kPrimNot || type == Primitive::kPrimChar)
Roland Levillainebea3d22016-04-12 15:42:57 +01002211 << "Unexpected element type: " << type;
2212 const int32_t element_size = Primitive::ComponentSize(type);
2213 const int32_t element_size_shift = Primitive::ComponentSizeShift(type);
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002214 const uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002215
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002216 if (src_pos.IsConstant()) {
2217 int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002218 __ Add(src_base, src, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002219 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002220 __ Add(src_base, src, data_offset);
2221 __ Add(src_base, src_base, Operand(XRegisterFrom(src_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002222 }
2223
2224 if (dst_pos.IsConstant()) {
2225 int32_t constant = dst_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002226 __ Add(dst_base, dst, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002227 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002228 __ Add(dst_base, dst, data_offset);
2229 __ Add(dst_base, dst_base, Operand(XRegisterFrom(dst_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002230 }
2231
2232 if (copy_length.IsConstant()) {
2233 int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002234 __ Add(src_end, src_base, element_size * constant);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002235 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002236 __ Add(src_end, src_base, Operand(XRegisterFrom(copy_length), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002237 }
2238}
2239
2240void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01002241 MacroAssembler* masm = GetVIXLAssembler();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002242 LocationSummary* locations = invoke->GetLocations();
2243 Register src = XRegisterFrom(locations->InAt(0));
2244 Location src_pos = locations->InAt(1);
2245 Register dst = XRegisterFrom(locations->InAt(2));
2246 Location dst_pos = locations->InAt(3);
2247 Location length = locations->InAt(4);
2248
2249 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2250 codegen_->AddSlowPath(slow_path);
2251
2252 // If source and destination are the same, take the slow path. Overlapping copy regions must be
2253 // copied in reverse and we can't know in all cases if it's needed.
2254 __ Cmp(src, dst);
2255 __ B(slow_path->GetEntryLabel(), eq);
2256
2257 // Bail out if the source is null.
2258 __ Cbz(src, slow_path->GetEntryLabel());
2259
2260 // Bail out if the destination is null.
2261 __ Cbz(dst, slow_path->GetEntryLabel());
2262
2263 if (!length.IsConstant()) {
Vladimir Markoc5646202016-11-28 16:03:15 +00002264 // Merge the following two comparisons into one:
2265 // If the length is negative, bail out (delegate to libcore's native implementation).
2266 // If the length > 32 then (currently) prefer libcore's native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002267 __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold);
Vladimir Markoc5646202016-11-28 16:03:15 +00002268 __ B(slow_path->GetEntryLabel(), hi);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002269 } else {
2270 // We have already checked in the LocationsBuilder for the constant case.
2271 DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0);
2272 DCHECK_LE(length.GetConstant()->AsIntConstant()->GetValue(), 32);
2273 }
2274
2275 Register src_curr_addr = WRegisterFrom(locations->GetTemp(0));
2276 Register dst_curr_addr = WRegisterFrom(locations->GetTemp(1));
2277 Register src_stop_addr = WRegisterFrom(locations->GetTemp(2));
2278
2279 CheckSystemArrayCopyPosition(masm,
2280 src_pos,
2281 src,
2282 length,
2283 slow_path,
2284 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002285 false);
2286
2287 CheckSystemArrayCopyPosition(masm,
2288 dst_pos,
2289 dst,
2290 length,
2291 slow_path,
2292 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002293 false);
2294
2295 src_curr_addr = src_curr_addr.X();
2296 dst_curr_addr = dst_curr_addr.X();
2297 src_stop_addr = src_stop_addr.X();
2298
2299 GenSystemArrayCopyAddresses(masm,
2300 Primitive::kPrimChar,
2301 src,
2302 src_pos,
2303 dst,
2304 dst_pos,
2305 length,
2306 src_curr_addr,
2307 dst_curr_addr,
2308 src_stop_addr);
2309
2310 // Iterate over the arrays and do a raw copy of the chars.
2311 const int32_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2312 UseScratchRegisterScope temps(masm);
2313 Register tmp = temps.AcquireW();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002314 vixl::aarch64::Label loop, done;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002315 __ Bind(&loop);
2316 __ Cmp(src_curr_addr, src_stop_addr);
2317 __ B(&done, eq);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002318 __ Ldrh(tmp, MemOperand(src_curr_addr, char_size, PostIndex));
2319 __ Strh(tmp, MemOperand(dst_curr_addr, char_size, PostIndex));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002320 __ B(&loop);
2321 __ Bind(&done);
2322
2323 __ Bind(slow_path->GetExitLabel());
2324}
2325
donghui.baic2ec9ad2016-03-10 14:02:55 +08002326// We can choose to use the native implementation there for longer copy lengths.
2327static constexpr int32_t kSystemArrayCopyThreshold = 128;
2328
2329// CodeGenerator::CreateSystemArrayCopyLocationSummary use three temporary registers.
2330// We want to use two temporary registers in order to reduce the register pressure in arm64.
2331// So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary.
2332void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002333 // The only read barrier implementation supporting the
2334 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2335 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain3d312422016-06-23 13:53:42 +01002336 return;
2337 }
2338
donghui.baic2ec9ad2016-03-10 14:02:55 +08002339 // Check to see if we have known failures that will cause us to have to bail out
2340 // to the runtime, and just generate the runtime call directly.
2341 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2342 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
2343
2344 // The positions must be non-negative.
2345 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2346 (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
2347 // We will have to fail anyways.
2348 return;
2349 }
2350
2351 // The length must be >= 0.
2352 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2353 if (length != nullptr) {
2354 int32_t len = length->GetValue();
2355 if (len < 0 || len >= kSystemArrayCopyThreshold) {
2356 // Just call as normal.
2357 return;
2358 }
2359 }
2360
2361 SystemArrayCopyOptimizations optimizations(invoke);
2362
2363 if (optimizations.GetDestinationIsSource()) {
2364 if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) {
2365 // We only support backward copying if source and destination are the same.
2366 return;
2367 }
2368 }
2369
2370 if (optimizations.GetDestinationIsPrimitiveArray() || optimizations.GetSourceIsPrimitiveArray()) {
2371 // We currently don't intrinsify primitive copying.
2372 return;
2373 }
2374
2375 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2376 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2377 LocationSummary::kCallOnSlowPath,
2378 kIntrinsified);
2379 // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length).
2380 locations->SetInAt(0, Location::RequiresRegister());
2381 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2382 locations->SetInAt(2, Location::RequiresRegister());
2383 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2384 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2385
2386 locations->AddTemp(Location::RequiresRegister());
2387 locations->AddTemp(Location::RequiresRegister());
Roland Levillain0b671c02016-08-19 12:02:34 +01002388 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2389 // Temporary register IP0, obtained from the VIXL scratch register
2390 // pool, cannot be used in ReadBarrierSystemArrayCopySlowPathARM64
2391 // (because that register is clobbered by ReadBarrierMarkRegX
Roland Levillain54f869e2017-03-06 13:54:11 +00002392 // entry points). It cannot be used in calls to
2393 // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier
2394 // either. For these reasons, get a third extra temporary register
2395 // from the register allocator.
Roland Levillain0b671c02016-08-19 12:02:34 +01002396 locations->AddTemp(Location::RequiresRegister());
Roland Levillain54f869e2017-03-06 13:54:11 +00002397 } else {
2398 // Cases other than Baker read barriers: the third temporary will
2399 // be acquired from the VIXL scratch register pool.
Roland Levillain0b671c02016-08-19 12:02:34 +01002400 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002401}
2402
2403void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002404 // The only read barrier implementation supporting the
2405 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2406 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01002407
Scott Wakeling97c72b72016-06-24 16:19:36 +01002408 MacroAssembler* masm = GetVIXLAssembler();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002409 LocationSummary* locations = invoke->GetLocations();
2410
2411 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2412 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2413 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2414 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Roland Levillain0b671c02016-08-19 12:02:34 +01002415 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002416
2417 Register src = XRegisterFrom(locations->InAt(0));
2418 Location src_pos = locations->InAt(1);
2419 Register dest = XRegisterFrom(locations->InAt(2));
2420 Location dest_pos = locations->InAt(3);
2421 Location length = locations->InAt(4);
2422 Register temp1 = WRegisterFrom(locations->GetTemp(0));
Roland Levillain0b671c02016-08-19 12:02:34 +01002423 Location temp1_loc = LocationFrom(temp1);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002424 Register temp2 = WRegisterFrom(locations->GetTemp(1));
Roland Levillain0b671c02016-08-19 12:02:34 +01002425 Location temp2_loc = LocationFrom(temp2);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002426
Roland Levillain0b671c02016-08-19 12:02:34 +01002427 SlowPathCodeARM64* intrinsic_slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2428 codegen_->AddSlowPath(intrinsic_slow_path);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002429
Scott Wakeling97c72b72016-06-24 16:19:36 +01002430 vixl::aarch64::Label conditions_on_positions_validated;
donghui.baic2ec9ad2016-03-10 14:02:55 +08002431 SystemArrayCopyOptimizations optimizations(invoke);
2432
donghui.baic2ec9ad2016-03-10 14:02:55 +08002433 // If source and destination are the same, we go to slow path if we need to do
2434 // forward copying.
2435 if (src_pos.IsConstant()) {
2436 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
2437 if (dest_pos.IsConstant()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002438 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
2439 if (optimizations.GetDestinationIsSource()) {
2440 // Checked when building locations.
2441 DCHECK_GE(src_pos_constant, dest_pos_constant);
2442 } else if (src_pos_constant < dest_pos_constant) {
2443 __ Cmp(src, dest);
Roland Levillain0b671c02016-08-19 12:02:34 +01002444 __ B(intrinsic_slow_path->GetEntryLabel(), eq);
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002445 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002446 // Checked when building locations.
2447 DCHECK(!optimizations.GetDestinationIsSource()
2448 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
2449 } else {
2450 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002451 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002452 __ B(&conditions_on_positions_validated, ne);
2453 }
2454 __ Cmp(WRegisterFrom(dest_pos), src_pos_constant);
Roland Levillain0b671c02016-08-19 12:02:34 +01002455 __ B(intrinsic_slow_path->GetEntryLabel(), gt);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002456 }
2457 } else {
2458 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002459 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002460 __ B(&conditions_on_positions_validated, ne);
2461 }
2462 __ Cmp(RegisterFrom(src_pos, invoke->InputAt(1)->GetType()),
2463 OperandFrom(dest_pos, invoke->InputAt(3)->GetType()));
Roland Levillain0b671c02016-08-19 12:02:34 +01002464 __ B(intrinsic_slow_path->GetEntryLabel(), lt);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002465 }
2466
2467 __ Bind(&conditions_on_positions_validated);
2468
2469 if (!optimizations.GetSourceIsNotNull()) {
2470 // Bail out if the source is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01002471 __ Cbz(src, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002472 }
2473
2474 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
2475 // Bail out if the destination is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01002476 __ Cbz(dest, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002477 }
2478
2479 // We have already checked in the LocationsBuilder for the constant case.
2480 if (!length.IsConstant() &&
2481 !optimizations.GetCountIsSourceLength() &&
2482 !optimizations.GetCountIsDestinationLength()) {
Vladimir Markoc5646202016-11-28 16:03:15 +00002483 // Merge the following two comparisons into one:
2484 // If the length is negative, bail out (delegate to libcore's native implementation).
2485 // If the length >= 128 then (currently) prefer native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002486 __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold);
Vladimir Markoc5646202016-11-28 16:03:15 +00002487 __ B(intrinsic_slow_path->GetEntryLabel(), hs);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002488 }
2489 // Validity checks: source.
2490 CheckSystemArrayCopyPosition(masm,
2491 src_pos,
2492 src,
2493 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002494 intrinsic_slow_path,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002495 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002496 optimizations.GetCountIsSourceLength());
2497
2498 // Validity checks: dest.
2499 CheckSystemArrayCopyPosition(masm,
2500 dest_pos,
2501 dest,
2502 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002503 intrinsic_slow_path,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002504 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002505 optimizations.GetCountIsDestinationLength());
2506 {
2507 // We use a block to end the scratch scope before the write barrier, thus
2508 // freeing the temporary registers so they can be used in `MarkGCCard`.
2509 UseScratchRegisterScope temps(masm);
Roland Levillain54f869e2017-03-06 13:54:11 +00002510 Register temp3;
2511 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2512 temp3 = WRegisterFrom(locations->GetTemp(2));
2513 } else {
2514 temp3 = temps.AcquireW();
2515 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002516
donghui.baic2ec9ad2016-03-10 14:02:55 +08002517 if (!optimizations.GetDoesNotNeedTypeCheck()) {
2518 // Check whether all elements of the source array are assignable to the component
2519 // type of the destination array. We do two checks: the classes are the same,
2520 // or the destination is Object[]. If none of these checks succeed, we go to the
2521 // slow path.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002522
Roland Levillain0b671c02016-08-19 12:02:34 +01002523 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2524 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2525 // /* HeapReference<Class> */ temp1 = src->klass_
2526 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2527 temp1_loc,
2528 src.W(),
2529 class_offset,
2530 temp2,
2531 /* needs_null_check */ false,
2532 /* use_load_acquire */ false);
2533 // Bail out if the source is not a non primitive array.
2534 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2535 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2536 temp1_loc,
2537 temp1,
2538 component_offset,
2539 temp2,
2540 /* needs_null_check */ false,
2541 /* use_load_acquire */ false);
2542 __ Cbz(temp1, intrinsic_slow_path->GetEntryLabel());
2543 // If heap poisoning is enabled, `temp1` has been unpoisoned
2544 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2545 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
2546 __ Ldrh(temp1, HeapOperand(temp1, primitive_offset));
2547 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2548 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002549 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002550
2551 // /* HeapReference<Class> */ temp1 = dest->klass_
2552 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2553 temp1_loc,
2554 dest.W(),
2555 class_offset,
2556 temp2,
2557 /* needs_null_check */ false,
2558 /* use_load_acquire */ false);
2559
2560 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2561 // Bail out if the destination is not a non primitive array.
2562 //
2563 // Register `temp1` is not trashed by the read barrier emitted
2564 // by GenerateFieldLoadWithBakerReadBarrier below, as that
2565 // method produces a call to a ReadBarrierMarkRegX entry point,
2566 // which saves all potentially live registers, including
2567 // temporaries such a `temp1`.
2568 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2569 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2570 temp2_loc,
2571 temp1,
2572 component_offset,
2573 temp3,
2574 /* needs_null_check */ false,
2575 /* use_load_acquire */ false);
2576 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2577 // If heap poisoning is enabled, `temp2` has been unpoisoned
2578 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2579 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2580 __ Ldrh(temp2, HeapOperand(temp2, primitive_offset));
2581 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2582 __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
2583 }
2584
2585 // For the same reason given earlier, `temp1` is not trashed by the
2586 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
2587 // /* HeapReference<Class> */ temp2 = src->klass_
2588 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2589 temp2_loc,
2590 src.W(),
2591 class_offset,
2592 temp3,
2593 /* needs_null_check */ false,
2594 /* use_load_acquire */ false);
2595 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
2596 __ Cmp(temp1, temp2);
2597
2598 if (optimizations.GetDestinationIsTypedObjectArray()) {
2599 vixl::aarch64::Label do_copy;
2600 __ B(&do_copy, eq);
2601 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2602 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2603 temp1_loc,
2604 temp1,
2605 component_offset,
2606 temp2,
2607 /* needs_null_check */ false,
2608 /* use_load_acquire */ false);
2609 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2610 // We do not need to emit a read barrier for the following
2611 // heap reference load, as `temp1` is only used in a
2612 // comparison with null below, and this reference is not
2613 // kept afterwards.
2614 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2615 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
2616 __ Bind(&do_copy);
2617 } else {
2618 __ B(intrinsic_slow_path->GetEntryLabel(), ne);
2619 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002620 } else {
Roland Levillain0b671c02016-08-19 12:02:34 +01002621 // Non read barrier code.
2622
2623 // /* HeapReference<Class> */ temp1 = dest->klass_
2624 __ Ldr(temp1, MemOperand(dest, class_offset));
2625 // /* HeapReference<Class> */ temp2 = src->klass_
2626 __ Ldr(temp2, MemOperand(src, class_offset));
2627 bool did_unpoison = false;
2628 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2629 !optimizations.GetSourceIsNonPrimitiveArray()) {
2630 // One or two of the references need to be unpoisoned. Unpoison them
2631 // both to make the identity check valid.
2632 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2633 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2634 did_unpoison = true;
2635 }
2636
2637 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2638 // Bail out if the destination is not a non primitive array.
2639 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2640 __ Ldr(temp3, HeapOperand(temp1, component_offset));
2641 __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
2642 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2643 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2644 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2645 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2646 __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
2647 }
2648
2649 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2650 // Bail out if the source is not a non primitive array.
2651 // /* HeapReference<Class> */ temp3 = temp2->component_type_
2652 __ Ldr(temp3, HeapOperand(temp2, component_offset));
2653 __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
2654 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2655 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2656 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2657 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2658 __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
2659 }
2660
2661 __ Cmp(temp1, temp2);
2662
2663 if (optimizations.GetDestinationIsTypedObjectArray()) {
2664 vixl::aarch64::Label do_copy;
2665 __ B(&do_copy, eq);
2666 if (!did_unpoison) {
2667 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2668 }
2669 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2670 __ Ldr(temp1, HeapOperand(temp1, component_offset));
2671 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2672 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2673 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2674 // No need to unpoison the result, we're comparing against null.
2675 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
2676 __ Bind(&do_copy);
2677 } else {
2678 __ B(intrinsic_slow_path->GetEntryLabel(), ne);
2679 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002680 }
2681 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2682 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2683 // Bail out if the source is not a non primitive array.
Roland Levillain0b671c02016-08-19 12:02:34 +01002684 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2685 // /* HeapReference<Class> */ temp1 = src->klass_
2686 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2687 temp1_loc,
2688 src.W(),
2689 class_offset,
2690 temp2,
2691 /* needs_null_check */ false,
2692 /* use_load_acquire */ false);
2693 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2694 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2695 temp2_loc,
2696 temp1,
2697 component_offset,
2698 temp3,
2699 /* needs_null_check */ false,
2700 /* use_load_acquire */ false);
2701 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2702 // If heap poisoning is enabled, `temp2` has been unpoisoned
2703 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2704 } else {
2705 // /* HeapReference<Class> */ temp1 = src->klass_
2706 __ Ldr(temp1, HeapOperand(src.W(), class_offset));
2707 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2708 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2709 __ Ldr(temp2, HeapOperand(temp1, component_offset));
2710 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2711 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2712 }
2713 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2714 __ Ldrh(temp2, HeapOperand(temp2, primitive_offset));
donghui.baic2ec9ad2016-03-10 14:02:55 +08002715 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Roland Levillain0b671c02016-08-19 12:02:34 +01002716 __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002717 }
2718
Roland Levillain1663d162017-03-17 15:15:21 +00002719 if (length.IsConstant() && length.GetConstant()->AsIntConstant()->GetValue() == 0) {
2720 // Null constant length: not need to emit the loop code at all.
Roland Levillain0b671c02016-08-19 12:02:34 +01002721 } else {
Roland Levillain1663d162017-03-17 15:15:21 +00002722 Register src_curr_addr = temp1.X();
2723 Register dst_curr_addr = temp2.X();
2724 Register src_stop_addr = temp3.X();
2725 vixl::aarch64::Label done;
2726 const Primitive::Type type = Primitive::kPrimNot;
2727 const int32_t element_size = Primitive::ComponentSize(type);
2728
2729 if (length.IsRegister()) {
2730 // Don't enter the copy loop if the length is null.
2731 __ Cbz(WRegisterFrom(length), &done);
2732 }
2733
2734 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2735 // TODO: Also convert this intrinsic to the IsGcMarking strategy?
2736
2737 // SystemArrayCopy implementation for Baker read barriers (see
2738 // also CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier):
2739 //
2740 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
2741 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
2742 // bool is_gray = (rb_state == ReadBarrier::GrayState());
2743 // if (is_gray) {
2744 // // Slow-path copy.
2745 // do {
2746 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
2747 // } while (src_ptr != end_ptr)
2748 // } else {
2749 // // Fast-path copy.
2750 // do {
2751 // *dest_ptr++ = *src_ptr++;
2752 // } while (src_ptr != end_ptr)
2753 // }
2754
2755 // Make sure `tmp` is not IP0, as it is clobbered by
2756 // ReadBarrierMarkRegX entry points in
2757 // ReadBarrierSystemArrayCopySlowPathARM64.
2758 temps.Exclude(ip0);
Roland Levillain0b671c02016-08-19 12:02:34 +01002759 Register tmp = temps.AcquireW();
Roland Levillain1663d162017-03-17 15:15:21 +00002760 DCHECK_NE(LocationFrom(tmp).reg(), IP0);
2761
2762 // /* int32_t */ monitor = src->monitor_
2763 __ Ldr(tmp, HeapOperand(src.W(), monitor_offset));
2764 // /* LockWord */ lock_word = LockWord(monitor)
2765 static_assert(sizeof(LockWord) == sizeof(int32_t),
2766 "art::LockWord and int32_t have different sizes.");
2767
2768 // Introduce a dependency on the lock_word including rb_state,
2769 // to prevent load-load reordering, and without using
2770 // a memory barrier (which would be more expensive).
2771 // `src` is unchanged by this operation, but its value now depends
2772 // on `tmp`.
2773 __ Add(src.X(), src.X(), Operand(tmp.X(), LSR, 32));
2774
2775 // Compute base source address, base destination address, and end
2776 // source address for System.arraycopy* intrinsics in `src_base`,
2777 // `dst_base` and `src_end` respectively.
2778 // Note that `src_curr_addr` is computed from from `src` (and
2779 // `src_pos`) here, and thus honors the artificial dependency
2780 // of `src` on `tmp`.
2781 GenSystemArrayCopyAddresses(masm,
2782 type,
2783 src,
2784 src_pos,
2785 dest,
2786 dest_pos,
2787 length,
2788 src_curr_addr,
2789 dst_curr_addr,
2790 src_stop_addr);
2791
2792 // Slow path used to copy array when `src` is gray.
2793 SlowPathCodeARM64* read_barrier_slow_path =
2794 new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathARM64(invoke, LocationFrom(tmp));
2795 codegen_->AddSlowPath(read_barrier_slow_path);
2796
2797 // Given the numeric representation, it's enough to check the low bit of the rb_state.
2798 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
2799 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
2800 __ Tbnz(tmp, LockWord::kReadBarrierStateShift, read_barrier_slow_path->GetEntryLabel());
2801
2802 // Fast-path copy.
2803 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2804 // poison/unpoison.
2805 vixl::aarch64::Label loop;
2806 __ Bind(&loop);
Roland Levillain0b671c02016-08-19 12:02:34 +01002807 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
2808 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
Roland Levillain1663d162017-03-17 15:15:21 +00002809 __ Cmp(src_curr_addr, src_stop_addr);
2810 __ B(&loop, ne);
2811
2812 __ Bind(read_barrier_slow_path->GetExitLabel());
2813 } else {
2814 // Non read barrier code.
2815 // Compute base source address, base destination address, and end
2816 // source address for System.arraycopy* intrinsics in `src_base`,
2817 // `dst_base` and `src_end` respectively.
2818 GenSystemArrayCopyAddresses(masm,
2819 type,
2820 src,
2821 src_pos,
2822 dest,
2823 dest_pos,
2824 length,
2825 src_curr_addr,
2826 dst_curr_addr,
2827 src_stop_addr);
2828 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2829 // poison/unpoison.
2830 vixl::aarch64::Label loop;
2831 __ Bind(&loop);
2832 {
2833 Register tmp = temps.AcquireW();
2834 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
2835 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
2836 }
2837 __ Cmp(src_curr_addr, src_stop_addr);
2838 __ B(&loop, ne);
Roland Levillain0b671c02016-08-19 12:02:34 +01002839 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002840 __ Bind(&done);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002841 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002842 }
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002843
donghui.baic2ec9ad2016-03-10 14:02:55 +08002844 // We only need one card marking on the destination array.
2845 codegen_->MarkGCCard(dest.W(), Register(), /* value_can_be_null */ false);
2846
Roland Levillain0b671c02016-08-19 12:02:34 +01002847 __ Bind(intrinsic_slow_path->GetExitLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002848}
2849
Anton Kirilova3ffea22016-04-07 17:02:37 +01002850static void GenIsInfinite(LocationSummary* locations,
2851 bool is64bit,
Scott Wakeling97c72b72016-06-24 16:19:36 +01002852 MacroAssembler* masm) {
Anton Kirilova3ffea22016-04-07 17:02:37 +01002853 Operand infinity;
2854 Register out;
2855
2856 if (is64bit) {
2857 infinity = kPositiveInfinityDouble;
2858 out = XRegisterFrom(locations->Out());
2859 } else {
2860 infinity = kPositiveInfinityFloat;
2861 out = WRegisterFrom(locations->Out());
2862 }
2863
Scott Wakeling97c72b72016-06-24 16:19:36 +01002864 const Register zero = vixl::aarch64::Assembler::AppropriateZeroRegFor(out);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002865
2866 MoveFPToInt(locations, is64bit, masm);
2867 __ Eor(out, out, infinity);
2868 // We don't care about the sign bit, so shift left.
2869 __ Cmp(zero, Operand(out, LSL, 1));
2870 __ Cset(out, eq);
2871}
2872
2873void IntrinsicLocationsBuilderARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2874 CreateFPToIntLocations(arena_, invoke);
2875}
2876
2877void IntrinsicCodeGeneratorARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2878 GenIsInfinite(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
2879}
2880
2881void IntrinsicLocationsBuilderARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2882 CreateFPToIntLocations(arena_, invoke);
2883}
2884
2885void IntrinsicCodeGeneratorARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2886 GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
2887}
2888
TatWai Chongd8c052a2016-11-02 16:12:48 +08002889void IntrinsicLocationsBuilderARM64::VisitReferenceGetReferent(HInvoke* invoke) {
2890 if (kEmitCompilerReadBarrier) {
2891 // Do not intrinsify this call with the read barrier configuration.
2892 return;
2893 }
2894 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2895 LocationSummary::kCallOnSlowPath,
2896 kIntrinsified);
2897 locations->SetInAt(0, Location::RequiresRegister());
2898 locations->SetOut(Location::SameAsFirstInput());
2899 locations->AddTemp(Location::RequiresRegister());
2900}
2901
2902void IntrinsicCodeGeneratorARM64::VisitReferenceGetReferent(HInvoke* invoke) {
2903 DCHECK(!kEmitCompilerReadBarrier);
2904 MacroAssembler* masm = GetVIXLAssembler();
2905 LocationSummary* locations = invoke->GetLocations();
2906
2907 Register obj = InputRegisterAt(invoke, 0);
2908 Register out = OutputRegister(invoke);
2909
2910 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2911 codegen_->AddSlowPath(slow_path);
2912
2913 // Load ArtMethod first.
2914 HInvokeStaticOrDirect* invoke_direct = invoke->AsInvokeStaticOrDirect();
2915 DCHECK(invoke_direct != nullptr);
2916 Register temp0 = XRegisterFrom(codegen_->GenerateCalleeMethodStaticOrDirectCall(
2917 invoke_direct, locations->GetTemp(0)));
2918
2919 // Now get declaring class.
2920 __ Ldr(temp0.W(), MemOperand(temp0, ArtMethod::DeclaringClassOffset().Int32Value()));
2921
2922 uint32_t slow_path_flag_offset = codegen_->GetReferenceSlowFlagOffset();
2923 uint32_t disable_flag_offset = codegen_->GetReferenceDisableFlagOffset();
2924 DCHECK_NE(slow_path_flag_offset, 0u);
2925 DCHECK_NE(disable_flag_offset, 0u);
2926 DCHECK_NE(slow_path_flag_offset, disable_flag_offset);
2927
2928 // Check static flags that prevent using intrinsic.
2929 if (slow_path_flag_offset == disable_flag_offset + 1) {
2930 // Load two adjacent flags in one 64-bit load.
2931 __ Ldr(temp0, MemOperand(temp0, disable_flag_offset));
2932 } else {
2933 UseScratchRegisterScope temps(masm);
2934 Register temp1 = temps.AcquireW();
2935 __ Ldr(temp1.W(), MemOperand(temp0, disable_flag_offset));
2936 __ Ldr(temp0.W(), MemOperand(temp0, slow_path_flag_offset));
2937 __ Orr(temp0, temp1, temp0);
2938 }
2939 __ Cbnz(temp0, slow_path->GetEntryLabel());
2940
Artem Serov914d7a82017-02-07 14:33:49 +00002941 {
2942 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
2943 vixl::EmissionCheckScope guard(codegen_->GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
2944 // Fast path.
2945 __ Ldr(out, HeapOperand(obj, mirror::Reference::ReferentOffset().Int32Value()));
2946 codegen_->MaybeRecordImplicitNullCheck(invoke);
2947 }
TatWai Chongd8c052a2016-11-02 16:12:48 +08002948 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(out);
2949 __ Bind(slow_path->GetExitLabel());
2950}
2951
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002952void IntrinsicLocationsBuilderARM64::VisitIntegerValueOf(HInvoke* invoke) {
2953 InvokeRuntimeCallingConvention calling_convention;
2954 IntrinsicVisitor::ComputeIntegerValueOfLocations(
2955 invoke,
2956 codegen_,
2957 calling_convention.GetReturnLocation(Primitive::kPrimNot),
2958 Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
2959}
2960
2961void IntrinsicCodeGeneratorARM64::VisitIntegerValueOf(HInvoke* invoke) {
2962 IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo();
2963 LocationSummary* locations = invoke->GetLocations();
2964 MacroAssembler* masm = GetVIXLAssembler();
2965
2966 Register out = RegisterFrom(locations->Out(), Primitive::kPrimNot);
2967 UseScratchRegisterScope temps(masm);
2968 Register temp = temps.AcquireW();
2969 InvokeRuntimeCallingConvention calling_convention;
2970 Register argument = calling_convention.GetRegisterAt(0);
2971 if (invoke->InputAt(0)->IsConstant()) {
2972 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
2973 if (value >= info.low && value <= info.high) {
2974 // Just embed the j.l.Integer in the code.
2975 ScopedObjectAccess soa(Thread::Current());
2976 mirror::Object* boxed = info.cache->Get(value + (-info.low));
2977 DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed));
2978 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(boxed));
2979 __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
2980 } else {
2981 // Allocate and initialize a new j.l.Integer.
2982 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
2983 // JIT object table.
2984 uint32_t address =
2985 dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2986 __ Ldr(argument.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
2987 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2988 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2989 __ Mov(temp.W(), value);
2990 __ Str(temp.W(), HeapOperand(out.W(), info.value_offset));
2991 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2992 // one.
2993 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2994 }
2995 } else {
2996 Register in = RegisterFrom(locations->InAt(0), Primitive::kPrimInt);
2997 // Check bounds of our cache.
2998 __ Add(out.W(), in.W(), -info.low);
2999 __ Cmp(out.W(), info.high - info.low + 1);
3000 vixl::aarch64::Label allocate, done;
3001 __ B(&allocate, hs);
3002 // If the value is within the bounds, load the j.l.Integer directly from the array.
3003 uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
3004 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.cache));
3005 __ Ldr(temp.W(), codegen_->DeduplicateBootImageAddressLiteral(data_offset + address));
3006 MemOperand source = HeapOperand(
3007 temp, out.X(), LSL, Primitive::ComponentSizeShift(Primitive::kPrimNot));
3008 codegen_->Load(Primitive::kPrimNot, out, source);
3009 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(out);
3010 __ B(&done);
3011 __ Bind(&allocate);
3012 // Otherwise allocate and initialize a new j.l.Integer.
3013 address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
3014 __ Ldr(argument.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
3015 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
3016 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
3017 __ Str(in.W(), HeapOperand(out.W(), info.value_offset));
3018 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
3019 // one.
3020 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3021 __ Bind(&done);
3022 }
3023}
3024
Aart Bik2f9fcc92016-03-01 15:16:54 -08003025UNIMPLEMENTED_INTRINSIC(ARM64, IntegerHighestOneBit)
3026UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit)
3027UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit)
3028UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit)
Andreas Gampe878d58c2015-01-15 23:24:00 -08003029
Aart Bikff7d89c2016-11-07 08:49:28 -08003030UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf);
3031UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08003032UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferAppend);
3033UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferLength);
3034UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferToString);
3035UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderAppend);
3036UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderLength);
3037UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08003038
Aart Bik0e54c012016-03-04 12:08:31 -08003039// 1.8.
3040UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt)
3041UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong)
3042UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt)
3043UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong)
3044UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08003045
Aart Bik2f9fcc92016-03-01 15:16:54 -08003046UNREACHABLE_INTRINSICS(ARM64)
Roland Levillain4d027112015-07-01 15:41:14 +01003047
3048#undef __
3049
Andreas Gampe878d58c2015-01-15 23:24:00 -08003050} // namespace arm64
3051} // namespace art