blob: 2963308da834e0597cb3acc8fd9445e77acb2c4f [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;
Anton Kirilov5ec62182016-10-13 20:16:02 +010044using helpers::Int32ConstantFrom;
45using helpers::LocationFrom;
46using helpers::LowRegisterFrom;
47using helpers::LowSRegisterFrom;
xueliang.zhong53463ba2017-02-16 15:18:03 +000048using helpers::HighSRegisterFrom;
Anton Kirilov5ec62182016-10-13 20:16:02 +010049using helpers::OutputDRegister;
50using helpers::OutputRegister;
Anton Kirilov5ec62182016-10-13 20:16:02 +010051using helpers::RegisterFrom;
52using helpers::SRegisterFrom;
53
54using namespace vixl::aarch32; // NOLINT(build/namespaces)
55
Artem Serov0fb37192016-12-06 18:13:40 +000056using vixl::ExactAssemblyScope;
57using vixl::CodeBufferCheckScope;
58
Anton Kirilov5ec62182016-10-13 20:16:02 +010059ArmVIXLAssembler* IntrinsicCodeGeneratorARMVIXL::GetAssembler() {
60 return codegen_->GetAssembler();
61}
62
63ArenaAllocator* IntrinsicCodeGeneratorARMVIXL::GetAllocator() {
Vladimir Markoca6fff82017-10-03 14:49:14 +010064 return codegen_->GetGraph()->GetAllocator();
Anton Kirilov5ec62182016-10-13 20:16:02 +010065}
66
67// Default slow-path for fallback (calling the managed code to handle the intrinsic) in an
68// intrinsified call. This will copy the arguments into the positions for a regular call.
69//
70// Note: The actual parameters are required to be in the locations given by the invoke's location
71// summary. If an intrinsic modifies those locations before a slowpath call, they must be
72// restored!
73//
74// Note: If an invoke wasn't sharpened, we will put down an invoke-virtual here. That's potentially
75// sub-optimal (compared to a direct pointer call), but this is a slow-path.
76
77class IntrinsicSlowPathARMVIXL : public SlowPathCodeARMVIXL {
78 public:
79 explicit IntrinsicSlowPathARMVIXL(HInvoke* invoke)
80 : SlowPathCodeARMVIXL(invoke), invoke_(invoke) {}
81
82 Location MoveArguments(CodeGenerator* codegen) {
Artem Serovd4cc5b22016-11-04 11:19:09 +000083 InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
Anton Kirilov5ec62182016-10-13 20:16:02 +010084 IntrinsicVisitor::MoveArguments(invoke_, codegen, &calling_convention_visitor);
85 return calling_convention_visitor.GetMethodLocation();
86 }
87
88 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
89 ArmVIXLAssembler* assembler = down_cast<ArmVIXLAssembler*>(codegen->GetAssembler());
90 __ Bind(GetEntryLabel());
91
92 SaveLiveRegisters(codegen, invoke_->GetLocations());
93
94 Location method_loc = MoveArguments(codegen);
95
96 if (invoke_->IsInvokeStaticOrDirect()) {
Vladimir Markoe7197bf2017-06-02 17:00:23 +010097 codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), method_loc, this);
Anton Kirilov5ec62182016-10-13 20:16:02 +010098 } else {
Vladimir Markoe7197bf2017-06-02 17:00:23 +010099 codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), method_loc, this);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100100 }
Anton Kirilov5ec62182016-10-13 20:16:02 +0100101
102 // Copy the result back to the expected output.
103 Location out = invoke_->GetLocations()->Out();
104 if (out.IsValid()) {
105 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
106 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
107 codegen->MoveFromReturnRegister(out, invoke_->GetType());
108 }
109
110 RestoreLiveRegisters(codegen, invoke_->GetLocations());
111 __ B(GetExitLabel());
112 }
113
114 const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPath"; }
115
116 private:
117 // The instruction where this slow path is happening.
118 HInvoke* const invoke_;
119
120 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARMVIXL);
121};
122
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000123// Compute base address for the System.arraycopy intrinsic in `base`.
124static void GenSystemArrayCopyBaseAddress(ArmVIXLAssembler* assembler,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100125 DataType::Type type,
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000126 const vixl32::Register& array,
127 const Location& pos,
128 const vixl32::Register& base) {
129 // This routine is only used by the SystemArrayCopy intrinsic at the
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100130 // moment. We can allow DataType::Type::kReference as `type` to implement
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000131 // the SystemArrayCopyChar intrinsic.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100132 DCHECK_EQ(type, DataType::Type::kReference);
133 const int32_t element_size = DataType::Size(type);
134 const uint32_t element_size_shift = DataType::SizeShift(type);
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000135 const uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
136
137 if (pos.IsConstant()) {
138 int32_t constant = Int32ConstantFrom(pos);
139 __ Add(base, array, element_size * constant + data_offset);
140 } else {
141 __ Add(base, array, Operand(RegisterFrom(pos), vixl32::LSL, element_size_shift));
142 __ Add(base, base, data_offset);
143 }
144}
145
146// Compute end address for the System.arraycopy intrinsic in `end`.
147static void GenSystemArrayCopyEndAddress(ArmVIXLAssembler* assembler,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100148 DataType::Type type,
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000149 const Location& copy_length,
150 const vixl32::Register& base,
151 const vixl32::Register& end) {
152 // This routine is only used by the SystemArrayCopy intrinsic at the
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100153 // moment. We can allow DataType::Type::kReference as `type` to implement
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000154 // the SystemArrayCopyChar intrinsic.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100155 DCHECK_EQ(type, DataType::Type::kReference);
156 const int32_t element_size = DataType::Size(type);
157 const uint32_t element_size_shift = DataType::SizeShift(type);
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000158
159 if (copy_length.IsConstant()) {
160 int32_t constant = Int32ConstantFrom(copy_length);
161 __ Add(end, base, element_size * constant);
162 } else {
163 __ Add(end, base, Operand(RegisterFrom(copy_length), vixl32::LSL, element_size_shift));
164 }
165}
166
Anton Kirilov5ec62182016-10-13 20:16:02 +0100167// Slow path implementing the SystemArrayCopy intrinsic copy loop with read barriers.
168class ReadBarrierSystemArrayCopySlowPathARMVIXL : public SlowPathCodeARMVIXL {
169 public:
170 explicit ReadBarrierSystemArrayCopySlowPathARMVIXL(HInstruction* instruction)
171 : SlowPathCodeARMVIXL(instruction) {
172 DCHECK(kEmitCompilerReadBarrier);
173 DCHECK(kUseBakerReadBarrier);
174 }
175
176 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
177 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
178 ArmVIXLAssembler* assembler = arm_codegen->GetAssembler();
179 LocationSummary* locations = instruction_->GetLocations();
180 DCHECK(locations->CanCall());
181 DCHECK(instruction_->IsInvokeStaticOrDirect())
182 << "Unexpected instruction in read barrier arraycopy slow path: "
183 << instruction_->DebugName();
184 DCHECK(instruction_->GetLocations()->Intrinsified());
185 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kSystemArrayCopy);
186
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100187 DataType::Type type = DataType::Type::kReference;
188 const int32_t element_size = DataType::Size(type);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100189
190 vixl32::Register dest = InputRegisterAt(instruction_, 2);
191 Location dest_pos = locations->InAt(3);
192 vixl32::Register src_curr_addr = RegisterFrom(locations->GetTemp(0));
193 vixl32::Register dst_curr_addr = RegisterFrom(locations->GetTemp(1));
194 vixl32::Register src_stop_addr = RegisterFrom(locations->GetTemp(2));
195 vixl32::Register tmp = RegisterFrom(locations->GetTemp(3));
196
197 __ Bind(GetEntryLabel());
198 // Compute the base destination address in `dst_curr_addr`.
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000199 GenSystemArrayCopyBaseAddress(assembler, type, dest, dest_pos, dst_curr_addr);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100200
201 vixl32::Label loop;
202 __ Bind(&loop);
203 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
204 assembler->MaybeUnpoisonHeapReference(tmp);
205 // TODO: Inline the mark bit check before calling the runtime?
206 // tmp = ReadBarrier::Mark(tmp);
207 // No need to save live registers; it's taken care of by the
208 // entrypoint. Also, there is no need to update the stack mask,
209 // as this runtime call will not trigger a garbage collection.
210 // (See ReadBarrierMarkSlowPathARM::EmitNativeCode for more
211 // explanations.)
212 DCHECK(!tmp.IsSP());
213 DCHECK(!tmp.IsLR());
214 DCHECK(!tmp.IsPC());
215 // IP is used internally by the ReadBarrierMarkRegX entry point
216 // as a temporary (and not preserved). It thus cannot be used by
217 // any live register in this slow path.
218 DCHECK(!src_curr_addr.Is(ip));
219 DCHECK(!dst_curr_addr.Is(ip));
220 DCHECK(!src_stop_addr.Is(ip));
221 DCHECK(!tmp.Is(ip));
222 DCHECK(tmp.IsRegister()) << tmp;
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000223 // TODO: Load the entrypoint once before the loop, instead of
224 // loading it at every iteration.
Anton Kirilov5ec62182016-10-13 20:16:02 +0100225 int32_t entry_point_offset =
Roland Levillain97c46462017-05-11 14:04:03 +0100226 Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(tmp.GetCode());
Anton Kirilov5ec62182016-10-13 20:16:02 +0100227 // This runtime call does not require a stack map.
228 arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
229 assembler->MaybePoisonHeapReference(tmp);
230 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
231 __ Cmp(src_curr_addr, src_stop_addr);
Artem Serov517d9f62016-12-12 15:51:15 +0000232 __ B(ne, &loop, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100233 __ B(GetExitLabel());
234 }
235
236 const char* GetDescription() const OVERRIDE {
237 return "ReadBarrierSystemArrayCopySlowPathARMVIXL";
238 }
239
240 private:
241 DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathARMVIXL);
242};
243
244IntrinsicLocationsBuilderARMVIXL::IntrinsicLocationsBuilderARMVIXL(CodeGeneratorARMVIXL* codegen)
Vladimir Markoca6fff82017-10-03 14:49:14 +0100245 : allocator_(codegen->GetGraph()->GetAllocator()),
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000246 codegen_(codegen),
Anton Kirilov5ec62182016-10-13 20:16:02 +0100247 assembler_(codegen->GetAssembler()),
248 features_(codegen->GetInstructionSetFeatures()) {}
249
250bool IntrinsicLocationsBuilderARMVIXL::TryDispatch(HInvoke* invoke) {
251 Dispatch(invoke);
252 LocationSummary* res = invoke->GetLocations();
253 if (res == nullptr) {
254 return false;
255 }
256 return res->Intrinsified();
257}
258
Vladimir Markoca6fff82017-10-03 14:49:14 +0100259static void CreateFPToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
260 LocationSummary* locations =
261 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100262 locations->SetInAt(0, Location::RequiresFpuRegister());
263 locations->SetOut(Location::RequiresRegister());
264}
265
Vladimir Markoca6fff82017-10-03 14:49:14 +0100266static void CreateIntToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
267 LocationSummary* locations =
268 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100269 locations->SetInAt(0, Location::RequiresRegister());
270 locations->SetOut(Location::RequiresFpuRegister());
271}
272
273static void MoveFPToInt(LocationSummary* locations, bool is64bit, ArmVIXLAssembler* assembler) {
274 Location input = locations->InAt(0);
275 Location output = locations->Out();
276 if (is64bit) {
277 __ Vmov(LowRegisterFrom(output), HighRegisterFrom(output), DRegisterFrom(input));
278 } else {
279 __ Vmov(RegisterFrom(output), SRegisterFrom(input));
280 }
281}
282
283static void MoveIntToFP(LocationSummary* locations, bool is64bit, ArmVIXLAssembler* assembler) {
284 Location input = locations->InAt(0);
285 Location output = locations->Out();
286 if (is64bit) {
287 __ Vmov(DRegisterFrom(output), LowRegisterFrom(input), HighRegisterFrom(input));
288 } else {
289 __ Vmov(SRegisterFrom(output), RegisterFrom(input));
290 }
291}
292
293void IntrinsicLocationsBuilderARMVIXL::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100294 CreateFPToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100295}
296void IntrinsicLocationsBuilderARMVIXL::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100297 CreateIntToFPLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100298}
299
300void IntrinsicCodeGeneratorARMVIXL::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
301 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
302}
303void IntrinsicCodeGeneratorARMVIXL::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
304 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
305}
306
307void IntrinsicLocationsBuilderARMVIXL::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100308 CreateFPToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100309}
310void IntrinsicLocationsBuilderARMVIXL::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100311 CreateIntToFPLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100312}
313
314void IntrinsicCodeGeneratorARMVIXL::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
315 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
316}
317void IntrinsicCodeGeneratorARMVIXL::VisitFloatIntBitsToFloat(HInvoke* invoke) {
318 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
319}
320
Vladimir Markoca6fff82017-10-03 14:49:14 +0100321static void CreateIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
322 LocationSummary* locations =
323 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100324 locations->SetInAt(0, Location::RequiresRegister());
325 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
326}
327
Vladimir Markoca6fff82017-10-03 14:49:14 +0100328static void CreateLongToLongLocationsWithOverlap(ArenaAllocator* allocator, HInvoke* invoke) {
329 LocationSummary* locations =
330 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +0100331 locations->SetInAt(0, Location::RequiresRegister());
332 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
333}
334
Vladimir Markoca6fff82017-10-03 14:49:14 +0100335static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
336 LocationSummary* locations =
337 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100338 locations->SetInAt(0, Location::RequiresFpuRegister());
339 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
340}
341
Anton Kirilov6f644202017-02-27 18:29:45 +0000342static void GenNumberOfLeadingZeros(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100343 DataType::Type type,
Anton Kirilov6f644202017-02-27 18:29:45 +0000344 CodeGeneratorARMVIXL* codegen) {
345 ArmVIXLAssembler* assembler = codegen->GetAssembler();
346 LocationSummary* locations = invoke->GetLocations();
Anton Kirilov5ec62182016-10-13 20:16:02 +0100347 Location in = locations->InAt(0);
348 vixl32::Register out = RegisterFrom(locations->Out());
349
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100350 DCHECK((type == DataType::Type::kInt32) || (type == DataType::Type::kInt64));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100351
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100352 if (type == DataType::Type::kInt64) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100353 vixl32::Register in_reg_lo = LowRegisterFrom(in);
354 vixl32::Register in_reg_hi = HighRegisterFrom(in);
355 vixl32::Label end;
Anton Kirilov6f644202017-02-27 18:29:45 +0000356 vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &end);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100357 __ Clz(out, in_reg_hi);
Anton Kirilov6f644202017-02-27 18:29:45 +0000358 __ CompareAndBranchIfNonZero(in_reg_hi, final_label, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100359 __ Clz(out, in_reg_lo);
360 __ Add(out, out, 32);
Anton Kirilov6f644202017-02-27 18:29:45 +0000361 if (end.IsReferenced()) {
362 __ Bind(&end);
363 }
Anton Kirilov5ec62182016-10-13 20:16:02 +0100364 } else {
365 __ Clz(out, RegisterFrom(in));
366 }
367}
368
369void IntrinsicLocationsBuilderARMVIXL::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100370 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100371}
372
373void IntrinsicCodeGeneratorARMVIXL::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100374 GenNumberOfLeadingZeros(invoke, DataType::Type::kInt32, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100375}
376
377void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100378 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100379}
380
381void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100382 GenNumberOfLeadingZeros(invoke, DataType::Type::kInt64, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100383}
384
Anton Kirilov6f644202017-02-27 18:29:45 +0000385static void GenNumberOfTrailingZeros(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100386 DataType::Type type,
Anton Kirilov6f644202017-02-27 18:29:45 +0000387 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100388 DCHECK((type == DataType::Type::kInt32) || (type == DataType::Type::kInt64));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100389
Anton Kirilov6f644202017-02-27 18:29:45 +0000390 ArmVIXLAssembler* assembler = codegen->GetAssembler();
391 LocationSummary* locations = invoke->GetLocations();
Anton Kirilov5ec62182016-10-13 20:16:02 +0100392 vixl32::Register out = RegisterFrom(locations->Out());
393
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100394 if (type == DataType::Type::kInt64) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100395 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
396 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
397 vixl32::Label end;
Anton Kirilov6f644202017-02-27 18:29:45 +0000398 vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &end);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100399 __ Rbit(out, in_reg_lo);
400 __ Clz(out, out);
Anton Kirilov6f644202017-02-27 18:29:45 +0000401 __ CompareAndBranchIfNonZero(in_reg_lo, final_label, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100402 __ Rbit(out, in_reg_hi);
403 __ Clz(out, out);
404 __ Add(out, out, 32);
Anton Kirilov6f644202017-02-27 18:29:45 +0000405 if (end.IsReferenced()) {
406 __ Bind(&end);
407 }
Anton Kirilov5ec62182016-10-13 20:16:02 +0100408 } else {
409 vixl32::Register in = RegisterFrom(locations->InAt(0));
410 __ Rbit(out, in);
411 __ Clz(out, out);
412 }
413}
414
415void IntrinsicLocationsBuilderARMVIXL::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100416 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100417}
418
419void IntrinsicCodeGeneratorARMVIXL::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100420 GenNumberOfTrailingZeros(invoke, DataType::Type::kInt32, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100421}
422
423void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100424 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100425}
426
427void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100428 GenNumberOfTrailingZeros(invoke, DataType::Type::kInt64, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100429}
430
Anton Kirilov5ec62182016-10-13 20:16:02 +0100431void IntrinsicLocationsBuilderARMVIXL::VisitMathSqrt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100432 CreateFPToFPLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100433}
434
435void IntrinsicCodeGeneratorARMVIXL::VisitMathSqrt(HInvoke* invoke) {
436 ArmVIXLAssembler* assembler = GetAssembler();
437 __ Vsqrt(OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
438}
439
xueliang.zhong6099d5e2016-04-20 18:44:56 +0100440void IntrinsicLocationsBuilderARMVIXL::VisitMathRint(HInvoke* invoke) {
441 if (features_.HasARMv8AInstructions()) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100442 CreateFPToFPLocations(allocator_, invoke);
xueliang.zhong6099d5e2016-04-20 18:44:56 +0100443 }
444}
445
446void IntrinsicCodeGeneratorARMVIXL::VisitMathRint(HInvoke* invoke) {
447 DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
448 ArmVIXLAssembler* assembler = GetAssembler();
449 __ Vrintn(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
450}
451
xueliang.zhong53463ba2017-02-16 15:18:03 +0000452void IntrinsicLocationsBuilderARMVIXL::VisitMathRoundFloat(HInvoke* invoke) {
453 if (features_.HasARMv8AInstructions()) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100454 LocationSummary* locations =
455 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
xueliang.zhong53463ba2017-02-16 15:18:03 +0000456 locations->SetInAt(0, Location::RequiresFpuRegister());
457 locations->SetOut(Location::RequiresRegister());
458 locations->AddTemp(Location::RequiresFpuRegister());
459 }
460}
461
462void IntrinsicCodeGeneratorARMVIXL::VisitMathRoundFloat(HInvoke* invoke) {
463 DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
464
465 ArmVIXLAssembler* assembler = GetAssembler();
466 vixl32::SRegister in_reg = InputSRegisterAt(invoke, 0);
467 vixl32::Register out_reg = OutputRegister(invoke);
468 vixl32::SRegister temp1 = LowSRegisterFrom(invoke->GetLocations()->GetTemp(0));
469 vixl32::SRegister temp2 = HighSRegisterFrom(invoke->GetLocations()->GetTemp(0));
470 vixl32::Label done;
471 vixl32::Label* final_label = codegen_->GetFinalLabel(invoke, &done);
472
473 // Round to nearest integer, ties away from zero.
474 __ Vcvta(S32, F32, temp1, in_reg);
475 __ Vmov(out_reg, temp1);
476
477 // For positive, zero or NaN inputs, rounding is done.
478 __ Cmp(out_reg, 0);
479 __ B(ge, final_label, /* far_target */ false);
480
481 // Handle input < 0 cases.
482 // If input is negative but not a tie, previous result (round to nearest) is valid.
483 // If input is a negative tie, change rounding direction to positive infinity, out_reg += 1.
484 __ Vrinta(F32, F32, temp1, in_reg);
485 __ Vmov(temp2, 0.5);
486 __ Vsub(F32, temp1, in_reg, temp1);
487 __ Vcmp(F32, temp1, temp2);
488 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
489 {
490 // Use ExactAsemblyScope here because we are using IT.
491 ExactAssemblyScope it_scope(assembler->GetVIXLAssembler(),
492 2 * kMaxInstructionSizeInBytes,
493 CodeBufferCheckScope::kMaximumSize);
494 __ it(eq);
495 __ add(eq, out_reg, out_reg, 1);
496 }
497
498 if (done.IsReferenced()) {
499 __ Bind(&done);
500 }
501}
502
Anton Kirilov5ec62182016-10-13 20:16:02 +0100503void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100504 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100505}
506
507void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) {
508 ArmVIXLAssembler* assembler = GetAssembler();
509 // Ignore upper 4B of long address.
Scott Wakelingb77051e2016-11-21 19:46:00 +0000510 __ Ldrsb(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100511}
512
513void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100514 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100515}
516
517void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) {
518 ArmVIXLAssembler* assembler = GetAssembler();
519 // Ignore upper 4B of long address.
Scott Wakelingb77051e2016-11-21 19:46:00 +0000520 __ Ldr(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100521}
522
523void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekLongNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100524 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100525}
526
527void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekLongNative(HInvoke* invoke) {
528 ArmVIXLAssembler* assembler = GetAssembler();
529 // Ignore upper 4B of long address.
530 vixl32::Register addr = LowRegisterFrom(invoke->GetLocations()->InAt(0));
531 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
532 // exception. So we can't use ldrd as addr may be unaligned.
533 vixl32::Register lo = LowRegisterFrom(invoke->GetLocations()->Out());
534 vixl32::Register hi = HighRegisterFrom(invoke->GetLocations()->Out());
535 if (addr.Is(lo)) {
536 __ Ldr(hi, MemOperand(addr, 4));
Scott Wakelingb77051e2016-11-21 19:46:00 +0000537 __ Ldr(lo, MemOperand(addr));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100538 } else {
Scott Wakelingb77051e2016-11-21 19:46:00 +0000539 __ Ldr(lo, MemOperand(addr));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100540 __ Ldr(hi, MemOperand(addr, 4));
541 }
542}
543
544void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekShortNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100545 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100546}
547
548void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekShortNative(HInvoke* invoke) {
549 ArmVIXLAssembler* assembler = GetAssembler();
550 // Ignore upper 4B of long address.
Scott Wakelingb77051e2016-11-21 19:46:00 +0000551 __ Ldrsh(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100552}
553
Vladimir Markoca6fff82017-10-03 14:49:14 +0100554static void CreateIntIntToVoidLocations(ArenaAllocator* allocator, HInvoke* invoke) {
555 LocationSummary* locations =
556 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100557 locations->SetInAt(0, Location::RequiresRegister());
558 locations->SetInAt(1, Location::RequiresRegister());
559}
560
561void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeByte(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100562 CreateIntIntToVoidLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100563}
564
565void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeByte(HInvoke* invoke) {
566 ArmVIXLAssembler* assembler = GetAssembler();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000567 __ Strb(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100568}
569
570void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100571 CreateIntIntToVoidLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100572}
573
574void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) {
575 ArmVIXLAssembler* assembler = GetAssembler();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000576 __ Str(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100577}
578
579void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeLongNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100580 CreateIntIntToVoidLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100581}
582
583void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeLongNative(HInvoke* invoke) {
584 ArmVIXLAssembler* assembler = GetAssembler();
585 // Ignore upper 4B of long address.
586 vixl32::Register addr = LowRegisterFrom(invoke->GetLocations()->InAt(0));
587 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
588 // exception. So we can't use ldrd as addr may be unaligned.
Scott Wakelingb77051e2016-11-21 19:46:00 +0000589 __ Str(LowRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100590 __ Str(HighRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr, 4));
591}
592
593void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeShortNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100594 CreateIntIntToVoidLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100595}
596
597void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeShortNative(HInvoke* invoke) {
598 ArmVIXLAssembler* assembler = GetAssembler();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000599 __ Strh(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100600}
601
602void IntrinsicLocationsBuilderARMVIXL::VisitThreadCurrentThread(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100603 LocationSummary* locations =
604 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100605 locations->SetOut(Location::RequiresRegister());
606}
607
608void IntrinsicCodeGeneratorARMVIXL::VisitThreadCurrentThread(HInvoke* invoke) {
609 ArmVIXLAssembler* assembler = GetAssembler();
610 __ Ldr(OutputRegister(invoke),
611 MemOperand(tr, Thread::PeerOffset<kArmPointerSize>().Int32Value()));
612}
613
614static void GenUnsafeGet(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100615 DataType::Type type,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100616 bool is_volatile,
617 CodeGeneratorARMVIXL* codegen) {
618 LocationSummary* locations = invoke->GetLocations();
619 ArmVIXLAssembler* assembler = codegen->GetAssembler();
620 Location base_loc = locations->InAt(1);
621 vixl32::Register base = InputRegisterAt(invoke, 1); // Object pointer.
622 Location offset_loc = locations->InAt(2);
623 vixl32::Register offset = LowRegisterFrom(offset_loc); // Long offset, lo part only.
624 Location trg_loc = locations->Out();
625
626 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100627 case DataType::Type::kInt32: {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100628 vixl32::Register trg = RegisterFrom(trg_loc);
629 __ Ldr(trg, MemOperand(base, offset));
630 if (is_volatile) {
631 __ Dmb(vixl32::ISH);
632 }
633 break;
634 }
635
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100636 case DataType::Type::kReference: {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100637 vixl32::Register trg = RegisterFrom(trg_loc);
638 if (kEmitCompilerReadBarrier) {
639 if (kUseBakerReadBarrier) {
640 Location temp = locations->GetTemp(0);
641 codegen->GenerateReferenceLoadWithBakerReadBarrier(
642 invoke, trg_loc, base, 0U, offset_loc, TIMES_1, temp, /* needs_null_check */ false);
643 if (is_volatile) {
644 __ Dmb(vixl32::ISH);
645 }
646 } else {
647 __ Ldr(trg, MemOperand(base, offset));
648 if (is_volatile) {
649 __ Dmb(vixl32::ISH);
650 }
651 codegen->GenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
652 }
653 } else {
654 __ Ldr(trg, MemOperand(base, offset));
655 if (is_volatile) {
656 __ Dmb(vixl32::ISH);
657 }
658 assembler->MaybeUnpoisonHeapReference(trg);
659 }
660 break;
661 }
662
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100663 case DataType::Type::kInt64: {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100664 vixl32::Register trg_lo = LowRegisterFrom(trg_loc);
665 vixl32::Register trg_hi = HighRegisterFrom(trg_loc);
666 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
Artem Serov657022c2016-11-23 14:19:38 +0000667 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
668 const vixl32::Register temp_reg = temps.Acquire();
669 __ Add(temp_reg, base, offset);
670 __ Ldrexd(trg_lo, trg_hi, MemOperand(temp_reg));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100671 } else {
672 __ Ldrd(trg_lo, trg_hi, MemOperand(base, offset));
673 }
674 if (is_volatile) {
675 __ Dmb(vixl32::ISH);
676 }
677 break;
678 }
679
680 default:
681 LOG(FATAL) << "Unexpected type " << type;
682 UNREACHABLE();
683 }
684}
685
Vladimir Markoca6fff82017-10-03 14:49:14 +0100686static void CreateIntIntIntToIntLocations(ArenaAllocator* allocator,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100687 HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100688 DataType::Type type) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100689 bool can_call = kEmitCompilerReadBarrier &&
690 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
691 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Vladimir Markoca6fff82017-10-03 14:49:14 +0100692 LocationSummary* locations =
693 new (allocator) LocationSummary(invoke,
694 can_call
695 ? LocationSummary::kCallOnSlowPath
696 : LocationSummary::kNoCall,
697 kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100698 if (can_call && kUseBakerReadBarrier) {
699 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
700 }
701 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
702 locations->SetInAt(1, Location::RequiresRegister());
703 locations->SetInAt(2, Location::RequiresRegister());
704 locations->SetOut(Location::RequiresRegister(),
705 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100706 if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100707 // We need a temporary register for the read barrier marking slow
Roland Levillain9983e302017-07-14 14:34:22 +0100708 // path in CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier.
Anton Kirilov5ec62182016-10-13 20:16:02 +0100709 locations->AddTemp(Location::RequiresRegister());
710 }
711}
712
713void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGet(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100714 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt32);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100715}
716void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100717 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt32);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100718}
719void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetLong(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100720 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt64);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100721}
722void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100723 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt64);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100724}
725void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetObject(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100726 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kReference);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100727}
728void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100729 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kReference);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100730}
731
732void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGet(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100733 GenUnsafeGet(invoke, DataType::Type::kInt32, /* is_volatile */ false, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100734}
735void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100736 GenUnsafeGet(invoke, DataType::Type::kInt32, /* is_volatile */ true, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100737}
738void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetLong(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100739 GenUnsafeGet(invoke, DataType::Type::kInt64, /* is_volatile */ false, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100740}
741void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100742 GenUnsafeGet(invoke, DataType::Type::kInt64, /* is_volatile */ true, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100743}
744void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetObject(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100745 GenUnsafeGet(invoke, DataType::Type::kReference, /* is_volatile */ false, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100746}
747void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100748 GenUnsafeGet(invoke, DataType::Type::kReference, /* is_volatile */ true, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100749}
750
Vladimir Markoca6fff82017-10-03 14:49:14 +0100751static void CreateIntIntIntIntToVoid(ArenaAllocator* allocator,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100752 const ArmInstructionSetFeatures& features,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100753 DataType::Type type,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100754 bool is_volatile,
755 HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100756 LocationSummary* locations =
757 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100758 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
759 locations->SetInAt(1, Location::RequiresRegister());
760 locations->SetInAt(2, Location::RequiresRegister());
761 locations->SetInAt(3, Location::RequiresRegister());
762
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100763 if (type == DataType::Type::kInt64) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100764 // Potentially need temps for ldrexd-strexd loop.
765 if (is_volatile && !features.HasAtomicLdrdAndStrd()) {
766 locations->AddTemp(Location::RequiresRegister()); // Temp_lo.
767 locations->AddTemp(Location::RequiresRegister()); // Temp_hi.
768 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100769 } else if (type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100770 // Temps for card-marking.
771 locations->AddTemp(Location::RequiresRegister()); // Temp.
772 locations->AddTemp(Location::RequiresRegister()); // Card.
773 }
774}
775
776void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePut(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100777 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100778 allocator_, features_, DataType::Type::kInt32, /* is_volatile */ false, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100779}
780void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutOrdered(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::VisitUnsafePutVolatile(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 */ true, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100787}
788void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutObject(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100789 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100790 allocator_, features_, DataType::Type::kReference, /* is_volatile */ false, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100791}
792void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutObjectOrdered(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::VisitUnsafePutObjectVolatile(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 */ true, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100799}
800void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutLong(HInvoke* invoke) {
801 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100802 allocator_, features_, DataType::Type::kInt64, /* is_volatile */ false, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100803}
804void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutLongOrdered(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::VisitUnsafePutLongVolatile(HInvoke* invoke) {
809 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100810 allocator_, features_, DataType::Type::kInt64, /* is_volatile */ true, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100811}
812
813static void GenUnsafePut(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100814 DataType::Type type,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100815 bool is_volatile,
816 bool is_ordered,
817 CodeGeneratorARMVIXL* codegen) {
818 ArmVIXLAssembler* assembler = codegen->GetAssembler();
819
820 vixl32::Register base = RegisterFrom(locations->InAt(1)); // Object pointer.
821 vixl32::Register offset = LowRegisterFrom(locations->InAt(2)); // Long offset, lo part only.
822 vixl32::Register value;
823
824 if (is_volatile || is_ordered) {
825 __ Dmb(vixl32::ISH);
826 }
827
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100828 if (type == DataType::Type::kInt64) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100829 vixl32::Register value_lo = LowRegisterFrom(locations->InAt(3));
830 vixl32::Register value_hi = HighRegisterFrom(locations->InAt(3));
831 value = value_lo;
832 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
833 vixl32::Register temp_lo = RegisterFrom(locations->GetTemp(0));
834 vixl32::Register temp_hi = RegisterFrom(locations->GetTemp(1));
835 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
836 const vixl32::Register temp_reg = temps.Acquire();
837
838 __ Add(temp_reg, base, offset);
839 vixl32::Label loop_head;
840 __ Bind(&loop_head);
Scott Wakelingb77051e2016-11-21 19:46:00 +0000841 __ Ldrexd(temp_lo, temp_hi, MemOperand(temp_reg));
842 __ Strexd(temp_lo, value_lo, value_hi, MemOperand(temp_reg));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100843 __ Cmp(temp_lo, 0);
Artem Serov517d9f62016-12-12 15:51:15 +0000844 __ B(ne, &loop_head, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100845 } else {
846 __ Strd(value_lo, value_hi, MemOperand(base, offset));
847 }
848 } else {
849 value = RegisterFrom(locations->InAt(3));
850 vixl32::Register source = value;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100851 if (kPoisonHeapReferences && type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100852 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
853 __ Mov(temp, value);
854 assembler->PoisonHeapReference(temp);
855 source = temp;
856 }
857 __ Str(source, MemOperand(base, offset));
858 }
859
860 if (is_volatile) {
861 __ Dmb(vixl32::ISH);
862 }
863
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100864 if (type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100865 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
866 vixl32::Register card = RegisterFrom(locations->GetTemp(1));
867 bool value_can_be_null = true; // TODO: Worth finding out this information?
868 codegen->MarkGCCard(temp, card, base, value, value_can_be_null);
869 }
870}
871
872void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePut(HInvoke* invoke) {
873 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100874 DataType::Type::kInt32,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100875 /* is_volatile */ false,
876 /* is_ordered */ false,
877 codegen_);
878}
879void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutOrdered(HInvoke* invoke) {
880 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100881 DataType::Type::kInt32,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100882 /* is_volatile */ false,
883 /* is_ordered */ true,
884 codegen_);
885}
886void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutVolatile(HInvoke* invoke) {
887 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100888 DataType::Type::kInt32,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100889 /* is_volatile */ true,
890 /* is_ordered */ false,
891 codegen_);
892}
893void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutObject(HInvoke* invoke) {
894 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100895 DataType::Type::kReference,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100896 /* is_volatile */ false,
897 /* is_ordered */ false,
898 codegen_);
899}
900void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
901 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100902 DataType::Type::kReference,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100903 /* is_volatile */ false,
904 /* is_ordered */ true,
905 codegen_);
906}
907void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
908 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100909 DataType::Type::kReference,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100910 /* is_volatile */ true,
911 /* is_ordered */ false,
912 codegen_);
913}
914void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutLong(HInvoke* invoke) {
915 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100916 DataType::Type::kInt64,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100917 /* is_volatile */ false,
918 /* is_ordered */ false,
919 codegen_);
920}
921void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutLongOrdered(HInvoke* invoke) {
922 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100923 DataType::Type::kInt64,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100924 /* is_volatile */ false,
925 /* is_ordered */ true,
926 codegen_);
927}
928void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutLongVolatile(HInvoke* invoke) {
929 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100930 DataType::Type::kInt64,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100931 /* is_volatile */ true,
932 /* is_ordered */ false,
933 codegen_);
934}
935
Vladimir Markoca6fff82017-10-03 14:49:14 +0100936static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* allocator,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100937 HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100938 DataType::Type type) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100939 bool can_call = kEmitCompilerReadBarrier &&
940 kUseBakerReadBarrier &&
941 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
Vladimir Markoca6fff82017-10-03 14:49:14 +0100942 LocationSummary* locations =
943 new (allocator) LocationSummary(invoke,
944 can_call
945 ? LocationSummary::kCallOnSlowPath
946 : LocationSummary::kNoCall,
947 kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100948 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
949 locations->SetInAt(1, Location::RequiresRegister());
950 locations->SetInAt(2, Location::RequiresRegister());
951 locations->SetInAt(3, Location::RequiresRegister());
952 locations->SetInAt(4, Location::RequiresRegister());
953
954 // If heap poisoning is enabled, we don't want the unpoisoning
955 // operations to potentially clobber the output. Likewise when
956 // emitting a (Baker) read barrier, which may call.
957 Location::OutputOverlap overlaps =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100958 ((kPoisonHeapReferences && type == DataType::Type::kReference) || can_call)
Anton Kirilov5ec62182016-10-13 20:16:02 +0100959 ? Location::kOutputOverlap
960 : Location::kNoOutputOverlap;
961 locations->SetOut(Location::RequiresRegister(), overlaps);
962
963 // Temporary registers used in CAS. In the object case
964 // (UnsafeCASObject intrinsic), these are also used for
965 // card-marking, and possibly for (Baker) read barrier.
966 locations->AddTemp(Location::RequiresRegister()); // Pointer.
967 locations->AddTemp(Location::RequiresRegister()); // Temp 1.
968}
969
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100970static void GenCas(HInvoke* invoke, DataType::Type type, CodeGeneratorARMVIXL* codegen) {
971 DCHECK_NE(type, DataType::Type::kInt64);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100972
973 ArmVIXLAssembler* assembler = codegen->GetAssembler();
974 LocationSummary* locations = invoke->GetLocations();
975
976 Location out_loc = locations->Out();
977 vixl32::Register out = OutputRegister(invoke); // Boolean result.
978
979 vixl32::Register base = InputRegisterAt(invoke, 1); // Object pointer.
980 Location offset_loc = locations->InAt(2);
981 vixl32::Register offset = LowRegisterFrom(offset_loc); // Offset (discard high 4B).
982 vixl32::Register expected = InputRegisterAt(invoke, 3); // Expected.
983 vixl32::Register value = InputRegisterAt(invoke, 4); // Value.
984
985 Location tmp_ptr_loc = locations->GetTemp(0);
986 vixl32::Register tmp_ptr = RegisterFrom(tmp_ptr_loc); // Pointer to actual memory.
987 vixl32::Register tmp = RegisterFrom(locations->GetTemp(1)); // Value in memory.
988
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100989 if (type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100990 // The only read barrier implementation supporting the
991 // UnsafeCASObject intrinsic is the Baker-style read barriers.
992 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
993
994 // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
995 // object and scan the receiver at the next GC for nothing.
996 bool value_can_be_null = true; // TODO: Worth finding out this information?
997 codegen->MarkGCCard(tmp_ptr, tmp, base, value, value_can_be_null);
998
999 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1000 // Need to make sure the reference stored in the field is a to-space
1001 // one before attempting the CAS or the CAS could fail incorrectly.
Roland Levillainff487002017-03-07 16:50:01 +00001002 codegen->UpdateReferenceFieldWithBakerReadBarrier(
Anton Kirilov5ec62182016-10-13 20:16:02 +01001003 invoke,
1004 out_loc, // Unused, used only as a "temporary" within the read barrier.
1005 base,
Roland Levillainff487002017-03-07 16:50:01 +00001006 /* field_offset */ offset_loc,
Anton Kirilov5ec62182016-10-13 20:16:02 +01001007 tmp_ptr_loc,
1008 /* needs_null_check */ false,
Roland Levillainff487002017-03-07 16:50:01 +00001009 tmp);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001010 }
1011 }
1012
1013 // Prevent reordering with prior memory operations.
1014 // Emit a DMB ISH instruction instead of an DMB ISHST one, as the
1015 // latter allows a preceding load to be delayed past the STXR
1016 // instruction below.
1017 __ Dmb(vixl32::ISH);
1018
1019 __ Add(tmp_ptr, base, offset);
1020
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001021 if (kPoisonHeapReferences && type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01001022 codegen->GetAssembler()->PoisonHeapReference(expected);
1023 if (value.Is(expected)) {
1024 // Do not poison `value`, as it is the same register as
1025 // `expected`, which has just been poisoned.
1026 } else {
1027 codegen->GetAssembler()->PoisonHeapReference(value);
1028 }
1029 }
1030
1031 // do {
1032 // tmp = [r_ptr] - expected;
1033 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
1034 // result = tmp != 0;
1035
1036 vixl32::Label loop_head;
1037 __ Bind(&loop_head);
1038
Scott Wakelingb77051e2016-11-21 19:46:00 +00001039 __ Ldrex(tmp, MemOperand(tmp_ptr));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001040
1041 __ Subs(tmp, tmp, expected);
1042
1043 {
Artem Serov0fb37192016-12-06 18:13:40 +00001044 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1045 3 * kMaxInstructionSizeInBytes,
1046 CodeBufferCheckScope::kMaximumSize);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001047
1048 __ itt(eq);
Scott Wakelingb77051e2016-11-21 19:46:00 +00001049 __ strex(eq, tmp, value, MemOperand(tmp_ptr));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001050 __ cmp(eq, tmp, 1);
1051 }
1052
Artem Serov517d9f62016-12-12 15:51:15 +00001053 __ B(eq, &loop_head, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001054
1055 __ Dmb(vixl32::ISH);
1056
1057 __ Rsbs(out, tmp, 1);
1058
1059 {
Artem Serov0fb37192016-12-06 18:13:40 +00001060 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1061 2 * kMaxInstructionSizeInBytes,
1062 CodeBufferCheckScope::kMaximumSize);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001063
1064 __ it(cc);
1065 __ mov(cc, out, 0);
1066 }
1067
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001068 if (kPoisonHeapReferences && type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01001069 codegen->GetAssembler()->UnpoisonHeapReference(expected);
1070 if (value.Is(expected)) {
1071 // Do not unpoison `value`, as it is the same register as
1072 // `expected`, which has just been unpoisoned.
1073 } else {
1074 codegen->GetAssembler()->UnpoisonHeapReference(value);
1075 }
1076 }
1077}
1078
1079void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeCASInt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001080 CreateIntIntIntIntIntToIntPlusTemps(allocator_, invoke, DataType::Type::kInt32);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001081}
1082void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeCASObject(HInvoke* invoke) {
1083 // The only read barrier implementation supporting the
1084 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1085 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
1086 return;
1087 }
1088
Vladimir Markoca6fff82017-10-03 14:49:14 +01001089 CreateIntIntIntIntIntToIntPlusTemps(allocator_, invoke, DataType::Type::kReference);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001090}
1091void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeCASInt(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001092 GenCas(invoke, DataType::Type::kInt32, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001093}
1094void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeCASObject(HInvoke* invoke) {
1095 // The only read barrier implementation supporting the
1096 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1097 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1098
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001099 GenCas(invoke, DataType::Type::kReference, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001100}
1101
1102void IntrinsicLocationsBuilderARMVIXL::VisitStringCompareTo(HInvoke* invoke) {
1103 // The inputs plus one temp.
Vladimir Markoca6fff82017-10-03 14:49:14 +01001104 LocationSummary* locations =
1105 new (allocator_) LocationSummary(invoke,
1106 invoke->InputAt(1)->CanBeNull()
1107 ? LocationSummary::kCallOnSlowPath
1108 : LocationSummary::kNoCall,
1109 kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001110 locations->SetInAt(0, Location::RequiresRegister());
1111 locations->SetInAt(1, Location::RequiresRegister());
1112 locations->AddTemp(Location::RequiresRegister());
1113 locations->AddTemp(Location::RequiresRegister());
1114 locations->AddTemp(Location::RequiresRegister());
1115 // Need temporary registers for String compression's feature.
1116 if (mirror::kUseStringCompression) {
1117 locations->AddTemp(Location::RequiresRegister());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001118 }
1119 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1120}
1121
Artem Serov672b9c12017-12-05 18:04:07 +00001122// Forward declaration.
1123//
1124// ART build system imposes a size limit (deviceFrameSizeLimit) on the stack frames generated
1125// by the compiler for every C++ function, and if this function gets inlined in
1126// IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo, the limit will be exceeded, resulting in a
1127// build failure. That is the reason why NO_INLINE attribute is used.
1128static void NO_INLINE GenerateStringCompareToLoop(ArmVIXLAssembler* assembler,
1129 HInvoke* invoke,
1130 vixl32::Label* end,
1131 vixl32::Label* different_compression);
1132
Anton Kirilov5ec62182016-10-13 20:16:02 +01001133void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) {
1134 ArmVIXLAssembler* assembler = GetAssembler();
1135 LocationSummary* locations = invoke->GetLocations();
1136
Artem Serov672b9c12017-12-05 18:04:07 +00001137 const vixl32::Register str = InputRegisterAt(invoke, 0);
1138 const vixl32::Register arg = InputRegisterAt(invoke, 1);
1139 const vixl32::Register out = OutputRegister(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001140
Artem Serov672b9c12017-12-05 18:04:07 +00001141 const vixl32::Register temp0 = RegisterFrom(locations->GetTemp(0));
1142 const vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1143 const vixl32::Register temp2 = RegisterFrom(locations->GetTemp(2));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001144 vixl32::Register temp3;
Anton Kirilov5ec62182016-10-13 20:16:02 +01001145 if (mirror::kUseStringCompression) {
1146 temp3 = RegisterFrom(locations->GetTemp(3));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001147 }
1148
Anton Kirilov5ec62182016-10-13 20:16:02 +01001149 vixl32::Label end;
1150 vixl32::Label different_compression;
1151
1152 // Get offsets of count and value fields within a string object.
1153 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
Anton Kirilov5ec62182016-10-13 20:16:02 +01001154
1155 // Note that the null check must have been done earlier.
1156 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1157
1158 // Take slow path and throw if input can be and is null.
1159 SlowPathCodeARMVIXL* slow_path = nullptr;
1160 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1161 if (can_slow_path) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001162 slow_path = new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001163 codegen_->AddSlowPath(slow_path);
xueliang.zhongf51bc622016-11-04 09:23:32 +00001164 __ CompareAndBranchIfZero(arg, slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001165 }
1166
1167 // Reference equality check, return 0 if same reference.
1168 __ Subs(out, str, arg);
1169 __ B(eq, &end);
1170
Anton Kirilov5ec62182016-10-13 20:16:02 +01001171 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001172 // Load `count` fields of this and argument strings.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001173 __ Ldr(temp3, MemOperand(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001174 __ Ldr(temp2, MemOperand(arg, count_offset));
1175 // Extract lengths from the `count` fields.
1176 __ Lsr(temp0, temp3, 1u);
1177 __ Lsr(temp1, temp2, 1u);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001178 } else {
1179 // Load lengths of this and argument strings.
1180 __ Ldr(temp0, MemOperand(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001181 __ Ldr(temp1, MemOperand(arg, count_offset));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001182 }
1183 // out = length diff.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001184 __ Subs(out, temp0, temp1);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001185 // temp0 = min(len(str), len(arg)).
1186
1187 {
Artem Serov0fb37192016-12-06 18:13:40 +00001188 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1189 2 * kMaxInstructionSizeInBytes,
1190 CodeBufferCheckScope::kMaximumSize);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001191
1192 __ it(gt);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001193 __ mov(gt, temp0, temp1);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001194 }
1195
Anton Kirilov5ec62182016-10-13 20:16:02 +01001196 // Shorter string is empty?
xueliang.zhongf51bc622016-11-04 09:23:32 +00001197 // Note that mirror::kUseStringCompression==true introduces lots of instructions,
1198 // which makes &end label far away from this branch and makes it not 'CBZ-encodable'.
1199 __ CompareAndBranchIfZero(temp0, &end, mirror::kUseStringCompression);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001200
1201 if (mirror::kUseStringCompression) {
1202 // Check if both strings using same compression style to use this comparison loop.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001203 __ Eors(temp2, temp2, temp3);
1204 __ Lsrs(temp2, temp2, 1u);
1205 __ B(cs, &different_compression);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001206 // For string compression, calculate the number of bytes to compare (not chars).
1207 // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001208 __ Lsls(temp3, temp3, 31u); // Extract purely the compression flag.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001209
Artem Serov0fb37192016-12-06 18:13:40 +00001210 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1211 2 * kMaxInstructionSizeInBytes,
1212 CodeBufferCheckScope::kMaximumSize);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001213
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001214 __ it(ne);
1215 __ add(ne, temp0, temp0, temp0);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001216 }
1217
Artem Serov672b9c12017-12-05 18:04:07 +00001218
1219 GenerateStringCompareToLoop(assembler, invoke, &end, &different_compression);
1220
1221 __ Bind(&end);
1222
1223 if (can_slow_path) {
1224 __ Bind(slow_path->GetExitLabel());
1225 }
1226}
1227
1228static void GenerateStringCompareToLoop(ArmVIXLAssembler* assembler,
1229 HInvoke* invoke,
1230 vixl32::Label* end,
1231 vixl32::Label* different_compression) {
1232 LocationSummary* locations = invoke->GetLocations();
1233
1234 const vixl32::Register str = InputRegisterAt(invoke, 0);
1235 const vixl32::Register arg = InputRegisterAt(invoke, 1);
1236 const vixl32::Register out = OutputRegister(invoke);
1237
1238 const vixl32::Register temp0 = RegisterFrom(locations->GetTemp(0));
1239 const vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1240 const vixl32::Register temp2 = RegisterFrom(locations->GetTemp(2));
1241 vixl32::Register temp3;
1242 if (mirror::kUseStringCompression) {
1243 temp3 = RegisterFrom(locations->GetTemp(3));
1244 }
1245
1246 vixl32::Label loop;
1247 vixl32::Label find_char_diff;
1248
1249 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001250 // Store offset of string value in preparation for comparison loop.
1251 __ Mov(temp1, value_offset);
1252
Anton Kirilov5ec62182016-10-13 20:16:02 +01001253 // Assertions that must hold in order to compare multiple characters at a time.
1254 CHECK_ALIGNED(value_offset, 8);
1255 static_assert(IsAligned<8>(kObjectAlignment),
1256 "String data must be 8-byte aligned for unrolled CompareTo loop.");
1257
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001258 const unsigned char_size = DataType::Size(DataType::Type::kUint16);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001259 DCHECK_EQ(char_size, 2u);
1260
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001261 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
1262
Anton Kirilov5ec62182016-10-13 20:16:02 +01001263 vixl32::Label find_char_diff_2nd_cmp;
1264 // Unrolled loop comparing 4x16-bit chars per iteration (ok because of string data alignment).
1265 __ Bind(&loop);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001266 vixl32::Register temp_reg = temps.Acquire();
Anton Kirilov5ec62182016-10-13 20:16:02 +01001267 __ Ldr(temp_reg, MemOperand(str, temp1));
1268 __ Ldr(temp2, MemOperand(arg, temp1));
1269 __ Cmp(temp_reg, temp2);
Artem Serov517d9f62016-12-12 15:51:15 +00001270 __ B(ne, &find_char_diff, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001271 __ Add(temp1, temp1, char_size * 2);
1272
1273 __ Ldr(temp_reg, MemOperand(str, temp1));
1274 __ Ldr(temp2, MemOperand(arg, temp1));
1275 __ Cmp(temp_reg, temp2);
Artem Serov517d9f62016-12-12 15:51:15 +00001276 __ B(ne, &find_char_diff_2nd_cmp, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001277 __ Add(temp1, temp1, char_size * 2);
1278 // With string compression, we have compared 8 bytes, otherwise 4 chars.
1279 __ Subs(temp0, temp0, (mirror::kUseStringCompression ? 8 : 4));
Artem Serov517d9f62016-12-12 15:51:15 +00001280 __ B(hi, &loop, /* far_target */ false);
Artem Serov672b9c12017-12-05 18:04:07 +00001281 __ B(end);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001282
1283 __ Bind(&find_char_diff_2nd_cmp);
1284 if (mirror::kUseStringCompression) {
1285 __ Subs(temp0, temp0, 4); // 4 bytes previously compared.
Artem Serov672b9c12017-12-05 18:04:07 +00001286 __ B(ls, end, /* far_target */ false); // Was the second comparison fully beyond the end?
Anton Kirilov5ec62182016-10-13 20:16:02 +01001287 } else {
1288 // Without string compression, we can start treating temp0 as signed
1289 // and rely on the signed comparison below.
1290 __ Sub(temp0, temp0, 2);
1291 }
1292
1293 // Find the single character difference.
1294 __ Bind(&find_char_diff);
1295 // Get the bit position of the first character that differs.
1296 __ Eor(temp1, temp2, temp_reg);
1297 __ Rbit(temp1, temp1);
1298 __ Clz(temp1, temp1);
1299
1300 // temp0 = number of characters remaining to compare.
1301 // (Without string compression, it could be < 1 if a difference is found by the second CMP
1302 // in the comparison loop, and after the end of the shorter string data).
1303
1304 // Without string compression (temp1 >> 4) = character where difference occurs between the last
1305 // two words compared, in the interval [0,1].
1306 // (0 for low half-word different, 1 for high half-word different).
1307 // With string compression, (temp1 << 3) = byte where the difference occurs,
1308 // in the interval [0,3].
1309
1310 // If temp0 <= (temp1 >> (kUseStringCompression ? 3 : 4)), the difference occurs outside
1311 // the remaining string data, so just return length diff (out).
1312 // The comparison is unsigned for string compression, otherwise signed.
1313 __ Cmp(temp0, Operand(temp1, vixl32::LSR, (mirror::kUseStringCompression ? 3 : 4)));
Artem Serov672b9c12017-12-05 18:04:07 +00001314 __ B((mirror::kUseStringCompression ? ls : le), end, /* far_target */ false);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001315
Anton Kirilov5ec62182016-10-13 20:16:02 +01001316 // Extract the characters and calculate the difference.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001317 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001318 // For compressed strings we need to clear 0x7 from temp1, for uncompressed we need to clear
1319 // 0xf. We also need to prepare the character extraction mask `uncompressed ? 0xffffu : 0xffu`.
1320 // The compression flag is now in the highest bit of temp3, so let's play some tricks.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001321 __ Orr(temp3, temp3, 0xffu << 23); // uncompressed ? 0xff800000u : 0x7ff80000u
1322 __ Bic(temp1, temp1, Operand(temp3, vixl32::LSR, 31 - 3)); // &= ~(uncompressed ? 0xfu : 0x7u)
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001323 __ Asr(temp3, temp3, 7u); // uncompressed ? 0xffff0000u : 0xff0000u.
1324 __ Lsr(temp2, temp2, temp1); // Extract second character.
1325 __ Lsr(temp3, temp3, 16u); // uncompressed ? 0xffffu : 0xffu
1326 __ Lsr(out, temp_reg, temp1); // Extract first character.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001327 __ And(temp2, temp2, temp3);
1328 __ And(out, out, temp3);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001329 } else {
Anton Kirilovb88c4842016-11-14 14:37:00 +00001330 __ Bic(temp1, temp1, 0xf);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001331 __ Lsr(temp2, temp2, temp1);
1332 __ Lsr(out, temp_reg, temp1);
Anton Kirilovb88c4842016-11-14 14:37:00 +00001333 __ Movt(temp2, 0);
1334 __ Movt(out, 0);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001335 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01001336
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001337 __ Sub(out, out, temp2);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001338 temps.Release(temp_reg);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001339
1340 if (mirror::kUseStringCompression) {
Artem Serov672b9c12017-12-05 18:04:07 +00001341 __ B(end);
1342 __ Bind(different_compression);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001343
1344 // Comparison for different compression style.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001345 const size_t c_char_size = DataType::Size(DataType::Type::kInt8);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001346 DCHECK_EQ(c_char_size, 1u);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001347
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001348 // We want to free up the temp3, currently holding `str.count`, for comparison.
1349 // So, we move it to the bottom bit of the iteration count `temp0` which we tnen
1350 // need to treat as unsigned. Start by freeing the bit with an ADD and continue
1351 // further down by a LSRS+SBC which will flip the meaning of the flag but allow
1352 // `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001353 __ Add(temp0, temp0, temp0); // Unlike LSL, this ADD is always 16-bit.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001354 // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001355 __ Mov(temp1, str);
1356 __ Mov(temp2, arg);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001357 __ Lsrs(temp3, temp3, 1u); // Continue the move of the compression flag.
1358 {
Artem Serov0fb37192016-12-06 18:13:40 +00001359 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1360 3 * kMaxInstructionSizeInBytes,
1361 CodeBufferCheckScope::kMaximumSize);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001362 __ itt(cs); // Interleave with selection of temp1 and temp2.
1363 __ mov(cs, temp1, arg); // Preserves flags.
1364 __ mov(cs, temp2, str); // Preserves flags.
1365 }
Anton Kirilovb88c4842016-11-14 14:37:00 +00001366 __ Sbc(temp0, temp0, 0); // Complete the move of the compression flag.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001367
1368 // Adjust temp1 and temp2 from string pointers to data pointers.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001369 __ Add(temp1, temp1, value_offset);
1370 __ Add(temp2, temp2, value_offset);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001371
1372 vixl32::Label different_compression_loop;
1373 vixl32::Label different_compression_diff;
1374
1375 // Main loop for different compression.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001376 temp_reg = temps.Acquire();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001377 __ Bind(&different_compression_loop);
1378 __ Ldrb(temp_reg, MemOperand(temp1, c_char_size, PostIndex));
1379 __ Ldrh(temp3, MemOperand(temp2, char_size, PostIndex));
Anton Kirilovb88c4842016-11-14 14:37:00 +00001380 __ Cmp(temp_reg, temp3);
Artem Serov517d9f62016-12-12 15:51:15 +00001381 __ B(ne, &different_compression_diff, /* far_target */ false);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001382 __ Subs(temp0, temp0, 2);
Artem Serov517d9f62016-12-12 15:51:15 +00001383 __ B(hi, &different_compression_loop, /* far_target */ false);
Artem Serov672b9c12017-12-05 18:04:07 +00001384 __ B(end);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001385
1386 // Calculate the difference.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001387 __ Bind(&different_compression_diff);
1388 __ Sub(out, temp_reg, temp3);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001389 temps.Release(temp_reg);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001390 // Flip the difference if the `arg` is compressed.
1391 // `temp0` contains inverted `str` compression flag, i.e the same as `arg` compression flag.
1392 __ Lsrs(temp0, temp0, 1u);
1393 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1394 "Expecting 0=compressed, 1=uncompressed");
1395
Artem Serov0fb37192016-12-06 18:13:40 +00001396 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1397 2 * kMaxInstructionSizeInBytes,
1398 CodeBufferCheckScope::kMaximumSize);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001399 __ it(cc);
1400 __ rsb(cc, out, out, 0);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001401 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01001402}
1403
Vladimir Marko984519c2017-08-23 10:45:29 +01001404// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
1405// The normal loop plus the pre-header is 9 instructions (18-26 bytes) without string compression
1406// and 12 instructions (24-32 bytes) with string compression. We can compare up to 4 bytes in 4
1407// instructions (LDR+LDR+CMP+BNE) and up to 8 bytes in 6 instructions (LDRD+LDRD+CMP+BNE+CMP+BNE).
1408// Allow up to 12 instructions (32 bytes) for the unrolled loop.
1409constexpr size_t kShortConstStringEqualsCutoffInBytes = 16;
1410
1411static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
1412 if (candidate->IsLoadString()) {
1413 HLoadString* load_string = candidate->AsLoadString();
1414 const DexFile& dex_file = load_string->GetDexFile();
1415 return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
1416 }
1417 return nullptr;
1418}
1419
Anton Kirilov5ec62182016-10-13 20:16:02 +01001420void IntrinsicLocationsBuilderARMVIXL::VisitStringEquals(HInvoke* invoke) {
Vladimir Markoda283052017-11-07 21:17:24 +00001421 if (kEmitCompilerReadBarrier &&
1422 !StringEqualsOptimizations(invoke).GetArgumentIsString() &&
1423 !StringEqualsOptimizations(invoke).GetNoReadBarrierForStringClass()) {
1424 // No support for this odd case (String class is moveable, not in the boot image).
1425 return;
1426 }
1427
Vladimir Markoca6fff82017-10-03 14:49:14 +01001428 LocationSummary* locations =
1429 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001430 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1431 locations->SetInAt(0, Location::RequiresRegister());
1432 locations->SetInAt(1, Location::RequiresRegister());
Vladimir Marko984519c2017-08-23 10:45:29 +01001433
Anton Kirilov5ec62182016-10-13 20:16:02 +01001434 // Temporary registers to store lengths of strings and for calculations.
1435 // Using instruction cbz requires a low register, so explicitly set a temp to be R0.
1436 locations->AddTemp(LocationFrom(r0));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001437
Vladimir Marko984519c2017-08-23 10:45:29 +01001438 // For the generic implementation and for long const strings we need an extra temporary.
1439 // We do not need it for short const strings, up to 4 bytes, see code generation below.
1440 uint32_t const_string_length = 0u;
1441 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1442 if (const_string == nullptr) {
1443 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1444 }
1445 bool is_compressed =
1446 mirror::kUseStringCompression &&
1447 const_string != nullptr &&
1448 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1449 if (const_string == nullptr || const_string_length > (is_compressed ? 4u : 2u)) {
1450 locations->AddTemp(Location::RequiresRegister());
1451 }
1452
1453 // TODO: If the String.equals() is used only for an immediately following HIf, we can
1454 // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
1455 // Then we shall need an extra temporary register instead of the output register.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001456 locations->SetOut(Location::RequiresRegister());
1457}
1458
1459void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) {
1460 ArmVIXLAssembler* assembler = GetAssembler();
1461 LocationSummary* locations = invoke->GetLocations();
1462
1463 vixl32::Register str = InputRegisterAt(invoke, 0);
1464 vixl32::Register arg = InputRegisterAt(invoke, 1);
1465 vixl32::Register out = OutputRegister(invoke);
1466
1467 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001468
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001469 vixl32::Label loop;
Anton Kirilov5ec62182016-10-13 20:16:02 +01001470 vixl32::Label end;
1471 vixl32::Label return_true;
1472 vixl32::Label return_false;
Anton Kirilov6f644202017-02-27 18:29:45 +00001473 vixl32::Label* final_label = codegen_->GetFinalLabel(invoke, &end);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001474
1475 // Get offsets of count, value, and class fields within a string object.
1476 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
1477 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1478 const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
1479
1480 // Note that the null check must have been done earlier.
1481 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1482
1483 StringEqualsOptimizations optimizations(invoke);
1484 if (!optimizations.GetArgumentNotNull()) {
1485 // Check if input is null, return false if it is.
xueliang.zhongf51bc622016-11-04 09:23:32 +00001486 __ CompareAndBranchIfZero(arg, &return_false, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001487 }
1488
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001489 // Reference equality check, return true if same reference.
1490 __ Cmp(str, arg);
Artem Serov517d9f62016-12-12 15:51:15 +00001491 __ B(eq, &return_true, /* far_target */ false);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001492
Anton Kirilov5ec62182016-10-13 20:16:02 +01001493 if (!optimizations.GetArgumentIsString()) {
1494 // Instanceof check for the argument by comparing class fields.
1495 // All string objects must have the same type since String cannot be subclassed.
1496 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1497 // If the argument is a string object, its class field must be equal to receiver's class field.
1498 __ Ldr(temp, MemOperand(str, class_offset));
Vladimir Marko984519c2017-08-23 10:45:29 +01001499 __ Ldr(out, MemOperand(arg, class_offset));
1500 __ Cmp(temp, out);
Artem Serov517d9f62016-12-12 15:51:15 +00001501 __ B(ne, &return_false, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001502 }
1503
Vladimir Marko984519c2017-08-23 10:45:29 +01001504 // Check if one of the inputs is a const string. Do not special-case both strings
1505 // being const, such cases should be handled by constant folding if needed.
1506 uint32_t const_string_length = 0u;
1507 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1508 if (const_string == nullptr) {
1509 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1510 if (const_string != nullptr) {
1511 std::swap(str, arg); // Make sure the const string is in `str`.
1512 }
1513 }
1514 bool is_compressed =
1515 mirror::kUseStringCompression &&
1516 const_string != nullptr &&
1517 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1518
1519 if (const_string != nullptr) {
1520 // Load `count` field of the argument string and check if it matches the const string.
1521 // Also compares the compression style, if differs return false.
1522 __ Ldr(temp, MemOperand(arg, count_offset));
1523 __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
1524 __ B(ne, &return_false, /* far_target */ false);
1525 } else {
1526 // Load `count` fields of this and argument strings.
1527 __ Ldr(temp, MemOperand(str, count_offset));
1528 __ Ldr(out, MemOperand(arg, count_offset));
1529 // Check if `count` fields are equal, return false if they're not.
1530 // Also compares the compression style, if differs return false.
1531 __ Cmp(temp, out);
1532 __ B(ne, &return_false, /* far_target */ false);
1533 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01001534
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001535 // Assertions that must hold in order to compare strings 4 bytes at a time.
Vladimir Marko984519c2017-08-23 10:45:29 +01001536 // Ok to do this because strings are zero-padded to kObjectAlignment.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001537 DCHECK_ALIGNED(value_offset, 4);
1538 static_assert(IsAligned<4>(kObjectAlignment), "String data must be aligned for fast compare.");
1539
Vladimir Marko984519c2017-08-23 10:45:29 +01001540 if (const_string != nullptr &&
1541 const_string_length <= (is_compressed ? kShortConstStringEqualsCutoffInBytes
1542 : kShortConstStringEqualsCutoffInBytes / 2u)) {
1543 // Load and compare the contents. Though we know the contents of the short const string
1544 // at compile time, materializing constants may be more code than loading from memory.
1545 int32_t offset = value_offset;
1546 size_t remaining_bytes =
1547 RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 4u);
1548 while (remaining_bytes > sizeof(uint32_t)) {
1549 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1550 UseScratchRegisterScope scratch_scope(assembler->GetVIXLAssembler());
1551 vixl32::Register temp2 = scratch_scope.Acquire();
1552 __ Ldrd(temp, temp1, MemOperand(str, offset));
1553 __ Ldrd(temp2, out, MemOperand(arg, offset));
1554 __ Cmp(temp, temp2);
1555 __ B(ne, &return_false, /* far_label */ false);
1556 __ Cmp(temp1, out);
1557 __ B(ne, &return_false, /* far_label */ false);
1558 offset += 2u * sizeof(uint32_t);
1559 remaining_bytes -= 2u * sizeof(uint32_t);
1560 }
1561 if (remaining_bytes != 0u) {
1562 __ Ldr(temp, MemOperand(str, offset));
1563 __ Ldr(out, MemOperand(arg, offset));
1564 __ Cmp(temp, out);
1565 __ B(ne, &return_false, /* far_label */ false);
1566 }
1567 } else {
1568 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
1569 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1570 "Expecting 0=compressed, 1=uncompressed");
1571 __ CompareAndBranchIfZero(temp, &return_true, /* far_target */ false);
1572
1573 if (mirror::kUseStringCompression) {
1574 // For string compression, calculate the number of bytes to compare (not chars).
1575 // This could in theory exceed INT32_MAX, so treat temp as unsigned.
1576 __ Lsrs(temp, temp, 1u); // Extract length and check compression flag.
1577 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1578 2 * kMaxInstructionSizeInBytes,
1579 CodeBufferCheckScope::kMaximumSize);
1580 __ it(cs); // If uncompressed,
1581 __ add(cs, temp, temp, temp); // double the byte count.
1582 }
1583
1584 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1585 UseScratchRegisterScope scratch_scope(assembler->GetVIXLAssembler());
1586 vixl32::Register temp2 = scratch_scope.Acquire();
1587
1588 // Store offset of string value in preparation for comparison loop.
1589 __ Mov(temp1, value_offset);
1590
1591 // Loop to compare strings 4 bytes at a time starting at the front of the string.
1592 __ Bind(&loop);
1593 __ Ldr(out, MemOperand(str, temp1));
1594 __ Ldr(temp2, MemOperand(arg, temp1));
1595 __ Add(temp1, temp1, Operand::From(sizeof(uint32_t)));
1596 __ Cmp(out, temp2);
1597 __ B(ne, &return_false, /* far_target */ false);
1598 // With string compression, we have compared 4 bytes, otherwise 2 chars.
1599 __ Subs(temp, temp, mirror::kUseStringCompression ? 4 : 2);
1600 __ B(hi, &loop, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001601 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001602
Anton Kirilov5ec62182016-10-13 20:16:02 +01001603 // Return true and exit the function.
1604 // If loop does not result in returning false, we return true.
1605 __ Bind(&return_true);
1606 __ Mov(out, 1);
Anton Kirilov6f644202017-02-27 18:29:45 +00001607 __ B(final_label);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001608
1609 // Return false and exit the function.
1610 __ Bind(&return_false);
1611 __ Mov(out, 0);
Anton Kirilov6f644202017-02-27 18:29:45 +00001612
1613 if (end.IsReferenced()) {
1614 __ Bind(&end);
1615 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01001616}
1617
1618static void GenerateVisitStringIndexOf(HInvoke* invoke,
1619 ArmVIXLAssembler* assembler,
1620 CodeGeneratorARMVIXL* codegen,
Anton Kirilov5ec62182016-10-13 20:16:02 +01001621 bool start_at_zero) {
1622 LocationSummary* locations = invoke->GetLocations();
1623
1624 // Note that the null check must have been done earlier.
1625 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1626
1627 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
1628 // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
1629 SlowPathCodeARMVIXL* slow_path = nullptr;
1630 HInstruction* code_point = invoke->InputAt(1);
1631 if (code_point->IsIntConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00001632 if (static_cast<uint32_t>(Int32ConstantFrom(code_point)) >
Anton Kirilov5ec62182016-10-13 20:16:02 +01001633 std::numeric_limits<uint16_t>::max()) {
1634 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1635 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
Vladimir Marko174b2e22017-10-12 13:34:49 +01001636 slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001637 codegen->AddSlowPath(slow_path);
1638 __ B(slow_path->GetEntryLabel());
1639 __ Bind(slow_path->GetExitLabel());
1640 return;
1641 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001642 } else if (code_point->GetType() != DataType::Type::kUint16) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01001643 vixl32::Register char_reg = InputRegisterAt(invoke, 1);
1644 // 0xffff is not modified immediate but 0x10000 is, so use `>= 0x10000` instead of `> 0xffff`.
1645 __ Cmp(char_reg, static_cast<uint32_t>(std::numeric_limits<uint16_t>::max()) + 1);
Vladimir Marko174b2e22017-10-12 13:34:49 +01001646 slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001647 codegen->AddSlowPath(slow_path);
1648 __ B(hs, slow_path->GetEntryLabel());
1649 }
1650
1651 if (start_at_zero) {
1652 vixl32::Register tmp_reg = RegisterFrom(locations->GetTemp(0));
1653 DCHECK(tmp_reg.Is(r2));
1654 // Start-index = 0.
1655 __ Mov(tmp_reg, 0);
1656 }
1657
1658 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
1659 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
1660
1661 if (slow_path != nullptr) {
1662 __ Bind(slow_path->GetExitLabel());
1663 }
1664}
1665
1666void IntrinsicLocationsBuilderARMVIXL::VisitStringIndexOf(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001667 LocationSummary* locations = new (allocator_) LocationSummary(
1668 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001669 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1670 // best to align the inputs accordingly.
1671 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1672 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1673 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1674 locations->SetOut(LocationFrom(r0));
1675
1676 // Need to send start-index=0.
1677 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1678}
1679
1680void IntrinsicCodeGeneratorARMVIXL::VisitStringIndexOf(HInvoke* invoke) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001681 GenerateVisitStringIndexOf(invoke, GetAssembler(), codegen_, /* start_at_zero */ true);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001682}
1683
1684void IntrinsicLocationsBuilderARMVIXL::VisitStringIndexOfAfter(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001685 LocationSummary* locations = new (allocator_) LocationSummary(
1686 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001687 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1688 // best to align the inputs accordingly.
1689 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1690 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1691 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1692 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1693 locations->SetOut(LocationFrom(r0));
1694}
1695
1696void IntrinsicCodeGeneratorARMVIXL::VisitStringIndexOfAfter(HInvoke* invoke) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001697 GenerateVisitStringIndexOf(invoke, GetAssembler(), codegen_, /* start_at_zero */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001698}
1699
1700void IntrinsicLocationsBuilderARMVIXL::VisitStringNewStringFromBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001701 LocationSummary* locations = new (allocator_) LocationSummary(
1702 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001703 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1704 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1705 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1706 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1707 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1708 locations->SetOut(LocationFrom(r0));
1709}
1710
1711void IntrinsicCodeGeneratorARMVIXL::VisitStringNewStringFromBytes(HInvoke* invoke) {
1712 ArmVIXLAssembler* assembler = GetAssembler();
1713 vixl32::Register byte_array = InputRegisterAt(invoke, 0);
1714 __ Cmp(byte_array, 0);
Vladimir Marko174b2e22017-10-12 13:34:49 +01001715 SlowPathCodeARMVIXL* slow_path =
1716 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001717 codegen_->AddSlowPath(slow_path);
1718 __ B(eq, slow_path->GetEntryLabel());
1719
1720 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
1721 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
1722 __ Bind(slow_path->GetExitLabel());
1723}
1724
1725void IntrinsicLocationsBuilderARMVIXL::VisitStringNewStringFromChars(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001726 LocationSummary* locations =
1727 new (allocator_) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001728 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1729 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1730 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1731 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1732 locations->SetOut(LocationFrom(r0));
1733}
1734
1735void IntrinsicCodeGeneratorARMVIXL::VisitStringNewStringFromChars(HInvoke* invoke) {
1736 // No need to emit code checking whether `locations->InAt(2)` is a null
1737 // pointer, as callers of the native method
1738 //
1739 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1740 //
1741 // all include a null check on `data` before calling that method.
1742 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
1743 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
1744}
1745
1746void IntrinsicLocationsBuilderARMVIXL::VisitStringNewStringFromString(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001747 LocationSummary* locations = new (allocator_) LocationSummary(
1748 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001749 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1750 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1751 locations->SetOut(LocationFrom(r0));
1752}
1753
1754void IntrinsicCodeGeneratorARMVIXL::VisitStringNewStringFromString(HInvoke* invoke) {
1755 ArmVIXLAssembler* assembler = GetAssembler();
1756 vixl32::Register string_to_copy = InputRegisterAt(invoke, 0);
1757 __ Cmp(string_to_copy, 0);
Vladimir Marko174b2e22017-10-12 13:34:49 +01001758 SlowPathCodeARMVIXL* slow_path =
1759 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001760 codegen_->AddSlowPath(slow_path);
1761 __ B(eq, slow_path->GetEntryLabel());
1762
1763 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
1764 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
1765
1766 __ Bind(slow_path->GetExitLabel());
1767}
1768
1769void IntrinsicLocationsBuilderARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) {
1770 // The only read barrier implementation supporting the
1771 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1772 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
1773 return;
1774 }
1775
1776 CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
1777 LocationSummary* locations = invoke->GetLocations();
1778 if (locations == nullptr) {
1779 return;
1780 }
1781
1782 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1783 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
1784 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1785
1786 if (src_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(src_pos->GetValue())) {
1787 locations->SetInAt(1, Location::RequiresRegister());
1788 }
1789 if (dest_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(dest_pos->GetValue())) {
1790 locations->SetInAt(3, Location::RequiresRegister());
1791 }
1792 if (length != nullptr && !assembler_->ShifterOperandCanAlwaysHold(length->GetValue())) {
1793 locations->SetInAt(4, Location::RequiresRegister());
1794 }
1795 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1796 // Temporary register IP cannot be used in
1797 // ReadBarrierSystemArrayCopySlowPathARM (because that register
1798 // is clobbered by ReadBarrierMarkRegX entry points). Get an extra
1799 // temporary register from the register allocator.
1800 locations->AddTemp(Location::RequiresRegister());
1801 }
1802}
1803
1804static void CheckPosition(ArmVIXLAssembler* assembler,
1805 Location pos,
1806 vixl32::Register input,
1807 Location length,
1808 SlowPathCodeARMVIXL* slow_path,
1809 vixl32::Register temp,
1810 bool length_is_input_length = false) {
1811 // Where is the length in the Array?
1812 const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
1813
1814 if (pos.IsConstant()) {
1815 int32_t pos_const = Int32ConstantFrom(pos);
1816 if (pos_const == 0) {
1817 if (!length_is_input_length) {
1818 // Check that length(input) >= length.
1819 __ Ldr(temp, MemOperand(input, length_offset));
1820 if (length.IsConstant()) {
1821 __ Cmp(temp, Int32ConstantFrom(length));
1822 } else {
1823 __ Cmp(temp, RegisterFrom(length));
1824 }
1825 __ B(lt, slow_path->GetEntryLabel());
1826 }
1827 } else {
1828 // Check that length(input) >= pos.
1829 __ Ldr(temp, MemOperand(input, length_offset));
1830 __ Subs(temp, temp, pos_const);
1831 __ B(lt, slow_path->GetEntryLabel());
1832
1833 // Check that (length(input) - pos) >= length.
1834 if (length.IsConstant()) {
1835 __ Cmp(temp, Int32ConstantFrom(length));
1836 } else {
1837 __ Cmp(temp, RegisterFrom(length));
1838 }
1839 __ B(lt, slow_path->GetEntryLabel());
1840 }
1841 } else if (length_is_input_length) {
1842 // The only way the copy can succeed is if pos is zero.
1843 vixl32::Register pos_reg = RegisterFrom(pos);
xueliang.zhongf51bc622016-11-04 09:23:32 +00001844 __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001845 } else {
1846 // Check that pos >= 0.
1847 vixl32::Register pos_reg = RegisterFrom(pos);
1848 __ Cmp(pos_reg, 0);
1849 __ B(lt, slow_path->GetEntryLabel());
1850
1851 // Check that pos <= length(input).
1852 __ Ldr(temp, MemOperand(input, length_offset));
1853 __ Subs(temp, temp, pos_reg);
1854 __ B(lt, slow_path->GetEntryLabel());
1855
1856 // Check that (length(input) - pos) >= length.
1857 if (length.IsConstant()) {
1858 __ Cmp(temp, Int32ConstantFrom(length));
1859 } else {
1860 __ Cmp(temp, RegisterFrom(length));
1861 }
1862 __ B(lt, slow_path->GetEntryLabel());
1863 }
1864}
1865
1866void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) {
1867 // The only read barrier implementation supporting the
1868 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1869 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1870
1871 ArmVIXLAssembler* assembler = GetAssembler();
1872 LocationSummary* locations = invoke->GetLocations();
1873
1874 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1875 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
1876 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
1877 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
1878 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
1879
1880 vixl32::Register src = InputRegisterAt(invoke, 0);
1881 Location src_pos = locations->InAt(1);
1882 vixl32::Register dest = InputRegisterAt(invoke, 2);
1883 Location dest_pos = locations->InAt(3);
1884 Location length = locations->InAt(4);
1885 Location temp1_loc = locations->GetTemp(0);
1886 vixl32::Register temp1 = RegisterFrom(temp1_loc);
1887 Location temp2_loc = locations->GetTemp(1);
1888 vixl32::Register temp2 = RegisterFrom(temp2_loc);
1889 Location temp3_loc = locations->GetTemp(2);
1890 vixl32::Register temp3 = RegisterFrom(temp3_loc);
1891
Vladimir Marko174b2e22017-10-12 13:34:49 +01001892 SlowPathCodeARMVIXL* intrinsic_slow_path =
1893 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001894 codegen_->AddSlowPath(intrinsic_slow_path);
1895
1896 vixl32::Label conditions_on_positions_validated;
1897 SystemArrayCopyOptimizations optimizations(invoke);
1898
1899 // If source and destination are the same, we go to slow path if we need to do
1900 // forward copying.
1901 if (src_pos.IsConstant()) {
1902 int32_t src_pos_constant = Int32ConstantFrom(src_pos);
1903 if (dest_pos.IsConstant()) {
1904 int32_t dest_pos_constant = Int32ConstantFrom(dest_pos);
1905 if (optimizations.GetDestinationIsSource()) {
1906 // Checked when building locations.
1907 DCHECK_GE(src_pos_constant, dest_pos_constant);
1908 } else if (src_pos_constant < dest_pos_constant) {
1909 __ Cmp(src, dest);
1910 __ B(eq, intrinsic_slow_path->GetEntryLabel());
1911 }
1912
1913 // Checked when building locations.
1914 DCHECK(!optimizations.GetDestinationIsSource()
1915 || (src_pos_constant >= Int32ConstantFrom(dest_pos)));
1916 } else {
1917 if (!optimizations.GetDestinationIsSource()) {
1918 __ Cmp(src, dest);
Artem Serov517d9f62016-12-12 15:51:15 +00001919 __ B(ne, &conditions_on_positions_validated, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001920 }
1921 __ Cmp(RegisterFrom(dest_pos), src_pos_constant);
1922 __ B(gt, intrinsic_slow_path->GetEntryLabel());
1923 }
1924 } else {
1925 if (!optimizations.GetDestinationIsSource()) {
1926 __ Cmp(src, dest);
Artem Serov517d9f62016-12-12 15:51:15 +00001927 __ B(ne, &conditions_on_positions_validated, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001928 }
1929 if (dest_pos.IsConstant()) {
1930 int32_t dest_pos_constant = Int32ConstantFrom(dest_pos);
1931 __ Cmp(RegisterFrom(src_pos), dest_pos_constant);
1932 } else {
1933 __ Cmp(RegisterFrom(src_pos), RegisterFrom(dest_pos));
1934 }
1935 __ B(lt, intrinsic_slow_path->GetEntryLabel());
1936 }
1937
1938 __ Bind(&conditions_on_positions_validated);
1939
1940 if (!optimizations.GetSourceIsNotNull()) {
1941 // Bail out if the source is null.
xueliang.zhongf51bc622016-11-04 09:23:32 +00001942 __ CompareAndBranchIfZero(src, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001943 }
1944
1945 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
1946 // Bail out if the destination is null.
xueliang.zhongf51bc622016-11-04 09:23:32 +00001947 __ CompareAndBranchIfZero(dest, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001948 }
1949
1950 // If the length is negative, bail out.
1951 // We have already checked in the LocationsBuilder for the constant case.
1952 if (!length.IsConstant() &&
1953 !optimizations.GetCountIsSourceLength() &&
1954 !optimizations.GetCountIsDestinationLength()) {
1955 __ Cmp(RegisterFrom(length), 0);
1956 __ B(lt, intrinsic_slow_path->GetEntryLabel());
1957 }
1958
1959 // Validity checks: source.
1960 CheckPosition(assembler,
1961 src_pos,
1962 src,
1963 length,
1964 intrinsic_slow_path,
1965 temp1,
1966 optimizations.GetCountIsSourceLength());
1967
1968 // Validity checks: dest.
1969 CheckPosition(assembler,
1970 dest_pos,
1971 dest,
1972 length,
1973 intrinsic_slow_path,
1974 temp1,
1975 optimizations.GetCountIsDestinationLength());
1976
1977 if (!optimizations.GetDoesNotNeedTypeCheck()) {
1978 // Check whether all elements of the source array are assignable to the component
1979 // type of the destination array. We do two checks: the classes are the same,
1980 // or the destination is Object[]. If none of these checks succeed, we go to the
1981 // slow path.
1982
1983 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1984 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1985 // /* HeapReference<Class> */ temp1 = src->klass_
1986 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1987 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
1988 // Bail out if the source is not a non primitive array.
1989 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1990 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1991 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
xueliang.zhongf51bc622016-11-04 09:23:32 +00001992 __ CompareAndBranchIfZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001993 // If heap poisoning is enabled, `temp1` has been unpoisoned
1994 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1995 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
1996 __ Ldrh(temp1, MemOperand(temp1, primitive_offset));
1997 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00001998 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001999 }
2000
2001 // /* HeapReference<Class> */ temp1 = dest->klass_
2002 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2003 invoke, temp1_loc, dest, class_offset, temp2_loc, /* needs_null_check */ false);
2004
2005 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2006 // Bail out if the destination is not a non primitive array.
2007 //
2008 // Register `temp1` is not trashed by the read barrier emitted
2009 // by GenerateFieldLoadWithBakerReadBarrier below, as that
2010 // method produces a call to a ReadBarrierMarkRegX entry point,
2011 // which saves all potentially live registers, including
2012 // temporaries such a `temp1`.
2013 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2014 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2015 invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
xueliang.zhongf51bc622016-11-04 09:23:32 +00002016 __ CompareAndBranchIfZero(temp2, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002017 // If heap poisoning is enabled, `temp2` has been unpoisoned
2018 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2019 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2020 __ Ldrh(temp2, MemOperand(temp2, primitive_offset));
2021 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00002022 __ CompareAndBranchIfNonZero(temp2, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002023 }
2024
2025 // For the same reason given earlier, `temp1` is not trashed by the
2026 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
2027 // /* HeapReference<Class> */ temp2 = src->klass_
2028 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2029 invoke, temp2_loc, src, class_offset, temp3_loc, /* needs_null_check */ false);
2030 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
2031 __ Cmp(temp1, temp2);
2032
2033 if (optimizations.GetDestinationIsTypedObjectArray()) {
2034 vixl32::Label do_copy;
Artem Serov517d9f62016-12-12 15:51:15 +00002035 __ B(eq, &do_copy, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002036 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2037 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2038 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
2039 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2040 // We do not need to emit a read barrier for the following
2041 // heap reference load, as `temp1` is only used in a
2042 // comparison with null below, and this reference is not
2043 // kept afterwards.
2044 __ Ldr(temp1, MemOperand(temp1, super_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00002045 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002046 __ Bind(&do_copy);
2047 } else {
2048 __ B(ne, intrinsic_slow_path->GetEntryLabel());
2049 }
2050 } else {
2051 // Non read barrier code.
2052
2053 // /* HeapReference<Class> */ temp1 = dest->klass_
2054 __ Ldr(temp1, MemOperand(dest, class_offset));
2055 // /* HeapReference<Class> */ temp2 = src->klass_
2056 __ Ldr(temp2, MemOperand(src, class_offset));
2057 bool did_unpoison = false;
2058 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2059 !optimizations.GetSourceIsNonPrimitiveArray()) {
2060 // One or two of the references need to be unpoisoned. Unpoison them
2061 // both to make the identity check valid.
2062 assembler->MaybeUnpoisonHeapReference(temp1);
2063 assembler->MaybeUnpoisonHeapReference(temp2);
2064 did_unpoison = true;
2065 }
2066
2067 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2068 // Bail out if the destination is not a non primitive array.
2069 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2070 __ Ldr(temp3, MemOperand(temp1, component_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00002071 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002072 assembler->MaybeUnpoisonHeapReference(temp3);
2073 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2074 __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
2075 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00002076 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002077 }
2078
2079 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2080 // Bail out if the source is not a non primitive array.
2081 // /* HeapReference<Class> */ temp3 = temp2->component_type_
2082 __ Ldr(temp3, MemOperand(temp2, component_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00002083 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002084 assembler->MaybeUnpoisonHeapReference(temp3);
2085 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2086 __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
2087 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00002088 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002089 }
2090
2091 __ Cmp(temp1, temp2);
2092
2093 if (optimizations.GetDestinationIsTypedObjectArray()) {
2094 vixl32::Label do_copy;
Artem Serov517d9f62016-12-12 15:51:15 +00002095 __ B(eq, &do_copy, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002096 if (!did_unpoison) {
2097 assembler->MaybeUnpoisonHeapReference(temp1);
2098 }
2099 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2100 __ Ldr(temp1, MemOperand(temp1, component_offset));
2101 assembler->MaybeUnpoisonHeapReference(temp1);
2102 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2103 __ Ldr(temp1, MemOperand(temp1, super_offset));
2104 // No need to unpoison the result, we're comparing against null.
xueliang.zhongf51bc622016-11-04 09:23:32 +00002105 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002106 __ Bind(&do_copy);
2107 } else {
2108 __ B(ne, intrinsic_slow_path->GetEntryLabel());
2109 }
2110 }
2111 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2112 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2113 // Bail out if the source is not a non primitive array.
2114 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2115 // /* HeapReference<Class> */ temp1 = src->klass_
2116 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2117 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
2118 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2119 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2120 invoke, temp3_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
xueliang.zhongf51bc622016-11-04 09:23:32 +00002121 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002122 // If heap poisoning is enabled, `temp3` has been unpoisoned
2123 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2124 } else {
2125 // /* HeapReference<Class> */ temp1 = src->klass_
2126 __ Ldr(temp1, MemOperand(src, class_offset));
2127 assembler->MaybeUnpoisonHeapReference(temp1);
2128 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2129 __ Ldr(temp3, MemOperand(temp1, component_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00002130 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002131 assembler->MaybeUnpoisonHeapReference(temp3);
2132 }
2133 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2134 __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
2135 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00002136 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002137 }
2138
Roland Levillain1663d162017-03-17 15:15:21 +00002139 if (length.IsConstant() && Int32ConstantFrom(length) == 0) {
2140 // Null constant length: not need to emit the loop code at all.
Anton Kirilov5ec62182016-10-13 20:16:02 +01002141 } else {
Roland Levillain1663d162017-03-17 15:15:21 +00002142 vixl32::Label done;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002143 const DataType::Type type = DataType::Type::kReference;
2144 const int32_t element_size = DataType::Size(type);
Roland Levillain1663d162017-03-17 15:15:21 +00002145
2146 if (length.IsRegister()) {
2147 // Don't enter the copy loop if the length is null.
2148 __ CompareAndBranchIfZero(RegisterFrom(length), &done, /* is_far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002149 }
Roland Levillain1663d162017-03-17 15:15:21 +00002150
2151 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2152 // TODO: Also convert this intrinsic to the IsGcMarking strategy?
2153
2154 // SystemArrayCopy implementation for Baker read barriers (see
Roland Levillain9983e302017-07-14 14:34:22 +01002155 // also CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier):
Roland Levillain1663d162017-03-17 15:15:21 +00002156 //
2157 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
2158 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
2159 // bool is_gray = (rb_state == ReadBarrier::GrayState());
2160 // if (is_gray) {
2161 // // Slow-path copy.
2162 // do {
2163 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
2164 // } while (src_ptr != end_ptr)
2165 // } else {
2166 // // Fast-path copy.
2167 // do {
2168 // *dest_ptr++ = *src_ptr++;
2169 // } while (src_ptr != end_ptr)
2170 // }
2171
2172 // /* int32_t */ monitor = src->monitor_
2173 __ Ldr(temp2, MemOperand(src, monitor_offset));
2174 // /* LockWord */ lock_word = LockWord(monitor)
2175 static_assert(sizeof(LockWord) == sizeof(int32_t),
2176 "art::LockWord and int32_t have different sizes.");
2177
2178 // Introduce a dependency on the lock_word including the rb_state,
2179 // which shall prevent load-load reordering without using
2180 // a memory barrier (which would be more expensive).
2181 // `src` is unchanged by this operation, but its value now depends
2182 // on `temp2`.
2183 __ Add(src, src, Operand(temp2, vixl32::LSR, 32));
2184
2185 // Compute the base source address in `temp1`.
2186 // Note that `temp1` (the base source address) is computed from
2187 // `src` (and `src_pos`) here, and thus honors the artificial
2188 // dependency of `src` on `temp2`.
2189 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2190 // Compute the end source address in `temp3`.
2191 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2192 // The base destination address is computed later, as `temp2` is
2193 // used for intermediate computations.
2194
2195 // Slow path used to copy array when `src` is gray.
2196 // Note that the base destination address is computed in `temp2`
2197 // by the slow path code.
2198 SlowPathCodeARMVIXL* read_barrier_slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01002199 new (codegen_->GetScopedAllocator()) ReadBarrierSystemArrayCopySlowPathARMVIXL(invoke);
Roland Levillain1663d162017-03-17 15:15:21 +00002200 codegen_->AddSlowPath(read_barrier_slow_path);
2201
2202 // Given the numeric representation, it's enough to check the low bit of the
2203 // rb_state. We do that by shifting the bit out of the lock word with LSRS
2204 // which can be a 16-bit instruction unlike the TST immediate.
2205 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
2206 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
2207 __ Lsrs(temp2, temp2, LockWord::kReadBarrierStateShift + 1);
2208 // Carry flag is the last bit shifted out by LSRS.
2209 __ B(cs, read_barrier_slow_path->GetEntryLabel());
2210
2211 // Fast-path copy.
2212 // Compute the base destination address in `temp2`.
2213 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2214 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2215 // poison/unpoison.
2216 vixl32::Label loop;
2217 __ Bind(&loop);
2218 {
2219 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2220 const vixl32::Register temp_reg = temps.Acquire();
2221 __ Ldr(temp_reg, MemOperand(temp1, element_size, PostIndex));
2222 __ Str(temp_reg, MemOperand(temp2, element_size, PostIndex));
2223 }
2224 __ Cmp(temp1, temp3);
2225 __ B(ne, &loop, /* far_target */ false);
2226
2227 __ Bind(read_barrier_slow_path->GetExitLabel());
2228 } else {
2229 // Non read barrier code.
2230 // Compute the base source address in `temp1`.
2231 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2232 // Compute the base destination address in `temp2`.
2233 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2234 // Compute the end source address in `temp3`.
2235 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2236 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2237 // poison/unpoison.
2238 vixl32::Label loop;
2239 __ Bind(&loop);
2240 {
2241 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2242 const vixl32::Register temp_reg = temps.Acquire();
2243 __ Ldr(temp_reg, MemOperand(temp1, element_size, PostIndex));
2244 __ Str(temp_reg, MemOperand(temp2, element_size, PostIndex));
2245 }
2246 __ Cmp(temp1, temp3);
2247 __ B(ne, &loop, /* far_target */ false);
2248 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01002249 __ Bind(&done);
2250 }
2251
2252 // We only need one card marking on the destination array.
2253 codegen_->MarkGCCard(temp1, temp2, dest, NoReg, /* value_can_be_null */ false);
2254
2255 __ Bind(intrinsic_slow_path->GetExitLabel());
2256}
2257
Vladimir Markoca6fff82017-10-03 14:49:14 +01002258static void CreateFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01002259 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2260 // the code generator. Furthermore, the register allocator creates fixed live intervals
2261 // for all caller-saved registers because we are doing a function call. As a result, if
2262 // the input and output locations are unallocated, the register allocator runs out of
2263 // registers and fails; however, a debuggable graph is not the common case.
2264 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2265 return;
2266 }
2267
2268 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002269 DCHECK_EQ(invoke->InputAt(0)->GetType(), DataType::Type::kFloat64);
2270 DCHECK_EQ(invoke->GetType(), DataType::Type::kFloat64);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002271
Vladimir Markoca6fff82017-10-03 14:49:14 +01002272 LocationSummary* const locations =
2273 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002274 const InvokeRuntimeCallingConventionARMVIXL calling_convention;
2275
2276 locations->SetInAt(0, Location::RequiresFpuRegister());
2277 locations->SetOut(Location::RequiresFpuRegister());
2278 // Native code uses the soft float ABI.
2279 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
2280 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
2281}
2282
Vladimir Markoca6fff82017-10-03 14:49:14 +01002283static void CreateFPFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01002284 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2285 // the code generator. Furthermore, the register allocator creates fixed live intervals
2286 // for all caller-saved registers because we are doing a function call. As a result, if
2287 // the input and output locations are unallocated, the register allocator runs out of
2288 // registers and fails; however, a debuggable graph is not the common case.
2289 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2290 return;
2291 }
2292
2293 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002294 DCHECK_EQ(invoke->InputAt(0)->GetType(), DataType::Type::kFloat64);
2295 DCHECK_EQ(invoke->InputAt(1)->GetType(), DataType::Type::kFloat64);
2296 DCHECK_EQ(invoke->GetType(), DataType::Type::kFloat64);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002297
Vladimir Markoca6fff82017-10-03 14:49:14 +01002298 LocationSummary* const locations =
2299 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002300 const InvokeRuntimeCallingConventionARMVIXL calling_convention;
2301
2302 locations->SetInAt(0, Location::RequiresFpuRegister());
2303 locations->SetInAt(1, Location::RequiresFpuRegister());
2304 locations->SetOut(Location::RequiresFpuRegister());
2305 // Native code uses the soft float ABI.
2306 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
2307 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
2308 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
2309 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(3)));
2310}
2311
2312static void GenFPToFPCall(HInvoke* invoke,
2313 ArmVIXLAssembler* assembler,
2314 CodeGeneratorARMVIXL* codegen,
2315 QuickEntrypointEnum entry) {
2316 LocationSummary* const locations = invoke->GetLocations();
2317
2318 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2319 DCHECK(locations->WillCall() && locations->Intrinsified());
2320
2321 // Native code uses the soft float ABI.
2322 __ Vmov(RegisterFrom(locations->GetTemp(0)),
2323 RegisterFrom(locations->GetTemp(1)),
2324 InputDRegisterAt(invoke, 0));
2325 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
2326 __ Vmov(OutputDRegister(invoke),
2327 RegisterFrom(locations->GetTemp(0)),
2328 RegisterFrom(locations->GetTemp(1)));
2329}
2330
2331static void GenFPFPToFPCall(HInvoke* invoke,
2332 ArmVIXLAssembler* assembler,
2333 CodeGeneratorARMVIXL* codegen,
2334 QuickEntrypointEnum entry) {
2335 LocationSummary* const locations = invoke->GetLocations();
2336
2337 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2338 DCHECK(locations->WillCall() && locations->Intrinsified());
2339
2340 // Native code uses the soft float ABI.
2341 __ Vmov(RegisterFrom(locations->GetTemp(0)),
2342 RegisterFrom(locations->GetTemp(1)),
2343 InputDRegisterAt(invoke, 0));
2344 __ Vmov(RegisterFrom(locations->GetTemp(2)),
2345 RegisterFrom(locations->GetTemp(3)),
2346 InputDRegisterAt(invoke, 1));
2347 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
2348 __ Vmov(OutputDRegister(invoke),
2349 RegisterFrom(locations->GetTemp(0)),
2350 RegisterFrom(locations->GetTemp(1)));
2351}
2352
2353void IntrinsicLocationsBuilderARMVIXL::VisitMathCos(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002354 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002355}
2356
2357void IntrinsicCodeGeneratorARMVIXL::VisitMathCos(HInvoke* invoke) {
2358 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCos);
2359}
2360
2361void IntrinsicLocationsBuilderARMVIXL::VisitMathSin(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002362 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002363}
2364
2365void IntrinsicCodeGeneratorARMVIXL::VisitMathSin(HInvoke* invoke) {
2366 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSin);
2367}
2368
2369void IntrinsicLocationsBuilderARMVIXL::VisitMathAcos(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002370 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002371}
2372
2373void IntrinsicCodeGeneratorARMVIXL::VisitMathAcos(HInvoke* invoke) {
2374 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAcos);
2375}
2376
2377void IntrinsicLocationsBuilderARMVIXL::VisitMathAsin(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002378 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002379}
2380
2381void IntrinsicCodeGeneratorARMVIXL::VisitMathAsin(HInvoke* invoke) {
2382 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAsin);
2383}
2384
2385void IntrinsicLocationsBuilderARMVIXL::VisitMathAtan(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002386 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002387}
2388
2389void IntrinsicCodeGeneratorARMVIXL::VisitMathAtan(HInvoke* invoke) {
2390 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan);
2391}
2392
2393void IntrinsicLocationsBuilderARMVIXL::VisitMathCbrt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002394 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002395}
2396
2397void IntrinsicCodeGeneratorARMVIXL::VisitMathCbrt(HInvoke* invoke) {
2398 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCbrt);
2399}
2400
2401void IntrinsicLocationsBuilderARMVIXL::VisitMathCosh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002402 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002403}
2404
2405void IntrinsicCodeGeneratorARMVIXL::VisitMathCosh(HInvoke* invoke) {
2406 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCosh);
2407}
2408
2409void IntrinsicLocationsBuilderARMVIXL::VisitMathExp(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002410 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002411}
2412
2413void IntrinsicCodeGeneratorARMVIXL::VisitMathExp(HInvoke* invoke) {
2414 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExp);
2415}
2416
2417void IntrinsicLocationsBuilderARMVIXL::VisitMathExpm1(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002418 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002419}
2420
2421void IntrinsicCodeGeneratorARMVIXL::VisitMathExpm1(HInvoke* invoke) {
2422 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExpm1);
2423}
2424
2425void IntrinsicLocationsBuilderARMVIXL::VisitMathLog(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002426 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002427}
2428
2429void IntrinsicCodeGeneratorARMVIXL::VisitMathLog(HInvoke* invoke) {
2430 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog);
2431}
2432
2433void IntrinsicLocationsBuilderARMVIXL::VisitMathLog10(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002434 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002435}
2436
2437void IntrinsicCodeGeneratorARMVIXL::VisitMathLog10(HInvoke* invoke) {
2438 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog10);
2439}
2440
2441void IntrinsicLocationsBuilderARMVIXL::VisitMathSinh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002442 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002443}
2444
2445void IntrinsicCodeGeneratorARMVIXL::VisitMathSinh(HInvoke* invoke) {
2446 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSinh);
2447}
2448
2449void IntrinsicLocationsBuilderARMVIXL::VisitMathTan(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002450 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002451}
2452
2453void IntrinsicCodeGeneratorARMVIXL::VisitMathTan(HInvoke* invoke) {
2454 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTan);
2455}
2456
2457void IntrinsicLocationsBuilderARMVIXL::VisitMathTanh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002458 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002459}
2460
2461void IntrinsicCodeGeneratorARMVIXL::VisitMathTanh(HInvoke* invoke) {
2462 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTanh);
2463}
2464
2465void IntrinsicLocationsBuilderARMVIXL::VisitMathAtan2(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002466 CreateFPFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002467}
2468
2469void IntrinsicCodeGeneratorARMVIXL::VisitMathAtan2(HInvoke* invoke) {
2470 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan2);
2471}
2472
Vladimir Marko4d179872018-01-19 14:50:10 +00002473void IntrinsicLocationsBuilderARMVIXL::VisitMathPow(HInvoke* invoke) {
2474 CreateFPFPToFPCallLocations(allocator_, invoke);
2475}
2476
2477void IntrinsicCodeGeneratorARMVIXL::VisitMathPow(HInvoke* invoke) {
2478 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickPow);
2479}
2480
Anton Kirilov5ec62182016-10-13 20:16:02 +01002481void IntrinsicLocationsBuilderARMVIXL::VisitMathHypot(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002482 CreateFPFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002483}
2484
2485void IntrinsicCodeGeneratorARMVIXL::VisitMathHypot(HInvoke* invoke) {
2486 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickHypot);
2487}
2488
2489void IntrinsicLocationsBuilderARMVIXL::VisitMathNextAfter(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002490 CreateFPFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002491}
2492
2493void IntrinsicCodeGeneratorARMVIXL::VisitMathNextAfter(HInvoke* invoke) {
2494 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter);
2495}
2496
2497void IntrinsicLocationsBuilderARMVIXL::VisitIntegerReverse(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002498 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002499}
2500
2501void IntrinsicCodeGeneratorARMVIXL::VisitIntegerReverse(HInvoke* invoke) {
2502 ArmVIXLAssembler* assembler = GetAssembler();
2503 __ Rbit(OutputRegister(invoke), InputRegisterAt(invoke, 0));
2504}
2505
2506void IntrinsicLocationsBuilderARMVIXL::VisitLongReverse(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002507 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002508}
2509
2510void IntrinsicCodeGeneratorARMVIXL::VisitLongReverse(HInvoke* invoke) {
2511 ArmVIXLAssembler* assembler = GetAssembler();
2512 LocationSummary* locations = invoke->GetLocations();
2513
2514 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
2515 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
2516 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
2517 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
2518
2519 __ Rbit(out_reg_lo, in_reg_hi);
2520 __ Rbit(out_reg_hi, in_reg_lo);
2521}
2522
2523void IntrinsicLocationsBuilderARMVIXL::VisitIntegerReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002524 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002525}
2526
2527void IntrinsicCodeGeneratorARMVIXL::VisitIntegerReverseBytes(HInvoke* invoke) {
2528 ArmVIXLAssembler* assembler = GetAssembler();
2529 __ Rev(OutputRegister(invoke), InputRegisterAt(invoke, 0));
2530}
2531
2532void IntrinsicLocationsBuilderARMVIXL::VisitLongReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002533 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002534}
2535
2536void IntrinsicCodeGeneratorARMVIXL::VisitLongReverseBytes(HInvoke* invoke) {
2537 ArmVIXLAssembler* assembler = GetAssembler();
2538 LocationSummary* locations = invoke->GetLocations();
2539
2540 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
2541 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
2542 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
2543 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
2544
2545 __ Rev(out_reg_lo, in_reg_hi);
2546 __ Rev(out_reg_hi, in_reg_lo);
2547}
2548
2549void IntrinsicLocationsBuilderARMVIXL::VisitShortReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002550 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002551}
2552
2553void IntrinsicCodeGeneratorARMVIXL::VisitShortReverseBytes(HInvoke* invoke) {
2554 ArmVIXLAssembler* assembler = GetAssembler();
2555 __ Revsh(OutputRegister(invoke), InputRegisterAt(invoke, 0));
2556}
2557
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002558static void GenBitCount(HInvoke* instr, DataType::Type type, ArmVIXLAssembler* assembler) {
2559 DCHECK(DataType::IsIntOrLongType(type)) << type;
2560 DCHECK_EQ(instr->GetType(), DataType::Type::kInt32);
2561 DCHECK_EQ(DataType::Kind(instr->InputAt(0)->GetType()), type);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002562
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002563 bool is_long = type == DataType::Type::kInt64;
Anton Kirilov5ec62182016-10-13 20:16:02 +01002564 LocationSummary* locations = instr->GetLocations();
2565 Location in = locations->InAt(0);
2566 vixl32::Register src_0 = is_long ? LowRegisterFrom(in) : RegisterFrom(in);
2567 vixl32::Register src_1 = is_long ? HighRegisterFrom(in) : src_0;
2568 vixl32::SRegister tmp_s = LowSRegisterFrom(locations->GetTemp(0));
2569 vixl32::DRegister tmp_d = DRegisterFrom(locations->GetTemp(0));
2570 vixl32::Register out_r = OutputRegister(instr);
2571
2572 // Move data from core register(s) to temp D-reg for bit count calculation, then move back.
2573 // According to Cortex A57 and A72 optimization guides, compared to transferring to full D-reg,
2574 // transferring data from core reg to upper or lower half of vfp D-reg requires extra latency,
2575 // That's why for integer bit count, we use 'vmov d0, r0, r0' instead of 'vmov d0[0], r0'.
2576 __ Vmov(tmp_d, src_1, src_0); // Temp DReg |--src_1|--src_0|
2577 __ Vcnt(Untyped8, tmp_d, tmp_d); // Temp DReg |c|c|c|c|c|c|c|c|
2578 __ Vpaddl(U8, tmp_d, tmp_d); // Temp DReg |--c|--c|--c|--c|
2579 __ Vpaddl(U16, tmp_d, tmp_d); // Temp DReg |------c|------c|
2580 if (is_long) {
2581 __ Vpaddl(U32, tmp_d, tmp_d); // Temp DReg |--------------c|
2582 }
2583 __ Vmov(out_r, tmp_s);
2584}
2585
2586void IntrinsicLocationsBuilderARMVIXL::VisitIntegerBitCount(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002587 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002588 invoke->GetLocations()->AddTemp(Location::RequiresFpuRegister());
2589}
2590
2591void IntrinsicCodeGeneratorARMVIXL::VisitIntegerBitCount(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002592 GenBitCount(invoke, DataType::Type::kInt32, GetAssembler());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002593}
2594
2595void IntrinsicLocationsBuilderARMVIXL::VisitLongBitCount(HInvoke* invoke) {
2596 VisitIntegerBitCount(invoke);
2597}
2598
2599void IntrinsicCodeGeneratorARMVIXL::VisitLongBitCount(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002600 GenBitCount(invoke, DataType::Type::kInt64, GetAssembler());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002601}
2602
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002603static void GenHighestOneBit(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002604 DataType::Type type,
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002605 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002606 DCHECK(DataType::IsIntOrLongType(type));
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002607
2608 ArmVIXLAssembler* assembler = codegen->GetAssembler();
2609 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2610 const vixl32::Register temp = temps.Acquire();
2611
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002612 if (type == DataType::Type::kInt64) {
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002613 LocationSummary* locations = invoke->GetLocations();
2614 Location in = locations->InAt(0);
2615 Location out = locations->Out();
2616
2617 vixl32::Register in_reg_lo = LowRegisterFrom(in);
2618 vixl32::Register in_reg_hi = HighRegisterFrom(in);
2619 vixl32::Register out_reg_lo = LowRegisterFrom(out);
2620 vixl32::Register out_reg_hi = HighRegisterFrom(out);
2621
2622 __ Mov(temp, 0x80000000); // Modified immediate.
2623 __ Clz(out_reg_lo, in_reg_lo);
2624 __ Clz(out_reg_hi, in_reg_hi);
2625 __ Lsr(out_reg_lo, temp, out_reg_lo);
2626 __ Lsrs(out_reg_hi, temp, out_reg_hi);
2627
2628 // Discard result for lowest 32 bits if highest 32 bits are not zero.
2629 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
2630 // we check that the output is in a low register, so that a 16-bit MOV
2631 // encoding can be used. If output is in a high register, then we generate
2632 // 4 more bytes of code to avoid a branch.
2633 Operand mov_src(0);
2634 if (!out_reg_lo.IsLow()) {
2635 __ Mov(LeaveFlags, temp, 0);
2636 mov_src = Operand(temp);
2637 }
2638 ExactAssemblyScope it_scope(codegen->GetVIXLAssembler(),
2639 2 * vixl32::k16BitT32InstructionSizeInBytes,
2640 CodeBufferCheckScope::kExactSize);
2641 __ it(ne);
2642 __ mov(ne, out_reg_lo, mov_src);
2643 } else {
2644 vixl32::Register out = OutputRegister(invoke);
2645 vixl32::Register in = InputRegisterAt(invoke, 0);
2646
2647 __ Mov(temp, 0x80000000); // Modified immediate.
2648 __ Clz(out, in);
2649 __ Lsr(out, temp, out);
2650 }
2651}
2652
2653void IntrinsicLocationsBuilderARMVIXL::VisitIntegerHighestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002654 CreateIntToIntLocations(allocator_, invoke);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002655}
2656
2657void IntrinsicCodeGeneratorARMVIXL::VisitIntegerHighestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002658 GenHighestOneBit(invoke, DataType::Type::kInt32, codegen_);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002659}
2660
2661void IntrinsicLocationsBuilderARMVIXL::VisitLongHighestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002662 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002663}
2664
2665void IntrinsicCodeGeneratorARMVIXL::VisitLongHighestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002666 GenHighestOneBit(invoke, DataType::Type::kInt64, codegen_);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002667}
2668
2669static void GenLowestOneBit(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002670 DataType::Type type,
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002671 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002672 DCHECK(DataType::IsIntOrLongType(type));
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002673
2674 ArmVIXLAssembler* assembler = codegen->GetAssembler();
2675 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2676 const vixl32::Register temp = temps.Acquire();
2677
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002678 if (type == DataType::Type::kInt64) {
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002679 LocationSummary* locations = invoke->GetLocations();
2680 Location in = locations->InAt(0);
2681 Location out = locations->Out();
2682
2683 vixl32::Register in_reg_lo = LowRegisterFrom(in);
2684 vixl32::Register in_reg_hi = HighRegisterFrom(in);
2685 vixl32::Register out_reg_lo = LowRegisterFrom(out);
2686 vixl32::Register out_reg_hi = HighRegisterFrom(out);
2687
2688 __ Rsb(out_reg_hi, in_reg_hi, 0);
2689 __ Rsb(out_reg_lo, in_reg_lo, 0);
2690 __ And(out_reg_hi, out_reg_hi, in_reg_hi);
2691 // The result of this operation is 0 iff in_reg_lo is 0
2692 __ Ands(out_reg_lo, out_reg_lo, in_reg_lo);
2693
2694 // Discard result for highest 32 bits if lowest 32 bits are not zero.
2695 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
2696 // we check that the output is in a low register, so that a 16-bit MOV
2697 // encoding can be used. If output is in a high register, then we generate
2698 // 4 more bytes of code to avoid a branch.
2699 Operand mov_src(0);
2700 if (!out_reg_lo.IsLow()) {
2701 __ Mov(LeaveFlags, temp, 0);
2702 mov_src = Operand(temp);
2703 }
2704 ExactAssemblyScope it_scope(codegen->GetVIXLAssembler(),
2705 2 * vixl32::k16BitT32InstructionSizeInBytes,
2706 CodeBufferCheckScope::kExactSize);
2707 __ it(ne);
2708 __ mov(ne, out_reg_hi, mov_src);
2709 } else {
2710 vixl32::Register out = OutputRegister(invoke);
2711 vixl32::Register in = InputRegisterAt(invoke, 0);
2712
2713 __ Rsb(temp, in, 0);
2714 __ And(out, temp, in);
2715 }
2716}
2717
2718void IntrinsicLocationsBuilderARMVIXL::VisitIntegerLowestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002719 CreateIntToIntLocations(allocator_, invoke);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002720}
2721
2722void IntrinsicCodeGeneratorARMVIXL::VisitIntegerLowestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002723 GenLowestOneBit(invoke, DataType::Type::kInt32, codegen_);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002724}
2725
2726void IntrinsicLocationsBuilderARMVIXL::VisitLongLowestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002727 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002728}
2729
2730void IntrinsicCodeGeneratorARMVIXL::VisitLongLowestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002731 GenLowestOneBit(invoke, DataType::Type::kInt64, codegen_);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002732}
2733
Anton Kirilov5ec62182016-10-13 20:16:02 +01002734void IntrinsicLocationsBuilderARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002735 LocationSummary* locations =
2736 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002737 locations->SetInAt(0, Location::RequiresRegister());
2738 locations->SetInAt(1, Location::RequiresRegister());
2739 locations->SetInAt(2, Location::RequiresRegister());
2740 locations->SetInAt(3, Location::RequiresRegister());
2741 locations->SetInAt(4, Location::RequiresRegister());
2742
2743 // Temporary registers to store lengths of strings and for calculations.
2744 locations->AddTemp(Location::RequiresRegister());
2745 locations->AddTemp(Location::RequiresRegister());
2746 locations->AddTemp(Location::RequiresRegister());
2747}
2748
2749void IntrinsicCodeGeneratorARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2750 ArmVIXLAssembler* assembler = GetAssembler();
2751 LocationSummary* locations = invoke->GetLocations();
2752
2753 // Check assumption that sizeof(Char) is 2 (used in scaling below).
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002754 const size_t char_size = DataType::Size(DataType::Type::kUint16);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002755 DCHECK_EQ(char_size, 2u);
2756
2757 // Location of data in char array buffer.
2758 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2759
2760 // Location of char array data in string.
2761 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2762
2763 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2764 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2765 vixl32::Register srcObj = InputRegisterAt(invoke, 0);
2766 vixl32::Register srcBegin = InputRegisterAt(invoke, 1);
2767 vixl32::Register srcEnd = InputRegisterAt(invoke, 2);
2768 vixl32::Register dstObj = InputRegisterAt(invoke, 3);
2769 vixl32::Register dstBegin = InputRegisterAt(invoke, 4);
2770
2771 vixl32::Register num_chr = RegisterFrom(locations->GetTemp(0));
2772 vixl32::Register src_ptr = RegisterFrom(locations->GetTemp(1));
2773 vixl32::Register dst_ptr = RegisterFrom(locations->GetTemp(2));
2774
2775 vixl32::Label done, compressed_string_loop;
Anton Kirilov6f644202017-02-27 18:29:45 +00002776 vixl32::Label* final_label = codegen_->GetFinalLabel(invoke, &done);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002777 // dst to be copied.
2778 __ Add(dst_ptr, dstObj, data_offset);
2779 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, vixl32::LSL, 1));
2780
2781 __ Subs(num_chr, srcEnd, srcBegin);
2782 // Early out for valid zero-length retrievals.
Anton Kirilov6f644202017-02-27 18:29:45 +00002783 __ B(eq, final_label, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002784
2785 // src range to copy.
2786 __ Add(src_ptr, srcObj, value_offset);
2787
2788 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2789 vixl32::Register temp;
2790 vixl32::Label compressed_string_preloop;
2791 if (mirror::kUseStringCompression) {
2792 // Location of count in string.
2793 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2794 temp = temps.Acquire();
2795 // String's length.
2796 __ Ldr(temp, MemOperand(srcObj, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002797 __ Tst(temp, 1);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002798 temps.Release(temp);
Artem Serov517d9f62016-12-12 15:51:15 +00002799 __ B(eq, &compressed_string_preloop, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002800 }
2801 __ Add(src_ptr, src_ptr, Operand(srcBegin, vixl32::LSL, 1));
2802
2803 // Do the copy.
2804 vixl32::Label loop, remainder;
2805
2806 temp = temps.Acquire();
2807 // Save repairing the value of num_chr on the < 4 character path.
2808 __ Subs(temp, num_chr, 4);
Artem Serov517d9f62016-12-12 15:51:15 +00002809 __ B(lt, &remainder, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002810
2811 // Keep the result of the earlier subs, we are going to fetch at least 4 characters.
2812 __ Mov(num_chr, temp);
2813
2814 // Main loop used for longer fetches loads and stores 4x16-bit characters at a time.
2815 // (LDRD/STRD fault on unaligned addresses and it's not worth inlining extra code
2816 // to rectify these everywhere this intrinsic applies.)
2817 __ Bind(&loop);
2818 __ Ldr(temp, MemOperand(src_ptr, char_size * 2));
2819 __ Subs(num_chr, num_chr, 4);
2820 __ Str(temp, MemOperand(dst_ptr, char_size * 2));
2821 __ Ldr(temp, MemOperand(src_ptr, char_size * 4, PostIndex));
2822 __ Str(temp, MemOperand(dst_ptr, char_size * 4, PostIndex));
2823 temps.Release(temp);
Artem Serov517d9f62016-12-12 15:51:15 +00002824 __ B(ge, &loop, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002825
2826 __ Adds(num_chr, num_chr, 4);
Anton Kirilov6f644202017-02-27 18:29:45 +00002827 __ B(eq, final_label, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002828
2829 // Main loop for < 4 character case and remainder handling. Loads and stores one
2830 // 16-bit Java character at a time.
2831 __ Bind(&remainder);
2832 temp = temps.Acquire();
2833 __ Ldrh(temp, MemOperand(src_ptr, char_size, PostIndex));
2834 __ Subs(num_chr, num_chr, 1);
2835 __ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex));
2836 temps.Release(temp);
Artem Serov517d9f62016-12-12 15:51:15 +00002837 __ B(gt, &remainder, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002838
2839 if (mirror::kUseStringCompression) {
Anton Kirilov6f644202017-02-27 18:29:45 +00002840 __ B(final_label);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002841
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002842 const size_t c_char_size = DataType::Size(DataType::Type::kInt8);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002843 DCHECK_EQ(c_char_size, 1u);
2844 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2845 __ Bind(&compressed_string_preloop);
2846 __ Add(src_ptr, src_ptr, srcBegin);
2847 __ Bind(&compressed_string_loop);
2848 temp = temps.Acquire();
2849 __ Ldrb(temp, MemOperand(src_ptr, c_char_size, PostIndex));
2850 __ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex));
2851 temps.Release(temp);
2852 __ Subs(num_chr, num_chr, 1);
Artem Serov517d9f62016-12-12 15:51:15 +00002853 __ B(gt, &compressed_string_loop, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002854 }
2855
Anton Kirilov6f644202017-02-27 18:29:45 +00002856 if (done.IsReferenced()) {
2857 __ Bind(&done);
2858 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01002859}
2860
2861void IntrinsicLocationsBuilderARMVIXL::VisitFloatIsInfinite(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002862 CreateFPToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002863}
2864
2865void IntrinsicCodeGeneratorARMVIXL::VisitFloatIsInfinite(HInvoke* invoke) {
2866 ArmVIXLAssembler* const assembler = GetAssembler();
2867 const vixl32::Register out = OutputRegister(invoke);
2868 // Shifting left by 1 bit makes the value encodable as an immediate operand;
2869 // we don't care about the sign bit anyway.
2870 constexpr uint32_t infinity = kPositiveInfinityFloat << 1U;
2871
2872 __ Vmov(out, InputSRegisterAt(invoke, 0));
2873 // We don't care about the sign bit, so shift left.
2874 __ Lsl(out, out, 1);
2875 __ Eor(out, out, infinity);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002876 codegen_->GenerateConditionWithZero(kCondEQ, out, out);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002877}
2878
2879void IntrinsicLocationsBuilderARMVIXL::VisitDoubleIsInfinite(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002880 CreateFPToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002881}
2882
2883void IntrinsicCodeGeneratorARMVIXL::VisitDoubleIsInfinite(HInvoke* invoke) {
2884 ArmVIXLAssembler* const assembler = GetAssembler();
2885 const vixl32::Register out = OutputRegister(invoke);
2886 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2887 const vixl32::Register temp = temps.Acquire();
2888 // The highest 32 bits of double precision positive infinity separated into
2889 // two constants encodable as immediate operands.
2890 constexpr uint32_t infinity_high = 0x7f000000U;
2891 constexpr uint32_t infinity_high2 = 0x00f00000U;
2892
2893 static_assert((infinity_high | infinity_high2) ==
2894 static_cast<uint32_t>(kPositiveInfinityDouble >> 32U),
2895 "The constants do not add up to the high 32 bits of double "
2896 "precision positive infinity.");
2897 __ Vmov(temp, out, InputDRegisterAt(invoke, 0));
2898 __ Eor(out, out, infinity_high);
2899 __ Eor(out, out, infinity_high2);
2900 // We don't care about the sign bit, so shift left.
2901 __ Orr(out, temp, Operand(out, vixl32::LSL, 1));
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002902 codegen_->GenerateConditionWithZero(kCondEQ, out, out);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002903}
2904
Artem Serov9aee2d42017-01-06 15:58:31 +00002905void IntrinsicLocationsBuilderARMVIXL::VisitMathCeil(HInvoke* invoke) {
2906 if (features_.HasARMv8AInstructions()) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002907 CreateFPToFPLocations(allocator_, invoke);
Artem Serov9aee2d42017-01-06 15:58:31 +00002908 }
2909}
2910
2911void IntrinsicCodeGeneratorARMVIXL::VisitMathCeil(HInvoke* invoke) {
2912 ArmVIXLAssembler* assembler = GetAssembler();
2913 DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
2914 __ Vrintp(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
2915}
2916
2917void IntrinsicLocationsBuilderARMVIXL::VisitMathFloor(HInvoke* invoke) {
2918 if (features_.HasARMv8AInstructions()) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002919 CreateFPToFPLocations(allocator_, invoke);
Artem Serov9aee2d42017-01-06 15:58:31 +00002920 }
2921}
2922
2923void IntrinsicCodeGeneratorARMVIXL::VisitMathFloor(HInvoke* invoke) {
2924 ArmVIXLAssembler* assembler = GetAssembler();
2925 DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
2926 __ Vrintm(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
2927}
2928
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002929void IntrinsicLocationsBuilderARMVIXL::VisitIntegerValueOf(HInvoke* invoke) {
2930 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2931 IntrinsicVisitor::ComputeIntegerValueOfLocations(
2932 invoke,
2933 codegen_,
2934 LocationFrom(r0),
2935 LocationFrom(calling_convention.GetRegisterAt(0)));
2936}
2937
2938void IntrinsicCodeGeneratorARMVIXL::VisitIntegerValueOf(HInvoke* invoke) {
Vladimir Marko6fd16062018-06-26 11:02:04 +01002939 IntrinsicVisitor::IntegerValueOfInfo info =
2940 IntrinsicVisitor::ComputeIntegerValueOfInfo(invoke, codegen_->GetCompilerOptions());
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002941 LocationSummary* locations = invoke->GetLocations();
2942 ArmVIXLAssembler* const assembler = GetAssembler();
2943
2944 vixl32::Register out = RegisterFrom(locations->Out());
2945 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2946 vixl32::Register temp = temps.Acquire();
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002947 if (invoke->InputAt(0)->IsConstant()) {
2948 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
Vladimir Marko6fd16062018-06-26 11:02:04 +01002949 if (static_cast<uint32_t>(value - info.low) < info.length) {
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002950 // Just embed the j.l.Integer in the code.
Vladimir Marko6fd16062018-06-26 11:02:04 +01002951 DCHECK_NE(info.value_boot_image_reference, IntegerValueOfInfo::kInvalidReference);
2952 codegen_->LoadBootImageAddress(out, info.value_boot_image_reference);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002953 } else {
Vladimir Markoeebb8212018-06-05 14:57:24 +01002954 DCHECK(locations->CanCall());
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002955 // Allocate and initialize a new j.l.Integer.
2956 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
2957 // JIT object table.
Vladimir Marko6fd16062018-06-26 11:02:04 +01002958 codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(),
2959 info.integer_boot_image_offset);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002960 __ Mov(temp, value);
2961 assembler->StoreToOffset(kStoreWord, temp, out, info.value_offset);
2962 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2963 // one.
2964 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2965 }
2966 } else {
Vladimir Markoeebb8212018-06-05 14:57:24 +01002967 DCHECK(locations->CanCall());
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002968 vixl32::Register in = RegisterFrom(locations->InAt(0));
2969 // Check bounds of our cache.
2970 __ Add(out, in, -info.low);
Vladimir Markoeebb8212018-06-05 14:57:24 +01002971 __ Cmp(out, info.length);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002972 vixl32::Label allocate, done;
Anton Kirilovfd522532017-05-10 12:46:57 +01002973 __ B(hs, &allocate, /* is_far_target */ false);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002974 // If the value is within the bounds, load the j.l.Integer directly from the array.
Vladimir Marko6fd16062018-06-26 11:02:04 +01002975 codegen_->LoadBootImageAddress(temp, info.array_data_boot_image_reference);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002976 codegen_->LoadFromShiftedRegOffset(DataType::Type::kReference, locations->Out(), temp, out);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002977 assembler->MaybeUnpoisonHeapReference(out);
2978 __ B(&done);
2979 __ Bind(&allocate);
2980 // Otherwise allocate and initialize a new j.l.Integer.
Vladimir Marko6fd16062018-06-26 11:02:04 +01002981 codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(),
2982 info.integer_boot_image_offset);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002983 assembler->StoreToOffset(kStoreWord, in, out, info.value_offset);
2984 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2985 // one.
2986 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2987 __ Bind(&done);
2988 }
2989}
2990
Nicolas Geoffray365719c2017-03-08 13:11:50 +00002991void IntrinsicLocationsBuilderARMVIXL::VisitThreadInterrupted(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002992 LocationSummary* locations =
2993 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Nicolas Geoffray365719c2017-03-08 13:11:50 +00002994 locations->SetOut(Location::RequiresRegister());
2995}
2996
2997void IntrinsicCodeGeneratorARMVIXL::VisitThreadInterrupted(HInvoke* invoke) {
2998 ArmVIXLAssembler* assembler = GetAssembler();
2999 vixl32::Register out = RegisterFrom(invoke->GetLocations()->Out());
3000 int32_t offset = Thread::InterruptedOffset<kArmPointerSize>().Int32Value();
3001 __ Ldr(out, MemOperand(tr, offset));
3002 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
3003 vixl32::Register temp = temps.Acquire();
3004 vixl32::Label done;
Anton Kirilovfd522532017-05-10 12:46:57 +01003005 vixl32::Label* const final_label = codegen_->GetFinalLabel(invoke, &done);
3006 __ CompareAndBranchIfZero(out, final_label, /* far_target */ false);
Nicolas Geoffray365719c2017-03-08 13:11:50 +00003007 __ Dmb(vixl32::ISH);
3008 __ Mov(temp, 0);
3009 assembler->StoreToOffset(kStoreWord, temp, tr, offset);
3010 __ Dmb(vixl32::ISH);
Anton Kirilovfd522532017-05-10 12:46:57 +01003011 if (done.IsReferenced()) {
3012 __ Bind(&done);
3013 }
Nicolas Geoffray365719c2017-03-08 13:11:50 +00003014}
3015
Hans Boehmc7b28de2018-03-09 17:05:28 -08003016void IntrinsicLocationsBuilderARMVIXL::VisitReachabilityFence(HInvoke* invoke) {
3017 LocationSummary* locations =
3018 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
3019 locations->SetInAt(0, Location::Any());
3020}
3021
3022void IntrinsicCodeGeneratorARMVIXL::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
3023
Anton Kirilov5ec62182016-10-13 20:16:02 +01003024UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundDouble) // Could be done by changing rounding mode, maybe?
Anton Kirilov5ec62182016-10-13 20:16:02 +01003025UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong) // High register pressure.
3026UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
Vladimir Markod254f5c2017-06-02 15:18:36 +00003027UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent)
Anton Kirilov5ec62182016-10-13 20:16:02 +01003028
Aart Bikff7d89c2016-11-07 08:49:28 -08003029UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf);
3030UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08003031UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBufferAppend);
3032UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBufferLength);
3033UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBufferToString);
3034UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBuilderAppend);
3035UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBuilderLength);
3036UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08003037
Anton Kirilov5ec62182016-10-13 20:16:02 +01003038// 1.8.
3039UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndAddInt)
3040UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndAddLong)
3041UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndSetInt)
3042UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndSetLong)
3043UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndSetObject)
3044
3045UNREACHABLE_INTRINSICS(ARMVIXL)
3046
3047#undef __
3048
3049} // namespace arm
3050} // namespace art