blob: 0b17c9d27e60152f56fe1f1c3c265a89b698d172 [file] [log] [blame]
Andreas Gampe878d58c2015-01-15 23:24:00 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "intrinsics_arm64.h"
18
Serban Constantinescu579885a2015-02-22 20:51:33 +000019#include "arch/arm64/instruction_set_features_arm64.h"
Mathieu Chartiere401d142015-04-22 13:56:20 -070020#include "art_method.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080021#include "code_generator_arm64.h"
22#include "common_arm64.h"
23#include "entrypoints/quick/quick_entrypoints.h"
Andreas Gampe09659c22017-09-18 18:23:32 -070024#include "heap_poisoning.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080025#include "intrinsics.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080026#include "lock_word.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080027#include "mirror/array-inl.h"
Andreas Gampec15a2f42017-04-21 12:09:39 -070028#include "mirror/object_array-inl.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080029#include "mirror/reference.h"
Vladimir Markoe39f14f2017-02-10 15:44:25 +000030#include "mirror/string-inl.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080031#include "scoped_thread_state_change-inl.h"
Andreas Gampeb486a982017-06-01 13:45:54 -070032#include "thread-current-inl.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080033#include "utils/arm64/assembler_arm64.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080034
Scott Wakeling97c72b72016-06-24 16:19:36 +010035using namespace vixl::aarch64; // NOLINT(build/namespaces)
Andreas Gampe878d58c2015-01-15 23:24:00 -080036
Artem Serovaf4e42a2016-08-08 15:11:24 +010037// TODO(VIXL): Make VIXL compile with -Wshadow.
Scott Wakeling97c72b72016-06-24 16:19:36 +010038#pragma GCC diagnostic push
39#pragma GCC diagnostic ignored "-Wshadow"
Artem Serovaf4e42a2016-08-08 15:11:24 +010040#include "aarch64/disasm-aarch64.h"
41#include "aarch64/macro-assembler-aarch64.h"
Scott Wakeling97c72b72016-06-24 16:19:36 +010042#pragma GCC diagnostic pop
Andreas Gampe878d58c2015-01-15 23:24:00 -080043
44namespace art {
45
46namespace arm64 {
47
48using helpers::DRegisterFrom;
49using helpers::FPRegisterFrom;
50using helpers::HeapOperand;
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +000051using helpers::LocationFrom;
Scott Wakeling9ee23f42015-07-23 10:44:35 +010052using helpers::OperandFrom;
Andreas Gampe878d58c2015-01-15 23:24:00 -080053using helpers::RegisterFrom;
54using helpers::SRegisterFrom;
55using helpers::WRegisterFrom;
56using helpers::XRegisterFrom;
xueliang.zhong49924c92016-03-03 10:52:51 +000057using helpers::InputRegisterAt;
Scott Wakeling1f36f412016-04-21 11:13:45 +010058using helpers::OutputRegister;
Andreas Gampe878d58c2015-01-15 23:24:00 -080059
Andreas Gampe878d58c2015-01-15 23:24:00 -080060namespace {
61
62ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) {
63 return MemOperand(XRegisterFrom(location), offset);
64}
65
66} // namespace
67
Scott Wakeling97c72b72016-06-24 16:19:36 +010068MacroAssembler* IntrinsicCodeGeneratorARM64::GetVIXLAssembler() {
Alexandre Rames087930f2016-08-02 13:45:28 +010069 return codegen_->GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -080070}
71
72ArenaAllocator* IntrinsicCodeGeneratorARM64::GetAllocator() {
Vladimir Markoca6fff82017-10-03 14:49:14 +010073 return codegen_->GetGraph()->GetAllocator();
Andreas Gampe878d58c2015-01-15 23:24:00 -080074}
75
Alexandre Rames087930f2016-08-02 13:45:28 +010076#define __ codegen->GetVIXLAssembler()->
Andreas Gampe878d58c2015-01-15 23:24:00 -080077
78static void MoveFromReturnRegister(Location trg,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010079 DataType::Type type,
Andreas Gampe878d58c2015-01-15 23:24:00 -080080 CodeGeneratorARM64* codegen) {
81 if (!trg.IsValid()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010082 DCHECK(type == DataType::Type::kVoid);
Andreas Gampe878d58c2015-01-15 23:24:00 -080083 return;
84 }
85
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010086 DCHECK_NE(type, DataType::Type::kVoid);
Andreas Gampe878d58c2015-01-15 23:24:00 -080087
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010088 if (DataType::IsIntegralType(type) || type == DataType::Type::kReference) {
Andreas Gampe878d58c2015-01-15 23:24:00 -080089 Register trg_reg = RegisterFrom(trg, type);
90 Register res_reg = RegisterFrom(ARM64ReturnLocation(type), type);
91 __ Mov(trg_reg, res_reg, kDiscardForSameWReg);
92 } else {
93 FPRegister trg_reg = FPRegisterFrom(trg, type);
94 FPRegister res_reg = FPRegisterFrom(ARM64ReturnLocation(type), type);
95 __ Fmov(trg_reg, res_reg);
96 }
97}
98
Roland Levillainec525fc2015-04-28 15:50:20 +010099static void MoveArguments(HInvoke* invoke, CodeGeneratorARM64* codegen) {
Roland Levillain2d27c8e2015-04-28 15:48:45 +0100100 InvokeDexCallingConventionVisitorARM64 calling_convention_visitor;
Roland Levillainec525fc2015-04-28 15:50:20 +0100101 IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800102}
103
104// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
105// call. This will copy the arguments into the positions for a regular call.
106//
107// Note: The actual parameters are required to be in the locations given by the invoke's location
108// summary. If an intrinsic modifies those locations before a slowpath call, they must be
109// restored!
110class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 {
111 public:
David Srbecky9cd6d372016-02-09 15:24:47 +0000112 explicit IntrinsicSlowPathARM64(HInvoke* invoke)
113 : SlowPathCodeARM64(invoke), invoke_(invoke) { }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800114
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100115 void EmitNativeCode(CodeGenerator* codegen_in) override {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800116 CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
117 __ Bind(GetEntryLabel());
118
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000119 SaveLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800120
Roland Levillainec525fc2015-04-28 15:50:20 +0100121 MoveArguments(invoke_, codegen);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800122
Artem Serov914d7a82017-02-07 14:33:49 +0000123 {
124 // Ensure that between the BLR (emitted by Generate*Call) and RecordPcInfo there
125 // are no pools emitted.
126 vixl::EmissionCheckScope guard(codegen->GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes);
127 if (invoke_->IsInvokeStaticOrDirect()) {
Vladimir Markoe7197bf2017-06-02 17:00:23 +0100128 codegen->GenerateStaticOrDirectCall(
129 invoke_->AsInvokeStaticOrDirect(), LocationFrom(kArtMethodRegister), this);
Artem Serov914d7a82017-02-07 14:33:49 +0000130 } else {
Vladimir Markoe7197bf2017-06-02 17:00:23 +0100131 codegen->GenerateVirtualCall(
132 invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister), this);
Artem Serov914d7a82017-02-07 14:33:49 +0000133 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800134 }
135
136 // Copy the result back to the expected output.
137 Location out = invoke_->GetLocations()->Out();
138 if (out.IsValid()) {
139 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
140 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
141 MoveFromReturnRegister(out, invoke_->GetType(), codegen);
142 }
143
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000144 RestoreLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800145 __ B(GetExitLabel());
146 }
147
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100148 const char* GetDescription() const override { return "IntrinsicSlowPathARM64"; }
Alexandre Rames9931f312015-06-19 14:47:01 +0100149
Andreas Gampe878d58c2015-01-15 23:24:00 -0800150 private:
151 // The instruction where this slow path is happening.
152 HInvoke* const invoke_;
153
154 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM64);
155};
156
Roland Levillain0b671c02016-08-19 12:02:34 +0100157// Slow path implementing the SystemArrayCopy intrinsic copy loop with read barriers.
158class ReadBarrierSystemArrayCopySlowPathARM64 : public SlowPathCodeARM64 {
159 public:
160 ReadBarrierSystemArrayCopySlowPathARM64(HInstruction* instruction, Location tmp)
161 : SlowPathCodeARM64(instruction), tmp_(tmp) {
162 DCHECK(kEmitCompilerReadBarrier);
163 DCHECK(kUseBakerReadBarrier);
164 }
165
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100166 void EmitNativeCode(CodeGenerator* codegen_in) override {
Roland Levillain0b671c02016-08-19 12:02:34 +0100167 CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
168 LocationSummary* locations = instruction_->GetLocations();
169 DCHECK(locations->CanCall());
170 DCHECK(instruction_->IsInvokeStaticOrDirect())
171 << "Unexpected instruction in read barrier arraycopy slow path: "
172 << instruction_->DebugName();
173 DCHECK(instruction_->GetLocations()->Intrinsified());
174 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kSystemArrayCopy);
175
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100176 const int32_t element_size = DataType::Size(DataType::Type::kReference);
Roland Levillain0b671c02016-08-19 12:02:34 +0100177
178 Register src_curr_addr = XRegisterFrom(locations->GetTemp(0));
179 Register dst_curr_addr = XRegisterFrom(locations->GetTemp(1));
180 Register src_stop_addr = XRegisterFrom(locations->GetTemp(2));
181 Register tmp_reg = WRegisterFrom(tmp_);
182
183 __ Bind(GetEntryLabel());
184 vixl::aarch64::Label slow_copy_loop;
185 __ Bind(&slow_copy_loop);
186 __ Ldr(tmp_reg, MemOperand(src_curr_addr, element_size, PostIndex));
187 codegen->GetAssembler()->MaybeUnpoisonHeapReference(tmp_reg);
188 // TODO: Inline the mark bit check before calling the runtime?
189 // tmp_reg = ReadBarrier::Mark(tmp_reg);
190 // No need to save live registers; it's taken care of by the
191 // entrypoint. Also, there is no need to update the stack mask,
192 // as this runtime call will not trigger a garbage collection.
193 // (See ReadBarrierMarkSlowPathARM64::EmitNativeCode for more
194 // explanations.)
195 DCHECK_NE(tmp_.reg(), LR);
196 DCHECK_NE(tmp_.reg(), WSP);
197 DCHECK_NE(tmp_.reg(), WZR);
198 // IP0 is used internally by the ReadBarrierMarkRegX entry point
199 // as a temporary (and not preserved). It thus cannot be used by
200 // any live register in this slow path.
201 DCHECK_NE(LocationFrom(src_curr_addr).reg(), IP0);
202 DCHECK_NE(LocationFrom(dst_curr_addr).reg(), IP0);
203 DCHECK_NE(LocationFrom(src_stop_addr).reg(), IP0);
204 DCHECK_NE(tmp_.reg(), IP0);
205 DCHECK(0 <= tmp_.reg() && tmp_.reg() < kNumberOfWRegisters) << tmp_.reg();
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000206 // TODO: Load the entrypoint once before the loop, instead of
207 // loading it at every iteration.
Roland Levillain0b671c02016-08-19 12:02:34 +0100208 int32_t entry_point_offset =
Roland Levillain97c46462017-05-11 14:04:03 +0100209 Thread::ReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(tmp_.reg());
Roland Levillain0b671c02016-08-19 12:02:34 +0100210 // This runtime call does not require a stack map.
211 codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
212 codegen->GetAssembler()->MaybePoisonHeapReference(tmp_reg);
213 __ Str(tmp_reg, MemOperand(dst_curr_addr, element_size, PostIndex));
214 __ Cmp(src_curr_addr, src_stop_addr);
215 __ B(&slow_copy_loop, ne);
216 __ B(GetExitLabel());
217 }
218
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100219 const char* GetDescription() const override { return "ReadBarrierSystemArrayCopySlowPathARM64"; }
Roland Levillain0b671c02016-08-19 12:02:34 +0100220
221 private:
222 Location tmp_;
223
224 DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathARM64);
225};
Andreas Gampe878d58c2015-01-15 23:24:00 -0800226#undef __
227
228bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) {
229 Dispatch(invoke);
230 LocationSummary* res = invoke->GetLocations();
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000231 if (res == nullptr) {
232 return false;
233 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000234 return res->Intrinsified();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800235}
236
237#define __ masm->
238
Vladimir Markoca6fff82017-10-03 14:49:14 +0100239static void CreateFPToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
240 LocationSummary* locations =
241 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800242 locations->SetInAt(0, Location::RequiresFpuRegister());
243 locations->SetOut(Location::RequiresRegister());
244}
245
Vladimir Markoca6fff82017-10-03 14:49:14 +0100246static void CreateIntToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
247 LocationSummary* locations =
248 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800249 locations->SetInAt(0, Location::RequiresRegister());
250 locations->SetOut(Location::RequiresFpuRegister());
251}
252
Scott Wakeling97c72b72016-06-24 16:19:36 +0100253static void MoveFPToInt(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800254 Location input = locations->InAt(0);
255 Location output = locations->Out();
256 __ Fmov(is64bit ? XRegisterFrom(output) : WRegisterFrom(output),
257 is64bit ? DRegisterFrom(input) : SRegisterFrom(input));
258}
259
Scott Wakeling97c72b72016-06-24 16:19:36 +0100260static void MoveIntToFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800261 Location input = locations->InAt(0);
262 Location output = locations->Out();
263 __ Fmov(is64bit ? DRegisterFrom(output) : SRegisterFrom(output),
264 is64bit ? XRegisterFrom(input) : WRegisterFrom(input));
265}
266
267void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100268 CreateFPToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800269}
270void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100271 CreateIntToFPLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800272}
273
274void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000275 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800276}
277void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000278 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800279}
280
281void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100282 CreateFPToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800283}
284void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100285 CreateIntToFPLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800286}
287
288void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000289 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800290}
291void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000292 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800293}
294
Vladimir Markoca6fff82017-10-03 14:49:14 +0100295static void CreateIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
296 LocationSummary* locations =
297 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800298 locations->SetInAt(0, Location::RequiresRegister());
299 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
300}
301
302static void GenReverseBytes(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100303 DataType::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100304 MacroAssembler* masm) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800305 Location in = locations->InAt(0);
306 Location out = locations->Out();
307
308 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100309 case DataType::Type::kInt16:
Andreas Gampe878d58c2015-01-15 23:24:00 -0800310 __ Rev16(WRegisterFrom(out), WRegisterFrom(in));
311 __ Sxth(WRegisterFrom(out), WRegisterFrom(out));
312 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100313 case DataType::Type::kInt32:
314 case DataType::Type::kInt64:
Andreas Gampe878d58c2015-01-15 23:24:00 -0800315 __ Rev(RegisterFrom(out, type), RegisterFrom(in, type));
316 break;
317 default:
318 LOG(FATAL) << "Unexpected size for reverse-bytes: " << type;
319 UNREACHABLE();
320 }
321}
322
323void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100324 CreateIntToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800325}
326
327void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100328 GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt32, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800329}
330
331void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100332 CreateIntToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800333}
334
335void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100336 GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt64, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800337}
338
339void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100340 CreateIntToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800341}
342
343void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100344 GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt16, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800345}
346
Scott Wakeling611d3392015-07-10 11:42:06 +0100347static void GenNumberOfLeadingZeros(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100348 DataType::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100349 MacroAssembler* masm) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100350 DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64);
Scott Wakeling611d3392015-07-10 11:42:06 +0100351
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) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100359 CreateIntToIntLocations(allocator_, invoke);
Scott Wakeling611d3392015-07-10 11:42:06 +0100360}
361
362void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100363 GenNumberOfLeadingZeros(invoke->GetLocations(), DataType::Type::kInt32, GetVIXLAssembler());
Scott Wakeling611d3392015-07-10 11:42:06 +0100364}
365
366void IntrinsicLocationsBuilderARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100367 CreateIntToIntLocations(allocator_, invoke);
Scott Wakeling611d3392015-07-10 11:42:06 +0100368}
369
370void IntrinsicCodeGeneratorARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100371 GenNumberOfLeadingZeros(invoke->GetLocations(), DataType::Type::kInt64, GetVIXLAssembler());
Scott Wakeling611d3392015-07-10 11:42:06 +0100372}
373
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100374static void GenNumberOfTrailingZeros(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100375 DataType::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100376 MacroAssembler* masm) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100377 DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64);
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100378
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) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100387 CreateIntToIntLocations(allocator_, invoke);
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100388}
389
390void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100391 GenNumberOfTrailingZeros(invoke->GetLocations(), DataType::Type::kInt32, GetVIXLAssembler());
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100392}
393
394void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100395 CreateIntToIntLocations(allocator_, invoke);
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100396}
397
398void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100399 GenNumberOfTrailingZeros(invoke->GetLocations(), DataType::Type::kInt64, GetVIXLAssembler());
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100400}
401
Andreas Gampe878d58c2015-01-15 23:24:00 -0800402static void GenReverse(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100403 DataType::Type type,
Scott Wakeling97c72b72016-06-24 16:19:36 +0100404 MacroAssembler* masm) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100405 DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800406
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) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100414 CreateIntToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800415}
416
417void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100418 GenReverse(invoke->GetLocations(), DataType::Type::kInt32, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800419}
420
421void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100422 CreateIntToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800423}
424
425void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100426 GenReverse(invoke->GetLocations(), DataType::Type::kInt64, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800427}
428
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100429static void GenBitCount(HInvoke* instr, DataType::Type type, MacroAssembler* masm) {
430 DCHECK(DataType::IsIntOrLongType(type)) << type;
431 DCHECK_EQ(instr->GetType(), DataType::Type::kInt32);
432 DCHECK_EQ(DataType::Kind(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);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100438 FPRegister fpr = (type == DataType::Type::kInt64) ? 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) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100447 CreateIntToIntLocations(allocator_, invoke);
xueliang.zhong49924c92016-03-03 10:52:51 +0000448}
449
450void IntrinsicCodeGeneratorARM64::VisitLongBitCount(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100451 GenBitCount(invoke, DataType::Type::kInt64, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000452}
453
454void IntrinsicLocationsBuilderARM64::VisitIntegerBitCount(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100455 CreateIntToIntLocations(allocator_, invoke);
xueliang.zhong49924c92016-03-03 10:52:51 +0000456}
457
458void IntrinsicCodeGeneratorARM64::VisitIntegerBitCount(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100459 GenBitCount(invoke, DataType::Type::kInt32, GetVIXLAssembler());
xueliang.zhong49924c92016-03-03 10:52:51 +0000460}
461
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100462static void GenHighestOneBit(HInvoke* invoke, DataType::Type type, MacroAssembler* masm) {
463 DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64);
Petre-Ionut Tudorda483162017-08-14 13:54:31 +0100464
465 UseScratchRegisterScope temps(masm);
466
467 Register src = InputRegisterAt(invoke, 0);
468 Register dst = RegisterFrom(invoke->GetLocations()->Out(), type);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100469 Register temp = (type == DataType::Type::kInt64) ? temps.AcquireX() : temps.AcquireW();
470 size_t high_bit = (type == DataType::Type::kInt64) ? 63u : 31u;
471 size_t clz_high_bit = (type == DataType::Type::kInt64) ? 6u : 5u;
Petre-Ionut Tudorda483162017-08-14 13:54:31 +0100472
473 __ Clz(temp, src);
474 __ Mov(dst, UINT64_C(1) << high_bit); // MOV (bitmask immediate)
475 __ Bic(dst, dst, Operand(temp, LSL, high_bit - clz_high_bit)); // Clear dst if src was 0.
476 __ Lsr(dst, dst, temp);
477}
478
479void IntrinsicLocationsBuilderARM64::VisitIntegerHighestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100480 CreateIntToIntLocations(allocator_, invoke);
Petre-Ionut Tudorda483162017-08-14 13:54:31 +0100481}
482
483void IntrinsicCodeGeneratorARM64::VisitIntegerHighestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100484 GenHighestOneBit(invoke, DataType::Type::kInt32, GetVIXLAssembler());
Petre-Ionut Tudorda483162017-08-14 13:54:31 +0100485}
486
487void IntrinsicLocationsBuilderARM64::VisitLongHighestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100488 CreateIntToIntLocations(allocator_, invoke);
Petre-Ionut Tudorda483162017-08-14 13:54:31 +0100489}
490
491void IntrinsicCodeGeneratorARM64::VisitLongHighestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100492 GenHighestOneBit(invoke, DataType::Type::kInt64, GetVIXLAssembler());
Petre-Ionut Tudorda483162017-08-14 13:54:31 +0100493}
494
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100495static void GenLowestOneBit(HInvoke* invoke, DataType::Type type, MacroAssembler* masm) {
496 DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64);
Petre-Ionut Tudorda483162017-08-14 13:54:31 +0100497
498 UseScratchRegisterScope temps(masm);
499
500 Register src = InputRegisterAt(invoke, 0);
501 Register dst = RegisterFrom(invoke->GetLocations()->Out(), type);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100502 Register temp = (type == DataType::Type::kInt64) ? temps.AcquireX() : temps.AcquireW();
Petre-Ionut Tudorda483162017-08-14 13:54:31 +0100503
504 __ Neg(temp, src);
505 __ And(dst, temp, src);
506}
507
508void IntrinsicLocationsBuilderARM64::VisitIntegerLowestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100509 CreateIntToIntLocations(allocator_, invoke);
Petre-Ionut Tudorda483162017-08-14 13:54:31 +0100510}
511
512void IntrinsicCodeGeneratorARM64::VisitIntegerLowestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100513 GenLowestOneBit(invoke, DataType::Type::kInt32, GetVIXLAssembler());
Petre-Ionut Tudorda483162017-08-14 13:54:31 +0100514}
515
516void IntrinsicLocationsBuilderARM64::VisitLongLowestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100517 CreateIntToIntLocations(allocator_, invoke);
Petre-Ionut Tudorda483162017-08-14 13:54:31 +0100518}
519
520void IntrinsicCodeGeneratorARM64::VisitLongLowestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100521 GenLowestOneBit(invoke, DataType::Type::kInt64, GetVIXLAssembler());
Petre-Ionut Tudorda483162017-08-14 13:54:31 +0100522}
523
Aart Bik3dad3412018-02-28 12:01:46 -0800524static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
525 LocationSummary* locations =
526 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
527 locations->SetInAt(0, Location::RequiresFpuRegister());
528 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
529}
530
Andreas Gampe878d58c2015-01-15 23:24:00 -0800531void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100532 CreateFPToFPLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800533}
534
535void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) {
536 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100537 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800538 __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
539}
540
541void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100542 CreateFPToFPLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800543}
544
545void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) {
546 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100547 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800548 __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
549}
550
551void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100552 CreateFPToFPLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800553}
554
555void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) {
556 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100557 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800558 __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
559}
560
561void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100562 CreateFPToFPLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800563}
564
565void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) {
566 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100567 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800568 __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
569}
570
Vladimir Markoca6fff82017-10-03 14:49:14 +0100571static void CreateFPToIntPlusFPTempLocations(ArenaAllocator* allocator, HInvoke* invoke) {
572 LocationSummary* locations =
573 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800574 locations->SetInAt(0, Location::RequiresFpuRegister());
575 locations->SetOut(Location::RequiresRegister());
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100576 locations->AddTemp(Location::RequiresFpuRegister());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800577}
578
Scott Wakeling97c72b72016-06-24 16:19:36 +0100579static void GenMathRound(HInvoke* invoke, bool is_double, vixl::aarch64::MacroAssembler* masm) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100580 // Java 8 API definition for Math.round():
581 // Return the closest long or int to the argument, with ties rounding to positive infinity.
582 //
583 // There is no single instruction in ARMv8 that can support the above definition.
584 // We choose to use FCVTAS here, because it has closest semantic.
585 // FCVTAS performs rounding to nearest integer, ties away from zero.
586 // For most inputs (positive values, zero or NaN), this instruction is enough.
587 // We only need a few handling code after FCVTAS if the input is negative half value.
588 //
589 // The reason why we didn't choose FCVTPS instruction here is that
590 // although it performs rounding toward positive infinity, it doesn't perform rounding to nearest.
591 // For example, FCVTPS(-1.9) = -1 and FCVTPS(1.1) = 2.
592 // If we were using this instruction, for most inputs, more handling code would be needed.
593 LocationSummary* l = invoke->GetLocations();
594 FPRegister in_reg = is_double ? DRegisterFrom(l->InAt(0)) : SRegisterFrom(l->InAt(0));
595 FPRegister tmp_fp = is_double ? DRegisterFrom(l->GetTemp(0)) : SRegisterFrom(l->GetTemp(0));
596 Register out_reg = is_double ? XRegisterFrom(l->Out()) : WRegisterFrom(l->Out());
Scott Wakeling97c72b72016-06-24 16:19:36 +0100597 vixl::aarch64::Label done;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800598
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100599 // Round to nearest integer, ties away from zero.
600 __ Fcvtas(out_reg, in_reg);
601
602 // For positive values, zero or NaN inputs, rounding is done.
Scott Wakeling97c72b72016-06-24 16:19:36 +0100603 __ Tbz(out_reg, out_reg.GetSizeInBits() - 1, &done);
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100604
605 // Handle input < 0 cases.
606 // If input is negative but not a tie, previous result (round to nearest) is valid.
607 // If input is a negative tie, out_reg += 1.
608 __ Frinta(tmp_fp, in_reg);
609 __ Fsub(tmp_fp, in_reg, tmp_fp);
610 __ Fcmp(tmp_fp, 0.5);
611 __ Cinc(out_reg, out_reg, eq);
612
613 __ Bind(&done);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800614}
615
616void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100617 CreateFPToIntPlusFPTempLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800618}
619
620void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100621 GenMathRound(invoke, /* is_double */ true, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800622}
623
624void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100625 CreateFPToIntPlusFPTempLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800626}
627
628void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) {
xueliang.zhongd1e153c2016-05-27 18:56:13 +0100629 GenMathRound(invoke, /* is_double */ false, GetVIXLAssembler());
Andreas Gampe878d58c2015-01-15 23:24:00 -0800630}
631
632void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100633 CreateIntToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800634}
635
636void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100637 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800638 __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()),
639 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
640}
641
642void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100643 CreateIntToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800644}
645
646void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100647 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800648 __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()),
649 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
650}
651
652void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100653 CreateIntToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800654}
655
656void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100657 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800658 __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()),
659 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
660}
661
662void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100663 CreateIntToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800664}
665
666void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100667 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800668 __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()),
669 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
670}
671
Vladimir Markoca6fff82017-10-03 14:49:14 +0100672static void CreateIntIntToVoidLocations(ArenaAllocator* allocator, HInvoke* invoke) {
673 LocationSummary* locations =
674 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800675 locations->SetInAt(0, Location::RequiresRegister());
676 locations->SetInAt(1, Location::RequiresRegister());
677}
678
679void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100680 CreateIntIntToVoidLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800681}
682
683void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100684 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800685 __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)),
686 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
687}
688
689void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100690 CreateIntIntToVoidLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800691}
692
693void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100694 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800695 __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)),
696 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
697}
698
699void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100700 CreateIntIntToVoidLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800701}
702
703void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100704 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800705 __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)),
706 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
707}
708
709void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100710 CreateIntIntToVoidLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800711}
712
713void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100714 MacroAssembler* masm = GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800715 __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)),
716 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
717}
718
719void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100720 LocationSummary* locations =
721 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800722 locations->SetOut(Location::RequiresRegister());
723}
724
725void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100726 codegen_->Load(DataType::Type::kReference, WRegisterFrom(invoke->GetLocations()->Out()),
Andreas Gampe542451c2016-07-26 09:02:02 -0700727 MemOperand(tr, Thread::PeerOffset<kArm64PointerSize>().Int32Value()));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800728}
729
730static void GenUnsafeGet(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100731 DataType::Type type,
Andreas Gampe878d58c2015-01-15 23:24:00 -0800732 bool is_volatile,
733 CodeGeneratorARM64* codegen) {
734 LocationSummary* locations = invoke->GetLocations();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100735 DCHECK((type == DataType::Type::kInt32) ||
736 (type == DataType::Type::kInt64) ||
737 (type == DataType::Type::kReference));
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000738 Location base_loc = locations->InAt(1);
739 Register base = WRegisterFrom(base_loc); // Object pointer.
740 Location offset_loc = locations->InAt(2);
741 Register offset = XRegisterFrom(offset_loc); // Long offset.
742 Location trg_loc = locations->Out();
743 Register trg = RegisterFrom(trg_loc, type);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800744
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100745 if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Roland Levillain44015862016-01-22 11:47:17 +0000746 // UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case.
Roland Levillain54f869e2017-03-06 13:54:11 +0000747 Register temp = WRegisterFrom(locations->GetTemp(0));
Vladimir Marko248141f2018-08-10 10:40:07 +0100748 MacroAssembler* masm = codegen->GetVIXLAssembler();
749 // Piggy-back on the field load path using introspection for the Baker read barrier.
750 __ Add(temp, base, offset.W()); // Offset should not exceed 32 bits.
751 codegen->GenerateFieldLoadWithBakerReadBarrier(invoke,
752 trg_loc,
753 base,
754 MemOperand(temp.X()),
755 /* needs_null_check */ false,
756 is_volatile);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800757 } else {
Roland Levillain44015862016-01-22 11:47:17 +0000758 // Other cases.
759 MemOperand mem_op(base.X(), offset);
760 if (is_volatile) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +0000761 codegen->LoadAcquire(invoke, trg, mem_op, /* needs_null_check */ true);
Roland Levillain44015862016-01-22 11:47:17 +0000762 } else {
763 codegen->Load(type, trg, mem_op);
764 }
Roland Levillain4d027112015-07-01 15:41:14 +0100765
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100766 if (type == DataType::Type::kReference) {
Roland Levillain44015862016-01-22 11:47:17 +0000767 DCHECK(trg.IsW());
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100768 codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0u, offset_loc);
Roland Levillain44015862016-01-22 11:47:17 +0000769 }
Roland Levillain4d027112015-07-01 15:41:14 +0100770 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800771}
772
Vladimir Markoca6fff82017-10-03 14:49:14 +0100773static void CreateIntIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +0000774 bool can_call = kEmitCompilerReadBarrier &&
775 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
776 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Vladimir Markoca6fff82017-10-03 14:49:14 +0100777 LocationSummary* locations =
778 new (allocator) LocationSummary(invoke,
779 can_call
780 ? LocationSummary::kCallOnSlowPath
781 : LocationSummary::kNoCall,
782 kIntrinsified);
Vladimir Marko70e97462016-08-09 11:04:26 +0100783 if (can_call && kUseBakerReadBarrier) {
Vladimir Marko804b03f2016-09-14 16:26:36 +0100784 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Vladimir Marko248141f2018-08-10 10:40:07 +0100785 // We need a temporary register for the read barrier load in order to use
786 // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier().
787 locations->AddTemp(FixedTempLocation());
Vladimir Marko70e97462016-08-09 11:04:26 +0100788 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800789 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
790 locations->SetInAt(1, Location::RequiresRegister());
791 locations->SetInAt(2, Location::RequiresRegister());
Roland Levillainbfea3352016-06-23 13:48:47 +0100792 locations->SetOut(Location::RequiresRegister(),
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100793 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800794}
795
796void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100797 CreateIntIntIntToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800798}
799void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100800 CreateIntIntIntToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800801}
802void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100803 CreateIntIntIntToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800804}
805void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100806 CreateIntIntIntToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800807}
808void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100809 CreateIntIntIntToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800810}
811void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100812 CreateIntIntIntToIntLocations(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800813}
814
815void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100816 GenUnsafeGet(invoke, DataType::Type::kInt32, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800817}
818void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100819 GenUnsafeGet(invoke, DataType::Type::kInt32, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800820}
821void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100822 GenUnsafeGet(invoke, DataType::Type::kInt64, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800823}
824void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100825 GenUnsafeGet(invoke, DataType::Type::kInt64, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800826}
827void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100828 GenUnsafeGet(invoke, DataType::Type::kReference, /* is_volatile */ false, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800829}
830void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100831 GenUnsafeGet(invoke, DataType::Type::kReference, /* is_volatile */ true, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800832}
833
Vladimir Markoca6fff82017-10-03 14:49:14 +0100834static void CreateIntIntIntIntToVoid(ArenaAllocator* allocator, HInvoke* invoke) {
835 LocationSummary* locations =
836 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800837 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
838 locations->SetInAt(1, Location::RequiresRegister());
839 locations->SetInAt(2, Location::RequiresRegister());
840 locations->SetInAt(3, Location::RequiresRegister());
841}
842
843void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100844 CreateIntIntIntIntToVoid(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800845}
846void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100847 CreateIntIntIntIntToVoid(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800848}
849void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100850 CreateIntIntIntIntToVoid(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800851}
852void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100853 CreateIntIntIntIntToVoid(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800854}
855void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100856 CreateIntIntIntIntToVoid(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800857}
858void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100859 CreateIntIntIntIntToVoid(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800860}
861void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100862 CreateIntIntIntIntToVoid(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800863}
864void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100865 CreateIntIntIntIntToVoid(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800866}
867void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100868 CreateIntIntIntIntToVoid(allocator_, invoke);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800869}
870
Artem Serov914d7a82017-02-07 14:33:49 +0000871static void GenUnsafePut(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100872 DataType::Type type,
Andreas Gampe878d58c2015-01-15 23:24:00 -0800873 bool is_volatile,
874 bool is_ordered,
875 CodeGeneratorARM64* codegen) {
Artem Serov914d7a82017-02-07 14:33:49 +0000876 LocationSummary* locations = invoke->GetLocations();
Alexandre Rames087930f2016-08-02 13:45:28 +0100877 MacroAssembler* masm = codegen->GetVIXLAssembler();
Andreas Gampe878d58c2015-01-15 23:24:00 -0800878
879 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
880 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
881 Register value = RegisterFrom(locations->InAt(3), type);
Roland Levillain4d027112015-07-01 15:41:14 +0100882 Register source = value;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800883 MemOperand mem_op(base.X(), offset);
884
Roland Levillain4d027112015-07-01 15:41:14 +0100885 {
886 // We use a block to end the scratch scope before the write barrier, thus
887 // freeing the temporary registers so they can be used in `MarkGCCard`.
888 UseScratchRegisterScope temps(masm);
889
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100890 if (kPoisonHeapReferences && type == DataType::Type::kReference) {
Roland Levillain4d027112015-07-01 15:41:14 +0100891 DCHECK(value.IsW());
892 Register temp = temps.AcquireW();
893 __ Mov(temp.W(), value.W());
894 codegen->GetAssembler()->PoisonHeapReference(temp.W());
895 source = temp;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800896 }
Roland Levillain4d027112015-07-01 15:41:14 +0100897
898 if (is_volatile || is_ordered) {
Artem Serov914d7a82017-02-07 14:33:49 +0000899 codegen->StoreRelease(invoke, type, source, mem_op, /* needs_null_check */ false);
Roland Levillain4d027112015-07-01 15:41:14 +0100900 } else {
901 codegen->Store(type, source, mem_op);
902 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800903 }
904
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100905 if (type == DataType::Type::kReference) {
Nicolas Geoffray07276db2015-05-18 14:22:09 +0100906 bool value_can_be_null = true; // TODO: Worth finding out this information?
907 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800908 }
909}
910
911void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +0000912 GenUnsafePut(invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100913 DataType::Type::kInt32,
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000914 /* is_volatile */ false,
915 /* is_ordered */ false,
916 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800917}
918void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +0000919 GenUnsafePut(invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100920 DataType::Type::kInt32,
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000921 /* is_volatile */ false,
922 /* is_ordered */ true,
923 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800924}
925void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +0000926 GenUnsafePut(invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100927 DataType::Type::kInt32,
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000928 /* is_volatile */ true,
929 /* is_ordered */ false,
930 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800931}
932void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +0000933 GenUnsafePut(invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100934 DataType::Type::kReference,
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000935 /* is_volatile */ false,
936 /* is_ordered */ false,
937 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800938}
939void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +0000940 GenUnsafePut(invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100941 DataType::Type::kReference,
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000942 /* is_volatile */ false,
943 /* is_ordered */ true,
944 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800945}
946void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +0000947 GenUnsafePut(invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100948 DataType::Type::kReference,
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000949 /* is_volatile */ true,
950 /* is_ordered */ false,
951 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800952}
953void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +0000954 GenUnsafePut(invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100955 DataType::Type::kInt64,
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000956 /* is_volatile */ false,
957 /* is_ordered */ false,
958 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800959}
960void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +0000961 GenUnsafePut(invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100962 DataType::Type::kInt64,
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000963 /* is_volatile */ false,
964 /* is_ordered */ true,
965 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800966}
967void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Artem Serov914d7a82017-02-07 14:33:49 +0000968 GenUnsafePut(invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100969 DataType::Type::kInt64,
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000970 /* is_volatile */ true,
971 /* is_ordered */ false,
972 codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -0800973}
974
Vladimir Markoca6fff82017-10-03 14:49:14 +0100975static void CreateIntIntIntIntIntToInt(ArenaAllocator* allocator,
Roland Levillain2e50ecb2016-01-27 14:08:33 +0000976 HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100977 DataType::Type type) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100978 bool can_call = kEmitCompilerReadBarrier &&
979 kUseBakerReadBarrier &&
980 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
Vladimir Markoca6fff82017-10-03 14:49:14 +0100981 LocationSummary* locations =
982 new (allocator) LocationSummary(invoke,
983 can_call
984 ? LocationSummary::kCallOnSlowPath
985 : LocationSummary::kNoCall,
986 kIntrinsified);
Vladimir Marko94796f82018-08-08 15:15:33 +0100987 if (can_call) {
988 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
989 }
Andreas Gampe878d58c2015-01-15 23:24:00 -0800990 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
991 locations->SetInAt(1, Location::RequiresRegister());
992 locations->SetInAt(2, Location::RequiresRegister());
993 locations->SetInAt(3, Location::RequiresRegister());
994 locations->SetInAt(4, Location::RequiresRegister());
995
Vladimir Marko94796f82018-08-08 15:15:33 +0100996 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100997 if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Vladimir Marko94796f82018-08-08 15:15:33 +0100998 // We need two non-scratch temporary registers for (Baker) read barrier.
999 locations->AddTemp(Location::RequiresRegister());
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001000 locations->AddTemp(Location::RequiresRegister());
1001 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001002}
1003
Vladimir Marko94796f82018-08-08 15:15:33 +01001004class BakerReadBarrierCasSlowPathARM64 : public SlowPathCodeARM64 {
1005 public:
1006 explicit BakerReadBarrierCasSlowPathARM64(HInvoke* invoke)
1007 : SlowPathCodeARM64(invoke) {}
1008
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001009 const char* GetDescription() const override { return "BakerReadBarrierCasSlowPathARM64"; }
Vladimir Marko94796f82018-08-08 15:15:33 +01001010
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001011 void EmitNativeCode(CodeGenerator* codegen) override {
Vladimir Marko94796f82018-08-08 15:15:33 +01001012 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
1013 Arm64Assembler* assembler = arm64_codegen->GetAssembler();
1014 MacroAssembler* masm = assembler->GetVIXLAssembler();
1015 __ Bind(GetEntryLabel());
1016
1017 // Get the locations.
1018 LocationSummary* locations = instruction_->GetLocations();
1019 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
1020 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
1021 Register expected = WRegisterFrom(locations->InAt(3)); // Expected.
1022 Register value = WRegisterFrom(locations->InAt(4)); // Value.
1023
1024 Register old_value = WRegisterFrom(locations->GetTemp(0)); // The old value from main path.
1025 Register marked = WRegisterFrom(locations->GetTemp(1)); // The marked old value.
1026
1027 // Mark the `old_value` from the main path and compare with `expected`. This clobbers the
1028 // `tmp_ptr` scratch register but we do not want to allocate another non-scratch temporary.
1029 arm64_codegen->GenerateUnsafeCasOldValueMovWithBakerReadBarrier(marked, old_value);
1030 __ Cmp(marked, expected);
1031 __ B(GetExitLabel(), ne); // If taken, Z=false indicates failure.
1032
1033 // The `old_value` we have read did not match `expected` (which is always a to-space reference)
1034 // but after the read barrier in GenerateUnsafeCasOldValueMovWithBakerReadBarrier() the marked
1035 // to-space value matched, so the `old_value` must be a from-space reference to the same
1036 // object. Do the same CAS loop as the main path but check for both `expected` and the unmarked
1037 // old value representing the to-space and from-space references for the same object.
1038
1039 UseScratchRegisterScope temps(masm);
1040 Register tmp_ptr = temps.AcquireX();
1041 Register tmp = temps.AcquireSameSizeAs(value);
1042
1043 // Recalculate the `tmp_ptr` clobbered above.
1044 __ Add(tmp_ptr, base.X(), Operand(offset));
1045
1046 // do {
1047 // tmp_value = [tmp_ptr];
1048 // } while ((tmp_value == expected || tmp == old_value) && failure([tmp_ptr] <- r_new_value));
1049 // result = (tmp_value == expected || tmp == old_value);
1050
1051 vixl::aarch64::Label loop_head;
1052 __ Bind(&loop_head);
1053 __ Ldaxr(tmp, MemOperand(tmp_ptr));
1054 assembler->MaybeUnpoisonHeapReference(tmp);
1055 __ Cmp(tmp, expected);
1056 __ Ccmp(tmp, old_value, ZFlag, ne);
1057 __ B(GetExitLabel(), ne); // If taken, Z=false indicates failure.
1058 assembler->MaybePoisonHeapReference(value);
1059 __ Stlxr(tmp.W(), value, MemOperand(tmp_ptr));
1060 assembler->MaybeUnpoisonHeapReference(value);
1061 __ Cbnz(tmp.W(), &loop_head);
1062
1063 // Z=true from the above CMP+CCMP indicates success.
1064 __ B(GetExitLabel());
1065 }
1066};
1067
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001068static void GenCas(HInvoke* invoke, DataType::Type type, CodeGeneratorARM64* codegen) {
Vladimir Marko94796f82018-08-08 15:15:33 +01001069 Arm64Assembler* assembler = codegen->GetAssembler();
1070 MacroAssembler* masm = assembler->GetVIXLAssembler();
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001071 LocationSummary* locations = invoke->GetLocations();
Andreas Gampe878d58c2015-01-15 23:24:00 -08001072
Vladimir Marko94796f82018-08-08 15:15:33 +01001073 Register out = WRegisterFrom(locations->Out()); // Boolean result.
1074 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
1075 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
1076 Register expected = RegisterFrom(locations->InAt(3), type); // Expected.
1077 Register value = RegisterFrom(locations->InAt(4), type); // Value.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001078
1079 // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001080 if (type == DataType::Type::kReference) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08001081 // Mark card for object assuming new value is stored.
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001082 bool value_can_be_null = true; // TODO: Worth finding out this information?
1083 codegen->MarkGCCard(base, value, value_can_be_null);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001084 }
1085
1086 UseScratchRegisterScope temps(masm);
1087 Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
Vladimir Marko94796f82018-08-08 15:15:33 +01001088 Register old_value; // Value in memory.
Andreas Gampe878d58c2015-01-15 23:24:00 -08001089
Vladimir Marko94796f82018-08-08 15:15:33 +01001090 vixl::aarch64::Label exit_loop_label;
1091 vixl::aarch64::Label* exit_loop = &exit_loop_label;
1092 vixl::aarch64::Label* failure = &exit_loop_label;
1093
1094 if (kEmitCompilerReadBarrier && type == DataType::Type::kReference) {
1095 // The only read barrier implementation supporting the
1096 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1097 DCHECK(kUseBakerReadBarrier);
1098
1099 BakerReadBarrierCasSlowPathARM64* slow_path =
1100 new (codegen->GetScopedAllocator()) BakerReadBarrierCasSlowPathARM64(invoke);
1101 codegen->AddSlowPath(slow_path);
1102 exit_loop = slow_path->GetExitLabel();
1103 failure = slow_path->GetEntryLabel();
1104 // We need to store the `old_value` in a non-scratch register to make sure
1105 // the Baker read barrier in the slow path does not clobber it.
1106 old_value = WRegisterFrom(locations->GetTemp(0));
1107 } else {
1108 old_value = temps.AcquireSameSizeAs(value);
1109 }
Andreas Gampe878d58c2015-01-15 23:24:00 -08001110
1111 __ Add(tmp_ptr, base.X(), Operand(offset));
1112
1113 // do {
Vladimir Marko94796f82018-08-08 15:15:33 +01001114 // tmp_value = [tmp_ptr];
1115 // } while (tmp_value == expected && failure([tmp_ptr] <- r_new_value));
1116 // result = tmp_value == expected;
Andreas Gampe878d58c2015-01-15 23:24:00 -08001117
Vladimir Marko94796f82018-08-08 15:15:33 +01001118 vixl::aarch64::Label loop_head;
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00001119 __ Bind(&loop_head);
Vladimir Marko94796f82018-08-08 15:15:33 +01001120 __ Ldaxr(old_value, MemOperand(tmp_ptr));
1121 if (type == DataType::Type::kReference) {
1122 assembler->MaybeUnpoisonHeapReference(old_value);
Roland Levillain4d027112015-07-01 15:41:14 +01001123 }
Vladimir Marko94796f82018-08-08 15:15:33 +01001124 __ Cmp(old_value, expected);
1125 __ B(failure, ne);
1126 if (type == DataType::Type::kReference) {
1127 assembler->MaybePoisonHeapReference(value);
1128 }
1129 __ Stlxr(old_value.W(), value, MemOperand(tmp_ptr)); // Reuse `old_value` for STLXR result.
1130 if (type == DataType::Type::kReference) {
1131 assembler->MaybeUnpoisonHeapReference(value);
1132 }
1133 __ Cbnz(old_value.W(), &loop_head);
1134 __ Bind(exit_loop);
1135 __ Cset(out, eq);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001136}
1137
1138void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001139 CreateIntIntIntIntIntToInt(allocator_, invoke, DataType::Type::kInt32);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001140}
1141void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001142 CreateIntIntIntIntIntToInt(allocator_, invoke, DataType::Type::kInt64);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001143}
1144void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001145 // The only read barrier implementation supporting the
1146 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1147 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain985ff702015-10-23 13:25:35 +01001148 return;
1149 }
1150
Vladimir Markoca6fff82017-10-03 14:49:14 +01001151 CreateIntIntIntIntIntToInt(allocator_, invoke, DataType::Type::kReference);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001152}
1153
1154void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001155 GenCas(invoke, DataType::Type::kInt32, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001156}
1157void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001158 GenCas(invoke, DataType::Type::kInt64, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001159}
1160void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001161 // The only read barrier implementation supporting the
1162 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1163 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01001164
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001165 GenCas(invoke, DataType::Type::kReference, codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001166}
1167
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001168void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001169 LocationSummary* locations =
1170 new (allocator_) LocationSummary(invoke,
1171 invoke->InputAt(1)->CanBeNull()
1172 ? LocationSummary::kCallOnSlowPath
1173 : LocationSummary::kNoCall,
1174 kIntrinsified);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001175 locations->SetInAt(0, Location::RequiresRegister());
1176 locations->SetInAt(1, Location::RequiresRegister());
1177 locations->AddTemp(Location::RequiresRegister());
1178 locations->AddTemp(Location::RequiresRegister());
1179 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001180 // Need temporary registers for String compression's feature.
1181 if (mirror::kUseStringCompression) {
1182 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001183 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001184 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001185}
1186
1187void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001188 MacroAssembler* masm = GetVIXLAssembler();
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001189 LocationSummary* locations = invoke->GetLocations();
1190
Alexandre Rames2ea91532016-08-11 17:04:14 +01001191 Register str = InputRegisterAt(invoke, 0);
1192 Register arg = InputRegisterAt(invoke, 1);
1193 DCHECK(str.IsW());
1194 DCHECK(arg.IsW());
Scott Wakeling1f36f412016-04-21 11:13:45 +01001195 Register out = OutputRegister(invoke);
1196
1197 Register temp0 = WRegisterFrom(locations->GetTemp(0));
1198 Register temp1 = WRegisterFrom(locations->GetTemp(1));
1199 Register temp2 = WRegisterFrom(locations->GetTemp(2));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001200 Register temp3;
jessicahandojo05765752016-09-09 19:01:32 -07001201 if (mirror::kUseStringCompression) {
1202 temp3 = WRegisterFrom(locations->GetTemp(3));
jessicahandojo05765752016-09-09 19:01:32 -07001203 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001204
Scott Wakeling97c72b72016-06-24 16:19:36 +01001205 vixl::aarch64::Label loop;
1206 vixl::aarch64::Label find_char_diff;
1207 vixl::aarch64::Label end;
jessicahandojo05765752016-09-09 19:01:32 -07001208 vixl::aarch64::Label different_compression;
Scott Wakeling1f36f412016-04-21 11:13:45 +01001209
1210 // Get offsets of count and value fields within a string object.
1211 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1212 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1213
Nicolas Geoffray512e04d2015-03-27 17:21:24 +00001214 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +01001215 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001216
Scott Wakeling1f36f412016-04-21 11:13:45 +01001217 // Take slow path and throw if input can be and is null.
1218 SlowPathCodeARM64* slow_path = nullptr;
1219 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1220 if (can_slow_path) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001221 slow_path = new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARM64(invoke);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001222 codegen_->AddSlowPath(slow_path);
1223 __ Cbz(arg, slow_path->GetEntryLabel());
1224 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001225
Scott Wakeling1f36f412016-04-21 11:13:45 +01001226 // Reference equality check, return 0 if same reference.
1227 __ Subs(out, str, arg);
1228 __ B(&end, eq);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001229
jessicahandojo05765752016-09-09 19:01:32 -07001230 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001231 // Load `count` fields of this and argument strings.
jessicahandojo05765752016-09-09 19:01:32 -07001232 __ Ldr(temp3, HeapOperand(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001233 __ Ldr(temp2, HeapOperand(arg, count_offset));
jessicahandojo05765752016-09-09 19:01:32 -07001234 // Clean out compression flag from lengths.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001235 __ Lsr(temp0, temp3, 1u);
1236 __ Lsr(temp1, temp2, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001237 } else {
1238 // Load lengths of this and argument strings.
1239 __ Ldr(temp0, HeapOperand(str, count_offset));
1240 __ Ldr(temp1, HeapOperand(arg, count_offset));
1241 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001242 // out = length diff.
1243 __ Subs(out, temp0, temp1);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001244 // temp0 = min(len(str), len(arg)).
1245 __ Csel(temp0, temp1, temp0, ge);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001246 // Shorter string is empty?
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001247 __ Cbz(temp0, &end);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001248
jessicahandojo05765752016-09-09 19:01:32 -07001249 if (mirror::kUseStringCompression) {
1250 // Check if both strings using same compression style to use this comparison loop.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001251 __ Eor(temp2, temp2, Operand(temp3));
1252 // Interleave with compression flag extraction which is needed for both paths
1253 // and also set flags which is needed only for the different compressions path.
1254 __ Ands(temp3.W(), temp3.W(), Operand(1));
1255 __ Tbnz(temp2, 0, &different_compression); // Does not use flags.
jessicahandojo05765752016-09-09 19:01:32 -07001256 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001257 // Store offset of string value in preparation for comparison loop.
1258 __ Mov(temp1, value_offset);
jessicahandojo05765752016-09-09 19:01:32 -07001259 if (mirror::kUseStringCompression) {
1260 // For string compression, calculate the number of bytes to compare (not chars).
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001261 // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
1262 __ Lsl(temp0, temp0, temp3);
jessicahandojo05765752016-09-09 19:01:32 -07001263 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001264
1265 UseScratchRegisterScope scratch_scope(masm);
1266 Register temp4 = scratch_scope.AcquireX();
1267
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001268 // Assertions that must hold in order to compare strings 8 bytes at a time.
Scott Wakeling1f36f412016-04-21 11:13:45 +01001269 DCHECK_ALIGNED(value_offset, 8);
1270 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1271
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001272 const size_t char_size = DataType::Size(DataType::Type::kUint16);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001273 DCHECK_EQ(char_size, 2u);
1274
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001275 // Promote temp2 to an X reg, ready for LDR.
1276 temp2 = temp2.X();
Scott Wakeling1f36f412016-04-21 11:13:45 +01001277
1278 // Loop to compare 4x16-bit characters at a time (ok because of string data alignment).
1279 __ Bind(&loop);
Alexandre Rames2ea91532016-08-11 17:04:14 +01001280 __ Ldr(temp4, MemOperand(str.X(), temp1.X()));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001281 __ Ldr(temp2, MemOperand(arg.X(), temp1.X()));
1282 __ Cmp(temp4, temp2);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001283 __ B(ne, &find_char_diff);
1284 __ Add(temp1, temp1, char_size * 4);
jessicahandojo05765752016-09-09 19:01:32 -07001285 // With string compression, we have compared 8 bytes, otherwise 4 chars.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001286 __ Subs(temp0, temp0, (mirror::kUseStringCompression) ? 8 : 4);
1287 __ B(&loop, hi);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001288 __ B(&end);
1289
1290 // Promote temp1 to an X reg, ready for EOR.
1291 temp1 = temp1.X();
1292
jessicahandojo05765752016-09-09 19:01:32 -07001293 // Find the single character difference.
Scott Wakeling1f36f412016-04-21 11:13:45 +01001294 __ Bind(&find_char_diff);
1295 // Get the bit position of the first character that differs.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001296 __ Eor(temp1, temp2, temp4);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001297 __ Rbit(temp1, temp1);
1298 __ Clz(temp1, temp1);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001299
jessicahandojo05765752016-09-09 19:01:32 -07001300 // If the number of chars remaining <= the index where the difference occurs (0-3), then
Scott Wakeling1f36f412016-04-21 11:13:45 +01001301 // the difference occurs outside the remaining string data, so just return length diff (out).
jessicahandojo05765752016-09-09 19:01:32 -07001302 // Unlike ARM, we're doing the comparison in one go here, without the subtraction at the
1303 // find_char_diff_2nd_cmp path, so it doesn't matter whether the comparison is signed or
1304 // unsigned when string compression is disabled.
1305 // When it's enabled, the comparison must be unsigned.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001306 __ Cmp(temp0, Operand(temp1.W(), LSR, (mirror::kUseStringCompression) ? 3 : 4));
jessicahandojo05765752016-09-09 19:01:32 -07001307 __ B(ls, &end);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001308
Scott Wakeling1f36f412016-04-21 11:13:45 +01001309 // Extract the characters and calculate the difference.
jessicahandojo05765752016-09-09 19:01:32 -07001310 if (mirror:: kUseStringCompression) {
jessicahandojo05765752016-09-09 19:01:32 -07001311 __ Bic(temp1, temp1, 0x7);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001312 __ Bic(temp1, temp1, Operand(temp3.X(), LSL, 3u));
1313 } else {
1314 __ Bic(temp1, temp1, 0xf);
jessicahandojo05765752016-09-09 19:01:32 -07001315 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001316 __ Lsr(temp2, temp2, temp1);
Scott Wakeling1f36f412016-04-21 11:13:45 +01001317 __ Lsr(temp4, temp4, temp1);
jessicahandojo05765752016-09-09 19:01:32 -07001318 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001319 // Prioritize the case of compressed strings and calculate such result first.
1320 __ Uxtb(temp1, temp4);
1321 __ Sub(out, temp1.W(), Operand(temp2.W(), UXTB));
1322 __ Tbz(temp3, 0u, &end); // If actually compressed, we're done.
jessicahandojo05765752016-09-09 19:01:32 -07001323 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001324 __ Uxth(temp4, temp4);
1325 __ Sub(out, temp4.W(), Operand(temp2.W(), UXTH));
jessicahandojo05765752016-09-09 19:01:32 -07001326
1327 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001328 __ B(&end);
1329 __ Bind(&different_compression);
1330
1331 // Comparison for different compression style.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001332 const size_t c_char_size = DataType::Size(DataType::Type::kInt8);
jessicahandojo05765752016-09-09 19:01:32 -07001333 DCHECK_EQ(c_char_size, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001334 temp1 = temp1.W();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001335 temp2 = temp2.W();
1336 temp4 = temp4.W();
jessicahandojo05765752016-09-09 19:01:32 -07001337
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001338 // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
1339 // Note that flags have been set by the `str` compression flag extraction to `temp3`
1340 // before branching to the `different_compression` label.
1341 __ Csel(temp1, str, arg, eq); // Pointer to the compressed string.
1342 __ Csel(temp2, str, arg, ne); // Pointer to the uncompressed string.
jessicahandojo05765752016-09-09 19:01:32 -07001343
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001344 // We want to free up the temp3, currently holding `str` compression flag, for comparison.
1345 // So, we move it to the bottom bit of the iteration count `temp0` which we then need to treat
1346 // as unsigned. Start by freeing the bit with a LSL and continue further down by a SUB which
1347 // will allow `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
1348 __ Lsl(temp0, temp0, 1u);
1349
1350 // Adjust temp1 and temp2 from string pointers to data pointers.
1351 __ Add(temp1, temp1, Operand(value_offset));
1352 __ Add(temp2, temp2, Operand(value_offset));
1353
1354 // Complete the move of the compression flag.
1355 __ Sub(temp0, temp0, Operand(temp3));
1356
1357 vixl::aarch64::Label different_compression_loop;
1358 vixl::aarch64::Label different_compression_diff;
1359
1360 __ Bind(&different_compression_loop);
1361 __ Ldrb(temp4, MemOperand(temp1.X(), c_char_size, PostIndex));
1362 __ Ldrh(temp3, MemOperand(temp2.X(), char_size, PostIndex));
1363 __ Subs(temp4, temp4, Operand(temp3));
1364 __ B(&different_compression_diff, ne);
1365 __ Subs(temp0, temp0, 2);
1366 __ B(&different_compression_loop, hi);
jessicahandojo05765752016-09-09 19:01:32 -07001367 __ B(&end);
1368
1369 // Calculate the difference.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001370 __ Bind(&different_compression_diff);
1371 __ Tst(temp0, Operand(1));
1372 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1373 "Expecting 0=compressed, 1=uncompressed");
1374 __ Cneg(out, temp4, ne);
jessicahandojo05765752016-09-09 19:01:32 -07001375 }
Scott Wakeling1f36f412016-04-21 11:13:45 +01001376
1377 __ Bind(&end);
1378
1379 if (can_slow_path) {
1380 __ Bind(slow_path->GetExitLabel());
1381 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001382}
1383
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001384// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
1385// The normal loop plus the pre-header is 9 instructions without string compression and 12
1386// instructions with string compression. We can compare up to 8 bytes in 4 instructions
1387// (LDR+LDR+CMP+BNE) and up to 16 bytes in 5 instructions (LDP+LDP+CMP+CCMP+BNE). Allow up
1388// to 10 instructions for the unrolled loop.
1389constexpr size_t kShortConstStringEqualsCutoffInBytes = 32;
1390
1391static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
1392 if (candidate->IsLoadString()) {
1393 HLoadString* load_string = candidate->AsLoadString();
1394 const DexFile& dex_file = load_string->GetDexFile();
1395 return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
1396 }
1397 return nullptr;
1398}
1399
Agi Csakiea34b402015-08-13 17:51:19 -07001400void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001401 LocationSummary* locations =
1402 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Agi Csakiea34b402015-08-13 17:51:19 -07001403 locations->SetInAt(0, Location::RequiresRegister());
1404 locations->SetInAt(1, Location::RequiresRegister());
Agi Csakiea34b402015-08-13 17:51:19 -07001405
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001406 // For the generic implementation and for long const strings we need a temporary.
1407 // We do not need it for short const strings, up to 8 bytes, see code generation below.
1408 uint32_t const_string_length = 0u;
1409 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1410 if (const_string == nullptr) {
1411 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1412 }
1413 bool is_compressed =
1414 mirror::kUseStringCompression &&
1415 const_string != nullptr &&
1416 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1417 if (const_string == nullptr || const_string_length > (is_compressed ? 8u : 4u)) {
1418 locations->AddTemp(Location::RequiresRegister());
1419 }
1420
1421 // TODO: If the String.equals() is used only for an immediately following HIf, we can
1422 // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
1423 // Then we shall need an extra temporary register instead of the output register.
Agi Csakiea34b402015-08-13 17:51:19 -07001424 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1425}
1426
1427void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001428 MacroAssembler* masm = GetVIXLAssembler();
Agi Csakiea34b402015-08-13 17:51:19 -07001429 LocationSummary* locations = invoke->GetLocations();
1430
1431 Register str = WRegisterFrom(locations->InAt(0));
1432 Register arg = WRegisterFrom(locations->InAt(1));
1433 Register out = XRegisterFrom(locations->Out());
1434
1435 UseScratchRegisterScope scratch_scope(masm);
1436 Register temp = scratch_scope.AcquireW();
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001437 Register temp1 = scratch_scope.AcquireW();
Agi Csakiea34b402015-08-13 17:51:19 -07001438
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001439 vixl::aarch64::Label loop;
Scott Wakeling97c72b72016-06-24 16:19:36 +01001440 vixl::aarch64::Label end;
1441 vixl::aarch64::Label return_true;
1442 vixl::aarch64::Label return_false;
Agi Csakiea34b402015-08-13 17:51:19 -07001443
1444 // Get offsets of count, value, and class fields within a string object.
1445 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1446 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1447 const int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1448
1449 // Note that the null check must have been done earlier.
1450 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1451
Vladimir Marko53b52002016-05-24 19:30:45 +01001452 StringEqualsOptimizations optimizations(invoke);
1453 if (!optimizations.GetArgumentNotNull()) {
1454 // Check if input is null, return false if it is.
1455 __ Cbz(arg, &return_false);
1456 }
Agi Csakiea34b402015-08-13 17:51:19 -07001457
1458 // Reference equality check, return true if same reference.
1459 __ Cmp(str, arg);
1460 __ B(&return_true, eq);
1461
Vladimir Marko53b52002016-05-24 19:30:45 +01001462 if (!optimizations.GetArgumentIsString()) {
1463 // Instanceof check for the argument by comparing class fields.
1464 // All string objects must have the same type since String cannot be subclassed.
1465 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1466 // If the argument is a string object, its class field must be equal to receiver's class field.
1467 __ Ldr(temp, MemOperand(str.X(), class_offset));
1468 __ Ldr(temp1, MemOperand(arg.X(), class_offset));
1469 __ Cmp(temp, temp1);
1470 __ B(&return_false, ne);
1471 }
Agi Csakiea34b402015-08-13 17:51:19 -07001472
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001473 // Check if one of the inputs is a const string. Do not special-case both strings
1474 // being const, such cases should be handled by constant folding if needed.
1475 uint32_t const_string_length = 0u;
1476 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1477 if (const_string == nullptr) {
1478 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1479 if (const_string != nullptr) {
1480 std::swap(str, arg); // Make sure the const string is in `str`.
1481 }
1482 }
1483 bool is_compressed =
1484 mirror::kUseStringCompression &&
1485 const_string != nullptr &&
1486 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1487
1488 if (const_string != nullptr) {
1489 // Load `count` field of the argument string and check if it matches the const string.
1490 // Also compares the compression style, if differs return false.
1491 __ Ldr(temp, MemOperand(arg.X(), count_offset));
Vladimir Marko26ec3ca2017-03-14 13:37:14 +00001492 // Temporarily release temp1 as we may not be able to embed the flagged count in CMP immediate.
1493 scratch_scope.Release(temp1);
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001494 __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
Vladimir Marko26ec3ca2017-03-14 13:37:14 +00001495 temp1 = scratch_scope.AcquireW();
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001496 __ B(&return_false, ne);
1497 } else {
1498 // Load `count` fields of this and argument strings.
1499 __ Ldr(temp, MemOperand(str.X(), count_offset));
1500 __ Ldr(temp1, MemOperand(arg.X(), count_offset));
1501 // Check if `count` fields are equal, return false if they're not.
1502 // Also compares the compression style, if differs return false.
1503 __ Cmp(temp, temp1);
1504 __ B(&return_false, ne);
1505 }
Agi Csakiea34b402015-08-13 17:51:19 -07001506
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001507 // Assertions that must hold in order to compare strings 8 bytes at a time.
Vladimir Marko984519c2017-08-23 10:45:29 +01001508 // Ok to do this because strings are zero-padded to kObjectAlignment.
Agi Csakiea34b402015-08-13 17:51:19 -07001509 DCHECK_ALIGNED(value_offset, 8);
1510 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1511
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001512 if (const_string != nullptr &&
Vladimir Marko984519c2017-08-23 10:45:29 +01001513 const_string_length <= (is_compressed ? kShortConstStringEqualsCutoffInBytes
1514 : kShortConstStringEqualsCutoffInBytes / 2u)) {
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001515 // Load and compare the contents. Though we know the contents of the short const string
1516 // at compile time, materializing constants may be more code than loading from memory.
1517 int32_t offset = value_offset;
1518 size_t remaining_bytes =
1519 RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u);
1520 temp = temp.X();
1521 temp1 = temp1.X();
Vladimir Marko984519c2017-08-23 10:45:29 +01001522 while (remaining_bytes > sizeof(uint64_t)) {
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001523 Register temp2 = XRegisterFrom(locations->GetTemp(0));
1524 __ Ldp(temp, temp1, MemOperand(str.X(), offset));
1525 __ Ldp(temp2, out, MemOperand(arg.X(), offset));
1526 __ Cmp(temp, temp2);
1527 __ Ccmp(temp1, out, NoFlag, eq);
1528 __ B(&return_false, ne);
1529 offset += 2u * sizeof(uint64_t);
1530 remaining_bytes -= 2u * sizeof(uint64_t);
1531 }
1532 if (remaining_bytes != 0u) {
1533 __ Ldr(temp, MemOperand(str.X(), offset));
1534 __ Ldr(temp1, MemOperand(arg.X(), offset));
1535 __ Cmp(temp, temp1);
1536 __ B(&return_false, ne);
1537 }
1538 } else {
1539 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
1540 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1541 "Expecting 0=compressed, 1=uncompressed");
1542 __ Cbz(temp, &return_true);
1543
1544 if (mirror::kUseStringCompression) {
1545 // For string compression, calculate the number of bytes to compare (not chars).
1546 // This could in theory exceed INT32_MAX, so treat temp as unsigned.
1547 __ And(temp1, temp, Operand(1)); // Extract compression flag.
1548 __ Lsr(temp, temp, 1u); // Extract length.
1549 __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare.
1550 }
1551
1552 // Store offset of string value in preparation for comparison loop
1553 __ Mov(temp1, value_offset);
1554
1555 temp1 = temp1.X();
1556 Register temp2 = XRegisterFrom(locations->GetTemp(0));
1557 // Loop to compare strings 8 bytes at a time starting at the front of the string.
Vladimir Markoe39f14f2017-02-10 15:44:25 +00001558 __ Bind(&loop);
1559 __ Ldr(out, MemOperand(str.X(), temp1));
1560 __ Ldr(temp2, MemOperand(arg.X(), temp1));
1561 __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
1562 __ Cmp(out, temp2);
1563 __ B(&return_false, ne);
1564 // With string compression, we have compared 8 bytes, otherwise 4 chars.
1565 __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags);
1566 __ B(&loop, hi);
jessicahandojo05765752016-09-09 19:01:32 -07001567 }
1568
Agi Csakiea34b402015-08-13 17:51:19 -07001569 // Return true and exit the function.
1570 // If loop does not result in returning false, we return true.
1571 __ Bind(&return_true);
1572 __ Mov(out, 1);
1573 __ B(&end);
1574
1575 // Return false and exit the function.
1576 __ Bind(&return_false);
1577 __ Mov(out, 0);
1578 __ Bind(&end);
1579}
1580
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001581static void GenerateVisitStringIndexOf(HInvoke* invoke,
Scott Wakeling97c72b72016-06-24 16:19:36 +01001582 MacroAssembler* masm,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001583 CodeGeneratorARM64* codegen,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001584 bool start_at_zero) {
1585 LocationSummary* locations = invoke->GetLocations();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001586
1587 // Note that the null check must have been done earlier.
1588 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1589
1590 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001591 // 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 -07001592 SlowPathCodeARM64* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001593 HInstruction* code_point = invoke->InputAt(1);
1594 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01001595 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > 0xFFFFU) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001596 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1597 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
Vladimir Marko174b2e22017-10-12 13:34:49 +01001598 slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathARM64(invoke);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001599 codegen->AddSlowPath(slow_path);
1600 __ B(slow_path->GetEntryLabel());
1601 __ Bind(slow_path->GetExitLabel());
1602 return;
1603 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001604 } else if (code_point->GetType() != DataType::Type::kUint16) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001605 Register char_reg = WRegisterFrom(locations->InAt(1));
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001606 __ Tst(char_reg, 0xFFFF0000);
Vladimir Marko174b2e22017-10-12 13:34:49 +01001607 slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathARM64(invoke);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001608 codegen->AddSlowPath(slow_path);
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001609 __ B(ne, slow_path->GetEntryLabel());
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001610 }
1611
1612 if (start_at_zero) {
1613 // Start-index = 0.
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001614 Register tmp_reg = WRegisterFrom(locations->GetTemp(0));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001615 __ Mov(tmp_reg, 0);
1616 }
1617
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001618 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
Roland Levillain42ad2882016-02-29 18:26:54 +00001619 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001620
1621 if (slow_path != nullptr) {
1622 __ Bind(slow_path->GetExitLabel());
1623 }
1624}
1625
1626void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001627 LocationSummary* locations = new (allocator_) LocationSummary(
1628 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001629 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1630 // best to align the inputs accordingly.
1631 InvokeRuntimeCallingConvention calling_convention;
1632 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1633 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001634 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kInt32));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001635
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001636 // Need to send start_index=0.
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001637 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1638}
1639
1640void IntrinsicCodeGeneratorARM64::VisitStringIndexOf(HInvoke* invoke) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001641 GenerateVisitStringIndexOf(invoke, GetVIXLAssembler(), codegen_, /* start_at_zero */ true);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001642}
1643
1644void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001645 LocationSummary* locations = new (allocator_) LocationSummary(
1646 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001647 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1648 // best to align the inputs accordingly.
1649 InvokeRuntimeCallingConvention calling_convention;
1650 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1651 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1652 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001653 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kInt32));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001654}
1655
1656void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001657 GenerateVisitStringIndexOf(invoke, GetVIXLAssembler(), codegen_, /* start_at_zero */ false);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001658}
1659
Jeff Hao848f70a2014-01-15 13:49:50 -08001660void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001661 LocationSummary* locations = new (allocator_) LocationSummary(
1662 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Jeff Hao848f70a2014-01-15 13:49:50 -08001663 InvokeRuntimeCallingConvention calling_convention;
1664 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1665 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1666 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1667 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001668 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
Jeff Hao848f70a2014-01-15 13:49:50 -08001669}
1670
1671void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001672 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001673 LocationSummary* locations = invoke->GetLocations();
1674
1675 Register byte_array = WRegisterFrom(locations->InAt(0));
1676 __ Cmp(byte_array, 0);
Vladimir Marko174b2e22017-10-12 13:34:49 +01001677 SlowPathCodeARM64* slow_path =
1678 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARM64(invoke);
Jeff Hao848f70a2014-01-15 13:49:50 -08001679 codegen_->AddSlowPath(slow_path);
1680 __ B(eq, slow_path->GetEntryLabel());
1681
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001682 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001683 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001684 __ Bind(slow_path->GetExitLabel());
1685}
1686
1687void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001688 LocationSummary* locations =
1689 new (allocator_) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Jeff Hao848f70a2014-01-15 13:49:50 -08001690 InvokeRuntimeCallingConvention calling_convention;
1691 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1692 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1693 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001694 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
Jeff Hao848f70a2014-01-15 13:49:50 -08001695}
1696
1697void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) {
Roland Levillaincc3839c2016-02-29 16:23:48 +00001698 // No need to emit code checking whether `locations->InAt(2)` is a null
1699 // pointer, as callers of the native method
1700 //
1701 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1702 //
1703 // all include a null check on `data` before calling that method.
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001704 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
Roland Levillainf969a202016-03-09 16:14:00 +00001705 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001706}
1707
1708void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001709 LocationSummary* locations = new (allocator_) LocationSummary(
1710 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Jeff Hao848f70a2014-01-15 13:49:50 -08001711 InvokeRuntimeCallingConvention calling_convention;
1712 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001713 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
Jeff Hao848f70a2014-01-15 13:49:50 -08001714}
1715
1716void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001717 MacroAssembler* masm = GetVIXLAssembler();
Jeff Hao848f70a2014-01-15 13:49:50 -08001718 LocationSummary* locations = invoke->GetLocations();
1719
1720 Register string_to_copy = WRegisterFrom(locations->InAt(0));
1721 __ Cmp(string_to_copy, 0);
Vladimir Marko174b2e22017-10-12 13:34:49 +01001722 SlowPathCodeARM64* slow_path =
1723 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARM64(invoke);
Jeff Hao848f70a2014-01-15 13:49:50 -08001724 codegen_->AddSlowPath(slow_path);
1725 __ B(eq, slow_path->GetEntryLabel());
1726
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001727 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001728 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001729 __ Bind(slow_path->GetExitLabel());
1730}
1731
Vladimir Markoca6fff82017-10-03 14:49:14 +01001732static void CreateFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001733 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001734 DCHECK(DataType::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1735 DCHECK(DataType::IsFloatingPointType(invoke->GetType()));
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001736
Vladimir Markoca6fff82017-10-03 14:49:14 +01001737 LocationSummary* const locations =
1738 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001739 InvokeRuntimeCallingConvention calling_convention;
1740
1741 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1742 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1743}
1744
Vladimir Markoca6fff82017-10-03 14:49:14 +01001745static void CreateFPFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001746 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001747 DCHECK(DataType::IsFloatingPointType(invoke->InputAt(0)->GetType()));
1748 DCHECK(DataType::IsFloatingPointType(invoke->InputAt(1)->GetType()));
1749 DCHECK(DataType::IsFloatingPointType(invoke->GetType()));
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001750
Vladimir Markoca6fff82017-10-03 14:49:14 +01001751 LocationSummary* const locations =
1752 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001753 InvokeRuntimeCallingConvention calling_convention;
1754
1755 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1756 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
1757 locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
1758}
1759
1760static void GenFPToFPCall(HInvoke* invoke,
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001761 CodeGeneratorARM64* codegen,
1762 QuickEntrypointEnum entry) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001763 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001764}
1765
1766void IntrinsicLocationsBuilderARM64::VisitMathCos(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001767 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001768}
1769
1770void IntrinsicCodeGeneratorARM64::VisitMathCos(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001771 GenFPToFPCall(invoke, codegen_, kQuickCos);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001772}
1773
1774void IntrinsicLocationsBuilderARM64::VisitMathSin(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001775 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001776}
1777
1778void IntrinsicCodeGeneratorARM64::VisitMathSin(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001779 GenFPToFPCall(invoke, codegen_, kQuickSin);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001780}
1781
1782void IntrinsicLocationsBuilderARM64::VisitMathAcos(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001783 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001784}
1785
1786void IntrinsicCodeGeneratorARM64::VisitMathAcos(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001787 GenFPToFPCall(invoke, codegen_, kQuickAcos);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001788}
1789
1790void IntrinsicLocationsBuilderARM64::VisitMathAsin(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001791 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001792}
1793
1794void IntrinsicCodeGeneratorARM64::VisitMathAsin(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001795 GenFPToFPCall(invoke, codegen_, kQuickAsin);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001796}
1797
1798void IntrinsicLocationsBuilderARM64::VisitMathAtan(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001799 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001800}
1801
1802void IntrinsicCodeGeneratorARM64::VisitMathAtan(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001803 GenFPToFPCall(invoke, codegen_, kQuickAtan);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001804}
1805
1806void IntrinsicLocationsBuilderARM64::VisitMathCbrt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001807 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001808}
1809
1810void IntrinsicCodeGeneratorARM64::VisitMathCbrt(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001811 GenFPToFPCall(invoke, codegen_, kQuickCbrt);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001812}
1813
1814void IntrinsicLocationsBuilderARM64::VisitMathCosh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001815 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001816}
1817
1818void IntrinsicCodeGeneratorARM64::VisitMathCosh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001819 GenFPToFPCall(invoke, codegen_, kQuickCosh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001820}
1821
1822void IntrinsicLocationsBuilderARM64::VisitMathExp(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001823 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001824}
1825
1826void IntrinsicCodeGeneratorARM64::VisitMathExp(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001827 GenFPToFPCall(invoke, codegen_, kQuickExp);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001828}
1829
1830void IntrinsicLocationsBuilderARM64::VisitMathExpm1(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001831 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001832}
1833
1834void IntrinsicCodeGeneratorARM64::VisitMathExpm1(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001835 GenFPToFPCall(invoke, codegen_, kQuickExpm1);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001836}
1837
1838void IntrinsicLocationsBuilderARM64::VisitMathLog(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001839 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001840}
1841
1842void IntrinsicCodeGeneratorARM64::VisitMathLog(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001843 GenFPToFPCall(invoke, codegen_, kQuickLog);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001844}
1845
1846void IntrinsicLocationsBuilderARM64::VisitMathLog10(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001847 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001848}
1849
1850void IntrinsicCodeGeneratorARM64::VisitMathLog10(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001851 GenFPToFPCall(invoke, codegen_, kQuickLog10);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001852}
1853
1854void IntrinsicLocationsBuilderARM64::VisitMathSinh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001855 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001856}
1857
1858void IntrinsicCodeGeneratorARM64::VisitMathSinh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001859 GenFPToFPCall(invoke, codegen_, kQuickSinh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001860}
1861
1862void IntrinsicLocationsBuilderARM64::VisitMathTan(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001863 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001864}
1865
1866void IntrinsicCodeGeneratorARM64::VisitMathTan(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001867 GenFPToFPCall(invoke, codegen_, kQuickTan);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001868}
1869
1870void IntrinsicLocationsBuilderARM64::VisitMathTanh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001871 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001872}
1873
1874void IntrinsicCodeGeneratorARM64::VisitMathTanh(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001875 GenFPToFPCall(invoke, codegen_, kQuickTanh);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001876}
1877
1878void IntrinsicLocationsBuilderARM64::VisitMathAtan2(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001879 CreateFPFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001880}
1881
1882void IntrinsicCodeGeneratorARM64::VisitMathAtan2(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001883 GenFPToFPCall(invoke, codegen_, kQuickAtan2);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001884}
1885
Vladimir Marko4d179872018-01-19 14:50:10 +00001886void IntrinsicLocationsBuilderARM64::VisitMathPow(HInvoke* invoke) {
1887 CreateFPFPToFPCallLocations(allocator_, invoke);
1888}
1889
1890void IntrinsicCodeGeneratorARM64::VisitMathPow(HInvoke* invoke) {
1891 GenFPToFPCall(invoke, codegen_, kQuickPow);
1892}
1893
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001894void IntrinsicLocationsBuilderARM64::VisitMathHypot(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001895 CreateFPFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001896}
1897
1898void IntrinsicCodeGeneratorARM64::VisitMathHypot(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001899 GenFPToFPCall(invoke, codegen_, kQuickHypot);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001900}
1901
1902void IntrinsicLocationsBuilderARM64::VisitMathNextAfter(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001903 CreateFPFPToFPCallLocations(allocator_, invoke);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001904}
1905
1906void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001907 GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
Anton Kirilov02fc24e2016-01-20 16:48:19 +00001908}
1909
Tim Zhang25abd6c2016-01-19 23:39:24 +08001910void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001911 LocationSummary* locations =
1912 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Tim Zhang25abd6c2016-01-19 23:39:24 +08001913 locations->SetInAt(0, Location::RequiresRegister());
1914 locations->SetInAt(1, Location::RequiresRegister());
1915 locations->SetInAt(2, Location::RequiresRegister());
1916 locations->SetInAt(3, Location::RequiresRegister());
1917 locations->SetInAt(4, Location::RequiresRegister());
1918
1919 locations->AddTemp(Location::RequiresRegister());
1920 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingdf109d92016-04-22 11:35:56 +01001921 locations->AddTemp(Location::RequiresRegister());
Tim Zhang25abd6c2016-01-19 23:39:24 +08001922}
1923
1924void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001925 MacroAssembler* masm = GetVIXLAssembler();
Tim Zhang25abd6c2016-01-19 23:39:24 +08001926 LocationSummary* locations = invoke->GetLocations();
1927
1928 // Check assumption that sizeof(Char) is 2 (used in scaling below).
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001929 const size_t char_size = DataType::Size(DataType::Type::kUint16);
Tim Zhang25abd6c2016-01-19 23:39:24 +08001930 DCHECK_EQ(char_size, 2u);
1931
1932 // Location of data in char array buffer.
1933 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
1934
1935 // Location of char array data in string.
1936 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1937
1938 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
1939 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
1940 Register srcObj = XRegisterFrom(locations->InAt(0));
1941 Register srcBegin = XRegisterFrom(locations->InAt(1));
1942 Register srcEnd = XRegisterFrom(locations->InAt(2));
1943 Register dstObj = XRegisterFrom(locations->InAt(3));
1944 Register dstBegin = XRegisterFrom(locations->InAt(4));
1945
1946 Register src_ptr = XRegisterFrom(locations->GetTemp(0));
Scott Wakelingdf109d92016-04-22 11:35:56 +01001947 Register num_chr = XRegisterFrom(locations->GetTemp(1));
1948 Register tmp1 = XRegisterFrom(locations->GetTemp(2));
Tim Zhang25abd6c2016-01-19 23:39:24 +08001949
1950 UseScratchRegisterScope temps(masm);
1951 Register dst_ptr = temps.AcquireX();
Scott Wakelingdf109d92016-04-22 11:35:56 +01001952 Register tmp2 = temps.AcquireX();
Tim Zhang25abd6c2016-01-19 23:39:24 +08001953
jessicahandojo05765752016-09-09 19:01:32 -07001954 vixl::aarch64::Label done;
1955 vixl::aarch64::Label compressed_string_loop;
1956 __ Sub(num_chr, srcEnd, srcBegin);
1957 // Early out for valid zero-length retrievals.
1958 __ Cbz(num_chr, &done);
Tim Zhang25abd6c2016-01-19 23:39:24 +08001959
Scott Wakelingdf109d92016-04-22 11:35:56 +01001960 // dst address start to copy to.
Tim Zhang25abd6c2016-01-19 23:39:24 +08001961 __ Add(dst_ptr, dstObj, Operand(data_offset));
1962 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
1963
jessicahandojo05765752016-09-09 19:01:32 -07001964 // src address to copy from.
1965 __ Add(src_ptr, srcObj, Operand(value_offset));
1966 vixl::aarch64::Label compressed_string_preloop;
1967 if (mirror::kUseStringCompression) {
1968 // Location of count in string.
1969 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
1970 // String's length.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001971 __ Ldr(tmp2, MemOperand(srcObj, count_offset));
1972 __ Tbz(tmp2, 0, &compressed_string_preloop);
jessicahandojo05765752016-09-09 19:01:32 -07001973 }
1974 __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
Scott Wakelingdf109d92016-04-22 11:35:56 +01001975
Tim Zhang25abd6c2016-01-19 23:39:24 +08001976 // Do the copy.
Scott Wakeling97c72b72016-06-24 16:19:36 +01001977 vixl::aarch64::Label loop;
Scott Wakeling97c72b72016-06-24 16:19:36 +01001978 vixl::aarch64::Label remainder;
Scott Wakelingdf109d92016-04-22 11:35:56 +01001979
Scott Wakelingdf109d92016-04-22 11:35:56 +01001980 // Save repairing the value of num_chr on the < 8 character path.
1981 __ Subs(tmp1, num_chr, 8);
1982 __ B(lt, &remainder);
1983
1984 // Keep the result of the earlier subs, we are going to fetch at least 8 characters.
1985 __ Mov(num_chr, tmp1);
1986
1987 // Main loop used for longer fetches loads and stores 8x16-bit characters at a time.
1988 // (Unaligned addresses are acceptable here and not worth inlining extra code to rectify.)
Tim Zhang25abd6c2016-01-19 23:39:24 +08001989 __ Bind(&loop);
Scott Wakeling97c72b72016-06-24 16:19:36 +01001990 __ Ldp(tmp1, tmp2, MemOperand(src_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01001991 __ Subs(num_chr, num_chr, 8);
Scott Wakeling97c72b72016-06-24 16:19:36 +01001992 __ Stp(tmp1, tmp2, MemOperand(dst_ptr, char_size * 8, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01001993 __ B(ge, &loop);
1994
1995 __ Adds(num_chr, num_chr, 8);
1996 __ B(eq, &done);
1997
1998 // Main loop for < 8 character case and remainder handling. Loads and stores one
1999 // 16-bit Java character at a time.
2000 __ Bind(&remainder);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002001 __ Ldrh(tmp1, MemOperand(src_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002002 __ Subs(num_chr, num_chr, 1);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002003 __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
Scott Wakelingdf109d92016-04-22 11:35:56 +01002004 __ B(gt, &remainder);
jessicahandojo05765752016-09-09 19:01:32 -07002005 __ B(&done);
2006
2007 if (mirror::kUseStringCompression) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002008 const size_t c_char_size = DataType::Size(DataType::Type::kInt8);
jessicahandojo05765752016-09-09 19:01:32 -07002009 DCHECK_EQ(c_char_size, 1u);
2010 __ Bind(&compressed_string_preloop);
2011 __ Add(src_ptr, src_ptr, Operand(srcBegin));
2012 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2013 __ Bind(&compressed_string_loop);
2014 __ Ldrb(tmp1, MemOperand(src_ptr, c_char_size, PostIndex));
2015 __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
2016 __ Subs(num_chr, num_chr, Operand(1));
2017 __ B(gt, &compressed_string_loop);
2018 }
Scott Wakelingdf109d92016-04-22 11:35:56 +01002019
Tim Zhang25abd6c2016-01-19 23:39:24 +08002020 __ Bind(&done);
2021}
2022
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002023// Mirrors ARRAYCOPY_SHORT_CHAR_ARRAY_THRESHOLD in libcore, so we can choose to use the native
2024// implementation there for longer copy lengths.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002025static constexpr int32_t kSystemArrayCopyCharThreshold = 32;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002026
2027static void SetSystemArrayCopyLocationRequires(LocationSummary* locations,
2028 uint32_t at,
2029 HInstruction* input) {
2030 HIntConstant* const_input = input->AsIntConstant();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002031 if (const_input != nullptr && !vixl::aarch64::Assembler::IsImmAddSub(const_input->GetValue())) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002032 locations->SetInAt(at, Location::RequiresRegister());
2033 } else {
2034 locations->SetInAt(at, Location::RegisterOrConstant(input));
2035 }
2036}
2037
2038void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
2039 // Check to see if we have known failures that will cause us to have to bail out
2040 // to the runtime, and just generate the runtime call directly.
2041 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2042 HIntConstant* dst_pos = invoke->InputAt(3)->AsIntConstant();
2043
2044 // The positions must be non-negative.
2045 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2046 (dst_pos != nullptr && dst_pos->GetValue() < 0)) {
2047 // We will have to fail anyways.
2048 return;
2049 }
2050
2051 // The length must be >= 0 and not so long that we would (currently) prefer libcore's
2052 // native implementation.
2053 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2054 if (length != nullptr) {
2055 int32_t len = length->GetValue();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002056 if (len < 0 || len > kSystemArrayCopyCharThreshold) {
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002057 // Just call as normal.
2058 return;
2059 }
2060 }
2061
Vladimir Markoca6fff82017-10-03 14:49:14 +01002062 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator();
2063 LocationSummary* locations =
2064 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnSlowPath, kIntrinsified);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002065 // arraycopy(char[] src, int src_pos, char[] dst, int dst_pos, int length).
2066 locations->SetInAt(0, Location::RequiresRegister());
2067 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2068 locations->SetInAt(2, Location::RequiresRegister());
2069 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2070 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2071
2072 locations->AddTemp(Location::RequiresRegister());
2073 locations->AddTemp(Location::RequiresRegister());
2074 locations->AddTemp(Location::RequiresRegister());
2075}
2076
Scott Wakeling97c72b72016-06-24 16:19:36 +01002077static void CheckSystemArrayCopyPosition(MacroAssembler* masm,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002078 const Location& pos,
2079 const Register& input,
2080 const Location& length,
2081 SlowPathCodeARM64* slow_path,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002082 const Register& temp,
2083 bool length_is_input_length = false) {
2084 const int32_t length_offset = mirror::Array::LengthOffset().Int32Value();
2085 if (pos.IsConstant()) {
2086 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
2087 if (pos_const == 0) {
2088 if (!length_is_input_length) {
2089 // Check that length(input) >= length.
2090 __ Ldr(temp, MemOperand(input, length_offset));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002091 __ Cmp(temp, OperandFrom(length, DataType::Type::kInt32));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002092 __ B(slow_path->GetEntryLabel(), lt);
2093 }
2094 } else {
2095 // Check that length(input) >= pos.
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002096 __ Ldr(temp, MemOperand(input, length_offset));
2097 __ Subs(temp, temp, pos_const);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002098 __ B(slow_path->GetEntryLabel(), lt);
2099
2100 // Check that (length(input) - pos) >= length.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002101 __ Cmp(temp, OperandFrom(length, DataType::Type::kInt32));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002102 __ B(slow_path->GetEntryLabel(), lt);
2103 }
2104 } else if (length_is_input_length) {
2105 // The only way the copy can succeed is if pos is zero.
2106 __ Cbnz(WRegisterFrom(pos), slow_path->GetEntryLabel());
2107 } else {
2108 // Check that pos >= 0.
2109 Register pos_reg = WRegisterFrom(pos);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002110 __ Tbnz(pos_reg, pos_reg.GetSizeInBits() - 1, slow_path->GetEntryLabel());
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002111
2112 // Check that pos <= length(input) && (length(input) - pos) >= length.
2113 __ Ldr(temp, MemOperand(input, length_offset));
2114 __ Subs(temp, temp, pos_reg);
2115 // Ccmp if length(input) >= pos, else definitely bail to slow path (N!=V == lt).
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002116 __ Ccmp(temp, OperandFrom(length, DataType::Type::kInt32), NFlag, ge);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002117 __ B(slow_path->GetEntryLabel(), lt);
2118 }
2119}
2120
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002121// Compute base source address, base destination address, and end
2122// source address for System.arraycopy* intrinsics in `src_base`,
2123// `dst_base` and `src_end` respectively.
Scott Wakeling97c72b72016-06-24 16:19:36 +01002124static void GenSystemArrayCopyAddresses(MacroAssembler* masm,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002125 DataType::Type type,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002126 const Register& src,
2127 const Location& src_pos,
2128 const Register& dst,
2129 const Location& dst_pos,
2130 const Location& copy_length,
2131 const Register& src_base,
2132 const Register& dst_base,
2133 const Register& src_end) {
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002134 // This routine is used by the SystemArrayCopy and the SystemArrayCopyChar intrinsics.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002135 DCHECK(type == DataType::Type::kReference || type == DataType::Type::kUint16)
Roland Levillainebea3d22016-04-12 15:42:57 +01002136 << "Unexpected element type: " << type;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002137 const int32_t element_size = DataType::Size(type);
2138 const int32_t element_size_shift = DataType::SizeShift(type);
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002139 const uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002140
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002141 if (src_pos.IsConstant()) {
2142 int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002143 __ Add(src_base, src, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002144 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002145 __ Add(src_base, src, data_offset);
2146 __ Add(src_base, src_base, Operand(XRegisterFrom(src_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002147 }
2148
2149 if (dst_pos.IsConstant()) {
2150 int32_t constant = dst_pos.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002151 __ Add(dst_base, dst, element_size * constant + data_offset);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002152 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002153 __ Add(dst_base, dst, data_offset);
2154 __ Add(dst_base, dst_base, Operand(XRegisterFrom(dst_pos), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002155 }
2156
2157 if (copy_length.IsConstant()) {
2158 int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
Roland Levillainebea3d22016-04-12 15:42:57 +01002159 __ Add(src_end, src_base, element_size * constant);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002160 } else {
Roland Levillainebea3d22016-04-12 15:42:57 +01002161 __ Add(src_end, src_base, Operand(XRegisterFrom(copy_length), LSL, element_size_shift));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002162 }
2163}
2164
2165void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01002166 MacroAssembler* masm = GetVIXLAssembler();
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002167 LocationSummary* locations = invoke->GetLocations();
2168 Register src = XRegisterFrom(locations->InAt(0));
2169 Location src_pos = locations->InAt(1);
2170 Register dst = XRegisterFrom(locations->InAt(2));
2171 Location dst_pos = locations->InAt(3);
2172 Location length = locations->InAt(4);
2173
Vladimir Marko174b2e22017-10-12 13:34:49 +01002174 SlowPathCodeARM64* slow_path =
2175 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARM64(invoke);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002176 codegen_->AddSlowPath(slow_path);
2177
2178 // If source and destination are the same, take the slow path. Overlapping copy regions must be
2179 // copied in reverse and we can't know in all cases if it's needed.
2180 __ Cmp(src, dst);
2181 __ B(slow_path->GetEntryLabel(), eq);
2182
2183 // Bail out if the source is null.
2184 __ Cbz(src, slow_path->GetEntryLabel());
2185
2186 // Bail out if the destination is null.
2187 __ Cbz(dst, slow_path->GetEntryLabel());
2188
2189 if (!length.IsConstant()) {
Vladimir Markoc5646202016-11-28 16:03:15 +00002190 // Merge the following two comparisons into one:
2191 // If the length is negative, bail out (delegate to libcore's native implementation).
2192 // If the length > 32 then (currently) prefer libcore's native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002193 __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold);
Vladimir Markoc5646202016-11-28 16:03:15 +00002194 __ B(slow_path->GetEntryLabel(), hi);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002195 } else {
2196 // We have already checked in the LocationsBuilder for the constant case.
2197 DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0);
2198 DCHECK_LE(length.GetConstant()->AsIntConstant()->GetValue(), 32);
2199 }
2200
2201 Register src_curr_addr = WRegisterFrom(locations->GetTemp(0));
2202 Register dst_curr_addr = WRegisterFrom(locations->GetTemp(1));
2203 Register src_stop_addr = WRegisterFrom(locations->GetTemp(2));
2204
2205 CheckSystemArrayCopyPosition(masm,
2206 src_pos,
2207 src,
2208 length,
2209 slow_path,
2210 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002211 false);
2212
2213 CheckSystemArrayCopyPosition(masm,
2214 dst_pos,
2215 dst,
2216 length,
2217 slow_path,
2218 src_curr_addr,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002219 false);
2220
2221 src_curr_addr = src_curr_addr.X();
2222 dst_curr_addr = dst_curr_addr.X();
2223 src_stop_addr = src_stop_addr.X();
2224
2225 GenSystemArrayCopyAddresses(masm,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002226 DataType::Type::kUint16,
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002227 src,
2228 src_pos,
2229 dst,
2230 dst_pos,
2231 length,
2232 src_curr_addr,
2233 dst_curr_addr,
2234 src_stop_addr);
2235
2236 // Iterate over the arrays and do a raw copy of the chars.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002237 const int32_t char_size = DataType::Size(DataType::Type::kUint16);
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002238 UseScratchRegisterScope temps(masm);
2239 Register tmp = temps.AcquireW();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002240 vixl::aarch64::Label loop, done;
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002241 __ Bind(&loop);
2242 __ Cmp(src_curr_addr, src_stop_addr);
2243 __ B(&done, eq);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002244 __ Ldrh(tmp, MemOperand(src_curr_addr, char_size, PostIndex));
2245 __ Strh(tmp, MemOperand(dst_curr_addr, char_size, PostIndex));
Scott Wakelingd3d0da52016-02-29 15:17:20 +00002246 __ B(&loop);
2247 __ Bind(&done);
2248
2249 __ Bind(slow_path->GetExitLabel());
2250}
2251
donghui.baic2ec9ad2016-03-10 14:02:55 +08002252// We can choose to use the native implementation there for longer copy lengths.
2253static constexpr int32_t kSystemArrayCopyThreshold = 128;
2254
2255// CodeGenerator::CreateSystemArrayCopyLocationSummary use three temporary registers.
2256// We want to use two temporary registers in order to reduce the register pressure in arm64.
2257// So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary.
2258void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002259 // The only read barrier implementation supporting the
2260 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2261 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain3d312422016-06-23 13:53:42 +01002262 return;
2263 }
2264
donghui.baic2ec9ad2016-03-10 14:02:55 +08002265 // Check to see if we have known failures that will cause us to have to bail out
2266 // to the runtime, and just generate the runtime call directly.
2267 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2268 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
2269
2270 // The positions must be non-negative.
2271 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2272 (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
2273 // We will have to fail anyways.
2274 return;
2275 }
2276
2277 // The length must be >= 0.
2278 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2279 if (length != nullptr) {
2280 int32_t len = length->GetValue();
2281 if (len < 0 || len >= kSystemArrayCopyThreshold) {
2282 // Just call as normal.
2283 return;
2284 }
2285 }
2286
2287 SystemArrayCopyOptimizations optimizations(invoke);
2288
2289 if (optimizations.GetDestinationIsSource()) {
2290 if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) {
2291 // We only support backward copying if source and destination are the same.
2292 return;
2293 }
2294 }
2295
2296 if (optimizations.GetDestinationIsPrimitiveArray() || optimizations.GetSourceIsPrimitiveArray()) {
2297 // We currently don't intrinsify primitive copying.
2298 return;
2299 }
2300
Vladimir Markoca6fff82017-10-03 14:49:14 +01002301 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator();
2302 LocationSummary* locations =
2303 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnSlowPath, kIntrinsified);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002304 // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length).
2305 locations->SetInAt(0, Location::RequiresRegister());
2306 SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1));
2307 locations->SetInAt(2, Location::RequiresRegister());
2308 SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3));
2309 SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4));
2310
2311 locations->AddTemp(Location::RequiresRegister());
2312 locations->AddTemp(Location::RequiresRegister());
Roland Levillain0b671c02016-08-19 12:02:34 +01002313 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2314 // Temporary register IP0, obtained from the VIXL scratch register
2315 // pool, cannot be used in ReadBarrierSystemArrayCopySlowPathARM64
2316 // (because that register is clobbered by ReadBarrierMarkRegX
Roland Levillain54f869e2017-03-06 13:54:11 +00002317 // entry points). It cannot be used in calls to
2318 // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier
2319 // either. For these reasons, get a third extra temporary register
2320 // from the register allocator.
Roland Levillain0b671c02016-08-19 12:02:34 +01002321 locations->AddTemp(Location::RequiresRegister());
Roland Levillain54f869e2017-03-06 13:54:11 +00002322 } else {
2323 // Cases other than Baker read barriers: the third temporary will
2324 // be acquired from the VIXL scratch register pool.
Roland Levillain0b671c02016-08-19 12:02:34 +01002325 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002326}
2327
2328void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002329 // The only read barrier implementation supporting the
2330 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2331 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01002332
Scott Wakeling97c72b72016-06-24 16:19:36 +01002333 MacroAssembler* masm = GetVIXLAssembler();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002334 LocationSummary* locations = invoke->GetLocations();
2335
2336 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2337 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2338 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2339 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Roland Levillain0b671c02016-08-19 12:02:34 +01002340 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
donghui.baic2ec9ad2016-03-10 14:02:55 +08002341
2342 Register src = XRegisterFrom(locations->InAt(0));
2343 Location src_pos = locations->InAt(1);
2344 Register dest = XRegisterFrom(locations->InAt(2));
2345 Location dest_pos = locations->InAt(3);
2346 Location length = locations->InAt(4);
2347 Register temp1 = WRegisterFrom(locations->GetTemp(0));
Roland Levillain0b671c02016-08-19 12:02:34 +01002348 Location temp1_loc = LocationFrom(temp1);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002349 Register temp2 = WRegisterFrom(locations->GetTemp(1));
Roland Levillain0b671c02016-08-19 12:02:34 +01002350 Location temp2_loc = LocationFrom(temp2);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002351
Vladimir Marko174b2e22017-10-12 13:34:49 +01002352 SlowPathCodeARM64* intrinsic_slow_path =
2353 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARM64(invoke);
Roland Levillain0b671c02016-08-19 12:02:34 +01002354 codegen_->AddSlowPath(intrinsic_slow_path);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002355
Scott Wakeling97c72b72016-06-24 16:19:36 +01002356 vixl::aarch64::Label conditions_on_positions_validated;
donghui.baic2ec9ad2016-03-10 14:02:55 +08002357 SystemArrayCopyOptimizations optimizations(invoke);
2358
donghui.baic2ec9ad2016-03-10 14:02:55 +08002359 // If source and destination are the same, we go to slow path if we need to do
2360 // forward copying.
2361 if (src_pos.IsConstant()) {
2362 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
2363 if (dest_pos.IsConstant()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002364 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
2365 if (optimizations.GetDestinationIsSource()) {
2366 // Checked when building locations.
2367 DCHECK_GE(src_pos_constant, dest_pos_constant);
2368 } else if (src_pos_constant < dest_pos_constant) {
2369 __ Cmp(src, dest);
Roland Levillain0b671c02016-08-19 12:02:34 +01002370 __ B(intrinsic_slow_path->GetEntryLabel(), eq);
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002371 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002372 // Checked when building locations.
2373 DCHECK(!optimizations.GetDestinationIsSource()
2374 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
2375 } else {
2376 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002377 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002378 __ B(&conditions_on_positions_validated, ne);
2379 }
2380 __ Cmp(WRegisterFrom(dest_pos), src_pos_constant);
Roland Levillain0b671c02016-08-19 12:02:34 +01002381 __ B(intrinsic_slow_path->GetEntryLabel(), gt);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002382 }
2383 } else {
2384 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01002385 __ Cmp(src, dest);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002386 __ B(&conditions_on_positions_validated, ne);
2387 }
2388 __ Cmp(RegisterFrom(src_pos, invoke->InputAt(1)->GetType()),
2389 OperandFrom(dest_pos, invoke->InputAt(3)->GetType()));
Roland Levillain0b671c02016-08-19 12:02:34 +01002390 __ B(intrinsic_slow_path->GetEntryLabel(), lt);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002391 }
2392
2393 __ Bind(&conditions_on_positions_validated);
2394
2395 if (!optimizations.GetSourceIsNotNull()) {
2396 // Bail out if the source is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01002397 __ Cbz(src, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002398 }
2399
2400 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
2401 // Bail out if the destination is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01002402 __ Cbz(dest, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002403 }
2404
2405 // We have already checked in the LocationsBuilder for the constant case.
2406 if (!length.IsConstant() &&
2407 !optimizations.GetCountIsSourceLength() &&
2408 !optimizations.GetCountIsDestinationLength()) {
Vladimir Markoc5646202016-11-28 16:03:15 +00002409 // Merge the following two comparisons into one:
2410 // If the length is negative, bail out (delegate to libcore's native implementation).
2411 // If the length >= 128 then (currently) prefer native implementation.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002412 __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold);
Vladimir Markoc5646202016-11-28 16:03:15 +00002413 __ B(intrinsic_slow_path->GetEntryLabel(), hs);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002414 }
2415 // Validity checks: source.
2416 CheckSystemArrayCopyPosition(masm,
2417 src_pos,
2418 src,
2419 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002420 intrinsic_slow_path,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002421 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002422 optimizations.GetCountIsSourceLength());
2423
2424 // Validity checks: dest.
2425 CheckSystemArrayCopyPosition(masm,
2426 dest_pos,
2427 dest,
2428 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002429 intrinsic_slow_path,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002430 temp1,
donghui.baic2ec9ad2016-03-10 14:02:55 +08002431 optimizations.GetCountIsDestinationLength());
2432 {
2433 // We use a block to end the scratch scope before the write barrier, thus
2434 // freeing the temporary registers so they can be used in `MarkGCCard`.
2435 UseScratchRegisterScope temps(masm);
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002436 Location temp3_loc; // Used only for Baker read barrier.
Roland Levillain54f869e2017-03-06 13:54:11 +00002437 Register temp3;
2438 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002439 temp3_loc = locations->GetTemp(2);
2440 temp3 = WRegisterFrom(temp3_loc);
Roland Levillain54f869e2017-03-06 13:54:11 +00002441 } else {
2442 temp3 = temps.AcquireW();
2443 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002444
donghui.baic2ec9ad2016-03-10 14:02:55 +08002445 if (!optimizations.GetDoesNotNeedTypeCheck()) {
2446 // Check whether all elements of the source array are assignable to the component
2447 // type of the destination array. We do two checks: the classes are the same,
2448 // or the destination is Object[]. If none of these checks succeed, we go to the
2449 // slow path.
donghui.baic2ec9ad2016-03-10 14:02:55 +08002450
Roland Levillain0b671c02016-08-19 12:02:34 +01002451 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2452 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2453 // /* HeapReference<Class> */ temp1 = src->klass_
2454 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2455 temp1_loc,
2456 src.W(),
2457 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002458 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002459 /* needs_null_check */ false,
2460 /* use_load_acquire */ false);
2461 // Bail out if the source is not a non primitive array.
2462 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2463 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2464 temp1_loc,
2465 temp1,
2466 component_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002467 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002468 /* needs_null_check */ false,
2469 /* use_load_acquire */ false);
2470 __ Cbz(temp1, intrinsic_slow_path->GetEntryLabel());
2471 // If heap poisoning is enabled, `temp1` has been unpoisoned
2472 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2473 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
2474 __ Ldrh(temp1, HeapOperand(temp1, primitive_offset));
2475 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2476 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002477 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002478
2479 // /* HeapReference<Class> */ temp1 = dest->klass_
2480 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2481 temp1_loc,
2482 dest.W(),
2483 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002484 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002485 /* needs_null_check */ false,
2486 /* use_load_acquire */ false);
2487
2488 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2489 // Bail out if the destination is not a non primitive array.
2490 //
2491 // Register `temp1` is not trashed by the read barrier emitted
2492 // by GenerateFieldLoadWithBakerReadBarrier below, as that
2493 // method produces a call to a ReadBarrierMarkRegX entry point,
2494 // which saves all potentially live registers, including
2495 // temporaries such a `temp1`.
2496 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2497 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2498 temp2_loc,
2499 temp1,
2500 component_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002501 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002502 /* needs_null_check */ false,
2503 /* use_load_acquire */ false);
2504 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2505 // If heap poisoning is enabled, `temp2` has been unpoisoned
2506 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2507 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2508 __ Ldrh(temp2, HeapOperand(temp2, primitive_offset));
2509 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2510 __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
2511 }
2512
2513 // For the same reason given earlier, `temp1` is not trashed by the
2514 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
2515 // /* HeapReference<Class> */ temp2 = src->klass_
2516 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2517 temp2_loc,
2518 src.W(),
2519 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002520 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002521 /* needs_null_check */ false,
2522 /* use_load_acquire */ false);
2523 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
2524 __ Cmp(temp1, temp2);
2525
2526 if (optimizations.GetDestinationIsTypedObjectArray()) {
2527 vixl::aarch64::Label do_copy;
2528 __ B(&do_copy, eq);
2529 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2530 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2531 temp1_loc,
2532 temp1,
2533 component_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002534 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002535 /* needs_null_check */ false,
2536 /* use_load_acquire */ false);
2537 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2538 // We do not need to emit a read barrier for the following
2539 // heap reference load, as `temp1` is only used in a
2540 // comparison with null below, and this reference is not
2541 // kept afterwards.
2542 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2543 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
2544 __ Bind(&do_copy);
2545 } else {
2546 __ B(intrinsic_slow_path->GetEntryLabel(), ne);
2547 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002548 } else {
Roland Levillain0b671c02016-08-19 12:02:34 +01002549 // Non read barrier code.
2550
2551 // /* HeapReference<Class> */ temp1 = dest->klass_
2552 __ Ldr(temp1, MemOperand(dest, class_offset));
2553 // /* HeapReference<Class> */ temp2 = src->klass_
2554 __ Ldr(temp2, MemOperand(src, class_offset));
2555 bool did_unpoison = false;
2556 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2557 !optimizations.GetSourceIsNonPrimitiveArray()) {
2558 // One or two of the references need to be unpoisoned. Unpoison them
2559 // both to make the identity check valid.
2560 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2561 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2562 did_unpoison = true;
2563 }
2564
2565 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2566 // Bail out if the destination is not a non primitive array.
2567 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2568 __ Ldr(temp3, HeapOperand(temp1, component_offset));
2569 __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
2570 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2571 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2572 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2573 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2574 __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
2575 }
2576
2577 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2578 // Bail out if the source is not a non primitive array.
2579 // /* HeapReference<Class> */ temp3 = temp2->component_type_
2580 __ Ldr(temp3, HeapOperand(temp2, component_offset));
2581 __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
2582 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp3);
2583 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2584 __ Ldrh(temp3, HeapOperand(temp3, primitive_offset));
2585 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2586 __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
2587 }
2588
2589 __ Cmp(temp1, temp2);
2590
2591 if (optimizations.GetDestinationIsTypedObjectArray()) {
2592 vixl::aarch64::Label do_copy;
2593 __ B(&do_copy, eq);
2594 if (!did_unpoison) {
2595 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2596 }
2597 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2598 __ Ldr(temp1, HeapOperand(temp1, component_offset));
2599 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2600 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2601 __ Ldr(temp1, HeapOperand(temp1, super_offset));
2602 // No need to unpoison the result, we're comparing against null.
2603 __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
2604 __ Bind(&do_copy);
2605 } else {
2606 __ B(intrinsic_slow_path->GetEntryLabel(), ne);
2607 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002608 }
2609 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2610 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2611 // Bail out if the source is not a non primitive array.
Roland Levillain0b671c02016-08-19 12:02:34 +01002612 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2613 // /* HeapReference<Class> */ temp1 = src->klass_
2614 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2615 temp1_loc,
2616 src.W(),
2617 class_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002618 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002619 /* needs_null_check */ false,
2620 /* use_load_acquire */ false);
2621 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2622 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
2623 temp2_loc,
2624 temp1,
2625 component_offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002626 temp3_loc,
Roland Levillain0b671c02016-08-19 12:02:34 +01002627 /* needs_null_check */ false,
2628 /* use_load_acquire */ false);
2629 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2630 // If heap poisoning is enabled, `temp2` has been unpoisoned
2631 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2632 } else {
2633 // /* HeapReference<Class> */ temp1 = src->klass_
2634 __ Ldr(temp1, HeapOperand(src.W(), class_offset));
2635 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp1);
2636 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2637 __ Ldr(temp2, HeapOperand(temp1, component_offset));
2638 __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
2639 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp2);
2640 }
2641 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2642 __ Ldrh(temp2, HeapOperand(temp2, primitive_offset));
donghui.baic2ec9ad2016-03-10 14:02:55 +08002643 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Roland Levillain0b671c02016-08-19 12:02:34 +01002644 __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002645 }
2646
Roland Levillain1663d162017-03-17 15:15:21 +00002647 if (length.IsConstant() && length.GetConstant()->AsIntConstant()->GetValue() == 0) {
2648 // Null constant length: not need to emit the loop code at all.
Roland Levillain0b671c02016-08-19 12:02:34 +01002649 } else {
Roland Levillain1663d162017-03-17 15:15:21 +00002650 Register src_curr_addr = temp1.X();
2651 Register dst_curr_addr = temp2.X();
2652 Register src_stop_addr = temp3.X();
2653 vixl::aarch64::Label done;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002654 const DataType::Type type = DataType::Type::kReference;
2655 const int32_t element_size = DataType::Size(type);
Roland Levillain1663d162017-03-17 15:15:21 +00002656
2657 if (length.IsRegister()) {
2658 // Don't enter the copy loop if the length is null.
2659 __ Cbz(WRegisterFrom(length), &done);
2660 }
2661
2662 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2663 // TODO: Also convert this intrinsic to the IsGcMarking strategy?
2664
2665 // SystemArrayCopy implementation for Baker read barriers (see
Roland Levillain9983e302017-07-14 14:34:22 +01002666 // also CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier):
Roland Levillain1663d162017-03-17 15:15:21 +00002667 //
2668 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
2669 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
2670 // bool is_gray = (rb_state == ReadBarrier::GrayState());
2671 // if (is_gray) {
2672 // // Slow-path copy.
2673 // do {
2674 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
2675 // } while (src_ptr != end_ptr)
2676 // } else {
2677 // // Fast-path copy.
2678 // do {
2679 // *dest_ptr++ = *src_ptr++;
2680 // } while (src_ptr != end_ptr)
2681 // }
2682
2683 // Make sure `tmp` is not IP0, as it is clobbered by
2684 // ReadBarrierMarkRegX entry points in
2685 // ReadBarrierSystemArrayCopySlowPathARM64.
Roland Levillain1ca955d2017-04-13 19:34:30 +01002686 DCHECK(temps.IsAvailable(ip0));
Roland Levillain1663d162017-03-17 15:15:21 +00002687 temps.Exclude(ip0);
Roland Levillain0b671c02016-08-19 12:02:34 +01002688 Register tmp = temps.AcquireW();
Roland Levillain1663d162017-03-17 15:15:21 +00002689 DCHECK_NE(LocationFrom(tmp).reg(), IP0);
Roland Levillain1ca955d2017-04-13 19:34:30 +01002690 // Put IP0 back in the pool so that VIXL has at least one
2691 // scratch register available to emit macro-instructions (note
2692 // that IP1 is already used for `tmp`). Indeed some
2693 // macro-instructions used in GenSystemArrayCopyAddresses
2694 // (invoked hereunder) may require a scratch register (for
2695 // instance to emit a load with a large constant offset).
2696 temps.Include(ip0);
Roland Levillain1663d162017-03-17 15:15:21 +00002697
2698 // /* int32_t */ monitor = src->monitor_
2699 __ Ldr(tmp, HeapOperand(src.W(), monitor_offset));
2700 // /* LockWord */ lock_word = LockWord(monitor)
2701 static_assert(sizeof(LockWord) == sizeof(int32_t),
2702 "art::LockWord and int32_t have different sizes.");
2703
2704 // Introduce a dependency on the lock_word including rb_state,
2705 // to prevent load-load reordering, and without using
2706 // a memory barrier (which would be more expensive).
2707 // `src` is unchanged by this operation, but its value now depends
2708 // on `tmp`.
2709 __ Add(src.X(), src.X(), Operand(tmp.X(), LSR, 32));
2710
2711 // Compute base source address, base destination address, and end
2712 // source address for System.arraycopy* intrinsics in `src_base`,
2713 // `dst_base` and `src_end` respectively.
2714 // Note that `src_curr_addr` is computed from from `src` (and
2715 // `src_pos`) here, and thus honors the artificial dependency
2716 // of `src` on `tmp`.
2717 GenSystemArrayCopyAddresses(masm,
2718 type,
2719 src,
2720 src_pos,
2721 dest,
2722 dest_pos,
2723 length,
2724 src_curr_addr,
2725 dst_curr_addr,
2726 src_stop_addr);
2727
2728 // Slow path used to copy array when `src` is gray.
2729 SlowPathCodeARM64* read_barrier_slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01002730 new (codegen_->GetScopedAllocator()) ReadBarrierSystemArrayCopySlowPathARM64(
2731 invoke, LocationFrom(tmp));
Roland Levillain1663d162017-03-17 15:15:21 +00002732 codegen_->AddSlowPath(read_barrier_slow_path);
2733
2734 // Given the numeric representation, it's enough to check the low bit of the rb_state.
Roland Levillain14e5a292018-06-28 12:00:56 +01002735 static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
Roland Levillain1663d162017-03-17 15:15:21 +00002736 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
2737 __ Tbnz(tmp, LockWord::kReadBarrierStateShift, read_barrier_slow_path->GetEntryLabel());
2738
2739 // Fast-path copy.
2740 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2741 // poison/unpoison.
2742 vixl::aarch64::Label loop;
2743 __ Bind(&loop);
Roland Levillain0b671c02016-08-19 12:02:34 +01002744 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
2745 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
Roland Levillain1663d162017-03-17 15:15:21 +00002746 __ Cmp(src_curr_addr, src_stop_addr);
2747 __ B(&loop, ne);
2748
2749 __ Bind(read_barrier_slow_path->GetExitLabel());
2750 } else {
2751 // Non read barrier code.
2752 // Compute base source address, base destination address, and end
2753 // source address for System.arraycopy* intrinsics in `src_base`,
2754 // `dst_base` and `src_end` respectively.
2755 GenSystemArrayCopyAddresses(masm,
2756 type,
2757 src,
2758 src_pos,
2759 dest,
2760 dest_pos,
2761 length,
2762 src_curr_addr,
2763 dst_curr_addr,
2764 src_stop_addr);
2765 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2766 // poison/unpoison.
2767 vixl::aarch64::Label loop;
2768 __ Bind(&loop);
2769 {
2770 Register tmp = temps.AcquireW();
2771 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
2772 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
2773 }
2774 __ Cmp(src_curr_addr, src_stop_addr);
2775 __ B(&loop, ne);
Roland Levillain0b671c02016-08-19 12:02:34 +01002776 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002777 __ Bind(&done);
donghui.baic2ec9ad2016-03-10 14:02:55 +08002778 }
donghui.baic2ec9ad2016-03-10 14:02:55 +08002779 }
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002780
donghui.baic2ec9ad2016-03-10 14:02:55 +08002781 // We only need one card marking on the destination array.
2782 codegen_->MarkGCCard(dest.W(), Register(), /* value_can_be_null */ false);
2783
Roland Levillain0b671c02016-08-19 12:02:34 +01002784 __ Bind(intrinsic_slow_path->GetExitLabel());
donghui.baic2ec9ad2016-03-10 14:02:55 +08002785}
2786
Anton Kirilova3ffea22016-04-07 17:02:37 +01002787static void GenIsInfinite(LocationSummary* locations,
2788 bool is64bit,
Scott Wakeling97c72b72016-06-24 16:19:36 +01002789 MacroAssembler* masm) {
Anton Kirilova3ffea22016-04-07 17:02:37 +01002790 Operand infinity;
2791 Register out;
2792
2793 if (is64bit) {
2794 infinity = kPositiveInfinityDouble;
2795 out = XRegisterFrom(locations->Out());
2796 } else {
2797 infinity = kPositiveInfinityFloat;
2798 out = WRegisterFrom(locations->Out());
2799 }
2800
Scott Wakeling97c72b72016-06-24 16:19:36 +01002801 const Register zero = vixl::aarch64::Assembler::AppropriateZeroRegFor(out);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002802
2803 MoveFPToInt(locations, is64bit, masm);
2804 __ Eor(out, out, infinity);
2805 // We don't care about the sign bit, so shift left.
2806 __ Cmp(zero, Operand(out, LSL, 1));
2807 __ Cset(out, eq);
2808}
2809
2810void IntrinsicLocationsBuilderARM64::VisitFloatIsInfinite(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002811 CreateFPToIntLocations(allocator_, invoke);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002812}
2813
2814void IntrinsicCodeGeneratorARM64::VisitFloatIsInfinite(HInvoke* invoke) {
2815 GenIsInfinite(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
2816}
2817
2818void IntrinsicLocationsBuilderARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002819 CreateFPToIntLocations(allocator_, invoke);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002820}
2821
2822void IntrinsicCodeGeneratorARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
2823 GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
2824}
2825
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002826void IntrinsicLocationsBuilderARM64::VisitIntegerValueOf(HInvoke* invoke) {
2827 InvokeRuntimeCallingConvention calling_convention;
2828 IntrinsicVisitor::ComputeIntegerValueOfLocations(
2829 invoke,
2830 codegen_,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002831 calling_convention.GetReturnLocation(DataType::Type::kReference),
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002832 Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
2833}
2834
2835void IntrinsicCodeGeneratorARM64::VisitIntegerValueOf(HInvoke* invoke) {
Vladimir Marko6fd16062018-06-26 11:02:04 +01002836 IntrinsicVisitor::IntegerValueOfInfo info =
2837 IntrinsicVisitor::ComputeIntegerValueOfInfo(invoke, codegen_->GetCompilerOptions());
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002838 LocationSummary* locations = invoke->GetLocations();
2839 MacroAssembler* masm = GetVIXLAssembler();
2840
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002841 Register out = RegisterFrom(locations->Out(), DataType::Type::kReference);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002842 UseScratchRegisterScope temps(masm);
2843 Register temp = temps.AcquireW();
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002844 if (invoke->InputAt(0)->IsConstant()) {
2845 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
Vladimir Marko6fd16062018-06-26 11:02:04 +01002846 if (static_cast<uint32_t>(value - info.low) < info.length) {
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002847 // Just embed the j.l.Integer in the code.
Vladimir Marko6fd16062018-06-26 11:02:04 +01002848 DCHECK_NE(info.value_boot_image_reference, IntegerValueOfInfo::kInvalidReference);
2849 codegen_->LoadBootImageAddress(out, info.value_boot_image_reference);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002850 } else {
Vladimir Markoeebb8212018-06-05 14:57:24 +01002851 DCHECK(locations->CanCall());
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002852 // Allocate and initialize a new j.l.Integer.
2853 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
2854 // JIT object table.
Vladimir Marko6fd16062018-06-26 11:02:04 +01002855 codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(),
2856 info.integer_boot_image_offset);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002857 __ Mov(temp.W(), value);
2858 __ Str(temp.W(), HeapOperand(out.W(), info.value_offset));
2859 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2860 // one.
2861 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2862 }
2863 } else {
Vladimir Markoeebb8212018-06-05 14:57:24 +01002864 DCHECK(locations->CanCall());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002865 Register in = RegisterFrom(locations->InAt(0), DataType::Type::kInt32);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002866 // Check bounds of our cache.
2867 __ Add(out.W(), in.W(), -info.low);
Vladimir Markoeebb8212018-06-05 14:57:24 +01002868 __ Cmp(out.W(), info.length);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002869 vixl::aarch64::Label allocate, done;
2870 __ B(&allocate, hs);
2871 // If the value is within the bounds, load the j.l.Integer directly from the array.
Vladimir Marko6fd16062018-06-26 11:02:04 +01002872 codegen_->LoadBootImageAddress(temp, info.array_data_boot_image_reference);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002873 MemOperand source = HeapOperand(
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002874 temp, out.X(), LSL, DataType::SizeShift(DataType::Type::kReference));
2875 codegen_->Load(DataType::Type::kReference, out, source);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002876 codegen_->GetAssembler()->MaybeUnpoisonHeapReference(out);
2877 __ B(&done);
2878 __ Bind(&allocate);
2879 // Otherwise allocate and initialize a new j.l.Integer.
Vladimir Marko6fd16062018-06-26 11:02:04 +01002880 codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(),
2881 info.integer_boot_image_offset);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002882 __ Str(in.W(), HeapOperand(out.W(), info.value_offset));
2883 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2884 // one.
2885 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2886 __ Bind(&done);
2887 }
2888}
2889
Nicolas Geoffray365719c2017-03-08 13:11:50 +00002890void IntrinsicLocationsBuilderARM64::VisitThreadInterrupted(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002891 LocationSummary* locations =
2892 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Nicolas Geoffray365719c2017-03-08 13:11:50 +00002893 locations->SetOut(Location::RequiresRegister());
2894}
2895
2896void IntrinsicCodeGeneratorARM64::VisitThreadInterrupted(HInvoke* invoke) {
2897 MacroAssembler* masm = GetVIXLAssembler();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002898 Register out = RegisterFrom(invoke->GetLocations()->Out(), DataType::Type::kInt32);
Nicolas Geoffray365719c2017-03-08 13:11:50 +00002899 UseScratchRegisterScope temps(masm);
2900 Register temp = temps.AcquireX();
2901
2902 __ Add(temp, tr, Thread::InterruptedOffset<kArm64PointerSize>().Int32Value());
2903 __ Ldar(out.W(), MemOperand(temp));
2904
2905 vixl::aarch64::Label done;
2906 __ Cbz(out.W(), &done);
2907 __ Stlr(wzr, MemOperand(temp));
2908 __ Bind(&done);
2909}
2910
Hans Boehmc7b28de2018-03-09 17:05:28 -08002911void IntrinsicLocationsBuilderARM64::VisitReachabilityFence(HInvoke* invoke) {
2912 LocationSummary* locations =
2913 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
2914 locations->SetInAt(0, Location::Any());
2915}
2916
2917void IntrinsicCodeGeneratorARM64::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
2918
xueliang.zhongcb58b072017-10-13 12:06:56 +01002919void IntrinsicLocationsBuilderARM64::VisitCRC32Update(HInvoke* invoke) {
2920 if (!codegen_->GetInstructionSetFeatures().HasCRC()) {
2921 return;
2922 }
2923
2924 LocationSummary* locations = new (allocator_) LocationSummary(invoke,
2925 LocationSummary::kNoCall,
2926 kIntrinsified);
2927
2928 locations->SetInAt(0, Location::RequiresRegister());
2929 locations->SetInAt(1, Location::RequiresRegister());
Evgeny Astigeevichc01dc292018-12-12 15:32:57 +00002930 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
xueliang.zhongcb58b072017-10-13 12:06:56 +01002931}
2932
2933// Lower the invoke of CRC32.update(int crc, int b).
2934void IntrinsicCodeGeneratorARM64::VisitCRC32Update(HInvoke* invoke) {
2935 DCHECK(codegen_->GetInstructionSetFeatures().HasCRC());
2936
2937 MacroAssembler* masm = GetVIXLAssembler();
2938
2939 Register crc = InputRegisterAt(invoke, 0);
2940 Register val = InputRegisterAt(invoke, 1);
2941 Register out = OutputRegister(invoke);
2942
2943 // The general algorithm of the CRC32 calculation is:
2944 // crc = ~crc
2945 // result = crc32_for_byte(crc, b)
2946 // crc = ~result
2947 // It is directly lowered to three instructions.
Evgeny Astigeevichc01dc292018-12-12 15:32:57 +00002948
2949 UseScratchRegisterScope temps(masm);
2950 Register tmp = temps.AcquireSameSizeAs(out);
2951
2952 __ Mvn(tmp, crc);
2953 __ Crc32b(tmp, tmp, val);
2954 __ Mvn(out, tmp);
xueliang.zhongcb58b072017-10-13 12:06:56 +01002955}
2956
Evgeny Astigeevich15c5b972018-11-20 13:41:40 +00002957// The threshold for sizes of arrays to use the library provided implementation
2958// of CRC32.updateBytes instead of the intrinsic.
2959static constexpr int32_t kCRC32UpdateBytesThreshold = 64 * 1024;
2960
2961void IntrinsicLocationsBuilderARM64::VisitCRC32UpdateBytes(HInvoke* invoke) {
2962 if (!codegen_->GetInstructionSetFeatures().HasCRC()) {
2963 return;
2964 }
2965
2966 LocationSummary* locations
2967 = new (allocator_) LocationSummary(invoke,
2968 LocationSummary::kCallOnSlowPath,
2969 kIntrinsified);
2970
2971 locations->SetInAt(0, Location::RequiresRegister());
2972 locations->SetInAt(1, Location::RequiresRegister());
2973 locations->SetInAt(2, Location::RegisterOrConstant(invoke->InputAt(2)));
2974 locations->SetInAt(3, Location::RequiresRegister());
2975 locations->AddTemp(Location::RequiresRegister());
2976 locations->SetOut(Location::RequiresRegister());
2977}
2978
2979// Lower the invoke of CRC32.updateBytes(int crc, byte[] b, int off, int len)
2980//
2981// Note: The intrinsic is not used if len exceeds a threshold.
2982void IntrinsicCodeGeneratorARM64::VisitCRC32UpdateBytes(HInvoke* invoke) {
2983 DCHECK(codegen_->GetInstructionSetFeatures().HasCRC());
2984
2985 auto masm = GetVIXLAssembler();
2986 auto locations = invoke->GetLocations();
2987
2988 auto slow_path =
2989 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARM64(invoke);
2990 codegen_->AddSlowPath(slow_path);
2991
2992 Register length = WRegisterFrom(locations->InAt(3));
2993 __ Cmp(length, kCRC32UpdateBytesThreshold);
2994 __ B(slow_path->GetEntryLabel(), hi);
2995
2996 const uint32_t array_data_offset =
2997 mirror::Array::DataOffset(Primitive::kPrimByte).Uint32Value();
2998 Register ptr = XRegisterFrom(locations->GetTemp(0));
2999 Register array = XRegisterFrom(locations->InAt(1));
3000 auto offset = locations->InAt(2);
3001 if (offset.IsConstant()) {
3002 int32_t offset_value = offset.GetConstant()->AsIntConstant()->GetValue();
3003 __ Add(ptr, array, array_data_offset + offset_value);
3004 } else {
3005 __ Add(ptr, array, array_data_offset);
3006 __ Add(ptr, ptr, XRegisterFrom(offset));
3007 }
3008
3009 // The algorithm of CRC32 of bytes is:
3010 // crc = ~crc
3011 // process a few first bytes to make the array 8-byte aligned
3012 // while array has 8 bytes do:
3013 // crc = crc32_of_8bytes(crc, 8_bytes(array))
3014 // if array has 4 bytes:
3015 // crc = crc32_of_4bytes(crc, 4_bytes(array))
3016 // if array has 2 bytes:
3017 // crc = crc32_of_2bytes(crc, 2_bytes(array))
3018 // if array has a byte:
3019 // crc = crc32_of_byte(crc, 1_byte(array))
3020 // crc = ~crc
3021
3022 vixl::aarch64::Label loop, done;
3023 vixl::aarch64::Label process_4bytes, process_2bytes, process_1byte;
3024 vixl::aarch64::Label aligned2, aligned4, aligned8;
3025
3026 // Use VIXL scratch registers as the VIXL macro assembler won't use them in
3027 // instructions below.
3028 UseScratchRegisterScope temps(masm);
3029 Register len = temps.AcquireW();
3030 Register array_elem = temps.AcquireW();
3031
3032 Register out = WRegisterFrom(locations->Out());
3033 __ Mvn(out, WRegisterFrom(locations->InAt(0)));
3034 __ Mov(len, length);
3035
3036 __ Tbz(ptr, 0, &aligned2);
3037 __ Subs(len, len, 1);
3038 __ B(&done, lo);
3039 __ Ldrb(array_elem, MemOperand(ptr, 1, PostIndex));
3040 __ Crc32b(out, out, array_elem);
3041
3042 __ Bind(&aligned2);
3043 __ Tbz(ptr, 1, &aligned4);
3044 __ Subs(len, len, 2);
3045 __ B(&process_1byte, lo);
3046 __ Ldrh(array_elem, MemOperand(ptr, 2, PostIndex));
3047 __ Crc32h(out, out, array_elem);
3048
3049 __ Bind(&aligned4);
3050 __ Tbz(ptr, 2, &aligned8);
3051 __ Subs(len, len, 4);
3052 __ B(&process_2bytes, lo);
3053 __ Ldr(array_elem, MemOperand(ptr, 4, PostIndex));
3054 __ Crc32w(out, out, array_elem);
3055
3056 __ Bind(&aligned8);
3057 __ Subs(len, len, 8);
3058 // If len < 8 go to process data by 4 bytes, 2 bytes and a byte.
3059 __ B(&process_4bytes, lo);
3060
3061 // The main loop processing data by 8 bytes.
3062 __ Bind(&loop);
3063 __ Ldr(array_elem.X(), MemOperand(ptr, 8, PostIndex));
3064 __ Subs(len, len, 8);
3065 __ Crc32x(out, out, array_elem.X());
3066 // if len >= 8, process the next 8 bytes.
3067 __ B(&loop, hs);
3068
3069 // Process the data which is less than 8 bytes.
3070 // The code generated below works with values of len
3071 // which come in the range [-8, 0].
3072 // The first three bits are used to detect whether 4 bytes or 2 bytes or
3073 // a byte can be processed.
3074 // The checking order is from bit 2 to bit 0:
3075 // bit 2 is set: at least 4 bytes available
3076 // bit 1 is set: at least 2 bytes available
3077 // bit 0 is set: at least a byte available
3078 __ Bind(&process_4bytes);
3079 // Goto process_2bytes if less than four bytes available
3080 __ Tbz(len, 2, &process_2bytes);
3081 __ Ldr(array_elem, MemOperand(ptr, 4, PostIndex));
3082 __ Crc32w(out, out, array_elem);
3083
3084 __ Bind(&process_2bytes);
3085 // Goto process_1bytes if less than two bytes available
3086 __ Tbz(len, 1, &process_1byte);
3087 __ Ldrh(array_elem, MemOperand(ptr, 2, PostIndex));
3088 __ Crc32h(out, out, array_elem);
3089
3090 __ Bind(&process_1byte);
3091 // Goto done if no bytes available
3092 __ Tbz(len, 0, &done);
3093 __ Ldrb(array_elem, MemOperand(ptr));
3094 __ Crc32b(out, out, array_elem);
3095
3096 __ Bind(&done);
3097 __ Mvn(out, out);
3098
3099 __ Bind(slow_path->GetExitLabel());
3100}
3101
Vladimir Markod254f5c2017-06-02 15:18:36 +00003102UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
Andreas Gampe878d58c2015-01-15 23:24:00 -08003103
Aart Bikff7d89c2016-11-07 08:49:28 -08003104UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf);
3105UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08003106UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferAppend);
3107UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferLength);
3108UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferToString);
3109UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderAppend);
3110UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderLength);
3111UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08003112
Aart Bik0e54c012016-03-04 12:08:31 -08003113// 1.8.
3114UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt)
3115UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong)
3116UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt)
3117UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong)
3118UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08003119
Aart Bik2f9fcc92016-03-01 15:16:54 -08003120UNREACHABLE_INTRINSICS(ARM64)
Roland Levillain4d027112015-07-01 15:41:14 +01003121
3122#undef __
3123
Andreas Gampe878d58c2015-01-15 23:24:00 -08003124} // namespace arm64
3125} // namespace art