blob: c8e3534164c12506f41234ac424bab7a13f35fc7 [file] [log] [blame]
Anton Kirilov5ec62182016-10-13 20:16:02 +01001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "intrinsics_arm_vixl.h"
18
19#include "arch/arm/instruction_set_features_arm.h"
20#include "code_generator_arm_vixl.h"
21#include "common_arm.h"
22#include "lock_word.h"
23#include "mirror/array-inl.h"
24
25#include "aarch32/constants-aarch32.h"
26
27namespace art {
28namespace arm {
29
30#define __ assembler->GetVIXLAssembler()->
31
32using helpers::DRegisterFrom;
33using helpers::HighRegisterFrom;
34using helpers::InputDRegisterAt;
35using helpers::InputRegisterAt;
36using helpers::InputSRegisterAt;
37using helpers::InputVRegisterAt;
38using helpers::Int32ConstantFrom;
39using helpers::LocationFrom;
40using helpers::LowRegisterFrom;
41using helpers::LowSRegisterFrom;
42using helpers::OutputDRegister;
43using helpers::OutputRegister;
44using helpers::OutputVRegister;
45using helpers::RegisterFrom;
46using helpers::SRegisterFrom;
47
48using namespace vixl::aarch32; // NOLINT(build/namespaces)
49
50ArmVIXLAssembler* IntrinsicCodeGeneratorARMVIXL::GetAssembler() {
51 return codegen_->GetAssembler();
52}
53
54ArenaAllocator* IntrinsicCodeGeneratorARMVIXL::GetAllocator() {
55 return codegen_->GetGraph()->GetArena();
56}
57
58// Default slow-path for fallback (calling the managed code to handle the intrinsic) in an
59// intrinsified call. This will copy the arguments into the positions for a regular call.
60//
61// Note: The actual parameters are required to be in the locations given by the invoke's location
62// summary. If an intrinsic modifies those locations before a slowpath call, they must be
63// restored!
64//
65// Note: If an invoke wasn't sharpened, we will put down an invoke-virtual here. That's potentially
66// sub-optimal (compared to a direct pointer call), but this is a slow-path.
67
68class IntrinsicSlowPathARMVIXL : public SlowPathCodeARMVIXL {
69 public:
70 explicit IntrinsicSlowPathARMVIXL(HInvoke* invoke)
71 : SlowPathCodeARMVIXL(invoke), invoke_(invoke) {}
72
73 Location MoveArguments(CodeGenerator* codegen) {
74 InvokeDexCallingConventionVisitorARM calling_convention_visitor;
75 IntrinsicVisitor::MoveArguments(invoke_, codegen, &calling_convention_visitor);
76 return calling_convention_visitor.GetMethodLocation();
77 }
78
79 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
80 ArmVIXLAssembler* assembler = down_cast<ArmVIXLAssembler*>(codegen->GetAssembler());
81 __ Bind(GetEntryLabel());
82
83 SaveLiveRegisters(codegen, invoke_->GetLocations());
84
85 Location method_loc = MoveArguments(codegen);
86
87 if (invoke_->IsInvokeStaticOrDirect()) {
88 codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), method_loc);
89 } else {
90 codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), method_loc);
91 }
92 codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
93
94 // Copy the result back to the expected output.
95 Location out = invoke_->GetLocations()->Out();
96 if (out.IsValid()) {
97 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
98 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
99 codegen->MoveFromReturnRegister(out, invoke_->GetType());
100 }
101
102 RestoreLiveRegisters(codegen, invoke_->GetLocations());
103 __ B(GetExitLabel());
104 }
105
106 const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPath"; }
107
108 private:
109 // The instruction where this slow path is happening.
110 HInvoke* const invoke_;
111
112 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARMVIXL);
113};
114
115// Slow path implementing the SystemArrayCopy intrinsic copy loop with read barriers.
116class ReadBarrierSystemArrayCopySlowPathARMVIXL : public SlowPathCodeARMVIXL {
117 public:
118 explicit ReadBarrierSystemArrayCopySlowPathARMVIXL(HInstruction* instruction)
119 : SlowPathCodeARMVIXL(instruction) {
120 DCHECK(kEmitCompilerReadBarrier);
121 DCHECK(kUseBakerReadBarrier);
122 }
123
124 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
125 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
126 ArmVIXLAssembler* assembler = arm_codegen->GetAssembler();
127 LocationSummary* locations = instruction_->GetLocations();
128 DCHECK(locations->CanCall());
129 DCHECK(instruction_->IsInvokeStaticOrDirect())
130 << "Unexpected instruction in read barrier arraycopy slow path: "
131 << instruction_->DebugName();
132 DCHECK(instruction_->GetLocations()->Intrinsified());
133 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kSystemArrayCopy);
134
135 int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
136 uint32_t element_size_shift = Primitive::ComponentSizeShift(Primitive::kPrimNot);
137 uint32_t offset = mirror::Array::DataOffset(element_size).Uint32Value();
138
139 vixl32::Register dest = InputRegisterAt(instruction_, 2);
140 Location dest_pos = locations->InAt(3);
141 vixl32::Register src_curr_addr = RegisterFrom(locations->GetTemp(0));
142 vixl32::Register dst_curr_addr = RegisterFrom(locations->GetTemp(1));
143 vixl32::Register src_stop_addr = RegisterFrom(locations->GetTemp(2));
144 vixl32::Register tmp = RegisterFrom(locations->GetTemp(3));
145
146 __ Bind(GetEntryLabel());
147 // Compute the base destination address in `dst_curr_addr`.
148 if (dest_pos.IsConstant()) {
149 int32_t constant = Int32ConstantFrom(dest_pos);
150 __ Add(dst_curr_addr, dest, element_size * constant + offset);
151 } else {
152 __ Add(dst_curr_addr,
153 dest,
154 Operand(RegisterFrom(dest_pos), vixl32::LSL, element_size_shift));
155 __ Add(dst_curr_addr, dst_curr_addr, offset);
156 }
157
158 vixl32::Label loop;
159 __ Bind(&loop);
160 __ Ldr(tmp, MemOperand(src_curr_addr, element_size, PostIndex));
161 assembler->MaybeUnpoisonHeapReference(tmp);
162 // TODO: Inline the mark bit check before calling the runtime?
163 // tmp = ReadBarrier::Mark(tmp);
164 // No need to save live registers; it's taken care of by the
165 // entrypoint. Also, there is no need to update the stack mask,
166 // as this runtime call will not trigger a garbage collection.
167 // (See ReadBarrierMarkSlowPathARM::EmitNativeCode for more
168 // explanations.)
169 DCHECK(!tmp.IsSP());
170 DCHECK(!tmp.IsLR());
171 DCHECK(!tmp.IsPC());
172 // IP is used internally by the ReadBarrierMarkRegX entry point
173 // as a temporary (and not preserved). It thus cannot be used by
174 // any live register in this slow path.
175 DCHECK(!src_curr_addr.Is(ip));
176 DCHECK(!dst_curr_addr.Is(ip));
177 DCHECK(!src_stop_addr.Is(ip));
178 DCHECK(!tmp.Is(ip));
179 DCHECK(tmp.IsRegister()) << tmp;
180 int32_t entry_point_offset =
181 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(tmp.GetCode());
182 // This runtime call does not require a stack map.
183 arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
184 assembler->MaybePoisonHeapReference(tmp);
185 __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
186 __ Cmp(src_curr_addr, src_stop_addr);
187 __ B(ne, &loop);
188 __ B(GetExitLabel());
189 }
190
191 const char* GetDescription() const OVERRIDE {
192 return "ReadBarrierSystemArrayCopySlowPathARMVIXL";
193 }
194
195 private:
196 DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathARMVIXL);
197};
198
199IntrinsicLocationsBuilderARMVIXL::IntrinsicLocationsBuilderARMVIXL(CodeGeneratorARMVIXL* codegen)
200 : arena_(codegen->GetGraph()->GetArena()),
201 assembler_(codegen->GetAssembler()),
202 features_(codegen->GetInstructionSetFeatures()) {}
203
204bool IntrinsicLocationsBuilderARMVIXL::TryDispatch(HInvoke* invoke) {
205 Dispatch(invoke);
206 LocationSummary* res = invoke->GetLocations();
207 if (res == nullptr) {
208 return false;
209 }
210 return res->Intrinsified();
211}
212
213static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
214 LocationSummary* locations = new (arena) LocationSummary(invoke,
215 LocationSummary::kNoCall,
216 kIntrinsified);
217 locations->SetInAt(0, Location::RequiresFpuRegister());
218 locations->SetOut(Location::RequiresRegister());
219}
220
221static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
222 LocationSummary* locations = new (arena) LocationSummary(invoke,
223 LocationSummary::kNoCall,
224 kIntrinsified);
225 locations->SetInAt(0, Location::RequiresRegister());
226 locations->SetOut(Location::RequiresFpuRegister());
227}
228
229static void MoveFPToInt(LocationSummary* locations, bool is64bit, ArmVIXLAssembler* assembler) {
230 Location input = locations->InAt(0);
231 Location output = locations->Out();
232 if (is64bit) {
233 __ Vmov(LowRegisterFrom(output), HighRegisterFrom(output), DRegisterFrom(input));
234 } else {
235 __ Vmov(RegisterFrom(output), SRegisterFrom(input));
236 }
237}
238
239static void MoveIntToFP(LocationSummary* locations, bool is64bit, ArmVIXLAssembler* assembler) {
240 Location input = locations->InAt(0);
241 Location output = locations->Out();
242 if (is64bit) {
243 __ Vmov(DRegisterFrom(output), LowRegisterFrom(input), HighRegisterFrom(input));
244 } else {
245 __ Vmov(SRegisterFrom(output), RegisterFrom(input));
246 }
247}
248
249void IntrinsicLocationsBuilderARMVIXL::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
250 CreateFPToIntLocations(arena_, invoke);
251}
252void IntrinsicLocationsBuilderARMVIXL::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
253 CreateIntToFPLocations(arena_, invoke);
254}
255
256void IntrinsicCodeGeneratorARMVIXL::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
257 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
258}
259void IntrinsicCodeGeneratorARMVIXL::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
260 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
261}
262
263void IntrinsicLocationsBuilderARMVIXL::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
264 CreateFPToIntLocations(arena_, invoke);
265}
266void IntrinsicLocationsBuilderARMVIXL::VisitFloatIntBitsToFloat(HInvoke* invoke) {
267 CreateIntToFPLocations(arena_, invoke);
268}
269
270void IntrinsicCodeGeneratorARMVIXL::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
271 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
272}
273void IntrinsicCodeGeneratorARMVIXL::VisitFloatIntBitsToFloat(HInvoke* invoke) {
274 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
275}
276
277static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
278 LocationSummary* locations = new (arena) LocationSummary(invoke,
279 LocationSummary::kNoCall,
280 kIntrinsified);
281 locations->SetInAt(0, Location::RequiresRegister());
282 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
283}
284
285static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
286 LocationSummary* locations = new (arena) LocationSummary(invoke,
287 LocationSummary::kNoCall,
288 kIntrinsified);
289 locations->SetInAt(0, Location::RequiresFpuRegister());
290 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
291}
292
293static void GenNumberOfLeadingZeros(LocationSummary* locations,
294 Primitive::Type type,
295 ArmVIXLAssembler* assembler) {
296 Location in = locations->InAt(0);
297 vixl32::Register out = RegisterFrom(locations->Out());
298
299 DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
300
301 if (type == Primitive::kPrimLong) {
302 vixl32::Register in_reg_lo = LowRegisterFrom(in);
303 vixl32::Register in_reg_hi = HighRegisterFrom(in);
304 vixl32::Label end;
305 __ Clz(out, in_reg_hi);
xueliang.zhongf51bc622016-11-04 09:23:32 +0000306 __ CompareAndBranchIfNonZero(in_reg_hi, &end, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100307 __ Clz(out, in_reg_lo);
308 __ Add(out, out, 32);
309 __ Bind(&end);
310 } else {
311 __ Clz(out, RegisterFrom(in));
312 }
313}
314
315void IntrinsicLocationsBuilderARMVIXL::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
316 CreateIntToIntLocations(arena_, invoke);
317}
318
319void IntrinsicCodeGeneratorARMVIXL::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
320 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
321}
322
323void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
324 LocationSummary* locations = new (arena_) LocationSummary(invoke,
325 LocationSummary::kNoCall,
326 kIntrinsified);
327 locations->SetInAt(0, Location::RequiresRegister());
328 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
329}
330
331void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
332 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
333}
334
335static void GenNumberOfTrailingZeros(LocationSummary* locations,
336 Primitive::Type type,
337 ArmVIXLAssembler* assembler) {
338 DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
339
340 vixl32::Register out = RegisterFrom(locations->Out());
341
342 if (type == Primitive::kPrimLong) {
343 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
344 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
345 vixl32::Label end;
346 __ Rbit(out, in_reg_lo);
347 __ Clz(out, out);
xueliang.zhongf51bc622016-11-04 09:23:32 +0000348 __ CompareAndBranchIfNonZero(in_reg_lo, &end, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +0100349 __ Rbit(out, in_reg_hi);
350 __ Clz(out, out);
351 __ Add(out, out, 32);
352 __ Bind(&end);
353 } else {
354 vixl32::Register in = RegisterFrom(locations->InAt(0));
355 __ Rbit(out, in);
356 __ Clz(out, out);
357 }
358}
359
360void IntrinsicLocationsBuilderARMVIXL::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
361 LocationSummary* locations = new (arena_) LocationSummary(invoke,
362 LocationSummary::kNoCall,
363 kIntrinsified);
364 locations->SetInAt(0, Location::RequiresRegister());
365 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
366}
367
368void IntrinsicCodeGeneratorARMVIXL::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
369 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
370}
371
372void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
373 LocationSummary* locations = new (arena_) LocationSummary(invoke,
374 LocationSummary::kNoCall,
375 kIntrinsified);
376 locations->SetInAt(0, Location::RequiresRegister());
377 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
378}
379
380void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
381 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
382}
383
384static void MathAbsFP(HInvoke* invoke, ArmVIXLAssembler* assembler) {
385 __ Vabs(OutputVRegister(invoke), InputVRegisterAt(invoke, 0));
386}
387
388void IntrinsicLocationsBuilderARMVIXL::VisitMathAbsDouble(HInvoke* invoke) {
389 CreateFPToFPLocations(arena_, invoke);
390}
391
392void IntrinsicCodeGeneratorARMVIXL::VisitMathAbsDouble(HInvoke* invoke) {
393 MathAbsFP(invoke, GetAssembler());
394}
395
396void IntrinsicLocationsBuilderARMVIXL::VisitMathAbsFloat(HInvoke* invoke) {
397 CreateFPToFPLocations(arena_, invoke);
398}
399
400void IntrinsicCodeGeneratorARMVIXL::VisitMathAbsFloat(HInvoke* invoke) {
401 MathAbsFP(invoke, GetAssembler());
402}
403
404static void CreateIntToIntPlusTemp(ArenaAllocator* arena, HInvoke* invoke) {
405 LocationSummary* locations = new (arena) LocationSummary(invoke,
406 LocationSummary::kNoCall,
407 kIntrinsified);
408 locations->SetInAt(0, Location::RequiresRegister());
409 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
410
411 locations->AddTemp(Location::RequiresRegister());
412}
413
414static void GenAbsInteger(LocationSummary* locations,
415 bool is64bit,
416 ArmVIXLAssembler* assembler) {
417 Location in = locations->InAt(0);
418 Location output = locations->Out();
419
420 vixl32::Register mask = RegisterFrom(locations->GetTemp(0));
421
422 if (is64bit) {
423 vixl32::Register in_reg_lo = LowRegisterFrom(in);
424 vixl32::Register in_reg_hi = HighRegisterFrom(in);
425 vixl32::Register out_reg_lo = LowRegisterFrom(output);
426 vixl32::Register out_reg_hi = HighRegisterFrom(output);
427
428 DCHECK(!out_reg_lo.Is(in_reg_hi)) << "Diagonal overlap unexpected.";
429
430 __ Asr(mask, in_reg_hi, 31);
431 __ Adds(out_reg_lo, in_reg_lo, mask);
432 __ Adc(out_reg_hi, in_reg_hi, mask);
433 __ Eor(out_reg_lo, mask, out_reg_lo);
434 __ Eor(out_reg_hi, mask, out_reg_hi);
435 } else {
436 vixl32::Register in_reg = RegisterFrom(in);
437 vixl32::Register out_reg = RegisterFrom(output);
438
439 __ Asr(mask, in_reg, 31);
440 __ Add(out_reg, in_reg, mask);
441 __ Eor(out_reg, mask, out_reg);
442 }
443}
444
445void IntrinsicLocationsBuilderARMVIXL::VisitMathAbsInt(HInvoke* invoke) {
446 CreateIntToIntPlusTemp(arena_, invoke);
447}
448
449void IntrinsicCodeGeneratorARMVIXL::VisitMathAbsInt(HInvoke* invoke) {
450 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
451}
452
453
454void IntrinsicLocationsBuilderARMVIXL::VisitMathAbsLong(HInvoke* invoke) {
455 CreateIntToIntPlusTemp(arena_, invoke);
456}
457
458void IntrinsicCodeGeneratorARMVIXL::VisitMathAbsLong(HInvoke* invoke) {
459 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
460}
461
462static void GenMinMax(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assembler) {
463 vixl32::Register op1 = InputRegisterAt(invoke, 0);
464 vixl32::Register op2 = InputRegisterAt(invoke, 1);
465 vixl32::Register out = OutputRegister(invoke);
466
467 __ Cmp(op1, op2);
468
469 {
470 AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
471 3 * kMaxInstructionSizeInBytes,
472 CodeBufferCheckScope::kMaximumSize);
473
474 __ ite(is_min ? lt : gt);
475 __ mov(is_min ? lt : gt, out, op1);
476 __ mov(is_min ? ge : le, out, op2);
477 }
478}
479
480static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
481 LocationSummary* locations = new (arena) LocationSummary(invoke,
482 LocationSummary::kNoCall,
483 kIntrinsified);
484 locations->SetInAt(0, Location::RequiresRegister());
485 locations->SetInAt(1, Location::RequiresRegister());
486 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
487}
488
489void IntrinsicLocationsBuilderARMVIXL::VisitMathMinIntInt(HInvoke* invoke) {
490 CreateIntIntToIntLocations(arena_, invoke);
491}
492
493void IntrinsicCodeGeneratorARMVIXL::VisitMathMinIntInt(HInvoke* invoke) {
494 GenMinMax(invoke, /* is_min */ true, GetAssembler());
495}
496
497void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxIntInt(HInvoke* invoke) {
498 CreateIntIntToIntLocations(arena_, invoke);
499}
500
501void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxIntInt(HInvoke* invoke) {
502 GenMinMax(invoke, /* is_min */ false, GetAssembler());
503}
504
505void IntrinsicLocationsBuilderARMVIXL::VisitMathSqrt(HInvoke* invoke) {
506 CreateFPToFPLocations(arena_, invoke);
507}
508
509void IntrinsicCodeGeneratorARMVIXL::VisitMathSqrt(HInvoke* invoke) {
510 ArmVIXLAssembler* assembler = GetAssembler();
511 __ Vsqrt(OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
512}
513
514void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) {
515 CreateIntToIntLocations(arena_, invoke);
516}
517
518void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) {
519 ArmVIXLAssembler* assembler = GetAssembler();
520 // Ignore upper 4B of long address.
Scott Wakelingb77051e2016-11-21 19:46:00 +0000521 __ Ldrsb(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100522}
523
524void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) {
525 CreateIntToIntLocations(arena_, invoke);
526}
527
528void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) {
529 ArmVIXLAssembler* assembler = GetAssembler();
530 // Ignore upper 4B of long address.
Scott Wakelingb77051e2016-11-21 19:46:00 +0000531 __ Ldr(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100532}
533
534void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekLongNative(HInvoke* invoke) {
535 CreateIntToIntLocations(arena_, invoke);
536}
537
538void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekLongNative(HInvoke* invoke) {
539 ArmVIXLAssembler* assembler = GetAssembler();
540 // Ignore upper 4B of long address.
541 vixl32::Register addr = LowRegisterFrom(invoke->GetLocations()->InAt(0));
542 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
543 // exception. So we can't use ldrd as addr may be unaligned.
544 vixl32::Register lo = LowRegisterFrom(invoke->GetLocations()->Out());
545 vixl32::Register hi = HighRegisterFrom(invoke->GetLocations()->Out());
546 if (addr.Is(lo)) {
547 __ Ldr(hi, MemOperand(addr, 4));
Scott Wakelingb77051e2016-11-21 19:46:00 +0000548 __ Ldr(lo, MemOperand(addr));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100549 } else {
Scott Wakelingb77051e2016-11-21 19:46:00 +0000550 __ Ldr(lo, MemOperand(addr));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100551 __ Ldr(hi, MemOperand(addr, 4));
552 }
553}
554
555void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekShortNative(HInvoke* invoke) {
556 CreateIntToIntLocations(arena_, invoke);
557}
558
559void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekShortNative(HInvoke* invoke) {
560 ArmVIXLAssembler* assembler = GetAssembler();
561 // Ignore upper 4B of long address.
Scott Wakelingb77051e2016-11-21 19:46:00 +0000562 __ Ldrsh(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100563}
564
565static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
566 LocationSummary* locations = new (arena) LocationSummary(invoke,
567 LocationSummary::kNoCall,
568 kIntrinsified);
569 locations->SetInAt(0, Location::RequiresRegister());
570 locations->SetInAt(1, Location::RequiresRegister());
571}
572
573void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeByte(HInvoke* invoke) {
574 CreateIntIntToVoidLocations(arena_, invoke);
575}
576
577void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeByte(HInvoke* invoke) {
578 ArmVIXLAssembler* assembler = GetAssembler();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000579 __ Strb(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100580}
581
582void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) {
583 CreateIntIntToVoidLocations(arena_, invoke);
584}
585
586void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) {
587 ArmVIXLAssembler* assembler = GetAssembler();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000588 __ Str(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100589}
590
591void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeLongNative(HInvoke* invoke) {
592 CreateIntIntToVoidLocations(arena_, invoke);
593}
594
595void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeLongNative(HInvoke* invoke) {
596 ArmVIXLAssembler* assembler = GetAssembler();
597 // Ignore upper 4B of long address.
598 vixl32::Register addr = LowRegisterFrom(invoke->GetLocations()->InAt(0));
599 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
600 // exception. So we can't use ldrd as addr may be unaligned.
Scott Wakelingb77051e2016-11-21 19:46:00 +0000601 __ Str(LowRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100602 __ Str(HighRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr, 4));
603}
604
605void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeShortNative(HInvoke* invoke) {
606 CreateIntIntToVoidLocations(arena_, invoke);
607}
608
609void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeShortNative(HInvoke* invoke) {
610 ArmVIXLAssembler* assembler = GetAssembler();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000611 __ Strh(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100612}
613
614void IntrinsicLocationsBuilderARMVIXL::VisitThreadCurrentThread(HInvoke* invoke) {
615 LocationSummary* locations = new (arena_) LocationSummary(invoke,
616 LocationSummary::kNoCall,
617 kIntrinsified);
618 locations->SetOut(Location::RequiresRegister());
619}
620
621void IntrinsicCodeGeneratorARMVIXL::VisitThreadCurrentThread(HInvoke* invoke) {
622 ArmVIXLAssembler* assembler = GetAssembler();
623 __ Ldr(OutputRegister(invoke),
624 MemOperand(tr, Thread::PeerOffset<kArmPointerSize>().Int32Value()));
625}
626
627static void GenUnsafeGet(HInvoke* invoke,
628 Primitive::Type type,
629 bool is_volatile,
630 CodeGeneratorARMVIXL* codegen) {
631 LocationSummary* locations = invoke->GetLocations();
632 ArmVIXLAssembler* assembler = codegen->GetAssembler();
633 Location base_loc = locations->InAt(1);
634 vixl32::Register base = InputRegisterAt(invoke, 1); // Object pointer.
635 Location offset_loc = locations->InAt(2);
636 vixl32::Register offset = LowRegisterFrom(offset_loc); // Long offset, lo part only.
637 Location trg_loc = locations->Out();
638
639 switch (type) {
640 case Primitive::kPrimInt: {
641 vixl32::Register trg = RegisterFrom(trg_loc);
642 __ Ldr(trg, MemOperand(base, offset));
643 if (is_volatile) {
644 __ Dmb(vixl32::ISH);
645 }
646 break;
647 }
648
649 case Primitive::kPrimNot: {
650 vixl32::Register trg = RegisterFrom(trg_loc);
651 if (kEmitCompilerReadBarrier) {
652 if (kUseBakerReadBarrier) {
653 Location temp = locations->GetTemp(0);
654 codegen->GenerateReferenceLoadWithBakerReadBarrier(
655 invoke, trg_loc, base, 0U, offset_loc, TIMES_1, temp, /* needs_null_check */ false);
656 if (is_volatile) {
657 __ Dmb(vixl32::ISH);
658 }
659 } else {
660 __ Ldr(trg, MemOperand(base, offset));
661 if (is_volatile) {
662 __ Dmb(vixl32::ISH);
663 }
664 codegen->GenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
665 }
666 } else {
667 __ Ldr(trg, MemOperand(base, offset));
668 if (is_volatile) {
669 __ Dmb(vixl32::ISH);
670 }
671 assembler->MaybeUnpoisonHeapReference(trg);
672 }
673 break;
674 }
675
676 case Primitive::kPrimLong: {
677 vixl32::Register trg_lo = LowRegisterFrom(trg_loc);
678 vixl32::Register trg_hi = HighRegisterFrom(trg_loc);
679 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
680 __ Ldrexd(trg_lo, trg_hi, MemOperand(base, offset));
681 } else {
682 __ Ldrd(trg_lo, trg_hi, MemOperand(base, offset));
683 }
684 if (is_volatile) {
685 __ Dmb(vixl32::ISH);
686 }
687 break;
688 }
689
690 default:
691 LOG(FATAL) << "Unexpected type " << type;
692 UNREACHABLE();
693 }
694}
695
696static void CreateIntIntIntToIntLocations(ArenaAllocator* arena,
697 HInvoke* invoke,
698 Primitive::Type type) {
699 bool can_call = kEmitCompilerReadBarrier &&
700 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
701 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
702 LocationSummary* locations = new (arena) LocationSummary(invoke,
703 (can_call
704 ? LocationSummary::kCallOnSlowPath
705 : LocationSummary::kNoCall),
706 kIntrinsified);
707 if (can_call && kUseBakerReadBarrier) {
708 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
709 }
710 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
711 locations->SetInAt(1, Location::RequiresRegister());
712 locations->SetInAt(2, Location::RequiresRegister());
713 locations->SetOut(Location::RequiresRegister(),
714 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
715 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
716 // We need a temporary register for the read barrier marking slow
717 // path in InstructionCodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier.
718 locations->AddTemp(Location::RequiresRegister());
719 }
720}
721
722void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGet(HInvoke* invoke) {
723 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
724}
725void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetVolatile(HInvoke* invoke) {
726 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
727}
728void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetLong(HInvoke* invoke) {
729 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
730}
731void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
732 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
733}
734void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetObject(HInvoke* invoke) {
735 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
736}
737void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
738 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
739}
740
741void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGet(HInvoke* invoke) {
742 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
743}
744void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetVolatile(HInvoke* invoke) {
745 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
746}
747void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetLong(HInvoke* invoke) {
748 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
749}
750void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
751 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
752}
753void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetObject(HInvoke* invoke) {
754 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
755}
756void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
757 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
758}
759
760static void CreateIntIntIntIntToVoid(ArenaAllocator* arena,
761 const ArmInstructionSetFeatures& features,
762 Primitive::Type type,
763 bool is_volatile,
764 HInvoke* invoke) {
765 LocationSummary* locations = new (arena) LocationSummary(invoke,
766 LocationSummary::kNoCall,
767 kIntrinsified);
768 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
769 locations->SetInAt(1, Location::RequiresRegister());
770 locations->SetInAt(2, Location::RequiresRegister());
771 locations->SetInAt(3, Location::RequiresRegister());
772
773 if (type == Primitive::kPrimLong) {
774 // Potentially need temps for ldrexd-strexd loop.
775 if (is_volatile && !features.HasAtomicLdrdAndStrd()) {
776 locations->AddTemp(Location::RequiresRegister()); // Temp_lo.
777 locations->AddTemp(Location::RequiresRegister()); // Temp_hi.
778 }
779 } else if (type == Primitive::kPrimNot) {
780 // Temps for card-marking.
781 locations->AddTemp(Location::RequiresRegister()); // Temp.
782 locations->AddTemp(Location::RequiresRegister()); // Card.
783 }
784}
785
786void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePut(HInvoke* invoke) {
787 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ false, invoke);
788}
789void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutOrdered(HInvoke* invoke) {
790 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ false, invoke);
791}
792void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutVolatile(HInvoke* invoke) {
793 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ true, invoke);
794}
795void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutObject(HInvoke* invoke) {
796 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ false, invoke);
797}
798void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
799 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ false, invoke);
800}
801void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
802 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ true, invoke);
803}
804void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutLong(HInvoke* invoke) {
805 CreateIntIntIntIntToVoid(
806 arena_, features_, Primitive::kPrimLong, /* is_volatile */ false, invoke);
807}
808void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutLongOrdered(HInvoke* invoke) {
809 CreateIntIntIntIntToVoid(
810 arena_, features_, Primitive::kPrimLong, /* is_volatile */ false, invoke);
811}
812void IntrinsicLocationsBuilderARMVIXL::VisitUnsafePutLongVolatile(HInvoke* invoke) {
813 CreateIntIntIntIntToVoid(
814 arena_, features_, Primitive::kPrimLong, /* is_volatile */ true, invoke);
815}
816
817static void GenUnsafePut(LocationSummary* locations,
818 Primitive::Type type,
819 bool is_volatile,
820 bool is_ordered,
821 CodeGeneratorARMVIXL* codegen) {
822 ArmVIXLAssembler* assembler = codegen->GetAssembler();
823
824 vixl32::Register base = RegisterFrom(locations->InAt(1)); // Object pointer.
825 vixl32::Register offset = LowRegisterFrom(locations->InAt(2)); // Long offset, lo part only.
826 vixl32::Register value;
827
828 if (is_volatile || is_ordered) {
829 __ Dmb(vixl32::ISH);
830 }
831
832 if (type == Primitive::kPrimLong) {
833 vixl32::Register value_lo = LowRegisterFrom(locations->InAt(3));
834 vixl32::Register value_hi = HighRegisterFrom(locations->InAt(3));
835 value = value_lo;
836 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
837 vixl32::Register temp_lo = RegisterFrom(locations->GetTemp(0));
838 vixl32::Register temp_hi = RegisterFrom(locations->GetTemp(1));
839 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
840 const vixl32::Register temp_reg = temps.Acquire();
841
842 __ Add(temp_reg, base, offset);
843 vixl32::Label loop_head;
844 __ Bind(&loop_head);
Scott Wakelingb77051e2016-11-21 19:46:00 +0000845 __ Ldrexd(temp_lo, temp_hi, MemOperand(temp_reg));
846 __ Strexd(temp_lo, value_lo, value_hi, MemOperand(temp_reg));
Anton Kirilov5ec62182016-10-13 20:16:02 +0100847 __ Cmp(temp_lo, 0);
848 __ B(ne, &loop_head);
849 } else {
850 __ Strd(value_lo, value_hi, MemOperand(base, offset));
851 }
852 } else {
853 value = RegisterFrom(locations->InAt(3));
854 vixl32::Register source = value;
855 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
856 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
857 __ Mov(temp, value);
858 assembler->PoisonHeapReference(temp);
859 source = temp;
860 }
861 __ Str(source, MemOperand(base, offset));
862 }
863
864 if (is_volatile) {
865 __ Dmb(vixl32::ISH);
866 }
867
868 if (type == Primitive::kPrimNot) {
869 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
870 vixl32::Register card = RegisterFrom(locations->GetTemp(1));
871 bool value_can_be_null = true; // TODO: Worth finding out this information?
872 codegen->MarkGCCard(temp, card, base, value, value_can_be_null);
873 }
874}
875
876void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePut(HInvoke* invoke) {
877 GenUnsafePut(invoke->GetLocations(),
878 Primitive::kPrimInt,
879 /* is_volatile */ false,
880 /* is_ordered */ false,
881 codegen_);
882}
883void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutOrdered(HInvoke* invoke) {
884 GenUnsafePut(invoke->GetLocations(),
885 Primitive::kPrimInt,
886 /* is_volatile */ false,
887 /* is_ordered */ true,
888 codegen_);
889}
890void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutVolatile(HInvoke* invoke) {
891 GenUnsafePut(invoke->GetLocations(),
892 Primitive::kPrimInt,
893 /* is_volatile */ true,
894 /* is_ordered */ false,
895 codegen_);
896}
897void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutObject(HInvoke* invoke) {
898 GenUnsafePut(invoke->GetLocations(),
899 Primitive::kPrimNot,
900 /* is_volatile */ false,
901 /* is_ordered */ false,
902 codegen_);
903}
904void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
905 GenUnsafePut(invoke->GetLocations(),
906 Primitive::kPrimNot,
907 /* is_volatile */ false,
908 /* is_ordered */ true,
909 codegen_);
910}
911void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
912 GenUnsafePut(invoke->GetLocations(),
913 Primitive::kPrimNot,
914 /* is_volatile */ true,
915 /* is_ordered */ false,
916 codegen_);
917}
918void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutLong(HInvoke* invoke) {
919 GenUnsafePut(invoke->GetLocations(),
920 Primitive::kPrimLong,
921 /* is_volatile */ false,
922 /* is_ordered */ false,
923 codegen_);
924}
925void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutLongOrdered(HInvoke* invoke) {
926 GenUnsafePut(invoke->GetLocations(),
927 Primitive::kPrimLong,
928 /* is_volatile */ false,
929 /* is_ordered */ true,
930 codegen_);
931}
932void IntrinsicCodeGeneratorARMVIXL::VisitUnsafePutLongVolatile(HInvoke* invoke) {
933 GenUnsafePut(invoke->GetLocations(),
934 Primitive::kPrimLong,
935 /* is_volatile */ true,
936 /* is_ordered */ false,
937 codegen_);
938}
939
940static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena,
941 HInvoke* invoke,
942 Primitive::Type type) {
943 bool can_call = kEmitCompilerReadBarrier &&
944 kUseBakerReadBarrier &&
945 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
946 LocationSummary* locations = new (arena) LocationSummary(invoke,
947 (can_call
948 ? LocationSummary::kCallOnSlowPath
949 : LocationSummary::kNoCall),
950 kIntrinsified);
951 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
952 locations->SetInAt(1, Location::RequiresRegister());
953 locations->SetInAt(2, Location::RequiresRegister());
954 locations->SetInAt(3, Location::RequiresRegister());
955 locations->SetInAt(4, Location::RequiresRegister());
956
957 // If heap poisoning is enabled, we don't want the unpoisoning
958 // operations to potentially clobber the output. Likewise when
959 // emitting a (Baker) read barrier, which may call.
960 Location::OutputOverlap overlaps =
961 ((kPoisonHeapReferences && type == Primitive::kPrimNot) || can_call)
962 ? Location::kOutputOverlap
963 : Location::kNoOutputOverlap;
964 locations->SetOut(Location::RequiresRegister(), overlaps);
965
966 // Temporary registers used in CAS. In the object case
967 // (UnsafeCASObject intrinsic), these are also used for
968 // card-marking, and possibly for (Baker) read barrier.
969 locations->AddTemp(Location::RequiresRegister()); // Pointer.
970 locations->AddTemp(Location::RequiresRegister()); // Temp 1.
971}
972
973static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARMVIXL* codegen) {
974 DCHECK_NE(type, Primitive::kPrimLong);
975
976 ArmVIXLAssembler* assembler = codegen->GetAssembler();
977 LocationSummary* locations = invoke->GetLocations();
978
979 Location out_loc = locations->Out();
980 vixl32::Register out = OutputRegister(invoke); // Boolean result.
981
982 vixl32::Register base = InputRegisterAt(invoke, 1); // Object pointer.
983 Location offset_loc = locations->InAt(2);
984 vixl32::Register offset = LowRegisterFrom(offset_loc); // Offset (discard high 4B).
985 vixl32::Register expected = InputRegisterAt(invoke, 3); // Expected.
986 vixl32::Register value = InputRegisterAt(invoke, 4); // Value.
987
988 Location tmp_ptr_loc = locations->GetTemp(0);
989 vixl32::Register tmp_ptr = RegisterFrom(tmp_ptr_loc); // Pointer to actual memory.
990 vixl32::Register tmp = RegisterFrom(locations->GetTemp(1)); // Value in memory.
991
992 if (type == Primitive::kPrimNot) {
993 // The only read barrier implementation supporting the
994 // UnsafeCASObject intrinsic is the Baker-style read barriers.
995 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
996
997 // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
998 // object and scan the receiver at the next GC for nothing.
999 bool value_can_be_null = true; // TODO: Worth finding out this information?
1000 codegen->MarkGCCard(tmp_ptr, tmp, base, value, value_can_be_null);
1001
1002 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1003 // Need to make sure the reference stored in the field is a to-space
1004 // one before attempting the CAS or the CAS could fail incorrectly.
1005 codegen->GenerateReferenceLoadWithBakerReadBarrier(
1006 invoke,
1007 out_loc, // Unused, used only as a "temporary" within the read barrier.
1008 base,
1009 /* offset */ 0u,
1010 /* index */ offset_loc,
1011 ScaleFactor::TIMES_1,
1012 tmp_ptr_loc,
1013 /* needs_null_check */ false,
1014 /* always_update_field */ true,
1015 &tmp);
1016 }
1017 }
1018
1019 // Prevent reordering with prior memory operations.
1020 // Emit a DMB ISH instruction instead of an DMB ISHST one, as the
1021 // latter allows a preceding load to be delayed past the STXR
1022 // instruction below.
1023 __ Dmb(vixl32::ISH);
1024
1025 __ Add(tmp_ptr, base, offset);
1026
1027 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1028 codegen->GetAssembler()->PoisonHeapReference(expected);
1029 if (value.Is(expected)) {
1030 // Do not poison `value`, as it is the same register as
1031 // `expected`, which has just been poisoned.
1032 } else {
1033 codegen->GetAssembler()->PoisonHeapReference(value);
1034 }
1035 }
1036
1037 // do {
1038 // tmp = [r_ptr] - expected;
1039 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
1040 // result = tmp != 0;
1041
1042 vixl32::Label loop_head;
1043 __ Bind(&loop_head);
1044
Scott Wakelingb77051e2016-11-21 19:46:00 +00001045 __ Ldrex(tmp, MemOperand(tmp_ptr));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001046
1047 __ Subs(tmp, tmp, expected);
1048
1049 {
1050 AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
1051 3 * kMaxInstructionSizeInBytes,
1052 CodeBufferCheckScope::kMaximumSize);
1053
1054 __ itt(eq);
Scott Wakelingb77051e2016-11-21 19:46:00 +00001055 __ strex(eq, tmp, value, MemOperand(tmp_ptr));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001056 __ cmp(eq, tmp, 1);
1057 }
1058
1059 __ B(eq, &loop_head);
1060
1061 __ Dmb(vixl32::ISH);
1062
1063 __ Rsbs(out, tmp, 1);
1064
1065 {
1066 AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
1067 2 * kMaxInstructionSizeInBytes,
1068 CodeBufferCheckScope::kMaximumSize);
1069
1070 __ it(cc);
1071 __ mov(cc, out, 0);
1072 }
1073
1074 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
1075 codegen->GetAssembler()->UnpoisonHeapReference(expected);
1076 if (value.Is(expected)) {
1077 // Do not unpoison `value`, as it is the same register as
1078 // `expected`, which has just been unpoisoned.
1079 } else {
1080 codegen->GetAssembler()->UnpoisonHeapReference(value);
1081 }
1082 }
1083}
1084
1085void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeCASInt(HInvoke* invoke) {
1086 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimInt);
1087}
1088void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeCASObject(HInvoke* invoke) {
1089 // The only read barrier implementation supporting the
1090 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1091 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
1092 return;
1093 }
1094
1095 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimNot);
1096}
1097void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeCASInt(HInvoke* invoke) {
1098 GenCas(invoke, Primitive::kPrimInt, codegen_);
1099}
1100void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeCASObject(HInvoke* invoke) {
1101 // The only read barrier implementation supporting the
1102 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1103 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1104
1105 GenCas(invoke, Primitive::kPrimNot, codegen_);
1106}
1107
1108void IntrinsicLocationsBuilderARMVIXL::VisitStringCompareTo(HInvoke* invoke) {
1109 // The inputs plus one temp.
1110 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1111 invoke->InputAt(1)->CanBeNull()
1112 ? LocationSummary::kCallOnSlowPath
1113 : LocationSummary::kNoCall,
1114 kIntrinsified);
1115 locations->SetInAt(0, Location::RequiresRegister());
1116 locations->SetInAt(1, Location::RequiresRegister());
1117 locations->AddTemp(Location::RequiresRegister());
1118 locations->AddTemp(Location::RequiresRegister());
1119 locations->AddTemp(Location::RequiresRegister());
1120 // Need temporary registers for String compression's feature.
1121 if (mirror::kUseStringCompression) {
1122 locations->AddTemp(Location::RequiresRegister());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001123 }
1124 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1125}
1126
1127void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) {
1128 ArmVIXLAssembler* assembler = GetAssembler();
1129 LocationSummary* locations = invoke->GetLocations();
1130
1131 vixl32::Register str = InputRegisterAt(invoke, 0);
1132 vixl32::Register arg = InputRegisterAt(invoke, 1);
1133 vixl32::Register out = OutputRegister(invoke);
1134
1135 vixl32::Register temp0 = RegisterFrom(locations->GetTemp(0));
1136 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1137 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(2));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001138 vixl32::Register temp3;
Anton Kirilov5ec62182016-10-13 20:16:02 +01001139 if (mirror::kUseStringCompression) {
1140 temp3 = RegisterFrom(locations->GetTemp(3));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001141 }
1142
1143 vixl32::Label loop;
1144 vixl32::Label find_char_diff;
1145 vixl32::Label end;
1146 vixl32::Label different_compression;
1147
1148 // Get offsets of count and value fields within a string object.
1149 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1150 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1151
1152 // Note that the null check must have been done earlier.
1153 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1154
1155 // Take slow path and throw if input can be and is null.
1156 SlowPathCodeARMVIXL* slow_path = nullptr;
1157 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1158 if (can_slow_path) {
1159 slow_path = new (GetAllocator()) IntrinsicSlowPathARMVIXL(invoke);
1160 codegen_->AddSlowPath(slow_path);
xueliang.zhongf51bc622016-11-04 09:23:32 +00001161 __ CompareAndBranchIfZero(arg, slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001162 }
1163
1164 // Reference equality check, return 0 if same reference.
1165 __ Subs(out, str, arg);
1166 __ B(eq, &end);
1167
Anton Kirilov5ec62182016-10-13 20:16:02 +01001168 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001169 // Load `count` fields of this and argument strings.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001170 __ Ldr(temp3, MemOperand(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001171 __ Ldr(temp2, MemOperand(arg, count_offset));
1172 // Extract lengths from the `count` fields.
1173 __ Lsr(temp0, temp3, 1u);
1174 __ Lsr(temp1, temp2, 1u);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001175 } else {
1176 // Load lengths of this and argument strings.
1177 __ Ldr(temp0, MemOperand(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001178 __ Ldr(temp1, MemOperand(arg, count_offset));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001179 }
1180 // out = length diff.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001181 __ Subs(out, temp0, temp1);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001182 // temp0 = min(len(str), len(arg)).
1183
1184 {
1185 AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
1186 2 * kMaxInstructionSizeInBytes,
1187 CodeBufferCheckScope::kMaximumSize);
1188
1189 __ it(gt);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001190 __ mov(gt, temp0, temp1);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001191 }
1192
Anton Kirilov5ec62182016-10-13 20:16:02 +01001193 // Shorter string is empty?
xueliang.zhongf51bc622016-11-04 09:23:32 +00001194 // Note that mirror::kUseStringCompression==true introduces lots of instructions,
1195 // which makes &end label far away from this branch and makes it not 'CBZ-encodable'.
1196 __ CompareAndBranchIfZero(temp0, &end, mirror::kUseStringCompression);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001197
1198 if (mirror::kUseStringCompression) {
1199 // Check if both strings using same compression style to use this comparison loop.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001200 __ Eors(temp2, temp2, temp3);
1201 __ Lsrs(temp2, temp2, 1u);
1202 __ B(cs, &different_compression);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001203 // For string compression, calculate the number of bytes to compare (not chars).
1204 // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001205 __ Lsls(temp3, temp3, 31u); // Extract purely the compression flag.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001206
1207 AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
1208 2 * kMaxInstructionSizeInBytes,
1209 CodeBufferCheckScope::kMaximumSize);
1210
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001211 __ it(ne);
1212 __ add(ne, temp0, temp0, temp0);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001213 }
1214
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001215 // Store offset of string value in preparation for comparison loop.
1216 __ Mov(temp1, value_offset);
1217
Anton Kirilov5ec62182016-10-13 20:16:02 +01001218 // Assertions that must hold in order to compare multiple characters at a time.
1219 CHECK_ALIGNED(value_offset, 8);
1220 static_assert(IsAligned<8>(kObjectAlignment),
1221 "String data must be 8-byte aligned for unrolled CompareTo loop.");
1222
Scott Wakelingb77051e2016-11-21 19:46:00 +00001223 const unsigned char_size = Primitive::ComponentSize(Primitive::kPrimChar);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001224 DCHECK_EQ(char_size, 2u);
1225
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001226 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
1227
Anton Kirilov5ec62182016-10-13 20:16:02 +01001228 vixl32::Label find_char_diff_2nd_cmp;
1229 // Unrolled loop comparing 4x16-bit chars per iteration (ok because of string data alignment).
1230 __ Bind(&loop);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001231 vixl32::Register temp_reg = temps.Acquire();
Anton Kirilov5ec62182016-10-13 20:16:02 +01001232 __ Ldr(temp_reg, MemOperand(str, temp1));
1233 __ Ldr(temp2, MemOperand(arg, temp1));
1234 __ Cmp(temp_reg, temp2);
1235 __ B(ne, &find_char_diff);
1236 __ Add(temp1, temp1, char_size * 2);
1237
1238 __ Ldr(temp_reg, MemOperand(str, temp1));
1239 __ Ldr(temp2, MemOperand(arg, temp1));
1240 __ Cmp(temp_reg, temp2);
1241 __ B(ne, &find_char_diff_2nd_cmp);
1242 __ Add(temp1, temp1, char_size * 2);
1243 // With string compression, we have compared 8 bytes, otherwise 4 chars.
1244 __ Subs(temp0, temp0, (mirror::kUseStringCompression ? 8 : 4));
1245 __ B(hi, &loop);
1246 __ B(&end);
1247
1248 __ Bind(&find_char_diff_2nd_cmp);
1249 if (mirror::kUseStringCompression) {
1250 __ Subs(temp0, temp0, 4); // 4 bytes previously compared.
1251 __ B(ls, &end); // Was the second comparison fully beyond the end?
1252 } else {
1253 // Without string compression, we can start treating temp0 as signed
1254 // and rely on the signed comparison below.
1255 __ Sub(temp0, temp0, 2);
1256 }
1257
1258 // Find the single character difference.
1259 __ Bind(&find_char_diff);
1260 // Get the bit position of the first character that differs.
1261 __ Eor(temp1, temp2, temp_reg);
1262 __ Rbit(temp1, temp1);
1263 __ Clz(temp1, temp1);
1264
1265 // temp0 = number of characters remaining to compare.
1266 // (Without string compression, it could be < 1 if a difference is found by the second CMP
1267 // in the comparison loop, and after the end of the shorter string data).
1268
1269 // Without string compression (temp1 >> 4) = character where difference occurs between the last
1270 // two words compared, in the interval [0,1].
1271 // (0 for low half-word different, 1 for high half-word different).
1272 // With string compression, (temp1 << 3) = byte where the difference occurs,
1273 // in the interval [0,3].
1274
1275 // If temp0 <= (temp1 >> (kUseStringCompression ? 3 : 4)), the difference occurs outside
1276 // the remaining string data, so just return length diff (out).
1277 // The comparison is unsigned for string compression, otherwise signed.
1278 __ Cmp(temp0, Operand(temp1, vixl32::LSR, (mirror::kUseStringCompression ? 3 : 4)));
1279 __ B((mirror::kUseStringCompression ? ls : le), &end);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001280
Anton Kirilov5ec62182016-10-13 20:16:02 +01001281 // Extract the characters and calculate the difference.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001282 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001283 // For compressed strings we need to clear 0x7 from temp1, for uncompressed we need to clear
1284 // 0xf. We also need to prepare the character extraction mask `uncompressed ? 0xffffu : 0xffu`.
1285 // The compression flag is now in the highest bit of temp3, so let's play some tricks.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001286 __ Orr(temp3, temp3, 0xffu << 23); // uncompressed ? 0xff800000u : 0x7ff80000u
1287 __ Bic(temp1, temp1, Operand(temp3, vixl32::LSR, 31 - 3)); // &= ~(uncompressed ? 0xfu : 0x7u)
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001288 __ Asr(temp3, temp3, 7u); // uncompressed ? 0xffff0000u : 0xff0000u.
1289 __ Lsr(temp2, temp2, temp1); // Extract second character.
1290 __ Lsr(temp3, temp3, 16u); // uncompressed ? 0xffffu : 0xffu
1291 __ Lsr(out, temp_reg, temp1); // Extract first character.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001292 __ And(temp2, temp2, temp3);
1293 __ And(out, out, temp3);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001294 } else {
Anton Kirilovb88c4842016-11-14 14:37:00 +00001295 __ Bic(temp1, temp1, 0xf);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001296 __ Lsr(temp2, temp2, temp1);
1297 __ Lsr(out, temp_reg, temp1);
Anton Kirilovb88c4842016-11-14 14:37:00 +00001298 __ Movt(temp2, 0);
1299 __ Movt(out, 0);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001300 }
Anton Kirilov5ec62182016-10-13 20:16:02 +01001301
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001302 __ Sub(out, out, temp2);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001303 temps.Release(temp_reg);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001304
1305 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001306 __ B(&end);
1307 __ Bind(&different_compression);
1308
1309 // Comparison for different compression style.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001310 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
1311 DCHECK_EQ(c_char_size, 1u);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001312
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001313 // We want to free up the temp3, currently holding `str.count`, for comparison.
1314 // So, we move it to the bottom bit of the iteration count `temp0` which we tnen
1315 // need to treat as unsigned. Start by freeing the bit with an ADD and continue
1316 // further down by a LSRS+SBC which will flip the meaning of the flag but allow
1317 // `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001318 __ Add(temp0, temp0, temp0); // Unlike LSL, this ADD is always 16-bit.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001319 // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001320 __ Mov(temp1, str);
1321 __ Mov(temp2, arg);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001322 __ Lsrs(temp3, temp3, 1u); // Continue the move of the compression flag.
1323 {
1324 AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
1325 3 * kMaxInstructionSizeInBytes,
1326 CodeBufferCheckScope::kMaximumSize);
1327 __ itt(cs); // Interleave with selection of temp1 and temp2.
1328 __ mov(cs, temp1, arg); // Preserves flags.
1329 __ mov(cs, temp2, str); // Preserves flags.
1330 }
Anton Kirilovb88c4842016-11-14 14:37:00 +00001331 __ Sbc(temp0, temp0, 0); // Complete the move of the compression flag.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001332
1333 // Adjust temp1 and temp2 from string pointers to data pointers.
Anton Kirilovb88c4842016-11-14 14:37:00 +00001334 __ Add(temp1, temp1, value_offset);
1335 __ Add(temp2, temp2, value_offset);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001336
1337 vixl32::Label different_compression_loop;
1338 vixl32::Label different_compression_diff;
1339
1340 // Main loop for different compression.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001341 temp_reg = temps.Acquire();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001342 __ Bind(&different_compression_loop);
1343 __ Ldrb(temp_reg, MemOperand(temp1, c_char_size, PostIndex));
1344 __ Ldrh(temp3, MemOperand(temp2, char_size, PostIndex));
Anton Kirilovb88c4842016-11-14 14:37:00 +00001345 __ Cmp(temp_reg, temp3);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001346 __ B(ne, &different_compression_diff);
1347 __ Subs(temp0, temp0, 2);
1348 __ B(hi, &different_compression_loop);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001349 __ B(&end);
1350
1351 // Calculate the difference.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001352 __ Bind(&different_compression_diff);
1353 __ Sub(out, temp_reg, temp3);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001354 temps.Release(temp_reg);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001355 // Flip the difference if the `arg` is compressed.
1356 // `temp0` contains inverted `str` compression flag, i.e the same as `arg` compression flag.
1357 __ Lsrs(temp0, temp0, 1u);
1358 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1359 "Expecting 0=compressed, 1=uncompressed");
1360
1361 AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
1362 2 * kMaxInstructionSizeInBytes,
1363 CodeBufferCheckScope::kMaximumSize);
1364 __ it(cc);
1365 __ rsb(cc, out, out, 0);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001366 }
1367
1368 __ Bind(&end);
1369
1370 if (can_slow_path) {
1371 __ Bind(slow_path->GetExitLabel());
1372 }
1373}
1374
1375void IntrinsicLocationsBuilderARMVIXL::VisitStringEquals(HInvoke* invoke) {
1376 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1377 LocationSummary::kNoCall,
1378 kIntrinsified);
1379 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1380 locations->SetInAt(0, Location::RequiresRegister());
1381 locations->SetInAt(1, Location::RequiresRegister());
1382 // Temporary registers to store lengths of strings and for calculations.
1383 // Using instruction cbz requires a low register, so explicitly set a temp to be R0.
1384 locations->AddTemp(LocationFrom(r0));
1385 locations->AddTemp(Location::RequiresRegister());
1386 locations->AddTemp(Location::RequiresRegister());
1387
1388 locations->SetOut(Location::RequiresRegister());
1389}
1390
1391void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) {
1392 ArmVIXLAssembler* assembler = GetAssembler();
1393 LocationSummary* locations = invoke->GetLocations();
1394
1395 vixl32::Register str = InputRegisterAt(invoke, 0);
1396 vixl32::Register arg = InputRegisterAt(invoke, 1);
1397 vixl32::Register out = OutputRegister(invoke);
1398
1399 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
1400 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
1401 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(2));
1402
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001403 vixl32::Label loop;
Anton Kirilov5ec62182016-10-13 20:16:02 +01001404 vixl32::Label end;
1405 vixl32::Label return_true;
1406 vixl32::Label return_false;
1407
1408 // Get offsets of count, value, and class fields within a string object.
1409 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
1410 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1411 const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
1412
1413 // Note that the null check must have been done earlier.
1414 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1415
1416 StringEqualsOptimizations optimizations(invoke);
1417 if (!optimizations.GetArgumentNotNull()) {
1418 // Check if input is null, return false if it is.
xueliang.zhongf51bc622016-11-04 09:23:32 +00001419 __ CompareAndBranchIfZero(arg, &return_false, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001420 }
1421
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001422 // Reference equality check, return true if same reference.
1423 __ Cmp(str, arg);
1424 __ B(eq, &return_true);
1425
Anton Kirilov5ec62182016-10-13 20:16:02 +01001426 if (!optimizations.GetArgumentIsString()) {
1427 // Instanceof check for the argument by comparing class fields.
1428 // All string objects must have the same type since String cannot be subclassed.
1429 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1430 // If the argument is a string object, its class field must be equal to receiver's class field.
1431 __ Ldr(temp, MemOperand(str, class_offset));
1432 __ Ldr(temp1, MemOperand(arg, class_offset));
1433 __ Cmp(temp, temp1);
1434 __ B(ne, &return_false);
1435 }
1436
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001437 // Load `count` fields of this and argument strings.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001438 __ Ldr(temp, MemOperand(str, count_offset));
1439 __ Ldr(temp1, MemOperand(arg, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001440 // Check if `count` fields are equal, return false if they're not.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001441 // Also compares the compression style, if differs return false.
1442 __ Cmp(temp, temp1);
1443 __ B(ne, &return_false);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001444 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
1445 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1446 "Expecting 0=compressed, 1=uncompressed");
xueliang.zhongf51bc622016-11-04 09:23:32 +00001447 __ CompareAndBranchIfZero(temp, &return_true, /* far_target */ false);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001448
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001449 // Assertions that must hold in order to compare strings 4 bytes at a time.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001450 DCHECK_ALIGNED(value_offset, 4);
1451 static_assert(IsAligned<4>(kObjectAlignment), "String data must be aligned for fast compare.");
1452
1453 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001454 // For string compression, calculate the number of bytes to compare (not chars).
1455 // This could in theory exceed INT32_MAX, so treat temp as unsigned.
1456 __ Lsrs(temp, temp, 1u); // Extract length and check compression flag.
1457 AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
1458 2 * kMaxInstructionSizeInBytes,
1459 CodeBufferCheckScope::kMaximumSize);
1460 __ it(cs); // If uncompressed,
1461 __ add(cs, temp, temp, temp); // double the byte count.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001462 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001463
1464 // Store offset of string value in preparation for comparison loop.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001465 __ Mov(temp1, value_offset);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001466
1467 // Loop to compare strings 4 bytes at a time starting at the front of the string.
1468 // Ok to do this because strings are zero-padded to kObjectAlignment.
Anton Kirilov5ec62182016-10-13 20:16:02 +01001469 __ Bind(&loop);
1470 __ Ldr(out, MemOperand(str, temp1));
1471 __ Ldr(temp2, MemOperand(arg, temp1));
Scott Wakelingb77051e2016-11-21 19:46:00 +00001472 __ Add(temp1, temp1, Operand::From(sizeof(uint32_t)));
Anton Kirilov5ec62182016-10-13 20:16:02 +01001473 __ Cmp(out, temp2);
1474 __ B(ne, &return_false);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001475 // With string compression, we have compared 4 bytes, otherwise 2 chars.
1476 __ Subs(temp, temp, mirror::kUseStringCompression ? 4 : 2);
1477 __ B(hi, &loop);
Anton Kirilov5ec62182016-10-13 20:16:02 +01001478
1479 // Return true and exit the function.
1480 // If loop does not result in returning false, we return true.
1481 __ Bind(&return_true);
1482 __ Mov(out, 1);
1483 __ B(&end);
1484
1485 // Return false and exit the function.
1486 __ Bind(&return_false);
1487 __ Mov(out, 0);
1488 __ Bind(&end);
1489}
1490
1491static void GenerateVisitStringIndexOf(HInvoke* invoke,
1492 ArmVIXLAssembler* assembler,
1493 CodeGeneratorARMVIXL* codegen,
1494 ArenaAllocator* allocator,
1495 bool start_at_zero) {
1496 LocationSummary* locations = invoke->GetLocations();
1497
1498 // Note that the null check must have been done earlier.
1499 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1500
1501 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
1502 // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
1503 SlowPathCodeARMVIXL* slow_path = nullptr;
1504 HInstruction* code_point = invoke->InputAt(1);
1505 if (code_point->IsIntConstant()) {
1506 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) >
1507 std::numeric_limits<uint16_t>::max()) {
1508 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1509 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1510 slow_path = new (allocator) IntrinsicSlowPathARMVIXL(invoke);
1511 codegen->AddSlowPath(slow_path);
1512 __ B(slow_path->GetEntryLabel());
1513 __ Bind(slow_path->GetExitLabel());
1514 return;
1515 }
1516 } else if (code_point->GetType() != Primitive::kPrimChar) {
1517 vixl32::Register char_reg = InputRegisterAt(invoke, 1);
1518 // 0xffff is not modified immediate but 0x10000 is, so use `>= 0x10000` instead of `> 0xffff`.
1519 __ Cmp(char_reg, static_cast<uint32_t>(std::numeric_limits<uint16_t>::max()) + 1);
1520 slow_path = new (allocator) IntrinsicSlowPathARMVIXL(invoke);
1521 codegen->AddSlowPath(slow_path);
1522 __ B(hs, slow_path->GetEntryLabel());
1523 }
1524
1525 if (start_at_zero) {
1526 vixl32::Register tmp_reg = RegisterFrom(locations->GetTemp(0));
1527 DCHECK(tmp_reg.Is(r2));
1528 // Start-index = 0.
1529 __ Mov(tmp_reg, 0);
1530 }
1531
1532 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
1533 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
1534
1535 if (slow_path != nullptr) {
1536 __ Bind(slow_path->GetExitLabel());
1537 }
1538}
1539
1540void IntrinsicLocationsBuilderARMVIXL::VisitStringIndexOf(HInvoke* invoke) {
1541 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1542 LocationSummary::kCallOnMainAndSlowPath,
1543 kIntrinsified);
1544 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1545 // best to align the inputs accordingly.
1546 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1547 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1548 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1549 locations->SetOut(LocationFrom(r0));
1550
1551 // Need to send start-index=0.
1552 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
1553}
1554
1555void IntrinsicCodeGeneratorARMVIXL::VisitStringIndexOf(HInvoke* invoke) {
1556 GenerateVisitStringIndexOf(
1557 invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
1558}
1559
1560void IntrinsicLocationsBuilderARMVIXL::VisitStringIndexOfAfter(HInvoke* invoke) {
1561 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1562 LocationSummary::kCallOnMainAndSlowPath,
1563 kIntrinsified);
1564 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1565 // best to align the inputs accordingly.
1566 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1567 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1568 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1569 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1570 locations->SetOut(LocationFrom(r0));
1571}
1572
1573void IntrinsicCodeGeneratorARMVIXL::VisitStringIndexOfAfter(HInvoke* invoke) {
1574 GenerateVisitStringIndexOf(
1575 invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
1576}
1577
1578void IntrinsicLocationsBuilderARMVIXL::VisitStringNewStringFromBytes(HInvoke* invoke) {
1579 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1580 LocationSummary::kCallOnMainAndSlowPath,
1581 kIntrinsified);
1582 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1583 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1584 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1585 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1586 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3)));
1587 locations->SetOut(LocationFrom(r0));
1588}
1589
1590void IntrinsicCodeGeneratorARMVIXL::VisitStringNewStringFromBytes(HInvoke* invoke) {
1591 ArmVIXLAssembler* assembler = GetAssembler();
1592 vixl32::Register byte_array = InputRegisterAt(invoke, 0);
1593 __ Cmp(byte_array, 0);
1594 SlowPathCodeARMVIXL* slow_path = new (GetAllocator()) IntrinsicSlowPathARMVIXL(invoke);
1595 codegen_->AddSlowPath(slow_path);
1596 __ B(eq, slow_path->GetEntryLabel());
1597
1598 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
1599 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
1600 __ Bind(slow_path->GetExitLabel());
1601}
1602
1603void IntrinsicLocationsBuilderARMVIXL::VisitStringNewStringFromChars(HInvoke* invoke) {
1604 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1605 LocationSummary::kCallOnMainOnly,
1606 kIntrinsified);
1607 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1608 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1609 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
1610 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
1611 locations->SetOut(LocationFrom(r0));
1612}
1613
1614void IntrinsicCodeGeneratorARMVIXL::VisitStringNewStringFromChars(HInvoke* invoke) {
1615 // No need to emit code checking whether `locations->InAt(2)` is a null
1616 // pointer, as callers of the native method
1617 //
1618 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1619 //
1620 // all include a null check on `data` before calling that method.
1621 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
1622 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
1623}
1624
1625void IntrinsicLocationsBuilderARMVIXL::VisitStringNewStringFromString(HInvoke* invoke) {
1626 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1627 LocationSummary::kCallOnMainAndSlowPath,
1628 kIntrinsified);
1629 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1630 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
1631 locations->SetOut(LocationFrom(r0));
1632}
1633
1634void IntrinsicCodeGeneratorARMVIXL::VisitStringNewStringFromString(HInvoke* invoke) {
1635 ArmVIXLAssembler* assembler = GetAssembler();
1636 vixl32::Register string_to_copy = InputRegisterAt(invoke, 0);
1637 __ Cmp(string_to_copy, 0);
1638 SlowPathCodeARMVIXL* slow_path = new (GetAllocator()) IntrinsicSlowPathARMVIXL(invoke);
1639 codegen_->AddSlowPath(slow_path);
1640 __ B(eq, slow_path->GetEntryLabel());
1641
1642 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
1643 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
1644
1645 __ Bind(slow_path->GetExitLabel());
1646}
1647
1648void IntrinsicLocationsBuilderARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) {
1649 // The only read barrier implementation supporting the
1650 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1651 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
1652 return;
1653 }
1654
1655 CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
1656 LocationSummary* locations = invoke->GetLocations();
1657 if (locations == nullptr) {
1658 return;
1659 }
1660
1661 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1662 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
1663 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1664
1665 if (src_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(src_pos->GetValue())) {
1666 locations->SetInAt(1, Location::RequiresRegister());
1667 }
1668 if (dest_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(dest_pos->GetValue())) {
1669 locations->SetInAt(3, Location::RequiresRegister());
1670 }
1671 if (length != nullptr && !assembler_->ShifterOperandCanAlwaysHold(length->GetValue())) {
1672 locations->SetInAt(4, Location::RequiresRegister());
1673 }
1674 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1675 // Temporary register IP cannot be used in
1676 // ReadBarrierSystemArrayCopySlowPathARM (because that register
1677 // is clobbered by ReadBarrierMarkRegX entry points). Get an extra
1678 // temporary register from the register allocator.
1679 locations->AddTemp(Location::RequiresRegister());
1680 }
1681}
1682
1683static void CheckPosition(ArmVIXLAssembler* assembler,
1684 Location pos,
1685 vixl32::Register input,
1686 Location length,
1687 SlowPathCodeARMVIXL* slow_path,
1688 vixl32::Register temp,
1689 bool length_is_input_length = false) {
1690 // Where is the length in the Array?
1691 const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
1692
1693 if (pos.IsConstant()) {
1694 int32_t pos_const = Int32ConstantFrom(pos);
1695 if (pos_const == 0) {
1696 if (!length_is_input_length) {
1697 // Check that length(input) >= length.
1698 __ Ldr(temp, MemOperand(input, length_offset));
1699 if (length.IsConstant()) {
1700 __ Cmp(temp, Int32ConstantFrom(length));
1701 } else {
1702 __ Cmp(temp, RegisterFrom(length));
1703 }
1704 __ B(lt, slow_path->GetEntryLabel());
1705 }
1706 } else {
1707 // Check that length(input) >= pos.
1708 __ Ldr(temp, MemOperand(input, length_offset));
1709 __ Subs(temp, temp, pos_const);
1710 __ B(lt, slow_path->GetEntryLabel());
1711
1712 // Check that (length(input) - pos) >= length.
1713 if (length.IsConstant()) {
1714 __ Cmp(temp, Int32ConstantFrom(length));
1715 } else {
1716 __ Cmp(temp, RegisterFrom(length));
1717 }
1718 __ B(lt, slow_path->GetEntryLabel());
1719 }
1720 } else if (length_is_input_length) {
1721 // The only way the copy can succeed is if pos is zero.
1722 vixl32::Register pos_reg = RegisterFrom(pos);
xueliang.zhongf51bc622016-11-04 09:23:32 +00001723 __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001724 } else {
1725 // Check that pos >= 0.
1726 vixl32::Register pos_reg = RegisterFrom(pos);
1727 __ Cmp(pos_reg, 0);
1728 __ B(lt, slow_path->GetEntryLabel());
1729
1730 // Check that pos <= length(input).
1731 __ Ldr(temp, MemOperand(input, length_offset));
1732 __ Subs(temp, temp, pos_reg);
1733 __ B(lt, slow_path->GetEntryLabel());
1734
1735 // Check that (length(input) - pos) >= length.
1736 if (length.IsConstant()) {
1737 __ Cmp(temp, Int32ConstantFrom(length));
1738 } else {
1739 __ Cmp(temp, RegisterFrom(length));
1740 }
1741 __ B(lt, slow_path->GetEntryLabel());
1742 }
1743}
1744
1745void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) {
1746 // The only read barrier implementation supporting the
1747 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1748 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1749
1750 ArmVIXLAssembler* assembler = GetAssembler();
1751 LocationSummary* locations = invoke->GetLocations();
1752
1753 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1754 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
1755 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
1756 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
1757 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
1758
1759 vixl32::Register src = InputRegisterAt(invoke, 0);
1760 Location src_pos = locations->InAt(1);
1761 vixl32::Register dest = InputRegisterAt(invoke, 2);
1762 Location dest_pos = locations->InAt(3);
1763 Location length = locations->InAt(4);
1764 Location temp1_loc = locations->GetTemp(0);
1765 vixl32::Register temp1 = RegisterFrom(temp1_loc);
1766 Location temp2_loc = locations->GetTemp(1);
1767 vixl32::Register temp2 = RegisterFrom(temp2_loc);
1768 Location temp3_loc = locations->GetTemp(2);
1769 vixl32::Register temp3 = RegisterFrom(temp3_loc);
1770
1771 SlowPathCodeARMVIXL* intrinsic_slow_path = new (GetAllocator()) IntrinsicSlowPathARMVIXL(invoke);
1772 codegen_->AddSlowPath(intrinsic_slow_path);
1773
1774 vixl32::Label conditions_on_positions_validated;
1775 SystemArrayCopyOptimizations optimizations(invoke);
1776
1777 // If source and destination are the same, we go to slow path if we need to do
1778 // forward copying.
1779 if (src_pos.IsConstant()) {
1780 int32_t src_pos_constant = Int32ConstantFrom(src_pos);
1781 if (dest_pos.IsConstant()) {
1782 int32_t dest_pos_constant = Int32ConstantFrom(dest_pos);
1783 if (optimizations.GetDestinationIsSource()) {
1784 // Checked when building locations.
1785 DCHECK_GE(src_pos_constant, dest_pos_constant);
1786 } else if (src_pos_constant < dest_pos_constant) {
1787 __ Cmp(src, dest);
1788 __ B(eq, intrinsic_slow_path->GetEntryLabel());
1789 }
1790
1791 // Checked when building locations.
1792 DCHECK(!optimizations.GetDestinationIsSource()
1793 || (src_pos_constant >= Int32ConstantFrom(dest_pos)));
1794 } else {
1795 if (!optimizations.GetDestinationIsSource()) {
1796 __ Cmp(src, dest);
1797 __ B(ne, &conditions_on_positions_validated);
1798 }
1799 __ Cmp(RegisterFrom(dest_pos), src_pos_constant);
1800 __ B(gt, intrinsic_slow_path->GetEntryLabel());
1801 }
1802 } else {
1803 if (!optimizations.GetDestinationIsSource()) {
1804 __ Cmp(src, dest);
1805 __ B(ne, &conditions_on_positions_validated);
1806 }
1807 if (dest_pos.IsConstant()) {
1808 int32_t dest_pos_constant = Int32ConstantFrom(dest_pos);
1809 __ Cmp(RegisterFrom(src_pos), dest_pos_constant);
1810 } else {
1811 __ Cmp(RegisterFrom(src_pos), RegisterFrom(dest_pos));
1812 }
1813 __ B(lt, intrinsic_slow_path->GetEntryLabel());
1814 }
1815
1816 __ Bind(&conditions_on_positions_validated);
1817
1818 if (!optimizations.GetSourceIsNotNull()) {
1819 // Bail out if the source is null.
xueliang.zhongf51bc622016-11-04 09:23:32 +00001820 __ CompareAndBranchIfZero(src, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001821 }
1822
1823 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
1824 // Bail out if the destination is null.
xueliang.zhongf51bc622016-11-04 09:23:32 +00001825 __ CompareAndBranchIfZero(dest, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001826 }
1827
1828 // If the length is negative, bail out.
1829 // We have already checked in the LocationsBuilder for the constant case.
1830 if (!length.IsConstant() &&
1831 !optimizations.GetCountIsSourceLength() &&
1832 !optimizations.GetCountIsDestinationLength()) {
1833 __ Cmp(RegisterFrom(length), 0);
1834 __ B(lt, intrinsic_slow_path->GetEntryLabel());
1835 }
1836
1837 // Validity checks: source.
1838 CheckPosition(assembler,
1839 src_pos,
1840 src,
1841 length,
1842 intrinsic_slow_path,
1843 temp1,
1844 optimizations.GetCountIsSourceLength());
1845
1846 // Validity checks: dest.
1847 CheckPosition(assembler,
1848 dest_pos,
1849 dest,
1850 length,
1851 intrinsic_slow_path,
1852 temp1,
1853 optimizations.GetCountIsDestinationLength());
1854
1855 if (!optimizations.GetDoesNotNeedTypeCheck()) {
1856 // Check whether all elements of the source array are assignable to the component
1857 // type of the destination array. We do two checks: the classes are the same,
1858 // or the destination is Object[]. If none of these checks succeed, we go to the
1859 // slow path.
1860
1861 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1862 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1863 // /* HeapReference<Class> */ temp1 = src->klass_
1864 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1865 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
1866 // Bail out if the source is not a non primitive array.
1867 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1868 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1869 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
xueliang.zhongf51bc622016-11-04 09:23:32 +00001870 __ CompareAndBranchIfZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001871 // If heap poisoning is enabled, `temp1` has been unpoisoned
1872 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1873 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
1874 __ Ldrh(temp1, MemOperand(temp1, primitive_offset));
1875 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00001876 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001877 }
1878
1879 // /* HeapReference<Class> */ temp1 = dest->klass_
1880 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1881 invoke, temp1_loc, dest, class_offset, temp2_loc, /* needs_null_check */ false);
1882
1883 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
1884 // Bail out if the destination is not a non primitive array.
1885 //
1886 // Register `temp1` is not trashed by the read barrier emitted
1887 // by GenerateFieldLoadWithBakerReadBarrier below, as that
1888 // method produces a call to a ReadBarrierMarkRegX entry point,
1889 // which saves all potentially live registers, including
1890 // temporaries such a `temp1`.
1891 // /* HeapReference<Class> */ temp2 = temp1->component_type_
1892 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1893 invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
xueliang.zhongf51bc622016-11-04 09:23:32 +00001894 __ CompareAndBranchIfZero(temp2, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001895 // If heap poisoning is enabled, `temp2` has been unpoisoned
1896 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1897 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
1898 __ Ldrh(temp2, MemOperand(temp2, primitive_offset));
1899 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00001900 __ CompareAndBranchIfNonZero(temp2, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001901 }
1902
1903 // For the same reason given earlier, `temp1` is not trashed by the
1904 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
1905 // /* HeapReference<Class> */ temp2 = src->klass_
1906 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1907 invoke, temp2_loc, src, class_offset, temp3_loc, /* needs_null_check */ false);
1908 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
1909 __ Cmp(temp1, temp2);
1910
1911 if (optimizations.GetDestinationIsTypedObjectArray()) {
1912 vixl32::Label do_copy;
1913 __ B(eq, &do_copy);
1914 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1915 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1916 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
1917 // /* HeapReference<Class> */ temp1 = temp1->super_class_
1918 // We do not need to emit a read barrier for the following
1919 // heap reference load, as `temp1` is only used in a
1920 // comparison with null below, and this reference is not
1921 // kept afterwards.
1922 __ Ldr(temp1, MemOperand(temp1, super_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00001923 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001924 __ Bind(&do_copy);
1925 } else {
1926 __ B(ne, intrinsic_slow_path->GetEntryLabel());
1927 }
1928 } else {
1929 // Non read barrier code.
1930
1931 // /* HeapReference<Class> */ temp1 = dest->klass_
1932 __ Ldr(temp1, MemOperand(dest, class_offset));
1933 // /* HeapReference<Class> */ temp2 = src->klass_
1934 __ Ldr(temp2, MemOperand(src, class_offset));
1935 bool did_unpoison = false;
1936 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
1937 !optimizations.GetSourceIsNonPrimitiveArray()) {
1938 // One or two of the references need to be unpoisoned. Unpoison them
1939 // both to make the identity check valid.
1940 assembler->MaybeUnpoisonHeapReference(temp1);
1941 assembler->MaybeUnpoisonHeapReference(temp2);
1942 did_unpoison = true;
1943 }
1944
1945 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
1946 // Bail out if the destination is not a non primitive array.
1947 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1948 __ Ldr(temp3, MemOperand(temp1, component_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00001949 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001950 assembler->MaybeUnpoisonHeapReference(temp3);
1951 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
1952 __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
1953 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00001954 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001955 }
1956
1957 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1958 // Bail out if the source is not a non primitive array.
1959 // /* HeapReference<Class> */ temp3 = temp2->component_type_
1960 __ Ldr(temp3, MemOperand(temp2, component_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00001961 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001962 assembler->MaybeUnpoisonHeapReference(temp3);
1963 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
1964 __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
1965 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00001966 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001967 }
1968
1969 __ Cmp(temp1, temp2);
1970
1971 if (optimizations.GetDestinationIsTypedObjectArray()) {
1972 vixl32::Label do_copy;
1973 __ B(eq, &do_copy);
1974 if (!did_unpoison) {
1975 assembler->MaybeUnpoisonHeapReference(temp1);
1976 }
1977 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1978 __ Ldr(temp1, MemOperand(temp1, component_offset));
1979 assembler->MaybeUnpoisonHeapReference(temp1);
1980 // /* HeapReference<Class> */ temp1 = temp1->super_class_
1981 __ Ldr(temp1, MemOperand(temp1, super_offset));
1982 // No need to unpoison the result, we're comparing against null.
xueliang.zhongf51bc622016-11-04 09:23:32 +00001983 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01001984 __ Bind(&do_copy);
1985 } else {
1986 __ B(ne, intrinsic_slow_path->GetEntryLabel());
1987 }
1988 }
1989 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1990 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
1991 // Bail out if the source is not a non primitive array.
1992 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1993 // /* HeapReference<Class> */ temp1 = src->klass_
1994 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1995 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
1996 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1997 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1998 invoke, temp3_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
xueliang.zhongf51bc622016-11-04 09:23:32 +00001999 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002000 // If heap poisoning is enabled, `temp3` has been unpoisoned
2001 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2002 } else {
2003 // /* HeapReference<Class> */ temp1 = src->klass_
2004 __ Ldr(temp1, MemOperand(src, class_offset));
2005 assembler->MaybeUnpoisonHeapReference(temp1);
2006 // /* HeapReference<Class> */ temp3 = temp1->component_type_
2007 __ Ldr(temp3, MemOperand(temp1, component_offset));
xueliang.zhongf51bc622016-11-04 09:23:32 +00002008 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002009 assembler->MaybeUnpoisonHeapReference(temp3);
2010 }
2011 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
2012 __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
2013 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00002014 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002015 }
2016
2017 int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
2018 uint32_t element_size_shift = Primitive::ComponentSizeShift(Primitive::kPrimNot);
2019 uint32_t offset = mirror::Array::DataOffset(element_size).Uint32Value();
2020
2021 // Compute the base source address in `temp1`.
2022 if (src_pos.IsConstant()) {
2023 int32_t constant = Int32ConstantFrom(src_pos);
2024 __ Add(temp1, src, element_size * constant + offset);
2025 } else {
2026 __ Add(temp1, src, Operand(RegisterFrom(src_pos), vixl32::LSL, element_size_shift));
2027 __ Add(temp1, temp1, offset);
2028 }
2029
2030 // Compute the end source address in `temp3`.
2031 if (length.IsConstant()) {
2032 int32_t constant = Int32ConstantFrom(length);
2033 __ Add(temp3, temp1, element_size * constant);
2034 } else {
2035 __ Add(temp3, temp1, Operand(RegisterFrom(length), vixl32::LSL, element_size_shift));
2036 }
2037
2038 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2039 // The base destination address is computed later, as `temp2` is
2040 // used for intermediate computations.
2041
2042 // SystemArrayCopy implementation for Baker read barriers (see
2043 // also CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier):
2044 //
2045 // if (src_ptr != end_ptr) {
2046 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
2047 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
Roland Levillain4bbca2a2016-11-03 18:09:18 +00002048 // bool is_gray = (rb_state == ReadBarrier::GrayState());
Anton Kirilov5ec62182016-10-13 20:16:02 +01002049 // if (is_gray) {
2050 // // Slow-path copy.
2051 // do {
2052 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
2053 // } while (src_ptr != end_ptr)
2054 // } else {
2055 // // Fast-path copy.
2056 // do {
2057 // *dest_ptr++ = *src_ptr++;
2058 // } while (src_ptr != end_ptr)
2059 // }
2060 // }
2061
2062 vixl32::Label loop, done;
2063
2064 // Don't enter copy loop if `length == 0`.
2065 __ Cmp(temp1, temp3);
2066 __ B(eq, &done);
2067
2068 // /* int32_t */ monitor = src->monitor_
2069 __ Ldr(temp2, MemOperand(src, monitor_offset));
2070 // /* LockWord */ lock_word = LockWord(monitor)
2071 static_assert(sizeof(LockWord) == sizeof(int32_t),
2072 "art::LockWord and int32_t have different sizes.");
2073
2074 // Introduce a dependency on the lock_word including the rb_state,
2075 // which shall prevent load-load reordering without using
2076 // a memory barrier (which would be more expensive).
2077 // `src` is unchanged by this operation, but its value now depends
2078 // on `temp2`.
2079 __ Add(src, src, Operand(temp2, vixl32::LSR, 32));
2080
2081 // Slow path used to copy array when `src` is gray.
2082 SlowPathCodeARMVIXL* read_barrier_slow_path =
2083 new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathARMVIXL(invoke);
2084 codegen_->AddSlowPath(read_barrier_slow_path);
2085
2086 // Given the numeric representation, it's enough to check the low bit of the
2087 // rb_state. We do that by shifting the bit out of the lock word with LSRS
2088 // which can be a 16-bit instruction unlike the TST immediate.
Roland Levillain4bbca2a2016-11-03 18:09:18 +00002089 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
2090 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
Anton Kirilov5ec62182016-10-13 20:16:02 +01002091 __ Lsrs(temp2, temp2, LockWord::kReadBarrierStateShift + 1);
2092 // Carry flag is the last bit shifted out by LSRS.
2093 __ B(cs, read_barrier_slow_path->GetEntryLabel());
2094
2095 // Fast-path copy.
2096
2097 // Compute the base destination address in `temp2`.
2098 if (dest_pos.IsConstant()) {
2099 int32_t constant = Int32ConstantFrom(dest_pos);
2100 __ Add(temp2, dest, element_size * constant + offset);
2101 } else {
2102 __ Add(temp2, dest, Operand(RegisterFrom(dest_pos), vixl32::LSL, element_size_shift));
2103 __ Add(temp2, temp2, offset);
2104 }
2105
2106 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2107 // poison/unpoison.
2108 __ Bind(&loop);
2109
2110 {
2111 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2112 const vixl32::Register temp_reg = temps.Acquire();
2113
2114 __ Ldr(temp_reg, MemOperand(temp1, element_size, PostIndex));
2115 __ Str(temp_reg, MemOperand(temp2, element_size, PostIndex));
2116 }
2117
2118 __ Cmp(temp1, temp3);
2119 __ B(ne, &loop);
2120
2121 __ Bind(read_barrier_slow_path->GetExitLabel());
2122 __ Bind(&done);
2123 } else {
2124 // Non read barrier code.
2125
2126 // Compute the base destination address in `temp2`.
2127 if (dest_pos.IsConstant()) {
2128 int32_t constant = Int32ConstantFrom(dest_pos);
2129 __ Add(temp2, dest, element_size * constant + offset);
2130 } else {
2131 __ Add(temp2, dest, Operand(RegisterFrom(dest_pos), vixl32::LSL, element_size_shift));
2132 __ Add(temp2, temp2, offset);
2133 }
2134
2135 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2136 // poison/unpoison.
2137 vixl32::Label loop, done;
2138 __ Cmp(temp1, temp3);
2139 __ B(eq, &done);
2140 __ Bind(&loop);
2141
2142 {
2143 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2144 const vixl32::Register temp_reg = temps.Acquire();
2145
2146 __ Ldr(temp_reg, MemOperand(temp1, element_size, PostIndex));
2147 __ Str(temp_reg, MemOperand(temp2, element_size, PostIndex));
2148 }
2149
2150 __ Cmp(temp1, temp3);
2151 __ B(ne, &loop);
2152 __ Bind(&done);
2153 }
2154
2155 // We only need one card marking on the destination array.
2156 codegen_->MarkGCCard(temp1, temp2, dest, NoReg, /* value_can_be_null */ false);
2157
2158 __ Bind(intrinsic_slow_path->GetExitLabel());
2159}
2160
2161static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
2162 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2163 // the code generator. Furthermore, the register allocator creates fixed live intervals
2164 // for all caller-saved registers because we are doing a function call. As a result, if
2165 // the input and output locations are unallocated, the register allocator runs out of
2166 // registers and fails; however, a debuggable graph is not the common case.
2167 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2168 return;
2169 }
2170
2171 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2172 DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
2173 DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
2174
2175 LocationSummary* const locations = new (arena) LocationSummary(invoke,
2176 LocationSummary::kCallOnMainOnly,
2177 kIntrinsified);
2178 const InvokeRuntimeCallingConventionARMVIXL calling_convention;
2179
2180 locations->SetInAt(0, Location::RequiresFpuRegister());
2181 locations->SetOut(Location::RequiresFpuRegister());
2182 // Native code uses the soft float ABI.
2183 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
2184 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
2185}
2186
2187static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
2188 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2189 // the code generator. Furthermore, the register allocator creates fixed live intervals
2190 // for all caller-saved registers because we are doing a function call. As a result, if
2191 // the input and output locations are unallocated, the register allocator runs out of
2192 // registers and fails; however, a debuggable graph is not the common case.
2193 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2194 return;
2195 }
2196
2197 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2198 DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
2199 DCHECK_EQ(invoke->InputAt(1)->GetType(), Primitive::kPrimDouble);
2200 DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
2201
2202 LocationSummary* const locations = new (arena) LocationSummary(invoke,
2203 LocationSummary::kCallOnMainOnly,
2204 kIntrinsified);
2205 const InvokeRuntimeCallingConventionARMVIXL calling_convention;
2206
2207 locations->SetInAt(0, Location::RequiresFpuRegister());
2208 locations->SetInAt(1, Location::RequiresFpuRegister());
2209 locations->SetOut(Location::RequiresFpuRegister());
2210 // Native code uses the soft float ABI.
2211 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
2212 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
2213 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
2214 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(3)));
2215}
2216
2217static void GenFPToFPCall(HInvoke* invoke,
2218 ArmVIXLAssembler* assembler,
2219 CodeGeneratorARMVIXL* codegen,
2220 QuickEntrypointEnum entry) {
2221 LocationSummary* const locations = invoke->GetLocations();
2222
2223 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2224 DCHECK(locations->WillCall() && locations->Intrinsified());
2225
2226 // Native code uses the soft float ABI.
2227 __ Vmov(RegisterFrom(locations->GetTemp(0)),
2228 RegisterFrom(locations->GetTemp(1)),
2229 InputDRegisterAt(invoke, 0));
2230 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
2231 __ Vmov(OutputDRegister(invoke),
2232 RegisterFrom(locations->GetTemp(0)),
2233 RegisterFrom(locations->GetTemp(1)));
2234}
2235
2236static void GenFPFPToFPCall(HInvoke* invoke,
2237 ArmVIXLAssembler* assembler,
2238 CodeGeneratorARMVIXL* codegen,
2239 QuickEntrypointEnum entry) {
2240 LocationSummary* const locations = invoke->GetLocations();
2241
2242 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2243 DCHECK(locations->WillCall() && locations->Intrinsified());
2244
2245 // Native code uses the soft float ABI.
2246 __ Vmov(RegisterFrom(locations->GetTemp(0)),
2247 RegisterFrom(locations->GetTemp(1)),
2248 InputDRegisterAt(invoke, 0));
2249 __ Vmov(RegisterFrom(locations->GetTemp(2)),
2250 RegisterFrom(locations->GetTemp(3)),
2251 InputDRegisterAt(invoke, 1));
2252 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
2253 __ Vmov(OutputDRegister(invoke),
2254 RegisterFrom(locations->GetTemp(0)),
2255 RegisterFrom(locations->GetTemp(1)));
2256}
2257
2258void IntrinsicLocationsBuilderARMVIXL::VisitMathCos(HInvoke* invoke) {
2259 CreateFPToFPCallLocations(arena_, invoke);
2260}
2261
2262void IntrinsicCodeGeneratorARMVIXL::VisitMathCos(HInvoke* invoke) {
2263 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCos);
2264}
2265
2266void IntrinsicLocationsBuilderARMVIXL::VisitMathSin(HInvoke* invoke) {
2267 CreateFPToFPCallLocations(arena_, invoke);
2268}
2269
2270void IntrinsicCodeGeneratorARMVIXL::VisitMathSin(HInvoke* invoke) {
2271 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSin);
2272}
2273
2274void IntrinsicLocationsBuilderARMVIXL::VisitMathAcos(HInvoke* invoke) {
2275 CreateFPToFPCallLocations(arena_, invoke);
2276}
2277
2278void IntrinsicCodeGeneratorARMVIXL::VisitMathAcos(HInvoke* invoke) {
2279 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAcos);
2280}
2281
2282void IntrinsicLocationsBuilderARMVIXL::VisitMathAsin(HInvoke* invoke) {
2283 CreateFPToFPCallLocations(arena_, invoke);
2284}
2285
2286void IntrinsicCodeGeneratorARMVIXL::VisitMathAsin(HInvoke* invoke) {
2287 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAsin);
2288}
2289
2290void IntrinsicLocationsBuilderARMVIXL::VisitMathAtan(HInvoke* invoke) {
2291 CreateFPToFPCallLocations(arena_, invoke);
2292}
2293
2294void IntrinsicCodeGeneratorARMVIXL::VisitMathAtan(HInvoke* invoke) {
2295 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan);
2296}
2297
2298void IntrinsicLocationsBuilderARMVIXL::VisitMathCbrt(HInvoke* invoke) {
2299 CreateFPToFPCallLocations(arena_, invoke);
2300}
2301
2302void IntrinsicCodeGeneratorARMVIXL::VisitMathCbrt(HInvoke* invoke) {
2303 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCbrt);
2304}
2305
2306void IntrinsicLocationsBuilderARMVIXL::VisitMathCosh(HInvoke* invoke) {
2307 CreateFPToFPCallLocations(arena_, invoke);
2308}
2309
2310void IntrinsicCodeGeneratorARMVIXL::VisitMathCosh(HInvoke* invoke) {
2311 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCosh);
2312}
2313
2314void IntrinsicLocationsBuilderARMVIXL::VisitMathExp(HInvoke* invoke) {
2315 CreateFPToFPCallLocations(arena_, invoke);
2316}
2317
2318void IntrinsicCodeGeneratorARMVIXL::VisitMathExp(HInvoke* invoke) {
2319 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExp);
2320}
2321
2322void IntrinsicLocationsBuilderARMVIXL::VisitMathExpm1(HInvoke* invoke) {
2323 CreateFPToFPCallLocations(arena_, invoke);
2324}
2325
2326void IntrinsicCodeGeneratorARMVIXL::VisitMathExpm1(HInvoke* invoke) {
2327 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExpm1);
2328}
2329
2330void IntrinsicLocationsBuilderARMVIXL::VisitMathLog(HInvoke* invoke) {
2331 CreateFPToFPCallLocations(arena_, invoke);
2332}
2333
2334void IntrinsicCodeGeneratorARMVIXL::VisitMathLog(HInvoke* invoke) {
2335 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog);
2336}
2337
2338void IntrinsicLocationsBuilderARMVIXL::VisitMathLog10(HInvoke* invoke) {
2339 CreateFPToFPCallLocations(arena_, invoke);
2340}
2341
2342void IntrinsicCodeGeneratorARMVIXL::VisitMathLog10(HInvoke* invoke) {
2343 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog10);
2344}
2345
2346void IntrinsicLocationsBuilderARMVIXL::VisitMathSinh(HInvoke* invoke) {
2347 CreateFPToFPCallLocations(arena_, invoke);
2348}
2349
2350void IntrinsicCodeGeneratorARMVIXL::VisitMathSinh(HInvoke* invoke) {
2351 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSinh);
2352}
2353
2354void IntrinsicLocationsBuilderARMVIXL::VisitMathTan(HInvoke* invoke) {
2355 CreateFPToFPCallLocations(arena_, invoke);
2356}
2357
2358void IntrinsicCodeGeneratorARMVIXL::VisitMathTan(HInvoke* invoke) {
2359 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTan);
2360}
2361
2362void IntrinsicLocationsBuilderARMVIXL::VisitMathTanh(HInvoke* invoke) {
2363 CreateFPToFPCallLocations(arena_, invoke);
2364}
2365
2366void IntrinsicCodeGeneratorARMVIXL::VisitMathTanh(HInvoke* invoke) {
2367 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTanh);
2368}
2369
2370void IntrinsicLocationsBuilderARMVIXL::VisitMathAtan2(HInvoke* invoke) {
2371 CreateFPFPToFPCallLocations(arena_, invoke);
2372}
2373
2374void IntrinsicCodeGeneratorARMVIXL::VisitMathAtan2(HInvoke* invoke) {
2375 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan2);
2376}
2377
2378void IntrinsicLocationsBuilderARMVIXL::VisitMathHypot(HInvoke* invoke) {
2379 CreateFPFPToFPCallLocations(arena_, invoke);
2380}
2381
2382void IntrinsicCodeGeneratorARMVIXL::VisitMathHypot(HInvoke* invoke) {
2383 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickHypot);
2384}
2385
2386void IntrinsicLocationsBuilderARMVIXL::VisitMathNextAfter(HInvoke* invoke) {
2387 CreateFPFPToFPCallLocations(arena_, invoke);
2388}
2389
2390void IntrinsicCodeGeneratorARMVIXL::VisitMathNextAfter(HInvoke* invoke) {
2391 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter);
2392}
2393
2394void IntrinsicLocationsBuilderARMVIXL::VisitIntegerReverse(HInvoke* invoke) {
2395 CreateIntToIntLocations(arena_, invoke);
2396}
2397
2398void IntrinsicCodeGeneratorARMVIXL::VisitIntegerReverse(HInvoke* invoke) {
2399 ArmVIXLAssembler* assembler = GetAssembler();
2400 __ Rbit(OutputRegister(invoke), InputRegisterAt(invoke, 0));
2401}
2402
2403void IntrinsicLocationsBuilderARMVIXL::VisitLongReverse(HInvoke* invoke) {
2404 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2405 LocationSummary::kNoCall,
2406 kIntrinsified);
2407 locations->SetInAt(0, Location::RequiresRegister());
2408 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2409}
2410
2411void IntrinsicCodeGeneratorARMVIXL::VisitLongReverse(HInvoke* invoke) {
2412 ArmVIXLAssembler* assembler = GetAssembler();
2413 LocationSummary* locations = invoke->GetLocations();
2414
2415 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
2416 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
2417 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
2418 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
2419
2420 __ Rbit(out_reg_lo, in_reg_hi);
2421 __ Rbit(out_reg_hi, in_reg_lo);
2422}
2423
2424void IntrinsicLocationsBuilderARMVIXL::VisitIntegerReverseBytes(HInvoke* invoke) {
2425 CreateIntToIntLocations(arena_, invoke);
2426}
2427
2428void IntrinsicCodeGeneratorARMVIXL::VisitIntegerReverseBytes(HInvoke* invoke) {
2429 ArmVIXLAssembler* assembler = GetAssembler();
2430 __ Rev(OutputRegister(invoke), InputRegisterAt(invoke, 0));
2431}
2432
2433void IntrinsicLocationsBuilderARMVIXL::VisitLongReverseBytes(HInvoke* invoke) {
2434 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2435 LocationSummary::kNoCall,
2436 kIntrinsified);
2437 locations->SetInAt(0, Location::RequiresRegister());
2438 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2439}
2440
2441void IntrinsicCodeGeneratorARMVIXL::VisitLongReverseBytes(HInvoke* invoke) {
2442 ArmVIXLAssembler* assembler = GetAssembler();
2443 LocationSummary* locations = invoke->GetLocations();
2444
2445 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
2446 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
2447 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
2448 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
2449
2450 __ Rev(out_reg_lo, in_reg_hi);
2451 __ Rev(out_reg_hi, in_reg_lo);
2452}
2453
2454void IntrinsicLocationsBuilderARMVIXL::VisitShortReverseBytes(HInvoke* invoke) {
2455 CreateIntToIntLocations(arena_, invoke);
2456}
2457
2458void IntrinsicCodeGeneratorARMVIXL::VisitShortReverseBytes(HInvoke* invoke) {
2459 ArmVIXLAssembler* assembler = GetAssembler();
2460 __ Revsh(OutputRegister(invoke), InputRegisterAt(invoke, 0));
2461}
2462
2463static void GenBitCount(HInvoke* instr, Primitive::Type type, ArmVIXLAssembler* assembler) {
2464 DCHECK(Primitive::IsIntOrLongType(type)) << type;
2465 DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
2466 DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
2467
2468 bool is_long = type == Primitive::kPrimLong;
2469 LocationSummary* locations = instr->GetLocations();
2470 Location in = locations->InAt(0);
2471 vixl32::Register src_0 = is_long ? LowRegisterFrom(in) : RegisterFrom(in);
2472 vixl32::Register src_1 = is_long ? HighRegisterFrom(in) : src_0;
2473 vixl32::SRegister tmp_s = LowSRegisterFrom(locations->GetTemp(0));
2474 vixl32::DRegister tmp_d = DRegisterFrom(locations->GetTemp(0));
2475 vixl32::Register out_r = OutputRegister(instr);
2476
2477 // Move data from core register(s) to temp D-reg for bit count calculation, then move back.
2478 // According to Cortex A57 and A72 optimization guides, compared to transferring to full D-reg,
2479 // transferring data from core reg to upper or lower half of vfp D-reg requires extra latency,
2480 // That's why for integer bit count, we use 'vmov d0, r0, r0' instead of 'vmov d0[0], r0'.
2481 __ Vmov(tmp_d, src_1, src_0); // Temp DReg |--src_1|--src_0|
2482 __ Vcnt(Untyped8, tmp_d, tmp_d); // Temp DReg |c|c|c|c|c|c|c|c|
2483 __ Vpaddl(U8, tmp_d, tmp_d); // Temp DReg |--c|--c|--c|--c|
2484 __ Vpaddl(U16, tmp_d, tmp_d); // Temp DReg |------c|------c|
2485 if (is_long) {
2486 __ Vpaddl(U32, tmp_d, tmp_d); // Temp DReg |--------------c|
2487 }
2488 __ Vmov(out_r, tmp_s);
2489}
2490
2491void IntrinsicLocationsBuilderARMVIXL::VisitIntegerBitCount(HInvoke* invoke) {
2492 CreateIntToIntLocations(arena_, invoke);
2493 invoke->GetLocations()->AddTemp(Location::RequiresFpuRegister());
2494}
2495
2496void IntrinsicCodeGeneratorARMVIXL::VisitIntegerBitCount(HInvoke* invoke) {
2497 GenBitCount(invoke, Primitive::kPrimInt, GetAssembler());
2498}
2499
2500void IntrinsicLocationsBuilderARMVIXL::VisitLongBitCount(HInvoke* invoke) {
2501 VisitIntegerBitCount(invoke);
2502}
2503
2504void IntrinsicCodeGeneratorARMVIXL::VisitLongBitCount(HInvoke* invoke) {
2505 GenBitCount(invoke, Primitive::kPrimLong, GetAssembler());
2506}
2507
2508void IntrinsicLocationsBuilderARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2509 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2510 LocationSummary::kNoCall,
2511 kIntrinsified);
2512 locations->SetInAt(0, Location::RequiresRegister());
2513 locations->SetInAt(1, Location::RequiresRegister());
2514 locations->SetInAt(2, Location::RequiresRegister());
2515 locations->SetInAt(3, Location::RequiresRegister());
2516 locations->SetInAt(4, Location::RequiresRegister());
2517
2518 // Temporary registers to store lengths of strings and for calculations.
2519 locations->AddTemp(Location::RequiresRegister());
2520 locations->AddTemp(Location::RequiresRegister());
2521 locations->AddTemp(Location::RequiresRegister());
2522}
2523
2524void IntrinsicCodeGeneratorARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2525 ArmVIXLAssembler* assembler = GetAssembler();
2526 LocationSummary* locations = invoke->GetLocations();
2527
2528 // Check assumption that sizeof(Char) is 2 (used in scaling below).
2529 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2530 DCHECK_EQ(char_size, 2u);
2531
2532 // Location of data in char array buffer.
2533 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2534
2535 // Location of char array data in string.
2536 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2537
2538 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2539 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2540 vixl32::Register srcObj = InputRegisterAt(invoke, 0);
2541 vixl32::Register srcBegin = InputRegisterAt(invoke, 1);
2542 vixl32::Register srcEnd = InputRegisterAt(invoke, 2);
2543 vixl32::Register dstObj = InputRegisterAt(invoke, 3);
2544 vixl32::Register dstBegin = InputRegisterAt(invoke, 4);
2545
2546 vixl32::Register num_chr = RegisterFrom(locations->GetTemp(0));
2547 vixl32::Register src_ptr = RegisterFrom(locations->GetTemp(1));
2548 vixl32::Register dst_ptr = RegisterFrom(locations->GetTemp(2));
2549
2550 vixl32::Label done, compressed_string_loop;
2551 // dst to be copied.
2552 __ Add(dst_ptr, dstObj, data_offset);
2553 __ Add(dst_ptr, dst_ptr, Operand(dstBegin, vixl32::LSL, 1));
2554
2555 __ Subs(num_chr, srcEnd, srcBegin);
2556 // Early out for valid zero-length retrievals.
2557 __ B(eq, &done);
2558
2559 // src range to copy.
2560 __ Add(src_ptr, srcObj, value_offset);
2561
2562 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2563 vixl32::Register temp;
2564 vixl32::Label compressed_string_preloop;
2565 if (mirror::kUseStringCompression) {
2566 // Location of count in string.
2567 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2568 temp = temps.Acquire();
2569 // String's length.
2570 __ Ldr(temp, MemOperand(srcObj, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002571 __ Tst(temp, 1);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002572 temps.Release(temp);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002573 __ B(eq, &compressed_string_preloop);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002574 }
2575 __ Add(src_ptr, src_ptr, Operand(srcBegin, vixl32::LSL, 1));
2576
2577 // Do the copy.
2578 vixl32::Label loop, remainder;
2579
2580 temp = temps.Acquire();
2581 // Save repairing the value of num_chr on the < 4 character path.
2582 __ Subs(temp, num_chr, 4);
2583 __ B(lt, &remainder);
2584
2585 // Keep the result of the earlier subs, we are going to fetch at least 4 characters.
2586 __ Mov(num_chr, temp);
2587
2588 // Main loop used for longer fetches loads and stores 4x16-bit characters at a time.
2589 // (LDRD/STRD fault on unaligned addresses and it's not worth inlining extra code
2590 // to rectify these everywhere this intrinsic applies.)
2591 __ Bind(&loop);
2592 __ Ldr(temp, MemOperand(src_ptr, char_size * 2));
2593 __ Subs(num_chr, num_chr, 4);
2594 __ Str(temp, MemOperand(dst_ptr, char_size * 2));
2595 __ Ldr(temp, MemOperand(src_ptr, char_size * 4, PostIndex));
2596 __ Str(temp, MemOperand(dst_ptr, char_size * 4, PostIndex));
2597 temps.Release(temp);
2598 __ B(ge, &loop);
2599
2600 __ Adds(num_chr, num_chr, 4);
2601 __ B(eq, &done);
2602
2603 // Main loop for < 4 character case and remainder handling. Loads and stores one
2604 // 16-bit Java character at a time.
2605 __ Bind(&remainder);
2606 temp = temps.Acquire();
2607 __ Ldrh(temp, MemOperand(src_ptr, char_size, PostIndex));
2608 __ Subs(num_chr, num_chr, 1);
2609 __ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex));
2610 temps.Release(temp);
2611 __ B(gt, &remainder);
Anton Kirilov5ec62182016-10-13 20:16:02 +01002612
2613 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002614 __ B(&done);
2615
Anton Kirilov5ec62182016-10-13 20:16:02 +01002616 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
2617 DCHECK_EQ(c_char_size, 1u);
2618 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2619 __ Bind(&compressed_string_preloop);
2620 __ Add(src_ptr, src_ptr, srcBegin);
2621 __ Bind(&compressed_string_loop);
2622 temp = temps.Acquire();
2623 __ Ldrb(temp, MemOperand(src_ptr, c_char_size, PostIndex));
2624 __ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex));
2625 temps.Release(temp);
2626 __ Subs(num_chr, num_chr, 1);
2627 __ B(gt, &compressed_string_loop);
2628 }
2629
2630 __ Bind(&done);
2631}
2632
2633void IntrinsicLocationsBuilderARMVIXL::VisitFloatIsInfinite(HInvoke* invoke) {
2634 CreateFPToIntLocations(arena_, invoke);
2635}
2636
2637void IntrinsicCodeGeneratorARMVIXL::VisitFloatIsInfinite(HInvoke* invoke) {
2638 ArmVIXLAssembler* const assembler = GetAssembler();
2639 const vixl32::Register out = OutputRegister(invoke);
2640 // Shifting left by 1 bit makes the value encodable as an immediate operand;
2641 // we don't care about the sign bit anyway.
2642 constexpr uint32_t infinity = kPositiveInfinityFloat << 1U;
2643
2644 __ Vmov(out, InputSRegisterAt(invoke, 0));
2645 // We don't care about the sign bit, so shift left.
2646 __ Lsl(out, out, 1);
2647 __ Eor(out, out, infinity);
2648 // If the result is 0, then it has 32 leading zeros, and less than that otherwise.
2649 __ Clz(out, out);
2650 // Any number less than 32 logically shifted right by 5 bits results in 0;
2651 // the same operation on 32 yields 1.
2652 __ Lsr(out, out, 5);
2653}
2654
2655void IntrinsicLocationsBuilderARMVIXL::VisitDoubleIsInfinite(HInvoke* invoke) {
2656 CreateFPToIntLocations(arena_, invoke);
2657}
2658
2659void IntrinsicCodeGeneratorARMVIXL::VisitDoubleIsInfinite(HInvoke* invoke) {
2660 ArmVIXLAssembler* const assembler = GetAssembler();
2661 const vixl32::Register out = OutputRegister(invoke);
2662 UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
2663 const vixl32::Register temp = temps.Acquire();
2664 // The highest 32 bits of double precision positive infinity separated into
2665 // two constants encodable as immediate operands.
2666 constexpr uint32_t infinity_high = 0x7f000000U;
2667 constexpr uint32_t infinity_high2 = 0x00f00000U;
2668
2669 static_assert((infinity_high | infinity_high2) ==
2670 static_cast<uint32_t>(kPositiveInfinityDouble >> 32U),
2671 "The constants do not add up to the high 32 bits of double "
2672 "precision positive infinity.");
2673 __ Vmov(temp, out, InputDRegisterAt(invoke, 0));
2674 __ Eor(out, out, infinity_high);
2675 __ Eor(out, out, infinity_high2);
2676 // We don't care about the sign bit, so shift left.
2677 __ Orr(out, temp, Operand(out, vixl32::LSL, 1));
2678 // If the result is 0, then it has 32 leading zeros, and less than that otherwise.
2679 __ Clz(out, out);
2680 // Any number less than 32 logically shifted right by 5 bits results in 0;
2681 // the same operation on 32 yields 1.
2682 __ Lsr(out, out, 5);
2683}
2684
2685UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMinDoubleDouble)
2686UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMinFloatFloat)
2687UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMaxDoubleDouble)
2688UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMaxFloatFloat)
2689UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMinLongLong)
2690UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMaxLongLong)
2691UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathCeil) // Could be done by changing rounding mode, maybe?
2692UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathFloor) // Could be done by changing rounding mode, maybe?
2693UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRint)
2694UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundDouble) // Could be done by changing rounding mode, maybe?
2695UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundFloat) // Could be done by changing rounding mode, maybe?
2696UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong) // High register pressure.
2697UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
2698UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent)
2699UNIMPLEMENTED_INTRINSIC(ARMVIXL, IntegerHighestOneBit)
2700UNIMPLEMENTED_INTRINSIC(ARMVIXL, LongHighestOneBit)
2701UNIMPLEMENTED_INTRINSIC(ARMVIXL, IntegerLowestOneBit)
2702UNIMPLEMENTED_INTRINSIC(ARMVIXL, LongLowestOneBit)
2703
Aart Bikff7d89c2016-11-07 08:49:28 -08002704UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf);
2705UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter);
2706
Anton Kirilov5ec62182016-10-13 20:16:02 +01002707// 1.8.
2708UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndAddInt)
2709UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndAddLong)
2710UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndSetInt)
2711UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndSetLong)
2712UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndSetObject)
2713
2714UNREACHABLE_INTRINSICS(ARMVIXL)
2715
2716#undef __
2717
2718} // namespace arm
2719} // namespace art