blob: 98b80f5d3ca2a7395b24f111b332dc43c80c549e [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 Levillain1663d162017-03-17 15:15:21 +00001970 if (length.IsConstant() && length.GetConstant()->AsIntConstant()->GetValue() == 0) {
1971 // Null constant length: not need to emit the loop code at all.
Roland Levillain0b671c02016-08-19 12:02:34 +01001972 } else {
Roland Levillain1663d162017-03-17 15:15:21 +00001973 Label done;
1974 const Primitive::Type type = Primitive::kPrimNot;
1975 const int32_t element_size = Primitive::ComponentSize(type);
1976
1977 if (length.IsRegister()) {
1978 // Don't enter the copy loop if the length is null.
1979 __ CompareAndBranchIfZero(length.AsRegister<Register>(), &done);
1980 }
1981
1982 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1983 // TODO: Also convert this intrinsic to the IsGcMarking strategy?
1984
1985 // SystemArrayCopy implementation for Baker read barriers (see
1986 // also CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier):
1987 //
1988 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
1989 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
1990 // bool is_gray = (rb_state == ReadBarrier::GrayState());
1991 // if (is_gray) {
1992 // // Slow-path copy.
1993 // do {
1994 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
1995 // } while (src_ptr != end_ptr)
1996 // } else {
1997 // // Fast-path copy.
1998 // do {
1999 // *dest_ptr++ = *src_ptr++;
2000 // } while (src_ptr != end_ptr)
2001 // }
2002
2003 // /* int32_t */ monitor = src->monitor_
2004 __ LoadFromOffset(kLoadWord, temp2, src, monitor_offset);
2005 // /* LockWord */ lock_word = LockWord(monitor)
2006 static_assert(sizeof(LockWord) == sizeof(int32_t),
2007 "art::LockWord and int32_t have different sizes.");
2008
2009 // Introduce a dependency on the lock_word including the rb_state,
2010 // which shall prevent load-load reordering without using
2011 // a memory barrier (which would be more expensive).
2012 // `src` is unchanged by this operation, but its value now depends
2013 // on `temp2`.
2014 __ add(src, src, ShifterOperand(temp2, LSR, 32));
2015
2016 // Compute the base source address in `temp1`.
2017 // Note that `temp1` (the base source address) is computed from
2018 // `src` (and `src_pos`) here, and thus honors the artificial
2019 // dependency of `src` on `temp2`.
2020 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2021 // Compute the end source address in `temp3`.
2022 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2023 // The base destination address is computed later, as `temp2` is
2024 // used for intermediate computations.
2025
2026 // Slow path used to copy array when `src` is gray.
2027 // Note that the base destination address is computed in `temp2`
2028 // by the slow path code.
2029 SlowPathCode* read_barrier_slow_path =
2030 new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathARM(invoke);
2031 codegen_->AddSlowPath(read_barrier_slow_path);
2032
2033 // Given the numeric representation, it's enough to check the low bit of the
2034 // rb_state. We do that by shifting the bit out of the lock word with LSRS
2035 // which can be a 16-bit instruction unlike the TST immediate.
2036 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
2037 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
2038 __ Lsrs(temp2, temp2, LockWord::kReadBarrierStateShift + 1);
2039 // Carry flag is the last bit shifted out by LSRS.
2040 __ b(read_barrier_slow_path->GetEntryLabel(), CS);
2041
2042 // Fast-path copy.
2043 // Compute the base destination address in `temp2`.
2044 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2045 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2046 // poison/unpoison.
2047 Label loop;
2048 __ Bind(&loop);
2049 __ ldr(IP, Address(temp1, element_size, Address::PostIndex));
2050 __ str(IP, Address(temp2, element_size, Address::PostIndex));
2051 __ cmp(temp1, ShifterOperand(temp3));
2052 __ b(&loop, NE);
2053
2054 __ Bind(read_barrier_slow_path->GetExitLabel());
2055 } else {
2056 // Non read barrier code.
2057 // Compute the base source address in `temp1`.
2058 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2059 // Compute the base destination address in `temp2`.
2060 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2061 // Compute the end source address in `temp3`.
2062 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2063 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2064 // poison/unpoison.
2065 Label loop;
2066 __ Bind(&loop);
2067 __ ldr(IP, Address(temp1, element_size, Address::PostIndex));
2068 __ str(IP, Address(temp2, element_size, Address::PostIndex));
2069 __ cmp(temp1, ShifterOperand(temp3));
2070 __ b(&loop, NE);
2071 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002072 __ Bind(&done);
2073 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01002074
2075 // We only need one card marking on the destination array.
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002076 codegen_->MarkGCCard(temp1, temp2, dest, Register(kNoRegister), /* value_can_be_null */ false);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01002077
Roland Levillain0b671c02016-08-19 12:02:34 +01002078 __ Bind(intrinsic_slow_path->GetExitLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01002079}
2080
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002081static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
2082 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2083 // the code generator. Furthermore, the register allocator creates fixed live intervals
2084 // for all caller-saved registers because we are doing a function call. As a result, if
2085 // the input and output locations are unallocated, the register allocator runs out of
2086 // registers and fails; however, a debuggable graph is not the common case.
2087 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2088 return;
2089 }
2090
2091 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2092 DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
2093 DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
2094
2095 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01002096 LocationSummary::kCallOnMainOnly,
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002097 kIntrinsified);
2098 const InvokeRuntimeCallingConvention calling_convention;
2099
2100 locations->SetInAt(0, Location::RequiresFpuRegister());
2101 locations->SetOut(Location::RequiresFpuRegister());
2102 // Native code uses the soft float ABI.
2103 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2104 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2105}
2106
2107static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
2108 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2109 // the code generator. Furthermore, the register allocator creates fixed live intervals
2110 // for all caller-saved registers because we are doing a function call. As a result, if
2111 // the input and output locations are unallocated, the register allocator runs out of
2112 // registers and fails; however, a debuggable graph is not the common case.
2113 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2114 return;
2115 }
2116
2117 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2118 DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
2119 DCHECK_EQ(invoke->InputAt(1)->GetType(), Primitive::kPrimDouble);
2120 DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
2121
2122 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01002123 LocationSummary::kCallOnMainOnly,
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002124 kIntrinsified);
2125 const InvokeRuntimeCallingConvention calling_convention;
2126
2127 locations->SetInAt(0, Location::RequiresFpuRegister());
2128 locations->SetInAt(1, Location::RequiresFpuRegister());
2129 locations->SetOut(Location::RequiresFpuRegister());
2130 // Native code uses the soft float ABI.
2131 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2132 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2133 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
2134 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
2135}
2136
2137static void GenFPToFPCall(HInvoke* invoke,
2138 ArmAssembler* assembler,
2139 CodeGeneratorARM* codegen,
2140 QuickEntrypointEnum entry) {
2141 LocationSummary* const locations = invoke->GetLocations();
2142 const InvokeRuntimeCallingConvention calling_convention;
2143
2144 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2145 DCHECK(locations->WillCall() && locations->Intrinsified());
2146 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
2147 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
2148
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002149 // Native code uses the soft float ABI.
2150 __ vmovrrd(calling_convention.GetRegisterAt(0),
2151 calling_convention.GetRegisterAt(1),
2152 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01002153 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002154 __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
2155 calling_convention.GetRegisterAt(0),
2156 calling_convention.GetRegisterAt(1));
2157}
2158
2159static void GenFPFPToFPCall(HInvoke* invoke,
2160 ArmAssembler* assembler,
2161 CodeGeneratorARM* codegen,
2162 QuickEntrypointEnum entry) {
2163 LocationSummary* const locations = invoke->GetLocations();
2164 const InvokeRuntimeCallingConvention calling_convention;
2165
2166 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2167 DCHECK(locations->WillCall() && locations->Intrinsified());
2168 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
2169 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
2170 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(2)));
2171 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(3)));
2172
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002173 // Native code uses the soft float ABI.
2174 __ vmovrrd(calling_convention.GetRegisterAt(0),
2175 calling_convention.GetRegisterAt(1),
2176 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
2177 __ vmovrrd(calling_convention.GetRegisterAt(2),
2178 calling_convention.GetRegisterAt(3),
2179 FromLowSToD(locations->InAt(1).AsFpuRegisterPairLow<SRegister>()));
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01002180 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002181 __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
2182 calling_convention.GetRegisterAt(0),
2183 calling_convention.GetRegisterAt(1));
2184}
2185
2186void IntrinsicLocationsBuilderARM::VisitMathCos(HInvoke* invoke) {
2187 CreateFPToFPCallLocations(arena_, invoke);
2188}
2189
2190void IntrinsicCodeGeneratorARM::VisitMathCos(HInvoke* invoke) {
2191 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCos);
2192}
2193
2194void IntrinsicLocationsBuilderARM::VisitMathSin(HInvoke* invoke) {
2195 CreateFPToFPCallLocations(arena_, invoke);
2196}
2197
2198void IntrinsicCodeGeneratorARM::VisitMathSin(HInvoke* invoke) {
2199 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSin);
2200}
2201
2202void IntrinsicLocationsBuilderARM::VisitMathAcos(HInvoke* invoke) {
2203 CreateFPToFPCallLocations(arena_, invoke);
2204}
2205
2206void IntrinsicCodeGeneratorARM::VisitMathAcos(HInvoke* invoke) {
2207 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAcos);
2208}
2209
2210void IntrinsicLocationsBuilderARM::VisitMathAsin(HInvoke* invoke) {
2211 CreateFPToFPCallLocations(arena_, invoke);
2212}
2213
2214void IntrinsicCodeGeneratorARM::VisitMathAsin(HInvoke* invoke) {
2215 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAsin);
2216}
2217
2218void IntrinsicLocationsBuilderARM::VisitMathAtan(HInvoke* invoke) {
2219 CreateFPToFPCallLocations(arena_, invoke);
2220}
2221
2222void IntrinsicCodeGeneratorARM::VisitMathAtan(HInvoke* invoke) {
2223 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan);
2224}
2225
2226void IntrinsicLocationsBuilderARM::VisitMathCbrt(HInvoke* invoke) {
2227 CreateFPToFPCallLocations(arena_, invoke);
2228}
2229
2230void IntrinsicCodeGeneratorARM::VisitMathCbrt(HInvoke* invoke) {
2231 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCbrt);
2232}
2233
2234void IntrinsicLocationsBuilderARM::VisitMathCosh(HInvoke* invoke) {
2235 CreateFPToFPCallLocations(arena_, invoke);
2236}
2237
2238void IntrinsicCodeGeneratorARM::VisitMathCosh(HInvoke* invoke) {
2239 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCosh);
2240}
2241
2242void IntrinsicLocationsBuilderARM::VisitMathExp(HInvoke* invoke) {
2243 CreateFPToFPCallLocations(arena_, invoke);
2244}
2245
2246void IntrinsicCodeGeneratorARM::VisitMathExp(HInvoke* invoke) {
2247 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExp);
2248}
2249
2250void IntrinsicLocationsBuilderARM::VisitMathExpm1(HInvoke* invoke) {
2251 CreateFPToFPCallLocations(arena_, invoke);
2252}
2253
2254void IntrinsicCodeGeneratorARM::VisitMathExpm1(HInvoke* invoke) {
2255 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExpm1);
2256}
2257
2258void IntrinsicLocationsBuilderARM::VisitMathLog(HInvoke* invoke) {
2259 CreateFPToFPCallLocations(arena_, invoke);
2260}
2261
2262void IntrinsicCodeGeneratorARM::VisitMathLog(HInvoke* invoke) {
2263 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog);
2264}
2265
2266void IntrinsicLocationsBuilderARM::VisitMathLog10(HInvoke* invoke) {
2267 CreateFPToFPCallLocations(arena_, invoke);
2268}
2269
2270void IntrinsicCodeGeneratorARM::VisitMathLog10(HInvoke* invoke) {
2271 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog10);
2272}
2273
2274void IntrinsicLocationsBuilderARM::VisitMathSinh(HInvoke* invoke) {
2275 CreateFPToFPCallLocations(arena_, invoke);
2276}
2277
2278void IntrinsicCodeGeneratorARM::VisitMathSinh(HInvoke* invoke) {
2279 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSinh);
2280}
2281
2282void IntrinsicLocationsBuilderARM::VisitMathTan(HInvoke* invoke) {
2283 CreateFPToFPCallLocations(arena_, invoke);
2284}
2285
2286void IntrinsicCodeGeneratorARM::VisitMathTan(HInvoke* invoke) {
2287 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTan);
2288}
2289
2290void IntrinsicLocationsBuilderARM::VisitMathTanh(HInvoke* invoke) {
2291 CreateFPToFPCallLocations(arena_, invoke);
2292}
2293
2294void IntrinsicCodeGeneratorARM::VisitMathTanh(HInvoke* invoke) {
2295 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTanh);
2296}
2297
2298void IntrinsicLocationsBuilderARM::VisitMathAtan2(HInvoke* invoke) {
2299 CreateFPFPToFPCallLocations(arena_, invoke);
2300}
2301
2302void IntrinsicCodeGeneratorARM::VisitMathAtan2(HInvoke* invoke) {
2303 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan2);
2304}
2305
2306void IntrinsicLocationsBuilderARM::VisitMathHypot(HInvoke* invoke) {
2307 CreateFPFPToFPCallLocations(arena_, invoke);
2308}
2309
2310void IntrinsicCodeGeneratorARM::VisitMathHypot(HInvoke* invoke) {
2311 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickHypot);
2312}
2313
2314void IntrinsicLocationsBuilderARM::VisitMathNextAfter(HInvoke* invoke) {
2315 CreateFPFPToFPCallLocations(arena_, invoke);
2316}
2317
2318void IntrinsicCodeGeneratorARM::VisitMathNextAfter(HInvoke* invoke) {
2319 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter);
2320}
2321
Artem Serovc257da72016-02-02 13:49:43 +00002322void IntrinsicLocationsBuilderARM::VisitIntegerReverse(HInvoke* invoke) {
2323 CreateIntToIntLocations(arena_, invoke);
2324}
2325
2326void IntrinsicCodeGeneratorARM::VisitIntegerReverse(HInvoke* invoke) {
2327 ArmAssembler* assembler = GetAssembler();
2328 LocationSummary* locations = invoke->GetLocations();
2329
2330 Register out = locations->Out().AsRegister<Register>();
2331 Register in = locations->InAt(0).AsRegister<Register>();
2332
2333 __ rbit(out, in);
2334}
2335
2336void IntrinsicLocationsBuilderARM::VisitLongReverse(HInvoke* invoke) {
2337 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2338 LocationSummary::kNoCall,
2339 kIntrinsified);
2340 locations->SetInAt(0, Location::RequiresRegister());
2341 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2342}
2343
2344void IntrinsicCodeGeneratorARM::VisitLongReverse(HInvoke* invoke) {
2345 ArmAssembler* assembler = GetAssembler();
2346 LocationSummary* locations = invoke->GetLocations();
2347
2348 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
2349 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
2350 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
2351 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
2352
2353 __ rbit(out_reg_lo, in_reg_hi);
2354 __ rbit(out_reg_hi, in_reg_lo);
2355}
2356
2357void IntrinsicLocationsBuilderARM::VisitIntegerReverseBytes(HInvoke* invoke) {
2358 CreateIntToIntLocations(arena_, invoke);
2359}
2360
2361void IntrinsicCodeGeneratorARM::VisitIntegerReverseBytes(HInvoke* invoke) {
2362 ArmAssembler* assembler = GetAssembler();
2363 LocationSummary* locations = invoke->GetLocations();
2364
2365 Register out = locations->Out().AsRegister<Register>();
2366 Register in = locations->InAt(0).AsRegister<Register>();
2367
2368 __ rev(out, in);
2369}
2370
2371void IntrinsicLocationsBuilderARM::VisitLongReverseBytes(HInvoke* invoke) {
2372 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2373 LocationSummary::kNoCall,
2374 kIntrinsified);
2375 locations->SetInAt(0, Location::RequiresRegister());
2376 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2377}
2378
2379void IntrinsicCodeGeneratorARM::VisitLongReverseBytes(HInvoke* invoke) {
2380 ArmAssembler* assembler = GetAssembler();
2381 LocationSummary* locations = invoke->GetLocations();
2382
2383 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
2384 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
2385 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
2386 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
2387
2388 __ rev(out_reg_lo, in_reg_hi);
2389 __ rev(out_reg_hi, in_reg_lo);
2390}
2391
2392void IntrinsicLocationsBuilderARM::VisitShortReverseBytes(HInvoke* invoke) {
2393 CreateIntToIntLocations(arena_, invoke);
2394}
2395
2396void IntrinsicCodeGeneratorARM::VisitShortReverseBytes(HInvoke* invoke) {
2397 ArmAssembler* assembler = GetAssembler();
2398 LocationSummary* locations = invoke->GetLocations();
2399
2400 Register out = locations->Out().AsRegister<Register>();
2401 Register in = locations->InAt(0).AsRegister<Register>();
2402
2403 __ revsh(out, in);
2404}
2405
xueliang.zhongf1073c82016-07-05 15:28:19 +01002406static void GenBitCount(HInvoke* instr, Primitive::Type type, ArmAssembler* assembler) {
2407 DCHECK(Primitive::IsIntOrLongType(type)) << type;
2408 DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
2409 DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
2410
2411 bool is_long = type == Primitive::kPrimLong;
2412 LocationSummary* locations = instr->GetLocations();
2413 Location in = locations->InAt(0);
2414 Register src_0 = is_long ? in.AsRegisterPairLow<Register>() : in.AsRegister<Register>();
2415 Register src_1 = is_long ? in.AsRegisterPairHigh<Register>() : src_0;
2416 SRegister tmp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
2417 DRegister tmp_d = FromLowSToD(tmp_s);
2418 Register out_r = locations->Out().AsRegister<Register>();
2419
2420 // Move data from core register(s) to temp D-reg for bit count calculation, then move back.
2421 // According to Cortex A57 and A72 optimization guides, compared to transferring to full D-reg,
2422 // transferring data from core reg to upper or lower half of vfp D-reg requires extra latency,
2423 // That's why for integer bit count, we use 'vmov d0, r0, r0' instead of 'vmov d0[0], r0'.
2424 __ vmovdrr(tmp_d, src_1, src_0); // Temp DReg |--src_1|--src_0|
2425 __ vcntd(tmp_d, tmp_d); // Temp DReg |c|c|c|c|c|c|c|c|
2426 __ vpaddld(tmp_d, tmp_d, 8, /* is_unsigned */ true); // Temp DReg |--c|--c|--c|--c|
2427 __ vpaddld(tmp_d, tmp_d, 16, /* is_unsigned */ true); // Temp DReg |------c|------c|
2428 if (is_long) {
2429 __ vpaddld(tmp_d, tmp_d, 32, /* is_unsigned */ true); // Temp DReg |--------------c|
2430 }
2431 __ vmovrs(out_r, tmp_s);
2432}
2433
2434void IntrinsicLocationsBuilderARM::VisitIntegerBitCount(HInvoke* invoke) {
2435 CreateIntToIntLocations(arena_, invoke);
2436 invoke->GetLocations()->AddTemp(Location::RequiresFpuRegister());
2437}
2438
2439void IntrinsicCodeGeneratorARM::VisitIntegerBitCount(HInvoke* invoke) {
2440 GenBitCount(invoke, Primitive::kPrimInt, GetAssembler());
2441}
2442
2443void IntrinsicLocationsBuilderARM::VisitLongBitCount(HInvoke* invoke) {
2444 VisitIntegerBitCount(invoke);
2445}
2446
2447void IntrinsicCodeGeneratorARM::VisitLongBitCount(HInvoke* invoke) {
2448 GenBitCount(invoke, Primitive::kPrimLong, GetAssembler());
2449}
2450
Tim Zhang25abd6c2016-01-19 23:39:24 +08002451void IntrinsicLocationsBuilderARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2452 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2453 LocationSummary::kNoCall,
2454 kIntrinsified);
2455 locations->SetInAt(0, Location::RequiresRegister());
2456 locations->SetInAt(1, Location::RequiresRegister());
2457 locations->SetInAt(2, Location::RequiresRegister());
2458 locations->SetInAt(3, Location::RequiresRegister());
2459 locations->SetInAt(4, Location::RequiresRegister());
2460
Scott Wakeling3fdab772016-04-25 11:32:37 +01002461 // Temporary registers to store lengths of strings and for calculations.
Tim Zhang25abd6c2016-01-19 23:39:24 +08002462 locations->AddTemp(Location::RequiresRegister());
2463 locations->AddTemp(Location::RequiresRegister());
2464 locations->AddTemp(Location::RequiresRegister());
2465}
2466
2467void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2468 ArmAssembler* assembler = GetAssembler();
2469 LocationSummary* locations = invoke->GetLocations();
2470
2471 // Check assumption that sizeof(Char) is 2 (used in scaling below).
2472 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2473 DCHECK_EQ(char_size, 2u);
2474
2475 // Location of data in char array buffer.
2476 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2477
2478 // Location of char array data in string.
2479 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2480
2481 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2482 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2483 Register srcObj = locations->InAt(0).AsRegister<Register>();
2484 Register srcBegin = locations->InAt(1).AsRegister<Register>();
2485 Register srcEnd = locations->InAt(2).AsRegister<Register>();
2486 Register dstObj = locations->InAt(3).AsRegister<Register>();
2487 Register dstBegin = locations->InAt(4).AsRegister<Register>();
2488
Scott Wakeling3fdab772016-04-25 11:32:37 +01002489 Register num_chr = locations->GetTemp(0).AsRegister<Register>();
2490 Register src_ptr = locations->GetTemp(1).AsRegister<Register>();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002491 Register dst_ptr = locations->GetTemp(2).AsRegister<Register>();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002492
jessicahandojo05765752016-09-09 19:01:32 -07002493 Label done, compressed_string_loop;
Tim Zhang25abd6c2016-01-19 23:39:24 +08002494 // dst to be copied.
2495 __ add(dst_ptr, dstObj, ShifterOperand(data_offset));
2496 __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1));
2497
Scott Wakeling3fdab772016-04-25 11:32:37 +01002498 __ subs(num_chr, srcEnd, ShifterOperand(srcBegin));
Scott Wakeling3fdab772016-04-25 11:32:37 +01002499 // Early out for valid zero-length retrievals.
Tim Zhang25abd6c2016-01-19 23:39:24 +08002500 __ b(&done, EQ);
Scott Wakeling3fdab772016-04-25 11:32:37 +01002501
jessicahandojo05765752016-09-09 19:01:32 -07002502 // src range to copy.
2503 __ add(src_ptr, srcObj, ShifterOperand(value_offset));
2504 Label compressed_string_preloop;
2505 if (mirror::kUseStringCompression) {
2506 // Location of count in string.
2507 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2508 // String's length.
2509 __ ldr(IP, Address(srcObj, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002510 __ tst(IP, ShifterOperand(1));
2511 __ b(&compressed_string_preloop, EQ);
jessicahandojo05765752016-09-09 19:01:32 -07002512 }
2513 __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1));
2514
2515 // Do the copy.
2516 Label loop, remainder;
2517
Scott Wakeling3fdab772016-04-25 11:32:37 +01002518 // Save repairing the value of num_chr on the < 4 character path.
2519 __ subs(IP, num_chr, ShifterOperand(4));
2520 __ b(&remainder, LT);
2521
2522 // Keep the result of the earlier subs, we are going to fetch at least 4 characters.
2523 __ mov(num_chr, ShifterOperand(IP));
2524
2525 // Main loop used for longer fetches loads and stores 4x16-bit characters at a time.
2526 // (LDRD/STRD fault on unaligned addresses and it's not worth inlining extra code
2527 // to rectify these everywhere this intrinsic applies.)
2528 __ Bind(&loop);
2529 __ ldr(IP, Address(src_ptr, char_size * 2));
2530 __ subs(num_chr, num_chr, ShifterOperand(4));
2531 __ str(IP, Address(dst_ptr, char_size * 2));
2532 __ ldr(IP, Address(src_ptr, char_size * 4, Address::PostIndex));
2533 __ str(IP, Address(dst_ptr, char_size * 4, Address::PostIndex));
2534 __ b(&loop, GE);
2535
2536 __ adds(num_chr, num_chr, ShifterOperand(4));
2537 __ b(&done, EQ);
2538
2539 // Main loop for < 4 character case and remainder handling. Loads and stores one
2540 // 16-bit Java character at a time.
2541 __ Bind(&remainder);
2542 __ ldrh(IP, Address(src_ptr, char_size, Address::PostIndex));
2543 __ subs(num_chr, num_chr, ShifterOperand(1));
2544 __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex));
2545 __ b(&remainder, GT);
jessicahandojo05765752016-09-09 19:01:32 -07002546
2547 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002548 __ b(&done);
2549
jessicahandojo05765752016-09-09 19:01:32 -07002550 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
2551 DCHECK_EQ(c_char_size, 1u);
2552 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2553 __ Bind(&compressed_string_preloop);
2554 __ add(src_ptr, src_ptr, ShifterOperand(srcBegin));
2555 __ Bind(&compressed_string_loop);
2556 __ ldrb(IP, Address(src_ptr, c_char_size, Address::PostIndex));
2557 __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex));
2558 __ subs(num_chr, num_chr, ShifterOperand(1));
2559 __ b(&compressed_string_loop, GT);
2560 }
Scott Wakeling3fdab772016-04-25 11:32:37 +01002561
Tim Zhang25abd6c2016-01-19 23:39:24 +08002562 __ Bind(&done);
2563}
2564
Anton Kirilova3ffea22016-04-07 17:02:37 +01002565void IntrinsicLocationsBuilderARM::VisitFloatIsInfinite(HInvoke* invoke) {
2566 CreateFPToIntLocations(arena_, invoke);
2567}
2568
2569void IntrinsicCodeGeneratorARM::VisitFloatIsInfinite(HInvoke* invoke) {
2570 ArmAssembler* const assembler = GetAssembler();
2571 LocationSummary* const locations = invoke->GetLocations();
2572 const Register out = locations->Out().AsRegister<Register>();
2573 // Shifting left by 1 bit makes the value encodable as an immediate operand;
2574 // we don't care about the sign bit anyway.
2575 constexpr uint32_t infinity = kPositiveInfinityFloat << 1U;
2576
2577 __ vmovrs(out, locations->InAt(0).AsFpuRegister<SRegister>());
2578 // We don't care about the sign bit, so shift left.
2579 __ Lsl(out, out, 1);
2580 __ eor(out, out, ShifterOperand(infinity));
2581 // If the result is 0, then it has 32 leading zeros, and less than that otherwise.
2582 __ clz(out, out);
2583 // Any number less than 32 logically shifted right by 5 bits results in 0;
2584 // the same operation on 32 yields 1.
2585 __ Lsr(out, out, 5);
2586}
2587
2588void IntrinsicLocationsBuilderARM::VisitDoubleIsInfinite(HInvoke* invoke) {
2589 CreateFPToIntLocations(arena_, invoke);
2590}
2591
2592void IntrinsicCodeGeneratorARM::VisitDoubleIsInfinite(HInvoke* invoke) {
2593 ArmAssembler* const assembler = GetAssembler();
2594 LocationSummary* const locations = invoke->GetLocations();
2595 const Register out = locations->Out().AsRegister<Register>();
2596 // The highest 32 bits of double precision positive infinity separated into
2597 // two constants encodable as immediate operands.
2598 constexpr uint32_t infinity_high = 0x7f000000U;
2599 constexpr uint32_t infinity_high2 = 0x00f00000U;
2600
2601 static_assert((infinity_high | infinity_high2) == static_cast<uint32_t>(kPositiveInfinityDouble >> 32U),
2602 "The constants do not add up to the high 32 bits of double precision positive infinity.");
2603 __ vmovrrd(IP, out, FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
2604 __ eor(out, out, ShifterOperand(infinity_high));
2605 __ eor(out, out, ShifterOperand(infinity_high2));
2606 // We don't care about the sign bit, so shift left.
2607 __ orr(out, IP, ShifterOperand(out, LSL, 1));
2608 // If the result is 0, then it has 32 leading zeros, and less than that otherwise.
2609 __ clz(out, out);
2610 // Any number less than 32 logically shifted right by 5 bits results in 0;
2611 // the same operation on 32 yields 1.
2612 __ Lsr(out, out, 5);
2613}
2614
TatWai Chongd8c052a2016-11-02 16:12:48 +08002615void IntrinsicLocationsBuilderARM::VisitReferenceGetReferent(HInvoke* invoke) {
2616 if (kEmitCompilerReadBarrier) {
2617 // Do not intrinsify this call with the read barrier configuration.
2618 return;
2619 }
2620 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2621 LocationSummary::kCallOnSlowPath,
2622 kIntrinsified);
2623 locations->SetInAt(0, Location::RequiresRegister());
2624 locations->SetOut(Location::SameAsFirstInput());
2625 locations->AddTemp(Location::RequiresRegister());
2626}
2627
2628void IntrinsicCodeGeneratorARM::VisitReferenceGetReferent(HInvoke* invoke) {
2629 DCHECK(!kEmitCompilerReadBarrier);
2630 ArmAssembler* const assembler = GetAssembler();
2631 LocationSummary* locations = invoke->GetLocations();
2632
2633 Register obj = locations->InAt(0).AsRegister<Register>();
2634 Register out = locations->Out().AsRegister<Register>();
2635
2636 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
2637 codegen_->AddSlowPath(slow_path);
2638
2639 // Load ArtMethod first.
2640 HInvokeStaticOrDirect* invoke_direct = invoke->AsInvokeStaticOrDirect();
2641 DCHECK(invoke_direct != nullptr);
2642 Register temp = codegen_->GenerateCalleeMethodStaticOrDirectCall(
2643 invoke_direct, locations->GetTemp(0)).AsRegister<Register>();
2644
2645 // Now get declaring class.
2646 __ ldr(temp, Address(temp, ArtMethod::DeclaringClassOffset().Int32Value()));
2647
2648 uint32_t slow_path_flag_offset = codegen_->GetReferenceSlowFlagOffset();
2649 uint32_t disable_flag_offset = codegen_->GetReferenceDisableFlagOffset();
2650 DCHECK_NE(slow_path_flag_offset, 0u);
2651 DCHECK_NE(disable_flag_offset, 0u);
2652 DCHECK_NE(slow_path_flag_offset, disable_flag_offset);
2653
2654 // Check static flags that prevent using intrinsic.
2655 __ ldr(IP, Address(temp, disable_flag_offset));
2656 __ ldr(temp, Address(temp, slow_path_flag_offset));
2657 __ orr(IP, IP, ShifterOperand(temp));
2658 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel());
2659
2660 // Fast path.
2661 __ ldr(out, Address(obj, mirror::Reference::ReferentOffset().Int32Value()));
2662 codegen_->MaybeRecordImplicitNullCheck(invoke);
2663 __ MaybeUnpoisonHeapReference(out);
2664 __ Bind(slow_path->GetExitLabel());
2665}
2666
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002667void IntrinsicLocationsBuilderARM::VisitIntegerValueOf(HInvoke* invoke) {
2668 InvokeRuntimeCallingConvention calling_convention;
2669 IntrinsicVisitor::ComputeIntegerValueOfLocations(
2670 invoke,
2671 codegen_,
2672 Location::RegisterLocation(R0),
2673 Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2674}
2675
2676void IntrinsicCodeGeneratorARM::VisitIntegerValueOf(HInvoke* invoke) {
2677 IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo();
2678 LocationSummary* locations = invoke->GetLocations();
2679 ArmAssembler* const assembler = GetAssembler();
2680
2681 Register out = locations->Out().AsRegister<Register>();
2682 InvokeRuntimeCallingConvention calling_convention;
2683 Register argument = calling_convention.GetRegisterAt(0);
2684 if (invoke->InputAt(0)->IsConstant()) {
2685 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
2686 if (value >= info.low && value <= info.high) {
2687 // Just embed the j.l.Integer in the code.
2688 ScopedObjectAccess soa(Thread::Current());
2689 mirror::Object* boxed = info.cache->Get(value + (-info.low));
2690 DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed));
2691 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(boxed));
2692 __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
2693 } else {
2694 // Allocate and initialize a new j.l.Integer.
2695 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
2696 // JIT object table.
2697 uint32_t address =
2698 dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2699 __ LoadLiteral(argument, codegen_->DeduplicateBootImageAddressLiteral(address));
2700 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2701 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2702 __ LoadImmediate(IP, value);
2703 __ StoreToOffset(kStoreWord, IP, out, info.value_offset);
2704 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2705 // one.
2706 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2707 }
2708 } else {
2709 Register in = locations->InAt(0).AsRegister<Register>();
2710 // Check bounds of our cache.
2711 __ AddConstant(out, in, -info.low);
2712 __ CmpConstant(out, info.high - info.low + 1);
2713 Label allocate, done;
2714 __ b(&allocate, HS);
2715 // If the value is within the bounds, load the j.l.Integer directly from the array.
2716 uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
2717 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.cache));
2718 __ LoadLiteral(IP, codegen_->DeduplicateBootImageAddressLiteral(data_offset + address));
2719 codegen_->LoadFromShiftedRegOffset(Primitive::kPrimNot, locations->Out(), IP, out);
2720 __ MaybeUnpoisonHeapReference(out);
2721 __ b(&done);
2722 __ Bind(&allocate);
2723 // Otherwise allocate and initialize a new j.l.Integer.
2724 address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2725 __ LoadLiteral(argument, codegen_->DeduplicateBootImageAddressLiteral(address));
2726 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2727 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2728 __ StoreToOffset(kStoreWord, in, out, info.value_offset);
2729 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2730 // one.
2731 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2732 __ Bind(&done);
2733 }
2734}
2735
Aart Bik2f9fcc92016-03-01 15:16:54 -08002736UNIMPLEMENTED_INTRINSIC(ARM, MathMinDoubleDouble)
2737UNIMPLEMENTED_INTRINSIC(ARM, MathMinFloatFloat)
2738UNIMPLEMENTED_INTRINSIC(ARM, MathMaxDoubleDouble)
2739UNIMPLEMENTED_INTRINSIC(ARM, MathMaxFloatFloat)
2740UNIMPLEMENTED_INTRINSIC(ARM, MathMinLongLong)
2741UNIMPLEMENTED_INTRINSIC(ARM, MathMaxLongLong)
2742UNIMPLEMENTED_INTRINSIC(ARM, MathCeil) // Could be done by changing rounding mode, maybe?
2743UNIMPLEMENTED_INTRINSIC(ARM, MathFloor) // Could be done by changing rounding mode, maybe?
2744UNIMPLEMENTED_INTRINSIC(ARM, MathRint)
2745UNIMPLEMENTED_INTRINSIC(ARM, MathRoundDouble) // Could be done by changing rounding mode, maybe?
2746UNIMPLEMENTED_INTRINSIC(ARM, MathRoundFloat) // Could be done by changing rounding mode, maybe?
2747UNIMPLEMENTED_INTRINSIC(ARM, UnsafeCASLong) // High register pressure.
2748UNIMPLEMENTED_INTRINSIC(ARM, SystemArrayCopyChar)
Aart Bik2f9fcc92016-03-01 15:16:54 -08002749UNIMPLEMENTED_INTRINSIC(ARM, IntegerHighestOneBit)
2750UNIMPLEMENTED_INTRINSIC(ARM, LongHighestOneBit)
2751UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit)
2752UNIMPLEMENTED_INTRINSIC(ARM, LongLowestOneBit)
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08002753
Aart Bikff7d89c2016-11-07 08:49:28 -08002754UNIMPLEMENTED_INTRINSIC(ARM, StringStringIndexOf);
2755UNIMPLEMENTED_INTRINSIC(ARM, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08002756UNIMPLEMENTED_INTRINSIC(ARM, StringBufferAppend);
2757UNIMPLEMENTED_INTRINSIC(ARM, StringBufferLength);
2758UNIMPLEMENTED_INTRINSIC(ARM, StringBufferToString);
2759UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderAppend);
2760UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderLength);
2761UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08002762
Aart Bik0e54c012016-03-04 12:08:31 -08002763// 1.8.
2764UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddInt)
2765UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddLong)
2766UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetInt)
2767UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetLong)
2768UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08002769
Aart Bik2f9fcc92016-03-01 15:16:54 -08002770UNREACHABLE_INTRINSICS(ARM)
Roland Levillain4d027112015-07-01 15:41:14 +01002771
2772#undef __
2773
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08002774} // namespace arm
2775} // namespace art