blob: b107280bd5b4c8c267f6346525c97aadd69ca4d3 [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
118 if (invoke_->IsInvokeStaticOrDirect()) {
Nicolas Geoffray94015b92015-06-04 18:21:04 +0100119 codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
120 LocationFrom(kArtMethodRegister));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800121 } else {
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000122 codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800123 }
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000124 codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800125
126 // Copy the result back to the expected output.
127 Location out = invoke_->GetLocations()->Out();
128 if (out.IsValid()) {
129 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
130 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
131 MoveFromReturnRegister(out, invoke_->GetType(), codegen);
132 }
133
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000134 RestoreLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800135 __ B(GetExitLabel());
136 }
137
Alexandre Rames9931f312015-06-19 14:47:01 +0100138 const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathARM64"; }
139
Andreas Gampe878d58c2015-01-15 23:24:00 -0800140 private:
141 // The instruction where this slow path is happening.
142 HInvoke* const invoke_;
143
144 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM64);
145};
146
Roland Levillain0b671c02016-08-19 12:02:34 +0100147// Slow path implementing the SystemArrayCopy intrinsic copy loop with read barriers.
148class ReadBarrierSystemArrayCopySlowPathARM64 : public SlowPathCodeARM64 {
149 public:
150 ReadBarrierSystemArrayCopySlowPathARM64(HInstruction* instruction, Location tmp)
151 : SlowPathCodeARM64(instruction), tmp_(tmp) {
152 DCHECK(kEmitCompilerReadBarrier);
153 DCHECK(kUseBakerReadBarrier);
154 }
155
156 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
157 CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
158 LocationSummary* locations = instruction_->GetLocations();
159 DCHECK(locations->CanCall());
160 DCHECK(instruction_->IsInvokeStaticOrDirect())
161 << "Unexpected instruction in read barrier arraycopy slow path: "
162 << instruction_->DebugName();
163 DCHECK(instruction_->GetLocations()->Intrinsified());
164 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kSystemArrayCopy);
165
166 const int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
167
168 Register src_curr_addr = XRegisterFrom(locations->GetTemp(0));
169 Register dst_curr_addr = XRegisterFrom(locations->GetTemp(1));
170 Register src_stop_addr = XRegisterFrom(locations->GetTemp(2));
171 Register tmp_reg = WRegisterFrom(tmp_);
172
173 __ Bind(GetEntryLabel());
174 vixl::aarch64::Label slow_copy_loop;
175 __ Bind(&slow_copy_loop);
176 __ Ldr(tmp_reg, MemOperand(src_curr_addr, element_size, PostIndex));
177 codegen->GetAssembler()->MaybeUnpoisonHeapReference(tmp_reg);
178 // TODO: Inline the mark bit check before calling the runtime?
179 // tmp_reg = ReadBarrier::Mark(tmp_reg);
180 // No need to save live registers; it's taken care of by the
181 // entrypoint. Also, there is no need to update the stack mask,
182 // as this runtime call will not trigger a garbage collection.
183 // (See ReadBarrierMarkSlowPathARM64::EmitNativeCode for more
184 // explanations.)
185 DCHECK_NE(tmp_.reg(), LR);
186 DCHECK_NE(tmp_.reg(), WSP);
187 DCHECK_NE(tmp_.reg(), WZR);
188 // IP0 is used internally by the ReadBarrierMarkRegX entry point
189 // as a temporary (and not preserved). It thus cannot be used by
190 // any live register in this slow path.
191 DCHECK_NE(LocationFrom(src_curr_addr).reg(), IP0);
192 DCHECK_NE(LocationFrom(dst_curr_addr).reg(), IP0);
193 DCHECK_NE(LocationFrom(src_stop_addr).reg(), IP0);
194 DCHECK_NE(tmp_.reg(), IP0);
195 DCHECK(0 <= tmp_.reg() && tmp_.reg() < kNumberOfWRegisters) << tmp_.reg();
196 int32_t entry_point_offset =
197 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(tmp_.reg());
198 // This runtime call does not require a stack map.
199 codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
200 codegen->GetAssembler()->MaybePoisonHeapReference(tmp_reg);
201 __ Str(tmp_reg, MemOperand(dst_curr_addr, element_size, PostIndex));
202 __ Cmp(src_curr_addr, src_stop_addr);
203 __ B(&slow_copy_loop, ne);
204 __ B(GetExitLabel());
205 }
206
207 const char* GetDescription() const OVERRIDE { return "ReadBarrierSystemArrayCopySlowPathARM64"; }
208
209 private:
210 Location tmp_;
211
212 DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathARM64);
213};
Andreas Gampe878d58c2015-01-15 23:24:00 -0800214#undef __
215
216bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) {
217 Dispatch(invoke);
218 LocationSummary* res = invoke->GetLocations();
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000219 if (res == nullptr) {
220 return false;
221 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000222 return res->Intrinsified();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800223}
224
225#define __ masm->
226
227static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
228 LocationSummary* locations = new (arena) LocationSummary(invoke,
229 LocationSummary::kNoCall,
230 kIntrinsified);
231 locations->SetInAt(0, Location::RequiresFpuRegister());
232 locations->SetOut(Location::RequiresRegister());
233}
234
235static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
236 LocationSummary* locations = new (arena) LocationSummary(invoke,
237 LocationSummary::kNoCall,
238 kIntrinsified);
239 locations->SetInAt(0, Location::RequiresRegister());
240 locations->SetOut(Location::RequiresFpuRegister());
241}
242
Scott Wakeling97c72b72016-06-24 16:19:36 +0100243static void MoveFPToInt(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800244 Location input = locations->InAt(0);
245 Location output = locations->Out();
246 __ Fmov(is64bit ? XRegisterFrom(output) : WRegisterFrom(output),
247 is64bit ? DRegisterFrom(input) : SRegisterFrom(input));
248}
249
Scott Wakeling97c72b72016-06-24 16:19:36 +0100250static void MoveIntToFP(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 ? DRegisterFrom(output) : SRegisterFrom(output),
254 is64bit ? XRegisterFrom(input) : WRegisterFrom(input));
255}
256
257void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
258 CreateFPToIntLocations(arena_, invoke);
259}
260void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
261 CreateIntToFPLocations(arena_, invoke);
262}
263
264void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000265 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800266}
267void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000268 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800269}
270
271void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
272 CreateFPToIntLocations(arena_, invoke);
273}
274void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
275 CreateIntToFPLocations(arena_, invoke);
276}
277
278void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000279 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800280}
281void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000282 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800283}
284
285static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
286 LocationSummary* locations = new (arena) LocationSummary(invoke,
287 LocationSummary::kNoCall,
288 kIntrinsified);
289 locations->SetInAt(0, Location::RequiresRegister());
290 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
291}
292
293static void GenReverseBytes(LocationSummary* locations,
294 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100295 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800296 Location in = locations->InAt(0);
297 Location out = locations->Out();
298
299 switch (type) {
300 case Primitive::kPrimShort:
301 __ Rev16(WRegisterFrom(out), WRegisterFrom(in));
302 __ Sxth(WRegisterFrom(out), WRegisterFrom(out));
303 break;
304 case Primitive::kPrimInt:
305 case Primitive::kPrimLong:
306 __ Rev(RegisterFrom(out, type), RegisterFrom(in, type));
307 break;
308 default:
309 LOG(FATAL) << "Unexpected size for reverse-bytes: " << type;
310 UNREACHABLE();
311 }
312}
313
314void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
315 CreateIntToIntLocations(arena_, invoke);
316}
317
318void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
319 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
320}
321
322void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) {
323 CreateIntToIntLocations(arena_, invoke);
324}
325
326void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) {
327 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
328}
329
330void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) {
331 CreateIntToIntLocations(arena_, invoke);
332}
333
334void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
335 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
336}
337
Aart Bik7b565022016-01-28 14:36:22 -0800338static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
339 LocationSummary* locations = new (arena) LocationSummary(invoke,
340 LocationSummary::kNoCall,
341 kIntrinsified);
342 locations->SetInAt(0, Location::RequiresRegister());
343 locations->SetInAt(1, Location::RequiresRegister());
344 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
345}
346
Scott Wakeling611d3392015-07-10 11:42:06 +0100347static void GenNumberOfLeadingZeros(LocationSummary* locations,
348 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100349 MacroAssembler* masm) {
Scott Wakeling611d3392015-07-10 11:42:06 +0100350 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
351
352 Location in = locations->InAt(0);
353 Location out = locations->Out();
354
355 __ Clz(RegisterFrom(out, type), RegisterFrom(in, type));
356}
357
358void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
359 CreateIntToIntLocations(arena_, invoke);
360}
361
362void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
363 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
364}
365
366void IntrinsicLocationsBuilderARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
367 CreateIntToIntLocations(arena_, invoke);
368}
369
370void IntrinsicCodeGeneratorARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
371 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
372}
373
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100374static void GenNumberOfTrailingZeros(LocationSummary* locations,
375 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100376 MacroAssembler* masm) {
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100377 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
378
379 Location in = locations->InAt(0);
380 Location out = locations->Out();
381
382 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
383 __ Clz(RegisterFrom(out, type), RegisterFrom(out, type));
384}
385
386void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
387 CreateIntToIntLocations(arena_, invoke);
388}
389
390void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
391 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
392}
393
394void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
395 CreateIntToIntLocations(arena_, invoke);
396}
397
398void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
399 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
400}
401
Andreas Gampe878d58c2015-01-15 23:24:00 -0800402static void GenReverse(LocationSummary* locations,
403 Primitive::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100404 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800405 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
406
407 Location in = locations->InAt(0);
408 Location out = locations->Out();
409
410 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
411}
412
413void IntrinsicLocationsBuilderARM64::VisitIntegerReverse(HInvoke* invoke) {
414 CreateIntToIntLocations(arena_, invoke);
415}
416
417void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) {
418 GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
419}
420
421void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) {
422 CreateIntToIntLocations(arena_, invoke);
423}
424
425void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
426 GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
427}
428
Scott Wakeling97c72b72016-06-24 16:19:36 +0100429static void GenBitCount(HInvoke* instr, Primitive::Type type, MacroAssembler* masm) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100430 DCHECK(Primitive::IsIntOrLongType(type)) << type;
431 DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
432 DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
xueliang.zhong49924c92016-03-03 10:52:51 +0000433
xueliang.zhong49924c92016-03-03 10:52:51 +0000434 UseScratchRegisterScope temps(masm);
435
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000436 Register src = InputRegisterAt(instr, 0);
Roland Levillainfa3912e2016-04-01 18:21:55 +0100437 Register dst = RegisterFrom(instr->GetLocations()->Out(), type);
438 FPRegister fpr = (type == Primitive::kPrimLong) ? temps.AcquireD() : temps.AcquireS();
xueliang.zhong49924c92016-03-03 10:52:51 +0000439
440 __ Fmov(fpr, src);
Nicolas Geoffray457413a2016-03-04 11:10:17 +0000441 __ Cnt(fpr.V8B(), fpr.V8B());
442 __ Addv(fpr.B(), fpr.V8B());
xueliang.zhong49924c92016-03-03 10:52:51 +0000443 __ Fmov(dst, fpr);
444}
445
446void IntrinsicLocationsBuilderARM64::VisitLongBitCount(HInvoke* invoke) {
447 CreateIntToIntLocations(arena_, invoke);
448}
449
450void IntrinsicCodeGeneratorARM64::VisitLongBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100451 GenBitCount(invoke, Primitive::kPrimLong, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000452}
453
454void IntrinsicLocationsBuilderARM64::VisitIntegerBitCount(HInvoke* invoke) {
455 CreateIntToIntLocations(arena_, invoke);
456}
457
458void IntrinsicCodeGeneratorARM64::VisitIntegerBitCount(HInvoke* invoke) {
Roland Levillainfa3912e2016-04-01 18:21:55 +0100459 GenBitCount(invoke, Primitive::kPrimInt, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000460}
461
Andreas Gampe878d58c2015-01-15 23:24:00 -0800462static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800463 LocationSummary* locations = new (arena) LocationSummary(invoke,
464 LocationSummary::kNoCall,
465 kIntrinsified);
466 locations->SetInAt(0, Location::RequiresFpuRegister());
467 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
468}
469
Scott Wakeling97c72b72016-06-24 16:19:36 +0100470static void MathAbsFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800471 Location in = locations->InAt(0);
472 Location out = locations->Out();
473
474 FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in);
475 FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out);
476
477 __ Fabs(out_reg, in_reg);
478}
479
480void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) {
481 CreateFPToFPLocations(arena_, invoke);
482}
483
484void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000485 MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800486}
487
488void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) {
489 CreateFPToFPLocations(arena_, invoke);
490}
491
492void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000493 MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800494}
495
496static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
497 LocationSummary* locations = new (arena) LocationSummary(invoke,
498 LocationSummary::kNoCall,
499 kIntrinsified);
500 locations->SetInAt(0, Location::RequiresRegister());
501 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
502}
503
504static void GenAbsInteger(LocationSummary* locations,
505 bool is64bit,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100506 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800507 Location in = locations->InAt(0);
508 Location output = locations->Out();
509
510 Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in);
511 Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output);
512
513 __ Cmp(in_reg, Operand(0));
514 __ Cneg(out_reg, in_reg, lt);
515}
516
517void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
518 CreateIntToInt(arena_, invoke);
519}
520
521void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000522 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800523}
524
525void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
526 CreateIntToInt(arena_, invoke);
527}
528
529void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000530 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800531}
532
533static void GenMinMaxFP(LocationSummary* locations,
534 bool is_min,
535 bool is_double,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100536 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800537 Location op1 = locations->InAt(0);
538 Location op2 = locations->InAt(1);
539 Location out = locations->Out();
540
541 FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1);
542 FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2);
543 FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out);
544 if (is_min) {
545 __ Fmin(out_reg, op1_reg, op2_reg);
546 } else {
547 __ Fmax(out_reg, op1_reg, op2_reg);
548 }
549}
550
551static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
552 LocationSummary* locations = new (arena) LocationSummary(invoke,
553 LocationSummary::kNoCall,
554 kIntrinsified);
555 locations->SetInAt(0, Location::RequiresFpuRegister());
556 locations->SetInAt(1, Location::RequiresFpuRegister());
557 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
558}
559
560void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
561 CreateFPFPToFPLocations(arena_, invoke);
562}
563
564void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000565 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800566}
567
568void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
569 CreateFPFPToFPLocations(arena_, invoke);
570}
571
572void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000573 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800574}
575
576void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
577 CreateFPFPToFPLocations(arena_, invoke);
578}
579
580void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000581 GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800582}
583
584void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
585 CreateFPFPToFPLocations(arena_, invoke);
586}
587
588void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000589 GenMinMaxFP(
590 invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800591}
592
593static void GenMinMax(LocationSummary* locations,
594 bool is_min,
595 bool is_long,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100596 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800597 Location op1 = locations->InAt(0);
598 Location op2 = locations->InAt(1);
599 Location out = locations->Out();
600
601 Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
602 Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
603 Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
604
605 __ Cmp(op1_reg, op2_reg);
606 __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
607}
608
Andreas Gampe878d58c2015-01-15 23:24:00 -0800609void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
610 CreateIntIntToIntLocations(arena_, invoke);
611}
612
613void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000614 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800615}
616
617void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) {
618 CreateIntIntToIntLocations(arena_, invoke);
619}
620
621void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000622 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800623}
624
625void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) {
626 CreateIntIntToIntLocations(arena_, invoke);
627}
628
629void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000630 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800631}
632
633void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) {
634 CreateIntIntToIntLocations(arena_, invoke);
635}
636
637void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000638 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800639}
640
641void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) {
642 CreateFPToFPLocations(arena_, invoke);
643}
644
645void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) {
646 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100647 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800648 __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
649}
650
651void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) {
652 CreateFPToFPLocations(arena_, invoke);
653}
654
655void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) {
656 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100657 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800658 __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
659}
660
661void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) {
662 CreateFPToFPLocations(arena_, invoke);
663}
664
665void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) {
666 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100667 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800668 __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
669}
670
671void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) {
672 CreateFPToFPLocations(arena_, invoke);
673}
674
675void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) {
676 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100677 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800678 __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
679}
680
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100681static void CreateFPToIntPlusFPTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800682 LocationSummary* locations = new (arena) LocationSummary(invoke,
683 LocationSummary::kNoCall,
684 kIntrinsified);
685 locations->SetInAt(0, Location::RequiresFpuRegister());
686 locations->SetOut(Location::RequiresRegister());
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100687 locations->AddTemp(Location::RequiresFpuRegister());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800688}
689
Scott Wakeling97c72b72016-06-24 16:19:36 +0100690static void GenMathRound(HInvoke* invoke, bool is_double, vixl::aarch64::MacroAssembler* masm) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100691 // Java 8 API definition for Math.round():
692 // Return the closest long or int to the argument, with ties rounding to positive infinity.
693 //
694 // There is no single instruction in ARMv8 that can support the above definition.
695 // We choose to use FCVTAS here, because it has closest semantic.
696 // FCVTAS performs rounding to nearest integer, ties away from zero.
697 // For most inputs (positive values, zero or NaN), this instruction is enough.
698 // We only need a few handling code after FCVTAS if the input is negative half value.
699 //
700 // The reason why we didn't choose FCVTPS instruction here is that
701 // although it performs rounding toward positive infinity, it doesn't perform rounding to nearest.
702 // For example, FCVTPS(-1.9) = -1 and FCVTPS(1.1) = 2.
703 // If we were using this instruction, for most inputs, more handling code would be needed.
704 LocationSummary* l = invoke->GetLocations();
705 FPRegister in_reg = is_double ? DRegisterFrom(l->InAt(0)) : SRegisterFrom(l->InAt(0));
706 FPRegister tmp_fp = is_double ? DRegisterFrom(l->GetTemp(0)) : SRegisterFrom(l->GetTemp(0));
707 Register out_reg = is_double ? XRegisterFrom(l->Out()) : WRegisterFrom(l->Out());
Scott Wakeling97c72b72016-06-24 16:19:36 +0100708 vixl::aarch64::Label done;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800709
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100710 // Round to nearest integer, ties away from zero.
711 __ Fcvtas(out_reg, in_reg);
712
713 // For positive values, zero or NaN inputs, rounding is done.
Scott Wakeling97c72b72016-06-24 16:19:36 +0100714 __ Tbz(out_reg, out_reg.GetSizeInBits() - 1, &done);
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100715
716 // Handle input < 0 cases.
717 // If input is negative but not a tie, previous result (round to nearest) is valid.
718 // If input is a negative tie, out_reg += 1.
719 __ Frinta(tmp_fp, in_reg);
720 __ Fsub(tmp_fp, in_reg, tmp_fp);
721 __ Fcmp(tmp_fp, 0.5);
722 __ Cinc(out_reg, out_reg, eq);
723
724 __ Bind(&done);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800725}
726
727void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100728 CreateFPToIntPlusFPTempLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800729}
730
731void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100732 GenMathRound(invoke, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800733}
734
735void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100736 CreateFPToIntPlusFPTempLocations(arena_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800737}
738
739void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100740 GenMathRound(invoke, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800741}
742
743void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) {
744 CreateIntToIntLocations(arena_, invoke);
745}
746
747void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100748 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800749 __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()),
750 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
751}
752
753void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
754 CreateIntToIntLocations(arena_, invoke);
755}
756
757void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100758 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800759 __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()),
760 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
761}
762
763void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
764 CreateIntToIntLocations(arena_, invoke);
765}
766
767void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100768 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800769 __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()),
770 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
771}
772
773void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
774 CreateIntToIntLocations(arena_, invoke);
775}
776
777void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100778 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800779 __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()),
780 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
781}
782
783static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
784 LocationSummary* locations = new (arena) LocationSummary(invoke,
785 LocationSummary::kNoCall,
786 kIntrinsified);
787 locations->SetInAt(0, Location::RequiresRegister());
788 locations->SetInAt(1, Location::RequiresRegister());
789}
790
791void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) {
792 CreateIntIntToVoidLocations(arena_, invoke);
793}
794
795void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100796 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800797 __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)),
798 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
799}
800
801void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
802 CreateIntIntToVoidLocations(arena_, invoke);
803}
804
805void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100806 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800807 __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)),
808 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
809}
810
811void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
812 CreateIntIntToVoidLocations(arena_, invoke);
813}
814
815void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100816 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800817 __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)),
818 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
819}
820
821void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
822 CreateIntIntToVoidLocations(arena_, invoke);
823}
824
825void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100826 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800827 __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)),
828 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
829}
830
831void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) {
832 LocationSummary* locations = new (arena_) LocationSummary(invoke,
833 LocationSummary::kNoCall,
834 kIntrinsified);
835 locations->SetOut(Location::RequiresRegister());
836}
837
838void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) {
839 codegen_->Load(Primitive::kPrimNot, WRegisterFrom(invoke->GetLocations()->Out()),
Andreas Gampe542451c2016-07-26 09:02:02 -0700840 MemOperand(tr, Thread::PeerOffset<kArm64PointerSize>().Int32Value()));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800841}
842
843static void GenUnsafeGet(HInvoke* invoke,
844 Primitive::Type type,
845 bool is_volatile,
846 CodeGeneratorARM64* codegen) {
847 LocationSummary* locations = invoke->GetLocations();
848 DCHECK((type == Primitive::kPrimInt) ||
849 (type == Primitive::kPrimLong) ||
850 (type == Primitive::kPrimNot));
Alexandre Rames087930f2016-08-02 13:45:28 +0100851 MacroAssembler* masm = codegen->GetVIXLAssembler();
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000852 Location base_loc = locations->InAt(1);
853 Register base = WRegisterFrom(base_loc); // Object pointer.
854 Location offset_loc = locations->InAt(2);
855 Register offset = XRegisterFrom(offset_loc); // Long offset.
856 Location trg_loc = locations->Out();
857 Register trg = RegisterFrom(trg_loc, type);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800858
Roland Levillain44015862016-01-22 11:47:17 +0000859 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
860 // UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case.
861 UseScratchRegisterScope temps(masm);
862 Register temp = temps.AcquireW();
Roland Levillainbfea3352016-06-23 13:48:47 +0100863 codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke,
864 trg_loc,
865 base,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100866 /* offset */ 0u,
Roland Levillainbfea3352016-06-23 13:48:47 +0100867 /* index */ offset_loc,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100868 /* scale_factor */ 0u,
Roland Levillainbfea3352016-06-23 13:48:47 +0100869 temp,
870 /* needs_null_check */ false,
871 is_volatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800872 } else {
Roland Levillain44015862016-01-22 11:47:17 +0000873 // Other cases.
874 MemOperand mem_op(base.X(), offset);
875 if (is_volatile) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +0000876 codegen->LoadAcquire(invoke, trg, mem_op, /* needs_null_check */ true);
Roland Levillain44015862016-01-22 11:47:17 +0000877 } else {
878 codegen->Load(type, trg, mem_op);
879 }
Roland Levillain4d027112015-07-01 15:41:14 +0100880
Roland Levillain44015862016-01-22 11:47:17 +0000881 if (type == Primitive::kPrimNot) {
882 DCHECK(trg.IsW());
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100883 codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0u, offset_loc);
Roland Levillain44015862016-01-22 11:47:17 +0000884 }
Roland Levillain4d027112015-07-01 15:41:14 +0100885 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800886}
887
888static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000889 bool can_call = kEmitCompilerReadBarrier &&
890 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
891 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800892 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100893 (can_call
894 ? LocationSummary::kCallOnSlowPath
895 : LocationSummary::kNoCall),
Andreas Gampe878d58c2015-01-15 23:24:00 -0800896 kIntrinsified);
Vladimir Marko70e97462016-08-09 11:04:26 +0100897 if (can_call && kUseBakerReadBarrier) {
Vladimir Marko804b03f2016-09-14 16:26:36 +0100898 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Vladimir Marko70e97462016-08-09 11:04:26 +0100899 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800900 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
901 locations->SetInAt(1, Location::RequiresRegister());
902 locations->SetInAt(2, Location::RequiresRegister());
Roland Levillainbfea3352016-06-23 13:48:47 +0100903 locations->SetOut(Location::RequiresRegister(),
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100904 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800905}
906
907void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
908 CreateIntIntIntToIntLocations(arena_, invoke);
909}
910void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
911 CreateIntIntIntToIntLocations(arena_, invoke);
912}
913void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
914 CreateIntIntIntToIntLocations(arena_, invoke);
915}
916void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
917 CreateIntIntIntToIntLocations(arena_, invoke);
918}
919void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
920 CreateIntIntIntToIntLocations(arena_, invoke);
921}
922void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
923 CreateIntIntIntToIntLocations(arena_, invoke);
924}
925
926void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000927 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800928}
929void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000930 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800931}
932void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000933 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800934}
935void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000936 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800937}
938void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000939 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800940}
941void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000942 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800943}
944
945static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
946 LocationSummary* locations = new (arena) LocationSummary(invoke,
947 LocationSummary::kNoCall,
948 kIntrinsified);
949 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
950 locations->SetInAt(1, Location::RequiresRegister());
951 locations->SetInAt(2, Location::RequiresRegister());
952 locations->SetInAt(3, Location::RequiresRegister());
953}
954
955void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
956 CreateIntIntIntIntToVoid(arena_, invoke);
957}
958void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
959 CreateIntIntIntIntToVoid(arena_, invoke);
960}
961void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
962 CreateIntIntIntIntToVoid(arena_, invoke);
963}
964void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
965 CreateIntIntIntIntToVoid(arena_, invoke);
966}
967void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
968 CreateIntIntIntIntToVoid(arena_, invoke);
969}
970void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
971 CreateIntIntIntIntToVoid(arena_, invoke);
972}
973void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
974 CreateIntIntIntIntToVoid(arena_, invoke);
975}
976void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
977 CreateIntIntIntIntToVoid(arena_, invoke);
978}
979void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
980 CreateIntIntIntIntToVoid(arena_, invoke);
981}
982
983static void GenUnsafePut(LocationSummary* locations,
984 Primitive::Type type,
985 bool is_volatile,
986 bool is_ordered,
987 CodeGeneratorARM64* codegen) {
Alexandre Rames087930f2016-08-02 13:45:28 +0100988 MacroAssembler* masm = codegen->GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800989
990 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
991 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
992 Register value = RegisterFrom(locations->InAt(3), type);
Roland Levillain4d027112015-07-01 15:41:14 +0100993 Register source = value;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800994 MemOperand mem_op(base.X(), offset);
995
Roland Levillain4d027112015-07-01 15:41:14 +0100996 {
997 // We use a block to end the scratch scope before the write barrier, thus
998 // freeing the temporary registers so they can be used in `MarkGCCard`.
999 UseScratchRegisterScope temps(masm);
1000
1001 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1002 DCHECK(value.IsW());
1003 Register temp = temps.AcquireW();
1004 __ Mov(temp.W(), value.W());
1005 codegen->GetAssembler()->PoisonHeapReference(temp.W());
1006 source = temp;
Andreas Gampe878d58c2015-01-15 23:24:00 -08001007 }
Roland Levillain4d027112015-07-01 15:41:14 +01001008
1009 if (is_volatile || is_ordered) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001010 codegen->StoreRelease(type, source, mem_op);
Roland Levillain4d027112015-07-01 15:41:14 +01001011 } else {
1012 codegen->Store(type, source, mem_op);
1013 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001014 }
1015
1016 if (type == Primitive::kPrimNot) {
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001017 bool value_can_be_null = true; // TODO: Worth finding out this information?
1018 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001019 }
1020}
1021
1022void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001023 GenUnsafePut(invoke->GetLocations(),
1024 Primitive::kPrimInt,
1025 /* is_volatile */ false,
1026 /* is_ordered */ false,
1027 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001028}
1029void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001030 GenUnsafePut(invoke->GetLocations(),
1031 Primitive::kPrimInt,
1032 /* is_volatile */ false,
1033 /* is_ordered */ true,
1034 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001035}
1036void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001037 GenUnsafePut(invoke->GetLocations(),
1038 Primitive::kPrimInt,
1039 /* is_volatile */ true,
1040 /* is_ordered */ false,
1041 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001042}
1043void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001044 GenUnsafePut(invoke->GetLocations(),
1045 Primitive::kPrimNot,
1046 /* is_volatile */ false,
1047 /* is_ordered */ false,
1048 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001049}
1050void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001051 GenUnsafePut(invoke->GetLocations(),
1052 Primitive::kPrimNot,
1053 /* is_volatile */ false,
1054 /* is_ordered */ true,
1055 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001056}
1057void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001058 GenUnsafePut(invoke->GetLocations(),
1059 Primitive::kPrimNot,
1060 /* is_volatile */ true,
1061 /* is_ordered */ false,
1062 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001063}
1064void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001065 GenUnsafePut(invoke->GetLocations(),
1066 Primitive::kPrimLong,
1067 /* is_volatile */ false,
1068 /* is_ordered */ false,
1069 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001070}
1071void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001072 GenUnsafePut(invoke->GetLocations(),
1073 Primitive::kPrimLong,
1074 /* is_volatile */ false,
1075 /* is_ordered */ true,
1076 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001077}
1078void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001079 GenUnsafePut(invoke->GetLocations(),
1080 Primitive::kPrimLong,
1081 /* is_volatile */ true,
1082 /* is_ordered */ false,
1083 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001084}
1085
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001086static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
1087 HInvoke* invoke,
1088 Primitive::Type type) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001089 bool can_call = kEmitCompilerReadBarrier &&
1090 kUseBakerReadBarrier &&
1091 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001092 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001093 (can_call
1094 ? LocationSummary::kCallOnSlowPath
1095 : LocationSummary::kNoCall),
Andreas Gampe878d58c2015-01-15 23:24:00 -08001096 kIntrinsified);
1097 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1098 locations->SetInAt(1, Location::RequiresRegister());
1099 locations->SetInAt(2, Location::RequiresRegister());
1100 locations->SetInAt(3, Location::RequiresRegister());
1101 locations->SetInAt(4, Location::RequiresRegister());
1102
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001103 // If heap poisoning is enabled, we don't want the unpoisoning
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001104 // operations to potentially clobber the output. Likewise when
1105 // emitting a (Baker) read barrier, which may call.
1106 Location::OutputOverlap overlaps =
1107 ((kPoisonHeapReferences && type == Primitive::kPrimNot) || can_call)
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001108 ? Location::kOutputOverlap
1109 : Location::kNoOutputOverlap;
1110 locations->SetOut(Location::RequiresRegister(), overlaps);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001111 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1112 // Temporary register for (Baker) read barrier.
1113 locations->AddTemp(Location::RequiresRegister());
1114 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001115}
1116
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001117static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARM64* codegen) {
Alexandre Rames087930f2016-08-02 13:45:28 +01001118 MacroAssembler* masm = codegen->GetVIXLAssembler();
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001119 LocationSummary* locations = invoke->GetLocations();
Andreas Gampe878d58c2015-01-15 23:24:00 -08001120
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001121 Location out_loc = locations->Out();
1122 Register out = WRegisterFrom(out_loc); // Boolean result.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001123
1124 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001125 Location offset_loc = locations->InAt(2);
1126 Register offset = XRegisterFrom(offset_loc); // Long offset.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001127 Register expected = RegisterFrom(locations->InAt(3), type); // Expected.
1128 Register value = RegisterFrom(locations->InAt(4), type); // Value.
1129
1130 // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
1131 if (type == Primitive::kPrimNot) {
1132 // Mark card for object assuming new value is stored.
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001133 bool value_can_be_null = true; // TODO: Worth finding out this information?
1134 codegen->MarkGCCard(base, value, value_can_be_null);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001135
1136 // The only read barrier implementation supporting the
1137 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1138 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1139
1140 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1141 Register temp = WRegisterFrom(locations->GetTemp(0));
1142 // Need to make sure the reference stored in the field is a to-space
1143 // one before attempting the CAS or the CAS could fail incorrectly.
1144 codegen->GenerateReferenceLoadWithBakerReadBarrier(
1145 invoke,
1146 out_loc, // Unused, used only as a "temporary" within the read barrier.
1147 base,
1148 /* offset */ 0u,
1149 /* index */ offset_loc,
1150 /* scale_factor */ 0u,
1151 temp,
1152 /* needs_null_check */ false,
1153 /* use_load_acquire */ false,
1154 /* always_update_field */ true);
1155 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001156 }
1157
1158 UseScratchRegisterScope temps(masm);
1159 Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
1160 Register tmp_value = temps.AcquireSameSizeAs(value); // Value in memory.
1161
1162 Register tmp_32 = tmp_value.W();
1163
1164 __ Add(tmp_ptr, base.X(), Operand(offset));
1165
Roland Levillain4d027112015-07-01 15:41:14 +01001166 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1167 codegen->GetAssembler()->PoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001168 if (value.Is(expected)) {
1169 // Do not poison `value`, as it is the same register as
1170 // `expected`, which has just been poisoned.
1171 } else {
1172 codegen->GetAssembler()->PoisonHeapReference(value);
1173 }
Roland Levillain4d027112015-07-01 15:41:14 +01001174 }
1175
Andreas Gampe878d58c2015-01-15 23:24:00 -08001176 // do {
1177 // tmp_value = [tmp_ptr] - expected;
1178 // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1179 // result = tmp_value != 0;
1180
Scott Wakeling97c72b72016-06-24 16:19:36 +01001181 vixl::aarch64::Label loop_head, exit_loop;
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001182 __ Bind(&loop_head);
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001183 __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
1184 __ Cmp(tmp_value, expected);
1185 __ B(&exit_loop, ne);
1186 __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
1187 __ Cbnz(tmp_32, &loop_head);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001188 __ Bind(&exit_loop);
1189 __ Cset(out, eq);
Roland Levillain4d027112015-07-01 15:41:14 +01001190
1191 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillain4d027112015-07-01 15:41:14 +01001192 codegen->GetAssembler()->UnpoisonHeapReference(expected);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001193 if (value.Is(expected)) {
1194 // Do not unpoison `value`, as it is the same register as
1195 // `expected`, which has just been unpoisoned.
1196 } else {
1197 codegen->GetAssembler()->UnpoisonHeapReference(value);
1198 }
Roland Levillain4d027112015-07-01 15:41:14 +01001199 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001200}
1201
1202void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001203 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001204}
1205void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001206 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001207}
1208void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001209 // The only read barrier implementation supporting the
1210 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1211 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain985ff702015-10-23 13:25:35 +01001212 return;
1213 }
1214
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001215 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001216}
1217
1218void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001219 GenCas(invoke, Primitive::kPrimInt, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001220}
1221void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001222 GenCas(invoke, Primitive::kPrimLong, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001223}
1224void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001225 // The only read barrier implementation supporting the
1226 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1227 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01001228
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001229 GenCas(invoke, Primitive::kPrimNot, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001230}
1231
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001232void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001233 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Scott Wakeling1f36f412016-04-21 11:13:45 +01001234 invoke->InputAt(1)->CanBeNull()
1235 ? LocationSummary::kCallOnSlowPath
1236 : LocationSummary::kNoCall,
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001237 kIntrinsified);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001238 locations->SetInAt(0, Location::RequiresRegister());
1239 locations->SetInAt(1, Location::RequiresRegister());
1240 locations->AddTemp(Location::RequiresRegister());
1241 locations->AddTemp(Location::RequiresRegister());
1242 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001243 // Need temporary registers for String compression's feature.
1244 if (mirror::kUseStringCompression) {
1245 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001246 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001247 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001248}
1249
1250void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001251 MacroAssembler* masm = GetVIXLAssembler();
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001252 LocationSummary* locations = invoke->GetLocations();
1253
Alexandre Rames2ea91532016-08-11 17:04:14 +01001254 Register str = InputRegisterAt(invoke, 0);
1255 Register arg = InputRegisterAt(invoke, 1);
1256 DCHECK(str.IsW());
1257 DCHECK(arg.IsW());
Scott Wakeling1f36f412016-04-21 11:13:45 +01001258 Register out = OutputRegister(invoke);
1259
1260 Register temp0 = WRegisterFrom(locations->GetTemp(0));
1261 Register temp1 = WRegisterFrom(locations->GetTemp(1));
1262 Register temp2 = WRegisterFrom(locations->GetTemp(2));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001263 Register temp3;
jessicahandojo05765752016-09-09 19:01:32 -07001264 if (mirror::kUseStringCompression) {
1265 temp3 = WRegisterFrom(locations->GetTemp(3));
jessicahandojo05765752016-09-09 19:01:32 -07001266 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001267
Scott Wakeling97c72b72016-06-24 16:19:36 +01001268 vixl::aarch64::Label loop;
1269 vixl::aarch64::Label find_char_diff;
1270 vixl::aarch64::Label end;
jessicahandojo05765752016-09-09 19:01:32 -07001271 vixl::aarch64::Label different_compression;
Scott Wakeling1f36f412016-04-21 11:13:45 +01001272
1273 // Get offsets of count and value fields within a string object.
1274 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1275 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1276
Nicolas Geoffray512e04d2015-03-27 17:21:24 +00001277 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +01001278 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001279
Scott Wakeling1f36f412016-04-21 11:13:45 +01001280 // Take slow path and throw if input can be and is null.
1281 SlowPathCodeARM64* slow_path = nullptr;
1282 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1283 if (can_slow_path) {
1284 slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1285 codegen_->AddSlowPath(slow_path);
1286 __ Cbz(arg, slow_path->GetEntryLabel());
1287 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001288
Scott Wakeling1f36f412016-04-21 11:13:45 +01001289 // Reference equality check, return 0 if same reference.
1290 __ Subs(out, str, arg);
1291 __ B(&end, eq);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001292
jessicahandojo05765752016-09-09 19:01:32 -07001293 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001294 // Load `count` fields of this and argument strings.
jessicahandojo05765752016-09-09 19:01:32 -07001295 __ Ldr(temp3, HeapOperand(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001296 __ Ldr(temp2, HeapOperand(arg, count_offset));
jessicahandojo05765752016-09-09 19:01:32 -07001297 // Clean out compression flag from lengths.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001298 __ Lsr(temp0, temp3, 1u);
1299 __ Lsr(temp1, temp2, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001300 } else {
1301 // Load lengths of this and argument strings.
1302 __ Ldr(temp0, HeapOperand(str, count_offset));
1303 __ Ldr(temp1, HeapOperand(arg, count_offset));
1304 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001305 // out = length diff.
1306 __ Subs(out, temp0, temp1);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001307 // temp0 = min(len(str), len(arg)).
1308 __ Csel(temp0, temp1, temp0, ge);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001309 // Shorter string is empty?
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001310 __ Cbz(temp0, &end);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001311
jessicahandojo05765752016-09-09 19:01:32 -07001312 if (mirror::kUseStringCompression) {
1313 // Check if both strings using same compression style to use this comparison loop.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001314 __ Eor(temp2, temp2, Operand(temp3));
1315 // Interleave with compression flag extraction which is needed for both paths
1316 // and also set flags which is needed only for the different compressions path.
1317 __ Ands(temp3.W(), temp3.W(), Operand(1));
1318 __ Tbnz(temp2, 0, &different_compression); // Does not use flags.
jessicahandojo05765752016-09-09 19:01:32 -07001319 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001320 // Store offset of string value in preparation for comparison loop.
1321 __ Mov(temp1, value_offset);
jessicahandojo05765752016-09-09 19:01:32 -07001322 if (mirror::kUseStringCompression) {
1323 // For string compression, calculate the number of bytes to compare (not chars).
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001324 // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
1325 __ Lsl(temp0, temp0, temp3);
jessicahandojo05765752016-09-09 19:01:32 -07001326 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001327
1328 UseScratchRegisterScope scratch_scope(masm);
1329 Register temp4 = scratch_scope.AcquireX();
1330
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001331 // Assertions that must hold in order to compare strings 8 bytes at a time.
Scott Wakeling1f36f412016-04-21 11:13:45 +01001332 DCHECK_ALIGNED(value_offset, 8);
1333 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1334
1335 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1336 DCHECK_EQ(char_size, 2u);
1337
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001338 // Promote temp2 to an X reg, ready for LDR.
1339 temp2 = temp2.X();
Scott Wakeling1f36f412016-04-21 11:13:45 +01001340
1341 // Loop to compare 4x16-bit characters at a time (ok because of string data alignment).
1342 __ Bind(&loop);
Alexandre Rames2ea91532016-08-11 17:04:14 +01001343 __ Ldr(temp4, MemOperand(str.X(), temp1.X()));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001344 __ Ldr(temp2, MemOperand(arg.X(), temp1.X()));
1345 __ Cmp(temp4, temp2);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001346 __ B(ne, &find_char_diff);
1347 __ Add(temp1, temp1, char_size * 4);
jessicahandojo05765752016-09-09 19:01:32 -07001348 // With string compression, we have compared 8 bytes, otherwise 4 chars.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001349 __ Subs(temp0, temp0, (mirror::kUseStringCompression) ? 8 : 4);
1350 __ B(&loop, hi);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001351 __ B(&end);
1352
1353 // Promote temp1 to an X reg, ready for EOR.
1354 temp1 = temp1.X();
1355
jessicahandojo05765752016-09-09 19:01:32 -07001356 // Find the single character difference.
Scott Wakeling1f36f412016-04-21 11:13:45 +01001357 __ Bind(&find_char_diff);
1358 // Get the bit position of the first character that differs.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001359 __ Eor(temp1, temp2, temp4);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001360 __ Rbit(temp1, temp1);
1361 __ Clz(temp1, temp1);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001362
jessicahandojo05765752016-09-09 19:01:32 -07001363 // If the number of chars remaining <= the index where the difference occurs (0-3), then
Scott Wakeling1f36f412016-04-21 11:13:45 +01001364 // the difference occurs outside the remaining string data, so just return length diff (out).
jessicahandojo05765752016-09-09 19:01:32 -07001365 // Unlike ARM, we're doing the comparison in one go here, without the subtraction at the
1366 // find_char_diff_2nd_cmp path, so it doesn't matter whether the comparison is signed or
1367 // unsigned when string compression is disabled.
1368 // When it's enabled, the comparison must be unsigned.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001369 __ Cmp(temp0, Operand(temp1.W(), LSR, (mirror::kUseStringCompression) ? 3 : 4));
jessicahandojo05765752016-09-09 19:01:32 -07001370 __ B(ls, &end);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001371
Scott Wakeling1f36f412016-04-21 11:13:45 +01001372 // Extract the characters and calculate the difference.
jessicahandojo05765752016-09-09 19:01:32 -07001373 if (mirror:: kUseStringCompression) {
jessicahandojo05765752016-09-09 19:01:32 -07001374 __ Bic(temp1, temp1, 0x7);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001375 __ Bic(temp1, temp1, Operand(temp3.X(), LSL, 3u));
1376 } else {
1377 __ Bic(temp1, temp1, 0xf);
jessicahandojo05765752016-09-09 19:01:32 -07001378 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001379 __ Lsr(temp2, temp2, temp1);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001380 __ Lsr(temp4, temp4, temp1);
jessicahandojo05765752016-09-09 19:01:32 -07001381 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001382 // Prioritize the case of compressed strings and calculate such result first.
1383 __ Uxtb(temp1, temp4);
1384 __ Sub(out, temp1.W(), Operand(temp2.W(), UXTB));
1385 __ Tbz(temp3, 0u, &end); // If actually compressed, we're done.
jessicahandojo05765752016-09-09 19:01:32 -07001386 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001387 __ Uxth(temp4, temp4);
1388 __ Sub(out, temp4.W(), Operand(temp2.W(), UXTH));
jessicahandojo05765752016-09-09 19:01:32 -07001389
1390 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001391 __ B(&end);
1392 __ Bind(&different_compression);
1393
1394 // Comparison for different compression style.
jessicahandojo05765752016-09-09 19:01:32 -07001395 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
1396 DCHECK_EQ(c_char_size, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001397 temp1 = temp1.W();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001398 temp2 = temp2.W();
1399 temp4 = temp4.W();
jessicahandojo05765752016-09-09 19:01:32 -07001400
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001401 // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
1402 // Note that flags have been set by the `str` compression flag extraction to `temp3`
1403 // before branching to the `different_compression` label.
1404 __ Csel(temp1, str, arg, eq); // Pointer to the compressed string.
1405 __ Csel(temp2, str, arg, ne); // Pointer to the uncompressed string.
jessicahandojo05765752016-09-09 19:01:32 -07001406
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001407 // We want to free up the temp3, currently holding `str` compression flag, for comparison.
1408 // So, we move it to the bottom bit of the iteration count `temp0` which we then need to treat
1409 // as unsigned. Start by freeing the bit with a LSL and continue further down by a SUB which
1410 // will allow `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
1411 __ Lsl(temp0, temp0, 1u);
1412
1413 // Adjust temp1 and temp2 from string pointers to data pointers.
1414 __ Add(temp1, temp1, Operand(value_offset));
1415 __ Add(temp2, temp2, Operand(value_offset));
1416
1417 // Complete the move of the compression flag.
1418 __ Sub(temp0, temp0, Operand(temp3));
1419
1420 vixl::aarch64::Label different_compression_loop;
1421 vixl::aarch64::Label different_compression_diff;
1422
1423 __ Bind(&different_compression_loop);
1424 __ Ldrb(temp4, MemOperand(temp1.X(), c_char_size, PostIndex));
1425 __ Ldrh(temp3, MemOperand(temp2.X(), char_size, PostIndex));
1426 __ Subs(temp4, temp4, Operand(temp3));
1427 __ B(&different_compression_diff, ne);
1428 __ Subs(temp0, temp0, 2);
1429 __ B(&different_compression_loop, hi);
jessicahandojo05765752016-09-09 19:01:32 -07001430 __ B(&end);
1431
1432 // Calculate the difference.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001433 __ Bind(&different_compression_diff);
1434 __ Tst(temp0, Operand(1));
1435 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1436 "Expecting 0=compressed, 1=uncompressed");
1437 __ Cneg(out, temp4, ne);
jessicahandojo05765752016-09-09 19:01:32 -07001438 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001439
1440 __ Bind(&end);
1441
1442 if (can_slow_path) {
1443 __ Bind(slow_path->GetExitLabel());
1444 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001445}
1446
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001447// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
1448// The normal loop plus the pre-header is 9 instructions without string compression and 12
1449// instructions with string compression. We can compare up to 8 bytes in 4 instructions
1450// (LDR+LDR+CMP+BNE) and up to 16 bytes in 5 instructions (LDP+LDP+CMP+CCMP+BNE). Allow up
1451// to 10 instructions for the unrolled loop.
1452constexpr size_t kShortConstStringEqualsCutoffInBytes = 32;
1453
1454static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
1455 if (candidate->IsLoadString()) {
1456 HLoadString* load_string = candidate->AsLoadString();
1457 const DexFile& dex_file = load_string->GetDexFile();
1458 return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
1459 }
1460 return nullptr;
1461}
1462
Agi Csakiea34b402015-08-13 17:51:19 -07001463void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
1464 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1465 LocationSummary::kNoCall,
1466 kIntrinsified);
1467 locations->SetInAt(0, Location::RequiresRegister());
1468 locations->SetInAt(1, Location::RequiresRegister());
Agi Csakiea34b402015-08-13 17:51:19 -07001469
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001470 // For the generic implementation and for long const strings we need a temporary.
1471 // We do not need it for short const strings, up to 8 bytes, see code generation below.
1472 uint32_t const_string_length = 0u;
1473 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1474 if (const_string == nullptr) {
1475 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1476 }
1477 bool is_compressed =
1478 mirror::kUseStringCompression &&
1479 const_string != nullptr &&
1480 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1481 if (const_string == nullptr || const_string_length > (is_compressed ? 8u : 4u)) {
1482 locations->AddTemp(Location::RequiresRegister());
1483 }
1484
1485 // TODO: If the String.equals() is used only for an immediately following HIf, we can
1486 // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
1487 // Then we shall need an extra temporary register instead of the output register.
Agi Csakiea34b402015-08-13 17:51:19 -07001488 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1489}
1490
1491void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001492 MacroAssembler* masm = GetVIXLAssembler();
Agi Csakiea34b402015-08-13 17:51:19 -07001493 LocationSummary* locations = invoke->GetLocations();
1494
1495 Register str = WRegisterFrom(locations->InAt(0));
1496 Register arg = WRegisterFrom(locations->InAt(1));
1497 Register out = XRegisterFrom(locations->Out());
1498
1499 UseScratchRegisterScope scratch_scope(masm);
1500 Register temp = scratch_scope.AcquireW();
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001501 Register temp1 = scratch_scope.AcquireW();
Agi Csakiea34b402015-08-13 17:51:19 -07001502
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001503 vixl::aarch64::Label loop;
Scott Wakeling97c72b72016-06-24 16:19:36 +01001504 vixl::aarch64::Label end;
1505 vixl::aarch64::Label return_true;
1506 vixl::aarch64::Label return_false;
Agi Csakiea34b402015-08-13 17:51:19 -07001507
1508 // Get offsets of count, value, and class fields within a string object.
1509 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1510 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1511 const int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1512
1513 // Note that the null check must have been done earlier.
1514 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1515
Vladimir Marko53b52002016-05-24 19:30:45 +01001516 StringEqualsOptimizations optimizations(invoke);
1517 if (!optimizations.GetArgumentNotNull()) {
1518 // Check if input is null, return false if it is.
1519 __ Cbz(arg, &return_false);
1520 }
Agi Csakiea34b402015-08-13 17:51:19 -07001521
1522 // Reference equality check, return true if same reference.
1523 __ Cmp(str, arg);
1524 __ B(&return_true, eq);
1525
Vladimir Marko53b52002016-05-24 19:30:45 +01001526 if (!optimizations.GetArgumentIsString()) {
1527 // Instanceof check for the argument by comparing class fields.
1528 // All string objects must have the same type since String cannot be subclassed.
1529 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1530 // If the argument is a string object, its class field must be equal to receiver's class field.
1531 __ Ldr(temp, MemOperand(str.X(), class_offset));
1532 __ Ldr(temp1, MemOperand(arg.X(), class_offset));
1533 __ Cmp(temp, temp1);
1534 __ B(&return_false, ne);
1535 }
Agi Csakiea34b402015-08-13 17:51:19 -07001536
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001537 // Check if one of the inputs is a const string. Do not special-case both strings
1538 // being const, such cases should be handled by constant folding if needed.
1539 uint32_t const_string_length = 0u;
1540 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1541 if (const_string == nullptr) {
1542 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1543 if (const_string != nullptr) {
1544 std::swap(str, arg); // Make sure the const string is in `str`.
1545 }
1546 }
1547 bool is_compressed =
1548 mirror::kUseStringCompression &&
1549 const_string != nullptr &&
1550 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1551
1552 if (const_string != nullptr) {
1553 // Load `count` field of the argument string and check if it matches the const string.
1554 // Also compares the compression style, if differs return false.
1555 __ Ldr(temp, MemOperand(arg.X(), count_offset));
1556 __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
1557 __ B(&return_false, ne);
1558 } else {
1559 // Load `count` fields of this and argument strings.
1560 __ Ldr(temp, MemOperand(str.X(), count_offset));
1561 __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1562 // Check if `count` fields are equal, return false if they're not.
1563 // Also compares the compression style, if differs return false.
1564 __ Cmp(temp, temp1);
1565 __ B(&return_false, ne);
1566 }
Agi Csakiea34b402015-08-13 17:51:19 -07001567
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001568 // Assertions that must hold in order to compare strings 8 bytes at a time.
Agi Csakiea34b402015-08-13 17:51:19 -07001569 DCHECK_ALIGNED(value_offset, 8);
1570 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1571
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001572 if (const_string != nullptr &&
1573 const_string_length < (is_compressed ? kShortConstStringEqualsCutoffInBytes
1574 : kShortConstStringEqualsCutoffInBytes / 2u)) {
1575 // Load and compare the contents. Though we know the contents of the short const string
1576 // at compile time, materializing constants may be more code than loading from memory.
1577 int32_t offset = value_offset;
1578 size_t remaining_bytes =
1579 RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u);
1580 temp = temp.X();
1581 temp1 = temp1.X();
1582 while (remaining_bytes > 8u) {
1583 Register temp2 = XRegisterFrom(locations->GetTemp(0));
1584 __ Ldp(temp, temp1, MemOperand(str.X(), offset));
1585 __ Ldp(temp2, out, MemOperand(arg.X(), offset));
1586 __ Cmp(temp, temp2);
1587 __ Ccmp(temp1, out, NoFlag, eq);
1588 __ B(&return_false, ne);
1589 offset += 2u * sizeof(uint64_t);
1590 remaining_bytes -= 2u * sizeof(uint64_t);
1591 }
1592 if (remaining_bytes != 0u) {
1593 __ Ldr(temp, MemOperand(str.X(), offset));
1594 __ Ldr(temp1, MemOperand(arg.X(), offset));
1595 __ Cmp(temp, temp1);
1596 __ B(&return_false, ne);
1597 }
1598 } else {
1599 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
1600 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1601 "Expecting 0=compressed, 1=uncompressed");
1602 __ Cbz(temp, &return_true);
1603
1604 if (mirror::kUseStringCompression) {
1605 // For string compression, calculate the number of bytes to compare (not chars).
1606 // This could in theory exceed INT32_MAX, so treat temp as unsigned.
1607 __ And(temp1, temp, Operand(1)); // Extract compression flag.
1608 __ Lsr(temp, temp, 1u); // Extract length.
1609 __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare.
1610 }
1611
1612 // Store offset of string value in preparation for comparison loop
1613 __ Mov(temp1, value_offset);
1614
1615 temp1 = temp1.X();
1616 Register temp2 = XRegisterFrom(locations->GetTemp(0));
1617 // Loop to compare strings 8 bytes at a time starting at the front of the string.
1618 // Ok to do this because strings are zero-padded to kObjectAlignment.
1619 __ Bind(&loop);
1620 __ Ldr(out, MemOperand(str.X(), temp1));
1621 __ Ldr(temp2, MemOperand(arg.X(), temp1));
1622 __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
1623 __ Cmp(out, temp2);
1624 __ B(&return_false, ne);
1625 // With string compression, we have compared 8 bytes, otherwise 4 chars.
1626 __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags);
1627 __ B(&loop, hi);
jessicahandojo05765752016-09-09 19:01:32 -07001628 }
1629
Agi Csakiea34b402015-08-13 17:51:19 -07001630 // Return true and exit the function.
1631 // If loop does not result in returning false, we return true.
1632 __ Bind(&return_true);
1633 __ Mov(out, 1);
1634 __ B(&end);
1635
1636 // Return false and exit the function.
1637 __ Bind(&return_false);
1638 __ Mov(out, 0);
1639 __ Bind(&end);
1640}
1641
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001642static void GenerateVisitStringIndexOf(HInvoke* invoke,
Scott Wakeling97c72b72016-06-24 16:19:36 +01001643 MacroAssembler* masm,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001644 CodeGeneratorARM64* codegen,
1645 ArenaAllocator* allocator,
1646 bool start_at_zero) {
1647 LocationSummary* locations = invoke->GetLocations();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001648
1649 // Note that the null check must have been done earlier.
1650 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1651
1652 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001653 // 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 -07001654 SlowPathCodeARM64* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001655 HInstruction* code_point = invoke->InputAt(1);
1656 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01001657 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > 0xFFFFU) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001658 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1659 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1660 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1661 codegen->AddSlowPath(slow_path);
1662 __ B(slow_path->GetEntryLabel());
1663 __ Bind(slow_path->GetExitLabel());
1664 return;
1665 }
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001666 } else if (code_point->GetType() != Primitive::kPrimChar) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001667 Register char_reg = WRegisterFrom(locations->InAt(1));
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001668 __ Tst(char_reg, 0xFFFF0000);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001669 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
1670 codegen->AddSlowPath(slow_path);
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001671 __ B(ne, slow_path->GetEntryLabel());
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001672 }
1673
1674 if (start_at_zero) {
1675 // Start-index = 0.
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001676 Register tmp_reg = WRegisterFrom(locations->GetTemp(0));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001677 __ Mov(tmp_reg, 0);
1678 }
1679
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001680 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
Roland Levillain42ad2882016-02-29 18:26:54 +00001681 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001682
1683 if (slow_path != nullptr) {
1684 __ Bind(slow_path->GetExitLabel());
1685 }
1686}
1687
1688void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) {
1689 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001690 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001691 kIntrinsified);
1692 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1693 // best to align the inputs accordingly.
1694 InvokeRuntimeCallingConvention calling_convention;
1695 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1696 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1697 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
1698
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001699 // Need to send start_index=0.
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001700 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1701}
1702
1703void IntrinsicCodeGeneratorARM64::VisitStringIndexOf(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001704 GenerateVisitStringIndexOf(
1705 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001706}
1707
1708void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
1709 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001710 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001711 kIntrinsified);
1712 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1713 // best to align the inputs accordingly.
1714 InvokeRuntimeCallingConvention calling_convention;
1715 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1716 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1717 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1718 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001719}
1720
1721void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001722 GenerateVisitStringIndexOf(
1723 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001724}
1725
Jeff Hao848f70a2014-01-15 13:49:50 -08001726void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1727 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001728 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001729 kIntrinsified);
1730 InvokeRuntimeCallingConvention calling_convention;
1731 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1732 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1733 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1734 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1735 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1736}
1737
1738void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001739 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001740 LocationSummary* locations = invoke->GetLocations();
1741
1742 Register byte_array = WRegisterFrom(locations->InAt(0));
1743 __ Cmp(byte_array, 0);
1744 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1745 codegen_->AddSlowPath(slow_path);
1746 __ B(eq, slow_path->GetEntryLabel());
1747
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001748 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001749 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001750 __ Bind(slow_path->GetExitLabel());
1751}
1752
1753void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
1754 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001755 LocationSummary::kCallOnMainOnly,
Jeff Hao848f70a2014-01-15 13:49:50 -08001756 kIntrinsified);
1757 InvokeRuntimeCallingConvention calling_convention;
1758 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1759 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1760 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1761 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1762}
1763
1764void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
Roland Levillaincc3839c2016-02-29 16:23:48 +00001765 // No need to emit code checking whether `locations->InAt(2)` is a null
1766 // pointer, as callers of the native method
1767 //
1768 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1769 //
1770 // all include a null check on `data` before calling that method.
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001771 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
Roland Levillainf969a202016-03-09 16:14:00 +00001772 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001773}
1774
1775void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Jeff Hao848f70a2014-01-15 13:49:50 -08001776 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001777 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001778 kIntrinsified);
1779 InvokeRuntimeCallingConvention calling_convention;
1780 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Jeff Hao848f70a2014-01-15 13:49:50 -08001781 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
1782}
1783
1784void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001785 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001786 LocationSummary* locations = invoke->GetLocations();
1787
1788 Register string_to_copy = WRegisterFrom(locations->InAt(0));
1789 __ Cmp(string_to_copy, 0);
1790 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
1791 codegen_->AddSlowPath(slow_path);
1792 __ B(eq, slow_path->GetEntryLabel());
1793
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001794 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001795 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001796 __ Bind(slow_path->GetExitLabel());
1797}
1798
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001799static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1800 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
1801 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1802 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1803
1804 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001805 LocationSummary::kCallOnMainOnly,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001806 kIntrinsified);
1807 InvokeRuntimeCallingConvention calling_convention;
1808
1809 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1810 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1811}
1812
1813static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
1814 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
1815 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1816 DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(1)->GetType()));
1817 DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
1818
1819 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001820 LocationSummary::kCallOnMainOnly,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001821 kIntrinsified);
1822 InvokeRuntimeCallingConvention calling_convention;
1823
1824 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1825 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
1826 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1827}
1828
1829static void GenFPToFPCall(HInvoke* invoke,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001830 CodeGeneratorARM64* codegen,
1831 QuickEntrypointEnum entry) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001832 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001833}
1834
1835void IntrinsicLocationsBuilderARM64::VisitMathCos(HInvoke* invoke) {
1836 CreateFPToFPCallLocations(arena_, invoke);
1837}
1838
1839void IntrinsicCodeGeneratorARM64::VisitMathCos(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001840 GenFPToFPCall(invoke, codegen_, kQuickCos);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001841}
1842
1843void IntrinsicLocationsBuilderARM64::VisitMathSin(HInvoke* invoke) {
1844 CreateFPToFPCallLocations(arena_, invoke);
1845}
1846
1847void IntrinsicCodeGeneratorARM64::VisitMathSin(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001848 GenFPToFPCall(invoke, codegen_, kQuickSin);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001849}
1850
1851void IntrinsicLocationsBuilderARM64::VisitMathAcos(HInvoke* invoke) {
1852 CreateFPToFPCallLocations(arena_, invoke);
1853}
1854
1855void IntrinsicCodeGeneratorARM64::VisitMathAcos(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001856 GenFPToFPCall(invoke, codegen_, kQuickAcos);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001857}
1858
1859void IntrinsicLocationsBuilderARM64::VisitMathAsin(HInvoke* invoke) {
1860 CreateFPToFPCallLocations(arena_, invoke);
1861}
1862
1863void IntrinsicCodeGeneratorARM64::VisitMathAsin(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001864 GenFPToFPCall(invoke, codegen_, kQuickAsin);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001865}
1866
1867void IntrinsicLocationsBuilderARM64::VisitMathAtan(HInvoke* invoke) {
1868 CreateFPToFPCallLocations(arena_, invoke);
1869}
1870
1871void IntrinsicCodeGeneratorARM64::VisitMathAtan(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001872 GenFPToFPCall(invoke, codegen_, kQuickAtan);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001873}
1874
1875void IntrinsicLocationsBuilderARM64::VisitMathCbrt(HInvoke* invoke) {
1876 CreateFPToFPCallLocations(arena_, invoke);
1877}
1878
1879void IntrinsicCodeGeneratorARM64::VisitMathCbrt(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001880 GenFPToFPCall(invoke, codegen_, kQuickCbrt);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001881}
1882
1883void IntrinsicLocationsBuilderARM64::VisitMathCosh(HInvoke* invoke) {
1884 CreateFPToFPCallLocations(arena_, invoke);
1885}
1886
1887void IntrinsicCodeGeneratorARM64::VisitMathCosh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001888 GenFPToFPCall(invoke, codegen_, kQuickCosh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001889}
1890
1891void IntrinsicLocationsBuilderARM64::VisitMathExp(HInvoke* invoke) {
1892 CreateFPToFPCallLocations(arena_, invoke);
1893}
1894
1895void IntrinsicCodeGeneratorARM64::VisitMathExp(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001896 GenFPToFPCall(invoke, codegen_, kQuickExp);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001897}
1898
1899void IntrinsicLocationsBuilderARM64::VisitMathExpm1(HInvoke* invoke) {
1900 CreateFPToFPCallLocations(arena_, invoke);
1901}
1902
1903void IntrinsicCodeGeneratorARM64::VisitMathExpm1(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001904 GenFPToFPCall(invoke, codegen_, kQuickExpm1);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001905}
1906
1907void IntrinsicLocationsBuilderARM64::VisitMathLog(HInvoke* invoke) {
1908 CreateFPToFPCallLocations(arena_, invoke);
1909}
1910
1911void IntrinsicCodeGeneratorARM64::VisitMathLog(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001912 GenFPToFPCall(invoke, codegen_, kQuickLog);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001913}
1914
1915void IntrinsicLocationsBuilderARM64::VisitMathLog10(HInvoke* invoke) {
1916 CreateFPToFPCallLocations(arena_, invoke);
1917}
1918
1919void IntrinsicCodeGeneratorARM64::VisitMathLog10(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001920 GenFPToFPCall(invoke, codegen_, kQuickLog10);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001921}
1922
1923void IntrinsicLocationsBuilderARM64::VisitMathSinh(HInvoke* invoke) {
1924 CreateFPToFPCallLocations(arena_, invoke);
1925}
1926
1927void IntrinsicCodeGeneratorARM64::VisitMathSinh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001928 GenFPToFPCall(invoke, codegen_, kQuickSinh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001929}
1930
1931void IntrinsicLocationsBuilderARM64::VisitMathTan(HInvoke* invoke) {
1932 CreateFPToFPCallLocations(arena_, invoke);
1933}
1934
1935void IntrinsicCodeGeneratorARM64::VisitMathTan(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001936 GenFPToFPCall(invoke, codegen_, kQuickTan);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001937}
1938
1939void IntrinsicLocationsBuilderARM64::VisitMathTanh(HInvoke* invoke) {
1940 CreateFPToFPCallLocations(arena_, invoke);
1941}
1942
1943void IntrinsicCodeGeneratorARM64::VisitMathTanh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001944 GenFPToFPCall(invoke, codegen_, kQuickTanh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001945}
1946
1947void IntrinsicLocationsBuilderARM64::VisitMathAtan2(HInvoke* invoke) {
1948 CreateFPFPToFPCallLocations(arena_, invoke);
1949}
1950
1951void IntrinsicCodeGeneratorARM64::VisitMathAtan2(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001952 GenFPToFPCall(invoke, codegen_, kQuickAtan2);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001953}
1954
1955void IntrinsicLocationsBuilderARM64::VisitMathHypot(HInvoke* invoke) {
1956 CreateFPFPToFPCallLocations(arena_, invoke);
1957}
1958
1959void IntrinsicCodeGeneratorARM64::VisitMathHypot(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001960 GenFPToFPCall(invoke, codegen_, kQuickHypot);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001961}
1962
1963void IntrinsicLocationsBuilderARM64::VisitMathNextAfter(HInvoke* invoke) {
1964 CreateFPFPToFPCallLocations(arena_, invoke);
1965}
1966
1967void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001968 GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001969}
1970
Tim Zhang25abd6c2016-01-19 23:39:24 +08001971void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1972 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1973 LocationSummary::kNoCall,
1974 kIntrinsified);
1975 locations->SetInAt(0, Location::RequiresRegister());
1976 locations->SetInAt(1, Location::RequiresRegister());
1977 locations->SetInAt(2, Location::RequiresRegister());
1978 locations->SetInAt(3, Location::RequiresRegister());
1979 locations->SetInAt(4, Location::RequiresRegister());
1980
1981 locations->AddTemp(Location::RequiresRegister());
1982 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingdf109d92016-04-22 11:35:56 +01001983 locations->AddTemp(Location::RequiresRegister());
Tim Zhang25abd6c2016-01-19 23:39:24 +08001984}
1985
1986void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001987 MacroAssembler* masm = GetVIXLAssembler();
Tim Zhang25abd6c2016-01-19 23:39:24 +08001988 LocationSummary* locations = invoke->GetLocations();
1989
1990 // Check assumption that sizeof(Char) is 2 (used in scaling below).
1991 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1992 DCHECK_EQ(char_size, 2u);
1993
1994 // Location of data in char array buffer.
1995 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
1996
1997 // Location of char array data in string.
1998 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1999
2000 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2001 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2002 Register srcObj = XRegisterFrom(locations->InAt(0));
2003 Register srcBegin = XRegisterFrom(locations->InAt(1));
2004 Register srcEnd = XRegisterFrom(locations->InAt(2));
2005 Register dstObj = XRegisterFrom(locations->InAt(3));
2006 Register dstBegin = XRegisterFrom(locations->InAt(4));
2007
2008 Register src_ptr = XRegisterFrom(locations->GetTemp(0));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002009 Register num_chr = XRegisterFrom(locations->GetTemp(1));
2010 Register tmp1 = XRegisterFrom(locations->GetTemp(2));
Tim Zhang25abd6c2016-01-19 23:39:24 +08002011
2012 UseScratchRegisterScope temps(masm);
2013 Register dst_ptr = temps.AcquireX();
Scott Wakelingdf109d92016-04-22 11:35:56 +01002014 Register tmp2 = temps.AcquireX();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002015
jessicahandojo05765752016-09-09 19:01:32 -07002016 vixl::aarch64::Label done;
2017 vixl::aarch64::Label compressed_string_loop;
2018 __ Sub(num_chr, srcEnd, srcBegin);
2019 // Early out for valid zero-length retrievals.
2020 __ Cbz(num_chr, &done);
Tim Zhang25abd6c2016-01-19 23:39:24 +08002021
Scott Wakelingdf109d92016-04-22 11:35:56 +01002022 // dst address start to copy to.
Tim Zhang25abd6c2016-01-19 23:39:24 +08002023 __ Add(dst_ptr, dstObj, Operand(data_offset));
2024 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
2025
jessicahandojo05765752016-09-09 19:01:32 -07002026 // src address to copy from.
2027 __ Add(src_ptr, srcObj, Operand(value_offset));
2028 vixl::aarch64::Label compressed_string_preloop;
2029 if (mirror::kUseStringCompression) {
2030 // Location of count in string.
2031 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2032 // String's length.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002033 __ Ldr(tmp2, MemOperand(srcObj, count_offset));
2034 __ Tbz(tmp2, 0, &compressed_string_preloop);
jessicahandojo05765752016-09-09 19:01:32 -07002035 }
2036 __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002037
Tim Zhang25abd6c2016-01-19 23:39:24 +08002038 // Do the copy.
Scott Wakeling97c72b72016-06-24 16:19:36 +01002039 vixl::aarch64::Label loop;
Scott Wakeling97c72b72016-06-24 16:19:36 +01002040 vixl::aarch64::Label remainder;
Scott Wakelingdf109d92016-04-22 11:35:56 +01002041
Scott Wakelingdf109d92016-04-22 11:35:56 +01002042 // Save repairing the value of num_chr on the < 8 character path.
2043 __ Subs(tmp1, num_chr, 8);
2044 __ B(lt, &remainder);
2045
2046 // Keep the result of the earlier subs, we are going to fetch at least 8 characters.
2047 __ Mov(num_chr, tmp1);
2048
2049 // Main loop used for longer fetches loads and stores 8x16-bit characters at a time.
2050 // (Unaligned addresses are acceptable here and not worth inlining extra code to rectify.)
Tim Zhang25abd6c2016-01-19 23:39:24 +08002051 __ Bind(&loop);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002052 __ Ldp(tmp1, tmp2, MemOperand(src_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002053 __ Subs(num_chr, num_chr, 8);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002054 __ Stp(tmp1, tmp2, MemOperand(dst_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002055 __ B(ge, &loop);
2056
2057 __ Adds(num_chr, num_chr, 8);
2058 __ B(eq, &done);
2059
2060 // Main loop for < 8 character case and remainder handling. Loads and stores one
2061 // 16-bit Java character at a time.
2062 __ Bind(&remainder);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002063 __ Ldrh(tmp1, MemOperand(src_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002064 __ Subs(num_chr, num_chr, 1);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002065 __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002066 __ B(gt, &remainder);
jessicahandojo05765752016-09-09 19:01:32 -07002067 __ B(&done);
2068
2069 if (mirror::kUseStringCompression) {
2070 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
2071 DCHECK_EQ(c_char_size, 1u);
2072 __ Bind(&compressed_string_preloop);
2073 __ Add(src_ptr, src_ptr, Operand(srcBegin));
2074 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2075 __ Bind(&compressed_string_loop);
2076 __ Ldrb(tmp1, MemOperand(src_ptr, c_char_size, PostIndex));
2077 __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
2078 __ Subs(num_chr, num_chr, Operand(1));
2079 __ B(gt, &compressed_string_loop);
2080 }
Scott Wakelingdf109d92016-04-22 11:35:56 +01002081
Tim Zhang25abd6c2016-01-19 23:39:24 +08002082 __ Bind(&done);
2083}
2084
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002085// Mirrors ARRAYCOPY_SHORT_CHAR_ARRAY_THRESHOLD in libcore, so we can choose to use the native
2086// implementation there for longer copy lengths.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002087static constexpr int32_t kSystemArrayCopyCharThreshold = 32;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002088
2089static void SetSystemArrayCopyLocationRequires(LocationSummary* locations,
2090 uint32_t at,
2091 HInstruction* input) {
2092 HIntConstant* const_input = input->AsIntConstant();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002093 if (const_input != nullptr && !vixl::aarch64::Assembler::IsImmAddSub(const_input->GetValue())) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002094 locations->SetInAt(at, Location::RequiresRegister());
2095 } else {
2096 locations->SetInAt(at, Location::RegisterOrConstant(input));
2097 }
2098}
2099
2100void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
2101 // Check to see if we have known failures that will cause us to have to bail out
2102 // to the runtime, and just generate the runtime call directly.
2103 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2104 HIntConstant* dst_pos = invoke->InputAt(3)->AsIntConstant();
2105
2106 // The positions must be non-negative.
2107 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2108 (dst_pos != nullptr && dst_pos->GetValue() < 0)) {
2109 // We will have to fail anyways.
2110 return;
2111 }
2112
2113 // The length must be >= 0 and not so long that we would (currently) prefer libcore's
2114 // native implementation.
2115 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2116 if (length != nullptr) {
2117 int32_t len = length->GetValue();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002118 if (len < 0 || len > kSystemArrayCopyCharThreshold) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002119 // Just call as normal.
2120 return;
2121 }
2122 }
2123
2124 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2125 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2126 LocationSummary::kCallOnSlowPath,
2127 kIntrinsified);
2128 // arraycopy(char[] src, int src_pos, char[] dst, int dst_pos, int length).
2129 locations->SetInAt(0, Location::RequiresRegister());
2130 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2131 locations->SetInAt(2, Location::RequiresRegister());
2132 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2133 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2134
2135 locations->AddTemp(Location::RequiresRegister());
2136 locations->AddTemp(Location::RequiresRegister());
2137 locations->AddTemp(Location::RequiresRegister());
2138}
2139
Scott Wakeling97c72b72016-06-24 16:19:36 +01002140static void CheckSystemArrayCopyPosition(MacroAssembler* masm,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002141 const Location& pos,
2142 const Register& input,
2143 const Location& length,
2144 SlowPathCodeARM64* slow_path,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002145 const Register& temp,
2146 bool length_is_input_length = false) {
2147 const int32_t length_offset = mirror::Array::LengthOffset().Int32Value();
2148 if (pos.IsConstant()) {
2149 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
2150 if (pos_const == 0) {
2151 if (!length_is_input_length) {
2152 // Check that length(input) >= length.
2153 __ Ldr(temp, MemOperand(input, length_offset));
2154 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
2155 __ B(slow_path->GetEntryLabel(), lt);
2156 }
2157 } else {
2158 // Check that length(input) >= pos.
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002159 __ Ldr(temp, MemOperand(input, length_offset));
2160 __ Subs(temp, temp, pos_const);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002161 __ B(slow_path->GetEntryLabel(), lt);
2162
2163 // Check that (length(input) - pos) >= length.
2164 __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt));
2165 __ B(slow_path->GetEntryLabel(), lt);
2166 }
2167 } else if (length_is_input_length) {
2168 // The only way the copy can succeed is if pos is zero.
2169 __ Cbnz(WRegisterFrom(pos), slow_path->GetEntryLabel());
2170 } else {
2171 // Check that pos >= 0.
2172 Register pos_reg = WRegisterFrom(pos);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002173 __ Tbnz(pos_reg, pos_reg.GetSizeInBits() - 1, slow_path->GetEntryLabel());
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002174
2175 // Check that pos <= length(input) && (length(input) - pos) >= length.
2176 __ Ldr(temp, MemOperand(input, length_offset));
2177 __ Subs(temp, temp, pos_reg);
2178 // Ccmp if length(input) >= pos, else definitely bail to slow path (N!=V == lt).
2179 __ Ccmp(temp, OperandFrom(length, Primitive::kPrimInt), NFlag, ge);
2180 __ B(slow_path->GetEntryLabel(), lt);
2181 }
2182}
2183
2184// Compute base source address, base destination address, and end source address
2185// for System.arraycopy* intrinsics.
Scott Wakeling97c72b72016-06-24 16:19:36 +01002186static void GenSystemArrayCopyAddresses(MacroAssembler* masm,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002187 Primitive::Type type,
2188 const Register& src,
2189 const Location& src_pos,
2190 const Register& dst,
2191 const Location& dst_pos,
2192 const Location& copy_length,
2193 const Register& src_base,
2194 const Register& dst_base,
2195 const Register& src_end) {
2196 DCHECK(type == Primitive::kPrimNot || type == Primitive::kPrimChar)
Roland Levillainebea3d22016-04-12 15:42:57 +01002197 << "Unexpected element type: " << type;
2198 const int32_t element_size = Primitive::ComponentSize(type);
2199 const int32_t element_size_shift = Primitive::ComponentSizeShift(type);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002200
Roland Levillainebea3d22016-04-12 15:42:57 +01002201 uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002202 if (src_pos.IsConstant()) {
2203 int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002204 __ Add(src_base, src, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002205 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002206 __ Add(src_base, src, data_offset);
2207 __ Add(src_base, src_base, Operand(XRegisterFrom(src_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002208 }
2209
2210 if (dst_pos.IsConstant()) {
2211 int32_t constant = dst_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002212 __ Add(dst_base, dst, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002213 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002214 __ Add(dst_base, dst, data_offset);
2215 __ Add(dst_base, dst_base, Operand(XRegisterFrom(dst_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002216 }
2217
2218 if (copy_length.IsConstant()) {
2219 int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002220 __ Add(src_end, src_base, element_size * constant);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002221 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002222 __ Add(src_end, src_base, Operand(XRegisterFrom(copy_length), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002223 }
2224}
2225
2226void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01002227 MacroAssembler* masm = GetVIXLAssembler();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002228 LocationSummary* locations = invoke->GetLocations();
2229 Register src = XRegisterFrom(locations->InAt(0));
2230 Location src_pos = locations->InAt(1);
2231 Register dst = XRegisterFrom(locations->InAt(2));
2232 Location dst_pos = locations->InAt(3);
2233 Location length = locations->InAt(4);
2234
2235 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2236 codegen_->AddSlowPath(slow_path);
2237
2238 // If source and destination are the same, take the slow path. Overlapping copy regions must be
2239 // copied in reverse and we can't know in all cases if it's needed.
2240 __ Cmp(src, dst);
2241 __ B(slow_path->GetEntryLabel(), eq);
2242
2243 // Bail out if the source is null.
2244 __ Cbz(src, slow_path->GetEntryLabel());
2245
2246 // Bail out if the destination is null.
2247 __ Cbz(dst, slow_path->GetEntryLabel());
2248
2249 if (!length.IsConstant()) {
Vladimir Markoc5646202016-11-28 16:03:15 +00002250 // Merge the following two comparisons into one:
2251 // If the length is negative, bail out (delegate to libcore's native implementation).
2252 // If the length > 32 then (currently) prefer libcore's native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002253 __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold);
Vladimir Markoc5646202016-11-28 16:03:15 +00002254 __ B(slow_path->GetEntryLabel(), hi);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002255 } else {
2256 // We have already checked in the LocationsBuilder for the constant case.
2257 DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0);
2258 DCHECK_LE(length.GetConstant()->AsIntConstant()->GetValue(), 32);
2259 }
2260
2261 Register src_curr_addr = WRegisterFrom(locations->GetTemp(0));
2262 Register dst_curr_addr = WRegisterFrom(locations->GetTemp(1));
2263 Register src_stop_addr = WRegisterFrom(locations->GetTemp(2));
2264
2265 CheckSystemArrayCopyPosition(masm,
2266 src_pos,
2267 src,
2268 length,
2269 slow_path,
2270 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002271 false);
2272
2273 CheckSystemArrayCopyPosition(masm,
2274 dst_pos,
2275 dst,
2276 length,
2277 slow_path,
2278 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002279 false);
2280
2281 src_curr_addr = src_curr_addr.X();
2282 dst_curr_addr = dst_curr_addr.X();
2283 src_stop_addr = src_stop_addr.X();
2284
2285 GenSystemArrayCopyAddresses(masm,
2286 Primitive::kPrimChar,
2287 src,
2288 src_pos,
2289 dst,
2290 dst_pos,
2291 length,
2292 src_curr_addr,
2293 dst_curr_addr,
2294 src_stop_addr);
2295
2296 // Iterate over the arrays and do a raw copy of the chars.
2297 const int32_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2298 UseScratchRegisterScope temps(masm);
2299 Register tmp = temps.AcquireW();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002300 vixl::aarch64::Label loop, done;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002301 __ Bind(&loop);
2302 __ Cmp(src_curr_addr, src_stop_addr);
2303 __ B(&done, eq);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002304 __ Ldrh(tmp, MemOperand(src_curr_addr, char_size, PostIndex));
2305 __ Strh(tmp, MemOperand(dst_curr_addr, char_size, PostIndex));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002306 __ B(&loop);
2307 __ Bind(&done);
2308
2309 __ Bind(slow_path->GetExitLabel());
2310}
2311
donghui.baic2ec9ad2016-03-10 14:02:55 +08002312// We can choose to use the native implementation there for longer copy lengths.
2313static constexpr int32_t kSystemArrayCopyThreshold = 128;
2314
2315// CodeGenerator::CreateSystemArrayCopyLocationSummary use three temporary registers.
2316// We want to use two temporary registers in order to reduce the register pressure in arm64.
2317// So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary.
2318void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002319 // The only read barrier implementation supporting the
2320 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2321 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain3d312422016-06-23 13:53:42 +01002322 return;
2323 }
2324
donghui.baic2ec9ad2016-03-10 14:02:55 +08002325 // Check to see if we have known failures that will cause us to have to bail out
2326 // to the runtime, and just generate the runtime call directly.
2327 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2328 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
2329
2330 // The positions must be non-negative.
2331 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2332 (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
2333 // We will have to fail anyways.
2334 return;
2335 }
2336
2337 // The length must be >= 0.
2338 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2339 if (length != nullptr) {
2340 int32_t len = length->GetValue();
2341 if (len < 0 || len >= kSystemArrayCopyThreshold) {
2342 // Just call as normal.
2343 return;
2344 }
2345 }
2346
2347 SystemArrayCopyOptimizations optimizations(invoke);
2348
2349 if (optimizations.GetDestinationIsSource()) {
2350 if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) {
2351 // We only support backward copying if source and destination are the same.
2352 return;
2353 }
2354 }
2355
2356 if (optimizations.GetDestinationIsPrimitiveArray() || optimizations.GetSourceIsPrimitiveArray()) {
2357 // We currently don't intrinsify primitive copying.
2358 return;
2359 }
2360
2361 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
2362 LocationSummary* locations = new (allocator) LocationSummary(invoke,
2363 LocationSummary::kCallOnSlowPath,
2364 kIntrinsified);
2365 // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length).
2366 locations->SetInAt(0, Location::RequiresRegister());
2367 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2368 locations->SetInAt(2, Location::RequiresRegister());
2369 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2370 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2371
2372 locations->AddTemp(Location::RequiresRegister());
2373 locations->AddTemp(Location::RequiresRegister());
Roland Levillain0b671c02016-08-19 12:02:34 +01002374 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2375 // Temporary register IP0, obtained from the VIXL scratch register
2376 // pool, cannot be used in ReadBarrierSystemArrayCopySlowPathARM64
2377 // (because that register is clobbered by ReadBarrierMarkRegX
2378 // entry points). Get an extra temporary register from the
2379 // register allocator.
2380 locations->AddTemp(Location::RequiresRegister());
2381 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002382}
2383
2384void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002385 // The only read barrier implementation supporting the
2386 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2387 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01002388
Scott Wakeling97c72b72016-06-24 16:19:36 +01002389 MacroAssembler* masm = GetVIXLAssembler();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002390 LocationSummary* locations = invoke->GetLocations();
2391
2392 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2393 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2394 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2395 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Roland Levillain0b671c02016-08-19 12:02:34 +01002396 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002397
2398 Register src = XRegisterFrom(locations->InAt(0));
2399 Location src_pos = locations->InAt(1);
2400 Register dest = XRegisterFrom(locations->InAt(2));
2401 Location dest_pos = locations->InAt(3);
2402 Location length = locations->InAt(4);
2403 Register temp1 = WRegisterFrom(locations->GetTemp(0));
Roland Levillain0b671c02016-08-19 12:02:34 +01002404 Location temp1_loc = LocationFrom(temp1);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002405 Register temp2 = WRegisterFrom(locations->GetTemp(1));
Roland Levillain0b671c02016-08-19 12:02:34 +01002406 Location temp2_loc = LocationFrom(temp2);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002407
Roland Levillain0b671c02016-08-19 12:02:34 +01002408 SlowPathCodeARM64* intrinsic_slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2409 codegen_->AddSlowPath(intrinsic_slow_path);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002410
Scott Wakeling97c72b72016-06-24 16:19:36 +01002411 vixl::aarch64::Label conditions_on_positions_validated;
donghui.baic2ec9ad2016-03-10 14:02:55 +08002412 SystemArrayCopyOptimizations optimizations(invoke);
2413
donghui.baic2ec9ad2016-03-10 14:02:55 +08002414 // If source and destination are the same, we go to slow path if we need to do
2415 // forward copying.
2416 if (src_pos.IsConstant()) {
2417 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
2418 if (dest_pos.IsConstant()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002419 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
2420 if (optimizations.GetDestinationIsSource()) {
2421 // Checked when building locations.
2422 DCHECK_GE(src_pos_constant, dest_pos_constant);
2423 } else if (src_pos_constant < dest_pos_constant) {
2424 __ Cmp(src, dest);
Roland Levillain0b671c02016-08-19 12:02:34 +01002425 __ B(intrinsic_slow_path->GetEntryLabel(), eq);
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002426 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002427 // Checked when building locations.
2428 DCHECK(!optimizations.GetDestinationIsSource()
2429 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
2430 } else {
2431 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002432 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002433 __ B(&conditions_on_positions_validated, ne);
2434 }
2435 __ Cmp(WRegisterFrom(dest_pos), src_pos_constant);
Roland Levillain0b671c02016-08-19 12:02:34 +01002436 __ B(intrinsic_slow_path->GetEntryLabel(), gt);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002437 }
2438 } else {
2439 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002440 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002441 __ B(&conditions_on_positions_validated, ne);
2442 }
2443 __ Cmp(RegisterFrom(src_pos, invoke->InputAt(1)->GetType()),
2444 OperandFrom(dest_pos, invoke->InputAt(3)->GetType()));
Roland Levillain0b671c02016-08-19 12:02:34 +01002445 __ B(intrinsic_slow_path->GetEntryLabel(), lt);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002446 }
2447
2448 __ Bind(&conditions_on_positions_validated);
2449
2450 if (!optimizations.GetSourceIsNotNull()) {
2451 // Bail out if the source is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01002452 __ Cbz(src, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002453 }
2454
2455 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
2456 // Bail out if the destination is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01002457 __ Cbz(dest, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002458 }
2459
2460 // We have already checked in the LocationsBuilder for the constant case.
2461 if (!length.IsConstant() &&
2462 !optimizations.GetCountIsSourceLength() &&
2463 !optimizations.GetCountIsDestinationLength()) {
Vladimir Markoc5646202016-11-28 16:03:15 +00002464 // Merge the following two comparisons into one:
2465 // If the length is negative, bail out (delegate to libcore's native implementation).
2466 // If the length >= 128 then (currently) prefer native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002467 __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold);
Vladimir Markoc5646202016-11-28 16:03:15 +00002468 __ B(intrinsic_slow_path->GetEntryLabel(), hs);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002469 }
2470 // Validity checks: source.
2471 CheckSystemArrayCopyPosition(masm,
2472 src_pos,
2473 src,
2474 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002475 intrinsic_slow_path,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002476 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002477 optimizations.GetCountIsSourceLength());
2478
2479 // Validity checks: dest.
2480 CheckSystemArrayCopyPosition(masm,
2481 dest_pos,
2482 dest,
2483 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002484 intrinsic_slow_path,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002485 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002486 optimizations.GetCountIsDestinationLength());
2487 {
2488 // We use a block to end the scratch scope before the write barrier, thus
2489 // freeing the temporary registers so they can be used in `MarkGCCard`.
2490 UseScratchRegisterScope temps(masm);
Roland Levillain0b671c02016-08-19 12:02:34 +01002491 // Note: Because it is acquired from VIXL's scratch register pool,
2492 // `temp3` might be IP0, and thus cannot be used as `ref` argument
2493 // of CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier
2494 // calls below (see ReadBarrierMarkSlowPathARM64 for more details).
donghui.baic2ec9ad2016-03-10 14:02:55 +08002495 Register temp3 = temps.AcquireW();
Roland Levillain0b671c02016-08-19 12:02:34 +01002496
donghui.baic2ec9ad2016-03-10 14:02:55 +08002497 if (!optimizations.GetDoesNotNeedTypeCheck()) {
2498 // Check whether all elements of the source array are assignable to the component
2499 // type of the destination array. We do two checks: the classes are the same,
2500 // or the destination is Object[]. If none of these checks succeed, we go to the
2501 // slow path.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002502
Roland Levillain0b671c02016-08-19 12:02:34 +01002503 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2504 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2505 // /* HeapReference<Class> */ temp1 = src->klass_
2506 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2507 temp1_loc,
2508 src.W(),
2509 class_offset,
2510 temp2,
2511 /* needs_null_check */ false,
2512 /* use_load_acquire */ false);
2513 // Bail out if the source is not a non primitive array.
2514 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2515 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2516 temp1_loc,
2517 temp1,
2518 component_offset,
2519 temp2,
2520 /* needs_null_check */ false,
2521 /* use_load_acquire */ false);
2522 __ Cbz(temp1, intrinsic_slow_path->GetEntryLabel());
2523 // If heap poisoning is enabled, `temp1` has been unpoisoned
2524 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2525 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
2526 __ Ldrh(temp1, HeapOperand(temp1, primitive_offset));
2527 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2528 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002529 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002530
2531 // /* HeapReference<Class> */ temp1 = dest->klass_
2532 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2533 temp1_loc,
2534 dest.W(),
2535 class_offset,
2536 temp2,
2537 /* needs_null_check */ false,
2538 /* use_load_acquire */ false);
2539
2540 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2541 // Bail out if the destination is not a non primitive array.
2542 //
2543 // Register `temp1` is not trashed by the read barrier emitted
2544 // by GenerateFieldLoadWithBakerReadBarrier below, as that
2545 // method produces a call to a ReadBarrierMarkRegX entry point,
2546 // which saves all potentially live registers, including
2547 // temporaries such a `temp1`.
2548 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2549 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2550 temp2_loc,
2551 temp1,
2552 component_offset,
2553 temp3,
2554 /* needs_null_check */ false,
2555 /* use_load_acquire */ false);
2556 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2557 // If heap poisoning is enabled, `temp2` has been unpoisoned
2558 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2559 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2560 __ Ldrh(temp2, HeapOperand(temp2, primitive_offset));
2561 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2562 __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
2563 }
2564
2565 // For the same reason given earlier, `temp1` is not trashed by the
2566 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
2567 // /* HeapReference<Class> */ temp2 = src->klass_
2568 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2569 temp2_loc,
2570 src.W(),
2571 class_offset,
2572 temp3,
2573 /* needs_null_check */ false,
2574 /* use_load_acquire */ false);
2575 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
2576 __ Cmp(temp1, temp2);
2577
2578 if (optimizations.GetDestinationIsTypedObjectArray()) {
2579 vixl::aarch64::Label do_copy;
2580 __ B(&do_copy, eq);
2581 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2582 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2583 temp1_loc,
2584 temp1,
2585 component_offset,
2586 temp2,
2587 /* needs_null_check */ false,
2588 /* use_load_acquire */ false);
2589 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2590 // We do not need to emit a read barrier for the following
2591 // heap reference load, as `temp1` is only used in a
2592 // comparison with null below, and this reference is not
2593 // kept afterwards.
2594 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2595 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
2596 __ Bind(&do_copy);
2597 } else {
2598 __ B(intrinsic_slow_path->GetEntryLabel(), ne);
2599 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002600 } else {
Roland Levillain0b671c02016-08-19 12:02:34 +01002601 // Non read barrier code.
2602
2603 // /* HeapReference<Class> */ temp1 = dest->klass_
2604 __ Ldr(temp1, MemOperand(dest, class_offset));
2605 // /* HeapReference<Class> */ temp2 = src->klass_
2606 __ Ldr(temp2, MemOperand(src, class_offset));
2607 bool did_unpoison = false;
2608 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2609 !optimizations.GetSourceIsNonPrimitiveArray()) {
2610 // One or two of the references need to be unpoisoned. Unpoison them
2611 // both to make the identity check valid.
2612 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2613 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2614 did_unpoison = true;
2615 }
2616
2617 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2618 // Bail out if the destination is not a non primitive array.
2619 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2620 __ Ldr(temp3, HeapOperand(temp1, component_offset));
2621 __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
2622 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2623 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2624 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2625 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2626 __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
2627 }
2628
2629 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2630 // Bail out if the source is not a non primitive array.
2631 // /* HeapReference<Class> */ temp3 = temp2->component_type_
2632 __ Ldr(temp3, HeapOperand(temp2, component_offset));
2633 __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
2634 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2635 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2636 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2637 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2638 __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
2639 }
2640
2641 __ Cmp(temp1, temp2);
2642
2643 if (optimizations.GetDestinationIsTypedObjectArray()) {
2644 vixl::aarch64::Label do_copy;
2645 __ B(&do_copy, eq);
2646 if (!did_unpoison) {
2647 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2648 }
2649 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2650 __ Ldr(temp1, HeapOperand(temp1, component_offset));
2651 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2652 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2653 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2654 // No need to unpoison the result, we're comparing against null.
2655 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
2656 __ Bind(&do_copy);
2657 } else {
2658 __ B(intrinsic_slow_path->GetEntryLabel(), ne);
2659 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002660 }
2661 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2662 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2663 // Bail out if the source is not a non primitive array.
Roland Levillain0b671c02016-08-19 12:02:34 +01002664 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2665 // /* HeapReference<Class> */ temp1 = src->klass_
2666 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2667 temp1_loc,
2668 src.W(),
2669 class_offset,
2670 temp2,
2671 /* needs_null_check */ false,
2672 /* use_load_acquire */ false);
2673 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2674 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2675 temp2_loc,
2676 temp1,
2677 component_offset,
2678 temp3,
2679 /* needs_null_check */ false,
2680 /* use_load_acquire */ false);
2681 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2682 // If heap poisoning is enabled, `temp2` has been unpoisoned
2683 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2684 } else {
2685 // /* HeapReference<Class> */ temp1 = src->klass_
2686 __ Ldr(temp1, HeapOperand(src.W(), class_offset));
2687 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2688 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2689 __ Ldr(temp2, HeapOperand(temp1, component_offset));
2690 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2691 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2692 }
2693 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2694 __ Ldrh(temp2, HeapOperand(temp2, primitive_offset));
donghui.baic2ec9ad2016-03-10 14:02:55 +08002695 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Roland Levillain0b671c02016-08-19 12:02:34 +01002696 __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002697 }
2698
2699 Register src_curr_addr = temp1.X();
2700 Register dst_curr_addr = temp2.X();
Roland Levillain0b671c02016-08-19 12:02:34 +01002701 Register src_stop_addr;
2702 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2703 // Temporary register IP0, obtained from the VIXL scratch
2704 // register pool as `temp3`, cannot be used in
2705 // ReadBarrierSystemArrayCopySlowPathARM64 (because that
2706 // register is clobbered by ReadBarrierMarkRegX entry points).
2707 // So another temporary register allocated by the register
2708 // allocator instead.
2709 DCHECK_EQ(LocationFrom(temp3).reg(), IP0);
2710 src_stop_addr = XRegisterFrom(locations->GetTemp(2));
2711 } else {
2712 src_stop_addr = temp3.X();
2713 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002714
2715 GenSystemArrayCopyAddresses(masm,
2716 Primitive::kPrimNot,
2717 src,
2718 src_pos,
2719 dest,
2720 dest_pos,
2721 length,
2722 src_curr_addr,
2723 dst_curr_addr,
2724 src_stop_addr);
2725
donghui.baic2ec9ad2016-03-10 14:02:55 +08002726 const int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
Roland Levillain0b671c02016-08-19 12:02:34 +01002727
2728 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2729 // SystemArrayCopy implementation for Baker read barriers (see
2730 // also CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier):
2731 //
2732 // if (src_ptr != end_ptr) {
2733 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
2734 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
Hiroshi Yamauchi12b58b22016-11-01 11:55:29 -07002735 // bool is_gray = (rb_state == ReadBarrier::GrayState());
Roland Levillain0b671c02016-08-19 12:02:34 +01002736 // if (is_gray) {
2737 // // Slow-path copy.
2738 // do {
2739 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
2740 // } while (src_ptr != end_ptr)
2741 // } else {
2742 // // Fast-path copy.
2743 // do {
2744 // *dest_ptr++ = *src_ptr++;
2745 // } while (src_ptr != end_ptr)
2746 // }
2747 // }
2748
2749 vixl::aarch64::Label loop, done;
2750
2751 // Don't enter copy loop if `length == 0`.
2752 __ Cmp(src_curr_addr, src_stop_addr);
2753 __ B(&done, eq);
2754
donghui.baic2ec9ad2016-03-10 14:02:55 +08002755 Register tmp = temps.AcquireW();
Roland Levillain0b671c02016-08-19 12:02:34 +01002756 // Make sure `tmp` is not IP0, as it is clobbered by
2757 // ReadBarrierMarkRegX entry points in
2758 // ReadBarrierSystemArrayCopySlowPathARM64.
2759 DCHECK_NE(LocationFrom(tmp).reg(), IP0);
2760
2761 // /* int32_t */ monitor = src->monitor_
2762 __ Ldr(tmp, HeapOperand(src.W(), monitor_offset));
2763 // /* LockWord */ lock_word = LockWord(monitor)
2764 static_assert(sizeof(LockWord) == sizeof(int32_t),
2765 "art::LockWord and int32_t have different sizes.");
2766
2767 // Introduce a dependency on the lock_word including rb_state,
2768 // to prevent load-load reordering, and without using
2769 // a memory barrier (which would be more expensive).
2770 // `src` is unchanged by this operation, but its value now depends
2771 // on `tmp`.
2772 __ Add(src.X(), src.X(), Operand(tmp.X(), LSR, 32));
2773
2774 // Slow path used to copy array when `src` is gray.
2775 SlowPathCodeARM64* read_barrier_slow_path =
2776 new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathARM64(invoke, LocationFrom(tmp));
2777 codegen_->AddSlowPath(read_barrier_slow_path);
2778
2779 // Given the numeric representation, it's enough to check the low bit of the rb_state.
Hiroshi Yamauchi12b58b22016-11-01 11:55:29 -07002780 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
2781 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
Roland Levillain0b671c02016-08-19 12:02:34 +01002782 __ Tbnz(tmp, LockWord::kReadBarrierStateShift, read_barrier_slow_path->GetEntryLabel());
2783
2784 // Fast-path copy.
2785 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2786 // poison/unpoison.
2787 __ Bind(&loop);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002788 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
2789 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
Roland Levillain0b671c02016-08-19 12:02:34 +01002790 __ Cmp(src_curr_addr, src_stop_addr);
2791 __ B(&loop, ne);
2792
2793 __ Bind(read_barrier_slow_path->GetExitLabel());
2794 __ Bind(&done);
2795 } else {
2796 // Non read barrier code.
2797
2798 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2799 // poison/unpoison.
2800 vixl::aarch64::Label loop, done;
2801 __ Bind(&loop);
2802 __ Cmp(src_curr_addr, src_stop_addr);
2803 __ B(&done, eq);
2804 {
2805 Register tmp = temps.AcquireW();
2806 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
2807 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
2808 }
2809 __ B(&loop);
2810 __ Bind(&done);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002811 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002812 }
2813 // We only need one card marking on the destination array.
2814 codegen_->MarkGCCard(dest.W(), Register(), /* value_can_be_null */ false);
2815
Roland Levillain0b671c02016-08-19 12:02:34 +01002816 __ Bind(intrinsic_slow_path->GetExitLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002817}
2818
Anton Kirilova3ffea22016-04-07 17:02:37 +01002819static void GenIsInfinite(LocationSummary* locations,
2820 bool is64bit,
Scott Wakeling97c72b72016-06-24 16:19:36 +01002821 MacroAssembler* masm) {
Anton Kirilova3ffea22016-04-07 17:02:37 +01002822 Operand infinity;
2823 Register out;
2824
2825 if (is64bit) {
2826 infinity = kPositiveInfinityDouble;
2827 out = XRegisterFrom(locations->Out());
2828 } else {
2829 infinity = kPositiveInfinityFloat;
2830 out = WRegisterFrom(locations->Out());
2831 }
2832
Scott Wakeling97c72b72016-06-24 16:19:36 +01002833 const Register zero = vixl::aarch64::Assembler::AppropriateZeroRegFor(out);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002834
2835 MoveFPToInt(locations, is64bit, masm);
2836 __ Eor(out, out, infinity);
2837 // We don't care about the sign bit, so shift left.
2838 __ Cmp(zero, Operand(out, LSL, 1));
2839 __ Cset(out, eq);
2840}
2841
2842void IntrinsicLocationsBuilderARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2843 CreateFPToIntLocations(arena_, invoke);
2844}
2845
2846void IntrinsicCodeGeneratorARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2847 GenIsInfinite(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
2848}
2849
2850void IntrinsicLocationsBuilderARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2851 CreateFPToIntLocations(arena_, invoke);
2852}
2853
2854void IntrinsicCodeGeneratorARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2855 GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
2856}
2857
TatWai Chongd8c052a2016-11-02 16:12:48 +08002858void IntrinsicLocationsBuilderARM64::VisitReferenceGetReferent(HInvoke* invoke) {
2859 if (kEmitCompilerReadBarrier) {
2860 // Do not intrinsify this call with the read barrier configuration.
2861 return;
2862 }
2863 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2864 LocationSummary::kCallOnSlowPath,
2865 kIntrinsified);
2866 locations->SetInAt(0, Location::RequiresRegister());
2867 locations->SetOut(Location::SameAsFirstInput());
2868 locations->AddTemp(Location::RequiresRegister());
2869}
2870
2871void IntrinsicCodeGeneratorARM64::VisitReferenceGetReferent(HInvoke* invoke) {
2872 DCHECK(!kEmitCompilerReadBarrier);
2873 MacroAssembler* masm = GetVIXLAssembler();
2874 LocationSummary* locations = invoke->GetLocations();
2875
2876 Register obj = InputRegisterAt(invoke, 0);
2877 Register out = OutputRegister(invoke);
2878
2879 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
2880 codegen_->AddSlowPath(slow_path);
2881
2882 // Load ArtMethod first.
2883 HInvokeStaticOrDirect* invoke_direct = invoke->AsInvokeStaticOrDirect();
2884 DCHECK(invoke_direct != nullptr);
2885 Register temp0 = XRegisterFrom(codegen_->GenerateCalleeMethodStaticOrDirectCall(
2886 invoke_direct, locations->GetTemp(0)));
2887
2888 // Now get declaring class.
2889 __ Ldr(temp0.W(), MemOperand(temp0, ArtMethod::DeclaringClassOffset().Int32Value()));
2890
2891 uint32_t slow_path_flag_offset = codegen_->GetReferenceSlowFlagOffset();
2892 uint32_t disable_flag_offset = codegen_->GetReferenceDisableFlagOffset();
2893 DCHECK_NE(slow_path_flag_offset, 0u);
2894 DCHECK_NE(disable_flag_offset, 0u);
2895 DCHECK_NE(slow_path_flag_offset, disable_flag_offset);
2896
2897 // Check static flags that prevent using intrinsic.
2898 if (slow_path_flag_offset == disable_flag_offset + 1) {
2899 // Load two adjacent flags in one 64-bit load.
2900 __ Ldr(temp0, MemOperand(temp0, disable_flag_offset));
2901 } else {
2902 UseScratchRegisterScope temps(masm);
2903 Register temp1 = temps.AcquireW();
2904 __ Ldr(temp1.W(), MemOperand(temp0, disable_flag_offset));
2905 __ Ldr(temp0.W(), MemOperand(temp0, slow_path_flag_offset));
2906 __ Orr(temp0, temp1, temp0);
2907 }
2908 __ Cbnz(temp0, slow_path->GetEntryLabel());
2909
2910 // Fast path.
2911 __ Ldr(out, HeapOperand(obj, mirror::Reference::ReferentOffset().Int32Value()));
2912 codegen_->MaybeRecordImplicitNullCheck(invoke);
2913 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(out);
2914 __ Bind(slow_path->GetExitLabel());
2915}
2916
Aart Bik2f9fcc92016-03-01 15:16:54 -08002917UNIMPLEMENTED_INTRINSIC(ARM64, IntegerHighestOneBit)
2918UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit)
2919UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit)
2920UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit)
Andreas Gampe878d58c2015-01-15 23:24:00 -08002921
Aart Bikff7d89c2016-11-07 08:49:28 -08002922UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf);
2923UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08002924UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferAppend);
2925UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferLength);
2926UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferToString);
2927UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderAppend);
2928UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderLength);
2929UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08002930
Aart Bik0e54c012016-03-04 12:08:31 -08002931// 1.8.
2932UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt)
2933UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong)
2934UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt)
2935UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong)
2936UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08002937
Aart Bik2f9fcc92016-03-01 15:16:54 -08002938UNREACHABLE_INTRINSICS(ARM64)
Roland Levillain4d027112015-07-01 15:41:14 +01002939
2940#undef __
2941
Andreas Gampe878d58c2015-01-15 23:24:00 -08002942} // namespace arm64
2943} // namespace art