blob: 987a2af64a9551537be4b59c0f1283b582ad2857 [file] [log] [blame]
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001/*
2 * Copyright (C) 2015 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.h"
18
19#include "arch/arm/instruction_set_features_arm.h"
Mathieu Chartiere401d142015-04-22 13:56:20 -070020#include "art_method.h"
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -080021#include "code_generator_arm.h"
22#include "entrypoints/quick/quick_entrypoints.h"
23#include "intrinsics.h"
Andreas Gampe85b62f22015-09-09 13:15:38 -070024#include "intrinsics_utils.h"
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -080025#include "mirror/array-inl.h"
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -080026#include "mirror/string.h"
27#include "thread.h"
28#include "utils/arm/assembler_arm.h"
29
30namespace art {
31
32namespace arm {
33
34ArmAssembler* IntrinsicCodeGeneratorARM::GetAssembler() {
35 return codegen_->GetAssembler();
36}
37
38ArenaAllocator* IntrinsicCodeGeneratorARM::GetAllocator() {
39 return codegen_->GetGraph()->GetArena();
40}
41
Andreas Gampe85b62f22015-09-09 13:15:38 -070042using IntrinsicSlowPathARM = IntrinsicSlowPath<InvokeDexCallingConventionVisitorARM>;
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -080043
Roland Levillain9cc0ea82017-03-16 11:25:59 +000044#define __ assembler->
45
46// Compute base address for the System.arraycopy intrinsic in `base`.
47static void GenSystemArrayCopyBaseAddress(ArmAssembler* assembler,
48 Primitive::Type type,
49 const Register& array,
50 const Location& pos,
51 const Register& base) {
52 // This routine is only used by the SystemArrayCopy intrinsic at the
53 // moment. We can allow Primitive::kPrimNot as `type` to implement
54 // the SystemArrayCopyChar intrinsic.
55 DCHECK_EQ(type, Primitive::kPrimNot);
56 const int32_t element_size = Primitive::ComponentSize(type);
57 const uint32_t element_size_shift = Primitive::ComponentSizeShift(type);
58 const uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
59
60 if (pos.IsConstant()) {
61 int32_t constant = pos.GetConstant()->AsIntConstant()->GetValue();
62 __ AddConstant(base, array, element_size * constant + data_offset);
63 } else {
64 __ add(base, array, ShifterOperand(pos.AsRegister<Register>(), LSL, element_size_shift));
65 __ AddConstant(base, data_offset);
66 }
67}
68
69// Compute end address for the System.arraycopy intrinsic in `end`.
70static void GenSystemArrayCopyEndAddress(ArmAssembler* assembler,
71 Primitive::Type type,
72 const Location& copy_length,
73 const Register& base,
74 const Register& end) {
75 // This routine is only used by the SystemArrayCopy intrinsic at the
76 // moment. We can allow Primitive::kPrimNot as `type` to implement
77 // the SystemArrayCopyChar intrinsic.
78 DCHECK_EQ(type, Primitive::kPrimNot);
79 const int32_t element_size = Primitive::ComponentSize(type);
80 const uint32_t element_size_shift = Primitive::ComponentSizeShift(type);
81
82 if (copy_length.IsConstant()) {
83 int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
84 __ AddConstant(end, base, element_size * constant);
85 } else {
86 __ add(end, base, ShifterOperand(copy_length.AsRegister<Register>(), LSL, element_size_shift));
87 }
88}
89
90#undef __
91
Roland Levillain0b671c02016-08-19 12:02:34 +010092// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
93#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())-> // NOLINT
94
95// Slow path implementing the SystemArrayCopy intrinsic copy loop with read barriers.
96class ReadBarrierSystemArrayCopySlowPathARM : public SlowPathCode {
97 public:
98 explicit ReadBarrierSystemArrayCopySlowPathARM(HInstruction* instruction)
99 : SlowPathCode(instruction) {
100 DCHECK(kEmitCompilerReadBarrier);
101 DCHECK(kUseBakerReadBarrier);
102 }
103
104 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
105 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000106 ArmAssembler* assembler = arm_codegen->GetAssembler();
Roland Levillain0b671c02016-08-19 12:02:34 +0100107 LocationSummary* locations = instruction_->GetLocations();
108 DCHECK(locations->CanCall());
109 DCHECK(instruction_->IsInvokeStaticOrDirect())
110 << "Unexpected instruction in read barrier arraycopy slow path: "
111 << instruction_->DebugName();
112 DCHECK(instruction_->GetLocations()->Intrinsified());
113 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kSystemArrayCopy);
114
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000115 Primitive::Type type = Primitive::kPrimNot;
116 const int32_t element_size = Primitive::ComponentSize(type);
Roland Levillain0b671c02016-08-19 12:02:34 +0100117
118 Register dest = locations->InAt(2).AsRegister<Register>();
119 Location dest_pos = locations->InAt(3);
120 Register src_curr_addr = locations->GetTemp(0).AsRegister<Register>();
121 Register dst_curr_addr = locations->GetTemp(1).AsRegister<Register>();
122 Register src_stop_addr = locations->GetTemp(2).AsRegister<Register>();
123 Register tmp = locations->GetTemp(3).AsRegister<Register>();
124
125 __ Bind(GetEntryLabel());
126 // Compute the base destination address in `dst_curr_addr`.
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000127 GenSystemArrayCopyBaseAddress(assembler, type, dest, dest_pos, dst_curr_addr);
Roland Levillain0b671c02016-08-19 12:02:34 +0100128
129 Label loop;
130 __ Bind(&loop);
131 __ ldr(tmp, Address(src_curr_addr, element_size, Address::PostIndex));
132 __ MaybeUnpoisonHeapReference(tmp);
133 // TODO: Inline the mark bit check before calling the runtime?
134 // tmp = ReadBarrier::Mark(tmp);
135 // No need to save live registers; it's taken care of by the
136 // entrypoint. Also, there is no need to update the stack mask,
137 // as this runtime call will not trigger a garbage collection.
138 // (See ReadBarrierMarkSlowPathARM::EmitNativeCode for more
139 // explanations.)
140 DCHECK_NE(tmp, SP);
141 DCHECK_NE(tmp, LR);
142 DCHECK_NE(tmp, PC);
143 // IP is used internally by the ReadBarrierMarkRegX entry point
144 // as a temporary (and not preserved). It thus cannot be used by
145 // any live register in this slow path.
146 DCHECK_NE(src_curr_addr, IP);
147 DCHECK_NE(dst_curr_addr, IP);
148 DCHECK_NE(src_stop_addr, IP);
149 DCHECK_NE(tmp, IP);
150 DCHECK(0 <= tmp && tmp < kNumberOfCoreRegisters) << tmp;
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000151 // TODO: Load the entrypoint once before the loop, instead of
152 // loading it at every iteration.
Roland Levillain0b671c02016-08-19 12:02:34 +0100153 int32_t entry_point_offset =
154 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(tmp);
155 // This runtime call does not require a stack map.
156 arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
157 __ MaybePoisonHeapReference(tmp);
158 __ str(tmp, Address(dst_curr_addr, element_size, Address::PostIndex));
159 __ cmp(src_curr_addr, ShifterOperand(src_stop_addr));
160 __ b(&loop, NE);
161 __ b(GetExitLabel());
162 }
163
164 const char* GetDescription() const OVERRIDE { return "ReadBarrierSystemArrayCopySlowPathARM"; }
165
166 private:
167 DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathARM);
168};
169
170#undef __
171
Vladimir Marko68c981f2016-08-26 13:13:33 +0100172IntrinsicLocationsBuilderARM::IntrinsicLocationsBuilderARM(CodeGeneratorARM* codegen)
173 : arena_(codegen->GetGraph()->GetArena()),
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000174 codegen_(codegen),
Vladimir Marko68c981f2016-08-26 13:13:33 +0100175 assembler_(codegen->GetAssembler()),
176 features_(codegen->GetInstructionSetFeatures()) {}
177
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800178bool IntrinsicLocationsBuilderARM::TryDispatch(HInvoke* invoke) {
179 Dispatch(invoke);
180 LocationSummary* res = invoke->GetLocations();
Roland Levillain3b359c72015-11-17 19:35:12 +0000181 if (res == nullptr) {
182 return false;
183 }
Roland Levillain3b359c72015-11-17 19:35:12 +0000184 return res->Intrinsified();
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800185}
186
187#define __ assembler->
188
189static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
190 LocationSummary* locations = new (arena) LocationSummary(invoke,
191 LocationSummary::kNoCall,
192 kIntrinsified);
193 locations->SetInAt(0, Location::RequiresFpuRegister());
194 locations->SetOut(Location::RequiresRegister());
195}
196
197static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
198 LocationSummary* locations = new (arena) LocationSummary(invoke,
199 LocationSummary::kNoCall,
200 kIntrinsified);
201 locations->SetInAt(0, Location::RequiresRegister());
202 locations->SetOut(Location::RequiresFpuRegister());
203}
204
205static void MoveFPToInt(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
206 Location input = locations->InAt(0);
207 Location output = locations->Out();
208 if (is64bit) {
209 __ vmovrrd(output.AsRegisterPairLow<Register>(),
210 output.AsRegisterPairHigh<Register>(),
211 FromLowSToD(input.AsFpuRegisterPairLow<SRegister>()));
212 } else {
213 __ vmovrs(output.AsRegister<Register>(), input.AsFpuRegister<SRegister>());
214 }
215}
216
217static void MoveIntToFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
218 Location input = locations->InAt(0);
219 Location output = locations->Out();
220 if (is64bit) {
221 __ vmovdrr(FromLowSToD(output.AsFpuRegisterPairLow<SRegister>()),
222 input.AsRegisterPairLow<Register>(),
223 input.AsRegisterPairHigh<Register>());
224 } else {
225 __ vmovsr(output.AsFpuRegister<SRegister>(), input.AsRegister<Register>());
226 }
227}
228
229void IntrinsicLocationsBuilderARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
230 CreateFPToIntLocations(arena_, invoke);
231}
232void IntrinsicLocationsBuilderARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
233 CreateIntToFPLocations(arena_, invoke);
234}
235
236void IntrinsicCodeGeneratorARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000237 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800238}
239void IntrinsicCodeGeneratorARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000240 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800241}
242
243void IntrinsicLocationsBuilderARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
244 CreateFPToIntLocations(arena_, invoke);
245}
246void IntrinsicLocationsBuilderARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
247 CreateIntToFPLocations(arena_, invoke);
248}
249
250void IntrinsicCodeGeneratorARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000251 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800252}
253void IntrinsicCodeGeneratorARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000254 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800255}
256
257static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
258 LocationSummary* locations = new (arena) LocationSummary(invoke,
259 LocationSummary::kNoCall,
260 kIntrinsified);
261 locations->SetInAt(0, Location::RequiresRegister());
262 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
263}
264
265static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
266 LocationSummary* locations = new (arena) LocationSummary(invoke,
267 LocationSummary::kNoCall,
268 kIntrinsified);
269 locations->SetInAt(0, Location::RequiresFpuRegister());
270 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
271}
272
Scott Wakeling611d3392015-07-10 11:42:06 +0100273static void GenNumberOfLeadingZeros(LocationSummary* locations,
274 Primitive::Type type,
275 ArmAssembler* assembler) {
276 Location in = locations->InAt(0);
277 Register out = locations->Out().AsRegister<Register>();
278
279 DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
280
281 if (type == Primitive::kPrimLong) {
282 Register in_reg_lo = in.AsRegisterPairLow<Register>();
283 Register in_reg_hi = in.AsRegisterPairHigh<Register>();
284 Label end;
285 __ clz(out, in_reg_hi);
286 __ CompareAndBranchIfNonZero(in_reg_hi, &end);
287 __ clz(out, in_reg_lo);
288 __ AddConstant(out, 32);
289 __ Bind(&end);
290 } else {
291 __ clz(out, in.AsRegister<Register>());
292 }
293}
294
295void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
296 CreateIntToIntLocations(arena_, invoke);
297}
298
299void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
300 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
301}
302
303void IntrinsicLocationsBuilderARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
304 LocationSummary* locations = new (arena_) LocationSummary(invoke,
305 LocationSummary::kNoCall,
306 kIntrinsified);
307 locations->SetInAt(0, Location::RequiresRegister());
308 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
309}
310
311void IntrinsicCodeGeneratorARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
312 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
313}
314
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100315static void GenNumberOfTrailingZeros(LocationSummary* locations,
316 Primitive::Type type,
317 ArmAssembler* assembler) {
318 DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
319
320 Register out = locations->Out().AsRegister<Register>();
321
322 if (type == Primitive::kPrimLong) {
323 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
324 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
325 Label end;
326 __ rbit(out, in_reg_lo);
327 __ clz(out, out);
328 __ CompareAndBranchIfNonZero(in_reg_lo, &end);
329 __ rbit(out, in_reg_hi);
330 __ clz(out, out);
331 __ AddConstant(out, 32);
332 __ Bind(&end);
333 } else {
334 Register in = locations->InAt(0).AsRegister<Register>();
335 __ rbit(out, in);
336 __ clz(out, out);
337 }
338}
339
340void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
341 LocationSummary* locations = new (arena_) LocationSummary(invoke,
342 LocationSummary::kNoCall,
343 kIntrinsified);
344 locations->SetInAt(0, Location::RequiresRegister());
345 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
346}
347
348void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
349 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
350}
351
352void IntrinsicLocationsBuilderARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
353 LocationSummary* locations = new (arena_) LocationSummary(invoke,
354 LocationSummary::kNoCall,
355 kIntrinsified);
356 locations->SetInAt(0, Location::RequiresRegister());
357 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
358}
359
360void IntrinsicCodeGeneratorARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
361 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
362}
363
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800364static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
365 Location in = locations->InAt(0);
366 Location out = locations->Out();
367
368 if (is64bit) {
369 __ vabsd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
370 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
371 } else {
372 __ vabss(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>());
373 }
374}
375
376void IntrinsicLocationsBuilderARM::VisitMathAbsDouble(HInvoke* invoke) {
377 CreateFPToFPLocations(arena_, invoke);
378}
379
380void IntrinsicCodeGeneratorARM::VisitMathAbsDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000381 MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800382}
383
384void IntrinsicLocationsBuilderARM::VisitMathAbsFloat(HInvoke* invoke) {
385 CreateFPToFPLocations(arena_, invoke);
386}
387
388void IntrinsicCodeGeneratorARM::VisitMathAbsFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000389 MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800390}
391
392static void CreateIntToIntPlusTemp(ArenaAllocator* arena, HInvoke* invoke) {
393 LocationSummary* locations = new (arena) LocationSummary(invoke,
394 LocationSummary::kNoCall,
395 kIntrinsified);
396 locations->SetInAt(0, Location::RequiresRegister());
397 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
398
399 locations->AddTemp(Location::RequiresRegister());
400}
401
402static void GenAbsInteger(LocationSummary* locations,
403 bool is64bit,
404 ArmAssembler* assembler) {
405 Location in = locations->InAt(0);
406 Location output = locations->Out();
407
408 Register mask = locations->GetTemp(0).AsRegister<Register>();
409
410 if (is64bit) {
411 Register in_reg_lo = in.AsRegisterPairLow<Register>();
412 Register in_reg_hi = in.AsRegisterPairHigh<Register>();
413 Register out_reg_lo = output.AsRegisterPairLow<Register>();
414 Register out_reg_hi = output.AsRegisterPairHigh<Register>();
415
416 DCHECK_NE(out_reg_lo, in_reg_hi) << "Diagonal overlap unexpected.";
417
418 __ Asr(mask, in_reg_hi, 31);
419 __ adds(out_reg_lo, in_reg_lo, ShifterOperand(mask));
420 __ adc(out_reg_hi, in_reg_hi, ShifterOperand(mask));
421 __ eor(out_reg_lo, mask, ShifterOperand(out_reg_lo));
422 __ eor(out_reg_hi, mask, ShifterOperand(out_reg_hi));
423 } else {
424 Register in_reg = in.AsRegister<Register>();
425 Register out_reg = output.AsRegister<Register>();
426
427 __ Asr(mask, in_reg, 31);
428 __ add(out_reg, in_reg, ShifterOperand(mask));
429 __ eor(out_reg, mask, ShifterOperand(out_reg));
430 }
431}
432
433void IntrinsicLocationsBuilderARM::VisitMathAbsInt(HInvoke* invoke) {
434 CreateIntToIntPlusTemp(arena_, invoke);
435}
436
437void IntrinsicCodeGeneratorARM::VisitMathAbsInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000438 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800439}
440
441
442void IntrinsicLocationsBuilderARM::VisitMathAbsLong(HInvoke* invoke) {
443 CreateIntToIntPlusTemp(arena_, invoke);
444}
445
446void IntrinsicCodeGeneratorARM::VisitMathAbsLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000447 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800448}
449
450static void GenMinMax(LocationSummary* locations,
451 bool is_min,
452 ArmAssembler* assembler) {
453 Register op1 = locations->InAt(0).AsRegister<Register>();
454 Register op2 = locations->InAt(1).AsRegister<Register>();
455 Register out = locations->Out().AsRegister<Register>();
456
457 __ cmp(op1, ShifterOperand(op2));
458
459 __ it((is_min) ? Condition::LT : Condition::GT, kItElse);
460 __ mov(out, ShifterOperand(op1), is_min ? Condition::LT : Condition::GT);
461 __ mov(out, ShifterOperand(op2), is_min ? Condition::GE : Condition::LE);
462}
463
464static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
465 LocationSummary* locations = new (arena) LocationSummary(invoke,
466 LocationSummary::kNoCall,
467 kIntrinsified);
468 locations->SetInAt(0, Location::RequiresRegister());
469 locations->SetInAt(1, Location::RequiresRegister());
470 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
471}
472
473void IntrinsicLocationsBuilderARM::VisitMathMinIntInt(HInvoke* invoke) {
474 CreateIntIntToIntLocations(arena_, invoke);
475}
476
477void IntrinsicCodeGeneratorARM::VisitMathMinIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000478 GenMinMax(invoke->GetLocations(), /* is_min */ true, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800479}
480
481void IntrinsicLocationsBuilderARM::VisitMathMaxIntInt(HInvoke* invoke) {
482 CreateIntIntToIntLocations(arena_, invoke);
483}
484
485void IntrinsicCodeGeneratorARM::VisitMathMaxIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000486 GenMinMax(invoke->GetLocations(), /* is_min */ false, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800487}
488
489void IntrinsicLocationsBuilderARM::VisitMathSqrt(HInvoke* invoke) {
490 CreateFPToFPLocations(arena_, invoke);
491}
492
493void IntrinsicCodeGeneratorARM::VisitMathSqrt(HInvoke* invoke) {
494 LocationSummary* locations = invoke->GetLocations();
495 ArmAssembler* assembler = GetAssembler();
496 __ vsqrtd(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
497 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
498}
499
500void IntrinsicLocationsBuilderARM::VisitMemoryPeekByte(HInvoke* invoke) {
501 CreateIntToIntLocations(arena_, invoke);
502}
503
504void IntrinsicCodeGeneratorARM::VisitMemoryPeekByte(HInvoke* invoke) {
505 ArmAssembler* assembler = GetAssembler();
506 // Ignore upper 4B of long address.
507 __ ldrsb(invoke->GetLocations()->Out().AsRegister<Register>(),
508 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
509}
510
511void IntrinsicLocationsBuilderARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
512 CreateIntToIntLocations(arena_, invoke);
513}
514
515void IntrinsicCodeGeneratorARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
516 ArmAssembler* assembler = GetAssembler();
517 // Ignore upper 4B of long address.
518 __ ldr(invoke->GetLocations()->Out().AsRegister<Register>(),
519 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
520}
521
522void IntrinsicLocationsBuilderARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
523 CreateIntToIntLocations(arena_, invoke);
524}
525
526void IntrinsicCodeGeneratorARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
527 ArmAssembler* assembler = GetAssembler();
528 // Ignore upper 4B of long address.
529 Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
530 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
531 // exception. So we can't use ldrd as addr may be unaligned.
532 Register lo = invoke->GetLocations()->Out().AsRegisterPairLow<Register>();
533 Register hi = invoke->GetLocations()->Out().AsRegisterPairHigh<Register>();
534 if (addr == lo) {
535 __ ldr(hi, Address(addr, 4));
536 __ ldr(lo, Address(addr, 0));
537 } else {
538 __ ldr(lo, Address(addr, 0));
539 __ ldr(hi, Address(addr, 4));
540 }
541}
542
543void IntrinsicLocationsBuilderARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
544 CreateIntToIntLocations(arena_, invoke);
545}
546
547void IntrinsicCodeGeneratorARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
548 ArmAssembler* assembler = GetAssembler();
549 // Ignore upper 4B of long address.
550 __ ldrsh(invoke->GetLocations()->Out().AsRegister<Register>(),
551 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
552}
553
554static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
555 LocationSummary* locations = new (arena) LocationSummary(invoke,
556 LocationSummary::kNoCall,
557 kIntrinsified);
558 locations->SetInAt(0, Location::RequiresRegister());
559 locations->SetInAt(1, Location::RequiresRegister());
560}
561
562void IntrinsicLocationsBuilderARM::VisitMemoryPokeByte(HInvoke* invoke) {
563 CreateIntIntToVoidLocations(arena_, invoke);
564}
565
566void IntrinsicCodeGeneratorARM::VisitMemoryPokeByte(HInvoke* invoke) {
567 ArmAssembler* assembler = GetAssembler();
568 __ strb(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
569 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
570}
571
572void IntrinsicLocationsBuilderARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
573 CreateIntIntToVoidLocations(arena_, invoke);
574}
575
576void IntrinsicCodeGeneratorARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
577 ArmAssembler* assembler = GetAssembler();
578 __ str(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
579 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
580}
581
582void IntrinsicLocationsBuilderARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
583 CreateIntIntToVoidLocations(arena_, invoke);
584}
585
586void IntrinsicCodeGeneratorARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
587 ArmAssembler* assembler = GetAssembler();
588 // Ignore upper 4B of long address.
589 Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
590 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
591 // exception. So we can't use ldrd as addr may be unaligned.
592 __ str(invoke->GetLocations()->InAt(1).AsRegisterPairLow<Register>(), Address(addr, 0));
593 __ str(invoke->GetLocations()->InAt(1).AsRegisterPairHigh<Register>(), Address(addr, 4));
594}
595
596void IntrinsicLocationsBuilderARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
597 CreateIntIntToVoidLocations(arena_, invoke);
598}
599
600void IntrinsicCodeGeneratorARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
601 ArmAssembler* assembler = GetAssembler();
602 __ strh(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
603 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
604}
605
606void IntrinsicLocationsBuilderARM::VisitThreadCurrentThread(HInvoke* invoke) {
607 LocationSummary* locations = new (arena_) LocationSummary(invoke,
608 LocationSummary::kNoCall,
609 kIntrinsified);
610 locations->SetOut(Location::RequiresRegister());
611}
612
613void IntrinsicCodeGeneratorARM::VisitThreadCurrentThread(HInvoke* invoke) {
614 ArmAssembler* assembler = GetAssembler();
615 __ LoadFromOffset(kLoadWord,
616 invoke->GetLocations()->Out().AsRegister<Register>(),
617 TR,
618 Thread::PeerOffset<kArmPointerSize>().Int32Value());
619}
620
621static void GenUnsafeGet(HInvoke* invoke,
622 Primitive::Type type,
623 bool is_volatile,
624 CodeGeneratorARM* codegen) {
625 LocationSummary* locations = invoke->GetLocations();
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800626 ArmAssembler* assembler = codegen->GetAssembler();
Roland Levillain3b359c72015-11-17 19:35:12 +0000627 Location base_loc = locations->InAt(1);
628 Register base = base_loc.AsRegister<Register>(); // Object pointer.
629 Location offset_loc = locations->InAt(2);
630 Register offset = offset_loc.AsRegisterPairLow<Register>(); // Long offset, lo part only.
631 Location trg_loc = locations->Out();
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800632
Roland Levillainc9285912015-12-18 10:38:42 +0000633 switch (type) {
634 case Primitive::kPrimInt: {
635 Register trg = trg_loc.AsRegister<Register>();
636 __ ldr(trg, Address(base, offset));
637 if (is_volatile) {
638 __ dmb(ISH);
639 }
640 break;
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800641 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800642
Roland Levillainc9285912015-12-18 10:38:42 +0000643 case Primitive::kPrimNot: {
644 Register trg = trg_loc.AsRegister<Register>();
645 if (kEmitCompilerReadBarrier) {
646 if (kUseBakerReadBarrier) {
647 Location temp = locations->GetTemp(0);
Roland Levillainbfea3352016-06-23 13:48:47 +0100648 codegen->GenerateReferenceLoadWithBakerReadBarrier(
649 invoke, trg_loc, base, 0U, offset_loc, TIMES_1, temp, /* needs_null_check */ false);
Roland Levillainc9285912015-12-18 10:38:42 +0000650 if (is_volatile) {
651 __ dmb(ISH);
652 }
653 } else {
654 __ ldr(trg, Address(base, offset));
655 if (is_volatile) {
656 __ dmb(ISH);
657 }
658 codegen->GenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
659 }
660 } else {
661 __ ldr(trg, Address(base, offset));
662 if (is_volatile) {
663 __ dmb(ISH);
664 }
665 __ MaybeUnpoisonHeapReference(trg);
666 }
667 break;
668 }
Roland Levillain4d027112015-07-01 15:41:14 +0100669
Roland Levillainc9285912015-12-18 10:38:42 +0000670 case Primitive::kPrimLong: {
671 Register trg_lo = trg_loc.AsRegisterPairLow<Register>();
672 __ add(IP, base, ShifterOperand(offset));
673 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
674 Register trg_hi = trg_loc.AsRegisterPairHigh<Register>();
675 __ ldrexd(trg_lo, trg_hi, IP);
676 } else {
677 __ ldrd(trg_lo, Address(IP));
678 }
679 if (is_volatile) {
680 __ dmb(ISH);
681 }
682 break;
683 }
684
685 default:
686 LOG(FATAL) << "Unexpected type " << type;
687 UNREACHABLE();
Roland Levillain4d027112015-07-01 15:41:14 +0100688 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800689}
690
Roland Levillainc9285912015-12-18 10:38:42 +0000691static void CreateIntIntIntToIntLocations(ArenaAllocator* arena,
692 HInvoke* invoke,
693 Primitive::Type type) {
Roland Levillain3b359c72015-11-17 19:35:12 +0000694 bool can_call = kEmitCompilerReadBarrier &&
695 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
696 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800697 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100698 (can_call
699 ? LocationSummary::kCallOnSlowPath
700 : LocationSummary::kNoCall),
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800701 kIntrinsified);
Vladimir Marko70e97462016-08-09 11:04:26 +0100702 if (can_call && kUseBakerReadBarrier) {
Vladimir Marko804b03f2016-09-14 16:26:36 +0100703 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Vladimir Marko70e97462016-08-09 11:04:26 +0100704 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800705 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
706 locations->SetInAt(1, Location::RequiresRegister());
707 locations->SetInAt(2, Location::RequiresRegister());
Roland Levillainbfea3352016-06-23 13:48:47 +0100708 locations->SetOut(Location::RequiresRegister(),
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100709 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Roland Levillainc9285912015-12-18 10:38:42 +0000710 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
711 // We need a temporary register for the read barrier marking slow
Roland Levillainbfea3352016-06-23 13:48:47 +0100712 // path in InstructionCodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier.
Roland Levillainc9285912015-12-18 10:38:42 +0000713 locations->AddTemp(Location::RequiresRegister());
714 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800715}
716
717void IntrinsicLocationsBuilderARM::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000718 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800719}
720void IntrinsicLocationsBuilderARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000721 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800722}
723void IntrinsicLocationsBuilderARM::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000724 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800725}
726void IntrinsicLocationsBuilderARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000727 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800728}
729void IntrinsicLocationsBuilderARM::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000730 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800731}
732void IntrinsicLocationsBuilderARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000733 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800734}
735
736void IntrinsicCodeGeneratorARM::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000737 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800738}
739void IntrinsicCodeGeneratorARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000740 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800741}
742void IntrinsicCodeGeneratorARM::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000743 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800744}
745void IntrinsicCodeGeneratorARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000746 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800747}
748void IntrinsicCodeGeneratorARM::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000749 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800750}
751void IntrinsicCodeGeneratorARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000752 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800753}
754
755static void CreateIntIntIntIntToVoid(ArenaAllocator* arena,
756 const ArmInstructionSetFeatures& features,
757 Primitive::Type type,
758 bool is_volatile,
759 HInvoke* invoke) {
760 LocationSummary* locations = new (arena) LocationSummary(invoke,
761 LocationSummary::kNoCall,
762 kIntrinsified);
763 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
764 locations->SetInAt(1, Location::RequiresRegister());
765 locations->SetInAt(2, Location::RequiresRegister());
766 locations->SetInAt(3, Location::RequiresRegister());
767
768 if (type == Primitive::kPrimLong) {
769 // Potentially need temps for ldrexd-strexd loop.
770 if (is_volatile && !features.HasAtomicLdrdAndStrd()) {
771 locations->AddTemp(Location::RequiresRegister()); // Temp_lo.
772 locations->AddTemp(Location::RequiresRegister()); // Temp_hi.
773 }
774 } else if (type == Primitive::kPrimNot) {
775 // Temps for card-marking.
776 locations->AddTemp(Location::RequiresRegister()); // Temp.
777 locations->AddTemp(Location::RequiresRegister()); // Card.
778 }
779}
780
781void IntrinsicLocationsBuilderARM::VisitUnsafePut(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000782 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800783}
784void IntrinsicLocationsBuilderARM::VisitUnsafePutOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000785 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800786}
787void IntrinsicLocationsBuilderARM::VisitUnsafePutVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000788 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ true, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800789}
790void IntrinsicLocationsBuilderARM::VisitUnsafePutObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000791 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800792}
793void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000794 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800795}
796void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000797 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ true, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800798}
799void IntrinsicLocationsBuilderARM::VisitUnsafePutLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000800 CreateIntIntIntIntToVoid(
801 arena_, features_, Primitive::kPrimLong, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800802}
803void IntrinsicLocationsBuilderARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000804 CreateIntIntIntIntToVoid(
805 arena_, features_, Primitive::kPrimLong, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800806}
807void IntrinsicLocationsBuilderARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000808 CreateIntIntIntIntToVoid(
809 arena_, features_, Primitive::kPrimLong, /* is_volatile */ true, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800810}
811
812static void GenUnsafePut(LocationSummary* locations,
813 Primitive::Type type,
814 bool is_volatile,
815 bool is_ordered,
816 CodeGeneratorARM* codegen) {
817 ArmAssembler* assembler = codegen->GetAssembler();
818
819 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
820 Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Long offset, lo part only.
821 Register value;
822
823 if (is_volatile || is_ordered) {
824 __ dmb(ISH);
825 }
826
827 if (type == Primitive::kPrimLong) {
828 Register value_lo = locations->InAt(3).AsRegisterPairLow<Register>();
829 value = value_lo;
830 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
831 Register temp_lo = locations->GetTemp(0).AsRegister<Register>();
832 Register temp_hi = locations->GetTemp(1).AsRegister<Register>();
833 Register value_hi = locations->InAt(3).AsRegisterPairHigh<Register>();
834
835 __ add(IP, base, ShifterOperand(offset));
836 Label loop_head;
837 __ Bind(&loop_head);
838 __ ldrexd(temp_lo, temp_hi, IP);
839 __ strexd(temp_lo, value_lo, value_hi, IP);
840 __ cmp(temp_lo, ShifterOperand(0));
841 __ b(&loop_head, NE);
842 } else {
843 __ add(IP, base, ShifterOperand(offset));
844 __ strd(value_lo, Address(IP));
845 }
846 } else {
Roland Levillain4d027112015-07-01 15:41:14 +0100847 value = locations->InAt(3).AsRegister<Register>();
848 Register source = value;
849 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
850 Register temp = locations->GetTemp(0).AsRegister<Register>();
851 __ Mov(temp, value);
852 __ PoisonHeapReference(temp);
853 source = temp;
854 }
855 __ str(source, Address(base, offset));
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800856 }
857
858 if (is_volatile) {
859 __ dmb(ISH);
860 }
861
862 if (type == Primitive::kPrimNot) {
863 Register temp = locations->GetTemp(0).AsRegister<Register>();
864 Register card = locations->GetTemp(1).AsRegister<Register>();
Nicolas Geoffray07276db2015-05-18 14:22:09 +0100865 bool value_can_be_null = true; // TODO: Worth finding out this information?
866 codegen->MarkGCCard(temp, card, base, value, value_can_be_null);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800867 }
868}
869
870void IntrinsicCodeGeneratorARM::VisitUnsafePut(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000871 GenUnsafePut(invoke->GetLocations(),
872 Primitive::kPrimInt,
873 /* is_volatile */ false,
874 /* is_ordered */ false,
875 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800876}
877void IntrinsicCodeGeneratorARM::VisitUnsafePutOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000878 GenUnsafePut(invoke->GetLocations(),
879 Primitive::kPrimInt,
880 /* is_volatile */ false,
881 /* is_ordered */ true,
882 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800883}
884void IntrinsicCodeGeneratorARM::VisitUnsafePutVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000885 GenUnsafePut(invoke->GetLocations(),
886 Primitive::kPrimInt,
887 /* is_volatile */ true,
888 /* is_ordered */ false,
889 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800890}
891void IntrinsicCodeGeneratorARM::VisitUnsafePutObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000892 GenUnsafePut(invoke->GetLocations(),
893 Primitive::kPrimNot,
894 /* is_volatile */ false,
895 /* is_ordered */ false,
896 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800897}
898void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000899 GenUnsafePut(invoke->GetLocations(),
900 Primitive::kPrimNot,
901 /* is_volatile */ false,
902 /* is_ordered */ true,
903 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800904}
905void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000906 GenUnsafePut(invoke->GetLocations(),
907 Primitive::kPrimNot,
908 /* is_volatile */ true,
909 /* is_ordered */ false,
910 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800911}
912void IntrinsicCodeGeneratorARM::VisitUnsafePutLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000913 GenUnsafePut(invoke->GetLocations(),
914 Primitive::kPrimLong,
915 /* is_volatile */ false,
916 /* is_ordered */ false,
917 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800918}
919void IntrinsicCodeGeneratorARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000920 GenUnsafePut(invoke->GetLocations(),
921 Primitive::kPrimLong,
922 /* is_volatile */ false,
923 /* is_ordered */ true,
924 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800925}
926void IntrinsicCodeGeneratorARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000927 GenUnsafePut(invoke->GetLocations(),
928 Primitive::kPrimLong,
929 /* is_volatile */ true,
930 /* is_ordered */ false,
931 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800932}
933
934static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena,
Roland Levillain2e50ecb2016-01-27 14:08:33 +0000935 HInvoke* invoke,
936 Primitive::Type type) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100937 bool can_call = kEmitCompilerReadBarrier &&
938 kUseBakerReadBarrier &&
939 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800940 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100941 (can_call
942 ? LocationSummary::kCallOnSlowPath
943 : LocationSummary::kNoCall),
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800944 kIntrinsified);
945 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
946 locations->SetInAt(1, Location::RequiresRegister());
947 locations->SetInAt(2, Location::RequiresRegister());
948 locations->SetInAt(3, Location::RequiresRegister());
949 locations->SetInAt(4, Location::RequiresRegister());
950
Roland Levillain2e50ecb2016-01-27 14:08:33 +0000951 // If heap poisoning is enabled, we don't want the unpoisoning
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100952 // operations to potentially clobber the output. Likewise when
953 // emitting a (Baker) read barrier, which may call.
954 Location::OutputOverlap overlaps =
955 ((kPoisonHeapReferences && type == Primitive::kPrimNot) || can_call)
Roland Levillain2e50ecb2016-01-27 14:08:33 +0000956 ? Location::kOutputOverlap
957 : Location::kNoOutputOverlap;
958 locations->SetOut(Location::RequiresRegister(), overlaps);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800959
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100960 // Temporary registers used in CAS. In the object case
961 // (UnsafeCASObject intrinsic), these are also used for
962 // card-marking, and possibly for (Baker) read barrier.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800963 locations->AddTemp(Location::RequiresRegister()); // Pointer.
964 locations->AddTemp(Location::RequiresRegister()); // Temp 1.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800965}
966
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100967static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARM* codegen) {
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800968 DCHECK_NE(type, Primitive::kPrimLong);
969
970 ArmAssembler* assembler = codegen->GetAssembler();
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100971 LocationSummary* locations = invoke->GetLocations();
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800972
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100973 Location out_loc = locations->Out();
974 Register out = out_loc.AsRegister<Register>(); // Boolean result.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800975
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100976 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
977 Location offset_loc = locations->InAt(2);
978 Register offset = offset_loc.AsRegisterPairLow<Register>(); // Offset (discard high 4B).
979 Register expected = locations->InAt(3).AsRegister<Register>(); // Expected.
980 Register value = locations->InAt(4).AsRegister<Register>(); // Value.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800981
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100982 Location tmp_ptr_loc = locations->GetTemp(0);
983 Register tmp_ptr = tmp_ptr_loc.AsRegister<Register>(); // Pointer to actual memory.
984 Register tmp = locations->GetTemp(1).AsRegister<Register>(); // Value in memory.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800985
986 if (type == Primitive::kPrimNot) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100987 // The only read barrier implementation supporting the
988 // UnsafeCASObject intrinsic is the Baker-style read barriers.
989 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
990
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800991 // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
992 // object and scan the receiver at the next GC for nothing.
Nicolas Geoffray07276db2015-05-18 14:22:09 +0100993 bool value_can_be_null = true; // TODO: Worth finding out this information?
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100994 codegen->MarkGCCard(tmp_ptr, tmp, base, value, value_can_be_null);
995
996 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
997 // Need to make sure the reference stored in the field is a to-space
998 // one before attempting the CAS or the CAS could fail incorrectly.
999 codegen->GenerateReferenceLoadWithBakerReadBarrier(
1000 invoke,
1001 out_loc, // Unused, used only as a "temporary" within the read barrier.
1002 base,
1003 /* offset */ 0u,
1004 /* index */ offset_loc,
1005 ScaleFactor::TIMES_1,
1006 tmp_ptr_loc,
1007 /* needs_null_check */ false,
1008 /* always_update_field */ true,
1009 &tmp);
1010 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001011 }
1012
1013 // Prevent reordering with prior memory operations.
Roland Levillain4bedb382016-01-12 12:01:04 +00001014 // Emit a DMB ISH instruction instead of an DMB ISHST one, as the
1015 // latter allows a preceding load to be delayed past the STXR
1016 // instruction below.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001017 __ dmb(ISH);
1018
1019 __ add(tmp_ptr, base, ShifterOperand(offset));
1020
Roland Levillain4d027112015-07-01 15:41:14 +01001021 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001022 __ PoisonHeapReference(expected);
1023 if (value == expected) {
1024 // Do not poison `value`, as it is the same register as
1025 // `expected`, which has just been poisoned.
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001026 } else {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001027 __ PoisonHeapReference(value);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001028 }
Roland Levillain4d027112015-07-01 15:41:14 +01001029 }
1030
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001031 // do {
1032 // tmp = [r_ptr] - expected;
1033 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
1034 // result = tmp != 0;
1035
1036 Label loop_head;
1037 __ Bind(&loop_head);
1038
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001039 __ ldrex(tmp, tmp_ptr);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001040
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001041 __ subs(tmp, tmp, ShifterOperand(expected));
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001042
1043 __ it(EQ, ItState::kItT);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001044 __ strex(tmp, value, tmp_ptr, EQ);
1045 __ cmp(tmp, ShifterOperand(1), EQ);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001046
1047 __ b(&loop_head, EQ);
1048
1049 __ dmb(ISH);
1050
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001051 __ rsbs(out, tmp, ShifterOperand(1));
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001052 __ it(CC);
1053 __ mov(out, ShifterOperand(0), CC);
Roland Levillain4d027112015-07-01 15:41:14 +01001054
1055 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001056 __ UnpoisonHeapReference(expected);
1057 if (value == expected) {
1058 // Do not unpoison `value`, as it is the same register as
1059 // `expected`, which has just been unpoisoned.
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001060 } else {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001061 __ UnpoisonHeapReference(value);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001062 }
Roland Levillain4d027112015-07-01 15:41:14 +01001063 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001064}
1065
Andreas Gampeca714582015-04-03 19:41:34 -07001066void IntrinsicLocationsBuilderARM::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001067 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001068}
Andreas Gampeca714582015-04-03 19:41:34 -07001069void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001070 // The only read barrier implementation supporting the
1071 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1072 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain985ff702015-10-23 13:25:35 +01001073 return;
1074 }
1075
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001076 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001077}
1078void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001079 GenCas(invoke, Primitive::kPrimInt, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001080}
1081void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001082 // The only read barrier implementation supporting the
1083 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1084 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01001085
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001086 GenCas(invoke, Primitive::kPrimNot, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001087}
1088
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001089void IntrinsicLocationsBuilderARM::VisitStringCompareTo(HInvoke* invoke) {
1090 // The inputs plus one temp.
1091 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001092 invoke->InputAt(1)->CanBeNull()
1093 ? LocationSummary::kCallOnSlowPath
1094 : LocationSummary::kNoCall,
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001095 kIntrinsified);
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001096 locations->SetInAt(0, Location::RequiresRegister());
1097 locations->SetInAt(1, Location::RequiresRegister());
1098 locations->AddTemp(Location::RequiresRegister());
1099 locations->AddTemp(Location::RequiresRegister());
1100 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001101 // Need temporary registers for String compression's feature.
1102 if (mirror::kUseStringCompression) {
1103 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001104 }
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001105 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001106}
1107
1108void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) {
1109 ArmAssembler* assembler = GetAssembler();
1110 LocationSummary* locations = invoke->GetLocations();
1111
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001112 Register str = locations->InAt(0).AsRegister<Register>();
1113 Register arg = locations->InAt(1).AsRegister<Register>();
1114 Register out = locations->Out().AsRegister<Register>();
1115
1116 Register temp0 = locations->GetTemp(0).AsRegister<Register>();
1117 Register temp1 = locations->GetTemp(1).AsRegister<Register>();
1118 Register temp2 = locations->GetTemp(2).AsRegister<Register>();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001119 Register temp3;
jessicahandojo05765752016-09-09 19:01:32 -07001120 if (mirror::kUseStringCompression) {
1121 temp3 = locations->GetTemp(3).AsRegister<Register>();
jessicahandojo05765752016-09-09 19:01:32 -07001122 }
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001123
1124 Label loop;
1125 Label find_char_diff;
1126 Label end;
jessicahandojo05765752016-09-09 19:01:32 -07001127 Label different_compression;
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001128
1129 // Get offsets of count and value fields within a string object.
1130 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1131 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1132
Nicolas Geoffray512e04d2015-03-27 17:21:24 +00001133 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +01001134 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001135
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001136 // Take slow path and throw if input can be and is null.
1137 SlowPathCode* slow_path = nullptr;
1138 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1139 if (can_slow_path) {
1140 slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
1141 codegen_->AddSlowPath(slow_path);
1142 __ CompareAndBranchIfZero(arg, slow_path->GetEntryLabel());
1143 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001144
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001145 // Reference equality check, return 0 if same reference.
1146 __ subs(out, str, ShifterOperand(arg));
1147 __ b(&end, EQ);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001148
jessicahandojo05765752016-09-09 19:01:32 -07001149 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001150 // Load `count` fields of this and argument strings.
jessicahandojo05765752016-09-09 19:01:32 -07001151 __ ldr(temp3, Address(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001152 __ ldr(temp2, Address(arg, count_offset));
1153 // Extract lengths from the `count` fields.
1154 __ Lsr(temp0, temp3, 1u);
1155 __ Lsr(temp1, temp2, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001156 } else {
1157 // Load lengths of this and argument strings.
1158 __ ldr(temp0, Address(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001159 __ ldr(temp1, Address(arg, count_offset));
jessicahandojo05765752016-09-09 19:01:32 -07001160 }
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001161 // out = length diff.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001162 __ subs(out, temp0, ShifterOperand(temp1));
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001163 // temp0 = min(len(str), len(arg)).
jessicahandojo05765752016-09-09 19:01:32 -07001164 __ it(GT);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001165 __ mov(temp0, ShifterOperand(temp1), GT);
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001166 // Shorter string is empty?
1167 __ CompareAndBranchIfZero(temp0, &end);
1168
jessicahandojo05765752016-09-09 19:01:32 -07001169 if (mirror::kUseStringCompression) {
1170 // Check if both strings using same compression style to use this comparison loop.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001171 __ eor(temp2, temp2, ShifterOperand(temp3));
1172 __ Lsrs(temp2, temp2, 1u);
1173 __ b(&different_compression, CS);
jessicahandojo05765752016-09-09 19:01:32 -07001174 // For string compression, calculate the number of bytes to compare (not chars).
1175 // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001176 __ Lsls(temp3, temp3, 31u); // Extract purely the compression flag.
1177 __ it(NE);
1178 __ add(temp0, temp0, ShifterOperand(temp0), NE);
jessicahandojo05765752016-09-09 19:01:32 -07001179 }
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001180
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001181 // Store offset of string value in preparation for comparison loop.
1182 __ mov(temp1, ShifterOperand(value_offset));
1183
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001184 // Assertions that must hold in order to compare multiple characters at a time.
1185 CHECK_ALIGNED(value_offset, 8);
1186 static_assert(IsAligned<8>(kObjectAlignment),
1187 "String data must be 8-byte aligned for unrolled CompareTo loop.");
1188
1189 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1190 DCHECK_EQ(char_size, 2u);
1191
jessicahandojo05765752016-09-09 19:01:32 -07001192 Label find_char_diff_2nd_cmp;
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001193 // Unrolled loop comparing 4x16-bit chars per iteration (ok because of string data alignment).
1194 __ Bind(&loop);
1195 __ ldr(IP, Address(str, temp1));
1196 __ ldr(temp2, Address(arg, temp1));
1197 __ cmp(IP, ShifterOperand(temp2));
1198 __ b(&find_char_diff, NE);
1199 __ add(temp1, temp1, ShifterOperand(char_size * 2));
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001200
1201 __ ldr(IP, Address(str, temp1));
1202 __ ldr(temp2, Address(arg, temp1));
1203 __ cmp(IP, ShifterOperand(temp2));
jessicahandojo05765752016-09-09 19:01:32 -07001204 __ b(&find_char_diff_2nd_cmp, NE);
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001205 __ add(temp1, temp1, ShifterOperand(char_size * 2));
jessicahandojo05765752016-09-09 19:01:32 -07001206 // With string compression, we have compared 8 bytes, otherwise 4 chars.
1207 __ subs(temp0, temp0, ShifterOperand(mirror::kUseStringCompression ? 8 : 4));
1208 __ b(&loop, HI);
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001209 __ b(&end);
1210
jessicahandojo05765752016-09-09 19:01:32 -07001211 __ Bind(&find_char_diff_2nd_cmp);
1212 if (mirror::kUseStringCompression) {
1213 __ subs(temp0, temp0, ShifterOperand(4)); // 4 bytes previously compared.
1214 __ b(&end, LS); // Was the second comparison fully beyond the end?
1215 } else {
1216 // Without string compression, we can start treating temp0 as signed
1217 // and rely on the signed comparison below.
1218 __ sub(temp0, temp0, ShifterOperand(2));
1219 }
1220
1221 // Find the single character difference.
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001222 __ Bind(&find_char_diff);
1223 // Get the bit position of the first character that differs.
1224 __ eor(temp1, temp2, ShifterOperand(IP));
1225 __ rbit(temp1, temp1);
1226 __ clz(temp1, temp1);
1227
jessicahandojo05765752016-09-09 19:01:32 -07001228 // temp0 = number of characters remaining to compare.
1229 // (Without string compression, it could be < 1 if a difference is found by the second CMP
1230 // in the comparison loop, and after the end of the shorter string data).
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001231
jessicahandojo05765752016-09-09 19:01:32 -07001232 // Without string compression (temp1 >> 4) = character where difference occurs between the last
1233 // two words compared, in the interval [0,1].
1234 // (0 for low half-word different, 1 for high half-word different).
1235 // With string compression, (temp1 << 3) = byte where the difference occurs,
1236 // in the interval [0,3].
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001237
jessicahandojo05765752016-09-09 19:01:32 -07001238 // If temp0 <= (temp1 >> (kUseStringCompression ? 3 : 4)), the difference occurs outside
1239 // the remaining string data, so just return length diff (out).
1240 // The comparison is unsigned for string compression, otherwise signed.
1241 __ cmp(temp0, ShifterOperand(temp1, LSR, mirror::kUseStringCompression ? 3 : 4));
1242 __ b(&end, mirror::kUseStringCompression ? LS : LE);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001243
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001244 // Extract the characters and calculate the difference.
jessicahandojo05765752016-09-09 19:01:32 -07001245 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001246 // For compressed strings we need to clear 0x7 from temp1, for uncompressed we need to clear
1247 // 0xf. We also need to prepare the character extraction mask `uncompressed ? 0xffffu : 0xffu`.
1248 // The compression flag is now in the highest bit of temp3, so let's play some tricks.
1249 __ orr(temp3, temp3, ShifterOperand(0xffu << 23)); // uncompressed ? 0xff800000u : 0x7ff80000u
1250 __ bic(temp1, temp1, ShifterOperand(temp3, LSR, 31 - 3)); // &= ~(uncompressed ? 0xfu : 0x7u)
1251 __ Asr(temp3, temp3, 7u); // uncompressed ? 0xffff0000u : 0xff0000u.
1252 __ Lsr(temp2, temp2, temp1); // Extract second character.
1253 __ Lsr(temp3, temp3, 16u); // uncompressed ? 0xffffu : 0xffu
1254 __ Lsr(out, IP, temp1); // Extract first character.
1255 __ and_(temp2, temp2, ShifterOperand(temp3));
1256 __ and_(out, out, ShifterOperand(temp3));
1257 } else {
1258 __ bic(temp1, temp1, ShifterOperand(0xf));
1259 __ Lsr(temp2, temp2, temp1);
1260 __ Lsr(out, IP, temp1);
1261 __ movt(temp2, 0);
1262 __ movt(out, 0);
jessicahandojo05765752016-09-09 19:01:32 -07001263 }
jessicahandojo05765752016-09-09 19:01:32 -07001264
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001265 __ sub(out, out, ShifterOperand(temp2));
jessicahandojo05765752016-09-09 19:01:32 -07001266
1267 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001268 __ b(&end);
1269 __ Bind(&different_compression);
1270
1271 // Comparison for different compression style.
jessicahandojo05765752016-09-09 19:01:32 -07001272 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
1273 DCHECK_EQ(c_char_size, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001274
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001275 // We want to free up the temp3, currently holding `str.count`, for comparison.
1276 // So, we move it to the bottom bit of the iteration count `temp0` which we tnen
1277 // need to treat as unsigned. Start by freeing the bit with an ADD and continue
1278 // further down by a LSRS+SBC which will flip the meaning of the flag but allow
1279 // `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
1280 __ add(temp0, temp0, ShifterOperand(temp0)); // Unlike LSL, this ADD is always 16-bit.
1281 // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
1282 __ mov(temp1, ShifterOperand(str));
1283 __ mov(temp2, ShifterOperand(arg));
1284 __ Lsrs(temp3, temp3, 1u); // Continue the move of the compression flag.
1285 __ it(CS, kItThen); // Interleave with selection of temp1 and temp2.
1286 __ mov(temp1, ShifterOperand(arg), CS); // Preserves flags.
1287 __ mov(temp2, ShifterOperand(str), CS); // Preserves flags.
1288 __ sbc(temp0, temp0, ShifterOperand(0)); // Complete the move of the compression flag.
jessicahandojo05765752016-09-09 19:01:32 -07001289
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001290 // Adjust temp1 and temp2 from string pointers to data pointers.
1291 __ add(temp1, temp1, ShifterOperand(value_offset));
1292 __ add(temp2, temp2, ShifterOperand(value_offset));
1293
1294 Label different_compression_loop;
1295 Label different_compression_diff;
1296
1297 // Main loop for different compression.
1298 __ Bind(&different_compression_loop);
1299 __ ldrb(IP, Address(temp1, c_char_size, Address::PostIndex));
1300 __ ldrh(temp3, Address(temp2, char_size, Address::PostIndex));
1301 __ cmp(IP, ShifterOperand(temp3));
1302 __ b(&different_compression_diff, NE);
1303 __ subs(temp0, temp0, ShifterOperand(2));
1304 __ b(&different_compression_loop, HI);
jessicahandojo05765752016-09-09 19:01:32 -07001305 __ b(&end);
1306
1307 // Calculate the difference.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001308 __ Bind(&different_compression_diff);
1309 __ sub(out, IP, ShifterOperand(temp3));
1310 // Flip the difference if the `arg` is compressed.
1311 // `temp0` contains inverted `str` compression flag, i.e the same as `arg` compression flag.
1312 __ Lsrs(temp0, temp0, 1u);
1313 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1314 "Expecting 0=compressed, 1=uncompressed");
1315 __ it(CC);
1316 __ rsb(out, out, ShifterOperand(0), CC);
jessicahandojo05765752016-09-09 19:01:32 -07001317 }
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001318
1319 __ Bind(&end);
1320
1321 if (can_slow_path) {
1322 __ Bind(slow_path->GetExitLabel());
1323 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001324}
1325
Agi Csaki289cd552015-08-18 17:10:38 -07001326void IntrinsicLocationsBuilderARM::VisitStringEquals(HInvoke* invoke) {
1327 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1328 LocationSummary::kNoCall,
1329 kIntrinsified);
1330 InvokeRuntimeCallingConvention calling_convention;
1331 locations->SetInAt(0, Location::RequiresRegister());
1332 locations->SetInAt(1, Location::RequiresRegister());
1333 // Temporary registers to store lengths of strings and for calculations.
1334 // Using instruction cbz requires a low register, so explicitly set a temp to be R0.
1335 locations->AddTemp(Location::RegisterLocation(R0));
1336 locations->AddTemp(Location::RequiresRegister());
1337 locations->AddTemp(Location::RequiresRegister());
1338
1339 locations->SetOut(Location::RequiresRegister());
1340}
1341
1342void IntrinsicCodeGeneratorARM::VisitStringEquals(HInvoke* invoke) {
1343 ArmAssembler* assembler = GetAssembler();
1344 LocationSummary* locations = invoke->GetLocations();
1345
1346 Register str = locations->InAt(0).AsRegister<Register>();
1347 Register arg = locations->InAt(1).AsRegister<Register>();
1348 Register out = locations->Out().AsRegister<Register>();
1349
1350 Register temp = locations->GetTemp(0).AsRegister<Register>();
1351 Register temp1 = locations->GetTemp(1).AsRegister<Register>();
1352 Register temp2 = locations->GetTemp(2).AsRegister<Register>();
1353
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001354 Label loop;
Agi Csaki289cd552015-08-18 17:10:38 -07001355 Label end;
1356 Label return_true;
1357 Label return_false;
1358
1359 // Get offsets of count, value, and class fields within a string object.
1360 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
1361 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1362 const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
1363
1364 // Note that the null check must have been done earlier.
1365 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1366
Vladimir Marko53b52002016-05-24 19:30:45 +01001367 StringEqualsOptimizations optimizations(invoke);
1368 if (!optimizations.GetArgumentNotNull()) {
1369 // Check if input is null, return false if it is.
1370 __ CompareAndBranchIfZero(arg, &return_false);
1371 }
Agi Csaki289cd552015-08-18 17:10:38 -07001372
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001373 // Reference equality check, return true if same reference.
1374 __ cmp(str, ShifterOperand(arg));
1375 __ b(&return_true, EQ);
1376
Vladimir Marko53b52002016-05-24 19:30:45 +01001377 if (!optimizations.GetArgumentIsString()) {
1378 // Instanceof check for the argument by comparing class fields.
1379 // All string objects must have the same type since String cannot be subclassed.
1380 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1381 // If the argument is a string object, its class field must be equal to receiver's class field.
1382 __ ldr(temp, Address(str, class_offset));
1383 __ ldr(temp1, Address(arg, class_offset));
1384 __ cmp(temp, ShifterOperand(temp1));
1385 __ b(&return_false, NE);
1386 }
Agi Csaki289cd552015-08-18 17:10:38 -07001387
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001388 // Load `count` fields of this and argument strings.
Agi Csaki289cd552015-08-18 17:10:38 -07001389 __ ldr(temp, Address(str, count_offset));
1390 __ ldr(temp1, Address(arg, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001391 // Check if `count` fields are equal, return false if they're not.
jessicahandojo05765752016-09-09 19:01:32 -07001392 // Also compares the compression style, if differs return false.
Agi Csaki289cd552015-08-18 17:10:38 -07001393 __ cmp(temp, ShifterOperand(temp1));
1394 __ b(&return_false, NE);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001395 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
1396 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1397 "Expecting 0=compressed, 1=uncompressed");
Agi Csaki289cd552015-08-18 17:10:38 -07001398 __ cbz(temp, &return_true);
Agi Csaki289cd552015-08-18 17:10:38 -07001399
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001400 // Assertions that must hold in order to compare strings 4 bytes at a time.
Agi Csaki289cd552015-08-18 17:10:38 -07001401 DCHECK_ALIGNED(value_offset, 4);
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001402 static_assert(IsAligned<4>(kObjectAlignment), "String data must be aligned for fast compare.");
Agi Csaki289cd552015-08-18 17:10:38 -07001403
jessicahandojo05765752016-09-09 19:01:32 -07001404 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001405 // For string compression, calculate the number of bytes to compare (not chars).
1406 // This could in theory exceed INT32_MAX, so treat temp as unsigned.
1407 __ Lsrs(temp, temp, 1u); // Extract length and check compression flag.
1408 __ it(CS); // If uncompressed,
1409 __ add(temp, temp, ShifterOperand(temp), CS); // double the byte count.
jessicahandojo05765752016-09-09 19:01:32 -07001410 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001411
1412 // Store offset of string value in preparation for comparison loop.
jessicahandojo05765752016-09-09 19:01:32 -07001413 __ LoadImmediate(temp1, value_offset);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001414
1415 // Loop to compare strings 4 bytes at a time starting at the front of the string.
1416 // Ok to do this because strings are zero-padded to kObjectAlignment.
Agi Csaki289cd552015-08-18 17:10:38 -07001417 __ Bind(&loop);
1418 __ ldr(out, Address(str, temp1));
1419 __ ldr(temp2, Address(arg, temp1));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001420 __ add(temp1, temp1, ShifterOperand(sizeof(uint32_t)));
Agi Csaki289cd552015-08-18 17:10:38 -07001421 __ cmp(out, ShifterOperand(temp2));
1422 __ b(&return_false, NE);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001423 // With string compression, we have compared 4 bytes, otherwise 2 chars.
1424 __ subs(temp, temp, ShifterOperand(mirror::kUseStringCompression ? 4 : 2));
1425 __ b(&loop, HI);
Agi Csaki289cd552015-08-18 17:10:38 -07001426
1427 // Return true and exit the function.
1428 // If loop does not result in returning false, we return true.
1429 __ Bind(&return_true);
1430 __ LoadImmediate(out, 1);
1431 __ b(&end);
1432
1433 // Return false and exit the function.
1434 __ Bind(&return_false);
1435 __ LoadImmediate(out, 0);
1436 __ Bind(&end);
1437}
1438
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001439static void GenerateVisitStringIndexOf(HInvoke* invoke,
1440 ArmAssembler* assembler,
1441 CodeGeneratorARM* codegen,
1442 ArenaAllocator* allocator,
1443 bool start_at_zero) {
1444 LocationSummary* locations = invoke->GetLocations();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001445
1446 // Note that the null check must have been done earlier.
1447 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1448
1449 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001450 // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
Andreas Gampe85b62f22015-09-09 13:15:38 -07001451 SlowPathCode* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001452 HInstruction* code_point = invoke->InputAt(1);
1453 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01001454 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) >
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001455 std::numeric_limits<uint16_t>::max()) {
1456 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1457 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1458 slow_path = new (allocator) IntrinsicSlowPathARM(invoke);
1459 codegen->AddSlowPath(slow_path);
1460 __ b(slow_path->GetEntryLabel());
1461 __ Bind(slow_path->GetExitLabel());
1462 return;
1463 }
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001464 } else if (code_point->GetType() != Primitive::kPrimChar) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001465 Register char_reg = locations->InAt(1).AsRegister<Register>();
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001466 // 0xffff is not modified immediate but 0x10000 is, so use `>= 0x10000` instead of `> 0xffff`.
1467 __ cmp(char_reg,
1468 ShifterOperand(static_cast<uint32_t>(std::numeric_limits<uint16_t>::max()) + 1));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001469 slow_path = new (allocator) IntrinsicSlowPathARM(invoke);
1470 codegen->AddSlowPath(slow_path);
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001471 __ b(slow_path->GetEntryLabel(), HS);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001472 }
1473
1474 if (start_at_zero) {
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001475 Register tmp_reg = locations->GetTemp(0).AsRegister<Register>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001476 DCHECK_EQ(tmp_reg, R2);
1477 // Start-index = 0.
1478 __ LoadImmediate(tmp_reg, 0);
1479 }
1480
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01001481 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
Roland Levillain42ad2882016-02-29 18:26:54 +00001482 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001483
1484 if (slow_path != nullptr) {
1485 __ Bind(slow_path->GetExitLabel());
1486 }
1487}
1488
1489void IntrinsicLocationsBuilderARM::VisitStringIndexOf(HInvoke* invoke) {
1490 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001491 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001492 kIntrinsified);
1493 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1494 // best to align the inputs accordingly.
1495 InvokeRuntimeCallingConvention calling_convention;
1496 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1497 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1498 locations->SetOut(Location::RegisterLocation(R0));
1499
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001500 // Need to send start-index=0.
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001501 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1502}
1503
1504void IntrinsicCodeGeneratorARM::VisitStringIndexOf(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001505 GenerateVisitStringIndexOf(
1506 invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001507}
1508
1509void IntrinsicLocationsBuilderARM::VisitStringIndexOfAfter(HInvoke* invoke) {
1510 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001511 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001512 kIntrinsified);
1513 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1514 // best to align the inputs accordingly.
1515 InvokeRuntimeCallingConvention calling_convention;
1516 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1517 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1518 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1519 locations->SetOut(Location::RegisterLocation(R0));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001520}
1521
1522void IntrinsicCodeGeneratorARM::VisitStringIndexOfAfter(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001523 GenerateVisitStringIndexOf(
1524 invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001525}
1526
Jeff Hao848f70a2014-01-15 13:49:50 -08001527void IntrinsicLocationsBuilderARM::VisitStringNewStringFromBytes(HInvoke* invoke) {
1528 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001529 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001530 kIntrinsified);
1531 InvokeRuntimeCallingConvention calling_convention;
1532 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1533 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1534 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1535 locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
1536 locations->SetOut(Location::RegisterLocation(R0));
1537}
1538
1539void IntrinsicCodeGeneratorARM::VisitStringNewStringFromBytes(HInvoke* invoke) {
1540 ArmAssembler* assembler = GetAssembler();
1541 LocationSummary* locations = invoke->GetLocations();
1542
1543 Register byte_array = locations->InAt(0).AsRegister<Register>();
1544 __ cmp(byte_array, ShifterOperand(0));
Andreas Gampe85b62f22015-09-09 13:15:38 -07001545 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
Jeff Hao848f70a2014-01-15 13:49:50 -08001546 codegen_->AddSlowPath(slow_path);
1547 __ b(slow_path->GetEntryLabel(), EQ);
1548
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01001549 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001550 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001551 __ Bind(slow_path->GetExitLabel());
1552}
1553
1554void IntrinsicLocationsBuilderARM::VisitStringNewStringFromChars(HInvoke* invoke) {
1555 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001556 LocationSummary::kCallOnMainOnly,
Jeff Hao848f70a2014-01-15 13:49:50 -08001557 kIntrinsified);
1558 InvokeRuntimeCallingConvention calling_convention;
1559 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1560 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1561 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1562 locations->SetOut(Location::RegisterLocation(R0));
1563}
1564
1565void IntrinsicCodeGeneratorARM::VisitStringNewStringFromChars(HInvoke* invoke) {
Roland Levillaincc3839c2016-02-29 16:23:48 +00001566 // No need to emit code checking whether `locations->InAt(2)` is a null
1567 // pointer, as callers of the native method
1568 //
1569 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1570 //
1571 // all include a null check on `data` before calling that method.
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01001572 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
Roland Levillainf969a202016-03-09 16:14:00 +00001573 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001574}
1575
1576void IntrinsicLocationsBuilderARM::VisitStringNewStringFromString(HInvoke* invoke) {
1577 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001578 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001579 kIntrinsified);
1580 InvokeRuntimeCallingConvention calling_convention;
1581 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1582 locations->SetOut(Location::RegisterLocation(R0));
1583}
1584
1585void IntrinsicCodeGeneratorARM::VisitStringNewStringFromString(HInvoke* invoke) {
1586 ArmAssembler* assembler = GetAssembler();
1587 LocationSummary* locations = invoke->GetLocations();
1588
1589 Register string_to_copy = locations->InAt(0).AsRegister<Register>();
1590 __ cmp(string_to_copy, ShifterOperand(0));
Andreas Gampe85b62f22015-09-09 13:15:38 -07001591 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
Jeff Hao848f70a2014-01-15 13:49:50 -08001592 codegen_->AddSlowPath(slow_path);
1593 __ b(slow_path->GetEntryLabel(), EQ);
1594
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01001595 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001596 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01001597
Jeff Hao848f70a2014-01-15 13:49:50 -08001598 __ Bind(slow_path->GetExitLabel());
1599}
1600
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001601void IntrinsicLocationsBuilderARM::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01001602 // The only read barrier implementation supporting the
1603 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1604 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain3d312422016-06-23 13:53:42 +01001605 return;
1606 }
1607
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001608 CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
1609 LocationSummary* locations = invoke->GetLocations();
1610 if (locations == nullptr) {
1611 return;
1612 }
1613
1614 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1615 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
1616 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1617
1618 if (src_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(src_pos->GetValue())) {
1619 locations->SetInAt(1, Location::RequiresRegister());
1620 }
1621 if (dest_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(dest_pos->GetValue())) {
1622 locations->SetInAt(3, Location::RequiresRegister());
1623 }
1624 if (length != nullptr && !assembler_->ShifterOperandCanAlwaysHold(length->GetValue())) {
1625 locations->SetInAt(4, Location::RequiresRegister());
1626 }
Roland Levillain0b671c02016-08-19 12:02:34 +01001627 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1628 // Temporary register IP cannot be used in
Roland Levillain16d9f942016-08-25 17:27:56 +01001629 // ReadBarrierSystemArrayCopySlowPathARM (because that register
Roland Levillain0b671c02016-08-19 12:02:34 +01001630 // is clobbered by ReadBarrierMarkRegX entry points). Get an extra
1631 // temporary register from the register allocator.
1632 locations->AddTemp(Location::RequiresRegister());
1633 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001634}
1635
1636static void CheckPosition(ArmAssembler* assembler,
1637 Location pos,
1638 Register input,
1639 Location length,
1640 SlowPathCode* slow_path,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001641 Register temp,
1642 bool length_is_input_length = false) {
1643 // Where is the length in the Array?
1644 const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
1645
1646 if (pos.IsConstant()) {
1647 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
1648 if (pos_const == 0) {
1649 if (!length_is_input_length) {
1650 // Check that length(input) >= length.
1651 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
1652 if (length.IsConstant()) {
1653 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1654 } else {
1655 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1656 }
1657 __ b(slow_path->GetEntryLabel(), LT);
1658 }
1659 } else {
1660 // Check that length(input) >= pos.
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01001661 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
1662 __ subs(temp, temp, ShifterOperand(pos_const));
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001663 __ b(slow_path->GetEntryLabel(), LT);
1664
1665 // Check that (length(input) - pos) >= length.
1666 if (length.IsConstant()) {
1667 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1668 } else {
1669 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1670 }
1671 __ b(slow_path->GetEntryLabel(), LT);
1672 }
1673 } else if (length_is_input_length) {
1674 // The only way the copy can succeed is if pos is zero.
1675 Register pos_reg = pos.AsRegister<Register>();
1676 __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
1677 } else {
1678 // Check that pos >= 0.
1679 Register pos_reg = pos.AsRegister<Register>();
1680 __ cmp(pos_reg, ShifterOperand(0));
1681 __ b(slow_path->GetEntryLabel(), LT);
1682
1683 // Check that pos <= length(input).
1684 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
1685 __ subs(temp, temp, ShifterOperand(pos_reg));
1686 __ b(slow_path->GetEntryLabel(), LT);
1687
1688 // Check that (length(input) - pos) >= length.
1689 if (length.IsConstant()) {
1690 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1691 } else {
1692 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1693 }
1694 __ b(slow_path->GetEntryLabel(), LT);
1695 }
1696}
1697
1698void IntrinsicCodeGeneratorARM::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01001699 // The only read barrier implementation supporting the
1700 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1701 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01001702
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001703 ArmAssembler* assembler = GetAssembler();
1704 LocationSummary* locations = invoke->GetLocations();
1705
1706 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1707 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
1708 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
1709 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Roland Levillain0b671c02016-08-19 12:02:34 +01001710 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001711
1712 Register src = locations->InAt(0).AsRegister<Register>();
1713 Location src_pos = locations->InAt(1);
1714 Register dest = locations->InAt(2).AsRegister<Register>();
1715 Location dest_pos = locations->InAt(3);
1716 Location length = locations->InAt(4);
Roland Levillain0b671c02016-08-19 12:02:34 +01001717 Location temp1_loc = locations->GetTemp(0);
1718 Register temp1 = temp1_loc.AsRegister<Register>();
1719 Location temp2_loc = locations->GetTemp(1);
1720 Register temp2 = temp2_loc.AsRegister<Register>();
1721 Location temp3_loc = locations->GetTemp(2);
1722 Register temp3 = temp3_loc.AsRegister<Register>();
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001723
Roland Levillain0b671c02016-08-19 12:02:34 +01001724 SlowPathCode* intrinsic_slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
1725 codegen_->AddSlowPath(intrinsic_slow_path);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001726
Roland Levillainebea3d22016-04-12 15:42:57 +01001727 Label conditions_on_positions_validated;
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001728 SystemArrayCopyOptimizations optimizations(invoke);
1729
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001730 // If source and destination are the same, we go to slow path if we need to do
1731 // forward copying.
1732 if (src_pos.IsConstant()) {
1733 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
1734 if (dest_pos.IsConstant()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001735 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
1736 if (optimizations.GetDestinationIsSource()) {
1737 // Checked when building locations.
1738 DCHECK_GE(src_pos_constant, dest_pos_constant);
1739 } else if (src_pos_constant < dest_pos_constant) {
1740 __ cmp(src, ShifterOperand(dest));
Roland Levillain0b671c02016-08-19 12:02:34 +01001741 __ b(intrinsic_slow_path->GetEntryLabel(), EQ);
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001742 }
1743
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001744 // Checked when building locations.
1745 DCHECK(!optimizations.GetDestinationIsSource()
1746 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
1747 } else {
1748 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001749 __ cmp(src, ShifterOperand(dest));
Roland Levillainebea3d22016-04-12 15:42:57 +01001750 __ b(&conditions_on_positions_validated, NE);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001751 }
1752 __ cmp(dest_pos.AsRegister<Register>(), ShifterOperand(src_pos_constant));
Roland Levillain0b671c02016-08-19 12:02:34 +01001753 __ b(intrinsic_slow_path->GetEntryLabel(), GT);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001754 }
1755 } else {
1756 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001757 __ cmp(src, ShifterOperand(dest));
Roland Levillainebea3d22016-04-12 15:42:57 +01001758 __ b(&conditions_on_positions_validated, NE);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001759 }
1760 if (dest_pos.IsConstant()) {
1761 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
1762 __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos_constant));
1763 } else {
1764 __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos.AsRegister<Register>()));
1765 }
Roland Levillain0b671c02016-08-19 12:02:34 +01001766 __ b(intrinsic_slow_path->GetEntryLabel(), LT);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001767 }
1768
Roland Levillainebea3d22016-04-12 15:42:57 +01001769 __ Bind(&conditions_on_positions_validated);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001770
1771 if (!optimizations.GetSourceIsNotNull()) {
1772 // Bail out if the source is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01001773 __ CompareAndBranchIfZero(src, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001774 }
1775
1776 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
1777 // Bail out if the destination is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01001778 __ CompareAndBranchIfZero(dest, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001779 }
1780
1781 // If the length is negative, bail out.
1782 // We have already checked in the LocationsBuilder for the constant case.
1783 if (!length.IsConstant() &&
1784 !optimizations.GetCountIsSourceLength() &&
1785 !optimizations.GetCountIsDestinationLength()) {
1786 __ cmp(length.AsRegister<Register>(), ShifterOperand(0));
Roland Levillain0b671c02016-08-19 12:02:34 +01001787 __ b(intrinsic_slow_path->GetEntryLabel(), LT);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001788 }
1789
1790 // Validity checks: source.
1791 CheckPosition(assembler,
1792 src_pos,
1793 src,
1794 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01001795 intrinsic_slow_path,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001796 temp1,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001797 optimizations.GetCountIsSourceLength());
1798
1799 // Validity checks: dest.
1800 CheckPosition(assembler,
1801 dest_pos,
1802 dest,
1803 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01001804 intrinsic_slow_path,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001805 temp1,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001806 optimizations.GetCountIsDestinationLength());
1807
1808 if (!optimizations.GetDoesNotNeedTypeCheck()) {
1809 // Check whether all elements of the source array are assignable to the component
1810 // type of the destination array. We do two checks: the classes are the same,
1811 // or the destination is Object[]. If none of these checks succeed, we go to the
1812 // slow path.
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001813
Roland Levillain0b671c02016-08-19 12:02:34 +01001814 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1815 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1816 // /* HeapReference<Class> */ temp1 = src->klass_
1817 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1818 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
1819 // Bail out if the source is not a non primitive array.
1820 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1821 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1822 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
1823 __ CompareAndBranchIfZero(temp1, intrinsic_slow_path->GetEntryLabel());
1824 // If heap poisoning is enabled, `temp1` has been unpoisoned
1825 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1826 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
1827 __ LoadFromOffset(kLoadUnsignedHalfword, temp1, temp1, primitive_offset);
1828 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1829 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001830 }
Roland Levillain0b671c02016-08-19 12:02:34 +01001831
1832 // /* HeapReference<Class> */ temp1 = dest->klass_
1833 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1834 invoke, temp1_loc, dest, class_offset, temp2_loc, /* needs_null_check */ false);
1835
1836 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
1837 // Bail out if the destination is not a non primitive array.
1838 //
1839 // Register `temp1` is not trashed by the read barrier emitted
1840 // by GenerateFieldLoadWithBakerReadBarrier below, as that
1841 // method produces a call to a ReadBarrierMarkRegX entry point,
1842 // which saves all potentially live registers, including
1843 // temporaries such a `temp1`.
1844 // /* HeapReference<Class> */ temp2 = temp1->component_type_
1845 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1846 invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
1847 __ CompareAndBranchIfZero(temp2, intrinsic_slow_path->GetEntryLabel());
1848 // If heap poisoning is enabled, `temp2` has been unpoisoned
1849 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1850 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
1851 __ LoadFromOffset(kLoadUnsignedHalfword, temp2, temp2, primitive_offset);
1852 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1853 __ CompareAndBranchIfNonZero(temp2, intrinsic_slow_path->GetEntryLabel());
1854 }
1855
1856 // For the same reason given earlier, `temp1` is not trashed by the
1857 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
1858 // /* HeapReference<Class> */ temp2 = src->klass_
1859 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1860 invoke, temp2_loc, src, class_offset, temp3_loc, /* needs_null_check */ false);
1861 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
1862 __ cmp(temp1, ShifterOperand(temp2));
1863
1864 if (optimizations.GetDestinationIsTypedObjectArray()) {
1865 Label do_copy;
1866 __ b(&do_copy, EQ);
1867 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1868 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1869 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
1870 // /* HeapReference<Class> */ temp1 = temp1->super_class_
1871 // We do not need to emit a read barrier for the following
1872 // heap reference load, as `temp1` is only used in a
1873 // comparison with null below, and this reference is not
1874 // kept afterwards.
1875 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
1876 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
1877 __ Bind(&do_copy);
1878 } else {
1879 __ b(intrinsic_slow_path->GetEntryLabel(), NE);
1880 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001881 } else {
Roland Levillain0b671c02016-08-19 12:02:34 +01001882 // Non read barrier code.
1883
1884 // /* HeapReference<Class> */ temp1 = dest->klass_
1885 __ LoadFromOffset(kLoadWord, temp1, dest, class_offset);
1886 // /* HeapReference<Class> */ temp2 = src->klass_
1887 __ LoadFromOffset(kLoadWord, temp2, src, class_offset);
1888 bool did_unpoison = false;
1889 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
1890 !optimizations.GetSourceIsNonPrimitiveArray()) {
1891 // One or two of the references need to be unpoisoned. Unpoison them
1892 // both to make the identity check valid.
1893 __ MaybeUnpoisonHeapReference(temp1);
1894 __ MaybeUnpoisonHeapReference(temp2);
1895 did_unpoison = true;
1896 }
1897
1898 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
1899 // Bail out if the destination is not a non primitive array.
1900 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1901 __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
1902 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1903 __ MaybeUnpoisonHeapReference(temp3);
1904 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
1905 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1906 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1907 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
1908 }
1909
1910 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1911 // Bail out if the source is not a non primitive array.
1912 // /* HeapReference<Class> */ temp3 = temp2->component_type_
1913 __ LoadFromOffset(kLoadWord, temp3, temp2, component_offset);
1914 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1915 __ MaybeUnpoisonHeapReference(temp3);
1916 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
1917 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1918 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1919 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
1920 }
1921
1922 __ cmp(temp1, ShifterOperand(temp2));
1923
1924 if (optimizations.GetDestinationIsTypedObjectArray()) {
1925 Label do_copy;
1926 __ b(&do_copy, EQ);
1927 if (!did_unpoison) {
1928 __ MaybeUnpoisonHeapReference(temp1);
1929 }
1930 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1931 __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
1932 __ MaybeUnpoisonHeapReference(temp1);
1933 // /* HeapReference<Class> */ temp1 = temp1->super_class_
1934 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
1935 // No need to unpoison the result, we're comparing against null.
1936 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
1937 __ Bind(&do_copy);
1938 } else {
1939 __ b(intrinsic_slow_path->GetEntryLabel(), NE);
1940 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001941 }
1942 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1943 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
1944 // Bail out if the source is not a non primitive array.
Roland Levillain0b671c02016-08-19 12:02:34 +01001945 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1946 // /* HeapReference<Class> */ temp1 = src->klass_
1947 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1948 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
1949 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1950 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1951 invoke, temp3_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
1952 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1953 // If heap poisoning is enabled, `temp3` has been unpoisoned
1954 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1955 } else {
1956 // /* HeapReference<Class> */ temp1 = src->klass_
1957 __ LoadFromOffset(kLoadWord, temp1, src, class_offset);
1958 __ MaybeUnpoisonHeapReference(temp1);
1959 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1960 __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
1961 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1962 __ MaybeUnpoisonHeapReference(temp3);
1963 }
1964 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001965 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1966 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Roland Levillain0b671c02016-08-19 12:02:34 +01001967 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001968 }
1969
Roland Levillain9cc0ea82017-03-16 11:25:59 +00001970 const Primitive::Type type = Primitive::kPrimNot;
1971 const int32_t element_size = Primitive::ComponentSize(type);
Roland Levillain0b671c02016-08-19 12:02:34 +01001972
1973 // Compute the base source address in `temp1`.
Roland Levillain9cc0ea82017-03-16 11:25:59 +00001974 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
Roland Levillain0b671c02016-08-19 12:02:34 +01001975 // Compute the end source address in `temp3`.
Roland Levillain9cc0ea82017-03-16 11:25:59 +00001976 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
1977 // The base destination address is computed later, as `temp2` is
1978 // used for intermediate computations.
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001979
Roland Levillain0b671c02016-08-19 12:02:34 +01001980 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Roland Levillainba650a42017-03-06 13:52:32 +00001981 // TODO: Also convert this intrinsic to the IsGcMarking strategy?
1982
Roland Levillain0b671c02016-08-19 12:02:34 +01001983 // SystemArrayCopy implementation for Baker read barriers (see
1984 // also CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier):
1985 //
1986 // if (src_ptr != end_ptr) {
1987 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
1988 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
Hiroshi Yamauchi12b58b22016-11-01 11:55:29 -07001989 // bool is_gray = (rb_state == ReadBarrier::GrayState());
Roland Levillain0b671c02016-08-19 12:02:34 +01001990 // if (is_gray) {
1991 // // Slow-path copy.
1992 // do {
1993 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
1994 // } while (src_ptr != end_ptr)
1995 // } else {
1996 // // Fast-path copy.
1997 // do {
1998 // *dest_ptr++ = *src_ptr++;
1999 // } while (src_ptr != end_ptr)
2000 // }
2001 // }
2002
2003 Label loop, done;
2004
2005 // Don't enter copy loop if `length == 0`.
2006 __ cmp(temp1, ShifterOperand(temp3));
2007 __ b(&done, EQ);
2008
2009 // /* int32_t */ monitor = src->monitor_
2010 __ LoadFromOffset(kLoadWord, temp2, src, monitor_offset);
2011 // /* LockWord */ lock_word = LockWord(monitor)
2012 static_assert(sizeof(LockWord) == sizeof(int32_t),
2013 "art::LockWord and int32_t have different sizes.");
2014
2015 // Introduce a dependency on the lock_word including the rb_state,
2016 // which shall prevent load-load reordering without using
2017 // a memory barrier (which would be more expensive).
2018 // `src` is unchanged by this operation, but its value now depends
2019 // on `temp2`.
2020 __ add(src, src, ShifterOperand(temp2, LSR, 32));
2021
2022 // Slow path used to copy array when `src` is gray.
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002023 // Note that the base destination address is computed in `temp2`
2024 // by the slow path code.
Roland Levillain0b671c02016-08-19 12:02:34 +01002025 SlowPathCode* read_barrier_slow_path =
2026 new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathARM(invoke);
2027 codegen_->AddSlowPath(read_barrier_slow_path);
2028
2029 // Given the numeric representation, it's enough to check the low bit of the
2030 // rb_state. We do that by shifting the bit out of the lock word with LSRS
2031 // which can be a 16-bit instruction unlike the TST immediate.
Hiroshi Yamauchi12b58b22016-11-01 11:55:29 -07002032 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
2033 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
Roland Levillain0b671c02016-08-19 12:02:34 +01002034 __ Lsrs(temp2, temp2, LockWord::kReadBarrierStateShift + 1);
2035 // Carry flag is the last bit shifted out by LSRS.
2036 __ b(read_barrier_slow_path->GetEntryLabel(), CS);
2037
2038 // Fast-path copy.
Roland Levillain0b671c02016-08-19 12:02:34 +01002039 // Compute the base destination address in `temp2`.
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002040 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
Roland Levillain0b671c02016-08-19 12:02:34 +01002041 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2042 // poison/unpoison.
2043 __ Bind(&loop);
2044 __ ldr(IP, Address(temp1, element_size, Address::PostIndex));
2045 __ str(IP, Address(temp2, element_size, Address::PostIndex));
2046 __ cmp(temp1, ShifterOperand(temp3));
2047 __ b(&loop, NE);
2048
2049 __ Bind(read_barrier_slow_path->GetExitLabel());
2050 __ Bind(&done);
2051 } else {
2052 // Non read barrier code.
Roland Levillain0b671c02016-08-19 12:02:34 +01002053 // Compute the base destination address in `temp2`.
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002054 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
Roland Levillain0b671c02016-08-19 12:02:34 +01002055 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2056 // poison/unpoison.
2057 Label loop, done;
2058 __ cmp(temp1, ShifterOperand(temp3));
2059 __ b(&done, EQ);
2060 __ Bind(&loop);
2061 __ ldr(IP, Address(temp1, element_size, Address::PostIndex));
2062 __ str(IP, Address(temp2, element_size, Address::PostIndex));
2063 __ cmp(temp1, ShifterOperand(temp3));
2064 __ b(&loop, NE);
2065 __ Bind(&done);
2066 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01002067
2068 // We only need one card marking on the destination array.
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002069 codegen_->MarkGCCard(temp1, temp2, dest, Register(kNoRegister), /* value_can_be_null */ false);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01002070
Roland Levillain0b671c02016-08-19 12:02:34 +01002071 __ Bind(intrinsic_slow_path->GetExitLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01002072}
2073
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002074static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
2075 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2076 // the code generator. Furthermore, the register allocator creates fixed live intervals
2077 // for all caller-saved registers because we are doing a function call. As a result, if
2078 // the input and output locations are unallocated, the register allocator runs out of
2079 // registers and fails; however, a debuggable graph is not the common case.
2080 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2081 return;
2082 }
2083
2084 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2085 DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
2086 DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
2087
2088 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01002089 LocationSummary::kCallOnMainOnly,
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002090 kIntrinsified);
2091 const InvokeRuntimeCallingConvention calling_convention;
2092
2093 locations->SetInAt(0, Location::RequiresFpuRegister());
2094 locations->SetOut(Location::RequiresFpuRegister());
2095 // Native code uses the soft float ABI.
2096 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2097 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2098}
2099
2100static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
2101 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2102 // the code generator. Furthermore, the register allocator creates fixed live intervals
2103 // for all caller-saved registers because we are doing a function call. As a result, if
2104 // the input and output locations are unallocated, the register allocator runs out of
2105 // registers and fails; however, a debuggable graph is not the common case.
2106 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2107 return;
2108 }
2109
2110 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2111 DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
2112 DCHECK_EQ(invoke->InputAt(1)->GetType(), Primitive::kPrimDouble);
2113 DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
2114
2115 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01002116 LocationSummary::kCallOnMainOnly,
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002117 kIntrinsified);
2118 const InvokeRuntimeCallingConvention calling_convention;
2119
2120 locations->SetInAt(0, Location::RequiresFpuRegister());
2121 locations->SetInAt(1, Location::RequiresFpuRegister());
2122 locations->SetOut(Location::RequiresFpuRegister());
2123 // Native code uses the soft float ABI.
2124 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2125 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2126 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
2127 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
2128}
2129
2130static void GenFPToFPCall(HInvoke* invoke,
2131 ArmAssembler* assembler,
2132 CodeGeneratorARM* codegen,
2133 QuickEntrypointEnum entry) {
2134 LocationSummary* const locations = invoke->GetLocations();
2135 const InvokeRuntimeCallingConvention calling_convention;
2136
2137 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2138 DCHECK(locations->WillCall() && locations->Intrinsified());
2139 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
2140 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
2141
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002142 // Native code uses the soft float ABI.
2143 __ vmovrrd(calling_convention.GetRegisterAt(0),
2144 calling_convention.GetRegisterAt(1),
2145 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01002146 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002147 __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
2148 calling_convention.GetRegisterAt(0),
2149 calling_convention.GetRegisterAt(1));
2150}
2151
2152static void GenFPFPToFPCall(HInvoke* invoke,
2153 ArmAssembler* assembler,
2154 CodeGeneratorARM* codegen,
2155 QuickEntrypointEnum entry) {
2156 LocationSummary* const locations = invoke->GetLocations();
2157 const InvokeRuntimeCallingConvention calling_convention;
2158
2159 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2160 DCHECK(locations->WillCall() && locations->Intrinsified());
2161 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
2162 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
2163 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(2)));
2164 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(3)));
2165
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002166 // Native code uses the soft float ABI.
2167 __ vmovrrd(calling_convention.GetRegisterAt(0),
2168 calling_convention.GetRegisterAt(1),
2169 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
2170 __ vmovrrd(calling_convention.GetRegisterAt(2),
2171 calling_convention.GetRegisterAt(3),
2172 FromLowSToD(locations->InAt(1).AsFpuRegisterPairLow<SRegister>()));
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01002173 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002174 __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
2175 calling_convention.GetRegisterAt(0),
2176 calling_convention.GetRegisterAt(1));
2177}
2178
2179void IntrinsicLocationsBuilderARM::VisitMathCos(HInvoke* invoke) {
2180 CreateFPToFPCallLocations(arena_, invoke);
2181}
2182
2183void IntrinsicCodeGeneratorARM::VisitMathCos(HInvoke* invoke) {
2184 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCos);
2185}
2186
2187void IntrinsicLocationsBuilderARM::VisitMathSin(HInvoke* invoke) {
2188 CreateFPToFPCallLocations(arena_, invoke);
2189}
2190
2191void IntrinsicCodeGeneratorARM::VisitMathSin(HInvoke* invoke) {
2192 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSin);
2193}
2194
2195void IntrinsicLocationsBuilderARM::VisitMathAcos(HInvoke* invoke) {
2196 CreateFPToFPCallLocations(arena_, invoke);
2197}
2198
2199void IntrinsicCodeGeneratorARM::VisitMathAcos(HInvoke* invoke) {
2200 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAcos);
2201}
2202
2203void IntrinsicLocationsBuilderARM::VisitMathAsin(HInvoke* invoke) {
2204 CreateFPToFPCallLocations(arena_, invoke);
2205}
2206
2207void IntrinsicCodeGeneratorARM::VisitMathAsin(HInvoke* invoke) {
2208 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAsin);
2209}
2210
2211void IntrinsicLocationsBuilderARM::VisitMathAtan(HInvoke* invoke) {
2212 CreateFPToFPCallLocations(arena_, invoke);
2213}
2214
2215void IntrinsicCodeGeneratorARM::VisitMathAtan(HInvoke* invoke) {
2216 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan);
2217}
2218
2219void IntrinsicLocationsBuilderARM::VisitMathCbrt(HInvoke* invoke) {
2220 CreateFPToFPCallLocations(arena_, invoke);
2221}
2222
2223void IntrinsicCodeGeneratorARM::VisitMathCbrt(HInvoke* invoke) {
2224 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCbrt);
2225}
2226
2227void IntrinsicLocationsBuilderARM::VisitMathCosh(HInvoke* invoke) {
2228 CreateFPToFPCallLocations(arena_, invoke);
2229}
2230
2231void IntrinsicCodeGeneratorARM::VisitMathCosh(HInvoke* invoke) {
2232 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCosh);
2233}
2234
2235void IntrinsicLocationsBuilderARM::VisitMathExp(HInvoke* invoke) {
2236 CreateFPToFPCallLocations(arena_, invoke);
2237}
2238
2239void IntrinsicCodeGeneratorARM::VisitMathExp(HInvoke* invoke) {
2240 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExp);
2241}
2242
2243void IntrinsicLocationsBuilderARM::VisitMathExpm1(HInvoke* invoke) {
2244 CreateFPToFPCallLocations(arena_, invoke);
2245}
2246
2247void IntrinsicCodeGeneratorARM::VisitMathExpm1(HInvoke* invoke) {
2248 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExpm1);
2249}
2250
2251void IntrinsicLocationsBuilderARM::VisitMathLog(HInvoke* invoke) {
2252 CreateFPToFPCallLocations(arena_, invoke);
2253}
2254
2255void IntrinsicCodeGeneratorARM::VisitMathLog(HInvoke* invoke) {
2256 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog);
2257}
2258
2259void IntrinsicLocationsBuilderARM::VisitMathLog10(HInvoke* invoke) {
2260 CreateFPToFPCallLocations(arena_, invoke);
2261}
2262
2263void IntrinsicCodeGeneratorARM::VisitMathLog10(HInvoke* invoke) {
2264 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog10);
2265}
2266
2267void IntrinsicLocationsBuilderARM::VisitMathSinh(HInvoke* invoke) {
2268 CreateFPToFPCallLocations(arena_, invoke);
2269}
2270
2271void IntrinsicCodeGeneratorARM::VisitMathSinh(HInvoke* invoke) {
2272 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSinh);
2273}
2274
2275void IntrinsicLocationsBuilderARM::VisitMathTan(HInvoke* invoke) {
2276 CreateFPToFPCallLocations(arena_, invoke);
2277}
2278
2279void IntrinsicCodeGeneratorARM::VisitMathTan(HInvoke* invoke) {
2280 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTan);
2281}
2282
2283void IntrinsicLocationsBuilderARM::VisitMathTanh(HInvoke* invoke) {
2284 CreateFPToFPCallLocations(arena_, invoke);
2285}
2286
2287void IntrinsicCodeGeneratorARM::VisitMathTanh(HInvoke* invoke) {
2288 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTanh);
2289}
2290
2291void IntrinsicLocationsBuilderARM::VisitMathAtan2(HInvoke* invoke) {
2292 CreateFPFPToFPCallLocations(arena_, invoke);
2293}
2294
2295void IntrinsicCodeGeneratorARM::VisitMathAtan2(HInvoke* invoke) {
2296 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan2);
2297}
2298
2299void IntrinsicLocationsBuilderARM::VisitMathHypot(HInvoke* invoke) {
2300 CreateFPFPToFPCallLocations(arena_, invoke);
2301}
2302
2303void IntrinsicCodeGeneratorARM::VisitMathHypot(HInvoke* invoke) {
2304 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickHypot);
2305}
2306
2307void IntrinsicLocationsBuilderARM::VisitMathNextAfter(HInvoke* invoke) {
2308 CreateFPFPToFPCallLocations(arena_, invoke);
2309}
2310
2311void IntrinsicCodeGeneratorARM::VisitMathNextAfter(HInvoke* invoke) {
2312 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter);
2313}
2314
Artem Serovc257da72016-02-02 13:49:43 +00002315void IntrinsicLocationsBuilderARM::VisitIntegerReverse(HInvoke* invoke) {
2316 CreateIntToIntLocations(arena_, invoke);
2317}
2318
2319void IntrinsicCodeGeneratorARM::VisitIntegerReverse(HInvoke* invoke) {
2320 ArmAssembler* assembler = GetAssembler();
2321 LocationSummary* locations = invoke->GetLocations();
2322
2323 Register out = locations->Out().AsRegister<Register>();
2324 Register in = locations->InAt(0).AsRegister<Register>();
2325
2326 __ rbit(out, in);
2327}
2328
2329void IntrinsicLocationsBuilderARM::VisitLongReverse(HInvoke* invoke) {
2330 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2331 LocationSummary::kNoCall,
2332 kIntrinsified);
2333 locations->SetInAt(0, Location::RequiresRegister());
2334 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2335}
2336
2337void IntrinsicCodeGeneratorARM::VisitLongReverse(HInvoke* invoke) {
2338 ArmAssembler* assembler = GetAssembler();
2339 LocationSummary* locations = invoke->GetLocations();
2340
2341 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
2342 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
2343 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
2344 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
2345
2346 __ rbit(out_reg_lo, in_reg_hi);
2347 __ rbit(out_reg_hi, in_reg_lo);
2348}
2349
2350void IntrinsicLocationsBuilderARM::VisitIntegerReverseBytes(HInvoke* invoke) {
2351 CreateIntToIntLocations(arena_, invoke);
2352}
2353
2354void IntrinsicCodeGeneratorARM::VisitIntegerReverseBytes(HInvoke* invoke) {
2355 ArmAssembler* assembler = GetAssembler();
2356 LocationSummary* locations = invoke->GetLocations();
2357
2358 Register out = locations->Out().AsRegister<Register>();
2359 Register in = locations->InAt(0).AsRegister<Register>();
2360
2361 __ rev(out, in);
2362}
2363
2364void IntrinsicLocationsBuilderARM::VisitLongReverseBytes(HInvoke* invoke) {
2365 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2366 LocationSummary::kNoCall,
2367 kIntrinsified);
2368 locations->SetInAt(0, Location::RequiresRegister());
2369 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2370}
2371
2372void IntrinsicCodeGeneratorARM::VisitLongReverseBytes(HInvoke* invoke) {
2373 ArmAssembler* assembler = GetAssembler();
2374 LocationSummary* locations = invoke->GetLocations();
2375
2376 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
2377 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
2378 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
2379 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
2380
2381 __ rev(out_reg_lo, in_reg_hi);
2382 __ rev(out_reg_hi, in_reg_lo);
2383}
2384
2385void IntrinsicLocationsBuilderARM::VisitShortReverseBytes(HInvoke* invoke) {
2386 CreateIntToIntLocations(arena_, invoke);
2387}
2388
2389void IntrinsicCodeGeneratorARM::VisitShortReverseBytes(HInvoke* invoke) {
2390 ArmAssembler* assembler = GetAssembler();
2391 LocationSummary* locations = invoke->GetLocations();
2392
2393 Register out = locations->Out().AsRegister<Register>();
2394 Register in = locations->InAt(0).AsRegister<Register>();
2395
2396 __ revsh(out, in);
2397}
2398
xueliang.zhongf1073c82016-07-05 15:28:19 +01002399static void GenBitCount(HInvoke* instr, Primitive::Type type, ArmAssembler* assembler) {
2400 DCHECK(Primitive::IsIntOrLongType(type)) << type;
2401 DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
2402 DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
2403
2404 bool is_long = type == Primitive::kPrimLong;
2405 LocationSummary* locations = instr->GetLocations();
2406 Location in = locations->InAt(0);
2407 Register src_0 = is_long ? in.AsRegisterPairLow<Register>() : in.AsRegister<Register>();
2408 Register src_1 = is_long ? in.AsRegisterPairHigh<Register>() : src_0;
2409 SRegister tmp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
2410 DRegister tmp_d = FromLowSToD(tmp_s);
2411 Register out_r = locations->Out().AsRegister<Register>();
2412
2413 // Move data from core register(s) to temp D-reg for bit count calculation, then move back.
2414 // According to Cortex A57 and A72 optimization guides, compared to transferring to full D-reg,
2415 // transferring data from core reg to upper or lower half of vfp D-reg requires extra latency,
2416 // That's why for integer bit count, we use 'vmov d0, r0, r0' instead of 'vmov d0[0], r0'.
2417 __ vmovdrr(tmp_d, src_1, src_0); // Temp DReg |--src_1|--src_0|
2418 __ vcntd(tmp_d, tmp_d); // Temp DReg |c|c|c|c|c|c|c|c|
2419 __ vpaddld(tmp_d, tmp_d, 8, /* is_unsigned */ true); // Temp DReg |--c|--c|--c|--c|
2420 __ vpaddld(tmp_d, tmp_d, 16, /* is_unsigned */ true); // Temp DReg |------c|------c|
2421 if (is_long) {
2422 __ vpaddld(tmp_d, tmp_d, 32, /* is_unsigned */ true); // Temp DReg |--------------c|
2423 }
2424 __ vmovrs(out_r, tmp_s);
2425}
2426
2427void IntrinsicLocationsBuilderARM::VisitIntegerBitCount(HInvoke* invoke) {
2428 CreateIntToIntLocations(arena_, invoke);
2429 invoke->GetLocations()->AddTemp(Location::RequiresFpuRegister());
2430}
2431
2432void IntrinsicCodeGeneratorARM::VisitIntegerBitCount(HInvoke* invoke) {
2433 GenBitCount(invoke, Primitive::kPrimInt, GetAssembler());
2434}
2435
2436void IntrinsicLocationsBuilderARM::VisitLongBitCount(HInvoke* invoke) {
2437 VisitIntegerBitCount(invoke);
2438}
2439
2440void IntrinsicCodeGeneratorARM::VisitLongBitCount(HInvoke* invoke) {
2441 GenBitCount(invoke, Primitive::kPrimLong, GetAssembler());
2442}
2443
Tim Zhang25abd6c2016-01-19 23:39:24 +08002444void IntrinsicLocationsBuilderARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2445 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2446 LocationSummary::kNoCall,
2447 kIntrinsified);
2448 locations->SetInAt(0, Location::RequiresRegister());
2449 locations->SetInAt(1, Location::RequiresRegister());
2450 locations->SetInAt(2, Location::RequiresRegister());
2451 locations->SetInAt(3, Location::RequiresRegister());
2452 locations->SetInAt(4, Location::RequiresRegister());
2453
Scott Wakeling3fdab772016-04-25 11:32:37 +01002454 // Temporary registers to store lengths of strings and for calculations.
Tim Zhang25abd6c2016-01-19 23:39:24 +08002455 locations->AddTemp(Location::RequiresRegister());
2456 locations->AddTemp(Location::RequiresRegister());
2457 locations->AddTemp(Location::RequiresRegister());
2458}
2459
2460void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2461 ArmAssembler* assembler = GetAssembler();
2462 LocationSummary* locations = invoke->GetLocations();
2463
2464 // Check assumption that sizeof(Char) is 2 (used in scaling below).
2465 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2466 DCHECK_EQ(char_size, 2u);
2467
2468 // Location of data in char array buffer.
2469 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2470
2471 // Location of char array data in string.
2472 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2473
2474 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2475 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2476 Register srcObj = locations->InAt(0).AsRegister<Register>();
2477 Register srcBegin = locations->InAt(1).AsRegister<Register>();
2478 Register srcEnd = locations->InAt(2).AsRegister<Register>();
2479 Register dstObj = locations->InAt(3).AsRegister<Register>();
2480 Register dstBegin = locations->InAt(4).AsRegister<Register>();
2481
Scott Wakeling3fdab772016-04-25 11:32:37 +01002482 Register num_chr = locations->GetTemp(0).AsRegister<Register>();
2483 Register src_ptr = locations->GetTemp(1).AsRegister<Register>();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002484 Register dst_ptr = locations->GetTemp(2).AsRegister<Register>();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002485
jessicahandojo05765752016-09-09 19:01:32 -07002486 Label done, compressed_string_loop;
Tim Zhang25abd6c2016-01-19 23:39:24 +08002487 // dst to be copied.
2488 __ add(dst_ptr, dstObj, ShifterOperand(data_offset));
2489 __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1));
2490
Scott Wakeling3fdab772016-04-25 11:32:37 +01002491 __ subs(num_chr, srcEnd, ShifterOperand(srcBegin));
Scott Wakeling3fdab772016-04-25 11:32:37 +01002492 // Early out for valid zero-length retrievals.
Tim Zhang25abd6c2016-01-19 23:39:24 +08002493 __ b(&done, EQ);
Scott Wakeling3fdab772016-04-25 11:32:37 +01002494
jessicahandojo05765752016-09-09 19:01:32 -07002495 // src range to copy.
2496 __ add(src_ptr, srcObj, ShifterOperand(value_offset));
2497 Label compressed_string_preloop;
2498 if (mirror::kUseStringCompression) {
2499 // Location of count in string.
2500 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2501 // String's length.
2502 __ ldr(IP, Address(srcObj, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002503 __ tst(IP, ShifterOperand(1));
2504 __ b(&compressed_string_preloop, EQ);
jessicahandojo05765752016-09-09 19:01:32 -07002505 }
2506 __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1));
2507
2508 // Do the copy.
2509 Label loop, remainder;
2510
Scott Wakeling3fdab772016-04-25 11:32:37 +01002511 // Save repairing the value of num_chr on the < 4 character path.
2512 __ subs(IP, num_chr, ShifterOperand(4));
2513 __ b(&remainder, LT);
2514
2515 // Keep the result of the earlier subs, we are going to fetch at least 4 characters.
2516 __ mov(num_chr, ShifterOperand(IP));
2517
2518 // Main loop used for longer fetches loads and stores 4x16-bit characters at a time.
2519 // (LDRD/STRD fault on unaligned addresses and it's not worth inlining extra code
2520 // to rectify these everywhere this intrinsic applies.)
2521 __ Bind(&loop);
2522 __ ldr(IP, Address(src_ptr, char_size * 2));
2523 __ subs(num_chr, num_chr, ShifterOperand(4));
2524 __ str(IP, Address(dst_ptr, char_size * 2));
2525 __ ldr(IP, Address(src_ptr, char_size * 4, Address::PostIndex));
2526 __ str(IP, Address(dst_ptr, char_size * 4, Address::PostIndex));
2527 __ b(&loop, GE);
2528
2529 __ adds(num_chr, num_chr, ShifterOperand(4));
2530 __ b(&done, EQ);
2531
2532 // Main loop for < 4 character case and remainder handling. Loads and stores one
2533 // 16-bit Java character at a time.
2534 __ Bind(&remainder);
2535 __ ldrh(IP, Address(src_ptr, char_size, Address::PostIndex));
2536 __ subs(num_chr, num_chr, ShifterOperand(1));
2537 __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex));
2538 __ b(&remainder, GT);
jessicahandojo05765752016-09-09 19:01:32 -07002539
2540 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002541 __ b(&done);
2542
jessicahandojo05765752016-09-09 19:01:32 -07002543 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
2544 DCHECK_EQ(c_char_size, 1u);
2545 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2546 __ Bind(&compressed_string_preloop);
2547 __ add(src_ptr, src_ptr, ShifterOperand(srcBegin));
2548 __ Bind(&compressed_string_loop);
2549 __ ldrb(IP, Address(src_ptr, c_char_size, Address::PostIndex));
2550 __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex));
2551 __ subs(num_chr, num_chr, ShifterOperand(1));
2552 __ b(&compressed_string_loop, GT);
2553 }
Scott Wakeling3fdab772016-04-25 11:32:37 +01002554
Tim Zhang25abd6c2016-01-19 23:39:24 +08002555 __ Bind(&done);
2556}
2557
Anton Kirilova3ffea22016-04-07 17:02:37 +01002558void IntrinsicLocationsBuilderARM::VisitFloatIsInfinite(HInvoke* invoke) {
2559 CreateFPToIntLocations(arena_, invoke);
2560}
2561
2562void IntrinsicCodeGeneratorARM::VisitFloatIsInfinite(HInvoke* invoke) {
2563 ArmAssembler* const assembler = GetAssembler();
2564 LocationSummary* const locations = invoke->GetLocations();
2565 const Register out = locations->Out().AsRegister<Register>();
2566 // Shifting left by 1 bit makes the value encodable as an immediate operand;
2567 // we don't care about the sign bit anyway.
2568 constexpr uint32_t infinity = kPositiveInfinityFloat << 1U;
2569
2570 __ vmovrs(out, locations->InAt(0).AsFpuRegister<SRegister>());
2571 // We don't care about the sign bit, so shift left.
2572 __ Lsl(out, out, 1);
2573 __ eor(out, out, ShifterOperand(infinity));
2574 // If the result is 0, then it has 32 leading zeros, and less than that otherwise.
2575 __ clz(out, out);
2576 // Any number less than 32 logically shifted right by 5 bits results in 0;
2577 // the same operation on 32 yields 1.
2578 __ Lsr(out, out, 5);
2579}
2580
2581void IntrinsicLocationsBuilderARM::VisitDoubleIsInfinite(HInvoke* invoke) {
2582 CreateFPToIntLocations(arena_, invoke);
2583}
2584
2585void IntrinsicCodeGeneratorARM::VisitDoubleIsInfinite(HInvoke* invoke) {
2586 ArmAssembler* const assembler = GetAssembler();
2587 LocationSummary* const locations = invoke->GetLocations();
2588 const Register out = locations->Out().AsRegister<Register>();
2589 // The highest 32 bits of double precision positive infinity separated into
2590 // two constants encodable as immediate operands.
2591 constexpr uint32_t infinity_high = 0x7f000000U;
2592 constexpr uint32_t infinity_high2 = 0x00f00000U;
2593
2594 static_assert((infinity_high | infinity_high2) == static_cast<uint32_t>(kPositiveInfinityDouble >> 32U),
2595 "The constants do not add up to the high 32 bits of double precision positive infinity.");
2596 __ vmovrrd(IP, out, FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
2597 __ eor(out, out, ShifterOperand(infinity_high));
2598 __ eor(out, out, ShifterOperand(infinity_high2));
2599 // We don't care about the sign bit, so shift left.
2600 __ orr(out, IP, ShifterOperand(out, LSL, 1));
2601 // If the result is 0, then it has 32 leading zeros, and less than that otherwise.
2602 __ clz(out, out);
2603 // Any number less than 32 logically shifted right by 5 bits results in 0;
2604 // the same operation on 32 yields 1.
2605 __ Lsr(out, out, 5);
2606}
2607
TatWai Chongd8c052a2016-11-02 16:12:48 +08002608void IntrinsicLocationsBuilderARM::VisitReferenceGetReferent(HInvoke* invoke) {
2609 if (kEmitCompilerReadBarrier) {
2610 // Do not intrinsify this call with the read barrier configuration.
2611 return;
2612 }
2613 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2614 LocationSummary::kCallOnSlowPath,
2615 kIntrinsified);
2616 locations->SetInAt(0, Location::RequiresRegister());
2617 locations->SetOut(Location::SameAsFirstInput());
2618 locations->AddTemp(Location::RequiresRegister());
2619}
2620
2621void IntrinsicCodeGeneratorARM::VisitReferenceGetReferent(HInvoke* invoke) {
2622 DCHECK(!kEmitCompilerReadBarrier);
2623 ArmAssembler* const assembler = GetAssembler();
2624 LocationSummary* locations = invoke->GetLocations();
2625
2626 Register obj = locations->InAt(0).AsRegister<Register>();
2627 Register out = locations->Out().AsRegister<Register>();
2628
2629 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
2630 codegen_->AddSlowPath(slow_path);
2631
2632 // Load ArtMethod first.
2633 HInvokeStaticOrDirect* invoke_direct = invoke->AsInvokeStaticOrDirect();
2634 DCHECK(invoke_direct != nullptr);
2635 Register temp = codegen_->GenerateCalleeMethodStaticOrDirectCall(
2636 invoke_direct, locations->GetTemp(0)).AsRegister<Register>();
2637
2638 // Now get declaring class.
2639 __ ldr(temp, Address(temp, ArtMethod::DeclaringClassOffset().Int32Value()));
2640
2641 uint32_t slow_path_flag_offset = codegen_->GetReferenceSlowFlagOffset();
2642 uint32_t disable_flag_offset = codegen_->GetReferenceDisableFlagOffset();
2643 DCHECK_NE(slow_path_flag_offset, 0u);
2644 DCHECK_NE(disable_flag_offset, 0u);
2645 DCHECK_NE(slow_path_flag_offset, disable_flag_offset);
2646
2647 // Check static flags that prevent using intrinsic.
2648 __ ldr(IP, Address(temp, disable_flag_offset));
2649 __ ldr(temp, Address(temp, slow_path_flag_offset));
2650 __ orr(IP, IP, ShifterOperand(temp));
2651 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel());
2652
2653 // Fast path.
2654 __ ldr(out, Address(obj, mirror::Reference::ReferentOffset().Int32Value()));
2655 codegen_->MaybeRecordImplicitNullCheck(invoke);
2656 __ MaybeUnpoisonHeapReference(out);
2657 __ Bind(slow_path->GetExitLabel());
2658}
2659
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002660void IntrinsicLocationsBuilderARM::VisitIntegerValueOf(HInvoke* invoke) {
2661 InvokeRuntimeCallingConvention calling_convention;
2662 IntrinsicVisitor::ComputeIntegerValueOfLocations(
2663 invoke,
2664 codegen_,
2665 Location::RegisterLocation(R0),
2666 Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2667}
2668
2669void IntrinsicCodeGeneratorARM::VisitIntegerValueOf(HInvoke* invoke) {
2670 IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo();
2671 LocationSummary* locations = invoke->GetLocations();
2672 ArmAssembler* const assembler = GetAssembler();
2673
2674 Register out = locations->Out().AsRegister<Register>();
2675 InvokeRuntimeCallingConvention calling_convention;
2676 Register argument = calling_convention.GetRegisterAt(0);
2677 if (invoke->InputAt(0)->IsConstant()) {
2678 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
2679 if (value >= info.low && value <= info.high) {
2680 // Just embed the j.l.Integer in the code.
2681 ScopedObjectAccess soa(Thread::Current());
2682 mirror::Object* boxed = info.cache->Get(value + (-info.low));
2683 DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed));
2684 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(boxed));
2685 __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
2686 } else {
2687 // Allocate and initialize a new j.l.Integer.
2688 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
2689 // JIT object table.
2690 uint32_t address =
2691 dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2692 __ LoadLiteral(argument, codegen_->DeduplicateBootImageAddressLiteral(address));
2693 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2694 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2695 __ LoadImmediate(IP, value);
2696 __ StoreToOffset(kStoreWord, IP, out, info.value_offset);
2697 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2698 // one.
2699 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2700 }
2701 } else {
2702 Register in = locations->InAt(0).AsRegister<Register>();
2703 // Check bounds of our cache.
2704 __ AddConstant(out, in, -info.low);
2705 __ CmpConstant(out, info.high - info.low + 1);
2706 Label allocate, done;
2707 __ b(&allocate, HS);
2708 // If the value is within the bounds, load the j.l.Integer directly from the array.
2709 uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
2710 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.cache));
2711 __ LoadLiteral(IP, codegen_->DeduplicateBootImageAddressLiteral(data_offset + address));
2712 codegen_->LoadFromShiftedRegOffset(Primitive::kPrimNot, locations->Out(), IP, out);
2713 __ MaybeUnpoisonHeapReference(out);
2714 __ b(&done);
2715 __ Bind(&allocate);
2716 // Otherwise allocate and initialize a new j.l.Integer.
2717 address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2718 __ LoadLiteral(argument, codegen_->DeduplicateBootImageAddressLiteral(address));
2719 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2720 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2721 __ StoreToOffset(kStoreWord, in, out, info.value_offset);
2722 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2723 // one.
2724 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2725 __ Bind(&done);
2726 }
2727}
2728
Aart Bik2f9fcc92016-03-01 15:16:54 -08002729UNIMPLEMENTED_INTRINSIC(ARM, MathMinDoubleDouble)
2730UNIMPLEMENTED_INTRINSIC(ARM, MathMinFloatFloat)
2731UNIMPLEMENTED_INTRINSIC(ARM, MathMaxDoubleDouble)
2732UNIMPLEMENTED_INTRINSIC(ARM, MathMaxFloatFloat)
2733UNIMPLEMENTED_INTRINSIC(ARM, MathMinLongLong)
2734UNIMPLEMENTED_INTRINSIC(ARM, MathMaxLongLong)
2735UNIMPLEMENTED_INTRINSIC(ARM, MathCeil) // Could be done by changing rounding mode, maybe?
2736UNIMPLEMENTED_INTRINSIC(ARM, MathFloor) // Could be done by changing rounding mode, maybe?
2737UNIMPLEMENTED_INTRINSIC(ARM, MathRint)
2738UNIMPLEMENTED_INTRINSIC(ARM, MathRoundDouble) // Could be done by changing rounding mode, maybe?
2739UNIMPLEMENTED_INTRINSIC(ARM, MathRoundFloat) // Could be done by changing rounding mode, maybe?
2740UNIMPLEMENTED_INTRINSIC(ARM, UnsafeCASLong) // High register pressure.
2741UNIMPLEMENTED_INTRINSIC(ARM, SystemArrayCopyChar)
Aart Bik2f9fcc92016-03-01 15:16:54 -08002742UNIMPLEMENTED_INTRINSIC(ARM, IntegerHighestOneBit)
2743UNIMPLEMENTED_INTRINSIC(ARM, LongHighestOneBit)
2744UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit)
2745UNIMPLEMENTED_INTRINSIC(ARM, LongLowestOneBit)
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08002746
Aart Bikff7d89c2016-11-07 08:49:28 -08002747UNIMPLEMENTED_INTRINSIC(ARM, StringStringIndexOf);
2748UNIMPLEMENTED_INTRINSIC(ARM, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08002749UNIMPLEMENTED_INTRINSIC(ARM, StringBufferAppend);
2750UNIMPLEMENTED_INTRINSIC(ARM, StringBufferLength);
2751UNIMPLEMENTED_INTRINSIC(ARM, StringBufferToString);
2752UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderAppend);
2753UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderLength);
2754UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08002755
Aart Bik0e54c012016-03-04 12:08:31 -08002756// 1.8.
2757UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddInt)
2758UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddLong)
2759UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetInt)
2760UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetLong)
2761UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08002762
Aart Bik2f9fcc92016-03-01 15:16:54 -08002763UNREACHABLE_INTRINSICS(ARM)
Roland Levillain4d027112015-07-01 15:41:14 +01002764
2765#undef __
2766
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08002767} // namespace arm
2768} // namespace art