blob: f0a418454df1ef1213d0e0753d475a615e6a14d0 [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
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010088 void EmitNativeCode(CodeGenerator* codegen) override {
Anton Kirilov5ec62182016-10-13 20:16:02 +010089 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
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100114 const char* GetDescription() const override { return "IntrinsicSlowPath"; }
Anton Kirilov5ec62182016-10-13 20:16:02 +0100115
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
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100176 void EmitNativeCode(CodeGenerator* codegen) override {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100177 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
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100236 const char* GetDescription() const override {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100237 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);
Vladimir Marko248141f2018-08-10 10:40:07 +0100641 // Piggy-back on the field load path using introspection for the Baker read barrier.
642 __ Add(RegisterFrom(temp), base, Operand(offset));
643 MemOperand src(RegisterFrom(temp), 0);
644 codegen->GenerateFieldLoadWithBakerReadBarrier(
645 invoke, trg_loc, base, src, /* needs_null_check */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100646 if (is_volatile) {
647 __ Dmb(vixl32::ISH);
648 }
649 } else {
650 __ Ldr(trg, MemOperand(base, offset));
651 if (is_volatile) {
652 __ Dmb(vixl32::ISH);
653 }
654 codegen->GenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
655 }
656 } else {
657 __ Ldr(trg, MemOperand(base, offset));
658 if (is_volatile) {
659 __ Dmb(vixl32::ISH);
660 }
661 assembler->MaybeUnpoisonHeapReference(trg);
662 }
663 break;
664 }
665
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100666 case DataType::Type::kInt64: {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100667 vixl32::Register trg_lo = LowRegisterFrom(trg_loc);
668 vixl32::Register trg_hi = HighRegisterFrom(trg_loc);
669 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
Artem Serov657022c2016-11-23 14:19:38 +0000670 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
671 const vixl32::Register temp_reg = temps.Acquire();
672 __ Add(temp_reg, base, offset);
673 __ Ldrexd(trg_lo, trg_hi, MemOperand(temp_reg));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100674 } else {
675 __ Ldrd(trg_lo, trg_hi, MemOperand(base, offset));
676 }
677 if (is_volatile) {
678 __ Dmb(vixl32::ISH);
679 }
680 break;
681 }
682
683 default:
684 LOG(FATAL) << "Unexpected type " << type;
685 UNREACHABLE();
686 }
687}
688
Vladimir Markoca6fff82017-10-03 14:49:14 +0100689static void CreateIntIntIntToIntLocations(ArenaAllocator* allocator,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100690 HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100691 DataType::Type type) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100692 bool can_call = kEmitCompilerReadBarrier &&
693 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
694 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Vladimir Markoca6fff82017-10-03 14:49:14 +0100695 LocationSummary* locations =
696 new (allocator) LocationSummary(invoke,
697 can_call
698 ? LocationSummary::kCallOnSlowPath
699 : LocationSummary::kNoCall,
700 kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100701 if (can_call && kUseBakerReadBarrier) {
702 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
703 }
704 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
705 locations->SetInAt(1, Location::RequiresRegister());
706 locations->SetInAt(2, Location::RequiresRegister());
707 locations->SetOut(Location::RequiresRegister(),
708 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100709 if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100710 // We need a temporary register for the read barrier marking slow
Roland Levillain9983e302017-07-14 14:34:22 +0100711 // path in CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier.
Anton Kirilov5ec62182016-10-13 20:16:02 +0100712 locations->AddTemp(Location::RequiresRegister());
713 }
714}
715
716void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGet(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::VisitUnsafeGetVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100720 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt32);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100721}
722void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetLong(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::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100726 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt64);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100727}
728void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetObject(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100729 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kReference);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100730}
731void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100732 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kReference);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100733}
734
735void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGet(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100736 GenUnsafeGet(invoke, DataType::Type::kInt32, /* is_volatile */ false, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100737}
738void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100739 GenUnsafeGet(invoke, DataType::Type::kInt32, /* is_volatile */ true, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100740}
741void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetLong(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100742 GenUnsafeGet(invoke, DataType::Type::kInt64, /* is_volatile */ false, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100743}
744void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100745 GenUnsafeGet(invoke, DataType::Type::kInt64, /* is_volatile */ true, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100746}
747void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetObject(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100748 GenUnsafeGet(invoke, DataType::Type::kReference, /* is_volatile */ false, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100749}
750void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100751 GenUnsafeGet(invoke, DataType::Type::kReference, /* is_volatile */ true, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100752}
753
Vladimir Markoca6fff82017-10-03 14:49:14 +0100754static void CreateIntIntIntIntToVoid(ArenaAllocator* allocator,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100755 const ArmInstructionSetFeatures& features,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100756 DataType::Type type,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100757 bool is_volatile,
758 HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100759 LocationSummary* locations =
760 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100761 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
762 locations->SetInAt(1, Location::RequiresRegister());
763 locations->SetInAt(2, Location::RequiresRegister());
764 locations->SetInAt(3, Location::RequiresRegister());
765
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100766 if (type == DataType::Type::kInt64) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100767 // Potentially need temps for ldrexd-strexd loop.
768 if (is_volatile && !features.HasAtomicLdrdAndStrd()) {
769 locations->AddTemp(Location::RequiresRegister()); // Temp_lo.
770 locations->AddTemp(Location::RequiresRegister()); // Temp_hi.
771 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100772 } else if (type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100773 // Temps for card-marking.
774 locations->AddTemp(Location::RequiresRegister()); // Temp.
775 locations->AddTemp(Location::RequiresRegister()); // Card.
776 }
777}
778
779void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePut(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100780 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100781 allocator_, features_, DataType::Type::kInt32, /* is_volatile */ false, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100782}
783void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutOrdered(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100784 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100785 allocator_, features_, DataType::Type::kInt32, /* is_volatile */ false, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100786}
787void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100788 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100789 allocator_, features_, DataType::Type::kInt32, /* is_volatile */ true, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100790}
791void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutObject(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100792 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100793 allocator_, features_, DataType::Type::kReference, /* is_volatile */ false, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100794}
795void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100796 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100797 allocator_, features_, DataType::Type::kReference, /* is_volatile */ false, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100798}
799void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100800 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100801 allocator_, features_, DataType::Type::kReference, /* is_volatile */ true, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100802}
803void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutLong(HInvoke* invoke) {
804 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100805 allocator_, features_, DataType::Type::kInt64, /* is_volatile */ false, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100806}
807void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutLongOrdered(HInvoke* invoke) {
808 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100809 allocator_, features_, DataType::Type::kInt64, /* is_volatile */ false, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100810}
811void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutLongVolatile(HInvoke* invoke) {
812 CreateIntIntIntIntToVoid(
Vladimir Markoca6fff82017-10-03 14:49:14 +0100813 allocator_, features_, DataType::Type::kInt64, /* is_volatile */ true, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100814}
815
816static void GenUnsafePut(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100817 DataType::Type type,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100818 bool is_volatile,
819 bool is_ordered,
820 CodeGeneratorARMVIXL* codegen) {
821 ArmVIXLAssembler* assembler = codegen->GetAssembler();
822
823 vixl32::Register base = RegisterFrom(locations->InAt(1)); // Object pointer.
824 vixl32::Register offset = LowRegisterFrom(locations->InAt(2)); // Long offset, lo part only.
825 vixl32::Register value;
826
827 if (is_volatile || is_ordered) {
828 __ Dmb(vixl32::ISH);
829 }
830
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100831 if (type == DataType::Type::kInt64) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100832 vixl32::Register value_lo = LowRegisterFrom(locations->InAt(3));
833 vixl32::Register value_hi = HighRegisterFrom(locations->InAt(3));
834 value = value_lo;
835 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
836 vixl32::Register temp_lo = RegisterFrom(locations->GetTemp(0));
837 vixl32::Register temp_hi = RegisterFrom(locations->GetTemp(1));
838 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
839 const vixl32::Register temp_reg = temps.Acquire();
840
841 __ Add(temp_reg, base, offset);
842 vixl32::Label loop_head;
843 __ Bind(&loop_head);
Scott Wakelingb77051e2016-11-21 19:46:00 +0000844 __ Ldrexd(temp_lo, temp_hi, MemOperand(temp_reg));
845 __ Strexd(temp_lo, value_lo, value_hi, MemOperand(temp_reg));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100846 __ Cmp(temp_lo, 0);
Artem Serov517d9f62016-12-12 15:51:15 +0000847 __ B(ne, &loop_head, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100848 } else {
849 __ Strd(value_lo, value_hi, MemOperand(base, offset));
850 }
851 } else {
852 value = RegisterFrom(locations->InAt(3));
853 vixl32::Register source = value;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100854 if (kPoisonHeapReferences && type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100855 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
856 __ Mov(temp, value);
857 assembler->PoisonHeapReference(temp);
858 source = temp;
859 }
860 __ Str(source, MemOperand(base, offset));
861 }
862
863 if (is_volatile) {
864 __ Dmb(vixl32::ISH);
865 }
866
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100867 if (type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100868 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
869 vixl32::Register card = RegisterFrom(locations->GetTemp(1));
870 bool value_can_be_null = true; // TODO: Worth finding out this information?
871 codegen->MarkGCCard(temp, card, base, value, value_can_be_null);
872 }
873}
874
875void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePut(HInvoke* invoke) {
876 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100877 DataType::Type::kInt32,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100878 /* is_volatile */ false,
879 /* is_ordered */ false,
880 codegen_);
881}
882void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutOrdered(HInvoke* invoke) {
883 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100884 DataType::Type::kInt32,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100885 /* is_volatile */ false,
886 /* is_ordered */ true,
887 codegen_);
888}
889void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutVolatile(HInvoke* invoke) {
890 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100891 DataType::Type::kInt32,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100892 /* is_volatile */ true,
893 /* is_ordered */ false,
894 codegen_);
895}
896void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutObject(HInvoke* invoke) {
897 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100898 DataType::Type::kReference,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100899 /* is_volatile */ false,
900 /* is_ordered */ false,
901 codegen_);
902}
903void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
904 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100905 DataType::Type::kReference,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100906 /* is_volatile */ false,
907 /* is_ordered */ true,
908 codegen_);
909}
910void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
911 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100912 DataType::Type::kReference,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100913 /* is_volatile */ true,
914 /* is_ordered */ false,
915 codegen_);
916}
917void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutLong(HInvoke* invoke) {
918 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100919 DataType::Type::kInt64,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100920 /* is_volatile */ false,
921 /* is_ordered */ false,
922 codegen_);
923}
924void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutLongOrdered(HInvoke* invoke) {
925 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100926 DataType::Type::kInt64,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100927 /* is_volatile */ false,
928 /* is_ordered */ true,
929 codegen_);
930}
931void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutLongVolatile(HInvoke* invoke) {
932 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100933 DataType::Type::kInt64,
Anton Kirilov5ec62182016-10-13 20:16:02 +0100934 /* is_volatile */ true,
935 /* is_ordered */ false,
936 codegen_);
937}
938
Vladimir Markod887ed82018-08-14 13:52:12 +0000939static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* allocator, HInvoke* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +0100940 bool can_call = kEmitCompilerReadBarrier &&
941 kUseBakerReadBarrier &&
942 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
Vladimir Markoca6fff82017-10-03 14:49:14 +0100943 LocationSummary* locations =
944 new (allocator) LocationSummary(invoke,
945 can_call
946 ? LocationSummary::kCallOnSlowPath
947 : LocationSummary::kNoCall,
948 kIntrinsified);
Vladimir Markod887ed82018-08-14 13:52:12 +0000949 if (can_call) {
950 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
951 }
Anton Kirilov5ec62182016-10-13 20:16:02 +0100952 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
953 locations->SetInAt(1, Location::RequiresRegister());
954 locations->SetInAt(2, Location::RequiresRegister());
955 locations->SetInAt(3, Location::RequiresRegister());
956 locations->SetInAt(4, Location::RequiresRegister());
957
Vladimir Markod887ed82018-08-14 13:52:12 +0000958 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100959
960 // Temporary registers used in CAS. In the object case
961 // (UnsafeCASObject intrinsic), these are also used for
962 // card-marking, and possibly for (Baker) read barrier.
963 locations->AddTemp(Location::RequiresRegister()); // Pointer.
964 locations->AddTemp(Location::RequiresRegister()); // Temp 1.
965}
966
Vladimir Markod887ed82018-08-14 13:52:12 +0000967class BakerReadBarrierCasSlowPathARMVIXL : public SlowPathCodeARMVIXL {
968 public:
969 explicit BakerReadBarrierCasSlowPathARMVIXL(HInvoke* invoke)
970 : SlowPathCodeARMVIXL(invoke) {}
971
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100972 const char* GetDescription() const override { return "BakerReadBarrierCasSlowPathARMVIXL"; }
Vladimir Markod887ed82018-08-14 13:52:12 +0000973
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100974 void EmitNativeCode(CodeGenerator* codegen) override {
Vladimir Markod887ed82018-08-14 13:52:12 +0000975 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
976 ArmVIXLAssembler* assembler = arm_codegen->GetAssembler();
977 __ Bind(GetEntryLabel());
978
979 LocationSummary* locations = instruction_->GetLocations();
980 vixl32::Register base = InputRegisterAt(instruction_, 1); // Object pointer.
981 vixl32::Register offset = LowRegisterFrom(locations->InAt(2)); // Offset (discard high 4B).
982 vixl32::Register expected = InputRegisterAt(instruction_, 3); // Expected.
983 vixl32::Register value = InputRegisterAt(instruction_, 4); // Value.
984
985 vixl32::Register tmp_ptr = RegisterFrom(locations->GetTemp(0)); // Pointer to actual memory.
986 vixl32::Register tmp = RegisterFrom(locations->GetTemp(1)); // Temporary.
987
988 // The `tmp` is initialized to `[tmp_ptr] - expected` in the main path. Reconstruct
989 // and mark the old value and compare with `expected`. We clobber `tmp_ptr` in the
990 // process due to lack of other temps suitable for the read barrier.
991 arm_codegen->GenerateUnsafeCasOldValueAddWithBakerReadBarrier(tmp_ptr, tmp, expected);
992 __ Cmp(tmp_ptr, expected);
993 __ B(ne, GetExitLabel());
994
995 // The old value we have read did not match `expected` (which is always a to-space reference)
996 // but after the read barrier in GenerateUnsafeCasOldValueAddWithBakerReadBarrier() the marked
997 // to-space value matched, so the old value must be a from-space reference to the same object.
998 // Do the same CAS loop as the main path but check for both `expected` and the unmarked
999 // old value representing the to-space and from-space references for the same object.
1000
1001 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
1002 vixl32::Register adjusted_old_value = temps.Acquire(); // For saved `tmp` from main path.
1003
1004 // Recalculate the `tmp_ptr` clobbered above and store the `adjusted_old_value`, i.e. IP.
1005 __ Add(tmp_ptr, base, offset);
1006 __ Mov(adjusted_old_value, tmp);
1007
1008 // do {
1009 // tmp = [r_ptr] - expected;
1010 // } while ((tmp == 0 || tmp == adjusted_old_value) && failure([r_ptr] <- r_new_value));
1011 // result = (tmp == 0 || tmp == adjusted_old_value);
1012
1013 vixl32::Label loop_head;
1014 __ Bind(&loop_head);
1015 __ Ldrex(tmp, MemOperand(tmp_ptr)); // This can now load null stored by another thread.
1016 assembler->MaybeUnpoisonHeapReference(tmp);
1017 __ Subs(tmp, tmp, expected); // Use SUBS to get non-zero value if both compares fail.
1018 {
1019 // If the newly loaded value did not match `expected`, compare with `adjusted_old_value`.
1020 ExactAssemblyScope aas(assembler->GetVIXLAssembler(), 2 * k16BitT32InstructionSizeInBytes);
1021 __ it(ne);
1022 __ cmp(ne, tmp, adjusted_old_value);
1023 }
1024 __ B(ne, GetExitLabel());
1025 assembler->MaybePoisonHeapReference(value);
1026 __ Strex(tmp, value, MemOperand(tmp_ptr));
1027 assembler->MaybeUnpoisonHeapReference(value);
1028 __ Cmp(tmp, 0);
1029 __ B(ne, &loop_head, /* far_target */ false);
1030 __ B(GetExitLabel());
1031 }
1032};
1033
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001034static void GenCas(HInvoke* invoke, DataType::Type type, CodeGeneratorARMVIXL* codegen) {
1035 DCHECK_NE(type, DataType::Type::kInt64);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001036
1037 ArmVIXLAssembler* assembler = codegen->GetAssembler();
1038 LocationSummary* locations = invoke->GetLocations();
1039
Anton Kirilov5ec62182016-10-13 20:16:02 +01001040 vixl32::Register out = OutputRegister(invoke); // Boolean result.
1041
1042 vixl32::Register base = InputRegisterAt(invoke, 1); // Object pointer.
Vladimir Markod887ed82018-08-14 13:52:12 +00001043 vixl32::Register offset = LowRegisterFrom(locations->InAt(2)); // Offset (discard high 4B).
Anton Kirilov5ec62182016-10-13 20:16:02 +01001044 vixl32::Register expected = InputRegisterAt(invoke, 3); // Expected.
1045 vixl32::Register value = InputRegisterAt(invoke, 4); // Value.
1046
Vladimir Markod887ed82018-08-14 13:52:12 +00001047 vixl32::Register tmp_ptr = RegisterFrom(locations->GetTemp(0)); // Pointer to actual memory.
1048 vixl32::Register tmp = RegisterFrom(locations->GetTemp(1)); // Temporary.
1049
1050 vixl32::Label loop_exit_label;
1051 vixl32::Label* loop_exit = &loop_exit_label;
1052 vixl32::Label* failure = &loop_exit_label;
Anton Kirilov5ec62182016-10-13 20:16:02 +01001053
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001054 if (type == DataType::Type::kReference) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01001055 // The only read barrier implementation supporting the
1056 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1057 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1058
1059 // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
1060 // object and scan the receiver at the next GC for nothing.
1061 bool value_can_be_null = true; // TODO: Worth finding out this information?
1062 codegen->MarkGCCard(tmp_ptr, tmp, base, value, value_can_be_null);
1063
1064 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Vladimir Markod887ed82018-08-14 13:52:12 +00001065 // If marking, check if the stored reference is a from-space reference to the same
1066 // object as the to-space reference `expected`. If so, perform a custom CAS loop.
1067 BakerReadBarrierCasSlowPathARMVIXL* slow_path =
1068 new (codegen->GetScopedAllocator()) BakerReadBarrierCasSlowPathARMVIXL(invoke);
1069 codegen->AddSlowPath(slow_path);
1070 failure = slow_path->GetEntryLabel();
1071 loop_exit = slow_path->GetExitLabel();
Anton Kirilov5ec62182016-10-13 20:16:02 +01001072 }
1073 }
1074
1075 // Prevent reordering with prior memory operations.
1076 // Emit a DMB ISH instruction instead of an DMB ISHST one, as the
Vladimir Markod887ed82018-08-14 13:52:12 +00001077 // latter allows a preceding load to be delayed past the STREX
Anton Kirilov5ec62182016-10-13 20:16:02 +01001078 // instruction below.
1079 __ Dmb(vixl32::ISH);
1080
1081 __ Add(tmp_ptr, base, offset);
1082
Anton Kirilov5ec62182016-10-13 20:16:02 +01001083 // do {
1084 // tmp = [r_ptr] - expected;
1085 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
Vladimir Markod887ed82018-08-14 13:52:12 +00001086 // result = tmp == 0;
Anton Kirilov5ec62182016-10-13 20:16:02 +01001087
1088 vixl32::Label loop_head;
1089 __ Bind(&loop_head);
Vladimir Markof28be432018-08-14 12:20:51 +00001090 __ Ldrex(tmp, MemOperand(tmp_ptr));
Vladimir Markod887ed82018-08-14 13:52:12 +00001091 if (type == DataType::Type::kReference) {
1092 assembler->MaybeUnpoisonHeapReference(tmp);
Vladimir Markof28be432018-08-14 12:20:51 +00001093 }
Vladimir Markod887ed82018-08-14 13:52:12 +00001094 __ Subs(tmp, tmp, expected);
1095 __ B(ne, failure, (failure == loop_exit) ? kNear : kBranchWithoutHint);
1096 if (type == DataType::Type::kReference) {
1097 assembler->MaybePoisonHeapReference(value);
1098 }
1099 __ Strex(tmp, value, MemOperand(tmp_ptr));
1100 if (type == DataType::Type::kReference) {
1101 assembler->MaybeUnpoisonHeapReference(value);
1102 }
1103 __ Cmp(tmp, 0);
1104 __ B(ne, &loop_head, /* far_target */ false);
Vladimir Markof28be432018-08-14 12:20:51 +00001105
Vladimir Markod887ed82018-08-14 13:52:12 +00001106 __ Bind(loop_exit);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001107
1108 __ Dmb(vixl32::ISH);
1109
Vladimir Markod887ed82018-08-14 13:52:12 +00001110 // out = tmp == 0.
1111 __ Clz(out, tmp);
1112 __ Lsr(out, out, WhichPowerOf2(out.GetSizeInBits()));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001113
Vladimir Markod887ed82018-08-14 13:52:12 +00001114 if (type == DataType::Type::kReference) {
1115 codegen->MaybeGenerateMarkingRegisterCheck(/* code */ 128);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001116 }
1117}
1118
1119void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeCASInt(HInvoke* invoke) {
Vladimir Markod887ed82018-08-14 13:52:12 +00001120 CreateIntIntIntIntIntToIntPlusTemps(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001121}
1122void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeCASObject(HInvoke* invoke) {
1123 // The only read barrier implementation supporting the
1124 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1125 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
1126 return;
1127 }
1128
Vladimir Markod887ed82018-08-14 13:52:12 +00001129 CreateIntIntIntIntIntToIntPlusTemps(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001130}
1131void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeCASInt(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001132 GenCas(invoke, DataType::Type::kInt32, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001133}
1134void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeCASObject(HInvoke* invoke) {
1135 // The only read barrier implementation supporting the
1136 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1137 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1138
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001139 GenCas(invoke, DataType::Type::kReference, codegen_);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001140}
1141
1142void IntrinsicLocationsBuilderARMVIXL::VisitStringCompareTo(HInvoke* invoke) {
1143 // The inputs plus one temp.
Vladimir Markoca6fff82017-10-03 14:49:14 +01001144 LocationSummary* locations =
1145 new (allocator_) LocationSummary(invoke,
1146 invoke->InputAt(1)->CanBeNull()
1147 ? LocationSummary::kCallOnSlowPath
1148 : LocationSummary::kNoCall,
1149 kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001150 locations->SetInAt(0, Location::RequiresRegister());
1151 locations->SetInAt(1, Location::RequiresRegister());
1152 locations->AddTemp(Location::RequiresRegister());
1153 locations->AddTemp(Location::RequiresRegister());
1154 locations->AddTemp(Location::RequiresRegister());
1155 // Need temporary registers for String compression's feature.
1156 if (mirror::kUseStringCompression) {
1157 locations->AddTemp(Location::RequiresRegister());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001158 }
1159 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1160}
1161
Artem Serov672b9c12017-12-05 18:04:07 +00001162// Forward declaration.
1163//
1164// ART build system imposes a size limit (deviceFrameSizeLimit) on the stack frames generated
1165// by the compiler for every C++ function, and if this function gets inlined in
1166// IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo, the limit will be exceeded, resulting in a
1167// build failure. That is the reason why NO_INLINE attribute is used.
1168static void NO_INLINE GenerateStringCompareToLoop(ArmVIXLAssembler* assembler,
1169 HInvoke* invoke,
1170 vixl32::Label* end,
1171 vixl32::Label* different_compression);
1172
Anton Kirilov5ec62182016-10-13 20:16:02 +01001173void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) {
1174 ArmVIXLAssembler* assembler = GetAssembler();
1175 LocationSummary* locations = invoke->GetLocations();
1176
Artem Serov672b9c12017-12-05 18:04:07 +00001177 const vixl32::Register str = InputRegisterAt(invoke, 0);
1178 const vixl32::Register arg = InputRegisterAt(invoke, 1);
1179 const vixl32::Register out = OutputRegister(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001180
Artem Serov672b9c12017-12-05 18:04:07 +00001181 const vixl32::Register temp0 = RegisterFrom(locations->GetTemp(0));
1182 const vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1183 const vixl32::Register temp2 = RegisterFrom(locations->GetTemp(2));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001184 vixl32::Register temp3;
Anton Kirilov5ec62182016-10-13 20:16:02 +01001185 if (mirror::kUseStringCompression) {
1186 temp3 = RegisterFrom(locations->GetTemp(3));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001187 }
1188
Anton Kirilov5ec62182016-10-13 20:16:02 +01001189 vixl32::Label end;
1190 vixl32::Label different_compression;
1191
1192 // Get offsets of count and value fields within a string object.
1193 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
Anton Kirilov5ec62182016-10-13 20:16:02 +01001194
1195 // Note that the null check must have been done earlier.
1196 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1197
1198 // Take slow path and throw if input can be and is null.
1199 SlowPathCodeARMVIXL* slow_path = nullptr;
1200 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1201 if (can_slow_path) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001202 slow_path = new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001203 codegen_->AddSlowPath(slow_path);
xueliang.zhongf51bc622016-11-04 09:23:32 +00001204 __ CompareAndBranchIfZero(arg, slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001205 }
1206
1207 // Reference equality check, return 0 if same reference.
1208 __ Subs(out, str, arg);
1209 __ B(eq, &end);
1210
Anton Kirilov5ec62182016-10-13 20:16:02 +01001211 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001212 // Load `count` fields of this and argument strings.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001213 __ Ldr(temp3, MemOperand(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001214 __ Ldr(temp2, MemOperand(arg, count_offset));
1215 // Extract lengths from the `count` fields.
1216 __ Lsr(temp0, temp3, 1u);
1217 __ Lsr(temp1, temp2, 1u);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001218 } else {
1219 // Load lengths of this and argument strings.
1220 __ Ldr(temp0, MemOperand(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001221 __ Ldr(temp1, MemOperand(arg, count_offset));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001222 }
1223 // out = length diff.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001224 __ Subs(out, temp0, temp1);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001225 // temp0 = min(len(str), len(arg)).
1226
1227 {
Artem Serov0fb37192016-12-06 18:13:40 +00001228 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1229 2 * kMaxInstructionSizeInBytes,
1230 CodeBufferCheckScope::kMaximumSize);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001231
1232 __ it(gt);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001233 __ mov(gt, temp0, temp1);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001234 }
1235
Anton Kirilov5ec62182016-10-13 20:16:02 +01001236 // Shorter string is empty?
xueliang.zhongf51bc622016-11-04 09:23:32 +00001237 // Note that mirror::kUseStringCompression==true introduces lots of instructions,
1238 // which makes &end label far away from this branch and makes it not 'CBZ-encodable'.
1239 __ CompareAndBranchIfZero(temp0, &end, mirror::kUseStringCompression);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001240
1241 if (mirror::kUseStringCompression) {
1242 // Check if both strings using same compression style to use this comparison loop.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001243 __ Eors(temp2, temp2, temp3);
1244 __ Lsrs(temp2, temp2, 1u);
1245 __ B(cs, &different_compression);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001246 // For string compression, calculate the number of bytes to compare (not chars).
1247 // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001248 __ Lsls(temp3, temp3, 31u); // Extract purely the compression flag.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001249
Artem Serov0fb37192016-12-06 18:13:40 +00001250 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1251 2 * kMaxInstructionSizeInBytes,
1252 CodeBufferCheckScope::kMaximumSize);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001253
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001254 __ it(ne);
1255 __ add(ne, temp0, temp0, temp0);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001256 }
1257
Artem Serov672b9c12017-12-05 18:04:07 +00001258
1259 GenerateStringCompareToLoop(assembler, invoke, &end, &different_compression);
1260
1261 __ Bind(&end);
1262
1263 if (can_slow_path) {
1264 __ Bind(slow_path->GetExitLabel());
1265 }
1266}
1267
1268static void GenerateStringCompareToLoop(ArmVIXLAssembler* assembler,
1269 HInvoke* invoke,
1270 vixl32::Label* end,
1271 vixl32::Label* different_compression) {
1272 LocationSummary* locations = invoke->GetLocations();
1273
1274 const vixl32::Register str = InputRegisterAt(invoke, 0);
1275 const vixl32::Register arg = InputRegisterAt(invoke, 1);
1276 const vixl32::Register out = OutputRegister(invoke);
1277
1278 const vixl32::Register temp0 = RegisterFrom(locations->GetTemp(0));
1279 const vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1280 const vixl32::Register temp2 = RegisterFrom(locations->GetTemp(2));
1281 vixl32::Register temp3;
1282 if (mirror::kUseStringCompression) {
1283 temp3 = RegisterFrom(locations->GetTemp(3));
1284 }
1285
1286 vixl32::Label loop;
1287 vixl32::Label find_char_diff;
1288
1289 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001290 // Store offset of string value in preparation for comparison loop.
1291 __ Mov(temp1, value_offset);
1292
Anton Kirilov5ec62182016-10-13 20:16:02 +01001293 // Assertions that must hold in order to compare multiple characters at a time.
1294 CHECK_ALIGNED(value_offset, 8);
1295 static_assert(IsAligned<8>(kObjectAlignment),
1296 "String data must be 8-byte aligned for unrolled CompareTo loop.");
1297
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001298 const unsigned char_size = DataType::Size(DataType::Type::kUint16);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001299 DCHECK_EQ(char_size, 2u);
1300
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001301 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
1302
Anton Kirilov5ec62182016-10-13 20:16:02 +01001303 vixl32::Label find_char_diff_2nd_cmp;
1304 // Unrolled loop comparing 4x16-bit chars per iteration (ok because of string data alignment).
1305 __ Bind(&loop);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001306 vixl32::Register temp_reg = temps.Acquire();
Anton Kirilov5ec62182016-10-13 20:16:02 +01001307 __ Ldr(temp_reg, MemOperand(str, temp1));
1308 __ Ldr(temp2, MemOperand(arg, temp1));
1309 __ Cmp(temp_reg, temp2);
Artem Serov517d9f62016-12-12 15:51:15 +00001310 __ B(ne, &find_char_diff, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001311 __ Add(temp1, temp1, char_size * 2);
1312
1313 __ Ldr(temp_reg, MemOperand(str, temp1));
1314 __ Ldr(temp2, MemOperand(arg, temp1));
1315 __ Cmp(temp_reg, temp2);
Artem Serov517d9f62016-12-12 15:51:15 +00001316 __ B(ne, &find_char_diff_2nd_cmp, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001317 __ Add(temp1, temp1, char_size * 2);
1318 // With string compression, we have compared 8 bytes, otherwise 4 chars.
1319 __ Subs(temp0, temp0, (mirror::kUseStringCompression ? 8 : 4));
Artem Serov517d9f62016-12-12 15:51:15 +00001320 __ B(hi, &loop, /* far_target */ false);
Artem Serov672b9c12017-12-05 18:04:07 +00001321 __ B(end);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001322
1323 __ Bind(&find_char_diff_2nd_cmp);
1324 if (mirror::kUseStringCompression) {
1325 __ Subs(temp0, temp0, 4); // 4 bytes previously compared.
Artem Serov672b9c12017-12-05 18:04:07 +00001326 __ B(ls, end, /* far_target */ false); // Was the second comparison fully beyond the end?
Anton Kirilov5ec62182016-10-13 20:16:02 +01001327 } else {
1328 // Without string compression, we can start treating temp0 as signed
1329 // and rely on the signed comparison below.
1330 __ Sub(temp0, temp0, 2);
1331 }
1332
1333 // Find the single character difference.
1334 __ Bind(&find_char_diff);
1335 // Get the bit position of the first character that differs.
1336 __ Eor(temp1, temp2, temp_reg);
1337 __ Rbit(temp1, temp1);
1338 __ Clz(temp1, temp1);
1339
1340 // temp0 = number of characters remaining to compare.
1341 // (Without string compression, it could be < 1 if a difference is found by the second CMP
1342 // in the comparison loop, and after the end of the shorter string data).
1343
1344 // Without string compression (temp1 >> 4) = character where difference occurs between the last
1345 // two words compared, in the interval [0,1].
1346 // (0 for low half-word different, 1 for high half-word different).
1347 // With string compression, (temp1 << 3) = byte where the difference occurs,
1348 // in the interval [0,3].
1349
1350 // If temp0 <= (temp1 >> (kUseStringCompression ? 3 : 4)), the difference occurs outside
1351 // the remaining string data, so just return length diff (out).
1352 // The comparison is unsigned for string compression, otherwise signed.
1353 __ Cmp(temp0, Operand(temp1, vixl32::LSR, (mirror::kUseStringCompression ? 3 : 4)));
Artem Serov672b9c12017-12-05 18:04:07 +00001354 __ B((mirror::kUseStringCompression ? ls : le), end, /* far_target */ false);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001355
Anton Kirilov5ec62182016-10-13 20:16:02 +01001356 // Extract the characters and calculate the difference.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001357 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001358 // For compressed strings we need to clear 0x7 from temp1, for uncompressed we need to clear
1359 // 0xf. We also need to prepare the character extraction mask `uncompressed ? 0xffffu : 0xffu`.
1360 // The compression flag is now in the highest bit of temp3, so let's play some tricks.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001361 __ Orr(temp3, temp3, 0xffu << 23); // uncompressed ? 0xff800000u : 0x7ff80000u
1362 __ Bic(temp1, temp1, Operand(temp3, vixl32::LSR, 31 - 3)); // &= ~(uncompressed ? 0xfu : 0x7u)
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001363 __ Asr(temp3, temp3, 7u); // uncompressed ? 0xffff0000u : 0xff0000u.
1364 __ Lsr(temp2, temp2, temp1); // Extract second character.
1365 __ Lsr(temp3, temp3, 16u); // uncompressed ? 0xffffu : 0xffu
1366 __ Lsr(out, temp_reg, temp1); // Extract first character.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001367 __ And(temp2, temp2, temp3);
1368 __ And(out, out, temp3);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001369 } else {
Anton Kirilovb88c4842016-11-14 14:37:00 +00001370 __ Bic(temp1, temp1, 0xf);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001371 __ Lsr(temp2, temp2, temp1);
1372 __ Lsr(out, temp_reg, temp1);
Anton Kirilovb88c4842016-11-14 14:37:00 +00001373 __ Movt(temp2, 0);
1374 __ Movt(out, 0);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001375 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01001376
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001377 __ Sub(out, out, temp2);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001378 temps.Release(temp_reg);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001379
1380 if (mirror::kUseStringCompression) {
Artem Serov672b9c12017-12-05 18:04:07 +00001381 __ B(end);
1382 __ Bind(different_compression);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001383
1384 // Comparison for different compression style.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001385 const size_t c_char_size = DataType::Size(DataType::Type::kInt8);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001386 DCHECK_EQ(c_char_size, 1u);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001387
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001388 // We want to free up the temp3, currently holding `str.count`, for comparison.
1389 // So, we move it to the bottom bit of the iteration count `temp0` which we tnen
1390 // need to treat as unsigned. Start by freeing the bit with an ADD and continue
1391 // further down by a LSRS+SBC which will flip the meaning of the flag but allow
1392 // `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001393 __ Add(temp0, temp0, temp0); // Unlike LSL, this ADD is always 16-bit.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001394 // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001395 __ Mov(temp1, str);
1396 __ Mov(temp2, arg);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001397 __ Lsrs(temp3, temp3, 1u); // Continue the move of the compression flag.
1398 {
Artem Serov0fb37192016-12-06 18:13:40 +00001399 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1400 3 * kMaxInstructionSizeInBytes,
1401 CodeBufferCheckScope::kMaximumSize);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001402 __ itt(cs); // Interleave with selection of temp1 and temp2.
1403 __ mov(cs, temp1, arg); // Preserves flags.
1404 __ mov(cs, temp2, str); // Preserves flags.
1405 }
Anton Kirilovb88c4842016-11-14 14:37:00 +00001406 __ Sbc(temp0, temp0, 0); // Complete the move of the compression flag.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001407
1408 // Adjust temp1 and temp2 from string pointers to data pointers.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001409 __ Add(temp1, temp1, value_offset);
1410 __ Add(temp2, temp2, value_offset);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001411
1412 vixl32::Label different_compression_loop;
1413 vixl32::Label different_compression_diff;
1414
1415 // Main loop for different compression.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001416 temp_reg = temps.Acquire();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001417 __ Bind(&different_compression_loop);
1418 __ Ldrb(temp_reg, MemOperand(temp1, c_char_size, PostIndex));
1419 __ Ldrh(temp3, MemOperand(temp2, char_size, PostIndex));
Anton Kirilovb88c4842016-11-14 14:37:00 +00001420 __ Cmp(temp_reg, temp3);
Artem Serov517d9f62016-12-12 15:51:15 +00001421 __ B(ne, &different_compression_diff, /* far_target */ false);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001422 __ Subs(temp0, temp0, 2);
Artem Serov517d9f62016-12-12 15:51:15 +00001423 __ B(hi, &different_compression_loop, /* far_target */ false);
Artem Serov672b9c12017-12-05 18:04:07 +00001424 __ B(end);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001425
1426 // Calculate the difference.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001427 __ Bind(&different_compression_diff);
1428 __ Sub(out, temp_reg, temp3);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001429 temps.Release(temp_reg);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001430 // Flip the difference if the `arg` is compressed.
1431 // `temp0` contains inverted `str` compression flag, i.e the same as `arg` compression flag.
1432 __ Lsrs(temp0, temp0, 1u);
1433 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1434 "Expecting 0=compressed, 1=uncompressed");
1435
Artem Serov0fb37192016-12-06 18:13:40 +00001436 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1437 2 * kMaxInstructionSizeInBytes,
1438 CodeBufferCheckScope::kMaximumSize);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001439 __ it(cc);
1440 __ rsb(cc, out, out, 0);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001441 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01001442}
1443
Vladimir Marko984519c2017-08-23 10:45:29 +01001444// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
1445// The normal loop plus the pre-header is 9 instructions (18-26 bytes) without string compression
1446// and 12 instructions (24-32 bytes) with string compression. We can compare up to 4 bytes in 4
1447// instructions (LDR+LDR+CMP+BNE) and up to 8 bytes in 6 instructions (LDRD+LDRD+CMP+BNE+CMP+BNE).
1448// Allow up to 12 instructions (32 bytes) for the unrolled loop.
1449constexpr size_t kShortConstStringEqualsCutoffInBytes = 16;
1450
1451static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
1452 if (candidate->IsLoadString()) {
1453 HLoadString* load_string = candidate->AsLoadString();
1454 const DexFile& dex_file = load_string->GetDexFile();
1455 return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
1456 }
1457 return nullptr;
1458}
1459
Anton Kirilov5ec62182016-10-13 20:16:02 +01001460void IntrinsicLocationsBuilderARMVIXL::VisitStringEquals(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001461 LocationSummary* locations =
1462 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001463 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1464 locations->SetInAt(0, Location::RequiresRegister());
1465 locations->SetInAt(1, Location::RequiresRegister());
Vladimir Marko984519c2017-08-23 10:45:29 +01001466
Anton Kirilov5ec62182016-10-13 20:16:02 +01001467 // Temporary registers to store lengths of strings and for calculations.
1468 // Using instruction cbz requires a low register, so explicitly set a temp to be R0.
1469 locations->AddTemp(LocationFrom(r0));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001470
Vladimir Marko984519c2017-08-23 10:45:29 +01001471 // For the generic implementation and for long const strings we need an extra temporary.
1472 // We do not need it for short const strings, up to 4 bytes, see code generation below.
1473 uint32_t const_string_length = 0u;
1474 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1475 if (const_string == nullptr) {
1476 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1477 }
1478 bool is_compressed =
1479 mirror::kUseStringCompression &&
1480 const_string != nullptr &&
1481 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1482 if (const_string == nullptr || const_string_length > (is_compressed ? 4u : 2u)) {
1483 locations->AddTemp(Location::RequiresRegister());
1484 }
1485
1486 // TODO: If the String.equals() is used only for an immediately following HIf, we can
1487 // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
1488 // Then we shall need an extra temporary register instead of the output register.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001489 locations->SetOut(Location::RequiresRegister());
1490}
1491
1492void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) {
1493 ArmVIXLAssembler* assembler = GetAssembler();
1494 LocationSummary* locations = invoke->GetLocations();
1495
1496 vixl32::Register str = InputRegisterAt(invoke, 0);
1497 vixl32::Register arg = InputRegisterAt(invoke, 1);
1498 vixl32::Register out = OutputRegister(invoke);
1499
1500 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001501
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001502 vixl32::Label loop;
Anton Kirilov5ec62182016-10-13 20:16:02 +01001503 vixl32::Label end;
1504 vixl32::Label return_true;
1505 vixl32::Label return_false;
Anton Kirilov6f644202017-02-27 18:29:45 +00001506 vixl32::Label* final_label = codegen_->GetFinalLabel(invoke, &end);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001507
1508 // Get offsets of count, value, and class fields within a string object.
1509 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
1510 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1511 const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
1512
1513 // Note that the null check must have been done earlier.
1514 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1515
1516 StringEqualsOptimizations optimizations(invoke);
1517 if (!optimizations.GetArgumentNotNull()) {
1518 // Check if input is null, return false if it is.
xueliang.zhongf51bc622016-11-04 09:23:32 +00001519 __ CompareAndBranchIfZero(arg, &return_false, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001520 }
1521
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001522 // Reference equality check, return true if same reference.
1523 __ Cmp(str, arg);
Artem Serov517d9f62016-12-12 15:51:15 +00001524 __ B(eq, &return_true, /* far_target */ false);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001525
Anton Kirilov5ec62182016-10-13 20:16:02 +01001526 if (!optimizations.GetArgumentIsString()) {
1527 // Instanceof check for the argument by comparing class fields.
1528 // All string objects must have the same type since String cannot be subclassed.
1529 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1530 // If the argument is a string object, its class field must be equal to receiver's class field.
Roland Levillain1d775d22018-09-07 13:56:57 +01001531 //
1532 // As the String class is expected to be non-movable, we can read the class
1533 // field from String.equals' arguments without read barriers.
1534 AssertNonMovableStringClass();
1535 // /* HeapReference<Class> */ temp = str->klass_
Anton Kirilov5ec62182016-10-13 20:16:02 +01001536 __ Ldr(temp, MemOperand(str, class_offset));
Roland Levillain1d775d22018-09-07 13:56:57 +01001537 // /* HeapReference<Class> */ out = arg->klass_
Vladimir Marko984519c2017-08-23 10:45:29 +01001538 __ Ldr(out, MemOperand(arg, class_offset));
Roland Levillain1d775d22018-09-07 13:56:57 +01001539 // Also, because we use the previously loaded class references only in the
1540 // following comparison, we don't need to unpoison them.
Vladimir Marko984519c2017-08-23 10:45:29 +01001541 __ Cmp(temp, out);
Artem Serov517d9f62016-12-12 15:51:15 +00001542 __ B(ne, &return_false, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001543 }
1544
Vladimir Marko984519c2017-08-23 10:45:29 +01001545 // Check if one of the inputs is a const string. Do not special-case both strings
1546 // being const, such cases should be handled by constant folding if needed.
1547 uint32_t const_string_length = 0u;
1548 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1549 if (const_string == nullptr) {
1550 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1551 if (const_string != nullptr) {
1552 std::swap(str, arg); // Make sure the const string is in `str`.
1553 }
1554 }
1555 bool is_compressed =
1556 mirror::kUseStringCompression &&
1557 const_string != nullptr &&
1558 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1559
1560 if (const_string != nullptr) {
1561 // Load `count` field of the argument string and check if it matches the const string.
1562 // Also compares the compression style, if differs return false.
1563 __ Ldr(temp, MemOperand(arg, count_offset));
1564 __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
1565 __ B(ne, &return_false, /* far_target */ false);
1566 } else {
1567 // Load `count` fields of this and argument strings.
1568 __ Ldr(temp, MemOperand(str, count_offset));
1569 __ Ldr(out, MemOperand(arg, count_offset));
1570 // Check if `count` fields are equal, return false if they're not.
1571 // Also compares the compression style, if differs return false.
1572 __ Cmp(temp, out);
1573 __ B(ne, &return_false, /* far_target */ false);
1574 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01001575
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001576 // Assertions that must hold in order to compare strings 4 bytes at a time.
Vladimir Marko984519c2017-08-23 10:45:29 +01001577 // Ok to do this because strings are zero-padded to kObjectAlignment.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001578 DCHECK_ALIGNED(value_offset, 4);
1579 static_assert(IsAligned<4>(kObjectAlignment), "String data must be aligned for fast compare.");
1580
Vladimir Marko984519c2017-08-23 10:45:29 +01001581 if (const_string != nullptr &&
1582 const_string_length <= (is_compressed ? kShortConstStringEqualsCutoffInBytes
1583 : kShortConstStringEqualsCutoffInBytes / 2u)) {
1584 // Load and compare the contents. Though we know the contents of the short const string
1585 // at compile time, materializing constants may be more code than loading from memory.
1586 int32_t offset = value_offset;
1587 size_t remaining_bytes =
1588 RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 4u);
1589 while (remaining_bytes > sizeof(uint32_t)) {
1590 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1591 UseScratchRegisterScope scratch_scope(assembler->GetVIXLAssembler());
1592 vixl32::Register temp2 = scratch_scope.Acquire();
1593 __ Ldrd(temp, temp1, MemOperand(str, offset));
1594 __ Ldrd(temp2, out, MemOperand(arg, offset));
1595 __ Cmp(temp, temp2);
1596 __ B(ne, &return_false, /* far_label */ false);
1597 __ Cmp(temp1, out);
1598 __ B(ne, &return_false, /* far_label */ false);
1599 offset += 2u * sizeof(uint32_t);
1600 remaining_bytes -= 2u * sizeof(uint32_t);
1601 }
1602 if (remaining_bytes != 0u) {
1603 __ Ldr(temp, MemOperand(str, offset));
1604 __ Ldr(out, MemOperand(arg, offset));
1605 __ Cmp(temp, out);
1606 __ B(ne, &return_false, /* far_label */ false);
1607 }
1608 } else {
1609 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
1610 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1611 "Expecting 0=compressed, 1=uncompressed");
1612 __ CompareAndBranchIfZero(temp, &return_true, /* far_target */ false);
1613
1614 if (mirror::kUseStringCompression) {
1615 // For string compression, calculate the number of bytes to compare (not chars).
1616 // This could in theory exceed INT32_MAX, so treat temp as unsigned.
1617 __ Lsrs(temp, temp, 1u); // Extract length and check compression flag.
1618 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1619 2 * kMaxInstructionSizeInBytes,
1620 CodeBufferCheckScope::kMaximumSize);
1621 __ it(cs); // If uncompressed,
1622 __ add(cs, temp, temp, temp); // double the byte count.
1623 }
1624
1625 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1626 UseScratchRegisterScope scratch_scope(assembler->GetVIXLAssembler());
1627 vixl32::Register temp2 = scratch_scope.Acquire();
1628
1629 // Store offset of string value in preparation for comparison loop.
1630 __ Mov(temp1, value_offset);
1631
1632 // Loop to compare strings 4 bytes at a time starting at the front of the string.
1633 __ Bind(&loop);
1634 __ Ldr(out, MemOperand(str, temp1));
1635 __ Ldr(temp2, MemOperand(arg, temp1));
1636 __ Add(temp1, temp1, Operand::From(sizeof(uint32_t)));
1637 __ Cmp(out, temp2);
1638 __ B(ne, &return_false, /* far_target */ false);
1639 // With string compression, we have compared 4 bytes, otherwise 2 chars.
1640 __ Subs(temp, temp, mirror::kUseStringCompression ? 4 : 2);
1641 __ B(hi, &loop, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001642 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001643
Anton Kirilov5ec62182016-10-13 20:16:02 +01001644 // Return true and exit the function.
1645 // If loop does not result in returning false, we return true.
1646 __ Bind(&return_true);
1647 __ Mov(out, 1);
Anton Kirilov6f644202017-02-27 18:29:45 +00001648 __ B(final_label);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001649
1650 // Return false and exit the function.
1651 __ Bind(&return_false);
1652 __ Mov(out, 0);
Anton Kirilov6f644202017-02-27 18:29:45 +00001653
1654 if (end.IsReferenced()) {
1655 __ Bind(&end);
1656 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01001657}
1658
1659static void GenerateVisitStringIndexOf(HInvoke* invoke,
1660 ArmVIXLAssembler* assembler,
1661 CodeGeneratorARMVIXL* codegen,
Anton Kirilov5ec62182016-10-13 20:16:02 +01001662 bool start_at_zero) {
1663 LocationSummary* locations = invoke->GetLocations();
1664
1665 // Note that the null check must have been done earlier.
1666 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1667
1668 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
1669 // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
1670 SlowPathCodeARMVIXL* slow_path = nullptr;
1671 HInstruction* code_point = invoke->InputAt(1);
1672 if (code_point->IsIntConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00001673 if (static_cast<uint32_t>(Int32ConstantFrom(code_point)) >
Anton Kirilov5ec62182016-10-13 20:16:02 +01001674 std::numeric_limits<uint16_t>::max()) {
1675 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1676 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
Vladimir Marko174b2e22017-10-12 13:34:49 +01001677 slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001678 codegen->AddSlowPath(slow_path);
1679 __ B(slow_path->GetEntryLabel());
1680 __ Bind(slow_path->GetExitLabel());
1681 return;
1682 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001683 } else if (code_point->GetType() != DataType::Type::kUint16) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01001684 vixl32::Register char_reg = InputRegisterAt(invoke, 1);
1685 // 0xffff is not modified immediate but 0x10000 is, so use `>= 0x10000` instead of `> 0xffff`.
1686 __ Cmp(char_reg, static_cast<uint32_t>(std::numeric_limits<uint16_t>::max()) + 1);
Vladimir Marko174b2e22017-10-12 13:34:49 +01001687 slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001688 codegen->AddSlowPath(slow_path);
1689 __ B(hs, slow_path->GetEntryLabel());
1690 }
1691
1692 if (start_at_zero) {
1693 vixl32::Register tmp_reg = RegisterFrom(locations->GetTemp(0));
1694 DCHECK(tmp_reg.Is(r2));
1695 // Start-index = 0.
1696 __ Mov(tmp_reg, 0);
1697 }
1698
1699 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
1700 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
1701
1702 if (slow_path != nullptr) {
1703 __ Bind(slow_path->GetExitLabel());
1704 }
1705}
1706
1707void IntrinsicLocationsBuilderARMVIXL::VisitStringIndexOf(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001708 LocationSummary* locations = new (allocator_) LocationSummary(
1709 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001710 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1711 // best to align the inputs accordingly.
1712 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1713 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1714 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1715 locations->SetOut(LocationFrom(r0));
1716
1717 // Need to send start-index=0.
1718 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1719}
1720
1721void IntrinsicCodeGeneratorARMVIXL::VisitStringIndexOf(HInvoke* invoke) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001722 GenerateVisitStringIndexOf(invoke, GetAssembler(), codegen_, /* start_at_zero */ true);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001723}
1724
1725void IntrinsicLocationsBuilderARMVIXL::VisitStringIndexOfAfter(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001726 LocationSummary* locations = new (allocator_) LocationSummary(
1727 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001728 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1729 // best to align the inputs accordingly.
1730 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1731 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1732 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1733 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1734 locations->SetOut(LocationFrom(r0));
1735}
1736
1737void IntrinsicCodeGeneratorARMVIXL::VisitStringIndexOfAfter(HInvoke* invoke) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001738 GenerateVisitStringIndexOf(invoke, GetAssembler(), codegen_, /* start_at_zero */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001739}
1740
1741void IntrinsicLocationsBuilderARMVIXL::VisitStringNewStringFromBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001742 LocationSummary* locations = new (allocator_) LocationSummary(
1743 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001744 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1745 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1746 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1747 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1748 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1749 locations->SetOut(LocationFrom(r0));
1750}
1751
1752void IntrinsicCodeGeneratorARMVIXL::VisitStringNewStringFromBytes(HInvoke* invoke) {
1753 ArmVIXLAssembler* assembler = GetAssembler();
1754 vixl32::Register byte_array = InputRegisterAt(invoke, 0);
1755 __ Cmp(byte_array, 0);
Vladimir Marko174b2e22017-10-12 13:34:49 +01001756 SlowPathCodeARMVIXL* slow_path =
1757 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001758 codegen_->AddSlowPath(slow_path);
1759 __ B(eq, slow_path->GetEntryLabel());
1760
1761 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
1762 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
1763 __ Bind(slow_path->GetExitLabel());
1764}
1765
1766void IntrinsicLocationsBuilderARMVIXL::VisitStringNewStringFromChars(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001767 LocationSummary* locations =
1768 new (allocator_) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001769 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1770 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1771 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1772 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1773 locations->SetOut(LocationFrom(r0));
1774}
1775
1776void IntrinsicCodeGeneratorARMVIXL::VisitStringNewStringFromChars(HInvoke* invoke) {
1777 // No need to emit code checking whether `locations->InAt(2)` is a null
1778 // pointer, as callers of the native method
1779 //
1780 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1781 //
1782 // all include a null check on `data` before calling that method.
1783 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
1784 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
1785}
1786
1787void IntrinsicLocationsBuilderARMVIXL::VisitStringNewStringFromString(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001788 LocationSummary* locations = new (allocator_) LocationSummary(
1789 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001790 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1791 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1792 locations->SetOut(LocationFrom(r0));
1793}
1794
1795void IntrinsicCodeGeneratorARMVIXL::VisitStringNewStringFromString(HInvoke* invoke) {
1796 ArmVIXLAssembler* assembler = GetAssembler();
1797 vixl32::Register string_to_copy = InputRegisterAt(invoke, 0);
1798 __ Cmp(string_to_copy, 0);
Vladimir Marko174b2e22017-10-12 13:34:49 +01001799 SlowPathCodeARMVIXL* slow_path =
1800 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001801 codegen_->AddSlowPath(slow_path);
1802 __ B(eq, slow_path->GetEntryLabel());
1803
1804 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
1805 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
1806
1807 __ Bind(slow_path->GetExitLabel());
1808}
1809
1810void IntrinsicLocationsBuilderARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) {
1811 // The only read barrier implementation supporting the
1812 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1813 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
1814 return;
1815 }
1816
1817 CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
1818 LocationSummary* locations = invoke->GetLocations();
1819 if (locations == nullptr) {
1820 return;
1821 }
1822
1823 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1824 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
1825 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1826
1827 if (src_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(src_pos->GetValue())) {
1828 locations->SetInAt(1, Location::RequiresRegister());
1829 }
1830 if (dest_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(dest_pos->GetValue())) {
1831 locations->SetInAt(3, Location::RequiresRegister());
1832 }
1833 if (length != nullptr && !assembler_->ShifterOperandCanAlwaysHold(length->GetValue())) {
1834 locations->SetInAt(4, Location::RequiresRegister());
1835 }
1836 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1837 // Temporary register IP cannot be used in
1838 // ReadBarrierSystemArrayCopySlowPathARM (because that register
1839 // is clobbered by ReadBarrierMarkRegX entry points). Get an extra
1840 // temporary register from the register allocator.
1841 locations->AddTemp(Location::RequiresRegister());
1842 }
1843}
1844
1845static void CheckPosition(ArmVIXLAssembler* assembler,
1846 Location pos,
1847 vixl32::Register input,
1848 Location length,
1849 SlowPathCodeARMVIXL* slow_path,
1850 vixl32::Register temp,
1851 bool length_is_input_length = false) {
1852 // Where is the length in the Array?
1853 const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
1854
1855 if (pos.IsConstant()) {
1856 int32_t pos_const = Int32ConstantFrom(pos);
1857 if (pos_const == 0) {
1858 if (!length_is_input_length) {
1859 // Check that length(input) >= length.
1860 __ Ldr(temp, MemOperand(input, length_offset));
1861 if (length.IsConstant()) {
1862 __ Cmp(temp, Int32ConstantFrom(length));
1863 } else {
1864 __ Cmp(temp, RegisterFrom(length));
1865 }
1866 __ B(lt, slow_path->GetEntryLabel());
1867 }
1868 } else {
1869 // Check that length(input) >= pos.
1870 __ Ldr(temp, MemOperand(input, length_offset));
1871 __ Subs(temp, temp, pos_const);
1872 __ B(lt, slow_path->GetEntryLabel());
1873
1874 // Check that (length(input) - pos) >= length.
1875 if (length.IsConstant()) {
1876 __ Cmp(temp, Int32ConstantFrom(length));
1877 } else {
1878 __ Cmp(temp, RegisterFrom(length));
1879 }
1880 __ B(lt, slow_path->GetEntryLabel());
1881 }
1882 } else if (length_is_input_length) {
1883 // The only way the copy can succeed is if pos is zero.
1884 vixl32::Register pos_reg = RegisterFrom(pos);
xueliang.zhongf51bc622016-11-04 09:23:32 +00001885 __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001886 } else {
1887 // Check that pos >= 0.
1888 vixl32::Register pos_reg = RegisterFrom(pos);
1889 __ Cmp(pos_reg, 0);
1890 __ B(lt, slow_path->GetEntryLabel());
1891
1892 // Check that pos <= length(input).
1893 __ Ldr(temp, MemOperand(input, length_offset));
1894 __ Subs(temp, temp, pos_reg);
1895 __ B(lt, slow_path->GetEntryLabel());
1896
1897 // Check that (length(input) - pos) >= length.
1898 if (length.IsConstant()) {
1899 __ Cmp(temp, Int32ConstantFrom(length));
1900 } else {
1901 __ Cmp(temp, RegisterFrom(length));
1902 }
1903 __ B(lt, slow_path->GetEntryLabel());
1904 }
1905}
1906
1907void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) {
1908 // The only read barrier implementation supporting the
1909 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1910 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1911
1912 ArmVIXLAssembler* assembler = GetAssembler();
1913 LocationSummary* locations = invoke->GetLocations();
1914
1915 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1916 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
1917 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
1918 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
1919 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
1920
1921 vixl32::Register src = InputRegisterAt(invoke, 0);
1922 Location src_pos = locations->InAt(1);
1923 vixl32::Register dest = InputRegisterAt(invoke, 2);
1924 Location dest_pos = locations->InAt(3);
1925 Location length = locations->InAt(4);
1926 Location temp1_loc = locations->GetTemp(0);
1927 vixl32::Register temp1 = RegisterFrom(temp1_loc);
1928 Location temp2_loc = locations->GetTemp(1);
1929 vixl32::Register temp2 = RegisterFrom(temp2_loc);
1930 Location temp3_loc = locations->GetTemp(2);
1931 vixl32::Register temp3 = RegisterFrom(temp3_loc);
1932
Vladimir Marko174b2e22017-10-12 13:34:49 +01001933 SlowPathCodeARMVIXL* intrinsic_slow_path =
1934 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001935 codegen_->AddSlowPath(intrinsic_slow_path);
1936
1937 vixl32::Label conditions_on_positions_validated;
1938 SystemArrayCopyOptimizations optimizations(invoke);
1939
1940 // If source and destination are the same, we go to slow path if we need to do
1941 // forward copying.
1942 if (src_pos.IsConstant()) {
1943 int32_t src_pos_constant = Int32ConstantFrom(src_pos);
1944 if (dest_pos.IsConstant()) {
1945 int32_t dest_pos_constant = Int32ConstantFrom(dest_pos);
1946 if (optimizations.GetDestinationIsSource()) {
1947 // Checked when building locations.
1948 DCHECK_GE(src_pos_constant, dest_pos_constant);
1949 } else if (src_pos_constant < dest_pos_constant) {
1950 __ Cmp(src, dest);
1951 __ B(eq, intrinsic_slow_path->GetEntryLabel());
1952 }
1953
1954 // Checked when building locations.
1955 DCHECK(!optimizations.GetDestinationIsSource()
1956 || (src_pos_constant >= Int32ConstantFrom(dest_pos)));
1957 } else {
1958 if (!optimizations.GetDestinationIsSource()) {
1959 __ Cmp(src, dest);
Artem Serov517d9f62016-12-12 15:51:15 +00001960 __ B(ne, &conditions_on_positions_validated, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001961 }
1962 __ Cmp(RegisterFrom(dest_pos), src_pos_constant);
1963 __ B(gt, intrinsic_slow_path->GetEntryLabel());
1964 }
1965 } else {
1966 if (!optimizations.GetDestinationIsSource()) {
1967 __ Cmp(src, dest);
Artem Serov517d9f62016-12-12 15:51:15 +00001968 __ B(ne, &conditions_on_positions_validated, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001969 }
1970 if (dest_pos.IsConstant()) {
1971 int32_t dest_pos_constant = Int32ConstantFrom(dest_pos);
1972 __ Cmp(RegisterFrom(src_pos), dest_pos_constant);
1973 } else {
1974 __ Cmp(RegisterFrom(src_pos), RegisterFrom(dest_pos));
1975 }
1976 __ B(lt, intrinsic_slow_path->GetEntryLabel());
1977 }
1978
1979 __ Bind(&conditions_on_positions_validated);
1980
1981 if (!optimizations.GetSourceIsNotNull()) {
1982 // Bail out if the source is null.
xueliang.zhongf51bc622016-11-04 09:23:32 +00001983 __ CompareAndBranchIfZero(src, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001984 }
1985
1986 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
1987 // Bail out if the destination is null.
xueliang.zhongf51bc622016-11-04 09:23:32 +00001988 __ CompareAndBranchIfZero(dest, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001989 }
1990
1991 // If the length is negative, bail out.
1992 // We have already checked in the LocationsBuilder for the constant case.
1993 if (!length.IsConstant() &&
1994 !optimizations.GetCountIsSourceLength() &&
1995 !optimizations.GetCountIsDestinationLength()) {
1996 __ Cmp(RegisterFrom(length), 0);
1997 __ B(lt, intrinsic_slow_path->GetEntryLabel());
1998 }
1999
2000 // Validity checks: source.
2001 CheckPosition(assembler,
2002 src_pos,
2003 src,
2004 length,
2005 intrinsic_slow_path,
2006 temp1,
2007 optimizations.GetCountIsSourceLength());
2008
2009 // Validity checks: dest.
2010 CheckPosition(assembler,
2011 dest_pos,
2012 dest,
2013 length,
2014 intrinsic_slow_path,
2015 temp1,
2016 optimizations.GetCountIsDestinationLength());
2017
2018 if (!optimizations.GetDoesNotNeedTypeCheck()) {
2019 // Check whether all elements of the source array are assignable to the component
2020 // type of the destination array. We do two checks: the classes are the same,
2021 // or the destination is Object[]. If none of these checks succeed, we go to the
2022 // slow path.
2023
2024 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2025 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2026 // /* HeapReference<Class> */ temp1 = src->klass_
2027 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2028 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
2029 // Bail out if the source is not a non primitive array.
2030 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2031 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2032 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
xueliang.zhongf51bc622016-11-04 09:23:32 +00002033 __ CompareAndBranchIfZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002034 // If heap poisoning is enabled, `temp1` has been unpoisoned
2035 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2036 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
2037 __ Ldrh(temp1, MemOperand(temp1, primitive_offset));
2038 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00002039 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002040 }
2041
2042 // /* HeapReference<Class> */ temp1 = dest->klass_
2043 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2044 invoke, temp1_loc, dest, class_offset, temp2_loc, /* needs_null_check */ false);
2045
2046 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2047 // Bail out if the destination is not a non primitive array.
2048 //
2049 // Register `temp1` is not trashed by the read barrier emitted
2050 // by GenerateFieldLoadWithBakerReadBarrier below, as that
2051 // method produces a call to a ReadBarrierMarkRegX entry point,
2052 // which saves all potentially live registers, including
2053 // temporaries such a `temp1`.
2054 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2055 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2056 invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
xueliang.zhongf51bc622016-11-04 09:23:32 +00002057 __ CompareAndBranchIfZero(temp2, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002058 // If heap poisoning is enabled, `temp2` has been unpoisoned
2059 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2060 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2061 __ Ldrh(temp2, MemOperand(temp2, primitive_offset));
2062 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00002063 __ CompareAndBranchIfNonZero(temp2, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002064 }
2065
2066 // For the same reason given earlier, `temp1` is not trashed by the
2067 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
2068 // /* HeapReference<Class> */ temp2 = src->klass_
2069 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2070 invoke, temp2_loc, src, class_offset, temp3_loc, /* needs_null_check */ false);
2071 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
2072 __ Cmp(temp1, temp2);
2073
2074 if (optimizations.GetDestinationIsTypedObjectArray()) {
2075 vixl32::Label do_copy;
Artem Serov517d9f62016-12-12 15:51:15 +00002076 __ B(eq, &do_copy, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002077 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2078 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2079 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
2080 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2081 // We do not need to emit a read barrier for the following
2082 // heap reference load, as `temp1` is only used in a
2083 // comparison with null below, and this reference is not
2084 // kept afterwards.
2085 __ Ldr(temp1, MemOperand(temp1, super_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00002086 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002087 __ Bind(&do_copy);
2088 } else {
2089 __ B(ne, intrinsic_slow_path->GetEntryLabel());
2090 }
2091 } else {
2092 // Non read barrier code.
2093
2094 // /* HeapReference<Class> */ temp1 = dest->klass_
2095 __ Ldr(temp1, MemOperand(dest, class_offset));
2096 // /* HeapReference<Class> */ temp2 = src->klass_
2097 __ Ldr(temp2, MemOperand(src, class_offset));
2098 bool did_unpoison = false;
2099 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2100 !optimizations.GetSourceIsNonPrimitiveArray()) {
2101 // One or two of the references need to be unpoisoned. Unpoison them
2102 // both to make the identity check valid.
2103 assembler->MaybeUnpoisonHeapReference(temp1);
2104 assembler->MaybeUnpoisonHeapReference(temp2);
2105 did_unpoison = true;
2106 }
2107
2108 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2109 // Bail out if the destination is not a non primitive array.
2110 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2111 __ Ldr(temp3, MemOperand(temp1, component_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00002112 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002113 assembler->MaybeUnpoisonHeapReference(temp3);
2114 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2115 __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
2116 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00002117 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002118 }
2119
2120 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2121 // Bail out if the source is not a non primitive array.
2122 // /* HeapReference<Class> */ temp3 = temp2->component_type_
2123 __ Ldr(temp3, MemOperand(temp2, component_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00002124 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002125 assembler->MaybeUnpoisonHeapReference(temp3);
2126 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2127 __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
2128 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00002129 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002130 }
2131
2132 __ Cmp(temp1, temp2);
2133
2134 if (optimizations.GetDestinationIsTypedObjectArray()) {
2135 vixl32::Label do_copy;
Artem Serov517d9f62016-12-12 15:51:15 +00002136 __ B(eq, &do_copy, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002137 if (!did_unpoison) {
2138 assembler->MaybeUnpoisonHeapReference(temp1);
2139 }
2140 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2141 __ Ldr(temp1, MemOperand(temp1, component_offset));
2142 assembler->MaybeUnpoisonHeapReference(temp1);
2143 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2144 __ Ldr(temp1, MemOperand(temp1, super_offset));
2145 // No need to unpoison the result, we're comparing against null.
xueliang.zhongf51bc622016-11-04 09:23:32 +00002146 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002147 __ Bind(&do_copy);
2148 } else {
2149 __ B(ne, intrinsic_slow_path->GetEntryLabel());
2150 }
2151 }
2152 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2153 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2154 // Bail out if the source is not a non primitive array.
2155 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2156 // /* HeapReference<Class> */ temp1 = src->klass_
2157 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2158 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
2159 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2160 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2161 invoke, temp3_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
xueliang.zhongf51bc622016-11-04 09:23:32 +00002162 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002163 // If heap poisoning is enabled, `temp3` has been unpoisoned
2164 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2165 } else {
2166 // /* HeapReference<Class> */ temp1 = src->klass_
2167 __ Ldr(temp1, MemOperand(src, class_offset));
2168 assembler->MaybeUnpoisonHeapReference(temp1);
2169 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2170 __ Ldr(temp3, MemOperand(temp1, component_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00002171 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002172 assembler->MaybeUnpoisonHeapReference(temp3);
2173 }
2174 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2175 __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
2176 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00002177 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002178 }
2179
Roland Levillain1663d162017-03-17 15:15:21 +00002180 if (length.IsConstant() && Int32ConstantFrom(length) == 0) {
2181 // Null constant length: not need to emit the loop code at all.
Anton Kirilov5ec62182016-10-13 20:16:02 +01002182 } else {
Roland Levillain1663d162017-03-17 15:15:21 +00002183 vixl32::Label done;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002184 const DataType::Type type = DataType::Type::kReference;
2185 const int32_t element_size = DataType::Size(type);
Roland Levillain1663d162017-03-17 15:15:21 +00002186
2187 if (length.IsRegister()) {
2188 // Don't enter the copy loop if the length is null.
2189 __ CompareAndBranchIfZero(RegisterFrom(length), &done, /* is_far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002190 }
Roland Levillain1663d162017-03-17 15:15:21 +00002191
2192 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2193 // TODO: Also convert this intrinsic to the IsGcMarking strategy?
2194
2195 // SystemArrayCopy implementation for Baker read barriers (see
Roland Levillain9983e302017-07-14 14:34:22 +01002196 // also CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier):
Roland Levillain1663d162017-03-17 15:15:21 +00002197 //
2198 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
2199 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
2200 // bool is_gray = (rb_state == ReadBarrier::GrayState());
2201 // if (is_gray) {
2202 // // Slow-path copy.
2203 // do {
2204 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
2205 // } while (src_ptr != end_ptr)
2206 // } else {
2207 // // Fast-path copy.
2208 // do {
2209 // *dest_ptr++ = *src_ptr++;
2210 // } while (src_ptr != end_ptr)
2211 // }
2212
2213 // /* int32_t */ monitor = src->monitor_
2214 __ Ldr(temp2, MemOperand(src, monitor_offset));
2215 // /* LockWord */ lock_word = LockWord(monitor)
2216 static_assert(sizeof(LockWord) == sizeof(int32_t),
2217 "art::LockWord and int32_t have different sizes.");
2218
2219 // Introduce a dependency on the lock_word including the rb_state,
2220 // which shall prevent load-load reordering without using
2221 // a memory barrier (which would be more expensive).
2222 // `src` is unchanged by this operation, but its value now depends
2223 // on `temp2`.
2224 __ Add(src, src, Operand(temp2, vixl32::LSR, 32));
2225
2226 // Compute the base source address in `temp1`.
2227 // Note that `temp1` (the base source address) is computed from
2228 // `src` (and `src_pos`) here, and thus honors the artificial
2229 // dependency of `src` on `temp2`.
2230 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2231 // Compute the end source address in `temp3`.
2232 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2233 // The base destination address is computed later, as `temp2` is
2234 // used for intermediate computations.
2235
2236 // Slow path used to copy array when `src` is gray.
2237 // Note that the base destination address is computed in `temp2`
2238 // by the slow path code.
2239 SlowPathCodeARMVIXL* read_barrier_slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01002240 new (codegen_->GetScopedAllocator()) ReadBarrierSystemArrayCopySlowPathARMVIXL(invoke);
Roland Levillain1663d162017-03-17 15:15:21 +00002241 codegen_->AddSlowPath(read_barrier_slow_path);
2242
2243 // Given the numeric representation, it's enough to check the low bit of the
2244 // rb_state. We do that by shifting the bit out of the lock word with LSRS
2245 // which can be a 16-bit instruction unlike the TST immediate.
Roland Levillain14e5a292018-06-28 12:00:56 +01002246 static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
Roland Levillain1663d162017-03-17 15:15:21 +00002247 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
2248 __ Lsrs(temp2, temp2, LockWord::kReadBarrierStateShift + 1);
2249 // Carry flag is the last bit shifted out by LSRS.
2250 __ B(cs, read_barrier_slow_path->GetEntryLabel());
2251
2252 // Fast-path copy.
2253 // Compute the base destination address in `temp2`.
2254 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2255 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2256 // poison/unpoison.
2257 vixl32::Label loop;
2258 __ Bind(&loop);
2259 {
2260 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2261 const vixl32::Register temp_reg = temps.Acquire();
2262 __ Ldr(temp_reg, MemOperand(temp1, element_size, PostIndex));
2263 __ Str(temp_reg, MemOperand(temp2, element_size, PostIndex));
2264 }
2265 __ Cmp(temp1, temp3);
2266 __ B(ne, &loop, /* far_target */ false);
2267
2268 __ Bind(read_barrier_slow_path->GetExitLabel());
2269 } else {
2270 // Non read barrier code.
2271 // Compute the base source address in `temp1`.
2272 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2273 // Compute the base destination address in `temp2`.
2274 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2275 // Compute the end source address in `temp3`.
2276 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2277 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2278 // poison/unpoison.
2279 vixl32::Label loop;
2280 __ Bind(&loop);
2281 {
2282 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2283 const vixl32::Register temp_reg = temps.Acquire();
2284 __ Ldr(temp_reg, MemOperand(temp1, element_size, PostIndex));
2285 __ Str(temp_reg, MemOperand(temp2, element_size, PostIndex));
2286 }
2287 __ Cmp(temp1, temp3);
2288 __ B(ne, &loop, /* far_target */ false);
2289 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01002290 __ Bind(&done);
2291 }
2292
2293 // We only need one card marking on the destination array.
2294 codegen_->MarkGCCard(temp1, temp2, dest, NoReg, /* value_can_be_null */ false);
2295
2296 __ Bind(intrinsic_slow_path->GetExitLabel());
2297}
2298
Vladimir Markoca6fff82017-10-03 14:49:14 +01002299static void CreateFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01002300 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2301 // the code generator. Furthermore, the register allocator creates fixed live intervals
2302 // for all caller-saved registers because we are doing a function call. As a result, if
2303 // the input and output locations are unallocated, the register allocator runs out of
2304 // registers and fails; however, a debuggable graph is not the common case.
2305 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2306 return;
2307 }
2308
2309 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002310 DCHECK_EQ(invoke->InputAt(0)->GetType(), DataType::Type::kFloat64);
2311 DCHECK_EQ(invoke->GetType(), DataType::Type::kFloat64);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002312
Vladimir Markoca6fff82017-10-03 14:49:14 +01002313 LocationSummary* const locations =
2314 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002315 const InvokeRuntimeCallingConventionARMVIXL calling_convention;
2316
2317 locations->SetInAt(0, Location::RequiresFpuRegister());
2318 locations->SetOut(Location::RequiresFpuRegister());
2319 // Native code uses the soft float ABI.
2320 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
2321 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
2322}
2323
Vladimir Markoca6fff82017-10-03 14:49:14 +01002324static void CreateFPFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01002325 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2326 // the code generator. Furthermore, the register allocator creates fixed live intervals
2327 // for all caller-saved registers because we are doing a function call. As a result, if
2328 // the input and output locations are unallocated, the register allocator runs out of
2329 // registers and fails; however, a debuggable graph is not the common case.
2330 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2331 return;
2332 }
2333
2334 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002335 DCHECK_EQ(invoke->InputAt(0)->GetType(), DataType::Type::kFloat64);
2336 DCHECK_EQ(invoke->InputAt(1)->GetType(), DataType::Type::kFloat64);
2337 DCHECK_EQ(invoke->GetType(), DataType::Type::kFloat64);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002338
Vladimir Markoca6fff82017-10-03 14:49:14 +01002339 LocationSummary* const locations =
2340 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002341 const InvokeRuntimeCallingConventionARMVIXL calling_convention;
2342
2343 locations->SetInAt(0, Location::RequiresFpuRegister());
2344 locations->SetInAt(1, Location::RequiresFpuRegister());
2345 locations->SetOut(Location::RequiresFpuRegister());
2346 // Native code uses the soft float ABI.
2347 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
2348 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
2349 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
2350 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(3)));
2351}
2352
2353static void GenFPToFPCall(HInvoke* invoke,
2354 ArmVIXLAssembler* assembler,
2355 CodeGeneratorARMVIXL* codegen,
2356 QuickEntrypointEnum entry) {
2357 LocationSummary* const locations = invoke->GetLocations();
2358
2359 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2360 DCHECK(locations->WillCall() && locations->Intrinsified());
2361
2362 // Native code uses the soft float ABI.
2363 __ Vmov(RegisterFrom(locations->GetTemp(0)),
2364 RegisterFrom(locations->GetTemp(1)),
2365 InputDRegisterAt(invoke, 0));
2366 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
2367 __ Vmov(OutputDRegister(invoke),
2368 RegisterFrom(locations->GetTemp(0)),
2369 RegisterFrom(locations->GetTemp(1)));
2370}
2371
2372static void GenFPFPToFPCall(HInvoke* invoke,
2373 ArmVIXLAssembler* assembler,
2374 CodeGeneratorARMVIXL* codegen,
2375 QuickEntrypointEnum entry) {
2376 LocationSummary* const locations = invoke->GetLocations();
2377
2378 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2379 DCHECK(locations->WillCall() && locations->Intrinsified());
2380
2381 // Native code uses the soft float ABI.
2382 __ Vmov(RegisterFrom(locations->GetTemp(0)),
2383 RegisterFrom(locations->GetTemp(1)),
2384 InputDRegisterAt(invoke, 0));
2385 __ Vmov(RegisterFrom(locations->GetTemp(2)),
2386 RegisterFrom(locations->GetTemp(3)),
2387 InputDRegisterAt(invoke, 1));
2388 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
2389 __ Vmov(OutputDRegister(invoke),
2390 RegisterFrom(locations->GetTemp(0)),
2391 RegisterFrom(locations->GetTemp(1)));
2392}
2393
2394void IntrinsicLocationsBuilderARMVIXL::VisitMathCos(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002395 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002396}
2397
2398void IntrinsicCodeGeneratorARMVIXL::VisitMathCos(HInvoke* invoke) {
2399 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCos);
2400}
2401
2402void IntrinsicLocationsBuilderARMVIXL::VisitMathSin(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002403 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002404}
2405
2406void IntrinsicCodeGeneratorARMVIXL::VisitMathSin(HInvoke* invoke) {
2407 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSin);
2408}
2409
2410void IntrinsicLocationsBuilderARMVIXL::VisitMathAcos(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002411 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002412}
2413
2414void IntrinsicCodeGeneratorARMVIXL::VisitMathAcos(HInvoke* invoke) {
2415 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAcos);
2416}
2417
2418void IntrinsicLocationsBuilderARMVIXL::VisitMathAsin(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002419 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002420}
2421
2422void IntrinsicCodeGeneratorARMVIXL::VisitMathAsin(HInvoke* invoke) {
2423 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAsin);
2424}
2425
2426void IntrinsicLocationsBuilderARMVIXL::VisitMathAtan(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002427 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002428}
2429
2430void IntrinsicCodeGeneratorARMVIXL::VisitMathAtan(HInvoke* invoke) {
2431 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan);
2432}
2433
2434void IntrinsicLocationsBuilderARMVIXL::VisitMathCbrt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002435 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002436}
2437
2438void IntrinsicCodeGeneratorARMVIXL::VisitMathCbrt(HInvoke* invoke) {
2439 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCbrt);
2440}
2441
2442void IntrinsicLocationsBuilderARMVIXL::VisitMathCosh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002443 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002444}
2445
2446void IntrinsicCodeGeneratorARMVIXL::VisitMathCosh(HInvoke* invoke) {
2447 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCosh);
2448}
2449
2450void IntrinsicLocationsBuilderARMVIXL::VisitMathExp(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002451 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002452}
2453
2454void IntrinsicCodeGeneratorARMVIXL::VisitMathExp(HInvoke* invoke) {
2455 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExp);
2456}
2457
2458void IntrinsicLocationsBuilderARMVIXL::VisitMathExpm1(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002459 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002460}
2461
2462void IntrinsicCodeGeneratorARMVIXL::VisitMathExpm1(HInvoke* invoke) {
2463 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExpm1);
2464}
2465
2466void IntrinsicLocationsBuilderARMVIXL::VisitMathLog(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002467 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002468}
2469
2470void IntrinsicCodeGeneratorARMVIXL::VisitMathLog(HInvoke* invoke) {
2471 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog);
2472}
2473
2474void IntrinsicLocationsBuilderARMVIXL::VisitMathLog10(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002475 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002476}
2477
2478void IntrinsicCodeGeneratorARMVIXL::VisitMathLog10(HInvoke* invoke) {
2479 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog10);
2480}
2481
2482void IntrinsicLocationsBuilderARMVIXL::VisitMathSinh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002483 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002484}
2485
2486void IntrinsicCodeGeneratorARMVIXL::VisitMathSinh(HInvoke* invoke) {
2487 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSinh);
2488}
2489
2490void IntrinsicLocationsBuilderARMVIXL::VisitMathTan(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002491 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002492}
2493
2494void IntrinsicCodeGeneratorARMVIXL::VisitMathTan(HInvoke* invoke) {
2495 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTan);
2496}
2497
2498void IntrinsicLocationsBuilderARMVIXL::VisitMathTanh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002499 CreateFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002500}
2501
2502void IntrinsicCodeGeneratorARMVIXL::VisitMathTanh(HInvoke* invoke) {
2503 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTanh);
2504}
2505
2506void IntrinsicLocationsBuilderARMVIXL::VisitMathAtan2(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002507 CreateFPFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002508}
2509
2510void IntrinsicCodeGeneratorARMVIXL::VisitMathAtan2(HInvoke* invoke) {
2511 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan2);
2512}
2513
Vladimir Marko4d179872018-01-19 14:50:10 +00002514void IntrinsicLocationsBuilderARMVIXL::VisitMathPow(HInvoke* invoke) {
2515 CreateFPFPToFPCallLocations(allocator_, invoke);
2516}
2517
2518void IntrinsicCodeGeneratorARMVIXL::VisitMathPow(HInvoke* invoke) {
2519 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickPow);
2520}
2521
Anton Kirilov5ec62182016-10-13 20:16:02 +01002522void IntrinsicLocationsBuilderARMVIXL::VisitMathHypot(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002523 CreateFPFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002524}
2525
2526void IntrinsicCodeGeneratorARMVIXL::VisitMathHypot(HInvoke* invoke) {
2527 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickHypot);
2528}
2529
2530void IntrinsicLocationsBuilderARMVIXL::VisitMathNextAfter(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002531 CreateFPFPToFPCallLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002532}
2533
2534void IntrinsicCodeGeneratorARMVIXL::VisitMathNextAfter(HInvoke* invoke) {
2535 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter);
2536}
2537
2538void IntrinsicLocationsBuilderARMVIXL::VisitIntegerReverse(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002539 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002540}
2541
2542void IntrinsicCodeGeneratorARMVIXL::VisitIntegerReverse(HInvoke* invoke) {
2543 ArmVIXLAssembler* assembler = GetAssembler();
2544 __ Rbit(OutputRegister(invoke), InputRegisterAt(invoke, 0));
2545}
2546
2547void IntrinsicLocationsBuilderARMVIXL::VisitLongReverse(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002548 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002549}
2550
2551void IntrinsicCodeGeneratorARMVIXL::VisitLongReverse(HInvoke* invoke) {
2552 ArmVIXLAssembler* assembler = GetAssembler();
2553 LocationSummary* locations = invoke->GetLocations();
2554
2555 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
2556 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
2557 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
2558 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
2559
2560 __ Rbit(out_reg_lo, in_reg_hi);
2561 __ Rbit(out_reg_hi, in_reg_lo);
2562}
2563
2564void IntrinsicLocationsBuilderARMVIXL::VisitIntegerReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002565 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002566}
2567
2568void IntrinsicCodeGeneratorARMVIXL::VisitIntegerReverseBytes(HInvoke* invoke) {
2569 ArmVIXLAssembler* assembler = GetAssembler();
2570 __ Rev(OutputRegister(invoke), InputRegisterAt(invoke, 0));
2571}
2572
2573void IntrinsicLocationsBuilderARMVIXL::VisitLongReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002574 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002575}
2576
2577void IntrinsicCodeGeneratorARMVIXL::VisitLongReverseBytes(HInvoke* invoke) {
2578 ArmVIXLAssembler* assembler = GetAssembler();
2579 LocationSummary* locations = invoke->GetLocations();
2580
2581 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
2582 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
2583 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
2584 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
2585
2586 __ Rev(out_reg_lo, in_reg_hi);
2587 __ Rev(out_reg_hi, in_reg_lo);
2588}
2589
2590void IntrinsicLocationsBuilderARMVIXL::VisitShortReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002591 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002592}
2593
2594void IntrinsicCodeGeneratorARMVIXL::VisitShortReverseBytes(HInvoke* invoke) {
2595 ArmVIXLAssembler* assembler = GetAssembler();
2596 __ Revsh(OutputRegister(invoke), InputRegisterAt(invoke, 0));
2597}
2598
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002599static void GenBitCount(HInvoke* instr, DataType::Type type, ArmVIXLAssembler* assembler) {
2600 DCHECK(DataType::IsIntOrLongType(type)) << type;
2601 DCHECK_EQ(instr->GetType(), DataType::Type::kInt32);
2602 DCHECK_EQ(DataType::Kind(instr->InputAt(0)->GetType()), type);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002603
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002604 bool is_long = type == DataType::Type::kInt64;
Anton Kirilov5ec62182016-10-13 20:16:02 +01002605 LocationSummary* locations = instr->GetLocations();
2606 Location in = locations->InAt(0);
2607 vixl32::Register src_0 = is_long ? LowRegisterFrom(in) : RegisterFrom(in);
2608 vixl32::Register src_1 = is_long ? HighRegisterFrom(in) : src_0;
2609 vixl32::SRegister tmp_s = LowSRegisterFrom(locations->GetTemp(0));
2610 vixl32::DRegister tmp_d = DRegisterFrom(locations->GetTemp(0));
2611 vixl32::Register out_r = OutputRegister(instr);
2612
2613 // Move data from core register(s) to temp D-reg for bit count calculation, then move back.
2614 // According to Cortex A57 and A72 optimization guides, compared to transferring to full D-reg,
2615 // transferring data from core reg to upper or lower half of vfp D-reg requires extra latency,
2616 // That's why for integer bit count, we use 'vmov d0, r0, r0' instead of 'vmov d0[0], r0'.
2617 __ Vmov(tmp_d, src_1, src_0); // Temp DReg |--src_1|--src_0|
2618 __ Vcnt(Untyped8, tmp_d, tmp_d); // Temp DReg |c|c|c|c|c|c|c|c|
2619 __ Vpaddl(U8, tmp_d, tmp_d); // Temp DReg |--c|--c|--c|--c|
2620 __ Vpaddl(U16, tmp_d, tmp_d); // Temp DReg |------c|------c|
2621 if (is_long) {
2622 __ Vpaddl(U32, tmp_d, tmp_d); // Temp DReg |--------------c|
2623 }
2624 __ Vmov(out_r, tmp_s);
2625}
2626
2627void IntrinsicLocationsBuilderARMVIXL::VisitIntegerBitCount(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002628 CreateIntToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002629 invoke->GetLocations()->AddTemp(Location::RequiresFpuRegister());
2630}
2631
2632void IntrinsicCodeGeneratorARMVIXL::VisitIntegerBitCount(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002633 GenBitCount(invoke, DataType::Type::kInt32, GetAssembler());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002634}
2635
2636void IntrinsicLocationsBuilderARMVIXL::VisitLongBitCount(HInvoke* invoke) {
2637 VisitIntegerBitCount(invoke);
2638}
2639
2640void IntrinsicCodeGeneratorARMVIXL::VisitLongBitCount(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002641 GenBitCount(invoke, DataType::Type::kInt64, GetAssembler());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002642}
2643
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002644static void GenHighestOneBit(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002645 DataType::Type type,
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002646 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002647 DCHECK(DataType::IsIntOrLongType(type));
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002648
2649 ArmVIXLAssembler* assembler = codegen->GetAssembler();
2650 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2651 const vixl32::Register temp = temps.Acquire();
2652
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002653 if (type == DataType::Type::kInt64) {
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002654 LocationSummary* locations = invoke->GetLocations();
2655 Location in = locations->InAt(0);
2656 Location out = locations->Out();
2657
2658 vixl32::Register in_reg_lo = LowRegisterFrom(in);
2659 vixl32::Register in_reg_hi = HighRegisterFrom(in);
2660 vixl32::Register out_reg_lo = LowRegisterFrom(out);
2661 vixl32::Register out_reg_hi = HighRegisterFrom(out);
2662
2663 __ Mov(temp, 0x80000000); // Modified immediate.
2664 __ Clz(out_reg_lo, in_reg_lo);
2665 __ Clz(out_reg_hi, in_reg_hi);
2666 __ Lsr(out_reg_lo, temp, out_reg_lo);
2667 __ Lsrs(out_reg_hi, temp, out_reg_hi);
2668
2669 // Discard result for lowest 32 bits if highest 32 bits are not zero.
2670 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
2671 // we check that the output is in a low register, so that a 16-bit MOV
2672 // encoding can be used. If output is in a high register, then we generate
2673 // 4 more bytes of code to avoid a branch.
2674 Operand mov_src(0);
2675 if (!out_reg_lo.IsLow()) {
2676 __ Mov(LeaveFlags, temp, 0);
2677 mov_src = Operand(temp);
2678 }
2679 ExactAssemblyScope it_scope(codegen->GetVIXLAssembler(),
2680 2 * vixl32::k16BitT32InstructionSizeInBytes,
2681 CodeBufferCheckScope::kExactSize);
2682 __ it(ne);
2683 __ mov(ne, out_reg_lo, mov_src);
2684 } else {
2685 vixl32::Register out = OutputRegister(invoke);
2686 vixl32::Register in = InputRegisterAt(invoke, 0);
2687
2688 __ Mov(temp, 0x80000000); // Modified immediate.
2689 __ Clz(out, in);
2690 __ Lsr(out, temp, out);
2691 }
2692}
2693
2694void IntrinsicLocationsBuilderARMVIXL::VisitIntegerHighestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002695 CreateIntToIntLocations(allocator_, invoke);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002696}
2697
2698void IntrinsicCodeGeneratorARMVIXL::VisitIntegerHighestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002699 GenHighestOneBit(invoke, DataType::Type::kInt32, codegen_);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002700}
2701
2702void IntrinsicLocationsBuilderARMVIXL::VisitLongHighestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002703 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002704}
2705
2706void IntrinsicCodeGeneratorARMVIXL::VisitLongHighestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002707 GenHighestOneBit(invoke, DataType::Type::kInt64, codegen_);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002708}
2709
2710static void GenLowestOneBit(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002711 DataType::Type type,
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002712 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002713 DCHECK(DataType::IsIntOrLongType(type));
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002714
2715 ArmVIXLAssembler* assembler = codegen->GetAssembler();
2716 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2717 const vixl32::Register temp = temps.Acquire();
2718
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002719 if (type == DataType::Type::kInt64) {
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002720 LocationSummary* locations = invoke->GetLocations();
2721 Location in = locations->InAt(0);
2722 Location out = locations->Out();
2723
2724 vixl32::Register in_reg_lo = LowRegisterFrom(in);
2725 vixl32::Register in_reg_hi = HighRegisterFrom(in);
2726 vixl32::Register out_reg_lo = LowRegisterFrom(out);
2727 vixl32::Register out_reg_hi = HighRegisterFrom(out);
2728
2729 __ Rsb(out_reg_hi, in_reg_hi, 0);
2730 __ Rsb(out_reg_lo, in_reg_lo, 0);
2731 __ And(out_reg_hi, out_reg_hi, in_reg_hi);
2732 // The result of this operation is 0 iff in_reg_lo is 0
2733 __ Ands(out_reg_lo, out_reg_lo, in_reg_lo);
2734
2735 // Discard result for highest 32 bits if lowest 32 bits are not zero.
2736 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
2737 // we check that the output is in a low register, so that a 16-bit MOV
2738 // encoding can be used. If output is in a high register, then we generate
2739 // 4 more bytes of code to avoid a branch.
2740 Operand mov_src(0);
2741 if (!out_reg_lo.IsLow()) {
2742 __ Mov(LeaveFlags, temp, 0);
2743 mov_src = Operand(temp);
2744 }
2745 ExactAssemblyScope it_scope(codegen->GetVIXLAssembler(),
2746 2 * vixl32::k16BitT32InstructionSizeInBytes,
2747 CodeBufferCheckScope::kExactSize);
2748 __ it(ne);
2749 __ mov(ne, out_reg_hi, mov_src);
2750 } else {
2751 vixl32::Register out = OutputRegister(invoke);
2752 vixl32::Register in = InputRegisterAt(invoke, 0);
2753
2754 __ Rsb(temp, in, 0);
2755 __ And(out, temp, in);
2756 }
2757}
2758
2759void IntrinsicLocationsBuilderARMVIXL::VisitIntegerLowestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002760 CreateIntToIntLocations(allocator_, invoke);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002761}
2762
2763void IntrinsicCodeGeneratorARMVIXL::VisitIntegerLowestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002764 GenLowestOneBit(invoke, DataType::Type::kInt32, codegen_);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002765}
2766
2767void IntrinsicLocationsBuilderARMVIXL::VisitLongLowestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002768 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002769}
2770
2771void IntrinsicCodeGeneratorARMVIXL::VisitLongLowestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002772 GenLowestOneBit(invoke, DataType::Type::kInt64, codegen_);
Petre-Ionut Tudor27292e62017-08-04 16:06:45 +01002773}
2774
Anton Kirilov5ec62182016-10-13 20:16:02 +01002775void IntrinsicLocationsBuilderARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002776 LocationSummary* locations =
2777 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002778 locations->SetInAt(0, Location::RequiresRegister());
2779 locations->SetInAt(1, Location::RequiresRegister());
2780 locations->SetInAt(2, Location::RequiresRegister());
2781 locations->SetInAt(3, Location::RequiresRegister());
2782 locations->SetInAt(4, Location::RequiresRegister());
2783
2784 // Temporary registers to store lengths of strings and for calculations.
2785 locations->AddTemp(Location::RequiresRegister());
2786 locations->AddTemp(Location::RequiresRegister());
2787 locations->AddTemp(Location::RequiresRegister());
2788}
2789
2790void IntrinsicCodeGeneratorARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2791 ArmVIXLAssembler* assembler = GetAssembler();
2792 LocationSummary* locations = invoke->GetLocations();
2793
2794 // Check assumption that sizeof(Char) is 2 (used in scaling below).
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002795 const size_t char_size = DataType::Size(DataType::Type::kUint16);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002796 DCHECK_EQ(char_size, 2u);
2797
2798 // Location of data in char array buffer.
2799 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2800
2801 // Location of char array data in string.
2802 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2803
2804 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2805 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2806 vixl32::Register srcObj = InputRegisterAt(invoke, 0);
2807 vixl32::Register srcBegin = InputRegisterAt(invoke, 1);
2808 vixl32::Register srcEnd = InputRegisterAt(invoke, 2);
2809 vixl32::Register dstObj = InputRegisterAt(invoke, 3);
2810 vixl32::Register dstBegin = InputRegisterAt(invoke, 4);
2811
2812 vixl32::Register num_chr = RegisterFrom(locations->GetTemp(0));
2813 vixl32::Register src_ptr = RegisterFrom(locations->GetTemp(1));
2814 vixl32::Register dst_ptr = RegisterFrom(locations->GetTemp(2));
2815
2816 vixl32::Label done, compressed_string_loop;
Anton Kirilov6f644202017-02-27 18:29:45 +00002817 vixl32::Label* final_label = codegen_->GetFinalLabel(invoke, &done);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002818 // dst to be copied.
2819 __ Add(dst_ptr, dstObj, data_offset);
2820 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, vixl32::LSL, 1));
2821
2822 __ Subs(num_chr, srcEnd, srcBegin);
2823 // Early out for valid zero-length retrievals.
Anton Kirilov6f644202017-02-27 18:29:45 +00002824 __ B(eq, final_label, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002825
2826 // src range to copy.
2827 __ Add(src_ptr, srcObj, value_offset);
2828
2829 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2830 vixl32::Register temp;
2831 vixl32::Label compressed_string_preloop;
2832 if (mirror::kUseStringCompression) {
2833 // Location of count in string.
2834 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2835 temp = temps.Acquire();
2836 // String's length.
2837 __ Ldr(temp, MemOperand(srcObj, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002838 __ Tst(temp, 1);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002839 temps.Release(temp);
Artem Serov517d9f62016-12-12 15:51:15 +00002840 __ B(eq, &compressed_string_preloop, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002841 }
2842 __ Add(src_ptr, src_ptr, Operand(srcBegin, vixl32::LSL, 1));
2843
2844 // Do the copy.
2845 vixl32::Label loop, remainder;
2846
2847 temp = temps.Acquire();
2848 // Save repairing the value of num_chr on the < 4 character path.
2849 __ Subs(temp, num_chr, 4);
Artem Serov517d9f62016-12-12 15:51:15 +00002850 __ B(lt, &remainder, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002851
2852 // Keep the result of the earlier subs, we are going to fetch at least 4 characters.
2853 __ Mov(num_chr, temp);
2854
2855 // Main loop used for longer fetches loads and stores 4x16-bit characters at a time.
2856 // (LDRD/STRD fault on unaligned addresses and it's not worth inlining extra code
2857 // to rectify these everywhere this intrinsic applies.)
2858 __ Bind(&loop);
2859 __ Ldr(temp, MemOperand(src_ptr, char_size * 2));
2860 __ Subs(num_chr, num_chr, 4);
2861 __ Str(temp, MemOperand(dst_ptr, char_size * 2));
2862 __ Ldr(temp, MemOperand(src_ptr, char_size * 4, PostIndex));
2863 __ Str(temp, MemOperand(dst_ptr, char_size * 4, PostIndex));
2864 temps.Release(temp);
Artem Serov517d9f62016-12-12 15:51:15 +00002865 __ B(ge, &loop, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002866
2867 __ Adds(num_chr, num_chr, 4);
Anton Kirilov6f644202017-02-27 18:29:45 +00002868 __ B(eq, final_label, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002869
2870 // Main loop for < 4 character case and remainder handling. Loads and stores one
2871 // 16-bit Java character at a time.
2872 __ Bind(&remainder);
2873 temp = temps.Acquire();
2874 __ Ldrh(temp, MemOperand(src_ptr, char_size, PostIndex));
2875 __ Subs(num_chr, num_chr, 1);
2876 __ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex));
2877 temps.Release(temp);
Artem Serov517d9f62016-12-12 15:51:15 +00002878 __ B(gt, &remainder, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002879
2880 if (mirror::kUseStringCompression) {
Anton Kirilov6f644202017-02-27 18:29:45 +00002881 __ B(final_label);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002882
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002883 const size_t c_char_size = DataType::Size(DataType::Type::kInt8);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002884 DCHECK_EQ(c_char_size, 1u);
2885 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2886 __ Bind(&compressed_string_preloop);
2887 __ Add(src_ptr, src_ptr, srcBegin);
2888 __ Bind(&compressed_string_loop);
2889 temp = temps.Acquire();
2890 __ Ldrb(temp, MemOperand(src_ptr, c_char_size, PostIndex));
2891 __ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex));
2892 temps.Release(temp);
2893 __ Subs(num_chr, num_chr, 1);
Artem Serov517d9f62016-12-12 15:51:15 +00002894 __ B(gt, &compressed_string_loop, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002895 }
2896
Anton Kirilov6f644202017-02-27 18:29:45 +00002897 if (done.IsReferenced()) {
2898 __ Bind(&done);
2899 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01002900}
2901
2902void IntrinsicLocationsBuilderARMVIXL::VisitFloatIsInfinite(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002903 CreateFPToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002904}
2905
2906void IntrinsicCodeGeneratorARMVIXL::VisitFloatIsInfinite(HInvoke* invoke) {
2907 ArmVIXLAssembler* const assembler = GetAssembler();
2908 const vixl32::Register out = OutputRegister(invoke);
2909 // Shifting left by 1 bit makes the value encodable as an immediate operand;
2910 // we don't care about the sign bit anyway.
2911 constexpr uint32_t infinity = kPositiveInfinityFloat << 1U;
2912
2913 __ Vmov(out, InputSRegisterAt(invoke, 0));
2914 // We don't care about the sign bit, so shift left.
2915 __ Lsl(out, out, 1);
2916 __ Eor(out, out, infinity);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002917 codegen_->GenerateConditionWithZero(kCondEQ, out, out);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002918}
2919
2920void IntrinsicLocationsBuilderARMVIXL::VisitDoubleIsInfinite(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002921 CreateFPToIntLocations(allocator_, invoke);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002922}
2923
2924void IntrinsicCodeGeneratorARMVIXL::VisitDoubleIsInfinite(HInvoke* invoke) {
2925 ArmVIXLAssembler* const assembler = GetAssembler();
2926 const vixl32::Register out = OutputRegister(invoke);
2927 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2928 const vixl32::Register temp = temps.Acquire();
2929 // The highest 32 bits of double precision positive infinity separated into
2930 // two constants encodable as immediate operands.
2931 constexpr uint32_t infinity_high = 0x7f000000U;
2932 constexpr uint32_t infinity_high2 = 0x00f00000U;
2933
2934 static_assert((infinity_high | infinity_high2) ==
2935 static_cast<uint32_t>(kPositiveInfinityDouble >> 32U),
2936 "The constants do not add up to the high 32 bits of double "
2937 "precision positive infinity.");
2938 __ Vmov(temp, out, InputDRegisterAt(invoke, 0));
2939 __ Eor(out, out, infinity_high);
2940 __ Eor(out, out, infinity_high2);
2941 // We don't care about the sign bit, so shift left.
2942 __ Orr(out, temp, Operand(out, vixl32::LSL, 1));
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002943 codegen_->GenerateConditionWithZero(kCondEQ, out, out);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002944}
2945
Artem Serov9aee2d42017-01-06 15:58:31 +00002946void IntrinsicLocationsBuilderARMVIXL::VisitMathCeil(HInvoke* invoke) {
2947 if (features_.HasARMv8AInstructions()) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002948 CreateFPToFPLocations(allocator_, invoke);
Artem Serov9aee2d42017-01-06 15:58:31 +00002949 }
2950}
2951
2952void IntrinsicCodeGeneratorARMVIXL::VisitMathCeil(HInvoke* invoke) {
2953 ArmVIXLAssembler* assembler = GetAssembler();
2954 DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
2955 __ Vrintp(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
2956}
2957
2958void IntrinsicLocationsBuilderARMVIXL::VisitMathFloor(HInvoke* invoke) {
2959 if (features_.HasARMv8AInstructions()) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002960 CreateFPToFPLocations(allocator_, invoke);
Artem Serov9aee2d42017-01-06 15:58:31 +00002961 }
2962}
2963
2964void IntrinsicCodeGeneratorARMVIXL::VisitMathFloor(HInvoke* invoke) {
2965 ArmVIXLAssembler* assembler = GetAssembler();
2966 DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
2967 __ Vrintm(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
2968}
2969
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002970void IntrinsicLocationsBuilderARMVIXL::VisitIntegerValueOf(HInvoke* invoke) {
2971 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2972 IntrinsicVisitor::ComputeIntegerValueOfLocations(
2973 invoke,
2974 codegen_,
2975 LocationFrom(r0),
2976 LocationFrom(calling_convention.GetRegisterAt(0)));
2977}
2978
2979void IntrinsicCodeGeneratorARMVIXL::VisitIntegerValueOf(HInvoke* invoke) {
Vladimir Marko6fd16062018-06-26 11:02:04 +01002980 IntrinsicVisitor::IntegerValueOfInfo info =
2981 IntrinsicVisitor::ComputeIntegerValueOfInfo(invoke, codegen_->GetCompilerOptions());
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002982 LocationSummary* locations = invoke->GetLocations();
2983 ArmVIXLAssembler* const assembler = GetAssembler();
2984
2985 vixl32::Register out = RegisterFrom(locations->Out());
2986 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2987 vixl32::Register temp = temps.Acquire();
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002988 if (invoke->InputAt(0)->IsConstant()) {
2989 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
Vladimir Marko6fd16062018-06-26 11:02:04 +01002990 if (static_cast<uint32_t>(value - info.low) < info.length) {
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002991 // Just embed the j.l.Integer in the code.
Vladimir Marko6fd16062018-06-26 11:02:04 +01002992 DCHECK_NE(info.value_boot_image_reference, IntegerValueOfInfo::kInvalidReference);
2993 codegen_->LoadBootImageAddress(out, info.value_boot_image_reference);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002994 } else {
Vladimir Markoeebb8212018-06-05 14:57:24 +01002995 DCHECK(locations->CanCall());
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002996 // Allocate and initialize a new j.l.Integer.
2997 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
2998 // JIT object table.
Vladimir Marko6fd16062018-06-26 11:02:04 +01002999 codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(),
3000 info.integer_boot_image_offset);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003001 __ Mov(temp, value);
3002 assembler->StoreToOffset(kStoreWord, temp, out, info.value_offset);
3003 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
3004 // one.
3005 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3006 }
3007 } else {
Vladimir Markoeebb8212018-06-05 14:57:24 +01003008 DCHECK(locations->CanCall());
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003009 vixl32::Register in = RegisterFrom(locations->InAt(0));
3010 // Check bounds of our cache.
3011 __ Add(out, in, -info.low);
Vladimir Markoeebb8212018-06-05 14:57:24 +01003012 __ Cmp(out, info.length);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003013 vixl32::Label allocate, done;
Anton Kirilovfd522532017-05-10 12:46:57 +01003014 __ B(hs, &allocate, /* is_far_target */ false);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003015 // If the value is within the bounds, load the j.l.Integer directly from the array.
Vladimir Marko6fd16062018-06-26 11:02:04 +01003016 codegen_->LoadBootImageAddress(temp, info.array_data_boot_image_reference);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003017 codegen_->LoadFromShiftedRegOffset(DataType::Type::kReference, locations->Out(), temp, out);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003018 assembler->MaybeUnpoisonHeapReference(out);
3019 __ B(&done);
3020 __ Bind(&allocate);
3021 // Otherwise allocate and initialize a new j.l.Integer.
Vladimir Marko6fd16062018-06-26 11:02:04 +01003022 codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(),
3023 info.integer_boot_image_offset);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003024 assembler->StoreToOffset(kStoreWord, in, out, info.value_offset);
3025 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
3026 // one.
3027 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3028 __ Bind(&done);
3029 }
3030}
3031
Nicolas Geoffray365719c2017-03-08 13:11:50 +00003032void IntrinsicLocationsBuilderARMVIXL::VisitThreadInterrupted(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003033 LocationSummary* locations =
3034 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Nicolas Geoffray365719c2017-03-08 13:11:50 +00003035 locations->SetOut(Location::RequiresRegister());
3036}
3037
3038void IntrinsicCodeGeneratorARMVIXL::VisitThreadInterrupted(HInvoke* invoke) {
3039 ArmVIXLAssembler* assembler = GetAssembler();
3040 vixl32::Register out = RegisterFrom(invoke->GetLocations()->Out());
3041 int32_t offset = Thread::InterruptedOffset<kArmPointerSize>().Int32Value();
3042 __ Ldr(out, MemOperand(tr, offset));
3043 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
3044 vixl32::Register temp = temps.Acquire();
3045 vixl32::Label done;
Anton Kirilovfd522532017-05-10 12:46:57 +01003046 vixl32::Label* const final_label = codegen_->GetFinalLabel(invoke, &done);
3047 __ CompareAndBranchIfZero(out, final_label, /* far_target */ false);
Nicolas Geoffray365719c2017-03-08 13:11:50 +00003048 __ Dmb(vixl32::ISH);
3049 __ Mov(temp, 0);
3050 assembler->StoreToOffset(kStoreWord, temp, tr, offset);
3051 __ Dmb(vixl32::ISH);
Anton Kirilovfd522532017-05-10 12:46:57 +01003052 if (done.IsReferenced()) {
3053 __ Bind(&done);
3054 }
Nicolas Geoffray365719c2017-03-08 13:11:50 +00003055}
3056
Hans Boehmc7b28de2018-03-09 17:05:28 -08003057void IntrinsicLocationsBuilderARMVIXL::VisitReachabilityFence(HInvoke* invoke) {
3058 LocationSummary* locations =
3059 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
3060 locations->SetInAt(0, Location::Any());
3061}
3062
3063void IntrinsicCodeGeneratorARMVIXL::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
3064
Anton Kirilov5ec62182016-10-13 20:16:02 +01003065UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundDouble) // Could be done by changing rounding mode, maybe?
Anton Kirilov5ec62182016-10-13 20:16:02 +01003066UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong) // High register pressure.
3067UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
Vladimir Markod254f5c2017-06-02 15:18:36 +00003068UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent)
Anton Kirilov5ec62182016-10-13 20:16:02 +01003069
Aart Bikff7d89c2016-11-07 08:49:28 -08003070UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf);
3071UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08003072UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBufferAppend);
3073UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBufferLength);
3074UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBufferToString);
3075UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBuilderAppend);
3076UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBuilderLength);
3077UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08003078
Anton Kirilov5ec62182016-10-13 20:16:02 +01003079// 1.8.
3080UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndAddInt)
3081UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndAddLong)
3082UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndSetInt)
3083UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndSetLong)
3084UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndSetObject)
3085
3086UNREACHABLE_INTRINSICS(ARMVIXL)
3087
3088#undef __
3089
3090} // namespace arm
3091} // namespace art