1 /*
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"
20 #include "art_method.h"
21 #include "code_generator_arm_vixl.h"
22 #include "common_arm.h"
23 #include "heap_poisoning.h"
24 #include "lock_word.h"
25 #include "mirror/array-inl.h"
26 #include "mirror/object_array-inl.h"
27 #include "mirror/reference.h"
28 #include "mirror/string-inl.h"
29 #include "scoped_thread_state_change-inl.h"
30 #include "thread-current-inl.h"
31
32 #include "aarch32/constants-aarch32.h"
33
34 namespace art {
35 namespace arm {
36
37 #define __ assembler->GetVIXLAssembler()->
38
39 using helpers::DRegisterFrom;
40 using helpers::HighRegisterFrom;
41 using helpers::InputDRegisterAt;
42 using helpers::InputRegisterAt;
43 using helpers::InputSRegisterAt;
44 using helpers::Int32ConstantFrom;
45 using helpers::LocationFrom;
46 using helpers::LowRegisterFrom;
47 using helpers::LowSRegisterFrom;
48 using helpers::HighSRegisterFrom;
49 using helpers::OutputDRegister;
50 using helpers::OutputRegister;
51 using helpers::RegisterFrom;
52 using helpers::SRegisterFrom;
53
54 using namespace vixl::aarch32; // NOLINT(build/namespaces)
55
56 using vixl::ExactAssemblyScope;
57 using vixl::CodeBufferCheckScope;
58
GetAssembler()59 ArmVIXLAssembler* IntrinsicCodeGeneratorARMVIXL::GetAssembler() {
60 return codegen_->GetAssembler();
61 }
62
GetAllocator()63 ArenaAllocator* IntrinsicCodeGeneratorARMVIXL::GetAllocator() {
64 return codegen_->GetGraph()->GetAllocator();
65 }
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
77 class IntrinsicSlowPathARMVIXL : public SlowPathCodeARMVIXL {
78 public:
IntrinsicSlowPathARMVIXL(HInvoke * invoke)79 explicit IntrinsicSlowPathARMVIXL(HInvoke* invoke)
80 : SlowPathCodeARMVIXL(invoke), invoke_(invoke) {}
81
MoveArguments(CodeGenerator * codegen)82 Location MoveArguments(CodeGenerator* codegen) {
83 InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
84 IntrinsicVisitor::MoveArguments(invoke_, codegen, &calling_convention_visitor);
85 return calling_convention_visitor.GetMethodLocation();
86 }
87
EmitNativeCode(CodeGenerator * codegen)88 void EmitNativeCode(CodeGenerator* codegen) override {
89 ArmVIXLAssembler* assembler = down_cast<ArmVIXLAssembler*>(codegen->GetAssembler());
90 __ Bind(GetEntryLabel());
91
92 SaveLiveRegisters(codegen, invoke_->GetLocations());
93
94 Location method_loc = MoveArguments(codegen);
95
96 if (invoke_->IsInvokeStaticOrDirect()) {
97 codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), method_loc, this);
98 } else {
99 codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), method_loc, this);
100 }
101
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
GetDescription() const114 const char* GetDescription() const override { return "IntrinsicSlowPath"; }
115
116 private:
117 // The instruction where this slow path is happening.
118 HInvoke* const invoke_;
119
120 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARMVIXL);
121 };
122
123 // Compute base address for the System.arraycopy intrinsic in `base`.
GenSystemArrayCopyBaseAddress(ArmVIXLAssembler * assembler,DataType::Type type,const vixl32::Register & array,const Location & pos,const vixl32::Register & base)124 static void GenSystemArrayCopyBaseAddress(ArmVIXLAssembler* assembler,
125 DataType::Type type,
126 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
130 // moment. We can allow DataType::Type::kReference as `type` to implement
131 // the SystemArrayCopyChar intrinsic.
132 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);
135 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`.
GenSystemArrayCopyEndAddress(ArmVIXLAssembler * assembler,DataType::Type type,const Location & copy_length,const vixl32::Register & base,const vixl32::Register & end)147 static void GenSystemArrayCopyEndAddress(ArmVIXLAssembler* assembler,
148 DataType::Type type,
149 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
153 // moment. We can allow DataType::Type::kReference as `type` to implement
154 // the SystemArrayCopyChar intrinsic.
155 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);
158
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
167 // Slow path implementing the SystemArrayCopy intrinsic copy loop with read barriers.
168 class ReadBarrierSystemArrayCopySlowPathARMVIXL : public SlowPathCodeARMVIXL {
169 public:
ReadBarrierSystemArrayCopySlowPathARMVIXL(HInstruction * instruction)170 explicit ReadBarrierSystemArrayCopySlowPathARMVIXL(HInstruction* instruction)
171 : SlowPathCodeARMVIXL(instruction) {
172 DCHECK(kEmitCompilerReadBarrier);
173 DCHECK(kUseBakerReadBarrier);
174 }
175
EmitNativeCode(CodeGenerator * codegen)176 void EmitNativeCode(CodeGenerator* codegen) override {
177 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
178 ArmVIXLAssembler* assembler = arm_codegen->GetAssembler();
179 LocationSummary* locations = instruction_->GetLocations();
180 DCHECK(locations->CanCall());
181 DCHECK(instruction_->IsInvokeStaticOrDirect())
182 << "Unexpected instruction in read barrier arraycopy slow path: "
183 << instruction_->DebugName();
184 DCHECK(instruction_->GetLocations()->Intrinsified());
185 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kSystemArrayCopy);
186
187 DataType::Type type = DataType::Type::kReference;
188 const int32_t element_size = DataType::Size(type);
189
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`.
199 GenSystemArrayCopyBaseAddress(assembler, type, dest, dest_pos, dst_curr_addr);
200
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;
223 // TODO: Load the entrypoint once before the loop, instead of
224 // loading it at every iteration.
225 int32_t entry_point_offset =
226 Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(tmp.GetCode());
227 // 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);
232 __ B(ne, &loop, /* is_far_target= */ false);
233 __ B(GetExitLabel());
234 }
235
GetDescription() const236 const char* GetDescription() const override {
237 return "ReadBarrierSystemArrayCopySlowPathARMVIXL";
238 }
239
240 private:
241 DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathARMVIXL);
242 };
243
IntrinsicLocationsBuilderARMVIXL(CodeGeneratorARMVIXL * codegen)244 IntrinsicLocationsBuilderARMVIXL::IntrinsicLocationsBuilderARMVIXL(CodeGeneratorARMVIXL* codegen)
245 : allocator_(codegen->GetGraph()->GetAllocator()),
246 codegen_(codegen),
247 assembler_(codegen->GetAssembler()),
248 features_(codegen->GetInstructionSetFeatures()) {}
249
TryDispatch(HInvoke * invoke)250 bool 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
CreateFPToIntLocations(ArenaAllocator * allocator,HInvoke * invoke)259 static void CreateFPToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
260 LocationSummary* locations =
261 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
262 locations->SetInAt(0, Location::RequiresFpuRegister());
263 locations->SetOut(Location::RequiresRegister());
264 }
265
CreateIntToFPLocations(ArenaAllocator * allocator,HInvoke * invoke)266 static void CreateIntToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
267 LocationSummary* locations =
268 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
269 locations->SetInAt(0, Location::RequiresRegister());
270 locations->SetOut(Location::RequiresFpuRegister());
271 }
272
MoveFPToInt(LocationSummary * locations,bool is64bit,ArmVIXLAssembler * assembler)273 static 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
MoveIntToFP(LocationSummary * locations,bool is64bit,ArmVIXLAssembler * assembler)283 static 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
VisitDoubleDoubleToRawLongBits(HInvoke * invoke)293 void IntrinsicLocationsBuilderARMVIXL::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
294 CreateFPToIntLocations(allocator_, invoke);
295 }
VisitDoubleLongBitsToDouble(HInvoke * invoke)296 void IntrinsicLocationsBuilderARMVIXL::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
297 CreateIntToFPLocations(allocator_, invoke);
298 }
299
VisitDoubleDoubleToRawLongBits(HInvoke * invoke)300 void IntrinsicCodeGeneratorARMVIXL::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
301 MoveFPToInt(invoke->GetLocations(), /* is64bit= */ true, GetAssembler());
302 }
VisitDoubleLongBitsToDouble(HInvoke * invoke)303 void IntrinsicCodeGeneratorARMVIXL::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
304 MoveIntToFP(invoke->GetLocations(), /* is64bit= */ true, GetAssembler());
305 }
306
VisitFloatFloatToRawIntBits(HInvoke * invoke)307 void IntrinsicLocationsBuilderARMVIXL::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
308 CreateFPToIntLocations(allocator_, invoke);
309 }
VisitFloatIntBitsToFloat(HInvoke * invoke)310 void IntrinsicLocationsBuilderARMVIXL::VisitFloatIntBitsToFloat(HInvoke* invoke) {
311 CreateIntToFPLocations(allocator_, invoke);
312 }
313
VisitFloatFloatToRawIntBits(HInvoke * invoke)314 void IntrinsicCodeGeneratorARMVIXL::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
315 MoveFPToInt(invoke->GetLocations(), /* is64bit= */ false, GetAssembler());
316 }
VisitFloatIntBitsToFloat(HInvoke * invoke)317 void IntrinsicCodeGeneratorARMVIXL::VisitFloatIntBitsToFloat(HInvoke* invoke) {
318 MoveIntToFP(invoke->GetLocations(), /* is64bit= */ false, GetAssembler());
319 }
320
CreateIntToIntLocations(ArenaAllocator * allocator,HInvoke * invoke)321 static void CreateIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
322 LocationSummary* locations =
323 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
324 locations->SetInAt(0, Location::RequiresRegister());
325 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
326 }
327
CreateLongToLongLocationsWithOverlap(ArenaAllocator * allocator,HInvoke * invoke)328 static void CreateLongToLongLocationsWithOverlap(ArenaAllocator* allocator, HInvoke* invoke) {
329 LocationSummary* locations =
330 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
331 locations->SetInAt(0, Location::RequiresRegister());
332 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
333 }
334
CreateFPToFPLocations(ArenaAllocator * allocator,HInvoke * invoke)335 static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
336 LocationSummary* locations =
337 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
338 locations->SetInAt(0, Location::RequiresFpuRegister());
339 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
340 }
341
GenNumberOfLeadingZeros(HInvoke * invoke,DataType::Type type,CodeGeneratorARMVIXL * codegen)342 static void GenNumberOfLeadingZeros(HInvoke* invoke,
343 DataType::Type type,
344 CodeGeneratorARMVIXL* codegen) {
345 ArmVIXLAssembler* assembler = codegen->GetAssembler();
346 LocationSummary* locations = invoke->GetLocations();
347 Location in = locations->InAt(0);
348 vixl32::Register out = RegisterFrom(locations->Out());
349
350 DCHECK((type == DataType::Type::kInt32) || (type == DataType::Type::kInt64));
351
352 if (type == DataType::Type::kInt64) {
353 vixl32::Register in_reg_lo = LowRegisterFrom(in);
354 vixl32::Register in_reg_hi = HighRegisterFrom(in);
355 vixl32::Label end;
356 vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &end);
357 __ Clz(out, in_reg_hi);
358 __ CompareAndBranchIfNonZero(in_reg_hi, final_label, /* is_far_target= */ false);
359 __ Clz(out, in_reg_lo);
360 __ Add(out, out, 32);
361 if (end.IsReferenced()) {
362 __ Bind(&end);
363 }
364 } else {
365 __ Clz(out, RegisterFrom(in));
366 }
367 }
368
VisitIntegerNumberOfLeadingZeros(HInvoke * invoke)369 void IntrinsicLocationsBuilderARMVIXL::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
370 CreateIntToIntLocations(allocator_, invoke);
371 }
372
VisitIntegerNumberOfLeadingZeros(HInvoke * invoke)373 void IntrinsicCodeGeneratorARMVIXL::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
374 GenNumberOfLeadingZeros(invoke, DataType::Type::kInt32, codegen_);
375 }
376
VisitLongNumberOfLeadingZeros(HInvoke * invoke)377 void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
378 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
379 }
380
VisitLongNumberOfLeadingZeros(HInvoke * invoke)381 void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
382 GenNumberOfLeadingZeros(invoke, DataType::Type::kInt64, codegen_);
383 }
384
GenNumberOfTrailingZeros(HInvoke * invoke,DataType::Type type,CodeGeneratorARMVIXL * codegen)385 static void GenNumberOfTrailingZeros(HInvoke* invoke,
386 DataType::Type type,
387 CodeGeneratorARMVIXL* codegen) {
388 DCHECK((type == DataType::Type::kInt32) || (type == DataType::Type::kInt64));
389
390 ArmVIXLAssembler* assembler = codegen->GetAssembler();
391 LocationSummary* locations = invoke->GetLocations();
392 vixl32::Register out = RegisterFrom(locations->Out());
393
394 if (type == DataType::Type::kInt64) {
395 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
396 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
397 vixl32::Label end;
398 vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &end);
399 __ Rbit(out, in_reg_lo);
400 __ Clz(out, out);
401 __ CompareAndBranchIfNonZero(in_reg_lo, final_label, /* is_far_target= */ false);
402 __ Rbit(out, in_reg_hi);
403 __ Clz(out, out);
404 __ Add(out, out, 32);
405 if (end.IsReferenced()) {
406 __ Bind(&end);
407 }
408 } else {
409 vixl32::Register in = RegisterFrom(locations->InAt(0));
410 __ Rbit(out, in);
411 __ Clz(out, out);
412 }
413 }
414
VisitIntegerNumberOfTrailingZeros(HInvoke * invoke)415 void IntrinsicLocationsBuilderARMVIXL::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
416 CreateIntToIntLocations(allocator_, invoke);
417 }
418
VisitIntegerNumberOfTrailingZeros(HInvoke * invoke)419 void IntrinsicCodeGeneratorARMVIXL::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
420 GenNumberOfTrailingZeros(invoke, DataType::Type::kInt32, codegen_);
421 }
422
VisitLongNumberOfTrailingZeros(HInvoke * invoke)423 void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
424 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
425 }
426
VisitLongNumberOfTrailingZeros(HInvoke * invoke)427 void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
428 GenNumberOfTrailingZeros(invoke, DataType::Type::kInt64, codegen_);
429 }
430
VisitMathSqrt(HInvoke * invoke)431 void IntrinsicLocationsBuilderARMVIXL::VisitMathSqrt(HInvoke* invoke) {
432 CreateFPToFPLocations(allocator_, invoke);
433 }
434
VisitMathSqrt(HInvoke * invoke)435 void IntrinsicCodeGeneratorARMVIXL::VisitMathSqrt(HInvoke* invoke) {
436 ArmVIXLAssembler* assembler = GetAssembler();
437 __ Vsqrt(OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
438 }
439
VisitMathRint(HInvoke * invoke)440 void IntrinsicLocationsBuilderARMVIXL::VisitMathRint(HInvoke* invoke) {
441 if (features_.HasARMv8AInstructions()) {
442 CreateFPToFPLocations(allocator_, invoke);
443 }
444 }
445
VisitMathRint(HInvoke * invoke)446 void IntrinsicCodeGeneratorARMVIXL::VisitMathRint(HInvoke* invoke) {
447 DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
448 ArmVIXLAssembler* assembler = GetAssembler();
449 __ Vrintn(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
450 }
451
VisitMathRoundFloat(HInvoke * invoke)452 void IntrinsicLocationsBuilderARMVIXL::VisitMathRoundFloat(HInvoke* invoke) {
453 if (features_.HasARMv8AInstructions()) {
454 LocationSummary* locations =
455 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
456 locations->SetInAt(0, Location::RequiresFpuRegister());
457 locations->SetOut(Location::RequiresRegister());
458 locations->AddTemp(Location::RequiresFpuRegister());
459 }
460 }
461
VisitMathRoundFloat(HInvoke * invoke)462 void 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, /* is_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, 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
VisitMemoryPeekByte(HInvoke * invoke)503 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) {
504 CreateIntToIntLocations(allocator_, invoke);
505 }
506
VisitMemoryPeekByte(HInvoke * invoke)507 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) {
508 ArmVIXLAssembler* assembler = GetAssembler();
509 // Ignore upper 4B of long address.
510 __ Ldrsb(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
511 }
512
VisitMemoryPeekIntNative(HInvoke * invoke)513 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) {
514 CreateIntToIntLocations(allocator_, invoke);
515 }
516
VisitMemoryPeekIntNative(HInvoke * invoke)517 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) {
518 ArmVIXLAssembler* assembler = GetAssembler();
519 // Ignore upper 4B of long address.
520 __ Ldr(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
521 }
522
VisitMemoryPeekLongNative(HInvoke * invoke)523 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekLongNative(HInvoke* invoke) {
524 CreateIntToIntLocations(allocator_, invoke);
525 }
526
VisitMemoryPeekLongNative(HInvoke * invoke)527 void 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));
537 __ Ldr(lo, MemOperand(addr));
538 } else {
539 __ Ldr(lo, MemOperand(addr));
540 __ Ldr(hi, MemOperand(addr, 4));
541 }
542 }
543
VisitMemoryPeekShortNative(HInvoke * invoke)544 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekShortNative(HInvoke* invoke) {
545 CreateIntToIntLocations(allocator_, invoke);
546 }
547
VisitMemoryPeekShortNative(HInvoke * invoke)548 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekShortNative(HInvoke* invoke) {
549 ArmVIXLAssembler* assembler = GetAssembler();
550 // Ignore upper 4B of long address.
551 __ Ldrsh(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
552 }
553
CreateIntIntToVoidLocations(ArenaAllocator * allocator,HInvoke * invoke)554 static void CreateIntIntToVoidLocations(ArenaAllocator* allocator, HInvoke* invoke) {
555 LocationSummary* locations =
556 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
557 locations->SetInAt(0, Location::RequiresRegister());
558 locations->SetInAt(1, Location::RequiresRegister());
559 }
560
VisitMemoryPokeByte(HInvoke * invoke)561 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeByte(HInvoke* invoke) {
562 CreateIntIntToVoidLocations(allocator_, invoke);
563 }
564
VisitMemoryPokeByte(HInvoke * invoke)565 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeByte(HInvoke* invoke) {
566 ArmVIXLAssembler* assembler = GetAssembler();
567 __ Strb(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
568 }
569
VisitMemoryPokeIntNative(HInvoke * invoke)570 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) {
571 CreateIntIntToVoidLocations(allocator_, invoke);
572 }
573
VisitMemoryPokeIntNative(HInvoke * invoke)574 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) {
575 ArmVIXLAssembler* assembler = GetAssembler();
576 __ Str(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
577 }
578
VisitMemoryPokeLongNative(HInvoke * invoke)579 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeLongNative(HInvoke* invoke) {
580 CreateIntIntToVoidLocations(allocator_, invoke);
581 }
582
VisitMemoryPokeLongNative(HInvoke * invoke)583 void 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.
589 __ Str(LowRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr));
590 __ Str(HighRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr, 4));
591 }
592
VisitMemoryPokeShortNative(HInvoke * invoke)593 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeShortNative(HInvoke* invoke) {
594 CreateIntIntToVoidLocations(allocator_, invoke);
595 }
596
VisitMemoryPokeShortNative(HInvoke * invoke)597 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeShortNative(HInvoke* invoke) {
598 ArmVIXLAssembler* assembler = GetAssembler();
599 __ Strh(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
600 }
601
VisitThreadCurrentThread(HInvoke * invoke)602 void IntrinsicLocationsBuilderARMVIXL::VisitThreadCurrentThread(HInvoke* invoke) {
603 LocationSummary* locations =
604 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
605 locations->SetOut(Location::RequiresRegister());
606 }
607
VisitThreadCurrentThread(HInvoke * invoke)608 void IntrinsicCodeGeneratorARMVIXL::VisitThreadCurrentThread(HInvoke* invoke) {
609 ArmVIXLAssembler* assembler = GetAssembler();
610 __ Ldr(OutputRegister(invoke),
611 MemOperand(tr, Thread::PeerOffset<kArmPointerSize>().Int32Value()));
612 }
613
GenUnsafeGet(HInvoke * invoke,DataType::Type type,bool is_volatile,CodeGeneratorARMVIXL * codegen)614 static void GenUnsafeGet(HInvoke* invoke,
615 DataType::Type type,
616 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) {
627 case DataType::Type::kInt32: {
628 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
636 case DataType::Type::kReference: {
637 vixl32::Register trg = RegisterFrom(trg_loc);
638 if (kEmitCompilerReadBarrier) {
639 if (kUseBakerReadBarrier) {
640 Location temp = locations->GetTemp(0);
641 // 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);
646 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
666 case DataType::Type::kInt64: {
667 vixl32::Register trg_lo = LowRegisterFrom(trg_loc);
668 vixl32::Register trg_hi = HighRegisterFrom(trg_loc);
669 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
670 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));
674 } 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
CreateIntIntIntToIntLocations(ArenaAllocator * allocator,HInvoke * invoke,DataType::Type type)689 static void CreateIntIntIntToIntLocations(ArenaAllocator* allocator,
690 HInvoke* invoke,
691 DataType::Type type) {
692 bool can_call = kEmitCompilerReadBarrier &&
693 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
694 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
695 LocationSummary* locations =
696 new (allocator) LocationSummary(invoke,
697 can_call
698 ? LocationSummary::kCallOnSlowPath
699 : LocationSummary::kNoCall,
700 kIntrinsified);
701 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));
709 if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
710 // We need a temporary register for the read barrier marking slow
711 // path in CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier.
712 locations->AddTemp(Location::RequiresRegister());
713 }
714 }
715
VisitUnsafeGet(HInvoke * invoke)716 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGet(HInvoke* invoke) {
717 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt32);
718 }
VisitUnsafeGetVolatile(HInvoke * invoke)719 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetVolatile(HInvoke* invoke) {
720 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt32);
721 }
VisitUnsafeGetLong(HInvoke * invoke)722 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetLong(HInvoke* invoke) {
723 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt64);
724 }
VisitUnsafeGetLongVolatile(HInvoke * invoke)725 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
726 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt64);
727 }
VisitUnsafeGetObject(HInvoke * invoke)728 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetObject(HInvoke* invoke) {
729 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kReference);
730 }
VisitUnsafeGetObjectVolatile(HInvoke * invoke)731 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
732 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kReference);
733 }
734
VisitUnsafeGet(HInvoke * invoke)735 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGet(HInvoke* invoke) {
736 GenUnsafeGet(invoke, DataType::Type::kInt32, /* is_volatile= */ false, codegen_);
737 }
VisitUnsafeGetVolatile(HInvoke * invoke)738 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetVolatile(HInvoke* invoke) {
739 GenUnsafeGet(invoke, DataType::Type::kInt32, /* is_volatile= */ true, codegen_);
740 }
VisitUnsafeGetLong(HInvoke * invoke)741 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetLong(HInvoke* invoke) {
742 GenUnsafeGet(invoke, DataType::Type::kInt64, /* is_volatile= */ false, codegen_);
743 }
VisitUnsafeGetLongVolatile(HInvoke * invoke)744 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
745 GenUnsafeGet(invoke, DataType::Type::kInt64, /* is_volatile= */ true, codegen_);
746 }
VisitUnsafeGetObject(HInvoke * invoke)747 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetObject(HInvoke* invoke) {
748 GenUnsafeGet(invoke, DataType::Type::kReference, /* is_volatile= */ false, codegen_);
749 }
VisitUnsafeGetObjectVolatile(HInvoke * invoke)750 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
751 GenUnsafeGet(invoke, DataType::Type::kReference, /* is_volatile= */ true, codegen_);
752 }
753
CreateIntIntIntIntToVoid(ArenaAllocator * allocator,const ArmInstructionSetFeatures & features,DataType::Type type,bool is_volatile,HInvoke * invoke)754 static void CreateIntIntIntIntToVoid(ArenaAllocator* allocator,
755 const ArmInstructionSetFeatures& features,
756 DataType::Type type,
757 bool is_volatile,
758 HInvoke* invoke) {
759 LocationSummary* locations =
760 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
761 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
766 if (type == DataType::Type::kInt64) {
767 // 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 }
772 } else if (type == DataType::Type::kReference) {
773 // Temps for card-marking.
774 locations->AddTemp(Location::RequiresRegister()); // Temp.
775 locations->AddTemp(Location::RequiresRegister()); // Card.
776 }
777 }
778
VisitUnsafePut(HInvoke * invoke)779 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePut(HInvoke* invoke) {
780 CreateIntIntIntIntToVoid(
781 allocator_, features_, DataType::Type::kInt32, /* is_volatile= */ false, invoke);
782 }
VisitUnsafePutOrdered(HInvoke * invoke)783 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutOrdered(HInvoke* invoke) {
784 CreateIntIntIntIntToVoid(
785 allocator_, features_, DataType::Type::kInt32, /* is_volatile= */ false, invoke);
786 }
VisitUnsafePutVolatile(HInvoke * invoke)787 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutVolatile(HInvoke* invoke) {
788 CreateIntIntIntIntToVoid(
789 allocator_, features_, DataType::Type::kInt32, /* is_volatile= */ true, invoke);
790 }
VisitUnsafePutObject(HInvoke * invoke)791 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutObject(HInvoke* invoke) {
792 CreateIntIntIntIntToVoid(
793 allocator_, features_, DataType::Type::kReference, /* is_volatile= */ false, invoke);
794 }
VisitUnsafePutObjectOrdered(HInvoke * invoke)795 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
796 CreateIntIntIntIntToVoid(
797 allocator_, features_, DataType::Type::kReference, /* is_volatile= */ false, invoke);
798 }
VisitUnsafePutObjectVolatile(HInvoke * invoke)799 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
800 CreateIntIntIntIntToVoid(
801 allocator_, features_, DataType::Type::kReference, /* is_volatile= */ true, invoke);
802 }
VisitUnsafePutLong(HInvoke * invoke)803 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutLong(HInvoke* invoke) {
804 CreateIntIntIntIntToVoid(
805 allocator_, features_, DataType::Type::kInt64, /* is_volatile= */ false, invoke);
806 }
VisitUnsafePutLongOrdered(HInvoke * invoke)807 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutLongOrdered(HInvoke* invoke) {
808 CreateIntIntIntIntToVoid(
809 allocator_, features_, DataType::Type::kInt64, /* is_volatile= */ false, invoke);
810 }
VisitUnsafePutLongVolatile(HInvoke * invoke)811 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutLongVolatile(HInvoke* invoke) {
812 CreateIntIntIntIntToVoid(
813 allocator_, features_, DataType::Type::kInt64, /* is_volatile= */ true, invoke);
814 }
815
GenUnsafePut(LocationSummary * locations,DataType::Type type,bool is_volatile,bool is_ordered,CodeGeneratorARMVIXL * codegen)816 static void GenUnsafePut(LocationSummary* locations,
817 DataType::Type type,
818 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
831 if (type == DataType::Type::kInt64) {
832 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);
844 __ Ldrexd(temp_lo, temp_hi, MemOperand(temp_reg));
845 __ Strexd(temp_lo, value_lo, value_hi, MemOperand(temp_reg));
846 __ Cmp(temp_lo, 0);
847 __ B(ne, &loop_head, /* is_far_target= */ false);
848 } else {
849 __ Strd(value_lo, value_hi, MemOperand(base, offset));
850 }
851 } else {
852 value = RegisterFrom(locations->InAt(3));
853 vixl32::Register source = value;
854 if (kPoisonHeapReferences && type == DataType::Type::kReference) {
855 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
867 if (type == DataType::Type::kReference) {
868 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
VisitUnsafePut(HInvoke * invoke)875 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePut(HInvoke* invoke) {
876 GenUnsafePut(invoke->GetLocations(),
877 DataType::Type::kInt32,
878 /* is_volatile= */ false,
879 /* is_ordered= */ false,
880 codegen_);
881 }
VisitUnsafePutOrdered(HInvoke * invoke)882 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutOrdered(HInvoke* invoke) {
883 GenUnsafePut(invoke->GetLocations(),
884 DataType::Type::kInt32,
885 /* is_volatile= */ false,
886 /* is_ordered= */ true,
887 codegen_);
888 }
VisitUnsafePutVolatile(HInvoke * invoke)889 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutVolatile(HInvoke* invoke) {
890 GenUnsafePut(invoke->GetLocations(),
891 DataType::Type::kInt32,
892 /* is_volatile= */ true,
893 /* is_ordered= */ false,
894 codegen_);
895 }
VisitUnsafePutObject(HInvoke * invoke)896 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutObject(HInvoke* invoke) {
897 GenUnsafePut(invoke->GetLocations(),
898 DataType::Type::kReference,
899 /* is_volatile= */ false,
900 /* is_ordered= */ false,
901 codegen_);
902 }
VisitUnsafePutObjectOrdered(HInvoke * invoke)903 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
904 GenUnsafePut(invoke->GetLocations(),
905 DataType::Type::kReference,
906 /* is_volatile= */ false,
907 /* is_ordered= */ true,
908 codegen_);
909 }
VisitUnsafePutObjectVolatile(HInvoke * invoke)910 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
911 GenUnsafePut(invoke->GetLocations(),
912 DataType::Type::kReference,
913 /* is_volatile= */ true,
914 /* is_ordered= */ false,
915 codegen_);
916 }
VisitUnsafePutLong(HInvoke * invoke)917 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutLong(HInvoke* invoke) {
918 GenUnsafePut(invoke->GetLocations(),
919 DataType::Type::kInt64,
920 /* is_volatile= */ false,
921 /* is_ordered= */ false,
922 codegen_);
923 }
VisitUnsafePutLongOrdered(HInvoke * invoke)924 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutLongOrdered(HInvoke* invoke) {
925 GenUnsafePut(invoke->GetLocations(),
926 DataType::Type::kInt64,
927 /* is_volatile= */ false,
928 /* is_ordered= */ true,
929 codegen_);
930 }
VisitUnsafePutLongVolatile(HInvoke * invoke)931 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutLongVolatile(HInvoke* invoke) {
932 GenUnsafePut(invoke->GetLocations(),
933 DataType::Type::kInt64,
934 /* is_volatile= */ true,
935 /* is_ordered= */ false,
936 codegen_);
937 }
938
CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator * allocator,HInvoke * invoke)939 static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* allocator, HInvoke* invoke) {
940 bool can_call = kEmitCompilerReadBarrier &&
941 kUseBakerReadBarrier &&
942 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
943 LocationSummary* locations =
944 new (allocator) LocationSummary(invoke,
945 can_call
946 ? LocationSummary::kCallOnSlowPath
947 : LocationSummary::kNoCall,
948 kIntrinsified);
949 if (can_call) {
950 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
951 }
952 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
953 locations->SetInAt(1, Location::RequiresRegister());
954 locations->SetInAt(2, Location::RequiresRegister());
955 locations->SetInAt(3, Location::RequiresRegister());
956 locations->SetInAt(4, Location::RequiresRegister());
957
958 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
959
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
967 class BakerReadBarrierCasSlowPathARMVIXL : public SlowPathCodeARMVIXL {
968 public:
BakerReadBarrierCasSlowPathARMVIXL(HInvoke * invoke)969 explicit BakerReadBarrierCasSlowPathARMVIXL(HInvoke* invoke)
970 : SlowPathCodeARMVIXL(invoke) {}
971
GetDescription() const972 const char* GetDescription() const override { return "BakerReadBarrierCasSlowPathARMVIXL"; }
973
EmitNativeCode(CodeGenerator * codegen)974 void EmitNativeCode(CodeGenerator* codegen) override {
975 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, /* is_far_target= */ false);
1030 __ B(GetExitLabel());
1031 }
1032 };
1033
GenCas(HInvoke * invoke,DataType::Type type,CodeGeneratorARMVIXL * codegen)1034 static void GenCas(HInvoke* invoke, DataType::Type type, CodeGeneratorARMVIXL* codegen) {
1035 DCHECK_NE(type, DataType::Type::kInt64);
1036
1037 ArmVIXLAssembler* assembler = codegen->GetAssembler();
1038 LocationSummary* locations = invoke->GetLocations();
1039
1040 vixl32::Register out = OutputRegister(invoke); // Boolean result.
1041
1042 vixl32::Register base = InputRegisterAt(invoke, 1); // Object pointer.
1043 vixl32::Register offset = LowRegisterFrom(locations->InAt(2)); // Offset (discard high 4B).
1044 vixl32::Register expected = InputRegisterAt(invoke, 3); // Expected.
1045 vixl32::Register value = InputRegisterAt(invoke, 4); // Value.
1046
1047 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;
1053
1054 if (type == DataType::Type::kReference) {
1055 // 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) {
1065 // 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();
1072 }
1073 }
1074
1075 // Prevent reordering with prior memory operations.
1076 // Emit a DMB ISH instruction instead of an DMB ISHST one, as the
1077 // latter allows a preceding load to be delayed past the STREX
1078 // instruction below.
1079 __ Dmb(vixl32::ISH);
1080
1081 __ Add(tmp_ptr, base, offset);
1082
1083 // do {
1084 // tmp = [r_ptr] - expected;
1085 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
1086 // result = tmp == 0;
1087
1088 vixl32::Label loop_head;
1089 __ Bind(&loop_head);
1090 __ Ldrex(tmp, MemOperand(tmp_ptr));
1091 if (type == DataType::Type::kReference) {
1092 assembler->MaybeUnpoisonHeapReference(tmp);
1093 }
1094 __ Subs(tmp, tmp, expected);
1095 static_cast<vixl32::MacroAssembler*>(assembler->GetVIXLAssembler())->
1096 B(ne, failure, /* hint= */ (failure == loop_exit) ? kNear : kBranchWithoutHint);
1097 if (type == DataType::Type::kReference) {
1098 assembler->MaybePoisonHeapReference(value);
1099 }
1100 __ Strex(tmp, value, MemOperand(tmp_ptr));
1101 if (type == DataType::Type::kReference) {
1102 assembler->MaybeUnpoisonHeapReference(value);
1103 }
1104 __ Cmp(tmp, 0);
1105 __ B(ne, &loop_head, /* is_far_target= */ false);
1106
1107 __ Bind(loop_exit);
1108
1109 __ Dmb(vixl32::ISH);
1110
1111 // out = tmp == 0.
1112 __ Clz(out, tmp);
1113 __ Lsr(out, out, WhichPowerOf2(out.GetSizeInBits()));
1114
1115 if (type == DataType::Type::kReference) {
1116 codegen->MaybeGenerateMarkingRegisterCheck(/* code= */ 128);
1117 }
1118 }
1119
VisitUnsafeCASInt(HInvoke * invoke)1120 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeCASInt(HInvoke* invoke) {
1121 CreateIntIntIntIntIntToIntPlusTemps(allocator_, invoke);
1122 }
VisitUnsafeCASObject(HInvoke * invoke)1123 void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeCASObject(HInvoke* invoke) {
1124 // The only read barrier implementation supporting the
1125 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1126 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
1127 return;
1128 }
1129
1130 CreateIntIntIntIntIntToIntPlusTemps(allocator_, invoke);
1131 }
VisitUnsafeCASInt(HInvoke * invoke)1132 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeCASInt(HInvoke* invoke) {
1133 GenCas(invoke, DataType::Type::kInt32, codegen_);
1134 }
VisitUnsafeCASObject(HInvoke * invoke)1135 void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeCASObject(HInvoke* invoke) {
1136 // The only read barrier implementation supporting the
1137 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1138 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1139
1140 GenCas(invoke, DataType::Type::kReference, codegen_);
1141 }
1142
VisitStringCompareTo(HInvoke * invoke)1143 void IntrinsicLocationsBuilderARMVIXL::VisitStringCompareTo(HInvoke* invoke) {
1144 // The inputs plus one temp.
1145 LocationSummary* locations =
1146 new (allocator_) LocationSummary(invoke,
1147 invoke->InputAt(1)->CanBeNull()
1148 ? LocationSummary::kCallOnSlowPath
1149 : LocationSummary::kNoCall,
1150 kIntrinsified);
1151 locations->SetInAt(0, Location::RequiresRegister());
1152 locations->SetInAt(1, Location::RequiresRegister());
1153 locations->AddTemp(Location::RequiresRegister());
1154 locations->AddTemp(Location::RequiresRegister());
1155 locations->AddTemp(Location::RequiresRegister());
1156 // Need temporary registers for String compression's feature.
1157 if (mirror::kUseStringCompression) {
1158 locations->AddTemp(Location::RequiresRegister());
1159 }
1160 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1161 }
1162
1163 // Forward declaration.
1164 //
1165 // ART build system imposes a size limit (deviceFrameSizeLimit) on the stack frames generated
1166 // by the compiler for every C++ function, and if this function gets inlined in
1167 // IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo, the limit will be exceeded, resulting in a
1168 // build failure. That is the reason why NO_INLINE attribute is used.
1169 static void NO_INLINE GenerateStringCompareToLoop(ArmVIXLAssembler* assembler,
1170 HInvoke* invoke,
1171 vixl32::Label* end,
1172 vixl32::Label* different_compression);
1173
VisitStringCompareTo(HInvoke * invoke)1174 void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) {
1175 ArmVIXLAssembler* assembler = GetAssembler();
1176 LocationSummary* locations = invoke->GetLocations();
1177
1178 const vixl32::Register str = InputRegisterAt(invoke, 0);
1179 const vixl32::Register arg = InputRegisterAt(invoke, 1);
1180 const vixl32::Register out = OutputRegister(invoke);
1181
1182 const vixl32::Register temp0 = RegisterFrom(locations->GetTemp(0));
1183 const vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1184 const vixl32::Register temp2 = RegisterFrom(locations->GetTemp(2));
1185 vixl32::Register temp3;
1186 if (mirror::kUseStringCompression) {
1187 temp3 = RegisterFrom(locations->GetTemp(3));
1188 }
1189
1190 vixl32::Label end;
1191 vixl32::Label different_compression;
1192
1193 // Get offsets of count and value fields within a string object.
1194 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1195
1196 // Note that the null check must have been done earlier.
1197 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1198
1199 // Take slow path and throw if input can be and is null.
1200 SlowPathCodeARMVIXL* slow_path = nullptr;
1201 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1202 if (can_slow_path) {
1203 slow_path = new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
1204 codegen_->AddSlowPath(slow_path);
1205 __ CompareAndBranchIfZero(arg, slow_path->GetEntryLabel());
1206 }
1207
1208 // Reference equality check, return 0 if same reference.
1209 __ Subs(out, str, arg);
1210 __ B(eq, &end);
1211
1212 if (mirror::kUseStringCompression) {
1213 // Load `count` fields of this and argument strings.
1214 __ Ldr(temp3, MemOperand(str, count_offset));
1215 __ Ldr(temp2, MemOperand(arg, count_offset));
1216 // Extract lengths from the `count` fields.
1217 __ Lsr(temp0, temp3, 1u);
1218 __ Lsr(temp1, temp2, 1u);
1219 } else {
1220 // Load lengths of this and argument strings.
1221 __ Ldr(temp0, MemOperand(str, count_offset));
1222 __ Ldr(temp1, MemOperand(arg, count_offset));
1223 }
1224 // out = length diff.
1225 __ Subs(out, temp0, temp1);
1226 // temp0 = min(len(str), len(arg)).
1227
1228 {
1229 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1230 2 * kMaxInstructionSizeInBytes,
1231 CodeBufferCheckScope::kMaximumSize);
1232
1233 __ it(gt);
1234 __ mov(gt, temp0, temp1);
1235 }
1236
1237 // Shorter string is empty?
1238 // Note that mirror::kUseStringCompression==true introduces lots of instructions,
1239 // which makes &end label far away from this branch and makes it not 'CBZ-encodable'.
1240 __ CompareAndBranchIfZero(temp0, &end, mirror::kUseStringCompression);
1241
1242 if (mirror::kUseStringCompression) {
1243 // Check if both strings using same compression style to use this comparison loop.
1244 __ Eors(temp2, temp2, temp3);
1245 __ Lsrs(temp2, temp2, 1u);
1246 __ B(cs, &different_compression);
1247 // For string compression, calculate the number of bytes to compare (not chars).
1248 // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
1249 __ Lsls(temp3, temp3, 31u); // Extract purely the compression flag.
1250
1251 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1252 2 * kMaxInstructionSizeInBytes,
1253 CodeBufferCheckScope::kMaximumSize);
1254
1255 __ it(ne);
1256 __ add(ne, temp0, temp0, temp0);
1257 }
1258
1259
1260 GenerateStringCompareToLoop(assembler, invoke, &end, &different_compression);
1261
1262 __ Bind(&end);
1263
1264 if (can_slow_path) {
1265 __ Bind(slow_path->GetExitLabel());
1266 }
1267 }
1268
GenerateStringCompareToLoop(ArmVIXLAssembler * assembler,HInvoke * invoke,vixl32::Label * end,vixl32::Label * different_compression)1269 static void GenerateStringCompareToLoop(ArmVIXLAssembler* assembler,
1270 HInvoke* invoke,
1271 vixl32::Label* end,
1272 vixl32::Label* different_compression) {
1273 LocationSummary* locations = invoke->GetLocations();
1274
1275 const vixl32::Register str = InputRegisterAt(invoke, 0);
1276 const vixl32::Register arg = InputRegisterAt(invoke, 1);
1277 const vixl32::Register out = OutputRegister(invoke);
1278
1279 const vixl32::Register temp0 = RegisterFrom(locations->GetTemp(0));
1280 const vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1281 const vixl32::Register temp2 = RegisterFrom(locations->GetTemp(2));
1282 vixl32::Register temp3;
1283 if (mirror::kUseStringCompression) {
1284 temp3 = RegisterFrom(locations->GetTemp(3));
1285 }
1286
1287 vixl32::Label loop;
1288 vixl32::Label find_char_diff;
1289
1290 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1291 // Store offset of string value in preparation for comparison loop.
1292 __ Mov(temp1, value_offset);
1293
1294 // Assertions that must hold in order to compare multiple characters at a time.
1295 CHECK_ALIGNED(value_offset, 8);
1296 static_assert(IsAligned<8>(kObjectAlignment),
1297 "String data must be 8-byte aligned for unrolled CompareTo loop.");
1298
1299 const unsigned char_size = DataType::Size(DataType::Type::kUint16);
1300 DCHECK_EQ(char_size, 2u);
1301
1302 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
1303
1304 vixl32::Label find_char_diff_2nd_cmp;
1305 // Unrolled loop comparing 4x16-bit chars per iteration (ok because of string data alignment).
1306 __ Bind(&loop);
1307 vixl32::Register temp_reg = temps.Acquire();
1308 __ Ldr(temp_reg, MemOperand(str, temp1));
1309 __ Ldr(temp2, MemOperand(arg, temp1));
1310 __ Cmp(temp_reg, temp2);
1311 __ B(ne, &find_char_diff, /* is_far_target= */ false);
1312 __ Add(temp1, temp1, char_size * 2);
1313
1314 __ Ldr(temp_reg, MemOperand(str, temp1));
1315 __ Ldr(temp2, MemOperand(arg, temp1));
1316 __ Cmp(temp_reg, temp2);
1317 __ B(ne, &find_char_diff_2nd_cmp, /* is_far_target= */ false);
1318 __ Add(temp1, temp1, char_size * 2);
1319 // With string compression, we have compared 8 bytes, otherwise 4 chars.
1320 __ Subs(temp0, temp0, (mirror::kUseStringCompression ? 8 : 4));
1321 __ B(hi, &loop, /* is_far_target= */ false);
1322 __ B(end);
1323
1324 __ Bind(&find_char_diff_2nd_cmp);
1325 if (mirror::kUseStringCompression) {
1326 __ Subs(temp0, temp0, 4); // 4 bytes previously compared.
1327 __ B(ls, end, /* is_far_target= */ false); // Was the second comparison fully beyond the end?
1328 } else {
1329 // Without string compression, we can start treating temp0 as signed
1330 // and rely on the signed comparison below.
1331 __ Sub(temp0, temp0, 2);
1332 }
1333
1334 // Find the single character difference.
1335 __ Bind(&find_char_diff);
1336 // Get the bit position of the first character that differs.
1337 __ Eor(temp1, temp2, temp_reg);
1338 __ Rbit(temp1, temp1);
1339 __ Clz(temp1, temp1);
1340
1341 // temp0 = number of characters remaining to compare.
1342 // (Without string compression, it could be < 1 if a difference is found by the second CMP
1343 // in the comparison loop, and after the end of the shorter string data).
1344
1345 // Without string compression (temp1 >> 4) = character where difference occurs between the last
1346 // two words compared, in the interval [0,1].
1347 // (0 for low half-word different, 1 for high half-word different).
1348 // With string compression, (temp1 << 3) = byte where the difference occurs,
1349 // in the interval [0,3].
1350
1351 // If temp0 <= (temp1 >> (kUseStringCompression ? 3 : 4)), the difference occurs outside
1352 // the remaining string data, so just return length diff (out).
1353 // The comparison is unsigned for string compression, otherwise signed.
1354 __ Cmp(temp0, Operand(temp1, vixl32::LSR, (mirror::kUseStringCompression ? 3 : 4)));
1355 __ B((mirror::kUseStringCompression ? ls : le), end, /* is_far_target= */ false);
1356
1357 // Extract the characters and calculate the difference.
1358 if (mirror::kUseStringCompression) {
1359 // For compressed strings we need to clear 0x7 from temp1, for uncompressed we need to clear
1360 // 0xf. We also need to prepare the character extraction mask `uncompressed ? 0xffffu : 0xffu`.
1361 // The compression flag is now in the highest bit of temp3, so let's play some tricks.
1362 __ Orr(temp3, temp3, 0xffu << 23); // uncompressed ? 0xff800000u : 0x7ff80000u
1363 __ Bic(temp1, temp1, Operand(temp3, vixl32::LSR, 31 - 3)); // &= ~(uncompressed ? 0xfu : 0x7u)
1364 __ Asr(temp3, temp3, 7u); // uncompressed ? 0xffff0000u : 0xff0000u.
1365 __ Lsr(temp2, temp2, temp1); // Extract second character.
1366 __ Lsr(temp3, temp3, 16u); // uncompressed ? 0xffffu : 0xffu
1367 __ Lsr(out, temp_reg, temp1); // Extract first character.
1368 __ And(temp2, temp2, temp3);
1369 __ And(out, out, temp3);
1370 } else {
1371 __ Bic(temp1, temp1, 0xf);
1372 __ Lsr(temp2, temp2, temp1);
1373 __ Lsr(out, temp_reg, temp1);
1374 __ Movt(temp2, 0);
1375 __ Movt(out, 0);
1376 }
1377
1378 __ Sub(out, out, temp2);
1379 temps.Release(temp_reg);
1380
1381 if (mirror::kUseStringCompression) {
1382 __ B(end);
1383 __ Bind(different_compression);
1384
1385 // Comparison for different compression style.
1386 const size_t c_char_size = DataType::Size(DataType::Type::kInt8);
1387 DCHECK_EQ(c_char_size, 1u);
1388
1389 // We want to free up the temp3, currently holding `str.count`, for comparison.
1390 // So, we move it to the bottom bit of the iteration count `temp0` which we tnen
1391 // need to treat as unsigned. Start by freeing the bit with an ADD and continue
1392 // further down by a LSRS+SBC which will flip the meaning of the flag but allow
1393 // `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
1394 __ Add(temp0, temp0, temp0); // Unlike LSL, this ADD is always 16-bit.
1395 // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
1396 __ Mov(temp1, str);
1397 __ Mov(temp2, arg);
1398 __ Lsrs(temp3, temp3, 1u); // Continue the move of the compression flag.
1399 {
1400 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1401 3 * kMaxInstructionSizeInBytes,
1402 CodeBufferCheckScope::kMaximumSize);
1403 __ itt(cs); // Interleave with selection of temp1 and temp2.
1404 __ mov(cs, temp1, arg); // Preserves flags.
1405 __ mov(cs, temp2, str); // Preserves flags.
1406 }
1407 __ Sbc(temp0, temp0, 0); // Complete the move of the compression flag.
1408
1409 // Adjust temp1 and temp2 from string pointers to data pointers.
1410 __ Add(temp1, temp1, value_offset);
1411 __ Add(temp2, temp2, value_offset);
1412
1413 vixl32::Label different_compression_loop;
1414 vixl32::Label different_compression_diff;
1415
1416 // Main loop for different compression.
1417 temp_reg = temps.Acquire();
1418 __ Bind(&different_compression_loop);
1419 __ Ldrb(temp_reg, MemOperand(temp1, c_char_size, PostIndex));
1420 __ Ldrh(temp3, MemOperand(temp2, char_size, PostIndex));
1421 __ Cmp(temp_reg, temp3);
1422 __ B(ne, &different_compression_diff, /* is_far_target= */ false);
1423 __ Subs(temp0, temp0, 2);
1424 __ B(hi, &different_compression_loop, /* is_far_target= */ false);
1425 __ B(end);
1426
1427 // Calculate the difference.
1428 __ Bind(&different_compression_diff);
1429 __ Sub(out, temp_reg, temp3);
1430 temps.Release(temp_reg);
1431 // Flip the difference if the `arg` is compressed.
1432 // `temp0` contains inverted `str` compression flag, i.e the same as `arg` compression flag.
1433 __ Lsrs(temp0, temp0, 1u);
1434 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1435 "Expecting 0=compressed, 1=uncompressed");
1436
1437 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1438 2 * kMaxInstructionSizeInBytes,
1439 CodeBufferCheckScope::kMaximumSize);
1440 __ it(cc);
1441 __ rsb(cc, out, out, 0);
1442 }
1443 }
1444
1445 // The cut off for unrolling the loop in String.equals() intrinsic for const strings.
1446 // The normal loop plus the pre-header is 9 instructions (18-26 bytes) without string compression
1447 // and 12 instructions (24-32 bytes) with string compression. We can compare up to 4 bytes in 4
1448 // instructions (LDR+LDR+CMP+BNE) and up to 8 bytes in 6 instructions (LDRD+LDRD+CMP+BNE+CMP+BNE).
1449 // Allow up to 12 instructions (32 bytes) for the unrolled loop.
1450 constexpr size_t kShortConstStringEqualsCutoffInBytes = 16;
1451
GetConstString(HInstruction * candidate,uint32_t * utf16_length)1452 static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
1453 if (candidate->IsLoadString()) {
1454 HLoadString* load_string = candidate->AsLoadString();
1455 const DexFile& dex_file = load_string->GetDexFile();
1456 return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
1457 }
1458 return nullptr;
1459 }
1460
VisitStringEquals(HInvoke * invoke)1461 void IntrinsicLocationsBuilderARMVIXL::VisitStringEquals(HInvoke* invoke) {
1462 LocationSummary* locations =
1463 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
1464 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1465 locations->SetInAt(0, Location::RequiresRegister());
1466 locations->SetInAt(1, Location::RequiresRegister());
1467
1468 // Temporary registers to store lengths of strings and for calculations.
1469 // Using instruction cbz requires a low register, so explicitly set a temp to be R0.
1470 locations->AddTemp(LocationFrom(r0));
1471
1472 // For the generic implementation and for long const strings we need an extra temporary.
1473 // We do not need it for short const strings, up to 4 bytes, see code generation below.
1474 uint32_t const_string_length = 0u;
1475 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1476 if (const_string == nullptr) {
1477 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1478 }
1479 bool is_compressed =
1480 mirror::kUseStringCompression &&
1481 const_string != nullptr &&
1482 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1483 if (const_string == nullptr || const_string_length > (is_compressed ? 4u : 2u)) {
1484 locations->AddTemp(Location::RequiresRegister());
1485 }
1486
1487 // TODO: If the String.equals() is used only for an immediately following HIf, we can
1488 // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
1489 // Then we shall need an extra temporary register instead of the output register.
1490 locations->SetOut(Location::RequiresRegister());
1491 }
1492
VisitStringEquals(HInvoke * invoke)1493 void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) {
1494 ArmVIXLAssembler* assembler = GetAssembler();
1495 LocationSummary* locations = invoke->GetLocations();
1496
1497 vixl32::Register str = InputRegisterAt(invoke, 0);
1498 vixl32::Register arg = InputRegisterAt(invoke, 1);
1499 vixl32::Register out = OutputRegister(invoke);
1500
1501 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
1502
1503 vixl32::Label loop;
1504 vixl32::Label end;
1505 vixl32::Label return_true;
1506 vixl32::Label return_false;
1507 vixl32::Label* final_label = codegen_->GetFinalLabel(invoke, &end);
1508
1509 // Get offsets of count, value, and class fields within a string object.
1510 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
1511 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1512 const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
1513
1514 // Note that the null check must have been done earlier.
1515 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1516
1517 StringEqualsOptimizations optimizations(invoke);
1518 if (!optimizations.GetArgumentNotNull()) {
1519 // Check if input is null, return false if it is.
1520 __ CompareAndBranchIfZero(arg, &return_false, /* is_far_target= */ false);
1521 }
1522
1523 // Reference equality check, return true if same reference.
1524 __ Cmp(str, arg);
1525 __ B(eq, &return_true, /* is_far_target= */ false);
1526
1527 if (!optimizations.GetArgumentIsString()) {
1528 // Instanceof check for the argument by comparing class fields.
1529 // All string objects must have the same type since String cannot be subclassed.
1530 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1531 // If the argument is a string object, its class field must be equal to receiver's class field.
1532 //
1533 // As the String class is expected to be non-movable, we can read the class
1534 // field from String.equals' arguments without read barriers.
1535 AssertNonMovableStringClass();
1536 // /* HeapReference<Class> */ temp = str->klass_
1537 __ Ldr(temp, MemOperand(str, class_offset));
1538 // /* HeapReference<Class> */ out = arg->klass_
1539 __ Ldr(out, MemOperand(arg, class_offset));
1540 // Also, because we use the previously loaded class references only in the
1541 // following comparison, we don't need to unpoison them.
1542 __ Cmp(temp, out);
1543 __ B(ne, &return_false, /* is_far_target= */ false);
1544 }
1545
1546 // Check if one of the inputs is a const string. Do not special-case both strings
1547 // being const, such cases should be handled by constant folding if needed.
1548 uint32_t const_string_length = 0u;
1549 const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
1550 if (const_string == nullptr) {
1551 const_string = GetConstString(invoke->InputAt(1), &const_string_length);
1552 if (const_string != nullptr) {
1553 std::swap(str, arg); // Make sure the const string is in `str`.
1554 }
1555 }
1556 bool is_compressed =
1557 mirror::kUseStringCompression &&
1558 const_string != nullptr &&
1559 mirror::String::DexFileStringAllASCII(const_string, const_string_length);
1560
1561 if (const_string != nullptr) {
1562 // Load `count` field of the argument string and check if it matches the const string.
1563 // Also compares the compression style, if differs return false.
1564 __ Ldr(temp, MemOperand(arg, count_offset));
1565 __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
1566 __ B(ne, &return_false, /* is_far_target= */ false);
1567 } else {
1568 // Load `count` fields of this and argument strings.
1569 __ Ldr(temp, MemOperand(str, count_offset));
1570 __ Ldr(out, MemOperand(arg, count_offset));
1571 // Check if `count` fields are equal, return false if they're not.
1572 // Also compares the compression style, if differs return false.
1573 __ Cmp(temp, out);
1574 __ B(ne, &return_false, /* is_far_target= */ false);
1575 }
1576
1577 // Assertions that must hold in order to compare strings 4 bytes at a time.
1578 // Ok to do this because strings are zero-padded to kObjectAlignment.
1579 DCHECK_ALIGNED(value_offset, 4);
1580 static_assert(IsAligned<4>(kObjectAlignment), "String data must be aligned for fast compare.");
1581
1582 if (const_string != nullptr &&
1583 const_string_length <= (is_compressed ? kShortConstStringEqualsCutoffInBytes
1584 : kShortConstStringEqualsCutoffInBytes / 2u)) {
1585 // Load and compare the contents. Though we know the contents of the short const string
1586 // at compile time, materializing constants may be more code than loading from memory.
1587 int32_t offset = value_offset;
1588 size_t remaining_bytes =
1589 RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 4u);
1590 while (remaining_bytes > sizeof(uint32_t)) {
1591 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1592 UseScratchRegisterScope scratch_scope(assembler->GetVIXLAssembler());
1593 vixl32::Register temp2 = scratch_scope.Acquire();
1594 __ Ldrd(temp, temp1, MemOperand(str, offset));
1595 __ Ldrd(temp2, out, MemOperand(arg, offset));
1596 __ Cmp(temp, temp2);
1597 __ B(ne, &return_false, /* is_far_target= */ false);
1598 __ Cmp(temp1, out);
1599 __ B(ne, &return_false, /* is_far_target= */ false);
1600 offset += 2u * sizeof(uint32_t);
1601 remaining_bytes -= 2u * sizeof(uint32_t);
1602 }
1603 if (remaining_bytes != 0u) {
1604 __ Ldr(temp, MemOperand(str, offset));
1605 __ Ldr(out, MemOperand(arg, offset));
1606 __ Cmp(temp, out);
1607 __ B(ne, &return_false, /* is_far_target= */ false);
1608 }
1609 } else {
1610 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
1611 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1612 "Expecting 0=compressed, 1=uncompressed");
1613 __ CompareAndBranchIfZero(temp, &return_true, /* is_far_target= */ false);
1614
1615 if (mirror::kUseStringCompression) {
1616 // For string compression, calculate the number of bytes to compare (not chars).
1617 // This could in theory exceed INT32_MAX, so treat temp as unsigned.
1618 __ Lsrs(temp, temp, 1u); // Extract length and check compression flag.
1619 ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
1620 2 * kMaxInstructionSizeInBytes,
1621 CodeBufferCheckScope::kMaximumSize);
1622 __ it(cs); // If uncompressed,
1623 __ add(cs, temp, temp, temp); // double the byte count.
1624 }
1625
1626 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1627 UseScratchRegisterScope scratch_scope(assembler->GetVIXLAssembler());
1628 vixl32::Register temp2 = scratch_scope.Acquire();
1629
1630 // Store offset of string value in preparation for comparison loop.
1631 __ Mov(temp1, value_offset);
1632
1633 // Loop to compare strings 4 bytes at a time starting at the front of the string.
1634 __ Bind(&loop);
1635 __ Ldr(out, MemOperand(str, temp1));
1636 __ Ldr(temp2, MemOperand(arg, temp1));
1637 __ Add(temp1, temp1, Operand::From(sizeof(uint32_t)));
1638 __ Cmp(out, temp2);
1639 __ B(ne, &return_false, /* is_far_target= */ false);
1640 // With string compression, we have compared 4 bytes, otherwise 2 chars.
1641 __ Subs(temp, temp, mirror::kUseStringCompression ? 4 : 2);
1642 __ B(hi, &loop, /* is_far_target= */ false);
1643 }
1644
1645 // Return true and exit the function.
1646 // If loop does not result in returning false, we return true.
1647 __ Bind(&return_true);
1648 __ Mov(out, 1);
1649 __ B(final_label);
1650
1651 // Return false and exit the function.
1652 __ Bind(&return_false);
1653 __ Mov(out, 0);
1654
1655 if (end.IsReferenced()) {
1656 __ Bind(&end);
1657 }
1658 }
1659
GenerateVisitStringIndexOf(HInvoke * invoke,ArmVIXLAssembler * assembler,CodeGeneratorARMVIXL * codegen,bool start_at_zero)1660 static void GenerateVisitStringIndexOf(HInvoke* invoke,
1661 ArmVIXLAssembler* assembler,
1662 CodeGeneratorARMVIXL* codegen,
1663 bool start_at_zero) {
1664 LocationSummary* locations = invoke->GetLocations();
1665
1666 // Note that the null check must have been done earlier.
1667 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1668
1669 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
1670 // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
1671 SlowPathCodeARMVIXL* slow_path = nullptr;
1672 HInstruction* code_point = invoke->InputAt(1);
1673 if (code_point->IsIntConstant()) {
1674 if (static_cast<uint32_t>(Int32ConstantFrom(code_point)) >
1675 std::numeric_limits<uint16_t>::max()) {
1676 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1677 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1678 slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
1679 codegen->AddSlowPath(slow_path);
1680 __ B(slow_path->GetEntryLabel());
1681 __ Bind(slow_path->GetExitLabel());
1682 return;
1683 }
1684 } else if (code_point->GetType() != DataType::Type::kUint16) {
1685 vixl32::Register char_reg = InputRegisterAt(invoke, 1);
1686 // 0xffff is not modified immediate but 0x10000 is, so use `>= 0x10000` instead of `> 0xffff`.
1687 __ Cmp(char_reg, static_cast<uint32_t>(std::numeric_limits<uint16_t>::max()) + 1);
1688 slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
1689 codegen->AddSlowPath(slow_path);
1690 __ B(hs, slow_path->GetEntryLabel());
1691 }
1692
1693 if (start_at_zero) {
1694 vixl32::Register tmp_reg = RegisterFrom(locations->GetTemp(0));
1695 DCHECK(tmp_reg.Is(r2));
1696 // Start-index = 0.
1697 __ Mov(tmp_reg, 0);
1698 }
1699
1700 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
1701 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
1702
1703 if (slow_path != nullptr) {
1704 __ Bind(slow_path->GetExitLabel());
1705 }
1706 }
1707
VisitStringIndexOf(HInvoke * invoke)1708 void IntrinsicLocationsBuilderARMVIXL::VisitStringIndexOf(HInvoke* invoke) {
1709 LocationSummary* locations = new (allocator_) LocationSummary(
1710 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
1711 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1712 // best to align the inputs accordingly.
1713 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1714 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1715 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1716 locations->SetOut(LocationFrom(r0));
1717
1718 // Need to send start-index=0.
1719 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1720 }
1721
VisitStringIndexOf(HInvoke * invoke)1722 void IntrinsicCodeGeneratorARMVIXL::VisitStringIndexOf(HInvoke* invoke) {
1723 GenerateVisitStringIndexOf(invoke, GetAssembler(), codegen_, /* start_at_zero= */ true);
1724 }
1725
VisitStringIndexOfAfter(HInvoke * invoke)1726 void IntrinsicLocationsBuilderARMVIXL::VisitStringIndexOfAfter(HInvoke* invoke) {
1727 LocationSummary* locations = new (allocator_) LocationSummary(
1728 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
1729 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1730 // best to align the inputs accordingly.
1731 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1732 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1733 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1734 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1735 locations->SetOut(LocationFrom(r0));
1736 }
1737
VisitStringIndexOfAfter(HInvoke * invoke)1738 void IntrinsicCodeGeneratorARMVIXL::VisitStringIndexOfAfter(HInvoke* invoke) {
1739 GenerateVisitStringIndexOf(invoke, GetAssembler(), codegen_, /* start_at_zero= */ false);
1740 }
1741
VisitStringNewStringFromBytes(HInvoke * invoke)1742 void IntrinsicLocationsBuilderARMVIXL::VisitStringNewStringFromBytes(HInvoke* invoke) {
1743 LocationSummary* locations = new (allocator_) LocationSummary(
1744 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
1745 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1746 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1747 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1748 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1749 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1750 locations->SetOut(LocationFrom(r0));
1751 }
1752
VisitStringNewStringFromBytes(HInvoke * invoke)1753 void IntrinsicCodeGeneratorARMVIXL::VisitStringNewStringFromBytes(HInvoke* invoke) {
1754 ArmVIXLAssembler* assembler = GetAssembler();
1755 vixl32::Register byte_array = InputRegisterAt(invoke, 0);
1756 __ Cmp(byte_array, 0);
1757 SlowPathCodeARMVIXL* slow_path =
1758 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
1759 codegen_->AddSlowPath(slow_path);
1760 __ B(eq, slow_path->GetEntryLabel());
1761
1762 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
1763 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
1764 __ Bind(slow_path->GetExitLabel());
1765 }
1766
VisitStringNewStringFromChars(HInvoke * invoke)1767 void IntrinsicLocationsBuilderARMVIXL::VisitStringNewStringFromChars(HInvoke* invoke) {
1768 LocationSummary* locations =
1769 new (allocator_) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
1770 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1771 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1772 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1773 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1774 locations->SetOut(LocationFrom(r0));
1775 }
1776
VisitStringNewStringFromChars(HInvoke * invoke)1777 void IntrinsicCodeGeneratorARMVIXL::VisitStringNewStringFromChars(HInvoke* invoke) {
1778 // No need to emit code checking whether `locations->InAt(2)` is a null
1779 // pointer, as callers of the native method
1780 //
1781 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1782 //
1783 // all include a null check on `data` before calling that method.
1784 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
1785 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
1786 }
1787
VisitStringNewStringFromString(HInvoke * invoke)1788 void IntrinsicLocationsBuilderARMVIXL::VisitStringNewStringFromString(HInvoke* invoke) {
1789 LocationSummary* locations = new (allocator_) LocationSummary(
1790 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
1791 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1792 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1793 locations->SetOut(LocationFrom(r0));
1794 }
1795
VisitStringNewStringFromString(HInvoke * invoke)1796 void IntrinsicCodeGeneratorARMVIXL::VisitStringNewStringFromString(HInvoke* invoke) {
1797 ArmVIXLAssembler* assembler = GetAssembler();
1798 vixl32::Register string_to_copy = InputRegisterAt(invoke, 0);
1799 __ Cmp(string_to_copy, 0);
1800 SlowPathCodeARMVIXL* slow_path =
1801 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
1802 codegen_->AddSlowPath(slow_path);
1803 __ B(eq, slow_path->GetEntryLabel());
1804
1805 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
1806 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
1807
1808 __ Bind(slow_path->GetExitLabel());
1809 }
1810
VisitSystemArrayCopy(HInvoke * invoke)1811 void IntrinsicLocationsBuilderARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) {
1812 // The only read barrier implementation supporting the
1813 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1814 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
1815 return;
1816 }
1817
1818 CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
1819 LocationSummary* locations = invoke->GetLocations();
1820 if (locations == nullptr) {
1821 return;
1822 }
1823
1824 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1825 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
1826 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1827
1828 if (src_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(src_pos->GetValue())) {
1829 locations->SetInAt(1, Location::RequiresRegister());
1830 }
1831 if (dest_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(dest_pos->GetValue())) {
1832 locations->SetInAt(3, Location::RequiresRegister());
1833 }
1834 if (length != nullptr && !assembler_->ShifterOperandCanAlwaysHold(length->GetValue())) {
1835 locations->SetInAt(4, Location::RequiresRegister());
1836 }
1837 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1838 // Temporary register IP cannot be used in
1839 // ReadBarrierSystemArrayCopySlowPathARM (because that register
1840 // is clobbered by ReadBarrierMarkRegX entry points). Get an extra
1841 // temporary register from the register allocator.
1842 locations->AddTemp(Location::RequiresRegister());
1843 }
1844 }
1845
CheckPosition(ArmVIXLAssembler * assembler,Location pos,vixl32::Register input,Location length,SlowPathCodeARMVIXL * slow_path,vixl32::Register temp,bool length_is_input_length=false)1846 static void CheckPosition(ArmVIXLAssembler* assembler,
1847 Location pos,
1848 vixl32::Register input,
1849 Location length,
1850 SlowPathCodeARMVIXL* slow_path,
1851 vixl32::Register temp,
1852 bool length_is_input_length = false) {
1853 // Where is the length in the Array?
1854 const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
1855
1856 if (pos.IsConstant()) {
1857 int32_t pos_const = Int32ConstantFrom(pos);
1858 if (pos_const == 0) {
1859 if (!length_is_input_length) {
1860 // Check that length(input) >= length.
1861 __ Ldr(temp, MemOperand(input, length_offset));
1862 if (length.IsConstant()) {
1863 __ Cmp(temp, Int32ConstantFrom(length));
1864 } else {
1865 __ Cmp(temp, RegisterFrom(length));
1866 }
1867 __ B(lt, slow_path->GetEntryLabel());
1868 }
1869 } else {
1870 // Check that length(input) >= pos.
1871 __ Ldr(temp, MemOperand(input, length_offset));
1872 __ Subs(temp, temp, pos_const);
1873 __ B(lt, slow_path->GetEntryLabel());
1874
1875 // Check that (length(input) - pos) >= length.
1876 if (length.IsConstant()) {
1877 __ Cmp(temp, Int32ConstantFrom(length));
1878 } else {
1879 __ Cmp(temp, RegisterFrom(length));
1880 }
1881 __ B(lt, slow_path->GetEntryLabel());
1882 }
1883 } else if (length_is_input_length) {
1884 // The only way the copy can succeed is if pos is zero.
1885 vixl32::Register pos_reg = RegisterFrom(pos);
1886 __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
1887 } else {
1888 // Check that pos >= 0.
1889 vixl32::Register pos_reg = RegisterFrom(pos);
1890 __ Cmp(pos_reg, 0);
1891 __ B(lt, slow_path->GetEntryLabel());
1892
1893 // Check that pos <= length(input).
1894 __ Ldr(temp, MemOperand(input, length_offset));
1895 __ Subs(temp, temp, pos_reg);
1896 __ B(lt, slow_path->GetEntryLabel());
1897
1898 // Check that (length(input) - pos) >= length.
1899 if (length.IsConstant()) {
1900 __ Cmp(temp, Int32ConstantFrom(length));
1901 } else {
1902 __ Cmp(temp, RegisterFrom(length));
1903 }
1904 __ B(lt, slow_path->GetEntryLabel());
1905 }
1906 }
1907
VisitSystemArrayCopy(HInvoke * invoke)1908 void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) {
1909 // The only read barrier implementation supporting the
1910 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1911 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1912
1913 ArmVIXLAssembler* assembler = GetAssembler();
1914 LocationSummary* locations = invoke->GetLocations();
1915
1916 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1917 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
1918 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
1919 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
1920 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
1921
1922 vixl32::Register src = InputRegisterAt(invoke, 0);
1923 Location src_pos = locations->InAt(1);
1924 vixl32::Register dest = InputRegisterAt(invoke, 2);
1925 Location dest_pos = locations->InAt(3);
1926 Location length = locations->InAt(4);
1927 Location temp1_loc = locations->GetTemp(0);
1928 vixl32::Register temp1 = RegisterFrom(temp1_loc);
1929 Location temp2_loc = locations->GetTemp(1);
1930 vixl32::Register temp2 = RegisterFrom(temp2_loc);
1931 Location temp3_loc = locations->GetTemp(2);
1932 vixl32::Register temp3 = RegisterFrom(temp3_loc);
1933
1934 SlowPathCodeARMVIXL* intrinsic_slow_path =
1935 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARMVIXL(invoke);
1936 codegen_->AddSlowPath(intrinsic_slow_path);
1937
1938 vixl32::Label conditions_on_positions_validated;
1939 SystemArrayCopyOptimizations optimizations(invoke);
1940
1941 // If source and destination are the same, we go to slow path if we need to do
1942 // forward copying.
1943 if (src_pos.IsConstant()) {
1944 int32_t src_pos_constant = Int32ConstantFrom(src_pos);
1945 if (dest_pos.IsConstant()) {
1946 int32_t dest_pos_constant = Int32ConstantFrom(dest_pos);
1947 if (optimizations.GetDestinationIsSource()) {
1948 // Checked when building locations.
1949 DCHECK_GE(src_pos_constant, dest_pos_constant);
1950 } else if (src_pos_constant < dest_pos_constant) {
1951 __ Cmp(src, dest);
1952 __ B(eq, intrinsic_slow_path->GetEntryLabel());
1953 }
1954
1955 // Checked when building locations.
1956 DCHECK(!optimizations.GetDestinationIsSource()
1957 || (src_pos_constant >= Int32ConstantFrom(dest_pos)));
1958 } else {
1959 if (!optimizations.GetDestinationIsSource()) {
1960 __ Cmp(src, dest);
1961 __ B(ne, &conditions_on_positions_validated, /* is_far_target= */ false);
1962 }
1963 __ Cmp(RegisterFrom(dest_pos), src_pos_constant);
1964 __ B(gt, intrinsic_slow_path->GetEntryLabel());
1965 }
1966 } else {
1967 if (!optimizations.GetDestinationIsSource()) {
1968 __ Cmp(src, dest);
1969 __ B(ne, &conditions_on_positions_validated, /* is_far_target= */ false);
1970 }
1971 if (dest_pos.IsConstant()) {
1972 int32_t dest_pos_constant = Int32ConstantFrom(dest_pos);
1973 __ Cmp(RegisterFrom(src_pos), dest_pos_constant);
1974 } else {
1975 __ Cmp(RegisterFrom(src_pos), RegisterFrom(dest_pos));
1976 }
1977 __ B(lt, intrinsic_slow_path->GetEntryLabel());
1978 }
1979
1980 __ Bind(&conditions_on_positions_validated);
1981
1982 if (!optimizations.GetSourceIsNotNull()) {
1983 // Bail out if the source is null.
1984 __ CompareAndBranchIfZero(src, intrinsic_slow_path->GetEntryLabel());
1985 }
1986
1987 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
1988 // Bail out if the destination is null.
1989 __ CompareAndBranchIfZero(dest, intrinsic_slow_path->GetEntryLabel());
1990 }
1991
1992 // If the length is negative, bail out.
1993 // We have already checked in the LocationsBuilder for the constant case.
1994 if (!length.IsConstant() &&
1995 !optimizations.GetCountIsSourceLength() &&
1996 !optimizations.GetCountIsDestinationLength()) {
1997 __ Cmp(RegisterFrom(length), 0);
1998 __ B(lt, intrinsic_slow_path->GetEntryLabel());
1999 }
2000
2001 // Validity checks: source.
2002 CheckPosition(assembler,
2003 src_pos,
2004 src,
2005 length,
2006 intrinsic_slow_path,
2007 temp1,
2008 optimizations.GetCountIsSourceLength());
2009
2010 // Validity checks: dest.
2011 CheckPosition(assembler,
2012 dest_pos,
2013 dest,
2014 length,
2015 intrinsic_slow_path,
2016 temp1,
2017 optimizations.GetCountIsDestinationLength());
2018
2019 if (!optimizations.GetDoesNotNeedTypeCheck()) {
2020 // Check whether all elements of the source array are assignable to the component
2021 // type of the destination array. We do two checks: the classes are the same,
2022 // or the destination is Object[]. If none of these checks succeed, we go to the
2023 // slow path.
2024
2025 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2026 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2027 // /* HeapReference<Class> */ temp1 = src->klass_
2028 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2029 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check= */ false);
2030 // Bail out if the source is not a non primitive array.
2031 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2032 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2033 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check= */ false);
2034 __ CompareAndBranchIfZero(temp1, intrinsic_slow_path->GetEntryLabel());
2035 // If heap poisoning is enabled, `temp1` has been unpoisoned
2036 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2037 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
2038 __ Ldrh(temp1, MemOperand(temp1, primitive_offset));
2039 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2040 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
2041 }
2042
2043 // /* HeapReference<Class> */ temp1 = dest->klass_
2044 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2045 invoke, temp1_loc, dest, class_offset, temp2_loc, /* needs_null_check= */ false);
2046
2047 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2048 // Bail out if the destination is not a non primitive array.
2049 //
2050 // Register `temp1` is not trashed by the read barrier emitted
2051 // by GenerateFieldLoadWithBakerReadBarrier below, as that
2052 // method produces a call to a ReadBarrierMarkRegX entry point,
2053 // which saves all potentially live registers, including
2054 // temporaries such a `temp1`.
2055 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2056 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2057 invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check= */ false);
2058 __ CompareAndBranchIfZero(temp2, intrinsic_slow_path->GetEntryLabel());
2059 // If heap poisoning is enabled, `temp2` has been unpoisoned
2060 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2061 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
2062 __ Ldrh(temp2, MemOperand(temp2, primitive_offset));
2063 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2064 __ CompareAndBranchIfNonZero(temp2, intrinsic_slow_path->GetEntryLabel());
2065 }
2066
2067 // For the same reason given earlier, `temp1` is not trashed by the
2068 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
2069 // /* HeapReference<Class> */ temp2 = src->klass_
2070 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2071 invoke, temp2_loc, src, class_offset, temp3_loc, /* needs_null_check= */ false);
2072 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
2073 __ Cmp(temp1, temp2);
2074
2075 if (optimizations.GetDestinationIsTypedObjectArray()) {
2076 vixl32::Label do_copy;
2077 __ B(eq, &do_copy, /* is_far_target= */ false);
2078 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2079 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2080 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check= */ false);
2081 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2082 // We do not need to emit a read barrier for the following
2083 // heap reference load, as `temp1` is only used in a
2084 // comparison with null below, and this reference is not
2085 // kept afterwards.
2086 __ Ldr(temp1, MemOperand(temp1, super_offset));
2087 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
2088 __ Bind(&do_copy);
2089 } else {
2090 __ B(ne, intrinsic_slow_path->GetEntryLabel());
2091 }
2092 } else {
2093 // Non read barrier code.
2094
2095 // /* HeapReference<Class> */ temp1 = dest->klass_
2096 __ Ldr(temp1, MemOperand(dest, class_offset));
2097 // /* HeapReference<Class> */ temp2 = src->klass_
2098 __ Ldr(temp2, MemOperand(src, class_offset));
2099 bool did_unpoison = false;
2100 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
2101 !optimizations.GetSourceIsNonPrimitiveArray()) {
2102 // One or two of the references need to be unpoisoned. Unpoison them
2103 // both to make the identity check valid.
2104 assembler->MaybeUnpoisonHeapReference(temp1);
2105 assembler->MaybeUnpoisonHeapReference(temp2);
2106 did_unpoison = true;
2107 }
2108
2109 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2110 // Bail out if the destination is not a non primitive array.
2111 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2112 __ Ldr(temp3, MemOperand(temp1, component_offset));
2113 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
2114 assembler->MaybeUnpoisonHeapReference(temp3);
2115 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2116 __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
2117 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2118 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
2119 }
2120
2121 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2122 // Bail out if the source is not a non primitive array.
2123 // /* HeapReference<Class> */ temp3 = temp2->component_type_
2124 __ Ldr(temp3, MemOperand(temp2, component_offset));
2125 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
2126 assembler->MaybeUnpoisonHeapReference(temp3);
2127 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2128 __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
2129 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2130 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
2131 }
2132
2133 __ Cmp(temp1, temp2);
2134
2135 if (optimizations.GetDestinationIsTypedObjectArray()) {
2136 vixl32::Label do_copy;
2137 __ B(eq, &do_copy, /* is_far_target= */ false);
2138 if (!did_unpoison) {
2139 assembler->MaybeUnpoisonHeapReference(temp1);
2140 }
2141 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2142 __ Ldr(temp1, MemOperand(temp1, component_offset));
2143 assembler->MaybeUnpoisonHeapReference(temp1);
2144 // /* HeapReference<Class> */ temp1 = temp1->super_class_
2145 __ Ldr(temp1, MemOperand(temp1, super_offset));
2146 // No need to unpoison the result, we're comparing against null.
2147 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
2148 __ Bind(&do_copy);
2149 } else {
2150 __ B(ne, intrinsic_slow_path->GetEntryLabel());
2151 }
2152 }
2153 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
2154 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
2155 // Bail out if the source is not a non primitive array.
2156 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2157 // /* HeapReference<Class> */ temp1 = src->klass_
2158 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2159 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check= */ false);
2160 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2161 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2162 invoke, temp3_loc, temp1, component_offset, temp2_loc, /* needs_null_check= */ false);
2163 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
2164 // If heap poisoning is enabled, `temp3` has been unpoisoned
2165 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2166 } else {
2167 // /* HeapReference<Class> */ temp1 = src->klass_
2168 __ Ldr(temp1, MemOperand(src, class_offset));
2169 assembler->MaybeUnpoisonHeapReference(temp1);
2170 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2171 __ Ldr(temp3, MemOperand(temp1, component_offset));
2172 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
2173 assembler->MaybeUnpoisonHeapReference(temp3);
2174 }
2175 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2176 __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
2177 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2178 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
2179 }
2180
2181 if (length.IsConstant() && Int32ConstantFrom(length) == 0) {
2182 // Null constant length: not need to emit the loop code at all.
2183 } else {
2184 vixl32::Label done;
2185 const DataType::Type type = DataType::Type::kReference;
2186 const int32_t element_size = DataType::Size(type);
2187
2188 if (length.IsRegister()) {
2189 // Don't enter the copy loop if the length is null.
2190 __ CompareAndBranchIfZero(RegisterFrom(length), &done, /* is_far_target= */ false);
2191 }
2192
2193 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2194 // TODO: Also convert this intrinsic to the IsGcMarking strategy?
2195
2196 // SystemArrayCopy implementation for Baker read barriers (see
2197 // also CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier):
2198 //
2199 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
2200 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
2201 // bool is_gray = (rb_state == ReadBarrier::GrayState());
2202 // if (is_gray) {
2203 // // Slow-path copy.
2204 // do {
2205 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
2206 // } while (src_ptr != end_ptr)
2207 // } else {
2208 // // Fast-path copy.
2209 // do {
2210 // *dest_ptr++ = *src_ptr++;
2211 // } while (src_ptr != end_ptr)
2212 // }
2213
2214 // /* int32_t */ monitor = src->monitor_
2215 __ Ldr(temp2, MemOperand(src, monitor_offset));
2216 // /* LockWord */ lock_word = LockWord(monitor)
2217 static_assert(sizeof(LockWord) == sizeof(int32_t),
2218 "art::LockWord and int32_t have different sizes.");
2219
2220 // Introduce a dependency on the lock_word including the rb_state,
2221 // which shall prevent load-load reordering without using
2222 // a memory barrier (which would be more expensive).
2223 // `src` is unchanged by this operation, but its value now depends
2224 // on `temp2`.
2225 __ Add(src, src, Operand(temp2, vixl32::LSR, 32));
2226
2227 // Compute the base source address in `temp1`.
2228 // Note that `temp1` (the base source address) is computed from
2229 // `src` (and `src_pos`) here, and thus honors the artificial
2230 // dependency of `src` on `temp2`.
2231 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2232 // Compute the end source address in `temp3`.
2233 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2234 // The base destination address is computed later, as `temp2` is
2235 // used for intermediate computations.
2236
2237 // Slow path used to copy array when `src` is gray.
2238 // Note that the base destination address is computed in `temp2`
2239 // by the slow path code.
2240 SlowPathCodeARMVIXL* read_barrier_slow_path =
2241 new (codegen_->GetScopedAllocator()) ReadBarrierSystemArrayCopySlowPathARMVIXL(invoke);
2242 codegen_->AddSlowPath(read_barrier_slow_path);
2243
2244 // Given the numeric representation, it's enough to check the low bit of the
2245 // rb_state. We do that by shifting the bit out of the lock word with LSRS
2246 // which can be a 16-bit instruction unlike the TST immediate.
2247 static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
2248 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
2249 __ Lsrs(temp2, temp2, LockWord::kReadBarrierStateShift + 1);
2250 // Carry flag is the last bit shifted out by LSRS.
2251 __ B(cs, read_barrier_slow_path->GetEntryLabel());
2252
2253 // Fast-path copy.
2254 // Compute the base destination address in `temp2`.
2255 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2256 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2257 // poison/unpoison.
2258 vixl32::Label loop;
2259 __ Bind(&loop);
2260 {
2261 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2262 const vixl32::Register temp_reg = temps.Acquire();
2263 __ Ldr(temp_reg, MemOperand(temp1, element_size, PostIndex));
2264 __ Str(temp_reg, MemOperand(temp2, element_size, PostIndex));
2265 }
2266 __ Cmp(temp1, temp3);
2267 __ B(ne, &loop, /* is_far_target= */ false);
2268
2269 __ Bind(read_barrier_slow_path->GetExitLabel());
2270 } else {
2271 // Non read barrier code.
2272 // Compute the base source address in `temp1`.
2273 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2274 // Compute the base destination address in `temp2`.
2275 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2276 // Compute the end source address in `temp3`.
2277 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2278 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2279 // poison/unpoison.
2280 vixl32::Label loop;
2281 __ Bind(&loop);
2282 {
2283 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2284 const vixl32::Register temp_reg = temps.Acquire();
2285 __ Ldr(temp_reg, MemOperand(temp1, element_size, PostIndex));
2286 __ Str(temp_reg, MemOperand(temp2, element_size, PostIndex));
2287 }
2288 __ Cmp(temp1, temp3);
2289 __ B(ne, &loop, /* is_far_target= */ false);
2290 }
2291 __ Bind(&done);
2292 }
2293
2294 // We only need one card marking on the destination array.
2295 codegen_->MarkGCCard(temp1, temp2, dest, NoReg, /* can_be_null= */ false);
2296
2297 __ Bind(intrinsic_slow_path->GetExitLabel());
2298 }
2299
CreateFPToFPCallLocations(ArenaAllocator * allocator,HInvoke * invoke)2300 static void CreateFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
2301 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2302 // the code generator. Furthermore, the register allocator creates fixed live intervals
2303 // for all caller-saved registers because we are doing a function call. As a result, if
2304 // the input and output locations are unallocated, the register allocator runs out of
2305 // registers and fails; however, a debuggable graph is not the common case.
2306 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2307 return;
2308 }
2309
2310 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2311 DCHECK_EQ(invoke->InputAt(0)->GetType(), DataType::Type::kFloat64);
2312 DCHECK_EQ(invoke->GetType(), DataType::Type::kFloat64);
2313
2314 LocationSummary* const locations =
2315 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
2316 const InvokeRuntimeCallingConventionARMVIXL calling_convention;
2317
2318 locations->SetInAt(0, Location::RequiresFpuRegister());
2319 locations->SetOut(Location::RequiresFpuRegister());
2320 // Native code uses the soft float ABI.
2321 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
2322 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
2323 }
2324
CreateFPFPToFPCallLocations(ArenaAllocator * allocator,HInvoke * invoke)2325 static void CreateFPFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
2326 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2327 // the code generator. Furthermore, the register allocator creates fixed live intervals
2328 // for all caller-saved registers because we are doing a function call. As a result, if
2329 // the input and output locations are unallocated, the register allocator runs out of
2330 // registers and fails; however, a debuggable graph is not the common case.
2331 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2332 return;
2333 }
2334
2335 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2336 DCHECK_EQ(invoke->InputAt(0)->GetType(), DataType::Type::kFloat64);
2337 DCHECK_EQ(invoke->InputAt(1)->GetType(), DataType::Type::kFloat64);
2338 DCHECK_EQ(invoke->GetType(), DataType::Type::kFloat64);
2339
2340 LocationSummary* const locations =
2341 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
2342 const InvokeRuntimeCallingConventionARMVIXL calling_convention;
2343
2344 locations->SetInAt(0, Location::RequiresFpuRegister());
2345 locations->SetInAt(1, Location::RequiresFpuRegister());
2346 locations->SetOut(Location::RequiresFpuRegister());
2347 // Native code uses the soft float ABI.
2348 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
2349 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
2350 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
2351 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(3)));
2352 }
2353
GenFPToFPCall(HInvoke * invoke,ArmVIXLAssembler * assembler,CodeGeneratorARMVIXL * codegen,QuickEntrypointEnum entry)2354 static void GenFPToFPCall(HInvoke* invoke,
2355 ArmVIXLAssembler* assembler,
2356 CodeGeneratorARMVIXL* codegen,
2357 QuickEntrypointEnum entry) {
2358 LocationSummary* const locations = invoke->GetLocations();
2359
2360 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2361 DCHECK(locations->WillCall() && locations->Intrinsified());
2362
2363 // Native code uses the soft float ABI.
2364 __ Vmov(RegisterFrom(locations->GetTemp(0)),
2365 RegisterFrom(locations->GetTemp(1)),
2366 InputDRegisterAt(invoke, 0));
2367 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
2368 __ Vmov(OutputDRegister(invoke),
2369 RegisterFrom(locations->GetTemp(0)),
2370 RegisterFrom(locations->GetTemp(1)));
2371 }
2372
GenFPFPToFPCall(HInvoke * invoke,ArmVIXLAssembler * assembler,CodeGeneratorARMVIXL * codegen,QuickEntrypointEnum entry)2373 static void GenFPFPToFPCall(HInvoke* invoke,
2374 ArmVIXLAssembler* assembler,
2375 CodeGeneratorARMVIXL* codegen,
2376 QuickEntrypointEnum entry) {
2377 LocationSummary* const locations = invoke->GetLocations();
2378
2379 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2380 DCHECK(locations->WillCall() && locations->Intrinsified());
2381
2382 // Native code uses the soft float ABI.
2383 __ Vmov(RegisterFrom(locations->GetTemp(0)),
2384 RegisterFrom(locations->GetTemp(1)),
2385 InputDRegisterAt(invoke, 0));
2386 __ Vmov(RegisterFrom(locations->GetTemp(2)),
2387 RegisterFrom(locations->GetTemp(3)),
2388 InputDRegisterAt(invoke, 1));
2389 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
2390 __ Vmov(OutputDRegister(invoke),
2391 RegisterFrom(locations->GetTemp(0)),
2392 RegisterFrom(locations->GetTemp(1)));
2393 }
2394
VisitMathCos(HInvoke * invoke)2395 void IntrinsicLocationsBuilderARMVIXL::VisitMathCos(HInvoke* invoke) {
2396 CreateFPToFPCallLocations(allocator_, invoke);
2397 }
2398
VisitMathCos(HInvoke * invoke)2399 void IntrinsicCodeGeneratorARMVIXL::VisitMathCos(HInvoke* invoke) {
2400 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCos);
2401 }
2402
VisitMathSin(HInvoke * invoke)2403 void IntrinsicLocationsBuilderARMVIXL::VisitMathSin(HInvoke* invoke) {
2404 CreateFPToFPCallLocations(allocator_, invoke);
2405 }
2406
VisitMathSin(HInvoke * invoke)2407 void IntrinsicCodeGeneratorARMVIXL::VisitMathSin(HInvoke* invoke) {
2408 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSin);
2409 }
2410
VisitMathAcos(HInvoke * invoke)2411 void IntrinsicLocationsBuilderARMVIXL::VisitMathAcos(HInvoke* invoke) {
2412 CreateFPToFPCallLocations(allocator_, invoke);
2413 }
2414
VisitMathAcos(HInvoke * invoke)2415 void IntrinsicCodeGeneratorARMVIXL::VisitMathAcos(HInvoke* invoke) {
2416 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAcos);
2417 }
2418
VisitMathAsin(HInvoke * invoke)2419 void IntrinsicLocationsBuilderARMVIXL::VisitMathAsin(HInvoke* invoke) {
2420 CreateFPToFPCallLocations(allocator_, invoke);
2421 }
2422
VisitMathAsin(HInvoke * invoke)2423 void IntrinsicCodeGeneratorARMVIXL::VisitMathAsin(HInvoke* invoke) {
2424 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAsin);
2425 }
2426
VisitMathAtan(HInvoke * invoke)2427 void IntrinsicLocationsBuilderARMVIXL::VisitMathAtan(HInvoke* invoke) {
2428 CreateFPToFPCallLocations(allocator_, invoke);
2429 }
2430
VisitMathAtan(HInvoke * invoke)2431 void IntrinsicCodeGeneratorARMVIXL::VisitMathAtan(HInvoke* invoke) {
2432 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan);
2433 }
2434
VisitMathCbrt(HInvoke * invoke)2435 void IntrinsicLocationsBuilderARMVIXL::VisitMathCbrt(HInvoke* invoke) {
2436 CreateFPToFPCallLocations(allocator_, invoke);
2437 }
2438
VisitMathCbrt(HInvoke * invoke)2439 void IntrinsicCodeGeneratorARMVIXL::VisitMathCbrt(HInvoke* invoke) {
2440 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCbrt);
2441 }
2442
VisitMathCosh(HInvoke * invoke)2443 void IntrinsicLocationsBuilderARMVIXL::VisitMathCosh(HInvoke* invoke) {
2444 CreateFPToFPCallLocations(allocator_, invoke);
2445 }
2446
VisitMathCosh(HInvoke * invoke)2447 void IntrinsicCodeGeneratorARMVIXL::VisitMathCosh(HInvoke* invoke) {
2448 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCosh);
2449 }
2450
VisitMathExp(HInvoke * invoke)2451 void IntrinsicLocationsBuilderARMVIXL::VisitMathExp(HInvoke* invoke) {
2452 CreateFPToFPCallLocations(allocator_, invoke);
2453 }
2454
VisitMathExp(HInvoke * invoke)2455 void IntrinsicCodeGeneratorARMVIXL::VisitMathExp(HInvoke* invoke) {
2456 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExp);
2457 }
2458
VisitMathExpm1(HInvoke * invoke)2459 void IntrinsicLocationsBuilderARMVIXL::VisitMathExpm1(HInvoke* invoke) {
2460 CreateFPToFPCallLocations(allocator_, invoke);
2461 }
2462
VisitMathExpm1(HInvoke * invoke)2463 void IntrinsicCodeGeneratorARMVIXL::VisitMathExpm1(HInvoke* invoke) {
2464 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExpm1);
2465 }
2466
VisitMathLog(HInvoke * invoke)2467 void IntrinsicLocationsBuilderARMVIXL::VisitMathLog(HInvoke* invoke) {
2468 CreateFPToFPCallLocations(allocator_, invoke);
2469 }
2470
VisitMathLog(HInvoke * invoke)2471 void IntrinsicCodeGeneratorARMVIXL::VisitMathLog(HInvoke* invoke) {
2472 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog);
2473 }
2474
VisitMathLog10(HInvoke * invoke)2475 void IntrinsicLocationsBuilderARMVIXL::VisitMathLog10(HInvoke* invoke) {
2476 CreateFPToFPCallLocations(allocator_, invoke);
2477 }
2478
VisitMathLog10(HInvoke * invoke)2479 void IntrinsicCodeGeneratorARMVIXL::VisitMathLog10(HInvoke* invoke) {
2480 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog10);
2481 }
2482
VisitMathSinh(HInvoke * invoke)2483 void IntrinsicLocationsBuilderARMVIXL::VisitMathSinh(HInvoke* invoke) {
2484 CreateFPToFPCallLocations(allocator_, invoke);
2485 }
2486
VisitMathSinh(HInvoke * invoke)2487 void IntrinsicCodeGeneratorARMVIXL::VisitMathSinh(HInvoke* invoke) {
2488 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSinh);
2489 }
2490
VisitMathTan(HInvoke * invoke)2491 void IntrinsicLocationsBuilderARMVIXL::VisitMathTan(HInvoke* invoke) {
2492 CreateFPToFPCallLocations(allocator_, invoke);
2493 }
2494
VisitMathTan(HInvoke * invoke)2495 void IntrinsicCodeGeneratorARMVIXL::VisitMathTan(HInvoke* invoke) {
2496 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTan);
2497 }
2498
VisitMathTanh(HInvoke * invoke)2499 void IntrinsicLocationsBuilderARMVIXL::VisitMathTanh(HInvoke* invoke) {
2500 CreateFPToFPCallLocations(allocator_, invoke);
2501 }
2502
VisitMathTanh(HInvoke * invoke)2503 void IntrinsicCodeGeneratorARMVIXL::VisitMathTanh(HInvoke* invoke) {
2504 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTanh);
2505 }
2506
VisitMathAtan2(HInvoke * invoke)2507 void IntrinsicLocationsBuilderARMVIXL::VisitMathAtan2(HInvoke* invoke) {
2508 CreateFPFPToFPCallLocations(allocator_, invoke);
2509 }
2510
VisitMathAtan2(HInvoke * invoke)2511 void IntrinsicCodeGeneratorARMVIXL::VisitMathAtan2(HInvoke* invoke) {
2512 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan2);
2513 }
2514
VisitMathPow(HInvoke * invoke)2515 void IntrinsicLocationsBuilderARMVIXL::VisitMathPow(HInvoke* invoke) {
2516 CreateFPFPToFPCallLocations(allocator_, invoke);
2517 }
2518
VisitMathPow(HInvoke * invoke)2519 void IntrinsicCodeGeneratorARMVIXL::VisitMathPow(HInvoke* invoke) {
2520 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickPow);
2521 }
2522
VisitMathHypot(HInvoke * invoke)2523 void IntrinsicLocationsBuilderARMVIXL::VisitMathHypot(HInvoke* invoke) {
2524 CreateFPFPToFPCallLocations(allocator_, invoke);
2525 }
2526
VisitMathHypot(HInvoke * invoke)2527 void IntrinsicCodeGeneratorARMVIXL::VisitMathHypot(HInvoke* invoke) {
2528 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickHypot);
2529 }
2530
VisitMathNextAfter(HInvoke * invoke)2531 void IntrinsicLocationsBuilderARMVIXL::VisitMathNextAfter(HInvoke* invoke) {
2532 CreateFPFPToFPCallLocations(allocator_, invoke);
2533 }
2534
VisitMathNextAfter(HInvoke * invoke)2535 void IntrinsicCodeGeneratorARMVIXL::VisitMathNextAfter(HInvoke* invoke) {
2536 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter);
2537 }
2538
VisitIntegerReverse(HInvoke * invoke)2539 void IntrinsicLocationsBuilderARMVIXL::VisitIntegerReverse(HInvoke* invoke) {
2540 CreateIntToIntLocations(allocator_, invoke);
2541 }
2542
VisitIntegerReverse(HInvoke * invoke)2543 void IntrinsicCodeGeneratorARMVIXL::VisitIntegerReverse(HInvoke* invoke) {
2544 ArmVIXLAssembler* assembler = GetAssembler();
2545 __ Rbit(OutputRegister(invoke), InputRegisterAt(invoke, 0));
2546 }
2547
VisitLongReverse(HInvoke * invoke)2548 void IntrinsicLocationsBuilderARMVIXL::VisitLongReverse(HInvoke* invoke) {
2549 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
2550 }
2551
VisitLongReverse(HInvoke * invoke)2552 void IntrinsicCodeGeneratorARMVIXL::VisitLongReverse(HInvoke* invoke) {
2553 ArmVIXLAssembler* assembler = GetAssembler();
2554 LocationSummary* locations = invoke->GetLocations();
2555
2556 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
2557 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
2558 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
2559 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
2560
2561 __ Rbit(out_reg_lo, in_reg_hi);
2562 __ Rbit(out_reg_hi, in_reg_lo);
2563 }
2564
VisitIntegerReverseBytes(HInvoke * invoke)2565 void IntrinsicLocationsBuilderARMVIXL::VisitIntegerReverseBytes(HInvoke* invoke) {
2566 CreateIntToIntLocations(allocator_, invoke);
2567 }
2568
VisitIntegerReverseBytes(HInvoke * invoke)2569 void IntrinsicCodeGeneratorARMVIXL::VisitIntegerReverseBytes(HInvoke* invoke) {
2570 ArmVIXLAssembler* assembler = GetAssembler();
2571 __ Rev(OutputRegister(invoke), InputRegisterAt(invoke, 0));
2572 }
2573
VisitLongReverseBytes(HInvoke * invoke)2574 void IntrinsicLocationsBuilderARMVIXL::VisitLongReverseBytes(HInvoke* invoke) {
2575 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
2576 }
2577
VisitLongReverseBytes(HInvoke * invoke)2578 void IntrinsicCodeGeneratorARMVIXL::VisitLongReverseBytes(HInvoke* invoke) {
2579 ArmVIXLAssembler* assembler = GetAssembler();
2580 LocationSummary* locations = invoke->GetLocations();
2581
2582 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
2583 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
2584 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
2585 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
2586
2587 __ Rev(out_reg_lo, in_reg_hi);
2588 __ Rev(out_reg_hi, in_reg_lo);
2589 }
2590
VisitShortReverseBytes(HInvoke * invoke)2591 void IntrinsicLocationsBuilderARMVIXL::VisitShortReverseBytes(HInvoke* invoke) {
2592 CreateIntToIntLocations(allocator_, invoke);
2593 }
2594
VisitShortReverseBytes(HInvoke * invoke)2595 void IntrinsicCodeGeneratorARMVIXL::VisitShortReverseBytes(HInvoke* invoke) {
2596 ArmVIXLAssembler* assembler = GetAssembler();
2597 __ Revsh(OutputRegister(invoke), InputRegisterAt(invoke, 0));
2598 }
2599
GenBitCount(HInvoke * instr,DataType::Type type,ArmVIXLAssembler * assembler)2600 static void GenBitCount(HInvoke* instr, DataType::Type type, ArmVIXLAssembler* assembler) {
2601 DCHECK(DataType::IsIntOrLongType(type)) << type;
2602 DCHECK_EQ(instr->GetType(), DataType::Type::kInt32);
2603 DCHECK_EQ(DataType::Kind(instr->InputAt(0)->GetType()), type);
2604
2605 bool is_long = type == DataType::Type::kInt64;
2606 LocationSummary* locations = instr->GetLocations();
2607 Location in = locations->InAt(0);
2608 vixl32::Register src_0 = is_long ? LowRegisterFrom(in) : RegisterFrom(in);
2609 vixl32::Register src_1 = is_long ? HighRegisterFrom(in) : src_0;
2610 vixl32::SRegister tmp_s = LowSRegisterFrom(locations->GetTemp(0));
2611 vixl32::DRegister tmp_d = DRegisterFrom(locations->GetTemp(0));
2612 vixl32::Register out_r = OutputRegister(instr);
2613
2614 // Move data from core register(s) to temp D-reg for bit count calculation, then move back.
2615 // According to Cortex A57 and A72 optimization guides, compared to transferring to full D-reg,
2616 // transferring data from core reg to upper or lower half of vfp D-reg requires extra latency,
2617 // That's why for integer bit count, we use 'vmov d0, r0, r0' instead of 'vmov d0[0], r0'.
2618 __ Vmov(tmp_d, src_1, src_0); // Temp DReg |--src_1|--src_0|
2619 __ Vcnt(Untyped8, tmp_d, tmp_d); // Temp DReg |c|c|c|c|c|c|c|c|
2620 __ Vpaddl(U8, tmp_d, tmp_d); // Temp DReg |--c|--c|--c|--c|
2621 __ Vpaddl(U16, tmp_d, tmp_d); // Temp DReg |------c|------c|
2622 if (is_long) {
2623 __ Vpaddl(U32, tmp_d, tmp_d); // Temp DReg |--------------c|
2624 }
2625 __ Vmov(out_r, tmp_s);
2626 }
2627
VisitIntegerBitCount(HInvoke * invoke)2628 void IntrinsicLocationsBuilderARMVIXL::VisitIntegerBitCount(HInvoke* invoke) {
2629 CreateIntToIntLocations(allocator_, invoke);
2630 invoke->GetLocations()->AddTemp(Location::RequiresFpuRegister());
2631 }
2632
VisitIntegerBitCount(HInvoke * invoke)2633 void IntrinsicCodeGeneratorARMVIXL::VisitIntegerBitCount(HInvoke* invoke) {
2634 GenBitCount(invoke, DataType::Type::kInt32, GetAssembler());
2635 }
2636
VisitLongBitCount(HInvoke * invoke)2637 void IntrinsicLocationsBuilderARMVIXL::VisitLongBitCount(HInvoke* invoke) {
2638 VisitIntegerBitCount(invoke);
2639 }
2640
VisitLongBitCount(HInvoke * invoke)2641 void IntrinsicCodeGeneratorARMVIXL::VisitLongBitCount(HInvoke* invoke) {
2642 GenBitCount(invoke, DataType::Type::kInt64, GetAssembler());
2643 }
2644
GenHighestOneBit(HInvoke * invoke,DataType::Type type,CodeGeneratorARMVIXL * codegen)2645 static void GenHighestOneBit(HInvoke* invoke,
2646 DataType::Type type,
2647 CodeGeneratorARMVIXL* codegen) {
2648 DCHECK(DataType::IsIntOrLongType(type));
2649
2650 ArmVIXLAssembler* assembler = codegen->GetAssembler();
2651 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2652 const vixl32::Register temp = temps.Acquire();
2653
2654 if (type == DataType::Type::kInt64) {
2655 LocationSummary* locations = invoke->GetLocations();
2656 Location in = locations->InAt(0);
2657 Location out = locations->Out();
2658
2659 vixl32::Register in_reg_lo = LowRegisterFrom(in);
2660 vixl32::Register in_reg_hi = HighRegisterFrom(in);
2661 vixl32::Register out_reg_lo = LowRegisterFrom(out);
2662 vixl32::Register out_reg_hi = HighRegisterFrom(out);
2663
2664 __ Mov(temp, 0x80000000); // Modified immediate.
2665 __ Clz(out_reg_lo, in_reg_lo);
2666 __ Clz(out_reg_hi, in_reg_hi);
2667 __ Lsr(out_reg_lo, temp, out_reg_lo);
2668 __ Lsrs(out_reg_hi, temp, out_reg_hi);
2669
2670 // Discard result for lowest 32 bits if highest 32 bits are not zero.
2671 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
2672 // we check that the output is in a low register, so that a 16-bit MOV
2673 // encoding can be used. If output is in a high register, then we generate
2674 // 4 more bytes of code to avoid a branch.
2675 Operand mov_src(0);
2676 if (!out_reg_lo.IsLow()) {
2677 __ Mov(LeaveFlags, temp, 0);
2678 mov_src = Operand(temp);
2679 }
2680 ExactAssemblyScope it_scope(codegen->GetVIXLAssembler(),
2681 2 * vixl32::k16BitT32InstructionSizeInBytes,
2682 CodeBufferCheckScope::kExactSize);
2683 __ it(ne);
2684 __ mov(ne, out_reg_lo, mov_src);
2685 } else {
2686 vixl32::Register out = OutputRegister(invoke);
2687 vixl32::Register in = InputRegisterAt(invoke, 0);
2688
2689 __ Mov(temp, 0x80000000); // Modified immediate.
2690 __ Clz(out, in);
2691 __ Lsr(out, temp, out);
2692 }
2693 }
2694
VisitIntegerHighestOneBit(HInvoke * invoke)2695 void IntrinsicLocationsBuilderARMVIXL::VisitIntegerHighestOneBit(HInvoke* invoke) {
2696 CreateIntToIntLocations(allocator_, invoke);
2697 }
2698
VisitIntegerHighestOneBit(HInvoke * invoke)2699 void IntrinsicCodeGeneratorARMVIXL::VisitIntegerHighestOneBit(HInvoke* invoke) {
2700 GenHighestOneBit(invoke, DataType::Type::kInt32, codegen_);
2701 }
2702
VisitLongHighestOneBit(HInvoke * invoke)2703 void IntrinsicLocationsBuilderARMVIXL::VisitLongHighestOneBit(HInvoke* invoke) {
2704 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
2705 }
2706
VisitLongHighestOneBit(HInvoke * invoke)2707 void IntrinsicCodeGeneratorARMVIXL::VisitLongHighestOneBit(HInvoke* invoke) {
2708 GenHighestOneBit(invoke, DataType::Type::kInt64, codegen_);
2709 }
2710
GenLowestOneBit(HInvoke * invoke,DataType::Type type,CodeGeneratorARMVIXL * codegen)2711 static void GenLowestOneBit(HInvoke* invoke,
2712 DataType::Type type,
2713 CodeGeneratorARMVIXL* codegen) {
2714 DCHECK(DataType::IsIntOrLongType(type));
2715
2716 ArmVIXLAssembler* assembler = codegen->GetAssembler();
2717 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2718 const vixl32::Register temp = temps.Acquire();
2719
2720 if (type == DataType::Type::kInt64) {
2721 LocationSummary* locations = invoke->GetLocations();
2722 Location in = locations->InAt(0);
2723 Location out = locations->Out();
2724
2725 vixl32::Register in_reg_lo = LowRegisterFrom(in);
2726 vixl32::Register in_reg_hi = HighRegisterFrom(in);
2727 vixl32::Register out_reg_lo = LowRegisterFrom(out);
2728 vixl32::Register out_reg_hi = HighRegisterFrom(out);
2729
2730 __ Rsb(out_reg_hi, in_reg_hi, 0);
2731 __ Rsb(out_reg_lo, in_reg_lo, 0);
2732 __ And(out_reg_hi, out_reg_hi, in_reg_hi);
2733 // The result of this operation is 0 iff in_reg_lo is 0
2734 __ Ands(out_reg_lo, out_reg_lo, in_reg_lo);
2735
2736 // Discard result for highest 32 bits if lowest 32 bits are not zero.
2737 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
2738 // we check that the output is in a low register, so that a 16-bit MOV
2739 // encoding can be used. If output is in a high register, then we generate
2740 // 4 more bytes of code to avoid a branch.
2741 Operand mov_src(0);
2742 if (!out_reg_lo.IsLow()) {
2743 __ Mov(LeaveFlags, temp, 0);
2744 mov_src = Operand(temp);
2745 }
2746 ExactAssemblyScope it_scope(codegen->GetVIXLAssembler(),
2747 2 * vixl32::k16BitT32InstructionSizeInBytes,
2748 CodeBufferCheckScope::kExactSize);
2749 __ it(ne);
2750 __ mov(ne, out_reg_hi, mov_src);
2751 } else {
2752 vixl32::Register out = OutputRegister(invoke);
2753 vixl32::Register in = InputRegisterAt(invoke, 0);
2754
2755 __ Rsb(temp, in, 0);
2756 __ And(out, temp, in);
2757 }
2758 }
2759
VisitIntegerLowestOneBit(HInvoke * invoke)2760 void IntrinsicLocationsBuilderARMVIXL::VisitIntegerLowestOneBit(HInvoke* invoke) {
2761 CreateIntToIntLocations(allocator_, invoke);
2762 }
2763
VisitIntegerLowestOneBit(HInvoke * invoke)2764 void IntrinsicCodeGeneratorARMVIXL::VisitIntegerLowestOneBit(HInvoke* invoke) {
2765 GenLowestOneBit(invoke, DataType::Type::kInt32, codegen_);
2766 }
2767
VisitLongLowestOneBit(HInvoke * invoke)2768 void IntrinsicLocationsBuilderARMVIXL::VisitLongLowestOneBit(HInvoke* invoke) {
2769 CreateLongToLongLocationsWithOverlap(allocator_, invoke);
2770 }
2771
VisitLongLowestOneBit(HInvoke * invoke)2772 void IntrinsicCodeGeneratorARMVIXL::VisitLongLowestOneBit(HInvoke* invoke) {
2773 GenLowestOneBit(invoke, DataType::Type::kInt64, codegen_);
2774 }
2775
VisitStringGetCharsNoCheck(HInvoke * invoke)2776 void IntrinsicLocationsBuilderARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2777 LocationSummary* locations =
2778 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
2779 locations->SetInAt(0, Location::RequiresRegister());
2780 locations->SetInAt(1, Location::RequiresRegister());
2781 locations->SetInAt(2, Location::RequiresRegister());
2782 locations->SetInAt(3, Location::RequiresRegister());
2783 locations->SetInAt(4, Location::RequiresRegister());
2784
2785 // Temporary registers to store lengths of strings and for calculations.
2786 locations->AddTemp(Location::RequiresRegister());
2787 locations->AddTemp(Location::RequiresRegister());
2788 locations->AddTemp(Location::RequiresRegister());
2789 }
2790
VisitStringGetCharsNoCheck(HInvoke * invoke)2791 void IntrinsicCodeGeneratorARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2792 ArmVIXLAssembler* assembler = GetAssembler();
2793 LocationSummary* locations = invoke->GetLocations();
2794
2795 // Check assumption that sizeof(Char) is 2 (used in scaling below).
2796 const size_t char_size = DataType::Size(DataType::Type::kUint16);
2797 DCHECK_EQ(char_size, 2u);
2798
2799 // Location of data in char array buffer.
2800 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2801
2802 // Location of char array data in string.
2803 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2804
2805 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2806 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2807 vixl32::Register srcObj = InputRegisterAt(invoke, 0);
2808 vixl32::Register srcBegin = InputRegisterAt(invoke, 1);
2809 vixl32::Register srcEnd = InputRegisterAt(invoke, 2);
2810 vixl32::Register dstObj = InputRegisterAt(invoke, 3);
2811 vixl32::Register dstBegin = InputRegisterAt(invoke, 4);
2812
2813 vixl32::Register num_chr = RegisterFrom(locations->GetTemp(0));
2814 vixl32::Register src_ptr = RegisterFrom(locations->GetTemp(1));
2815 vixl32::Register dst_ptr = RegisterFrom(locations->GetTemp(2));
2816
2817 vixl32::Label done, compressed_string_loop;
2818 vixl32::Label* final_label = codegen_->GetFinalLabel(invoke, &done);
2819 // dst to be copied.
2820 __ Add(dst_ptr, dstObj, data_offset);
2821 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, vixl32::LSL, 1));
2822
2823 __ Subs(num_chr, srcEnd, srcBegin);
2824 // Early out for valid zero-length retrievals.
2825 __ B(eq, final_label, /* is_far_target= */ false);
2826
2827 // src range to copy.
2828 __ Add(src_ptr, srcObj, value_offset);
2829
2830 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2831 vixl32::Register temp;
2832 vixl32::Label compressed_string_preloop;
2833 if (mirror::kUseStringCompression) {
2834 // Location of count in string.
2835 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2836 temp = temps.Acquire();
2837 // String's length.
2838 __ Ldr(temp, MemOperand(srcObj, count_offset));
2839 __ Tst(temp, 1);
2840 temps.Release(temp);
2841 __ B(eq, &compressed_string_preloop, /* is_far_target= */ false);
2842 }
2843 __ Add(src_ptr, src_ptr, Operand(srcBegin, vixl32::LSL, 1));
2844
2845 // Do the copy.
2846 vixl32::Label loop, remainder;
2847
2848 temp = temps.Acquire();
2849 // Save repairing the value of num_chr on the < 4 character path.
2850 __ Subs(temp, num_chr, 4);
2851 __ B(lt, &remainder, /* is_far_target= */ false);
2852
2853 // Keep the result of the earlier subs, we are going to fetch at least 4 characters.
2854 __ Mov(num_chr, temp);
2855
2856 // Main loop used for longer fetches loads and stores 4x16-bit characters at a time.
2857 // (LDRD/STRD fault on unaligned addresses and it's not worth inlining extra code
2858 // to rectify these everywhere this intrinsic applies.)
2859 __ Bind(&loop);
2860 __ Ldr(temp, MemOperand(src_ptr, char_size * 2));
2861 __ Subs(num_chr, num_chr, 4);
2862 __ Str(temp, MemOperand(dst_ptr, char_size * 2));
2863 __ Ldr(temp, MemOperand(src_ptr, char_size * 4, PostIndex));
2864 __ Str(temp, MemOperand(dst_ptr, char_size * 4, PostIndex));
2865 temps.Release(temp);
2866 __ B(ge, &loop, /* is_far_target= */ false);
2867
2868 __ Adds(num_chr, num_chr, 4);
2869 __ B(eq, final_label, /* is_far_target= */ false);
2870
2871 // Main loop for < 4 character case and remainder handling. Loads and stores one
2872 // 16-bit Java character at a time.
2873 __ Bind(&remainder);
2874 temp = temps.Acquire();
2875 __ Ldrh(temp, MemOperand(src_ptr, char_size, PostIndex));
2876 __ Subs(num_chr, num_chr, 1);
2877 __ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex));
2878 temps.Release(temp);
2879 __ B(gt, &remainder, /* is_far_target= */ false);
2880
2881 if (mirror::kUseStringCompression) {
2882 __ B(final_label);
2883
2884 const size_t c_char_size = DataType::Size(DataType::Type::kInt8);
2885 DCHECK_EQ(c_char_size, 1u);
2886 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2887 __ Bind(&compressed_string_preloop);
2888 __ Add(src_ptr, src_ptr, srcBegin);
2889 __ Bind(&compressed_string_loop);
2890 temp = temps.Acquire();
2891 __ Ldrb(temp, MemOperand(src_ptr, c_char_size, PostIndex));
2892 __ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex));
2893 temps.Release(temp);
2894 __ Subs(num_chr, num_chr, 1);
2895 __ B(gt, &compressed_string_loop, /* is_far_target= */ false);
2896 }
2897
2898 if (done.IsReferenced()) {
2899 __ Bind(&done);
2900 }
2901 }
2902
VisitFloatIsInfinite(HInvoke * invoke)2903 void IntrinsicLocationsBuilderARMVIXL::VisitFloatIsInfinite(HInvoke* invoke) {
2904 CreateFPToIntLocations(allocator_, invoke);
2905 }
2906
VisitFloatIsInfinite(HInvoke * invoke)2907 void IntrinsicCodeGeneratorARMVIXL::VisitFloatIsInfinite(HInvoke* invoke) {
2908 ArmVIXLAssembler* const assembler = GetAssembler();
2909 const vixl32::Register out = OutputRegister(invoke);
2910 // Shifting left by 1 bit makes the value encodable as an immediate operand;
2911 // we don't care about the sign bit anyway.
2912 constexpr uint32_t infinity = kPositiveInfinityFloat << 1U;
2913
2914 __ Vmov(out, InputSRegisterAt(invoke, 0));
2915 // We don't care about the sign bit, so shift left.
2916 __ Lsl(out, out, 1);
2917 __ Eor(out, out, infinity);
2918 codegen_->GenerateConditionWithZero(kCondEQ, out, out);
2919 }
2920
VisitDoubleIsInfinite(HInvoke * invoke)2921 void IntrinsicLocationsBuilderARMVIXL::VisitDoubleIsInfinite(HInvoke* invoke) {
2922 CreateFPToIntLocations(allocator_, invoke);
2923 }
2924
VisitDoubleIsInfinite(HInvoke * invoke)2925 void IntrinsicCodeGeneratorARMVIXL::VisitDoubleIsInfinite(HInvoke* invoke) {
2926 ArmVIXLAssembler* const assembler = GetAssembler();
2927 const vixl32::Register out = OutputRegister(invoke);
2928 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2929 const vixl32::Register temp = temps.Acquire();
2930 // The highest 32 bits of double precision positive infinity separated into
2931 // two constants encodable as immediate operands.
2932 constexpr uint32_t infinity_high = 0x7f000000U;
2933 constexpr uint32_t infinity_high2 = 0x00f00000U;
2934
2935 static_assert((infinity_high | infinity_high2) ==
2936 static_cast<uint32_t>(kPositiveInfinityDouble >> 32U),
2937 "The constants do not add up to the high 32 bits of double "
2938 "precision positive infinity.");
2939 __ Vmov(temp, out, InputDRegisterAt(invoke, 0));
2940 __ Eor(out, out, infinity_high);
2941 __ Eor(out, out, infinity_high2);
2942 // We don't care about the sign bit, so shift left.
2943 __ Orr(out, temp, Operand(out, vixl32::LSL, 1));
2944 codegen_->GenerateConditionWithZero(kCondEQ, out, out);
2945 }
2946
VisitMathCeil(HInvoke * invoke)2947 void IntrinsicLocationsBuilderARMVIXL::VisitMathCeil(HInvoke* invoke) {
2948 if (features_.HasARMv8AInstructions()) {
2949 CreateFPToFPLocations(allocator_, invoke);
2950 }
2951 }
2952
VisitMathCeil(HInvoke * invoke)2953 void IntrinsicCodeGeneratorARMVIXL::VisitMathCeil(HInvoke* invoke) {
2954 ArmVIXLAssembler* assembler = GetAssembler();
2955 DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
2956 __ Vrintp(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
2957 }
2958
VisitMathFloor(HInvoke * invoke)2959 void IntrinsicLocationsBuilderARMVIXL::VisitMathFloor(HInvoke* invoke) {
2960 if (features_.HasARMv8AInstructions()) {
2961 CreateFPToFPLocations(allocator_, invoke);
2962 }
2963 }
2964
VisitMathFloor(HInvoke * invoke)2965 void IntrinsicCodeGeneratorARMVIXL::VisitMathFloor(HInvoke* invoke) {
2966 ArmVIXLAssembler* assembler = GetAssembler();
2967 DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
2968 __ Vrintm(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
2969 }
2970
VisitIntegerValueOf(HInvoke * invoke)2971 void IntrinsicLocationsBuilderARMVIXL::VisitIntegerValueOf(HInvoke* invoke) {
2972 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2973 IntrinsicVisitor::ComputeIntegerValueOfLocations(
2974 invoke,
2975 codegen_,
2976 LocationFrom(r0),
2977 LocationFrom(calling_convention.GetRegisterAt(0)));
2978 }
2979
VisitIntegerValueOf(HInvoke * invoke)2980 void IntrinsicCodeGeneratorARMVIXL::VisitIntegerValueOf(HInvoke* invoke) {
2981 IntrinsicVisitor::IntegerValueOfInfo info =
2982 IntrinsicVisitor::ComputeIntegerValueOfInfo(invoke, codegen_->GetCompilerOptions());
2983 LocationSummary* locations = invoke->GetLocations();
2984 ArmVIXLAssembler* const assembler = GetAssembler();
2985
2986 vixl32::Register out = RegisterFrom(locations->Out());
2987 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2988 vixl32::Register temp = temps.Acquire();
2989 if (invoke->InputAt(0)->IsConstant()) {
2990 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
2991 if (static_cast<uint32_t>(value - info.low) < info.length) {
2992 // Just embed the j.l.Integer in the code.
2993 DCHECK_NE(info.value_boot_image_reference, IntegerValueOfInfo::kInvalidReference);
2994 codegen_->LoadBootImageAddress(out, info.value_boot_image_reference);
2995 } else {
2996 DCHECK(locations->CanCall());
2997 // Allocate and initialize a new j.l.Integer.
2998 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
2999 // JIT object table.
3000 codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(),
3001 info.integer_boot_image_offset);
3002 __ Mov(temp, value);
3003 assembler->StoreToOffset(kStoreWord, temp, out, info.value_offset);
3004 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
3005 // one.
3006 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3007 }
3008 } else {
3009 DCHECK(locations->CanCall());
3010 vixl32::Register in = RegisterFrom(locations->InAt(0));
3011 // Check bounds of our cache.
3012 __ Add(out, in, -info.low);
3013 __ Cmp(out, info.length);
3014 vixl32::Label allocate, done;
3015 __ B(hs, &allocate, /* is_far_target= */ false);
3016 // If the value is within the bounds, load the j.l.Integer directly from the array.
3017 codegen_->LoadBootImageAddress(temp, info.array_data_boot_image_reference);
3018 codegen_->LoadFromShiftedRegOffset(DataType::Type::kReference, locations->Out(), temp, out);
3019 assembler->MaybeUnpoisonHeapReference(out);
3020 __ B(&done);
3021 __ Bind(&allocate);
3022 // Otherwise allocate and initialize a new j.l.Integer.
3023 codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(),
3024 info.integer_boot_image_offset);
3025 assembler->StoreToOffset(kStoreWord, in, out, info.value_offset);
3026 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
3027 // one.
3028 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3029 __ Bind(&done);
3030 }
3031 }
3032
VisitThreadInterrupted(HInvoke * invoke)3033 void IntrinsicLocationsBuilderARMVIXL::VisitThreadInterrupted(HInvoke* invoke) {
3034 LocationSummary* locations =
3035 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
3036 locations->SetOut(Location::RequiresRegister());
3037 }
3038
VisitThreadInterrupted(HInvoke * invoke)3039 void IntrinsicCodeGeneratorARMVIXL::VisitThreadInterrupted(HInvoke* invoke) {
3040 ArmVIXLAssembler* assembler = GetAssembler();
3041 vixl32::Register out = RegisterFrom(invoke->GetLocations()->Out());
3042 int32_t offset = Thread::InterruptedOffset<kArmPointerSize>().Int32Value();
3043 __ Ldr(out, MemOperand(tr, offset));
3044 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
3045 vixl32::Register temp = temps.Acquire();
3046 vixl32::Label done;
3047 vixl32::Label* const final_label = codegen_->GetFinalLabel(invoke, &done);
3048 __ CompareAndBranchIfZero(out, final_label, /* is_far_target= */ false);
3049 __ Dmb(vixl32::ISH);
3050 __ Mov(temp, 0);
3051 assembler->StoreToOffset(kStoreWord, temp, tr, offset);
3052 __ Dmb(vixl32::ISH);
3053 if (done.IsReferenced()) {
3054 __ Bind(&done);
3055 }
3056 }
3057
VisitReachabilityFence(HInvoke * invoke)3058 void IntrinsicLocationsBuilderARMVIXL::VisitReachabilityFence(HInvoke* invoke) {
3059 LocationSummary* locations =
3060 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
3061 locations->SetInAt(0, Location::Any());
3062 }
3063
VisitReachabilityFence(HInvoke * invoke ATTRIBUTE_UNUSED)3064 void IntrinsicCodeGeneratorARMVIXL::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
3065
3066 UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundDouble) // Could be done by changing rounding mode, maybe?
3067 UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong) // High register pressure.
3068 UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
3069 UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent)
3070 UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32Update)
3071 UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32UpdateBytes)
3072 UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32UpdateByteBuffer)
3073
3074 UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf);
3075 UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter);
3076 UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBufferAppend);
3077 UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBufferLength);
3078 UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBufferToString);
3079 UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBuilderAppend);
3080 UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBuilderLength);
3081 UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBuilderToString);
3082
3083 // 1.8.
3084 UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndAddInt)
3085 UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndAddLong)
3086 UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndSetInt)
3087 UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndSetLong)
3088 UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndSetObject)
3089
3090 UNREACHABLE_INTRINSICS(ARMVIXL)
3091
3092 #undef __
3093
3094 } // namespace arm
3095 } // namespace art
3096