blob: 0835060f5c719e6843d45ce136a21e5adfa6e23f [file] [log] [blame]
Anton Kirilov5ec62182016-10-13 20:16:02 +01001/*
2 * Copyright (C) 2016 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_arm_vixl.h"
18
19#include "arch/arm/instruction_set_features_arm.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080020#include "art_method.h"
Anton Kirilov5ec62182016-10-13 20:16:02 +010021#include "code_generator_arm_vixl.h"
22#include "common_arm.h"
Andreas Gampe09659c22017-09-18 18:23:32 -070023#include "heap_poisoning.h"
Anton Kirilov5ec62182016-10-13 20:16:02 +010024#include "lock_word.h"
25#include "mirror/array-inl.h"
Andreas Gampec15a2f42017-04-21 12:09:39 -070026#include "mirror/object_array-inl.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080027#include "mirror/reference.h"
Vladimir Marko5924a4a2018-05-29 17:40:41 +010028#include "mirror/string-inl.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080029#include "scoped_thread_state_change-inl.h"
Andreas Gampeb486a982017-06-01 13:45:54 -070030#include "thread-current-inl.h"
Anton Kirilov5ec62182016-10-13 20:16:02 +010031
32#include "aarch32/constants-aarch32.h"
33
34namespace art {
35namespace arm {
36
37#define __ assembler->GetVIXLAssembler()->
38
39using helpers::DRegisterFrom;
40using helpers::HighRegisterFrom;
41using helpers::InputDRegisterAt;
42using helpers::InputRegisterAt;
43using helpers::InputSRegisterAt;
44using helpers::InputVRegisterAt;
45using helpers::Int32ConstantFrom;
46using helpers::LocationFrom;
47using helpers::LowRegisterFrom;
48using helpers::LowSRegisterFrom;
xueliang.zhong53463ba2017-02-16 15:18:03 +000049using helpers::HighSRegisterFrom;
Anton Kirilov5ec62182016-10-13 20:16:02 +010050using helpers::OutputDRegister;
xueliang.zhongc032e742016-03-28 16:44:32 +010051using helpers::OutputSRegister;
Anton Kirilov5ec62182016-10-13 20:16:02 +010052using helpers::OutputRegister;
53using helpers::OutputVRegister;
54using helpers::RegisterFrom;
55using helpers::SRegisterFrom;
xueliang.zhongc032e742016-03-28 16:44:32 +010056using helpers::DRegisterFromS;
Anton Kirilov5ec62182016-10-13 20:16:02 +010057
58using namespace vixl::aarch32; // NOLINT(build/namespaces)
59
Artem Serov0fb37192016-12-06 18:13:40 +000060using vixl::ExactAssemblyScope;
61using vixl::CodeBufferCheckScope;
62
Anton Kirilov5ec62182016-10-13 20:16:02 +010063ArmVIXLAssembler* IntrinsicCodeGeneratorARMVIXL::GetAssembler() {
64 return codegen_->GetAssembler();
65}
66
67ArenaAllocator* IntrinsicCodeGeneratorARMVIXL::GetAllocator() {
Vladimir Markoca6fff82017-10-03 14:49:14 +010068 return codegen_->GetGraph()->GetAllocator();
Anton Kirilov5ec62182016-10-13 20:16:02 +010069}
70
71// Default slow-path for fallback (calling the managed code to handle the intrinsic) in an
72// intrinsified call. This will copy the arguments into the positions for a regular call.
73//
74// Note: The actual parameters are required to be in the locations given by the invoke's location
75// summary. If an intrinsic modifies those locations before a slowpath call, they must be
76// restored!
77//
78// Note: If an invoke wasn't sharpened, we will put down an invoke-virtual here. That's potentially
79// sub-optimal (compared to a direct pointer call), but this is a slow-path.
80
81class IntrinsicSlowPathARMVIXL : public SlowPathCodeARMVIXL {
82 public:
83 explicit IntrinsicSlowPathARMVIXL(HInvoke* invoke)
84 : SlowPathCodeARMVIXL(invoke), invoke_(invoke) {}
85
86 Location MoveArguments(CodeGenerator* codegen) {
Artem Serovd4cc5b22016-11-04 11:19:09 +000087 InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
Anton Kirilov5ec62182016-10-13 20:16:02 +010088 IntrinsicVisitor::MoveArguments(invoke_, codegen, &calling_convention_visitor);
89 return calling_convention_visitor.GetMethodLocation();
90 }
91
92 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
93 ArmVIXLAssembler* assembler = down_cast<ArmVIXLAssembler*>(codegen->GetAssembler());
94 __ Bind(GetEntryLabel());
95
96 SaveLiveRegisters(codegen, invoke_->GetLocations());
97
98 Location method_loc = MoveArguments(codegen);
99
100 if (invoke_->IsInvokeStaticOrDirect()) {
Vladimir Markoe7197bf2017-06-02 17:00:23 +0100101 codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), method_loc, this);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100102 } else {
Vladimir Markoe7197bf2017-06-02 17:00:23 +0100103 codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), method_loc, this);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100104 }
Anton Kirilov5ec62182016-10-13 20:16:02 +0100105
106 // Copy the result back to the expected output.
107 Location out = invoke_->GetLocations()->Out();
108 if (out.IsValid()) {
109 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
110 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
111 codegen->MoveFromReturnRegister(out, invoke_->GetType());
112 }
113
114 RestoreLiveRegisters(codegen, invoke_->GetLocations());
115 __ B(GetExitLabel());
116 }
117
118 const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPath"; }
119
120 private:
121 // The instruction where this slow path is happening.
122 HInvoke* const invoke_;
123
124 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARMVIXL);
125};
126
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000127// Compute base address for the System.arraycopy intrinsic in `base`.
128static void GenSystemArrayCopyBaseAddress(ArmVIXLAssembler* assembler,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100129 DataType::Type type,
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000130 const vixl32::Register& array,
131 const Location& pos,
132 const vixl32::Register& base) {
133 // This routine is only used by the SystemArrayCopy intrinsic at the
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100134 // moment. We can allow DataType::Type::kReference as `type` to implement
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000135 // the SystemArrayCopyChar intrinsic.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100136 DCHECK_EQ(type, DataType::Type::kReference);
137 const int32_t element_size = DataType::Size(type);
138 const uint32_t element_size_shift = DataType::SizeShift(type);
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000139 const uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
140
141 if (pos.IsConstant()) {
142 int32_t constant = Int32ConstantFrom(pos);
143 __ Add(base, array, element_size * constant + data_offset);
144 } else {
145 __ Add(base, array, Operand(RegisterFrom(pos), vixl32::LSL, element_size_shift));
146 __ Add(base, base, data_offset);
147 }
148}
149
150// Compute end address for the System.arraycopy intrinsic in `end`.
151static void GenSystemArrayCopyEndAddress(ArmVIXLAssembler* assembler,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100152 DataType::Type type,
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000153 const Location& copy_length,
154 const vixl32::Register& base,
155 const vixl32::Register& end) {
156 // This routine is only used by the SystemArrayCopy intrinsic at the
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100157 // moment. We can allow DataType::Type::kReference as `type` to implement
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000158 // the SystemArrayCopyChar intrinsic.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100159 DCHECK_EQ(type, DataType::Type::kReference);
160 const int32_t element_size = DataType::Size(type);
161 const uint32_t element_size_shift = DataType::SizeShift(type);
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000162
163 if (copy_length.IsConstant()) {
164 int32_t constant = Int32ConstantFrom(copy_length);
165 __ Add(end, base, element_size * constant);
166 } else {
167 __ Add(end, base, Operand(RegisterFrom(copy_length), vixl32::LSL, element_size_shift));
168 }
169}
170
Anton Kirilov5ec62182016-10-13 20:16:02 +0100171// Slow path implementing the SystemArrayCopy intrinsic copy loop with read barriers.
172class ReadBarrierSystemArrayCopySlowPathARMVIXL : public SlowPathCodeARMVIXL {
173 public:
174 explicit ReadBarrierSystemArrayCopySlowPathARMVIXL(HInstruction* instruction)
175 : SlowPathCodeARMVIXL(instruction) {
176 DCHECK(kEmitCompilerReadBarrier);
177 DCHECK(kUseBakerReadBarrier);
178 }
179
180 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
181 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
182 ArmVIXLAssembler* assembler = arm_codegen->GetAssembler();
183 LocationSummary* locations = instruction_->GetLocations();
184 DCHECK(locations->CanCall());
185 DCHECK(instruction_->IsInvokeStaticOrDirect())
186 << "Unexpected instruction in read barrier arraycopy slow path: "
187 << instruction_->DebugName();
188 DCHECK(instruction_->GetLocations()->Intrinsified());
189 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kSystemArrayCopy);
190
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100191 DataType::Type type = DataType::Type::kReference;
192 const int32_t element_size = DataType::Size(type);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100193
194 vixl32::Register dest = InputRegisterAt(instruction_, 2);
195 Location dest_pos = locations->InAt(3);
196 vixl32::Register src_curr_addr = RegisterFrom(locations->GetTemp(0));
197 vixl32::Register dst_curr_addr = RegisterFrom(locations->GetTemp(1));
198 vixl32::Register src_stop_addr = RegisterFrom(locations->GetTemp(2));
199 vixl32::Register tmp = RegisterFrom(locations->GetTemp(3));
200
201 __ Bind(GetEntryLabel());
202 // Compute the base destination address in `dst_curr_addr`.
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000203 GenSystemArrayCopyBaseAddress(assembler, type, dest, dest_pos, dst_curr_addr);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100204
205 vixl32::Label loop;
206 __ Bind(&loop);
207 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
208 assembler->MaybeUnpoisonHeapReference(tmp);
209 // TODO: Inline the mark bit check before calling the runtime?
210 // tmp = ReadBarrier::Mark(tmp);
211 // No need to save live registers; it's taken care of by the
212 // entrypoint. Also, there is no need to update the stack mask,
213 // as this runtime call will not trigger a garbage collection.
214 // (See ReadBarrierMarkSlowPathARM::EmitNativeCode for more
215 // explanations.)
216 DCHECK(!tmp.IsSP());
217 DCHECK(!tmp.IsLR());
218 DCHECK(!tmp.IsPC());
219 // IP is used internally by the ReadBarrierMarkRegX entry point
220 // as a temporary (and not preserved). It thus cannot be used by
221 // any live register in this slow path.
222 DCHECK(!src_curr_addr.Is(ip));
223 DCHECK(!dst_curr_addr.Is(ip));
224 DCHECK(!src_stop_addr.Is(ip));
225 DCHECK(!tmp.Is(ip));
226 DCHECK(tmp.IsRegister()) << tmp;
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000227 // TODO: Load the entrypoint once before the loop, instead of
228 // loading it at every iteration.
Anton Kirilov5ec62182016-10-13 20:16:02 +0100229 int32_t entry_point_offset =
Roland Levillain97c46462017-05-11 14:04:03 +0100230 Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(tmp.GetCode());
Anton Kirilov5ec62182016-10-13 20:16:02 +0100231 // This runtime call does not require a stack map.
232 arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
233 assembler->MaybePoisonHeapReference(tmp);
234 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
235 __ Cmp(src_curr_addr, src_stop_addr);
Artem Serov517d9f62016-12-12 15:51:15 +0000236 __ B(ne, &loop, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100237 __ B(GetExitLabel());
238 }
239
240 const char* GetDescription() const OVERRIDE {
241 return "ReadBarrierSystemArrayCopySlowPathARMVIXL";
242 }
243
244 private:
245 DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathARMVIXL);
246};
247
248IntrinsicLocationsBuilderARMVIXL::IntrinsicLocationsBuilderARMVIXL(CodeGeneratorARMVIXL* codegen)
Vladimir Markoca6fff82017-10-03 14:49:14 +0100249 : allocator_(codegen->GetGraph()->GetAllocator()),
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000250 codegen_(codegen),
Anton Kirilov5ec62182016-10-13 20:16:02 +0100251 assembler_(codegen->GetAssembler()),
252 features_(codegen->GetInstructionSetFeatures()) {}
253
254bool IntrinsicLocationsBuilderARMVIXL::TryDispatch(HInvoke* invoke) {
255 Dispatch(invoke);
256 LocationSummary* res = invoke->GetLocations();
257 if (res == nullptr) {
258 return false;
259 }
260 return res->Intrinsified();
261}
262
Vladimir Markoca6fff82017-10-03 14:49:14 +0100263static void CreateFPToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
264 LocationSummary* locations =
265 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100266 locations->SetInAt(0, Location::RequiresFpuRegister());
267 locations->SetOut(Location::RequiresRegister());
268}
269
Vladimir Markoca6fff82017-10-03 14:49:14 +0100270static void CreateIntToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
271 LocationSummary* locations =
272 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100273 locations->SetInAt(0, Location::RequiresRegister());
274 locations->SetOut(Location::RequiresFpuRegister());
275}
276
277static void MoveFPToInt(LocationSummary* locations, bool is64bit, ArmVIXLAssembler* assembler) {
278 Location input = locations->InAt(0);
279 Location output = locations->Out();
280 if (is64bit) {
281 __ Vmov(LowRegisterFrom(output), HighRegisterFrom(output), DRegisterFrom(input));
282 } else {
283 __ Vmov(RegisterFrom(output), SRegisterFrom(input));
284 }
285}
286
287static void MoveIntToFP(LocationSummary* locations, bool is64bit, ArmVIXLAssembler* assembler) {
288 Location input = locations->InAt(0);
289 Location output = locations->Out();
290 if (is64bit) {
291 __ Vmov(DRegisterFrom(output), LowRegisterFrom(input), HighRegisterFrom(input));
292 } else {
293 __ Vmov(SRegisterFrom(output), RegisterFrom(input));
294 }
295}
296
297void IntrinsicLocationsBuilderARMVIXL::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100298 CreateFPToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100299}
300void IntrinsicLocationsBuilderARMVIXL::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100301 CreateIntToFPLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100302}
303
304void IntrinsicCodeGeneratorARMVIXL::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
305 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
306}
307void IntrinsicCodeGeneratorARMVIXL::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
308 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
309}
310
311void IntrinsicLocationsBuilderARMVIXL::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100312 CreateFPToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100313}
314void IntrinsicLocationsBuilderARMVIXL::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100315 CreateIntToFPLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100316}
317
318void IntrinsicCodeGeneratorARMVIXL::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
319 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
320}
321void IntrinsicCodeGeneratorARMVIXL::VisitFloatIntBitsToFloat(HInvoke* invoke) {
322 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
323}
324
Vladimir Markoca6fff82017-10-03 14:49:14 +0100325static void CreateIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
326 LocationSummary* locations =
327 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100328 locations->SetInAt(0, Location::RequiresRegister());
329 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
330}
331
Vladimir Markoca6fff82017-10-03 14:49:14 +0100332static void CreateLongToLongLocationsWithOverlap(ArenaAllocator* allocator, HInvoke* invoke) {
333 LocationSummary* locations =
334 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +0100335 locations->SetInAt(0, Location::RequiresRegister());
336 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
337}
338
Vladimir Markoca6fff82017-10-03 14:49:14 +0100339static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
340 LocationSummary* locations =
341 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100342 locations->SetInAt(0, Location::RequiresFpuRegister());
343 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
344}
345
Anton Kirilov6f644202017-02-27 18:29:45 +0000346static void GenNumberOfLeadingZeros(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100347 DataType::Type type,
Anton Kirilov6f644202017-02-27 18:29:45 +0000348 CodeGeneratorARMVIXL* codegen) {
349 ArmVIXLAssembler* assembler = codegen->GetAssembler();
350 LocationSummary* locations = invoke->GetLocations();
Anton Kirilov5ec62182016-10-13 20:16:02 +0100351 Location in = locations->InAt(0);
352 vixl32::Register out = RegisterFrom(locations->Out());
353
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100354 DCHECK((type == DataType::Type::kInt32) || (type == DataType::Type::kInt64));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100355
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100356 if (type == DataType::Type::kInt64) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100357 vixl32::Register in_reg_lo = LowRegisterFrom(in);
358 vixl32::Register in_reg_hi = HighRegisterFrom(in);
359 vixl32::Label end;
Anton Kirilov6f644202017-02-27 18:29:45 +0000360 vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &end);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100361 __ Clz(out, in_reg_hi);
Anton Kirilov6f644202017-02-27 18:29:45 +0000362 __ CompareAndBranchIfNonZero(in_reg_hi, final_label, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100363 __ Clz(out, in_reg_lo);
364 __ Add(out, out, 32);
Anton Kirilov6f644202017-02-27 18:29:45 +0000365 if (end.IsReferenced()) {
366 __ Bind(&end);
367 }
Anton Kirilov5ec62182016-10-13 20:16:02 +0100368 } else {
369 __ Clz(out, RegisterFrom(in));
370 }
371}
372
373void IntrinsicLocationsBuilderARMVIXL::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100374 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100375}
376
377void IntrinsicCodeGeneratorARMVIXL::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100378 GenNumberOfLeadingZeros(invoke, DataType::Type::kInt32, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100379}
380
381void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100382 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100383}
384
385void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100386 GenNumberOfLeadingZeros(invoke, DataType::Type::kInt64, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100387}
388
Anton Kirilov6f644202017-02-27 18:29:45 +0000389static void GenNumberOfTrailingZeros(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100390 DataType::Type type,
Anton Kirilov6f644202017-02-27 18:29:45 +0000391 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100392 DCHECK((type == DataType::Type::kInt32) || (type == DataType::Type::kInt64));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100393
Anton Kirilov6f644202017-02-27 18:29:45 +0000394 ArmVIXLAssembler* assembler = codegen->GetAssembler();
395 LocationSummary* locations = invoke->GetLocations();
Anton Kirilov5ec62182016-10-13 20:16:02 +0100396 vixl32::Register out = RegisterFrom(locations->Out());
397
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100398 if (type == DataType::Type::kInt64) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100399 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
400 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
401 vixl32::Label end;
Anton Kirilov6f644202017-02-27 18:29:45 +0000402 vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &end);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100403 __ Rbit(out, in_reg_lo);
404 __ Clz(out, out);
Anton Kirilov6f644202017-02-27 18:29:45 +0000405 __ CompareAndBranchIfNonZero(in_reg_lo, final_label, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100406 __ Rbit(out, in_reg_hi);
407 __ Clz(out, out);
408 __ Add(out, out, 32);
Anton Kirilov6f644202017-02-27 18:29:45 +0000409 if (end.IsReferenced()) {
410 __ Bind(&end);
411 }
Anton Kirilov5ec62182016-10-13 20:16:02 +0100412 } else {
413 vixl32::Register in = RegisterFrom(locations->InAt(0));
414 __ Rbit(out, in);
415 __ Clz(out, out);
416 }
417}
418
419void IntrinsicLocationsBuilderARMVIXL::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100420 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100421}
422
423void IntrinsicCodeGeneratorARMVIXL::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100424 GenNumberOfTrailingZeros(invoke, DataType::Type::kInt32, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100425}
426
427void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100428 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100429}
430
431void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100432 GenNumberOfTrailingZeros(invoke, DataType::Type::kInt64, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100433}
434
Anton Kirilov5ec62182016-10-13 20:16:02 +0100435void IntrinsicLocationsBuilderARMVIXL::VisitMathSqrt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100436 CreateFPToFPLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100437}
438
439void IntrinsicCodeGeneratorARMVIXL::VisitMathSqrt(HInvoke* invoke) {
440 ArmVIXLAssembler* assembler = GetAssembler();
441 __ Vsqrt(OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
442}
443
xueliang.zhong6099d5e2016-04-20 18:44:56 +0100444void IntrinsicLocationsBuilderARMVIXL::VisitMathRint(HInvoke* invoke) {
445 if (features_.HasARMv8AInstructions()) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100446 CreateFPToFPLocations(allocator_, invoke);
xueliang.zhong6099d5e2016-04-20 18:44:56 +0100447 }
448}
449
450void IntrinsicCodeGeneratorARMVIXL::VisitMathRint(HInvoke* invoke) {
451 DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
452 ArmVIXLAssembler* assembler = GetAssembler();
453 __ Vrintn(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
454}
455
xueliang.zhong53463ba2017-02-16 15:18:03 +0000456void IntrinsicLocationsBuilderARMVIXL::VisitMathRoundFloat(HInvoke* invoke) {
457 if (features_.HasARMv8AInstructions()) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100458 LocationSummary* locations =
459 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
xueliang.zhong53463ba2017-02-16 15:18:03 +0000460 locations->SetInAt(0, Location::RequiresFpuRegister());
461 locations->SetOut(Location::RequiresRegister());
462 locations->AddTemp(Location::RequiresFpuRegister());
463 }
464}
465
466void IntrinsicCodeGeneratorARMVIXL::VisitMathRoundFloat(HInvoke* invoke) {
467 DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
468
469 ArmVIXLAssembler* assembler = GetAssembler();
470 vixl32::SRegister in_reg = InputSRegisterAt(invoke, 0);
471 vixl32::Register out_reg = OutputRegister(invoke);
472 vixl32::SRegister temp1 = LowSRegisterFrom(invoke->GetLocations()->GetTemp(0));
473 vixl32::SRegister temp2 = HighSRegisterFrom(invoke->GetLocations()->GetTemp(0));
474 vixl32::Label done;
475 vixl32::Label* final_label = codegen_->GetFinalLabel(invoke, &done);
476
477 // Round to nearest integer, ties away from zero.
478 __ Vcvta(S32, F32, temp1, in_reg);
479 __ Vmov(out_reg, temp1);
480
481 // For positive, zero or NaN inputs, rounding is done.
482 __ Cmp(out_reg, 0);
483 __ B(ge, final_label, /* far_target */ false);
484
485 // Handle input < 0 cases.
486 // If input is negative but not a tie, previous result (round to nearest) is valid.
487 // If input is a negative tie, change rounding direction to positive infinity, out_reg += 1.
488 __ Vrinta(F32, F32, temp1, in_reg);
489 __ Vmov(temp2, 0.5);
490 __ Vsub(F32, temp1, in_reg, temp1);
491 __ Vcmp(F32, temp1, temp2);
492 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
493 {
494 // Use ExactAsemblyScope here because we are using IT.
495 ExactAssemblyScope it_scope(assembler->GetVIXLAssembler(),
496 2 * kMaxInstructionSizeInBytes,
497 CodeBufferCheckScope::kMaximumSize);
498 __ it(eq);
499 __ add(eq, out_reg, out_reg, 1);
500 }
501
502 if (done.IsReferenced()) {
503 __ Bind(&done);
504 }
505}
506
Anton Kirilov5ec62182016-10-13 20:16:02 +0100507void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100508 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100509}
510
511void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) {
512 ArmVIXLAssembler* assembler = GetAssembler();
513 // Ignore upper 4B of long address.
Scott Wakelingb77051e2016-11-21 19:46:00 +0000514 __ Ldrsb(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100515}
516
517void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100518 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100519}
520
521void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) {
522 ArmVIXLAssembler* assembler = GetAssembler();
523 // Ignore upper 4B of long address.
Scott Wakelingb77051e2016-11-21 19:46:00 +0000524 __ Ldr(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100525}
526
527void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekLongNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100528 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100529}
530
531void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekLongNative(HInvoke* invoke) {
532 ArmVIXLAssembler* assembler = GetAssembler();
533 // Ignore upper 4B of long address.
534 vixl32::Register addr = LowRegisterFrom(invoke->GetLocations()->InAt(0));
535 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
536 // exception. So we can't use ldrd as addr may be unaligned.
537 vixl32::Register lo = LowRegisterFrom(invoke->GetLocations()->Out());
538 vixl32::Register hi = HighRegisterFrom(invoke->GetLocations()->Out());
539 if (addr.Is(lo)) {
540 __ Ldr(hi, MemOperand(addr, 4));
Scott Wakelingb77051e2016-11-21 19:46:00 +0000541 __ Ldr(lo, MemOperand(addr));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100542 } else {
Scott Wakelingb77051e2016-11-21 19:46:00 +0000543 __ Ldr(lo, MemOperand(addr));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100544 __ Ldr(hi, MemOperand(addr, 4));
545 }
546}
547
548void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekShortNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100549 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100550}
551
552void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekShortNative(HInvoke* invoke) {
553 ArmVIXLAssembler* assembler = GetAssembler();
554 // Ignore upper 4B of long address.
Scott Wakelingb77051e2016-11-21 19:46:00 +0000555 __ Ldrsh(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100556}
557
Vladimir Markoca6fff82017-10-03 14:49:14 +0100558static void CreateIntIntToVoidLocations(ArenaAllocator* allocator, HInvoke* invoke) {
559 LocationSummary* locations =
560 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100561 locations->SetInAt(0, Location::RequiresRegister());
562 locations->SetInAt(1, Location::RequiresRegister());
563}
564
565void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeByte(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100566 CreateIntIntToVoidLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100567}
568
569void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeByte(HInvoke* invoke) {
570 ArmVIXLAssembler* assembler = GetAssembler();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000571 __ Strb(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100572}
573
574void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100575 CreateIntIntToVoidLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100576}
577
578void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) {
579 ArmVIXLAssembler* assembler = GetAssembler();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000580 __ Str(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100581}
582
583void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeLongNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100584 CreateIntIntToVoidLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100585}
586
587void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeLongNative(HInvoke* invoke) {
588 ArmVIXLAssembler* assembler = GetAssembler();
589 // Ignore upper 4B of long address.
590 vixl32::Register addr = LowRegisterFrom(invoke->GetLocations()->InAt(0));
591 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
592 // exception. So we can't use ldrd as addr may be unaligned.
Scott Wakelingb77051e2016-11-21 19:46:00 +0000593 __ Str(LowRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100594 __ Str(HighRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr, 4));
595}
596
597void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeShortNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100598 CreateIntIntToVoidLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100599}
600
601void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeShortNative(HInvoke* invoke) {
602 ArmVIXLAssembler* assembler = GetAssembler();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000603 __ Strh(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100604}
605
606void IntrinsicLocationsBuilderARMVIXL::VisitThreadCurrentThread(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100607 LocationSummary* locations =
608 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100609 locations->SetOut(Location::RequiresRegister());
610}
611
612void IntrinsicCodeGeneratorARMVIXL::VisitThreadCurrentThread(HInvoke* invoke) {
613 ArmVIXLAssembler* assembler = GetAssembler();
614 __ Ldr(OutputRegister(invoke),
615 MemOperand(tr, Thread::PeerOffset<kArmPointerSize>().Int32Value()));
616}
617
618static void GenUnsafeGet(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100619 DataType::Type type,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100620 bool is_volatile,
621 CodeGeneratorARMVIXL* codegen) {
622 LocationSummary* locations = invoke->GetLocations();
623 ArmVIXLAssembler* assembler = codegen->GetAssembler();
624 Location base_loc = locations->InAt(1);
625 vixl32::Register base = InputRegisterAt(invoke, 1); // Object pointer.
626 Location offset_loc = locations->InAt(2);
627 vixl32::Register offset = LowRegisterFrom(offset_loc); // Long offset, lo part only.
628 Location trg_loc = locations->Out();
629
630 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100631 case DataType::Type::kInt32: {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100632 vixl32::Register trg = RegisterFrom(trg_loc);
633 __ Ldr(trg, MemOperand(base, offset));
634 if (is_volatile) {
635 __ Dmb(vixl32::ISH);
636 }
637 break;
638 }
639
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100640 case DataType::Type::kReference: {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100641 vixl32::Register trg = RegisterFrom(trg_loc);
642 if (kEmitCompilerReadBarrier) {
643 if (kUseBakerReadBarrier) {
644 Location temp = locations->GetTemp(0);
645 codegen->GenerateReferenceLoadWithBakerReadBarrier(
646 invoke, trg_loc, base, 0U, offset_loc, TIMES_1, temp, /* needs_null_check */ false);
647 if (is_volatile) {
648 __ Dmb(vixl32::ISH);
649 }
650 } else {
651 __ Ldr(trg, MemOperand(base, offset));
652 if (is_volatile) {
653 __ Dmb(vixl32::ISH);
654 }
655 codegen->GenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
656 }
657 } else {
658 __ Ldr(trg, MemOperand(base, offset));
659 if (is_volatile) {
660 __ Dmb(vixl32::ISH);
661 }
662 assembler->MaybeUnpoisonHeapReference(trg);
663 }
664 break;
665 }
666
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100667 case DataType::Type::kInt64: {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100668 vixl32::Register trg_lo = LowRegisterFrom(trg_loc);
669 vixl32::Register trg_hi = HighRegisterFrom(trg_loc);
670 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
Artem Serov657022c2016-11-23 14:19:38 +0000671 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
672 const vixl32::Register temp_reg = temps.Acquire();
673 __ Add(temp_reg, base, offset);
674 __ Ldrexd(trg_lo, trg_hi, MemOperand(temp_reg));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100675 } else {
676 __ Ldrd(trg_lo, trg_hi, MemOperand(base, offset));
677 }
678 if (is_volatile) {
679 __ Dmb(vixl32::ISH);
680 }
681 break;
682 }
683
684 default:
685 LOG(FATAL) << "Unexpected type " << type;
686 UNREACHABLE();
687 }
688}
689
Vladimir Markoca6fff82017-10-03 14:49:14 +0100690static void CreateIntIntIntToIntLocations(ArenaAllocator* allocator,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100691 HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100692 DataType::Type type) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100693 bool can_call = kEmitCompilerReadBarrier &&
694 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
695 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Vladimir Markoca6fff82017-10-03 14:49:14 +0100696 LocationSummary* locations =
697 new (allocator) LocationSummary(invoke,
698 can_call
699 ? LocationSummary::kCallOnSlowPath
700 : LocationSummary::kNoCall,
701 kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100702 if (can_call && kUseBakerReadBarrier) {
703 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
704 }
705 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
706 locations->SetInAt(1, Location::RequiresRegister());
707 locations->SetInAt(2, Location::RequiresRegister());
708 locations->SetOut(Location::RequiresRegister(),
709 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100710 if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100711 // We need a temporary register for the read barrier marking slow
Roland Levillain9983e302017-07-14 14:34:22 +0100712 // path in CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier.
Anton Kirilov5ec62182016-10-13 20:16:02 +0100713 locations->AddTemp(Location::RequiresRegister());
714 }
715}
716
717void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGet(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100718 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt32);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100719}
720void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100721 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt32);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100722}
723void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetLong(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100724 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt64);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100725}
726void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100727 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt64);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100728}
729void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetObject(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100730 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kReference);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100731}
732void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100733 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kReference);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100734}
735
736void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGet(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100737 GenUnsafeGet(invoke, DataType::Type::kInt32, /* is_volatile */ false, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100738}
739void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100740 GenUnsafeGet(invoke, DataType::Type::kInt32, /* is_volatile */ true, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100741}
742void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetLong(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100743 GenUnsafeGet(invoke, DataType::Type::kInt64, /* is_volatile */ false, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100744}
745void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100746 GenUnsafeGet(invoke, DataType::Type::kInt64, /* is_volatile */ true, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100747}
748void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetObject(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100749 GenUnsafeGet(invoke, DataType::Type::kReference, /* is_volatile */ false, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100750}
751void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100752 GenUnsafeGet(invoke, DataType::Type::kReference, /* is_volatile */ true, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100753}
754
Vladimir Markoca6fff82017-10-03 14:49:14 +0100755static void CreateIntIntIntIntToVoid(ArenaAllocator* allocator,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100756 const ArmInstructionSetFeatures& features,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100757 DataType::Type type,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100758 bool is_volatile,
759 HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100760 LocationSummary* locations =
761 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100762 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
763 locations->SetInAt(1, Location::RequiresRegister());
764 locations->SetInAt(2, Location::RequiresRegister());
765 locations->SetInAt(3, Location::RequiresRegister());
766
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100767 if (type == DataType::Type::kInt64) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100768 // Potentially need temps for ldrexd-strexd loop.
769 if (is_volatile && !features.HasAtomicLdrdAndStrd()) {
770 locations->AddTemp(Location::RequiresRegister()); // Temp_lo.
771 locations->AddTemp(Location::RequiresRegister()); // Temp_hi.
772 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100773 } else if (type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100774 // Temps for card-marking.
775 locations->AddTemp(Location::RequiresRegister()); // Temp.
776 locations->AddTemp(Location::RequiresRegister()); // Card.
777 }
778}
779
780void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePut(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100781 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100782 allocator_, features_, DataType::Type::kInt32, /* is_volatile */ false, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100783}
784void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutOrdered(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100785 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100786 allocator_, features_, DataType::Type::kInt32, /* is_volatile */ false, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100787}
788void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100789 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100790 allocator_, features_, DataType::Type::kInt32, /* is_volatile */ true, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100791}
792void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutObject(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100793 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100794 allocator_, features_, DataType::Type::kReference, /* is_volatile */ false, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100795}
796void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100797 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100798 allocator_, features_, DataType::Type::kReference, /* is_volatile */ false, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100799}
800void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100801 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100802 allocator_, features_, DataType::Type::kReference, /* is_volatile */ true, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100803}
804void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutLong(HInvoke* invoke) {
805 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100806 allocator_, features_, DataType::Type::kInt64, /* is_volatile */ false, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100807}
808void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutLongOrdered(HInvoke* invoke) {
809 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100810 allocator_, features_, DataType::Type::kInt64, /* is_volatile */ false, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100811}
812void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutLongVolatile(HInvoke* invoke) {
813 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100814 allocator_, features_, DataType::Type::kInt64, /* is_volatile */ true, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100815}
816
817static void GenUnsafePut(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100818 DataType::Type type,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100819 bool is_volatile,
820 bool is_ordered,
821 CodeGeneratorARMVIXL* codegen) {
822 ArmVIXLAssembler* assembler = codegen->GetAssembler();
823
824 vixl32::Register base = RegisterFrom(locations->InAt(1)); // Object pointer.
825 vixl32::Register offset = LowRegisterFrom(locations->InAt(2)); // Long offset, lo part only.
826 vixl32::Register value;
827
828 if (is_volatile || is_ordered) {
829 __ Dmb(vixl32::ISH);
830 }
831
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100832 if (type == DataType::Type::kInt64) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100833 vixl32::Register value_lo = LowRegisterFrom(locations->InAt(3));
834 vixl32::Register value_hi = HighRegisterFrom(locations->InAt(3));
835 value = value_lo;
836 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
837 vixl32::Register temp_lo = RegisterFrom(locations->GetTemp(0));
838 vixl32::Register temp_hi = RegisterFrom(locations->GetTemp(1));
839 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
840 const vixl32::Register temp_reg = temps.Acquire();
841
842 __ Add(temp_reg, base, offset);
843 vixl32::Label loop_head;
844 __ Bind(&loop_head);
Scott Wakelingb77051e2016-11-21 19:46:00 +0000845 __ Ldrexd(temp_lo, temp_hi, MemOperand(temp_reg));
846 __ Strexd(temp_lo, value_lo, value_hi, MemOperand(temp_reg));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100847 __ Cmp(temp_lo, 0);
Artem Serov517d9f62016-12-12 15:51:15 +0000848 __ B(ne, &loop_head, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100849 } else {
850 __ Strd(value_lo, value_hi, MemOperand(base, offset));
851 }
852 } else {
853 value = RegisterFrom(locations->InAt(3));
854 vixl32::Register source = value;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100855 if (kPoisonHeapReferences && type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100856 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
857 __ Mov(temp, value);
858 assembler->PoisonHeapReference(temp);
859 source = temp;
860 }
861 __ Str(source, MemOperand(base, offset));
862 }
863
864 if (is_volatile) {
865 __ Dmb(vixl32::ISH);
866 }
867
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100868 if (type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100869 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
870 vixl32::Register card = RegisterFrom(locations->GetTemp(1));
871 bool value_can_be_null = true; // TODO: Worth finding out this information?
872 codegen->MarkGCCard(temp, card, base, value, value_can_be_null);
873 }
874}
875
876void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePut(HInvoke* invoke) {
877 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100878 DataType::Type::kInt32,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100879 /* is_volatile */ false,
880 /* is_ordered */ false,
881 codegen_);
882}
883void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutOrdered(HInvoke* invoke) {
884 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100885 DataType::Type::kInt32,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100886 /* is_volatile */ false,
887 /* is_ordered */ true,
888 codegen_);
889}
890void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutVolatile(HInvoke* invoke) {
891 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100892 DataType::Type::kInt32,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100893 /* is_volatile */ true,
894 /* is_ordered */ false,
895 codegen_);
896}
897void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutObject(HInvoke* invoke) {
898 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100899 DataType::Type::kReference,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100900 /* is_volatile */ false,
901 /* is_ordered */ false,
902 codegen_);
903}
904void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
905 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100906 DataType::Type::kReference,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100907 /* is_volatile */ false,
908 /* is_ordered */ true,
909 codegen_);
910}
911void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
912 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100913 DataType::Type::kReference,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100914 /* is_volatile */ true,
915 /* is_ordered */ false,
916 codegen_);
917}
918void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutLong(HInvoke* invoke) {
919 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100920 DataType::Type::kInt64,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100921 /* is_volatile */ false,
922 /* is_ordered */ false,
923 codegen_);
924}
925void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutLongOrdered(HInvoke* invoke) {
926 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100927 DataType::Type::kInt64,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100928 /* is_volatile */ false,
929 /* is_ordered */ true,
930 codegen_);
931}
932void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutLongVolatile(HInvoke* invoke) {
933 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100934 DataType::Type::kInt64,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100935 /* is_volatile */ true,
936 /* is_ordered */ false,
937 codegen_);
938}
939
Vladimir Markoca6fff82017-10-03 14:49:14 +0100940static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* allocator,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100941 HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100942 DataType::Type type) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100943 bool can_call = kEmitCompilerReadBarrier &&
944 kUseBakerReadBarrier &&
945 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
Vladimir Markoca6fff82017-10-03 14:49:14 +0100946 LocationSummary* locations =
947 new (allocator) LocationSummary(invoke,
948 can_call
949 ? LocationSummary::kCallOnSlowPath
950 : LocationSummary::kNoCall,
951 kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100952 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
953 locations->SetInAt(1, Location::RequiresRegister());
954 locations->SetInAt(2, Location::RequiresRegister());
955 locations->SetInAt(3, Location::RequiresRegister());
956 locations->SetInAt(4, Location::RequiresRegister());
957
958 // If heap poisoning is enabled, we don't want the unpoisoning
959 // operations to potentially clobber the output. Likewise when
960 // emitting a (Baker) read barrier, which may call.
961 Location::OutputOverlap overlaps =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100962 ((kPoisonHeapReferences && type == DataType::Type::kReference) || can_call)
Anton Kirilov5ec62182016-10-13 20:16:02 +0100963 ? Location::kOutputOverlap
964 : Location::kNoOutputOverlap;
965 locations->SetOut(Location::RequiresRegister(), overlaps);
966
967 // Temporary registers used in CAS. In the object case
968 // (UnsafeCASObject intrinsic), these are also used for
969 // card-marking, and possibly for (Baker) read barrier.
970 locations->AddTemp(Location::RequiresRegister()); // Pointer.
971 locations->AddTemp(Location::RequiresRegister()); // Temp 1.
972}
973
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100974static void GenCas(HInvoke* invoke, DataType::Type type, CodeGeneratorARMVIXL* codegen) {
975 DCHECK_NE(type, DataType::Type::kInt64);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100976
977 ArmVIXLAssembler* assembler = codegen->GetAssembler();
978 LocationSummary* locations = invoke->GetLocations();
979
980 Location out_loc = locations->Out();
981 vixl32::Register out = OutputRegister(invoke); // Boolean result.
982
983 vixl32::Register base = InputRegisterAt(invoke, 1); // Object pointer.
984 Location offset_loc = locations->InAt(2);
985 vixl32::Register offset = LowRegisterFrom(offset_loc); // Offset (discard high 4B).
986 vixl32::Register expected = InputRegisterAt(invoke, 3); // Expected.
987 vixl32::Register value = InputRegisterAt(invoke, 4); // Value.
988
989 Location tmp_ptr_loc = locations->GetTemp(0);
990 vixl32::Register tmp_ptr = RegisterFrom(tmp_ptr_loc); // Pointer to actual memory.
991 vixl32::Register tmp = RegisterFrom(locations->GetTemp(1)); // Value in memory.
992
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100993 if (type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100994 // The only read barrier implementation supporting the
995 // UnsafeCASObject intrinsic is the Baker-style read barriers.
996 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
997
998 // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
999 // object and scan the receiver at the next GC for nothing.
1000 bool value_can_be_null = true; // TODO: Worth finding out this information?
1001 codegen->MarkGCCard(tmp_ptr, tmp, base, value, value_can_be_null);
1002
1003 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1004 // Need to make sure the reference stored in the field is a to-space
1005 // one before attempting the CAS or the CAS could fail incorrectly.
Roland Levillainff487002017-03-07 16:50:01 +00001006 codegen->UpdateReferenceFieldWithBakerReadBarrier(
Anton Kirilov5ec62182016-10-13 20:16:02 +01001007 invoke,
1008 out_loc, // Unused, used only as a "temporary" within the read barrier.
1009 base,
Roland Levillainff487002017-03-07 16:50:01 +00001010 /* field_offset */ offset_loc,
Anton Kirilov5ec62182016-10-13 20:16:02 +01001011 tmp_ptr_loc,
1012 /* needs_null_check */ false,
Roland Levillainff487002017-03-07 16:50:01 +00001013 tmp);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001014 }
1015 }
1016
1017 // Prevent reordering with prior memory operations.
1018 // Emit a DMB ISH instruction instead of an DMB ISHST one, as the
1019 // latter allows a preceding load to be delayed past the STXR
1020 // instruction below.
1021 __ Dmb(vixl32::ISH);
1022
1023 __ Add(tmp_ptr, base, offset);
1024
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001025 if (kPoisonHeapReferences && type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01001026 codegen->GetAssembler()->PoisonHeapReference(expected);
1027 if (value.Is(expected)) {
1028 // Do not poison `value`, as it is the same register as
1029 // `expected`, which has just been poisoned.
1030 } else {
1031 codegen->GetAssembler()->PoisonHeapReference(value);
1032 }
1033 }
1034
1035 // do {
1036 // tmp = [r_ptr] - expected;
1037 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
1038 // result = tmp != 0;
1039
1040 vixl32::Label loop_head;
1041 __ Bind(&loop_head);
1042
Scott Wakelingb77051e2016-11-21 19:46:00 +00001043 __ Ldrex(tmp, MemOperand(tmp_ptr));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001044
1045 __ Subs(tmp, tmp, expected);
1046
1047 {
Artem Serov0fb37192016-12-06 18:13:40 +00001048 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1049 3 * kMaxInstructionSizeInBytes,
1050 CodeBufferCheckScope::kMaximumSize);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001051
1052 __ itt(eq);
Scott Wakelingb77051e2016-11-21 19:46:00 +00001053 __ strex(eq, tmp, value, MemOperand(tmp_ptr));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001054 __ cmp(eq, tmp, 1);
1055 }
1056
Artem Serov517d9f62016-12-12 15:51:15 +00001057 __ B(eq, &loop_head, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001058
1059 __ Dmb(vixl32::ISH);
1060
1061 __ Rsbs(out, tmp, 1);
1062
1063 {
Artem Serov0fb37192016-12-06 18:13:40 +00001064 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1065 2 * kMaxInstructionSizeInBytes,
1066 CodeBufferCheckScope::kMaximumSize);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001067
1068 __ it(cc);
1069 __ mov(cc, out, 0);
1070 }
1071
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001072 if (kPoisonHeapReferences && type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01001073 codegen->GetAssembler()->UnpoisonHeapReference(expected);
1074 if (value.Is(expected)) {
1075 // Do not unpoison `value`, as it is the same register as
1076 // `expected`, which has just been unpoisoned.
1077 } else {
1078 codegen->GetAssembler()->UnpoisonHeapReference(value);
1079 }
1080 }
1081}
1082
1083void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeCASInt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001084 CreateIntIntIntIntIntToIntPlusTemps(allocator_, invoke, DataType::Type::kInt32);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001085}
1086void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeCASObject(HInvoke* invoke) {
1087 // The only read barrier implementation supporting the
1088 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1089 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
1090 return;
1091 }
1092
Vladimir Markoca6fff82017-10-03 14:49:14 +01001093 CreateIntIntIntIntIntToIntPlusTemps(allocator_, invoke, DataType::Type::kReference);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001094}
1095void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeCASInt(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001096 GenCas(invoke, DataType::Type::kInt32, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001097}
1098void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeCASObject(HInvoke* invoke) {
1099 // The only read barrier implementation supporting the
1100 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1101 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1102
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001103 GenCas(invoke, DataType::Type::kReference, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001104}
1105
1106void IntrinsicLocationsBuilderARMVIXL::VisitStringCompareTo(HInvoke* invoke) {
1107 // The inputs plus one temp.
Vladimir Markoca6fff82017-10-03 14:49:14 +01001108 LocationSummary* locations =
1109 new (allocator_) LocationSummary(invoke,
1110 invoke->InputAt(1)->CanBeNull()
1111 ? LocationSummary::kCallOnSlowPath
1112 : LocationSummary::kNoCall,
1113 kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001114 locations->SetInAt(0, Location::RequiresRegister());
1115 locations->SetInAt(1, Location::RequiresRegister());
1116 locations->AddTemp(Location::RequiresRegister());
1117 locations->AddTemp(Location::RequiresRegister());
1118 locations->AddTemp(Location::RequiresRegister());
1119 // Need temporary registers for String compression's feature.
1120 if (mirror::kUseStringCompression) {
1121 locations->AddTemp(Location::RequiresRegister());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001122 }
1123 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1124}
1125
Artem Serov672b9c12017-12-05 18:04:07 +00001126// Forward declaration.
1127//
1128// ART build system imposes a size limit (deviceFrameSizeLimit) on the stack frames generated
1129// by the compiler for every C++ function, and if this function gets inlined in
1130// IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo, the limit will be exceeded, resulting in a
1131// build failure. That is the reason why NO_INLINE attribute is used.
1132static void NO_INLINE GenerateStringCompareToLoop(ArmVIXLAssembler* assembler,
1133 HInvoke* invoke,
1134 vixl32::Label* end,
1135 vixl32::Label* different_compression);
1136
Anton Kirilov5ec62182016-10-13 20:16:02 +01001137void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) {
1138 ArmVIXLAssembler* assembler = GetAssembler();
1139 LocationSummary* locations = invoke->GetLocations();
1140
Artem Serov672b9c12017-12-05 18:04:07 +00001141 const vixl32::Register str = InputRegisterAt(invoke, 0);
1142 const vixl32::Register arg = InputRegisterAt(invoke, 1);
1143 const vixl32::Register out = OutputRegister(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001144
Artem Serov672b9c12017-12-05 18:04:07 +00001145 const vixl32::Register temp0 = RegisterFrom(locations->GetTemp(0));
1146 const vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1147 const vixl32::Register temp2 = RegisterFrom(locations->GetTemp(2));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001148 vixl32::Register temp3;
Anton Kirilov5ec62182016-10-13 20:16:02 +01001149 if (mirror::kUseStringCompression) {
1150 temp3 = RegisterFrom(locations->GetTemp(3));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001151 }
1152
Anton Kirilov5ec62182016-10-13 20:16:02 +01001153 vixl32::Label end;
1154 vixl32::Label different_compression;
1155
1156 // Get offsets of count and value fields within a string object.
1157 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
Anton Kirilov5ec62182016-10-13 20:16:02 +01001158
1159 // Note that the null check must have been done earlier.
1160 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1161
1162 // Take slow path and throw if input can be and is null.
1163 SlowPathCodeARMVIXL* slow_path = nullptr;
1164 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1165 if (can_slow_path) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001166 slow_path = new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001167 codegen_->AddSlowPath(slow_path);
xueliang.zhongf51bc622016-11-04 09:23:32 +00001168 __ CompareAndBranchIfZero(arg, slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001169 }
1170
1171 // Reference equality check, return 0 if same reference.
1172 __ Subs(out, str, arg);
1173 __ B(eq, &end);
1174
Anton Kirilov5ec62182016-10-13 20:16:02 +01001175 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001176 // Load `count` fields of this and argument strings.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001177 __ Ldr(temp3, MemOperand(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001178 __ Ldr(temp2, MemOperand(arg, count_offset));
1179 // Extract lengths from the `count` fields.
1180 __ Lsr(temp0, temp3, 1u);
1181 __ Lsr(temp1, temp2, 1u);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001182 } else {
1183 // Load lengths of this and argument strings.
1184 __ Ldr(temp0, MemOperand(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001185 __ Ldr(temp1, MemOperand(arg, count_offset));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001186 }
1187 // out = length diff.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001188 __ Subs(out, temp0, temp1);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001189 // temp0 = min(len(str), len(arg)).
1190
1191 {
Artem Serov0fb37192016-12-06 18:13:40 +00001192 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1193 2 * kMaxInstructionSizeInBytes,
1194 CodeBufferCheckScope::kMaximumSize);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001195
1196 __ it(gt);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001197 __ mov(gt, temp0, temp1);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001198 }
1199
Anton Kirilov5ec62182016-10-13 20:16:02 +01001200 // Shorter string is empty?
xueliang.zhongf51bc622016-11-04 09:23:32 +00001201 // Note that mirror::kUseStringCompression==true introduces lots of instructions,
1202 // which makes &end label far away from this branch and makes it not 'CBZ-encodable'.
1203 __ CompareAndBranchIfZero(temp0, &end, mirror::kUseStringCompression);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001204
1205 if (mirror::kUseStringCompression) {
1206 // Check if both strings using same compression style to use this comparison loop.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001207 __ Eors(temp2, temp2, temp3);
1208 __ Lsrs(temp2, temp2, 1u);
1209 __ B(cs, &different_compression);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001210 // For string compression, calculate the number of bytes to compare (not chars).
1211 // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001212 __ Lsls(temp3, temp3, 31u); // Extract purely the compression flag.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001213
Artem Serov0fb37192016-12-06 18:13:40 +00001214 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1215 2 * kMaxInstructionSizeInBytes,
1216 CodeBufferCheckScope::kMaximumSize);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001217
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001218 __ it(ne);
1219 __ add(ne, temp0, temp0, temp0);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001220 }
1221
Artem Serov672b9c12017-12-05 18:04:07 +00001222
1223 GenerateStringCompareToLoop(assembler, invoke, &end, &different_compression);
1224
1225 __ Bind(&end);
1226
1227 if (can_slow_path) {
1228 __ Bind(slow_path->GetExitLabel());
1229 }
1230}
1231
1232static void GenerateStringCompareToLoop(ArmVIXLAssembler* assembler,
1233 HInvoke* invoke,
1234 vixl32::Label* end,
1235 vixl32::Label* different_compression) {
1236 LocationSummary* locations = invoke->GetLocations();
1237
1238 const vixl32::Register str = InputRegisterAt(invoke, 0);
1239 const vixl32::Register arg = InputRegisterAt(invoke, 1);
1240 const vixl32::Register out = OutputRegister(invoke);
1241
1242 const vixl32::Register temp0 = RegisterFrom(locations->GetTemp(0));
1243 const vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1244 const vixl32::Register temp2 = RegisterFrom(locations->GetTemp(2));
1245 vixl32::Register temp3;
1246 if (mirror::kUseStringCompression) {
1247 temp3 = RegisterFrom(locations->GetTemp(3));
1248 }
1249
1250 vixl32::Label loop;
1251 vixl32::Label find_char_diff;
1252
1253 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001254 // Store offset of string value in preparation for comparison loop.
1255 __ Mov(temp1, value_offset);
1256
Anton Kirilov5ec62182016-10-13 20:16:02 +01001257 // Assertions that must hold in order to compare multiple characters at a time.
1258 CHECK_ALIGNED(value_offset, 8);
1259 static_assert(IsAligned<8>(kObjectAlignment),
1260 "String data must be 8-byte aligned for unrolled CompareTo loop.");
1261
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001262 const unsigned char_size = DataType::Size(DataType::Type::kUint16);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001263 DCHECK_EQ(char_size, 2u);
1264
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001265 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
1266
Anton Kirilov5ec62182016-10-13 20:16:02 +01001267 vixl32::Label find_char_diff_2nd_cmp;
1268 // Unrolled loop comparing 4x16-bit chars per iteration (ok because of string data alignment).
1269 __ Bind(&loop);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001270 vixl32::Register temp_reg = temps.Acquire();
Anton Kirilov5ec62182016-10-13 20:16:02 +01001271 __ Ldr(temp_reg, MemOperand(str, temp1));
1272 __ Ldr(temp2, MemOperand(arg, temp1));
1273 __ Cmp(temp_reg, temp2);
Artem Serov517d9f62016-12-12 15:51:15 +00001274 __ B(ne, &find_char_diff, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001275 __ Add(temp1, temp1, char_size * 2);
1276
1277 __ Ldr(temp_reg, MemOperand(str, temp1));
1278 __ Ldr(temp2, MemOperand(arg, temp1));
1279 __ Cmp(temp_reg, temp2);
Artem Serov517d9f62016-12-12 15:51:15 +00001280 __ B(ne, &find_char_diff_2nd_cmp, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001281 __ Add(temp1, temp1, char_size * 2);
1282 // With string compression, we have compared 8 bytes, otherwise 4 chars.
1283 __ Subs(temp0, temp0, (mirror::kUseStringCompression ? 8 : 4));
Artem Serov517d9f62016-12-12 15:51:15 +00001284 __ B(hi, &loop, /* far_target */ false);
Artem Serov672b9c12017-12-05 18:04:07 +00001285 __ B(end);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001286
1287 __ Bind(&find_char_diff_2nd_cmp);
1288 if (mirror::kUseStringCompression) {
1289 __ Subs(temp0, temp0, 4); // 4 bytes previously compared.
Artem Serov672b9c12017-12-05 18:04:07 +00001290 __ B(ls, end, /* far_target */ false); // Was the second comparison fully beyond the end?
Anton Kirilov5ec62182016-10-13 20:16:02 +01001291 } else {
1292 // Without string compression, we can start treating temp0 as signed
1293 // and rely on the signed comparison below.
1294 __ Sub(temp0, temp0, 2);
1295 }
1296
1297 // Find the single character difference.
1298 __ Bind(&find_char_diff);
1299 // Get the bit position of the first character that differs.
1300 __ Eor(temp1, temp2, temp_reg);
1301 __ Rbit(temp1, temp1);
1302 __ Clz(temp1, temp1);
1303
1304 // temp0 = number of characters remaining to compare.
1305 // (Without string compression, it could be < 1 if a difference is found by the second CMP
1306 // in the comparison loop, and after the end of the shorter string data).
1307
1308 // Without string compression (temp1 >> 4) = character where difference occurs between the last
1309 // two words compared, in the interval [0,1].
1310 // (0 for low half-word different, 1 for high half-word different).
1311 // With string compression, (temp1 << 3) = byte where the difference occurs,
1312 // in the interval [0,3].
1313
1314 // If temp0 <= (temp1 >> (kUseStringCompression ? 3 : 4)), the difference occurs outside
1315 // the remaining string data, so just return length diff (out).
1316 // The comparison is unsigned for string compression, otherwise signed.
1317 __ Cmp(temp0, Operand(temp1, vixl32::LSR, (mirror::kUseStringCompression ? 3 : 4)));
Artem Serov672b9c12017-12-05 18:04:07 +00001318 __ B((mirror::kUseStringCompression ? ls : le), end, /* far_target */ false);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001319
Anton Kirilov5ec62182016-10-13 20:16:02 +01001320 // Extract the characters and calculate the difference.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001321 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001322 // For compressed strings we need to clear 0x7 from temp1, for uncompressed we need to clear
1323 // 0xf. We also need to prepare the character extraction mask `uncompressed ? 0xffffu : 0xffu`.
1324 // The compression flag is now in the highest bit of temp3, so let's play some tricks.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001325 __ Orr(temp3, temp3, 0xffu << 23); // uncompressed ? 0xff800000u : 0x7ff80000u
1326 __ Bic(temp1, temp1, Operand(temp3, vixl32::LSR, 31 - 3)); // &= ~(uncompressed ? 0xfu : 0x7u)
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001327 __ Asr(temp3, temp3, 7u); // uncompressed ? 0xffff0000u : 0xff0000u.
1328 __ Lsr(temp2, temp2, temp1); // Extract second character.
1329 __ Lsr(temp3, temp3, 16u); // uncompressed ? 0xffffu : 0xffu
1330 __ Lsr(out, temp_reg, temp1); // Extract first character.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001331 __ And(temp2, temp2, temp3);
1332 __ And(out, out, temp3);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001333 } else {
Anton Kirilovb88c4842016-11-14 14:37:00 +00001334 __ Bic(temp1, temp1, 0xf);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001335 __ Lsr(temp2, temp2, temp1);
1336 __ Lsr(out, temp_reg, temp1);
Anton Kirilovb88c4842016-11-14 14:37:00 +00001337 __ Movt(temp2, 0);
1338 __ Movt(out, 0);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001339 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01001340
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001341 __ Sub(out, out, temp2);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001342 temps.Release(temp_reg);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001343
1344 if (mirror::kUseStringCompression) {
Artem Serov672b9c12017-12-05 18:04:07 +00001345 __ B(end);
1346 __ Bind(different_compression);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001347
1348 // Comparison for different compression style.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001349 const size_t c_char_size = DataType::Size(DataType::Type::kInt8);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001350 DCHECK_EQ(c_char_size, 1u);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001351
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001352 // We want to free up the temp3, currently holding `str.count`, for comparison.
1353 // So, we move it to the bottom bit of the iteration count `temp0` which we tnen
1354 // need to treat as unsigned. Start by freeing the bit with an ADD and continue
1355 // further down by a LSRS+SBC which will flip the meaning of the flag but allow
1356 // `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001357 __ Add(temp0, temp0, temp0); // Unlike LSL, this ADD is always 16-bit.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001358 // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001359 __ Mov(temp1, str);
1360 __ Mov(temp2, arg);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001361 __ Lsrs(temp3, temp3, 1u); // Continue the move of the compression flag.
1362 {
Artem Serov0fb37192016-12-06 18:13:40 +00001363 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1364 3 * kMaxInstructionSizeInBytes,
1365 CodeBufferCheckScope::kMaximumSize);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001366 __ itt(cs); // Interleave with selection of temp1 and temp2.
1367 __ mov(cs, temp1, arg); // Preserves flags.
1368 __ mov(cs, temp2, str); // Preserves flags.
1369 }
Anton Kirilovb88c4842016-11-14 14:37:00 +00001370 __ Sbc(temp0, temp0, 0); // Complete the move of the compression flag.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001371
1372 // Adjust temp1 and temp2 from string pointers to data pointers.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001373 __ Add(temp1, temp1, value_offset);
1374 __ Add(temp2, temp2, value_offset);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001375
1376 vixl32::Label different_compression_loop;
1377 vixl32::Label different_compression_diff;
1378
1379 // Main loop for different compression.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001380 temp_reg = temps.Acquire();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001381 __ Bind(&different_compression_loop);
1382 __ Ldrb(temp_reg, MemOperand(temp1, c_char_size, PostIndex));
1383 __ Ldrh(temp3, MemOperand(temp2, char_size, PostIndex));
Anton Kirilovb88c4842016-11-14 14:37:00 +00001384 __ Cmp(temp_reg, temp3);
Artem Serov517d9f62016-12-12 15:51:15 +00001385 __ B(ne, &different_compression_diff, /* far_target */ false);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001386 __ Subs(temp0, temp0, 2);
Artem Serov517d9f62016-12-12 15:51:15 +00001387 __ B(hi, &different_compression_loop, /* far_target */ false);
Artem Serov672b9c12017-12-05 18:04:07 +00001388 __ B(end);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001389
1390 // Calculate the difference.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001391 __ Bind(&different_compression_diff);
1392 __ Sub(out, temp_reg, temp3);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001393 temps.Release(temp_reg);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001394 // Flip the difference if the `arg` is compressed.
1395 // `temp0` contains inverted `str` compression flag, i.e the same as `arg` compression flag.
1396 __ Lsrs(temp0, temp0, 1u);
1397 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1398 "Expecting 0=compressed, 1=uncompressed");
1399
Artem Serov0fb37192016-12-06 18:13:40 +00001400 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1401 2 * kMaxInstructionSizeInBytes,
1402 CodeBufferCheckScope::kMaximumSize);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001403 __ it(cc);
1404 __ rsb(cc, out, out, 0);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001405 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01001406}
1407
Vladimir Marko984519c2017-08-23 10:45:29 +01001408// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
1409// The normal loop plus the pre-header is 9 instructions (18-26 bytes) without string compression
1410// and 12 instructions (24-32 bytes) with string compression. We can compare up to 4 bytes in 4
1411// instructions (LDR+LDR+CMP+BNE) and up to 8 bytes in 6 instructions (LDRD+LDRD+CMP+BNE+CMP+BNE).
1412// Allow up to 12 instructions (32 bytes) for the unrolled loop.
1413constexpr size_t kShortConstStringEqualsCutoffInBytes = 16;
1414
1415static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
1416 if (candidate->IsLoadString()) {
1417 HLoadString* load_string = candidate->AsLoadString();
1418 const DexFile& dex_file = load_string->GetDexFile();
1419 return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
1420 }
1421 return nullptr;
1422}
1423
Anton Kirilov5ec62182016-10-13 20:16:02 +01001424void IntrinsicLocationsBuilderARMVIXL::VisitStringEquals(HInvoke* invoke) {
Vladimir Markoda283052017-11-07 21:17:24 +00001425 if (kEmitCompilerReadBarrier &&
1426 !StringEqualsOptimizations(invoke).GetArgumentIsString() &&
1427 !StringEqualsOptimizations(invoke).GetNoReadBarrierForStringClass()) {
1428 // No support for this odd case (String class is moveable, not in the boot image).
1429 return;
1430 }
1431
Vladimir Markoca6fff82017-10-03 14:49:14 +01001432 LocationSummary* locations =
1433 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001434 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1435 locations->SetInAt(0, Location::RequiresRegister());
1436 locations->SetInAt(1, Location::RequiresRegister());
Vladimir Marko984519c2017-08-23 10:45:29 +01001437
Anton Kirilov5ec62182016-10-13 20:16:02 +01001438 // Temporary registers to store lengths of strings and for calculations.
1439 // Using instruction cbz requires a low register, so explicitly set a temp to be R0.
1440 locations->AddTemp(LocationFrom(r0));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001441
Vladimir Marko984519c2017-08-23 10:45:29 +01001442 // For the generic implementation and for long const strings we need an extra temporary.
1443 // We do not need it for short const strings, up to 4 bytes, see code generation below.
1444 uint32_t const_string_length = 0u;
1445 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1446 if (const_string == nullptr) {
1447 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1448 }
1449 bool is_compressed =
1450 mirror::kUseStringCompression &&
1451 const_string != nullptr &&
1452 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1453 if (const_string == nullptr || const_string_length > (is_compressed ? 4u : 2u)) {
1454 locations->AddTemp(Location::RequiresRegister());
1455 }
1456
1457 // TODO: If the String.equals() is used only for an immediately following HIf, we can
1458 // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
1459 // Then we shall need an extra temporary register instead of the output register.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001460 locations->SetOut(Location::RequiresRegister());
1461}
1462
1463void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) {
1464 ArmVIXLAssembler* assembler = GetAssembler();
1465 LocationSummary* locations = invoke->GetLocations();
1466
1467 vixl32::Register str = InputRegisterAt(invoke, 0);
1468 vixl32::Register arg = InputRegisterAt(invoke, 1);
1469 vixl32::Register out = OutputRegister(invoke);
1470
1471 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001472
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001473 vixl32::Label loop;
Anton Kirilov5ec62182016-10-13 20:16:02 +01001474 vixl32::Label end;
1475 vixl32::Label return_true;
1476 vixl32::Label return_false;
Anton Kirilov6f644202017-02-27 18:29:45 +00001477 vixl32::Label* final_label = codegen_->GetFinalLabel(invoke, &end);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001478
1479 // Get offsets of count, value, and class fields within a string object.
1480 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
1481 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1482 const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
1483
1484 // Note that the null check must have been done earlier.
1485 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1486
1487 StringEqualsOptimizations optimizations(invoke);
1488 if (!optimizations.GetArgumentNotNull()) {
1489 // Check if input is null, return false if it is.
xueliang.zhongf51bc622016-11-04 09:23:32 +00001490 __ CompareAndBranchIfZero(arg, &return_false, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001491 }
1492
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001493 // Reference equality check, return true if same reference.
1494 __ Cmp(str, arg);
Artem Serov517d9f62016-12-12 15:51:15 +00001495 __ B(eq, &return_true, /* far_target */ false);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001496
Anton Kirilov5ec62182016-10-13 20:16:02 +01001497 if (!optimizations.GetArgumentIsString()) {
1498 // Instanceof check for the argument by comparing class fields.
1499 // All string objects must have the same type since String cannot be subclassed.
1500 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1501 // If the argument is a string object, its class field must be equal to receiver's class field.
1502 __ Ldr(temp, MemOperand(str, class_offset));
Vladimir Marko984519c2017-08-23 10:45:29 +01001503 __ Ldr(out, MemOperand(arg, class_offset));
1504 __ Cmp(temp, out);
Artem Serov517d9f62016-12-12 15:51:15 +00001505 __ B(ne, &return_false, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001506 }
1507
Vladimir Marko984519c2017-08-23 10:45:29 +01001508 // Check if one of the inputs is a const string. Do not special-case both strings
1509 // being const, such cases should be handled by constant folding if needed.
1510 uint32_t const_string_length = 0u;
1511 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1512 if (const_string == nullptr) {
1513 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1514 if (const_string != nullptr) {
1515 std::swap(str, arg); // Make sure the const string is in `str`.
1516 }
1517 }
1518 bool is_compressed =
1519 mirror::kUseStringCompression &&
1520 const_string != nullptr &&
1521 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1522
1523 if (const_string != nullptr) {
1524 // Load `count` field of the argument string and check if it matches the const string.
1525 // Also compares the compression style, if differs return false.
1526 __ Ldr(temp, MemOperand(arg, count_offset));
1527 __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
1528 __ B(ne, &return_false, /* far_target */ false);
1529 } else {
1530 // Load `count` fields of this and argument strings.
1531 __ Ldr(temp, MemOperand(str, count_offset));
1532 __ Ldr(out, MemOperand(arg, count_offset));
1533 // Check if `count` fields are equal, return false if they're not.
1534 // Also compares the compression style, if differs return false.
1535 __ Cmp(temp, out);
1536 __ B(ne, &return_false, /* far_target */ false);
1537 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01001538
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001539 // Assertions that must hold in order to compare strings 4 bytes at a time.
Vladimir Marko984519c2017-08-23 10:45:29 +01001540 // Ok to do this because strings are zero-padded to kObjectAlignment.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001541 DCHECK_ALIGNED(value_offset, 4);
1542 static_assert(IsAligned<4>(kObjectAlignment), "String data must be aligned for fast compare.");
1543
Vladimir Marko984519c2017-08-23 10:45:29 +01001544 if (const_string != nullptr &&
1545 const_string_length <= (is_compressed ? kShortConstStringEqualsCutoffInBytes
1546 : kShortConstStringEqualsCutoffInBytes / 2u)) {
1547 // Load and compare the contents. Though we know the contents of the short const string
1548 // at compile time, materializing constants may be more code than loading from memory.
1549 int32_t offset = value_offset;
1550 size_t remaining_bytes =
1551 RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 4u);
1552 while (remaining_bytes > sizeof(uint32_t)) {
1553 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1554 UseScratchRegisterScope scratch_scope(assembler->GetVIXLAssembler());
1555 vixl32::Register temp2 = scratch_scope.Acquire();
1556 __ Ldrd(temp, temp1, MemOperand(str, offset));
1557 __ Ldrd(temp2, out, MemOperand(arg, offset));
1558 __ Cmp(temp, temp2);
1559 __ B(ne, &return_false, /* far_label */ false);
1560 __ Cmp(temp1, out);
1561 __ B(ne, &return_false, /* far_label */ false);
1562 offset += 2u * sizeof(uint32_t);
1563 remaining_bytes -= 2u * sizeof(uint32_t);
1564 }
1565 if (remaining_bytes != 0u) {
1566 __ Ldr(temp, MemOperand(str, offset));
1567 __ Ldr(out, MemOperand(arg, offset));
1568 __ Cmp(temp, out);
1569 __ B(ne, &return_false, /* far_label */ false);
1570 }
1571 } else {
1572 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
1573 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1574 "Expecting 0=compressed, 1=uncompressed");
1575 __ CompareAndBranchIfZero(temp, &return_true, /* far_target */ false);
1576
1577 if (mirror::kUseStringCompression) {
1578 // For string compression, calculate the number of bytes to compare (not chars).
1579 // This could in theory exceed INT32_MAX, so treat temp as unsigned.
1580 __ Lsrs(temp, temp, 1u); // Extract length and check compression flag.
1581 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1582 2 * kMaxInstructionSizeInBytes,
1583 CodeBufferCheckScope::kMaximumSize);
1584 __ it(cs); // If uncompressed,
1585 __ add(cs, temp, temp, temp); // double the byte count.
1586 }
1587
1588 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1589 UseScratchRegisterScope scratch_scope(assembler->GetVIXLAssembler());
1590 vixl32::Register temp2 = scratch_scope.Acquire();
1591
1592 // Store offset of string value in preparation for comparison loop.
1593 __ Mov(temp1, value_offset);
1594
1595 // Loop to compare strings 4 bytes at a time starting at the front of the string.
1596 __ Bind(&loop);
1597 __ Ldr(out, MemOperand(str, temp1));
1598 __ Ldr(temp2, MemOperand(arg, temp1));
1599 __ Add(temp1, temp1, Operand::From(sizeof(uint32_t)));
1600 __ Cmp(out, temp2);
1601 __ B(ne, &return_false, /* far_target */ false);
1602 // With string compression, we have compared 4 bytes, otherwise 2 chars.
1603 __ Subs(temp, temp, mirror::kUseStringCompression ? 4 : 2);
1604 __ B(hi, &loop, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001605 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001606
Anton Kirilov5ec62182016-10-13 20:16:02 +01001607 // Return true and exit the function.
1608 // If loop does not result in returning false, we return true.
1609 __ Bind(&return_true);
1610 __ Mov(out, 1);
Anton Kirilov6f644202017-02-27 18:29:45 +00001611 __ B(final_label);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001612
1613 // Return false and exit the function.
1614 __ Bind(&return_false);
1615 __ Mov(out, 0);
Anton Kirilov6f644202017-02-27 18:29:45 +00001616
1617 if (end.IsReferenced()) {
1618 __ Bind(&end);
1619 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01001620}
1621
1622static void GenerateVisitStringIndexOf(HInvoke* invoke,
1623 ArmVIXLAssembler* assembler,
1624 CodeGeneratorARMVIXL* codegen,
Anton Kirilov5ec62182016-10-13 20:16:02 +01001625 bool start_at_zero) {
1626 LocationSummary* locations = invoke->GetLocations();
1627
1628 // Note that the null check must have been done earlier.
1629 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1630
1631 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
1632 // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
1633 SlowPathCodeARMVIXL* slow_path = nullptr;
1634 HInstruction* code_point = invoke->InputAt(1);
1635 if (code_point->IsIntConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00001636 if (static_cast<uint32_t>(Int32ConstantFrom(code_point)) >
Anton Kirilov5ec62182016-10-13 20:16:02 +01001637 std::numeric_limits<uint16_t>::max()) {
1638 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1639 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
Vladimir Marko174b2e22017-10-12 13:34:49 +01001640 slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001641 codegen->AddSlowPath(slow_path);
1642 __ B(slow_path->GetEntryLabel());
1643 __ Bind(slow_path->GetExitLabel());
1644 return;
1645 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001646 } else if (code_point->GetType() != DataType::Type::kUint16) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01001647 vixl32::Register char_reg = InputRegisterAt(invoke, 1);
1648 // 0xffff is not modified immediate but 0x10000 is, so use `>= 0x10000` instead of `> 0xffff`.
1649 __ Cmp(char_reg, static_cast<uint32_t>(std::numeric_limits<uint16_t>::max()) + 1);
Vladimir Marko174b2e22017-10-12 13:34:49 +01001650 slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001651 codegen->AddSlowPath(slow_path);
1652 __ B(hs, slow_path->GetEntryLabel());
1653 }
1654
1655 if (start_at_zero) {
1656 vixl32::Register tmp_reg = RegisterFrom(locations->GetTemp(0));
1657 DCHECK(tmp_reg.Is(r2));
1658 // Start-index = 0.
1659 __ Mov(tmp_reg, 0);
1660 }
1661
1662 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
1663 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
1664
1665 if (slow_path != nullptr) {
1666 __ Bind(slow_path->GetExitLabel());
1667 }
1668}
1669
1670void IntrinsicLocationsBuilderARMVIXL::VisitStringIndexOf(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001671 LocationSummary* locations = new (allocator_) LocationSummary(
1672 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001673 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1674 // best to align the inputs accordingly.
1675 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1676 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1677 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1678 locations->SetOut(LocationFrom(r0));
1679
1680 // Need to send start-index=0.
1681 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1682}
1683
1684void IntrinsicCodeGeneratorARMVIXL::VisitStringIndexOf(HInvoke* invoke) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001685 GenerateVisitStringIndexOf(invoke, GetAssembler(), codegen_, /* start_at_zero */ true);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001686}
1687
1688void IntrinsicLocationsBuilderARMVIXL::VisitStringIndexOfAfter(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001689 LocationSummary* locations = new (allocator_) LocationSummary(
1690 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001691 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1692 // best to align the inputs accordingly.
1693 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1694 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1695 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1696 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1697 locations->SetOut(LocationFrom(r0));
1698}
1699
1700void IntrinsicCodeGeneratorARMVIXL::VisitStringIndexOfAfter(HInvoke* invoke) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001701 GenerateVisitStringIndexOf(invoke, GetAssembler(), codegen_, /* start_at_zero */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001702}
1703
1704void IntrinsicLocationsBuilderARMVIXL::VisitStringNewStringFromBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001705 LocationSummary* locations = new (allocator_) LocationSummary(
1706 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001707 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1708 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1709 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1710 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1711 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1712 locations->SetOut(LocationFrom(r0));
1713}
1714
1715void IntrinsicCodeGeneratorARMVIXL::VisitStringNewStringFromBytes(HInvoke* invoke) {
1716 ArmVIXLAssembler* assembler = GetAssembler();
1717 vixl32::Register byte_array = InputRegisterAt(invoke, 0);
1718 __ Cmp(byte_array, 0);
Vladimir Marko174b2e22017-10-12 13:34:49 +01001719 SlowPathCodeARMVIXL* slow_path =
1720 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001721 codegen_->AddSlowPath(slow_path);
1722 __ B(eq, slow_path->GetEntryLabel());
1723
1724 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
1725 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
1726 __ Bind(slow_path->GetExitLabel());
1727}
1728
1729void IntrinsicLocationsBuilderARMVIXL::VisitStringNewStringFromChars(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001730 LocationSummary* locations =
1731 new (allocator_) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001732 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1733 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1734 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1735 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1736 locations->SetOut(LocationFrom(r0));
1737}
1738
1739void IntrinsicCodeGeneratorARMVIXL::VisitStringNewStringFromChars(HInvoke* invoke) {
1740 // No need to emit code checking whether `locations->InAt(2)` is a null
1741 // pointer, as callers of the native method
1742 //
1743 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1744 //
1745 // all include a null check on `data` before calling that method.
1746 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
1747 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
1748}
1749
1750void IntrinsicLocationsBuilderARMVIXL::VisitStringNewStringFromString(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001751 LocationSummary* locations = new (allocator_) LocationSummary(
1752 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001753 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1754 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1755 locations->SetOut(LocationFrom(r0));
1756}
1757
1758void IntrinsicCodeGeneratorARMVIXL::VisitStringNewStringFromString(HInvoke* invoke) {
1759 ArmVIXLAssembler* assembler = GetAssembler();
1760 vixl32::Register string_to_copy = InputRegisterAt(invoke, 0);
1761 __ Cmp(string_to_copy, 0);
Vladimir Marko174b2e22017-10-12 13:34:49 +01001762 SlowPathCodeARMVIXL* slow_path =
1763 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001764 codegen_->AddSlowPath(slow_path);
1765 __ B(eq, slow_path->GetEntryLabel());
1766
1767 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
1768 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
1769
1770 __ Bind(slow_path->GetExitLabel());
1771}
1772
1773void IntrinsicLocationsBuilderARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) {
1774 // The only read barrier implementation supporting the
1775 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1776 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
1777 return;
1778 }
1779
1780 CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
1781 LocationSummary* locations = invoke->GetLocations();
1782 if (locations == nullptr) {
1783 return;
1784 }
1785
1786 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1787 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
1788 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1789
1790 if (src_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(src_pos->GetValue())) {
1791 locations->SetInAt(1, Location::RequiresRegister());
1792 }
1793 if (dest_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(dest_pos->GetValue())) {
1794 locations->SetInAt(3, Location::RequiresRegister());
1795 }
1796 if (length != nullptr && !assembler_->ShifterOperandCanAlwaysHold(length->GetValue())) {
1797 locations->SetInAt(4, Location::RequiresRegister());
1798 }
1799 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1800 // Temporary register IP cannot be used in
1801 // ReadBarrierSystemArrayCopySlowPathARM (because that register
1802 // is clobbered by ReadBarrierMarkRegX entry points). Get an extra
1803 // temporary register from the register allocator.
1804 locations->AddTemp(Location::RequiresRegister());
1805 }
1806}
1807
1808static void CheckPosition(ArmVIXLAssembler* assembler,
1809 Location pos,
1810 vixl32::Register input,
1811 Location length,
1812 SlowPathCodeARMVIXL* slow_path,
1813 vixl32::Register temp,
1814 bool length_is_input_length = false) {
1815 // Where is the length in the Array?
1816 const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
1817
1818 if (pos.IsConstant()) {
1819 int32_t pos_const = Int32ConstantFrom(pos);
1820 if (pos_const == 0) {
1821 if (!length_is_input_length) {
1822 // Check that length(input) >= length.
1823 __ Ldr(temp, MemOperand(input, length_offset));
1824 if (length.IsConstant()) {
1825 __ Cmp(temp, Int32ConstantFrom(length));
1826 } else {
1827 __ Cmp(temp, RegisterFrom(length));
1828 }
1829 __ B(lt, slow_path->GetEntryLabel());
1830 }
1831 } else {
1832 // Check that length(input) >= pos.
1833 __ Ldr(temp, MemOperand(input, length_offset));
1834 __ Subs(temp, temp, pos_const);
1835 __ B(lt, slow_path->GetEntryLabel());
1836
1837 // Check that (length(input) - pos) >= length.
1838 if (length.IsConstant()) {
1839 __ Cmp(temp, Int32ConstantFrom(length));
1840 } else {
1841 __ Cmp(temp, RegisterFrom(length));
1842 }
1843 __ B(lt, slow_path->GetEntryLabel());
1844 }
1845 } else if (length_is_input_length) {
1846 // The only way the copy can succeed is if pos is zero.
1847 vixl32::Register pos_reg = RegisterFrom(pos);
xueliang.zhongf51bc622016-11-04 09:23:32 +00001848 __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001849 } else {
1850 // Check that pos >= 0.
1851 vixl32::Register pos_reg = RegisterFrom(pos);
1852 __ Cmp(pos_reg, 0);
1853 __ B(lt, slow_path->GetEntryLabel());
1854
1855 // Check that pos <= length(input).
1856 __ Ldr(temp, MemOperand(input, length_offset));
1857 __ Subs(temp, temp, pos_reg);
1858 __ B(lt, slow_path->GetEntryLabel());
1859
1860 // Check that (length(input) - pos) >= length.
1861 if (length.IsConstant()) {
1862 __ Cmp(temp, Int32ConstantFrom(length));
1863 } else {
1864 __ Cmp(temp, RegisterFrom(length));
1865 }
1866 __ B(lt, slow_path->GetEntryLabel());
1867 }
1868}
1869
1870void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) {
1871 // The only read barrier implementation supporting the
1872 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1873 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1874
1875 ArmVIXLAssembler* assembler = GetAssembler();
1876 LocationSummary* locations = invoke->GetLocations();
1877
1878 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1879 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
1880 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
1881 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
1882 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
1883
1884 vixl32::Register src = InputRegisterAt(invoke, 0);
1885 Location src_pos = locations->InAt(1);
1886 vixl32::Register dest = InputRegisterAt(invoke, 2);
1887 Location dest_pos = locations->InAt(3);
1888 Location length = locations->InAt(4);
1889 Location temp1_loc = locations->GetTemp(0);
1890 vixl32::Register temp1 = RegisterFrom(temp1_loc);
1891 Location temp2_loc = locations->GetTemp(1);
1892 vixl32::Register temp2 = RegisterFrom(temp2_loc);
1893 Location temp3_loc = locations->GetTemp(2);
1894 vixl32::Register temp3 = RegisterFrom(temp3_loc);
1895
Vladimir Marko174b2e22017-10-12 13:34:49 +01001896 SlowPathCodeARMVIXL* intrinsic_slow_path =
1897 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001898 codegen_->AddSlowPath(intrinsic_slow_path);
1899
1900 vixl32::Label conditions_on_positions_validated;
1901 SystemArrayCopyOptimizations optimizations(invoke);
1902
1903 // If source and destination are the same, we go to slow path if we need to do
1904 // forward copying.
1905 if (src_pos.IsConstant()) {
1906 int32_t src_pos_constant = Int32ConstantFrom(src_pos);
1907 if (dest_pos.IsConstant()) {
1908 int32_t dest_pos_constant = Int32ConstantFrom(dest_pos);
1909 if (optimizations.GetDestinationIsSource()) {
1910 // Checked when building locations.
1911 DCHECK_GE(src_pos_constant, dest_pos_constant);
1912 } else if (src_pos_constant < dest_pos_constant) {
1913 __ Cmp(src, dest);
1914 __ B(eq, intrinsic_slow_path->GetEntryLabel());
1915 }
1916
1917 // Checked when building locations.
1918 DCHECK(!optimizations.GetDestinationIsSource()
1919 || (src_pos_constant >= Int32ConstantFrom(dest_pos)));
1920 } else {
1921 if (!optimizations.GetDestinationIsSource()) {
1922 __ Cmp(src, dest);
Artem Serov517d9f62016-12-12 15:51:15 +00001923 __ B(ne, &conditions_on_positions_validated, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001924 }
1925 __ Cmp(RegisterFrom(dest_pos), src_pos_constant);
1926 __ B(gt, intrinsic_slow_path->GetEntryLabel());
1927 }
1928 } else {
1929 if (!optimizations.GetDestinationIsSource()) {
1930 __ Cmp(src, dest);
Artem Serov517d9f62016-12-12 15:51:15 +00001931 __ B(ne, &conditions_on_positions_validated, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001932 }
1933 if (dest_pos.IsConstant()) {
1934 int32_t dest_pos_constant = Int32ConstantFrom(dest_pos);
1935 __ Cmp(RegisterFrom(src_pos), dest_pos_constant);
1936 } else {
1937 __ Cmp(RegisterFrom(src_pos), RegisterFrom(dest_pos));
1938 }
1939 __ B(lt, intrinsic_slow_path->GetEntryLabel());
1940 }
1941
1942 __ Bind(&conditions_on_positions_validated);
1943
1944 if (!optimizations.GetSourceIsNotNull()) {
1945 // Bail out if the source is null.
xueliang.zhongf51bc622016-11-04 09:23:32 +00001946 __ CompareAndBranchIfZero(src, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001947 }
1948
1949 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
1950 // Bail out if the destination is null.
xueliang.zhongf51bc622016-11-04 09:23:32 +00001951 __ CompareAndBranchIfZero(dest, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001952 }
1953
1954 // If the length is negative, bail out.
1955 // We have already checked in the LocationsBuilder for the constant case.
1956 if (!length.IsConstant() &&
1957 !optimizations.GetCountIsSourceLength() &&
1958 !optimizations.GetCountIsDestinationLength()) {
1959 __ Cmp(RegisterFrom(length), 0);
1960 __ B(lt, intrinsic_slow_path->GetEntryLabel());
1961 }
1962
1963 // Validity checks: source.
1964 CheckPosition(assembler,
1965 src_pos,
1966 src,
1967 length,
1968 intrinsic_slow_path,
1969 temp1,
1970 optimizations.GetCountIsSourceLength());
1971
1972 // Validity checks: dest.
1973 CheckPosition(assembler,
1974 dest_pos,
1975 dest,
1976 length,
1977 intrinsic_slow_path,
1978 temp1,
1979 optimizations.GetCountIsDestinationLength());
1980
1981 if (!optimizations.GetDoesNotNeedTypeCheck()) {
1982 // Check whether all elements of the source array are assignable to the component
1983 // type of the destination array. We do two checks: the classes are the same,
1984 // or the destination is Object[]. If none of these checks succeed, we go to the
1985 // slow path.
1986
1987 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1988 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1989 // /* HeapReference<Class> */ temp1 = src->klass_
1990 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1991 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
1992 // Bail out if the source is not a non primitive array.
1993 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1994 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1995 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
xueliang.zhongf51bc622016-11-04 09:23:32 +00001996 __ CompareAndBranchIfZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001997 // If heap poisoning is enabled, `temp1` has been unpoisoned
1998 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1999 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
2000 __ Ldrh(temp1, MemOperand(temp1, primitive_offset));
2001 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00002002 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002003 }
2004
2005 // /* HeapReference<Class> */ temp1 = dest->klass_
2006 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2007 invoke, temp1_loc, dest, class_offset, temp2_loc, /* needs_null_check */ false);
2008
2009 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2010 // Bail out if the destination is not a non primitive array.
2011 //
2012 // Register `temp1` is not trashed by the read barrier emitted
2013 // by GenerateFieldLoadWithBakerReadBarrier below, as that
2014 // method produces a call to a ReadBarrierMarkRegX entry point,
2015 // which saves all potentially live registers, including
2016 // temporaries such a `temp1`.
2017 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2018 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2019 invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
xueliang.zhongf51bc622016-11-04 09:23:32 +00002020 __ CompareAndBranchIfZero(temp2, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002021 // If heap poisoning is enabled, `temp2` has been unpoisoned
2022 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2023 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2024 __ Ldrh(temp2, MemOperand(temp2, primitive_offset));
2025 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00002026 __ CompareAndBranchIfNonZero(temp2, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002027 }
2028
2029 // For the same reason given earlier, `temp1` is not trashed by the
2030 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
2031 // /* HeapReference<Class> */ temp2 = src->klass_
2032 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2033 invoke, temp2_loc, src, class_offset, temp3_loc, /* needs_null_check */ false);
2034 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
2035 __ Cmp(temp1, temp2);
2036
2037 if (optimizations.GetDestinationIsTypedObjectArray()) {
2038 vixl32::Label do_copy;
Artem Serov517d9f62016-12-12 15:51:15 +00002039 __ B(eq, &do_copy, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002040 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2041 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2042 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
2043 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2044 // We do not need to emit a read barrier for the following
2045 // heap reference load, as `temp1` is only used in a
2046 // comparison with null below, and this reference is not
2047 // kept afterwards.
2048 __ Ldr(temp1, MemOperand(temp1, super_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00002049 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002050 __ Bind(&do_copy);
2051 } else {
2052 __ B(ne, intrinsic_slow_path->GetEntryLabel());
2053 }
2054 } else {
2055 // Non read barrier code.
2056
2057 // /* HeapReference<Class> */ temp1 = dest->klass_
2058 __ Ldr(temp1, MemOperand(dest, class_offset));
2059 // /* HeapReference<Class> */ temp2 = src->klass_
2060 __ Ldr(temp2, MemOperand(src, class_offset));
2061 bool did_unpoison = false;
2062 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2063 !optimizations.GetSourceIsNonPrimitiveArray()) {
2064 // One or two of the references need to be unpoisoned. Unpoison them
2065 // both to make the identity check valid.
2066 assembler->MaybeUnpoisonHeapReference(temp1);
2067 assembler->MaybeUnpoisonHeapReference(temp2);
2068 did_unpoison = true;
2069 }
2070
2071 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2072 // Bail out if the destination is not a non primitive array.
2073 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2074 __ Ldr(temp3, MemOperand(temp1, component_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00002075 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002076 assembler->MaybeUnpoisonHeapReference(temp3);
2077 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2078 __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
2079 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00002080 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002081 }
2082
2083 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2084 // Bail out if the source is not a non primitive array.
2085 // /* HeapReference<Class> */ temp3 = temp2->component_type_
2086 __ Ldr(temp3, MemOperand(temp2, component_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00002087 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002088 assembler->MaybeUnpoisonHeapReference(temp3);
2089 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2090 __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
2091 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00002092 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002093 }
2094
2095 __ Cmp(temp1, temp2);
2096
2097 if (optimizations.GetDestinationIsTypedObjectArray()) {
2098 vixl32::Label do_copy;
Artem Serov517d9f62016-12-12 15:51:15 +00002099 __ B(eq, &do_copy, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002100 if (!did_unpoison) {
2101 assembler->MaybeUnpoisonHeapReference(temp1);
2102 }
2103 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2104 __ Ldr(temp1, MemOperand(temp1, component_offset));
2105 assembler->MaybeUnpoisonHeapReference(temp1);
2106 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2107 __ Ldr(temp1, MemOperand(temp1, super_offset));
2108 // No need to unpoison the result, we're comparing against null.
xueliang.zhongf51bc622016-11-04 09:23:32 +00002109 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002110 __ Bind(&do_copy);
2111 } else {
2112 __ B(ne, intrinsic_slow_path->GetEntryLabel());
2113 }
2114 }
2115 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2116 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2117 // Bail out if the source is not a non primitive array.
2118 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2119 // /* HeapReference<Class> */ temp1 = src->klass_
2120 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2121 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
2122 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2123 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2124 invoke, temp3_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
xueliang.zhongf51bc622016-11-04 09:23:32 +00002125 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002126 // If heap poisoning is enabled, `temp3` has been unpoisoned
2127 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2128 } else {
2129 // /* HeapReference<Class> */ temp1 = src->klass_
2130 __ Ldr(temp1, MemOperand(src, class_offset));
2131 assembler->MaybeUnpoisonHeapReference(temp1);
2132 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2133 __ Ldr(temp3, MemOperand(temp1, component_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00002134 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002135 assembler->MaybeUnpoisonHeapReference(temp3);
2136 }
2137 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2138 __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
2139 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00002140 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002141 }
2142
Roland Levillain1663d162017-03-17 15:15:21 +00002143 if (length.IsConstant() && Int32ConstantFrom(length) == 0) {
2144 // Null constant length: not need to emit the loop code at all.
Anton Kirilov5ec62182016-10-13 20:16:02 +01002145 } else {
Roland Levillain1663d162017-03-17 15:15:21 +00002146 vixl32::Label done;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002147 const DataType::Type type = DataType::Type::kReference;
2148 const int32_t element_size = DataType::Size(type);
Roland Levillain1663d162017-03-17 15:15:21 +00002149
2150 if (length.IsRegister()) {
2151 // Don't enter the copy loop if the length is null.
2152 __ CompareAndBranchIfZero(RegisterFrom(length), &done, /* is_far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002153 }
Roland Levillain1663d162017-03-17 15:15:21 +00002154
2155 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2156 // TODO: Also convert this intrinsic to the IsGcMarking strategy?
2157
2158 // SystemArrayCopy implementation for Baker read barriers (see
Roland Levillain9983e302017-07-14 14:34:22 +01002159 // also CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier):
Roland Levillain1663d162017-03-17 15:15:21 +00002160 //
2161 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
2162 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
2163 // bool is_gray = (rb_state == ReadBarrier::GrayState());
2164 // if (is_gray) {
2165 // // Slow-path copy.
2166 // do {
2167 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
2168 // } while (src_ptr != end_ptr)
2169 // } else {
2170 // // Fast-path copy.
2171 // do {
2172 // *dest_ptr++ = *src_ptr++;
2173 // } while (src_ptr != end_ptr)
2174 // }
2175
2176 // /* int32_t */ monitor = src->monitor_
2177 __ Ldr(temp2, MemOperand(src, monitor_offset));
2178 // /* LockWord */ lock_word = LockWord(monitor)
2179 static_assert(sizeof(LockWord) == sizeof(int32_t),
2180 "art::LockWord and int32_t have different sizes.");
2181
2182 // Introduce a dependency on the lock_word including the rb_state,
2183 // which shall prevent load-load reordering without using
2184 // a memory barrier (which would be more expensive).
2185 // `src` is unchanged by this operation, but its value now depends
2186 // on `temp2`.
2187 __ Add(src, src, Operand(temp2, vixl32::LSR, 32));
2188
2189 // Compute the base source address in `temp1`.
2190 // Note that `temp1` (the base source address) is computed from
2191 // `src` (and `src_pos`) here, and thus honors the artificial
2192 // dependency of `src` on `temp2`.
2193 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2194 // Compute the end source address in `temp3`.
2195 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2196 // The base destination address is computed later, as `temp2` is
2197 // used for intermediate computations.
2198
2199 // Slow path used to copy array when `src` is gray.
2200 // Note that the base destination address is computed in `temp2`
2201 // by the slow path code.
2202 SlowPathCodeARMVIXL* read_barrier_slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01002203 new (codegen_->GetScopedAllocator()) ReadBarrierSystemArrayCopySlowPathARMVIXL(invoke);
Roland Levillain1663d162017-03-17 15:15:21 +00002204 codegen_->AddSlowPath(read_barrier_slow_path);
2205
2206 // Given the numeric representation, it's enough to check the low bit of the
2207 // rb_state. We do that by shifting the bit out of the lock word with LSRS
2208 // which can be a 16-bit instruction unlike the TST immediate.
2209 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
2210 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
2211 __ Lsrs(temp2, temp2, LockWord::kReadBarrierStateShift + 1);
2212 // Carry flag is the last bit shifted out by LSRS.
2213 __ B(cs, read_barrier_slow_path->GetEntryLabel());
2214
2215 // Fast-path copy.
2216 // Compute the base destination address in `temp2`.
2217 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2218 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2219 // poison/unpoison.
2220 vixl32::Label loop;
2221 __ Bind(&loop);
2222 {
2223 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2224 const vixl32::Register temp_reg = temps.Acquire();
2225 __ Ldr(temp_reg, MemOperand(temp1, element_size, PostIndex));
2226 __ Str(temp_reg, MemOperand(temp2, element_size, PostIndex));
2227 }
2228 __ Cmp(temp1, temp3);
2229 __ B(ne, &loop, /* far_target */ false);
2230
2231 __ Bind(read_barrier_slow_path->GetExitLabel());
2232 } else {
2233 // Non read barrier code.
2234 // Compute the base source address in `temp1`.
2235 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2236 // Compute the base destination address in `temp2`.
2237 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2238 // Compute the end source address in `temp3`.
2239 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2240 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2241 // poison/unpoison.
2242 vixl32::Label loop;
2243 __ Bind(&loop);
2244 {
2245 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2246 const vixl32::Register temp_reg = temps.Acquire();
2247 __ Ldr(temp_reg, MemOperand(temp1, element_size, PostIndex));
2248 __ Str(temp_reg, MemOperand(temp2, element_size, PostIndex));
2249 }
2250 __ Cmp(temp1, temp3);
2251 __ B(ne, &loop, /* far_target */ false);
2252 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01002253 __ Bind(&done);
2254 }
2255
2256 // We only need one card marking on the destination array.
2257 codegen_->MarkGCCard(temp1, temp2, dest, NoReg, /* value_can_be_null */ false);
2258
2259 __ Bind(intrinsic_slow_path->GetExitLabel());
2260}
2261
Vladimir Markoca6fff82017-10-03 14:49:14 +01002262static void CreateFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01002263 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2264 // the code generator. Furthermore, the register allocator creates fixed live intervals
2265 // for all caller-saved registers because we are doing a function call. As a result, if
2266 // the input and output locations are unallocated, the register allocator runs out of
2267 // registers and fails; however, a debuggable graph is not the common case.
2268 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2269 return;
2270 }
2271
2272 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002273 DCHECK_EQ(invoke->InputAt(0)->GetType(), DataType::Type::kFloat64);
2274 DCHECK_EQ(invoke->GetType(), DataType::Type::kFloat64);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002275
Vladimir Markoca6fff82017-10-03 14:49:14 +01002276 LocationSummary* const locations =
2277 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002278 const InvokeRuntimeCallingConventionARMVIXL calling_convention;
2279
2280 locations->SetInAt(0, Location::RequiresFpuRegister());
2281 locations->SetOut(Location::RequiresFpuRegister());
2282 // Native code uses the soft float ABI.
2283 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
2284 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
2285}
2286
Vladimir Markoca6fff82017-10-03 14:49:14 +01002287static void CreateFPFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01002288 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2289 // the code generator. Furthermore, the register allocator creates fixed live intervals
2290 // for all caller-saved registers because we are doing a function call. As a result, if
2291 // the input and output locations are unallocated, the register allocator runs out of
2292 // registers and fails; however, a debuggable graph is not the common case.
2293 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2294 return;
2295 }
2296
2297 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002298 DCHECK_EQ(invoke->InputAt(0)->GetType(), DataType::Type::kFloat64);
2299 DCHECK_EQ(invoke->InputAt(1)->GetType(), DataType::Type::kFloat64);
2300 DCHECK_EQ(invoke->GetType(), DataType::Type::kFloat64);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002301
Vladimir Markoca6fff82017-10-03 14:49:14 +01002302 LocationSummary* const locations =
2303 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002304 const InvokeRuntimeCallingConventionARMVIXL calling_convention;
2305
2306 locations->SetInAt(0, Location::RequiresFpuRegister());
2307 locations->SetInAt(1, Location::RequiresFpuRegister());
2308 locations->SetOut(Location::RequiresFpuRegister());
2309 // Native code uses the soft float ABI.
2310 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
2311 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
2312 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
2313 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(3)));
2314}
2315
2316static void GenFPToFPCall(HInvoke* invoke,
2317 ArmVIXLAssembler* assembler,
2318 CodeGeneratorARMVIXL* codegen,
2319 QuickEntrypointEnum entry) {
2320 LocationSummary* const locations = invoke->GetLocations();
2321
2322 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2323 DCHECK(locations->WillCall() && locations->Intrinsified());
2324
2325 // Native code uses the soft float ABI.
2326 __ Vmov(RegisterFrom(locations->GetTemp(0)),
2327 RegisterFrom(locations->GetTemp(1)),
2328 InputDRegisterAt(invoke, 0));
2329 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
2330 __ Vmov(OutputDRegister(invoke),
2331 RegisterFrom(locations->GetTemp(0)),
2332 RegisterFrom(locations->GetTemp(1)));
2333}
2334
2335static void GenFPFPToFPCall(HInvoke* invoke,
2336 ArmVIXLAssembler* assembler,
2337 CodeGeneratorARMVIXL* codegen,
2338 QuickEntrypointEnum entry) {
2339 LocationSummary* const locations = invoke->GetLocations();
2340
2341 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2342 DCHECK(locations->WillCall() && locations->Intrinsified());
2343
2344 // Native code uses the soft float ABI.
2345 __ Vmov(RegisterFrom(locations->GetTemp(0)),
2346 RegisterFrom(locations->GetTemp(1)),
2347 InputDRegisterAt(invoke, 0));
2348 __ Vmov(RegisterFrom(locations->GetTemp(2)),
2349 RegisterFrom(locations->GetTemp(3)),
2350 InputDRegisterAt(invoke, 1));
2351 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
2352 __ Vmov(OutputDRegister(invoke),
2353 RegisterFrom(locations->GetTemp(0)),
2354 RegisterFrom(locations->GetTemp(1)));
2355}
2356
2357void IntrinsicLocationsBuilderARMVIXL::VisitMathCos(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002358 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002359}
2360
2361void IntrinsicCodeGeneratorARMVIXL::VisitMathCos(HInvoke* invoke) {
2362 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCos);
2363}
2364
2365void IntrinsicLocationsBuilderARMVIXL::VisitMathSin(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002366 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002367}
2368
2369void IntrinsicCodeGeneratorARMVIXL::VisitMathSin(HInvoke* invoke) {
2370 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSin);
2371}
2372
2373void IntrinsicLocationsBuilderARMVIXL::VisitMathAcos(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002374 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002375}
2376
2377void IntrinsicCodeGeneratorARMVIXL::VisitMathAcos(HInvoke* invoke) {
2378 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAcos);
2379}
2380
2381void IntrinsicLocationsBuilderARMVIXL::VisitMathAsin(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002382 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002383}
2384
2385void IntrinsicCodeGeneratorARMVIXL::VisitMathAsin(HInvoke* invoke) {
2386 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAsin);
2387}
2388
2389void IntrinsicLocationsBuilderARMVIXL::VisitMathAtan(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002390 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002391}
2392
2393void IntrinsicCodeGeneratorARMVIXL::VisitMathAtan(HInvoke* invoke) {
2394 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan);
2395}
2396
2397void IntrinsicLocationsBuilderARMVIXL::VisitMathCbrt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002398 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002399}
2400
2401void IntrinsicCodeGeneratorARMVIXL::VisitMathCbrt(HInvoke* invoke) {
2402 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCbrt);
2403}
2404
2405void IntrinsicLocationsBuilderARMVIXL::VisitMathCosh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002406 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002407}
2408
2409void IntrinsicCodeGeneratorARMVIXL::VisitMathCosh(HInvoke* invoke) {
2410 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCosh);
2411}
2412
2413void IntrinsicLocationsBuilderARMVIXL::VisitMathExp(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002414 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002415}
2416
2417void IntrinsicCodeGeneratorARMVIXL::VisitMathExp(HInvoke* invoke) {
2418 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExp);
2419}
2420
2421void IntrinsicLocationsBuilderARMVIXL::VisitMathExpm1(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002422 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002423}
2424
2425void IntrinsicCodeGeneratorARMVIXL::VisitMathExpm1(HInvoke* invoke) {
2426 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExpm1);
2427}
2428
2429void IntrinsicLocationsBuilderARMVIXL::VisitMathLog(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002430 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002431}
2432
2433void IntrinsicCodeGeneratorARMVIXL::VisitMathLog(HInvoke* invoke) {
2434 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog);
2435}
2436
2437void IntrinsicLocationsBuilderARMVIXL::VisitMathLog10(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002438 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002439}
2440
2441void IntrinsicCodeGeneratorARMVIXL::VisitMathLog10(HInvoke* invoke) {
2442 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog10);
2443}
2444
2445void IntrinsicLocationsBuilderARMVIXL::VisitMathSinh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002446 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002447}
2448
2449void IntrinsicCodeGeneratorARMVIXL::VisitMathSinh(HInvoke* invoke) {
2450 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSinh);
2451}
2452
2453void IntrinsicLocationsBuilderARMVIXL::VisitMathTan(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002454 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002455}
2456
2457void IntrinsicCodeGeneratorARMVIXL::VisitMathTan(HInvoke* invoke) {
2458 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTan);
2459}
2460
2461void IntrinsicLocationsBuilderARMVIXL::VisitMathTanh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002462 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002463}
2464
2465void IntrinsicCodeGeneratorARMVIXL::VisitMathTanh(HInvoke* invoke) {
2466 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTanh);
2467}
2468
2469void IntrinsicLocationsBuilderARMVIXL::VisitMathAtan2(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002470 CreateFPFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002471}
2472
2473void IntrinsicCodeGeneratorARMVIXL::VisitMathAtan2(HInvoke* invoke) {
2474 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan2);
2475}
2476
Vladimir Marko4d179872018-01-19 14:50:10 +00002477void IntrinsicLocationsBuilderARMVIXL::VisitMathPow(HInvoke* invoke) {
2478 CreateFPFPToFPCallLocations(allocator_, invoke);
2479}
2480
2481void IntrinsicCodeGeneratorARMVIXL::VisitMathPow(HInvoke* invoke) {
2482 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickPow);
2483}
2484
Anton Kirilov5ec62182016-10-13 20:16:02 +01002485void IntrinsicLocationsBuilderARMVIXL::VisitMathHypot(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002486 CreateFPFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002487}
2488
2489void IntrinsicCodeGeneratorARMVIXL::VisitMathHypot(HInvoke* invoke) {
2490 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickHypot);
2491}
2492
2493void IntrinsicLocationsBuilderARMVIXL::VisitMathNextAfter(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002494 CreateFPFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002495}
2496
2497void IntrinsicCodeGeneratorARMVIXL::VisitMathNextAfter(HInvoke* invoke) {
2498 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter);
2499}
2500
2501void IntrinsicLocationsBuilderARMVIXL::VisitIntegerReverse(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002502 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002503}
2504
2505void IntrinsicCodeGeneratorARMVIXL::VisitIntegerReverse(HInvoke* invoke) {
2506 ArmVIXLAssembler* assembler = GetAssembler();
2507 __ Rbit(OutputRegister(invoke), InputRegisterAt(invoke, 0));
2508}
2509
2510void IntrinsicLocationsBuilderARMVIXL::VisitLongReverse(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002511 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002512}
2513
2514void IntrinsicCodeGeneratorARMVIXL::VisitLongReverse(HInvoke* invoke) {
2515 ArmVIXLAssembler* assembler = GetAssembler();
2516 LocationSummary* locations = invoke->GetLocations();
2517
2518 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
2519 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
2520 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
2521 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
2522
2523 __ Rbit(out_reg_lo, in_reg_hi);
2524 __ Rbit(out_reg_hi, in_reg_lo);
2525}
2526
2527void IntrinsicLocationsBuilderARMVIXL::VisitIntegerReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002528 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002529}
2530
2531void IntrinsicCodeGeneratorARMVIXL::VisitIntegerReverseBytes(HInvoke* invoke) {
2532 ArmVIXLAssembler* assembler = GetAssembler();
2533 __ Rev(OutputRegister(invoke), InputRegisterAt(invoke, 0));
2534}
2535
2536void IntrinsicLocationsBuilderARMVIXL::VisitLongReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002537 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002538}
2539
2540void IntrinsicCodeGeneratorARMVIXL::VisitLongReverseBytes(HInvoke* invoke) {
2541 ArmVIXLAssembler* assembler = GetAssembler();
2542 LocationSummary* locations = invoke->GetLocations();
2543
2544 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
2545 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
2546 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
2547 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
2548
2549 __ Rev(out_reg_lo, in_reg_hi);
2550 __ Rev(out_reg_hi, in_reg_lo);
2551}
2552
2553void IntrinsicLocationsBuilderARMVIXL::VisitShortReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002554 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002555}
2556
2557void IntrinsicCodeGeneratorARMVIXL::VisitShortReverseBytes(HInvoke* invoke) {
2558 ArmVIXLAssembler* assembler = GetAssembler();
2559 __ Revsh(OutputRegister(invoke), InputRegisterAt(invoke, 0));
2560}
2561
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002562static void GenBitCount(HInvoke* instr, DataType::Type type, ArmVIXLAssembler* assembler) {
2563 DCHECK(DataType::IsIntOrLongType(type)) << type;
2564 DCHECK_EQ(instr->GetType(), DataType::Type::kInt32);
2565 DCHECK_EQ(DataType::Kind(instr->InputAt(0)->GetType()), type);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002566
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002567 bool is_long = type == DataType::Type::kInt64;
Anton Kirilov5ec62182016-10-13 20:16:02 +01002568 LocationSummary* locations = instr->GetLocations();
2569 Location in = locations->InAt(0);
2570 vixl32::Register src_0 = is_long ? LowRegisterFrom(in) : RegisterFrom(in);
2571 vixl32::Register src_1 = is_long ? HighRegisterFrom(in) : src_0;
2572 vixl32::SRegister tmp_s = LowSRegisterFrom(locations->GetTemp(0));
2573 vixl32::DRegister tmp_d = DRegisterFrom(locations->GetTemp(0));
2574 vixl32::Register out_r = OutputRegister(instr);
2575
2576 // Move data from core register(s) to temp D-reg for bit count calculation, then move back.
2577 // According to Cortex A57 and A72 optimization guides, compared to transferring to full D-reg,
2578 // transferring data from core reg to upper or lower half of vfp D-reg requires extra latency,
2579 // That's why for integer bit count, we use 'vmov d0, r0, r0' instead of 'vmov d0[0], r0'.
2580 __ Vmov(tmp_d, src_1, src_0); // Temp DReg |--src_1|--src_0|
2581 __ Vcnt(Untyped8, tmp_d, tmp_d); // Temp DReg |c|c|c|c|c|c|c|c|
2582 __ Vpaddl(U8, tmp_d, tmp_d); // Temp DReg |--c|--c|--c|--c|
2583 __ Vpaddl(U16, tmp_d, tmp_d); // Temp DReg |------c|------c|
2584 if (is_long) {
2585 __ Vpaddl(U32, tmp_d, tmp_d); // Temp DReg |--------------c|
2586 }
2587 __ Vmov(out_r, tmp_s);
2588}
2589
2590void IntrinsicLocationsBuilderARMVIXL::VisitIntegerBitCount(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002591 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002592 invoke->GetLocations()->AddTemp(Location::RequiresFpuRegister());
2593}
2594
2595void IntrinsicCodeGeneratorARMVIXL::VisitIntegerBitCount(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002596 GenBitCount(invoke, DataType::Type::kInt32, GetAssembler());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002597}
2598
2599void IntrinsicLocationsBuilderARMVIXL::VisitLongBitCount(HInvoke* invoke) {
2600 VisitIntegerBitCount(invoke);
2601}
2602
2603void IntrinsicCodeGeneratorARMVIXL::VisitLongBitCount(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002604 GenBitCount(invoke, DataType::Type::kInt64, GetAssembler());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002605}
2606
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002607static void GenHighestOneBit(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002608 DataType::Type type,
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002609 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002610 DCHECK(DataType::IsIntOrLongType(type));
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002611
2612 ArmVIXLAssembler* assembler = codegen->GetAssembler();
2613 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2614 const vixl32::Register temp = temps.Acquire();
2615
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002616 if (type == DataType::Type::kInt64) {
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002617 LocationSummary* locations = invoke->GetLocations();
2618 Location in = locations->InAt(0);
2619 Location out = locations->Out();
2620
2621 vixl32::Register in_reg_lo = LowRegisterFrom(in);
2622 vixl32::Register in_reg_hi = HighRegisterFrom(in);
2623 vixl32::Register out_reg_lo = LowRegisterFrom(out);
2624 vixl32::Register out_reg_hi = HighRegisterFrom(out);
2625
2626 __ Mov(temp, 0x80000000); // Modified immediate.
2627 __ Clz(out_reg_lo, in_reg_lo);
2628 __ Clz(out_reg_hi, in_reg_hi);
2629 __ Lsr(out_reg_lo, temp, out_reg_lo);
2630 __ Lsrs(out_reg_hi, temp, out_reg_hi);
2631
2632 // Discard result for lowest 32 bits if highest 32 bits are not zero.
2633 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
2634 // we check that the output is in a low register, so that a 16-bit MOV
2635 // encoding can be used. If output is in a high register, then we generate
2636 // 4 more bytes of code to avoid a branch.
2637 Operand mov_src(0);
2638 if (!out_reg_lo.IsLow()) {
2639 __ Mov(LeaveFlags, temp, 0);
2640 mov_src = Operand(temp);
2641 }
2642 ExactAssemblyScope it_scope(codegen->GetVIXLAssembler(),
2643 2 * vixl32::k16BitT32InstructionSizeInBytes,
2644 CodeBufferCheckScope::kExactSize);
2645 __ it(ne);
2646 __ mov(ne, out_reg_lo, mov_src);
2647 } else {
2648 vixl32::Register out = OutputRegister(invoke);
2649 vixl32::Register in = InputRegisterAt(invoke, 0);
2650
2651 __ Mov(temp, 0x80000000); // Modified immediate.
2652 __ Clz(out, in);
2653 __ Lsr(out, temp, out);
2654 }
2655}
2656
2657void IntrinsicLocationsBuilderARMVIXL::VisitIntegerHighestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002658 CreateIntToIntLocations(allocator_, invoke);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002659}
2660
2661void IntrinsicCodeGeneratorARMVIXL::VisitIntegerHighestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002662 GenHighestOneBit(invoke, DataType::Type::kInt32, codegen_);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002663}
2664
2665void IntrinsicLocationsBuilderARMVIXL::VisitLongHighestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002666 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002667}
2668
2669void IntrinsicCodeGeneratorARMVIXL::VisitLongHighestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002670 GenHighestOneBit(invoke, DataType::Type::kInt64, codegen_);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002671}
2672
2673static void GenLowestOneBit(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002674 DataType::Type type,
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002675 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002676 DCHECK(DataType::IsIntOrLongType(type));
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002677
2678 ArmVIXLAssembler* assembler = codegen->GetAssembler();
2679 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2680 const vixl32::Register temp = temps.Acquire();
2681
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002682 if (type == DataType::Type::kInt64) {
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002683 LocationSummary* locations = invoke->GetLocations();
2684 Location in = locations->InAt(0);
2685 Location out = locations->Out();
2686
2687 vixl32::Register in_reg_lo = LowRegisterFrom(in);
2688 vixl32::Register in_reg_hi = HighRegisterFrom(in);
2689 vixl32::Register out_reg_lo = LowRegisterFrom(out);
2690 vixl32::Register out_reg_hi = HighRegisterFrom(out);
2691
2692 __ Rsb(out_reg_hi, in_reg_hi, 0);
2693 __ Rsb(out_reg_lo, in_reg_lo, 0);
2694 __ And(out_reg_hi, out_reg_hi, in_reg_hi);
2695 // The result of this operation is 0 iff in_reg_lo is 0
2696 __ Ands(out_reg_lo, out_reg_lo, in_reg_lo);
2697
2698 // Discard result for highest 32 bits if lowest 32 bits are not zero.
2699 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
2700 // we check that the output is in a low register, so that a 16-bit MOV
2701 // encoding can be used. If output is in a high register, then we generate
2702 // 4 more bytes of code to avoid a branch.
2703 Operand mov_src(0);
2704 if (!out_reg_lo.IsLow()) {
2705 __ Mov(LeaveFlags, temp, 0);
2706 mov_src = Operand(temp);
2707 }
2708 ExactAssemblyScope it_scope(codegen->GetVIXLAssembler(),
2709 2 * vixl32::k16BitT32InstructionSizeInBytes,
2710 CodeBufferCheckScope::kExactSize);
2711 __ it(ne);
2712 __ mov(ne, out_reg_hi, mov_src);
2713 } else {
2714 vixl32::Register out = OutputRegister(invoke);
2715 vixl32::Register in = InputRegisterAt(invoke, 0);
2716
2717 __ Rsb(temp, in, 0);
2718 __ And(out, temp, in);
2719 }
2720}
2721
2722void IntrinsicLocationsBuilderARMVIXL::VisitIntegerLowestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002723 CreateIntToIntLocations(allocator_, invoke);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002724}
2725
2726void IntrinsicCodeGeneratorARMVIXL::VisitIntegerLowestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002727 GenLowestOneBit(invoke, DataType::Type::kInt32, codegen_);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002728}
2729
2730void IntrinsicLocationsBuilderARMVIXL::VisitLongLowestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002731 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002732}
2733
2734void IntrinsicCodeGeneratorARMVIXL::VisitLongLowestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002735 GenLowestOneBit(invoke, DataType::Type::kInt64, codegen_);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002736}
2737
Anton Kirilov5ec62182016-10-13 20:16:02 +01002738void IntrinsicLocationsBuilderARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002739 LocationSummary* locations =
2740 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002741 locations->SetInAt(0, Location::RequiresRegister());
2742 locations->SetInAt(1, Location::RequiresRegister());
2743 locations->SetInAt(2, Location::RequiresRegister());
2744 locations->SetInAt(3, Location::RequiresRegister());
2745 locations->SetInAt(4, Location::RequiresRegister());
2746
2747 // Temporary registers to store lengths of strings and for calculations.
2748 locations->AddTemp(Location::RequiresRegister());
2749 locations->AddTemp(Location::RequiresRegister());
2750 locations->AddTemp(Location::RequiresRegister());
2751}
2752
2753void IntrinsicCodeGeneratorARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2754 ArmVIXLAssembler* assembler = GetAssembler();
2755 LocationSummary* locations = invoke->GetLocations();
2756
2757 // Check assumption that sizeof(Char) is 2 (used in scaling below).
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002758 const size_t char_size = DataType::Size(DataType::Type::kUint16);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002759 DCHECK_EQ(char_size, 2u);
2760
2761 // Location of data in char array buffer.
2762 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2763
2764 // Location of char array data in string.
2765 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2766
2767 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2768 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2769 vixl32::Register srcObj = InputRegisterAt(invoke, 0);
2770 vixl32::Register srcBegin = InputRegisterAt(invoke, 1);
2771 vixl32::Register srcEnd = InputRegisterAt(invoke, 2);
2772 vixl32::Register dstObj = InputRegisterAt(invoke, 3);
2773 vixl32::Register dstBegin = InputRegisterAt(invoke, 4);
2774
2775 vixl32::Register num_chr = RegisterFrom(locations->GetTemp(0));
2776 vixl32::Register src_ptr = RegisterFrom(locations->GetTemp(1));
2777 vixl32::Register dst_ptr = RegisterFrom(locations->GetTemp(2));
2778
2779 vixl32::Label done, compressed_string_loop;
Anton Kirilov6f644202017-02-27 18:29:45 +00002780 vixl32::Label* final_label = codegen_->GetFinalLabel(invoke, &done);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002781 // dst to be copied.
2782 __ Add(dst_ptr, dstObj, data_offset);
2783 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, vixl32::LSL, 1));
2784
2785 __ Subs(num_chr, srcEnd, srcBegin);
2786 // Early out for valid zero-length retrievals.
Anton Kirilov6f644202017-02-27 18:29:45 +00002787 __ B(eq, final_label, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002788
2789 // src range to copy.
2790 __ Add(src_ptr, srcObj, value_offset);
2791
2792 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2793 vixl32::Register temp;
2794 vixl32::Label compressed_string_preloop;
2795 if (mirror::kUseStringCompression) {
2796 // Location of count in string.
2797 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2798 temp = temps.Acquire();
2799 // String's length.
2800 __ Ldr(temp, MemOperand(srcObj, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002801 __ Tst(temp, 1);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002802 temps.Release(temp);
Artem Serov517d9f62016-12-12 15:51:15 +00002803 __ B(eq, &compressed_string_preloop, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002804 }
2805 __ Add(src_ptr, src_ptr, Operand(srcBegin, vixl32::LSL, 1));
2806
2807 // Do the copy.
2808 vixl32::Label loop, remainder;
2809
2810 temp = temps.Acquire();
2811 // Save repairing the value of num_chr on the < 4 character path.
2812 __ Subs(temp, num_chr, 4);
Artem Serov517d9f62016-12-12 15:51:15 +00002813 __ B(lt, &remainder, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002814
2815 // Keep the result of the earlier subs, we are going to fetch at least 4 characters.
2816 __ Mov(num_chr, temp);
2817
2818 // Main loop used for longer fetches loads and stores 4x16-bit characters at a time.
2819 // (LDRD/STRD fault on unaligned addresses and it's not worth inlining extra code
2820 // to rectify these everywhere this intrinsic applies.)
2821 __ Bind(&loop);
2822 __ Ldr(temp, MemOperand(src_ptr, char_size * 2));
2823 __ Subs(num_chr, num_chr, 4);
2824 __ Str(temp, MemOperand(dst_ptr, char_size * 2));
2825 __ Ldr(temp, MemOperand(src_ptr, char_size * 4, PostIndex));
2826 __ Str(temp, MemOperand(dst_ptr, char_size * 4, PostIndex));
2827 temps.Release(temp);
Artem Serov517d9f62016-12-12 15:51:15 +00002828 __ B(ge, &loop, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002829
2830 __ Adds(num_chr, num_chr, 4);
Anton Kirilov6f644202017-02-27 18:29:45 +00002831 __ B(eq, final_label, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002832
2833 // Main loop for < 4 character case and remainder handling. Loads and stores one
2834 // 16-bit Java character at a time.
2835 __ Bind(&remainder);
2836 temp = temps.Acquire();
2837 __ Ldrh(temp, MemOperand(src_ptr, char_size, PostIndex));
2838 __ Subs(num_chr, num_chr, 1);
2839 __ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex));
2840 temps.Release(temp);
Artem Serov517d9f62016-12-12 15:51:15 +00002841 __ B(gt, &remainder, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002842
2843 if (mirror::kUseStringCompression) {
Anton Kirilov6f644202017-02-27 18:29:45 +00002844 __ B(final_label);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002845
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002846 const size_t c_char_size = DataType::Size(DataType::Type::kInt8);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002847 DCHECK_EQ(c_char_size, 1u);
2848 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2849 __ Bind(&compressed_string_preloop);
2850 __ Add(src_ptr, src_ptr, srcBegin);
2851 __ Bind(&compressed_string_loop);
2852 temp = temps.Acquire();
2853 __ Ldrb(temp, MemOperand(src_ptr, c_char_size, PostIndex));
2854 __ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex));
2855 temps.Release(temp);
2856 __ Subs(num_chr, num_chr, 1);
Artem Serov517d9f62016-12-12 15:51:15 +00002857 __ B(gt, &compressed_string_loop, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002858 }
2859
Anton Kirilov6f644202017-02-27 18:29:45 +00002860 if (done.IsReferenced()) {
2861 __ Bind(&done);
2862 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01002863}
2864
2865void IntrinsicLocationsBuilderARMVIXL::VisitFloatIsInfinite(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002866 CreateFPToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002867}
2868
2869void IntrinsicCodeGeneratorARMVIXL::VisitFloatIsInfinite(HInvoke* invoke) {
2870 ArmVIXLAssembler* const assembler = GetAssembler();
2871 const vixl32::Register out = OutputRegister(invoke);
2872 // Shifting left by 1 bit makes the value encodable as an immediate operand;
2873 // we don't care about the sign bit anyway.
2874 constexpr uint32_t infinity = kPositiveInfinityFloat << 1U;
2875
2876 __ Vmov(out, InputSRegisterAt(invoke, 0));
2877 // We don't care about the sign bit, so shift left.
2878 __ Lsl(out, out, 1);
2879 __ Eor(out, out, infinity);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002880 codegen_->GenerateConditionWithZero(kCondEQ, out, out);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002881}
2882
2883void IntrinsicLocationsBuilderARMVIXL::VisitDoubleIsInfinite(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002884 CreateFPToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002885}
2886
2887void IntrinsicCodeGeneratorARMVIXL::VisitDoubleIsInfinite(HInvoke* invoke) {
2888 ArmVIXLAssembler* const assembler = GetAssembler();
2889 const vixl32::Register out = OutputRegister(invoke);
2890 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2891 const vixl32::Register temp = temps.Acquire();
2892 // The highest 32 bits of double precision positive infinity separated into
2893 // two constants encodable as immediate operands.
2894 constexpr uint32_t infinity_high = 0x7f000000U;
2895 constexpr uint32_t infinity_high2 = 0x00f00000U;
2896
2897 static_assert((infinity_high | infinity_high2) ==
2898 static_cast<uint32_t>(kPositiveInfinityDouble >> 32U),
2899 "The constants do not add up to the high 32 bits of double "
2900 "precision positive infinity.");
2901 __ Vmov(temp, out, InputDRegisterAt(invoke, 0));
2902 __ Eor(out, out, infinity_high);
2903 __ Eor(out, out, infinity_high2);
2904 // We don't care about the sign bit, so shift left.
2905 __ Orr(out, temp, Operand(out, vixl32::LSL, 1));
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002906 codegen_->GenerateConditionWithZero(kCondEQ, out, out);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002907}
2908
Artem Serov9aee2d42017-01-06 15:58:31 +00002909void IntrinsicLocationsBuilderARMVIXL::VisitMathCeil(HInvoke* invoke) {
2910 if (features_.HasARMv8AInstructions()) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002911 CreateFPToFPLocations(allocator_, invoke);
Artem Serov9aee2d42017-01-06 15:58:31 +00002912 }
2913}
2914
2915void IntrinsicCodeGeneratorARMVIXL::VisitMathCeil(HInvoke* invoke) {
2916 ArmVIXLAssembler* assembler = GetAssembler();
2917 DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
2918 __ Vrintp(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
2919}
2920
2921void IntrinsicLocationsBuilderARMVIXL::VisitMathFloor(HInvoke* invoke) {
2922 if (features_.HasARMv8AInstructions()) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002923 CreateFPToFPLocations(allocator_, invoke);
Artem Serov9aee2d42017-01-06 15:58:31 +00002924 }
2925}
2926
2927void IntrinsicCodeGeneratorARMVIXL::VisitMathFloor(HInvoke* invoke) {
2928 ArmVIXLAssembler* assembler = GetAssembler();
2929 DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
2930 __ Vrintm(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
2931}
2932
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002933void IntrinsicLocationsBuilderARMVIXL::VisitIntegerValueOf(HInvoke* invoke) {
2934 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2935 IntrinsicVisitor::ComputeIntegerValueOfLocations(
2936 invoke,
2937 codegen_,
2938 LocationFrom(r0),
2939 LocationFrom(calling_convention.GetRegisterAt(0)));
2940}
2941
2942void IntrinsicCodeGeneratorARMVIXL::VisitIntegerValueOf(HInvoke* invoke) {
Vladimir Markoeebb8212018-06-05 14:57:24 +01002943 IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo(invoke);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002944 LocationSummary* locations = invoke->GetLocations();
2945 ArmVIXLAssembler* const assembler = GetAssembler();
2946
2947 vixl32::Register out = RegisterFrom(locations->Out());
2948 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2949 vixl32::Register temp = temps.Acquire();
2950 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2951 vixl32::Register argument = calling_convention.GetRegisterAt(0);
2952 if (invoke->InputAt(0)->IsConstant()) {
2953 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
Vladimir Markoeebb8212018-06-05 14:57:24 +01002954 if (info.value_boot_image_offset != 0u) {
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002955 // Just embed the j.l.Integer in the code.
Vladimir Markoeebb8212018-06-05 14:57:24 +01002956 codegen_->LoadBootImageAddress(out, info.value_boot_image_offset);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002957 } else {
Vladimir Markoeebb8212018-06-05 14:57:24 +01002958 DCHECK(locations->CanCall());
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002959 // Allocate and initialize a new j.l.Integer.
2960 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
2961 // JIT object table.
Vladimir Markoeebb8212018-06-05 14:57:24 +01002962 codegen_->LoadBootImageAddress(argument, info.integer_boot_image_offset);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002963 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2964 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2965 __ Mov(temp, value);
2966 assembler->StoreToOffset(kStoreWord, temp, out, info.value_offset);
2967 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2968 // one.
2969 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2970 }
2971 } else {
Vladimir Markoeebb8212018-06-05 14:57:24 +01002972 DCHECK(locations->CanCall());
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002973 vixl32::Register in = RegisterFrom(locations->InAt(0));
2974 // Check bounds of our cache.
2975 __ Add(out, in, -info.low);
Vladimir Markoeebb8212018-06-05 14:57:24 +01002976 __ Cmp(out, info.length);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002977 vixl32::Label allocate, done;
Anton Kirilovfd522532017-05-10 12:46:57 +01002978 __ B(hs, &allocate, /* is_far_target */ false);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002979 // If the value is within the bounds, load the j.l.Integer directly from the array.
Vladimir Markoeebb8212018-06-05 14:57:24 +01002980 codegen_->LoadBootImageAddress(temp, info.array_data_boot_image_offset);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002981 codegen_->LoadFromShiftedRegOffset(DataType::Type::kReference, locations->Out(), temp, out);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002982 assembler->MaybeUnpoisonHeapReference(out);
2983 __ B(&done);
2984 __ Bind(&allocate);
2985 // Otherwise allocate and initialize a new j.l.Integer.
Vladimir Markoeebb8212018-06-05 14:57:24 +01002986 codegen_->LoadBootImageAddress(argument, info.integer_boot_image_offset);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002987 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2988 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2989 assembler->StoreToOffset(kStoreWord, in, out, info.value_offset);
2990 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2991 // one.
2992 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2993 __ Bind(&done);
2994 }
2995}
2996
Nicolas Geoffray365719c2017-03-08 13:11:50 +00002997void IntrinsicLocationsBuilderARMVIXL::VisitThreadInterrupted(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002998 LocationSummary* locations =
2999 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Nicolas Geoffray365719c2017-03-08 13:11:50 +00003000 locations->SetOut(Location::RequiresRegister());
3001}
3002
3003void IntrinsicCodeGeneratorARMVIXL::VisitThreadInterrupted(HInvoke* invoke) {
3004 ArmVIXLAssembler* assembler = GetAssembler();
3005 vixl32::Register out = RegisterFrom(invoke->GetLocations()->Out());
3006 int32_t offset = Thread::InterruptedOffset<kArmPointerSize>().Int32Value();
3007 __ Ldr(out, MemOperand(tr, offset));
3008 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
3009 vixl32::Register temp = temps.Acquire();
3010 vixl32::Label done;
Anton Kirilovfd522532017-05-10 12:46:57 +01003011 vixl32::Label* const final_label = codegen_->GetFinalLabel(invoke, &done);
3012 __ CompareAndBranchIfZero(out, final_label, /* far_target */ false);
Nicolas Geoffray365719c2017-03-08 13:11:50 +00003013 __ Dmb(vixl32::ISH);
3014 __ Mov(temp, 0);
3015 assembler->StoreToOffset(kStoreWord, temp, tr, offset);
3016 __ Dmb(vixl32::ISH);
Anton Kirilovfd522532017-05-10 12:46:57 +01003017 if (done.IsReferenced()) {
3018 __ Bind(&done);
3019 }
Nicolas Geoffray365719c2017-03-08 13:11:50 +00003020}
3021
Hans Boehmc7b28de2018-03-09 17:05:28 -08003022void IntrinsicLocationsBuilderARMVIXL::VisitReachabilityFence(HInvoke* invoke) {
3023 LocationSummary* locations =
3024 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
3025 locations->SetInAt(0, Location::Any());
3026}
3027
3028void IntrinsicCodeGeneratorARMVIXL::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
3029
Anton Kirilov5ec62182016-10-13 20:16:02 +01003030UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundDouble) // Could be done by changing rounding mode, maybe?
Anton Kirilov5ec62182016-10-13 20:16:02 +01003031UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong) // High register pressure.
3032UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
Vladimir Markod254f5c2017-06-02 15:18:36 +00003033UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent)
Anton Kirilov5ec62182016-10-13 20:16:02 +01003034
Aart Bikff7d89c2016-11-07 08:49:28 -08003035UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf);
3036UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08003037UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBufferAppend);
3038UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBufferLength);
3039UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBufferToString);
3040UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBuilderAppend);
3041UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBuilderLength);
3042UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08003043
Anton Kirilov5ec62182016-10-13 20:16:02 +01003044// 1.8.
3045UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndAddInt)
3046UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndAddLong)
3047UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndSetInt)
3048UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndSetLong)
3049UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndSetObject)
3050
3051UNREACHABLE_INTRINSICS(ARMVIXL)
3052
3053#undef __
3054
3055} // namespace arm
3056} // namespace art