blob: 1006a776f0955739db8f8b14ae69c84860b7276c [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
Anton Kirilov6f644202017-02-27 18:29:45 +0000273static void GenNumberOfLeadingZeros(HInvoke* invoke,
Scott Wakeling611d3392015-07-10 11:42:06 +0100274 Primitive::Type type,
Anton Kirilov6f644202017-02-27 18:29:45 +0000275 CodeGeneratorARM* codegen) {
276 ArmAssembler* assembler = codegen->GetAssembler();
277 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling611d3392015-07-10 11:42:06 +0100278 Location in = locations->InAt(0);
279 Register out = locations->Out().AsRegister<Register>();
280
281 DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
282
283 if (type == Primitive::kPrimLong) {
284 Register in_reg_lo = in.AsRegisterPairLow<Register>();
285 Register in_reg_hi = in.AsRegisterPairHigh<Register>();
286 Label end;
Anton Kirilov6f644202017-02-27 18:29:45 +0000287 Label* final_label = codegen->GetFinalLabel(invoke, &end);
Scott Wakeling611d3392015-07-10 11:42:06 +0100288 __ clz(out, in_reg_hi);
Anton Kirilov6f644202017-02-27 18:29:45 +0000289 __ CompareAndBranchIfNonZero(in_reg_hi, final_label);
Scott Wakeling611d3392015-07-10 11:42:06 +0100290 __ clz(out, in_reg_lo);
291 __ AddConstant(out, 32);
Anton Kirilov6f644202017-02-27 18:29:45 +0000292 if (end.IsLinked()) {
293 __ Bind(&end);
294 }
Scott Wakeling611d3392015-07-10 11:42:06 +0100295 } else {
296 __ clz(out, in.AsRegister<Register>());
297 }
298}
299
300void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
301 CreateIntToIntLocations(arena_, invoke);
302}
303
304void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
Anton Kirilov6f644202017-02-27 18:29:45 +0000305 GenNumberOfLeadingZeros(invoke, Primitive::kPrimInt, codegen_);
Scott Wakeling611d3392015-07-10 11:42:06 +0100306}
307
308void IntrinsicLocationsBuilderARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
309 LocationSummary* locations = new (arena_) LocationSummary(invoke,
310 LocationSummary::kNoCall,
311 kIntrinsified);
312 locations->SetInAt(0, Location::RequiresRegister());
313 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
314}
315
316void IntrinsicCodeGeneratorARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
Anton Kirilov6f644202017-02-27 18:29:45 +0000317 GenNumberOfLeadingZeros(invoke, Primitive::kPrimLong, codegen_);
Scott Wakeling611d3392015-07-10 11:42:06 +0100318}
319
Anton Kirilov6f644202017-02-27 18:29:45 +0000320static void GenNumberOfTrailingZeros(HInvoke* invoke,
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100321 Primitive::Type type,
Anton Kirilov6f644202017-02-27 18:29:45 +0000322 CodeGeneratorARM* codegen) {
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100323 DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
324
Anton Kirilov6f644202017-02-27 18:29:45 +0000325 ArmAssembler* assembler = codegen->GetAssembler();
326 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100327 Register out = locations->Out().AsRegister<Register>();
328
329 if (type == Primitive::kPrimLong) {
330 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
331 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
332 Label end;
Anton Kirilov6f644202017-02-27 18:29:45 +0000333 Label* final_label = codegen->GetFinalLabel(invoke, &end);
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100334 __ rbit(out, in_reg_lo);
335 __ clz(out, out);
Anton Kirilov6f644202017-02-27 18:29:45 +0000336 __ CompareAndBranchIfNonZero(in_reg_lo, final_label);
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100337 __ rbit(out, in_reg_hi);
338 __ clz(out, out);
339 __ AddConstant(out, 32);
Anton Kirilov6f644202017-02-27 18:29:45 +0000340 if (end.IsLinked()) {
341 __ Bind(&end);
342 }
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100343 } else {
344 Register in = locations->InAt(0).AsRegister<Register>();
345 __ rbit(out, in);
346 __ clz(out, out);
347 }
348}
349
350void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
351 LocationSummary* locations = new (arena_) LocationSummary(invoke,
352 LocationSummary::kNoCall,
353 kIntrinsified);
354 locations->SetInAt(0, Location::RequiresRegister());
355 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
356}
357
358void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
Anton Kirilov6f644202017-02-27 18:29:45 +0000359 GenNumberOfTrailingZeros(invoke, Primitive::kPrimInt, codegen_);
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100360}
361
362void IntrinsicLocationsBuilderARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
363 LocationSummary* locations = new (arena_) LocationSummary(invoke,
364 LocationSummary::kNoCall,
365 kIntrinsified);
366 locations->SetInAt(0, Location::RequiresRegister());
367 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
368}
369
370void IntrinsicCodeGeneratorARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
Anton Kirilov6f644202017-02-27 18:29:45 +0000371 GenNumberOfTrailingZeros(invoke, Primitive::kPrimLong, codegen_);
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100372}
373
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800374static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
375 Location in = locations->InAt(0);
376 Location out = locations->Out();
377
378 if (is64bit) {
379 __ vabsd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
380 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
381 } else {
382 __ vabss(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>());
383 }
384}
385
386void IntrinsicLocationsBuilderARM::VisitMathAbsDouble(HInvoke* invoke) {
387 CreateFPToFPLocations(arena_, invoke);
388}
389
390void IntrinsicCodeGeneratorARM::VisitMathAbsDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000391 MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800392}
393
394void IntrinsicLocationsBuilderARM::VisitMathAbsFloat(HInvoke* invoke) {
395 CreateFPToFPLocations(arena_, invoke);
396}
397
398void IntrinsicCodeGeneratorARM::VisitMathAbsFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000399 MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800400}
401
402static void CreateIntToIntPlusTemp(ArenaAllocator* arena, HInvoke* invoke) {
403 LocationSummary* locations = new (arena) LocationSummary(invoke,
404 LocationSummary::kNoCall,
405 kIntrinsified);
406 locations->SetInAt(0, Location::RequiresRegister());
407 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
408
409 locations->AddTemp(Location::RequiresRegister());
410}
411
412static void GenAbsInteger(LocationSummary* locations,
413 bool is64bit,
414 ArmAssembler* assembler) {
415 Location in = locations->InAt(0);
416 Location output = locations->Out();
417
418 Register mask = locations->GetTemp(0).AsRegister<Register>();
419
420 if (is64bit) {
421 Register in_reg_lo = in.AsRegisterPairLow<Register>();
422 Register in_reg_hi = in.AsRegisterPairHigh<Register>();
423 Register out_reg_lo = output.AsRegisterPairLow<Register>();
424 Register out_reg_hi = output.AsRegisterPairHigh<Register>();
425
426 DCHECK_NE(out_reg_lo, in_reg_hi) << "Diagonal overlap unexpected.";
427
428 __ Asr(mask, in_reg_hi, 31);
429 __ adds(out_reg_lo, in_reg_lo, ShifterOperand(mask));
430 __ adc(out_reg_hi, in_reg_hi, ShifterOperand(mask));
431 __ eor(out_reg_lo, mask, ShifterOperand(out_reg_lo));
432 __ eor(out_reg_hi, mask, ShifterOperand(out_reg_hi));
433 } else {
434 Register in_reg = in.AsRegister<Register>();
435 Register out_reg = output.AsRegister<Register>();
436
437 __ Asr(mask, in_reg, 31);
438 __ add(out_reg, in_reg, ShifterOperand(mask));
439 __ eor(out_reg, mask, ShifterOperand(out_reg));
440 }
441}
442
443void IntrinsicLocationsBuilderARM::VisitMathAbsInt(HInvoke* invoke) {
444 CreateIntToIntPlusTemp(arena_, invoke);
445}
446
447void IntrinsicCodeGeneratorARM::VisitMathAbsInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000448 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800449}
450
451
452void IntrinsicLocationsBuilderARM::VisitMathAbsLong(HInvoke* invoke) {
453 CreateIntToIntPlusTemp(arena_, invoke);
454}
455
456void IntrinsicCodeGeneratorARM::VisitMathAbsLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000457 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800458}
459
460static void GenMinMax(LocationSummary* locations,
461 bool is_min,
462 ArmAssembler* assembler) {
463 Register op1 = locations->InAt(0).AsRegister<Register>();
464 Register op2 = locations->InAt(1).AsRegister<Register>();
465 Register out = locations->Out().AsRegister<Register>();
466
467 __ cmp(op1, ShifterOperand(op2));
468
469 __ it((is_min) ? Condition::LT : Condition::GT, kItElse);
470 __ mov(out, ShifterOperand(op1), is_min ? Condition::LT : Condition::GT);
471 __ mov(out, ShifterOperand(op2), is_min ? Condition::GE : Condition::LE);
472}
473
474static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
475 LocationSummary* locations = new (arena) LocationSummary(invoke,
476 LocationSummary::kNoCall,
477 kIntrinsified);
478 locations->SetInAt(0, Location::RequiresRegister());
479 locations->SetInAt(1, Location::RequiresRegister());
480 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
481}
482
483void IntrinsicLocationsBuilderARM::VisitMathMinIntInt(HInvoke* invoke) {
484 CreateIntIntToIntLocations(arena_, invoke);
485}
486
487void IntrinsicCodeGeneratorARM::VisitMathMinIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000488 GenMinMax(invoke->GetLocations(), /* is_min */ true, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800489}
490
491void IntrinsicLocationsBuilderARM::VisitMathMaxIntInt(HInvoke* invoke) {
492 CreateIntIntToIntLocations(arena_, invoke);
493}
494
495void IntrinsicCodeGeneratorARM::VisitMathMaxIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000496 GenMinMax(invoke->GetLocations(), /* is_min */ false, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800497}
498
499void IntrinsicLocationsBuilderARM::VisitMathSqrt(HInvoke* invoke) {
500 CreateFPToFPLocations(arena_, invoke);
501}
502
503void IntrinsicCodeGeneratorARM::VisitMathSqrt(HInvoke* invoke) {
504 LocationSummary* locations = invoke->GetLocations();
505 ArmAssembler* assembler = GetAssembler();
506 __ vsqrtd(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
507 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
508}
509
510void IntrinsicLocationsBuilderARM::VisitMemoryPeekByte(HInvoke* invoke) {
511 CreateIntToIntLocations(arena_, invoke);
512}
513
514void IntrinsicCodeGeneratorARM::VisitMemoryPeekByte(HInvoke* invoke) {
515 ArmAssembler* assembler = GetAssembler();
516 // Ignore upper 4B of long address.
517 __ ldrsb(invoke->GetLocations()->Out().AsRegister<Register>(),
518 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
519}
520
521void IntrinsicLocationsBuilderARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
522 CreateIntToIntLocations(arena_, invoke);
523}
524
525void IntrinsicCodeGeneratorARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
526 ArmAssembler* assembler = GetAssembler();
527 // Ignore upper 4B of long address.
528 __ ldr(invoke->GetLocations()->Out().AsRegister<Register>(),
529 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
530}
531
532void IntrinsicLocationsBuilderARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
533 CreateIntToIntLocations(arena_, invoke);
534}
535
536void IntrinsicCodeGeneratorARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
537 ArmAssembler* assembler = GetAssembler();
538 // Ignore upper 4B of long address.
539 Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
540 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
541 // exception. So we can't use ldrd as addr may be unaligned.
542 Register lo = invoke->GetLocations()->Out().AsRegisterPairLow<Register>();
543 Register hi = invoke->GetLocations()->Out().AsRegisterPairHigh<Register>();
544 if (addr == lo) {
545 __ ldr(hi, Address(addr, 4));
546 __ ldr(lo, Address(addr, 0));
547 } else {
548 __ ldr(lo, Address(addr, 0));
549 __ ldr(hi, Address(addr, 4));
550 }
551}
552
553void IntrinsicLocationsBuilderARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
554 CreateIntToIntLocations(arena_, invoke);
555}
556
557void IntrinsicCodeGeneratorARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
558 ArmAssembler* assembler = GetAssembler();
559 // Ignore upper 4B of long address.
560 __ ldrsh(invoke->GetLocations()->Out().AsRegister<Register>(),
561 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
562}
563
564static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
565 LocationSummary* locations = new (arena) LocationSummary(invoke,
566 LocationSummary::kNoCall,
567 kIntrinsified);
568 locations->SetInAt(0, Location::RequiresRegister());
569 locations->SetInAt(1, Location::RequiresRegister());
570}
571
572void IntrinsicLocationsBuilderARM::VisitMemoryPokeByte(HInvoke* invoke) {
573 CreateIntIntToVoidLocations(arena_, invoke);
574}
575
576void IntrinsicCodeGeneratorARM::VisitMemoryPokeByte(HInvoke* invoke) {
577 ArmAssembler* assembler = GetAssembler();
578 __ strb(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
579 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
580}
581
582void IntrinsicLocationsBuilderARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
583 CreateIntIntToVoidLocations(arena_, invoke);
584}
585
586void IntrinsicCodeGeneratorARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
587 ArmAssembler* assembler = GetAssembler();
588 __ str(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
589 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
590}
591
592void IntrinsicLocationsBuilderARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
593 CreateIntIntToVoidLocations(arena_, invoke);
594}
595
596void IntrinsicCodeGeneratorARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
597 ArmAssembler* assembler = GetAssembler();
598 // Ignore upper 4B of long address.
599 Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
600 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
601 // exception. So we can't use ldrd as addr may be unaligned.
602 __ str(invoke->GetLocations()->InAt(1).AsRegisterPairLow<Register>(), Address(addr, 0));
603 __ str(invoke->GetLocations()->InAt(1).AsRegisterPairHigh<Register>(), Address(addr, 4));
604}
605
606void IntrinsicLocationsBuilderARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
607 CreateIntIntToVoidLocations(arena_, invoke);
608}
609
610void IntrinsicCodeGeneratorARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
611 ArmAssembler* assembler = GetAssembler();
612 __ strh(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
613 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
614}
615
616void IntrinsicLocationsBuilderARM::VisitThreadCurrentThread(HInvoke* invoke) {
617 LocationSummary* locations = new (arena_) LocationSummary(invoke,
618 LocationSummary::kNoCall,
619 kIntrinsified);
620 locations->SetOut(Location::RequiresRegister());
621}
622
623void IntrinsicCodeGeneratorARM::VisitThreadCurrentThread(HInvoke* invoke) {
624 ArmAssembler* assembler = GetAssembler();
625 __ LoadFromOffset(kLoadWord,
626 invoke->GetLocations()->Out().AsRegister<Register>(),
627 TR,
628 Thread::PeerOffset<kArmPointerSize>().Int32Value());
629}
630
631static void GenUnsafeGet(HInvoke* invoke,
632 Primitive::Type type,
633 bool is_volatile,
634 CodeGeneratorARM* codegen) {
635 LocationSummary* locations = invoke->GetLocations();
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800636 ArmAssembler* assembler = codegen->GetAssembler();
Roland Levillain3b359c72015-11-17 19:35:12 +0000637 Location base_loc = locations->InAt(1);
638 Register base = base_loc.AsRegister<Register>(); // Object pointer.
639 Location offset_loc = locations->InAt(2);
640 Register offset = offset_loc.AsRegisterPairLow<Register>(); // Long offset, lo part only.
641 Location trg_loc = locations->Out();
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800642
Roland Levillainc9285912015-12-18 10:38:42 +0000643 switch (type) {
644 case Primitive::kPrimInt: {
645 Register trg = trg_loc.AsRegister<Register>();
646 __ ldr(trg, Address(base, offset));
647 if (is_volatile) {
648 __ dmb(ISH);
649 }
650 break;
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800651 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800652
Roland Levillainc9285912015-12-18 10:38:42 +0000653 case Primitive::kPrimNot: {
654 Register trg = trg_loc.AsRegister<Register>();
655 if (kEmitCompilerReadBarrier) {
656 if (kUseBakerReadBarrier) {
657 Location temp = locations->GetTemp(0);
Roland Levillainbfea3352016-06-23 13:48:47 +0100658 codegen->GenerateReferenceLoadWithBakerReadBarrier(
659 invoke, trg_loc, base, 0U, offset_loc, TIMES_1, temp, /* needs_null_check */ false);
Roland Levillainc9285912015-12-18 10:38:42 +0000660 if (is_volatile) {
661 __ dmb(ISH);
662 }
663 } else {
664 __ ldr(trg, Address(base, offset));
665 if (is_volatile) {
666 __ dmb(ISH);
667 }
668 codegen->GenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
669 }
670 } else {
671 __ ldr(trg, Address(base, offset));
672 if (is_volatile) {
673 __ dmb(ISH);
674 }
675 __ MaybeUnpoisonHeapReference(trg);
676 }
677 break;
678 }
Roland Levillain4d027112015-07-01 15:41:14 +0100679
Roland Levillainc9285912015-12-18 10:38:42 +0000680 case Primitive::kPrimLong: {
681 Register trg_lo = trg_loc.AsRegisterPairLow<Register>();
682 __ add(IP, base, ShifterOperand(offset));
683 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
684 Register trg_hi = trg_loc.AsRegisterPairHigh<Register>();
685 __ ldrexd(trg_lo, trg_hi, IP);
686 } else {
687 __ ldrd(trg_lo, Address(IP));
688 }
689 if (is_volatile) {
690 __ dmb(ISH);
691 }
692 break;
693 }
694
695 default:
696 LOG(FATAL) << "Unexpected type " << type;
697 UNREACHABLE();
Roland Levillain4d027112015-07-01 15:41:14 +0100698 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800699}
700
Roland Levillainc9285912015-12-18 10:38:42 +0000701static void CreateIntIntIntToIntLocations(ArenaAllocator* arena,
702 HInvoke* invoke,
703 Primitive::Type type) {
Roland Levillain3b359c72015-11-17 19:35:12 +0000704 bool can_call = kEmitCompilerReadBarrier &&
705 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
706 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800707 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100708 (can_call
709 ? LocationSummary::kCallOnSlowPath
710 : LocationSummary::kNoCall),
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800711 kIntrinsified);
Vladimir Marko70e97462016-08-09 11:04:26 +0100712 if (can_call && kUseBakerReadBarrier) {
Vladimir Marko804b03f2016-09-14 16:26:36 +0100713 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Vladimir Marko70e97462016-08-09 11:04:26 +0100714 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800715 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
716 locations->SetInAt(1, Location::RequiresRegister());
717 locations->SetInAt(2, Location::RequiresRegister());
Roland Levillainbfea3352016-06-23 13:48:47 +0100718 locations->SetOut(Location::RequiresRegister(),
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100719 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Roland Levillainc9285912015-12-18 10:38:42 +0000720 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
721 // We need a temporary register for the read barrier marking slow
Roland Levillainbfea3352016-06-23 13:48:47 +0100722 // path in InstructionCodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier.
Roland Levillainc9285912015-12-18 10:38:42 +0000723 locations->AddTemp(Location::RequiresRegister());
724 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800725}
726
727void IntrinsicLocationsBuilderARM::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000728 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800729}
730void IntrinsicLocationsBuilderARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000731 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800732}
733void IntrinsicLocationsBuilderARM::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000734 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800735}
736void IntrinsicLocationsBuilderARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000737 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800738}
739void IntrinsicLocationsBuilderARM::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000740 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800741}
742void IntrinsicLocationsBuilderARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000743 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800744}
745
746void IntrinsicCodeGeneratorARM::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000747 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800748}
749void IntrinsicCodeGeneratorARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000750 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800751}
752void IntrinsicCodeGeneratorARM::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000753 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800754}
755void IntrinsicCodeGeneratorARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000756 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800757}
758void IntrinsicCodeGeneratorARM::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000759 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800760}
761void IntrinsicCodeGeneratorARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000762 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800763}
764
765static void CreateIntIntIntIntToVoid(ArenaAllocator* arena,
766 const ArmInstructionSetFeatures& features,
767 Primitive::Type type,
768 bool is_volatile,
769 HInvoke* invoke) {
770 LocationSummary* locations = new (arena) LocationSummary(invoke,
771 LocationSummary::kNoCall,
772 kIntrinsified);
773 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
774 locations->SetInAt(1, Location::RequiresRegister());
775 locations->SetInAt(2, Location::RequiresRegister());
776 locations->SetInAt(3, Location::RequiresRegister());
777
778 if (type == Primitive::kPrimLong) {
779 // Potentially need temps for ldrexd-strexd loop.
780 if (is_volatile && !features.HasAtomicLdrdAndStrd()) {
781 locations->AddTemp(Location::RequiresRegister()); // Temp_lo.
782 locations->AddTemp(Location::RequiresRegister()); // Temp_hi.
783 }
784 } else if (type == Primitive::kPrimNot) {
785 // Temps for card-marking.
786 locations->AddTemp(Location::RequiresRegister()); // Temp.
787 locations->AddTemp(Location::RequiresRegister()); // Card.
788 }
789}
790
791void IntrinsicLocationsBuilderARM::VisitUnsafePut(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000792 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800793}
794void IntrinsicLocationsBuilderARM::VisitUnsafePutOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000795 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800796}
797void IntrinsicLocationsBuilderARM::VisitUnsafePutVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000798 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ true, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800799}
800void IntrinsicLocationsBuilderARM::VisitUnsafePutObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000801 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800802}
803void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000804 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800805}
806void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000807 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ true, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800808}
809void IntrinsicLocationsBuilderARM::VisitUnsafePutLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000810 CreateIntIntIntIntToVoid(
811 arena_, features_, Primitive::kPrimLong, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800812}
813void IntrinsicLocationsBuilderARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000814 CreateIntIntIntIntToVoid(
815 arena_, features_, Primitive::kPrimLong, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800816}
817void IntrinsicLocationsBuilderARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000818 CreateIntIntIntIntToVoid(
819 arena_, features_, Primitive::kPrimLong, /* is_volatile */ true, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800820}
821
822static void GenUnsafePut(LocationSummary* locations,
823 Primitive::Type type,
824 bool is_volatile,
825 bool is_ordered,
826 CodeGeneratorARM* codegen) {
827 ArmAssembler* assembler = codegen->GetAssembler();
828
829 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
830 Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Long offset, lo part only.
831 Register value;
832
833 if (is_volatile || is_ordered) {
834 __ dmb(ISH);
835 }
836
837 if (type == Primitive::kPrimLong) {
838 Register value_lo = locations->InAt(3).AsRegisterPairLow<Register>();
839 value = value_lo;
840 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
841 Register temp_lo = locations->GetTemp(0).AsRegister<Register>();
842 Register temp_hi = locations->GetTemp(1).AsRegister<Register>();
843 Register value_hi = locations->InAt(3).AsRegisterPairHigh<Register>();
844
845 __ add(IP, base, ShifterOperand(offset));
846 Label loop_head;
847 __ Bind(&loop_head);
848 __ ldrexd(temp_lo, temp_hi, IP);
849 __ strexd(temp_lo, value_lo, value_hi, IP);
850 __ cmp(temp_lo, ShifterOperand(0));
851 __ b(&loop_head, NE);
852 } else {
853 __ add(IP, base, ShifterOperand(offset));
854 __ strd(value_lo, Address(IP));
855 }
856 } else {
Roland Levillain4d027112015-07-01 15:41:14 +0100857 value = locations->InAt(3).AsRegister<Register>();
858 Register source = value;
859 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
860 Register temp = locations->GetTemp(0).AsRegister<Register>();
861 __ Mov(temp, value);
862 __ PoisonHeapReference(temp);
863 source = temp;
864 }
865 __ str(source, Address(base, offset));
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800866 }
867
868 if (is_volatile) {
869 __ dmb(ISH);
870 }
871
872 if (type == Primitive::kPrimNot) {
873 Register temp = locations->GetTemp(0).AsRegister<Register>();
874 Register card = locations->GetTemp(1).AsRegister<Register>();
Nicolas Geoffray07276db2015-05-18 14:22:09 +0100875 bool value_can_be_null = true; // TODO: Worth finding out this information?
876 codegen->MarkGCCard(temp, card, base, value, value_can_be_null);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800877 }
878}
879
880void IntrinsicCodeGeneratorARM::VisitUnsafePut(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000881 GenUnsafePut(invoke->GetLocations(),
882 Primitive::kPrimInt,
883 /* is_volatile */ false,
884 /* is_ordered */ false,
885 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800886}
887void IntrinsicCodeGeneratorARM::VisitUnsafePutOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000888 GenUnsafePut(invoke->GetLocations(),
889 Primitive::kPrimInt,
890 /* is_volatile */ false,
891 /* is_ordered */ true,
892 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800893}
894void IntrinsicCodeGeneratorARM::VisitUnsafePutVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000895 GenUnsafePut(invoke->GetLocations(),
896 Primitive::kPrimInt,
897 /* is_volatile */ true,
898 /* is_ordered */ false,
899 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800900}
901void IntrinsicCodeGeneratorARM::VisitUnsafePutObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000902 GenUnsafePut(invoke->GetLocations(),
903 Primitive::kPrimNot,
904 /* is_volatile */ false,
905 /* is_ordered */ false,
906 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800907}
908void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000909 GenUnsafePut(invoke->GetLocations(),
910 Primitive::kPrimNot,
911 /* is_volatile */ false,
912 /* is_ordered */ true,
913 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800914}
915void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000916 GenUnsafePut(invoke->GetLocations(),
917 Primitive::kPrimNot,
918 /* is_volatile */ true,
919 /* is_ordered */ false,
920 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800921}
922void IntrinsicCodeGeneratorARM::VisitUnsafePutLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000923 GenUnsafePut(invoke->GetLocations(),
924 Primitive::kPrimLong,
925 /* is_volatile */ false,
926 /* is_ordered */ false,
927 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800928}
929void IntrinsicCodeGeneratorARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000930 GenUnsafePut(invoke->GetLocations(),
931 Primitive::kPrimLong,
932 /* is_volatile */ false,
933 /* is_ordered */ true,
934 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800935}
936void IntrinsicCodeGeneratorARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000937 GenUnsafePut(invoke->GetLocations(),
938 Primitive::kPrimLong,
939 /* is_volatile */ true,
940 /* is_ordered */ false,
941 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800942}
943
944static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena,
Roland Levillain2e50ecb2016-01-27 14:08:33 +0000945 HInvoke* invoke,
946 Primitive::Type type) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100947 bool can_call = kEmitCompilerReadBarrier &&
948 kUseBakerReadBarrier &&
949 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800950 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100951 (can_call
952 ? LocationSummary::kCallOnSlowPath
953 : LocationSummary::kNoCall),
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800954 kIntrinsified);
955 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
956 locations->SetInAt(1, Location::RequiresRegister());
957 locations->SetInAt(2, Location::RequiresRegister());
958 locations->SetInAt(3, Location::RequiresRegister());
959 locations->SetInAt(4, Location::RequiresRegister());
960
Roland Levillain2e50ecb2016-01-27 14:08:33 +0000961 // If heap poisoning is enabled, we don't want the unpoisoning
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100962 // operations to potentially clobber the output. Likewise when
963 // emitting a (Baker) read barrier, which may call.
964 Location::OutputOverlap overlaps =
965 ((kPoisonHeapReferences && type == Primitive::kPrimNot) || can_call)
Roland Levillain2e50ecb2016-01-27 14:08:33 +0000966 ? Location::kOutputOverlap
967 : Location::kNoOutputOverlap;
968 locations->SetOut(Location::RequiresRegister(), overlaps);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800969
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100970 // Temporary registers used in CAS. In the object case
971 // (UnsafeCASObject intrinsic), these are also used for
972 // card-marking, and possibly for (Baker) read barrier.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800973 locations->AddTemp(Location::RequiresRegister()); // Pointer.
974 locations->AddTemp(Location::RequiresRegister()); // Temp 1.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800975}
976
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100977static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARM* codegen) {
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800978 DCHECK_NE(type, Primitive::kPrimLong);
979
980 ArmAssembler* assembler = codegen->GetAssembler();
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100981 LocationSummary* locations = invoke->GetLocations();
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800982
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100983 Location out_loc = locations->Out();
984 Register out = out_loc.AsRegister<Register>(); // Boolean result.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800985
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100986 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
987 Location offset_loc = locations->InAt(2);
988 Register offset = offset_loc.AsRegisterPairLow<Register>(); // Offset (discard high 4B).
989 Register expected = locations->InAt(3).AsRegister<Register>(); // Expected.
990 Register value = locations->InAt(4).AsRegister<Register>(); // Value.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800991
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100992 Location tmp_ptr_loc = locations->GetTemp(0);
993 Register tmp_ptr = tmp_ptr_loc.AsRegister<Register>(); // Pointer to actual memory.
994 Register tmp = locations->GetTemp(1).AsRegister<Register>(); // Value in memory.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800995
996 if (type == Primitive::kPrimNot) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100997 // The only read barrier implementation supporting the
998 // UnsafeCASObject intrinsic is the Baker-style read barriers.
999 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1000
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001001 // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
1002 // object and scan the receiver at the next GC for nothing.
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001003 bool value_can_be_null = true; // TODO: Worth finding out this information?
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001004 codegen->MarkGCCard(tmp_ptr, tmp, base, value, value_can_be_null);
1005
1006 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1007 // Need to make sure the reference stored in the field is a to-space
1008 // one before attempting the CAS or the CAS could fail incorrectly.
1009 codegen->GenerateReferenceLoadWithBakerReadBarrier(
1010 invoke,
1011 out_loc, // Unused, used only as a "temporary" within the read barrier.
1012 base,
1013 /* offset */ 0u,
1014 /* index */ offset_loc,
1015 ScaleFactor::TIMES_1,
1016 tmp_ptr_loc,
1017 /* needs_null_check */ false,
1018 /* always_update_field */ true,
1019 &tmp);
1020 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001021 }
1022
1023 // Prevent reordering with prior memory operations.
Roland Levillain4bedb382016-01-12 12:01:04 +00001024 // Emit a DMB ISH instruction instead of an DMB ISHST one, as the
1025 // latter allows a preceding load to be delayed past the STXR
1026 // instruction below.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001027 __ dmb(ISH);
1028
1029 __ add(tmp_ptr, base, ShifterOperand(offset));
1030
Roland Levillain4d027112015-07-01 15:41:14 +01001031 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001032 __ PoisonHeapReference(expected);
1033 if (value == expected) {
1034 // Do not poison `value`, as it is the same register as
1035 // `expected`, which has just been poisoned.
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001036 } else {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001037 __ PoisonHeapReference(value);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001038 }
Roland Levillain4d027112015-07-01 15:41:14 +01001039 }
1040
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001041 // do {
1042 // tmp = [r_ptr] - expected;
1043 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
1044 // result = tmp != 0;
1045
1046 Label loop_head;
1047 __ Bind(&loop_head);
1048
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001049 __ ldrex(tmp, tmp_ptr);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001050
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001051 __ subs(tmp, tmp, ShifterOperand(expected));
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001052
1053 __ it(EQ, ItState::kItT);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001054 __ strex(tmp, value, tmp_ptr, EQ);
1055 __ cmp(tmp, ShifterOperand(1), EQ);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001056
1057 __ b(&loop_head, EQ);
1058
1059 __ dmb(ISH);
1060
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001061 __ rsbs(out, tmp, ShifterOperand(1));
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001062 __ it(CC);
1063 __ mov(out, ShifterOperand(0), CC);
Roland Levillain4d027112015-07-01 15:41:14 +01001064
1065 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001066 __ UnpoisonHeapReference(expected);
1067 if (value == expected) {
1068 // Do not unpoison `value`, as it is the same register as
1069 // `expected`, which has just been unpoisoned.
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001070 } else {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001071 __ UnpoisonHeapReference(value);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001072 }
Roland Levillain4d027112015-07-01 15:41:14 +01001073 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001074}
1075
Andreas Gampeca714582015-04-03 19:41:34 -07001076void IntrinsicLocationsBuilderARM::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001077 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001078}
Andreas Gampeca714582015-04-03 19:41:34 -07001079void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001080 // The only read barrier implementation supporting the
1081 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1082 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain985ff702015-10-23 13:25:35 +01001083 return;
1084 }
1085
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001086 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001087}
1088void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001089 GenCas(invoke, Primitive::kPrimInt, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001090}
1091void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001092 // The only read barrier implementation supporting the
1093 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1094 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01001095
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001096 GenCas(invoke, Primitive::kPrimNot, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001097}
1098
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001099void IntrinsicLocationsBuilderARM::VisitStringCompareTo(HInvoke* invoke) {
1100 // The inputs plus one temp.
1101 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001102 invoke->InputAt(1)->CanBeNull()
1103 ? LocationSummary::kCallOnSlowPath
1104 : LocationSummary::kNoCall,
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001105 kIntrinsified);
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001106 locations->SetInAt(0, Location::RequiresRegister());
1107 locations->SetInAt(1, Location::RequiresRegister());
1108 locations->AddTemp(Location::RequiresRegister());
1109 locations->AddTemp(Location::RequiresRegister());
1110 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001111 // Need temporary registers for String compression's feature.
1112 if (mirror::kUseStringCompression) {
1113 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001114 }
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001115 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001116}
1117
1118void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) {
1119 ArmAssembler* assembler = GetAssembler();
1120 LocationSummary* locations = invoke->GetLocations();
1121
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001122 Register str = locations->InAt(0).AsRegister<Register>();
1123 Register arg = locations->InAt(1).AsRegister<Register>();
1124 Register out = locations->Out().AsRegister<Register>();
1125
1126 Register temp0 = locations->GetTemp(0).AsRegister<Register>();
1127 Register temp1 = locations->GetTemp(1).AsRegister<Register>();
1128 Register temp2 = locations->GetTemp(2).AsRegister<Register>();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001129 Register temp3;
jessicahandojo05765752016-09-09 19:01:32 -07001130 if (mirror::kUseStringCompression) {
1131 temp3 = locations->GetTemp(3).AsRegister<Register>();
jessicahandojo05765752016-09-09 19:01:32 -07001132 }
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001133
1134 Label loop;
1135 Label find_char_diff;
1136 Label end;
jessicahandojo05765752016-09-09 19:01:32 -07001137 Label different_compression;
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001138
1139 // Get offsets of count and value fields within a string object.
1140 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1141 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1142
Nicolas Geoffray512e04d2015-03-27 17:21:24 +00001143 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +01001144 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001145
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001146 // Take slow path and throw if input can be and is null.
1147 SlowPathCode* slow_path = nullptr;
1148 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1149 if (can_slow_path) {
1150 slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
1151 codegen_->AddSlowPath(slow_path);
1152 __ CompareAndBranchIfZero(arg, slow_path->GetEntryLabel());
1153 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001154
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001155 // Reference equality check, return 0 if same reference.
1156 __ subs(out, str, ShifterOperand(arg));
1157 __ b(&end, EQ);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001158
jessicahandojo05765752016-09-09 19:01:32 -07001159 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001160 // Load `count` fields of this and argument strings.
jessicahandojo05765752016-09-09 19:01:32 -07001161 __ ldr(temp3, Address(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001162 __ ldr(temp2, Address(arg, count_offset));
1163 // Extract lengths from the `count` fields.
1164 __ Lsr(temp0, temp3, 1u);
1165 __ Lsr(temp1, temp2, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001166 } else {
1167 // Load lengths of this and argument strings.
1168 __ ldr(temp0, Address(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001169 __ ldr(temp1, Address(arg, count_offset));
jessicahandojo05765752016-09-09 19:01:32 -07001170 }
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001171 // out = length diff.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001172 __ subs(out, temp0, ShifterOperand(temp1));
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001173 // temp0 = min(len(str), len(arg)).
jessicahandojo05765752016-09-09 19:01:32 -07001174 __ it(GT);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001175 __ mov(temp0, ShifterOperand(temp1), GT);
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001176 // Shorter string is empty?
1177 __ CompareAndBranchIfZero(temp0, &end);
1178
jessicahandojo05765752016-09-09 19:01:32 -07001179 if (mirror::kUseStringCompression) {
1180 // Check if both strings using same compression style to use this comparison loop.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001181 __ eor(temp2, temp2, ShifterOperand(temp3));
1182 __ Lsrs(temp2, temp2, 1u);
1183 __ b(&different_compression, CS);
jessicahandojo05765752016-09-09 19:01:32 -07001184 // For string compression, calculate the number of bytes to compare (not chars).
1185 // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001186 __ Lsls(temp3, temp3, 31u); // Extract purely the compression flag.
1187 __ it(NE);
1188 __ add(temp0, temp0, ShifterOperand(temp0), NE);
jessicahandojo05765752016-09-09 19:01:32 -07001189 }
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001190
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001191 // Store offset of string value in preparation for comparison loop.
1192 __ mov(temp1, ShifterOperand(value_offset));
1193
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001194 // Assertions that must hold in order to compare multiple characters at a time.
1195 CHECK_ALIGNED(value_offset, 8);
1196 static_assert(IsAligned<8>(kObjectAlignment),
1197 "String data must be 8-byte aligned for unrolled CompareTo loop.");
1198
1199 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1200 DCHECK_EQ(char_size, 2u);
1201
jessicahandojo05765752016-09-09 19:01:32 -07001202 Label find_char_diff_2nd_cmp;
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001203 // Unrolled loop comparing 4x16-bit chars per iteration (ok because of string data alignment).
1204 __ Bind(&loop);
1205 __ ldr(IP, Address(str, temp1));
1206 __ ldr(temp2, Address(arg, temp1));
1207 __ cmp(IP, ShifterOperand(temp2));
1208 __ b(&find_char_diff, NE);
1209 __ add(temp1, temp1, ShifterOperand(char_size * 2));
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001210
1211 __ ldr(IP, Address(str, temp1));
1212 __ ldr(temp2, Address(arg, temp1));
1213 __ cmp(IP, ShifterOperand(temp2));
jessicahandojo05765752016-09-09 19:01:32 -07001214 __ b(&find_char_diff_2nd_cmp, NE);
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001215 __ add(temp1, temp1, ShifterOperand(char_size * 2));
jessicahandojo05765752016-09-09 19:01:32 -07001216 // With string compression, we have compared 8 bytes, otherwise 4 chars.
1217 __ subs(temp0, temp0, ShifterOperand(mirror::kUseStringCompression ? 8 : 4));
1218 __ b(&loop, HI);
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001219 __ b(&end);
1220
jessicahandojo05765752016-09-09 19:01:32 -07001221 __ Bind(&find_char_diff_2nd_cmp);
1222 if (mirror::kUseStringCompression) {
1223 __ subs(temp0, temp0, ShifterOperand(4)); // 4 bytes previously compared.
1224 __ b(&end, LS); // Was the second comparison fully beyond the end?
1225 } else {
1226 // Without string compression, we can start treating temp0 as signed
1227 // and rely on the signed comparison below.
1228 __ sub(temp0, temp0, ShifterOperand(2));
1229 }
1230
1231 // Find the single character difference.
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001232 __ Bind(&find_char_diff);
1233 // Get the bit position of the first character that differs.
1234 __ eor(temp1, temp2, ShifterOperand(IP));
1235 __ rbit(temp1, temp1);
1236 __ clz(temp1, temp1);
1237
jessicahandojo05765752016-09-09 19:01:32 -07001238 // temp0 = number of characters remaining to compare.
1239 // (Without string compression, it could be < 1 if a difference is found by the second CMP
1240 // in the comparison loop, and after the end of the shorter string data).
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001241
jessicahandojo05765752016-09-09 19:01:32 -07001242 // Without string compression (temp1 >> 4) = character where difference occurs between the last
1243 // two words compared, in the interval [0,1].
1244 // (0 for low half-word different, 1 for high half-word different).
1245 // With string compression, (temp1 << 3) = byte where the difference occurs,
1246 // in the interval [0,3].
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001247
jessicahandojo05765752016-09-09 19:01:32 -07001248 // If temp0 <= (temp1 >> (kUseStringCompression ? 3 : 4)), the difference occurs outside
1249 // the remaining string data, so just return length diff (out).
1250 // The comparison is unsigned for string compression, otherwise signed.
1251 __ cmp(temp0, ShifterOperand(temp1, LSR, mirror::kUseStringCompression ? 3 : 4));
1252 __ b(&end, mirror::kUseStringCompression ? LS : LE);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001253
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001254 // Extract the characters and calculate the difference.
jessicahandojo05765752016-09-09 19:01:32 -07001255 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001256 // For compressed strings we need to clear 0x7 from temp1, for uncompressed we need to clear
1257 // 0xf. We also need to prepare the character extraction mask `uncompressed ? 0xffffu : 0xffu`.
1258 // The compression flag is now in the highest bit of temp3, so let's play some tricks.
1259 __ orr(temp3, temp3, ShifterOperand(0xffu << 23)); // uncompressed ? 0xff800000u : 0x7ff80000u
1260 __ bic(temp1, temp1, ShifterOperand(temp3, LSR, 31 - 3)); // &= ~(uncompressed ? 0xfu : 0x7u)
1261 __ Asr(temp3, temp3, 7u); // uncompressed ? 0xffff0000u : 0xff0000u.
1262 __ Lsr(temp2, temp2, temp1); // Extract second character.
1263 __ Lsr(temp3, temp3, 16u); // uncompressed ? 0xffffu : 0xffu
1264 __ Lsr(out, IP, temp1); // Extract first character.
1265 __ and_(temp2, temp2, ShifterOperand(temp3));
1266 __ and_(out, out, ShifterOperand(temp3));
1267 } else {
1268 __ bic(temp1, temp1, ShifterOperand(0xf));
1269 __ Lsr(temp2, temp2, temp1);
1270 __ Lsr(out, IP, temp1);
1271 __ movt(temp2, 0);
1272 __ movt(out, 0);
jessicahandojo05765752016-09-09 19:01:32 -07001273 }
jessicahandojo05765752016-09-09 19:01:32 -07001274
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001275 __ sub(out, out, ShifterOperand(temp2));
jessicahandojo05765752016-09-09 19:01:32 -07001276
1277 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001278 __ b(&end);
1279 __ Bind(&different_compression);
1280
1281 // Comparison for different compression style.
jessicahandojo05765752016-09-09 19:01:32 -07001282 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
1283 DCHECK_EQ(c_char_size, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001284
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001285 // We want to free up the temp3, currently holding `str.count`, for comparison.
1286 // So, we move it to the bottom bit of the iteration count `temp0` which we tnen
1287 // need to treat as unsigned. Start by freeing the bit with an ADD and continue
1288 // further down by a LSRS+SBC which will flip the meaning of the flag but allow
1289 // `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
1290 __ add(temp0, temp0, ShifterOperand(temp0)); // Unlike LSL, this ADD is always 16-bit.
1291 // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
1292 __ mov(temp1, ShifterOperand(str));
1293 __ mov(temp2, ShifterOperand(arg));
1294 __ Lsrs(temp3, temp3, 1u); // Continue the move of the compression flag.
1295 __ it(CS, kItThen); // Interleave with selection of temp1 and temp2.
1296 __ mov(temp1, ShifterOperand(arg), CS); // Preserves flags.
1297 __ mov(temp2, ShifterOperand(str), CS); // Preserves flags.
1298 __ sbc(temp0, temp0, ShifterOperand(0)); // Complete the move of the compression flag.
jessicahandojo05765752016-09-09 19:01:32 -07001299
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001300 // Adjust temp1 and temp2 from string pointers to data pointers.
1301 __ add(temp1, temp1, ShifterOperand(value_offset));
1302 __ add(temp2, temp2, ShifterOperand(value_offset));
1303
1304 Label different_compression_loop;
1305 Label different_compression_diff;
1306
1307 // Main loop for different compression.
1308 __ Bind(&different_compression_loop);
1309 __ ldrb(IP, Address(temp1, c_char_size, Address::PostIndex));
1310 __ ldrh(temp3, Address(temp2, char_size, Address::PostIndex));
1311 __ cmp(IP, ShifterOperand(temp3));
1312 __ b(&different_compression_diff, NE);
1313 __ subs(temp0, temp0, ShifterOperand(2));
1314 __ b(&different_compression_loop, HI);
jessicahandojo05765752016-09-09 19:01:32 -07001315 __ b(&end);
1316
1317 // Calculate the difference.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001318 __ Bind(&different_compression_diff);
1319 __ sub(out, IP, ShifterOperand(temp3));
1320 // Flip the difference if the `arg` is compressed.
1321 // `temp0` contains inverted `str` compression flag, i.e the same as `arg` compression flag.
1322 __ Lsrs(temp0, temp0, 1u);
1323 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1324 "Expecting 0=compressed, 1=uncompressed");
1325 __ it(CC);
1326 __ rsb(out, out, ShifterOperand(0), CC);
jessicahandojo05765752016-09-09 19:01:32 -07001327 }
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001328
1329 __ Bind(&end);
1330
1331 if (can_slow_path) {
1332 __ Bind(slow_path->GetExitLabel());
1333 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001334}
1335
Agi Csaki289cd552015-08-18 17:10:38 -07001336void IntrinsicLocationsBuilderARM::VisitStringEquals(HInvoke* invoke) {
1337 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1338 LocationSummary::kNoCall,
1339 kIntrinsified);
1340 InvokeRuntimeCallingConvention calling_convention;
1341 locations->SetInAt(0, Location::RequiresRegister());
1342 locations->SetInAt(1, Location::RequiresRegister());
1343 // Temporary registers to store lengths of strings and for calculations.
1344 // Using instruction cbz requires a low register, so explicitly set a temp to be R0.
1345 locations->AddTemp(Location::RegisterLocation(R0));
1346 locations->AddTemp(Location::RequiresRegister());
1347 locations->AddTemp(Location::RequiresRegister());
1348
1349 locations->SetOut(Location::RequiresRegister());
1350}
1351
1352void IntrinsicCodeGeneratorARM::VisitStringEquals(HInvoke* invoke) {
1353 ArmAssembler* assembler = GetAssembler();
1354 LocationSummary* locations = invoke->GetLocations();
1355
1356 Register str = locations->InAt(0).AsRegister<Register>();
1357 Register arg = locations->InAt(1).AsRegister<Register>();
1358 Register out = locations->Out().AsRegister<Register>();
1359
1360 Register temp = locations->GetTemp(0).AsRegister<Register>();
1361 Register temp1 = locations->GetTemp(1).AsRegister<Register>();
1362 Register temp2 = locations->GetTemp(2).AsRegister<Register>();
1363
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001364 Label loop;
Agi Csaki289cd552015-08-18 17:10:38 -07001365 Label end;
1366 Label return_true;
1367 Label return_false;
Anton Kirilov6f644202017-02-27 18:29:45 +00001368 Label* final_label = codegen_->GetFinalLabel(invoke, &end);
Agi Csaki289cd552015-08-18 17:10:38 -07001369
1370 // Get offsets of count, value, and class fields within a string object.
1371 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
1372 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1373 const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
1374
1375 // Note that the null check must have been done earlier.
1376 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1377
Vladimir Marko53b52002016-05-24 19:30:45 +01001378 StringEqualsOptimizations optimizations(invoke);
1379 if (!optimizations.GetArgumentNotNull()) {
1380 // Check if input is null, return false if it is.
1381 __ CompareAndBranchIfZero(arg, &return_false);
1382 }
Agi Csaki289cd552015-08-18 17:10:38 -07001383
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001384 // Reference equality check, return true if same reference.
1385 __ cmp(str, ShifterOperand(arg));
1386 __ b(&return_true, EQ);
1387
Vladimir Marko53b52002016-05-24 19:30:45 +01001388 if (!optimizations.GetArgumentIsString()) {
1389 // Instanceof check for the argument by comparing class fields.
1390 // All string objects must have the same type since String cannot be subclassed.
1391 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1392 // If the argument is a string object, its class field must be equal to receiver's class field.
1393 __ ldr(temp, Address(str, class_offset));
1394 __ ldr(temp1, Address(arg, class_offset));
1395 __ cmp(temp, ShifterOperand(temp1));
1396 __ b(&return_false, NE);
1397 }
Agi Csaki289cd552015-08-18 17:10:38 -07001398
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001399 // Load `count` fields of this and argument strings.
Agi Csaki289cd552015-08-18 17:10:38 -07001400 __ ldr(temp, Address(str, count_offset));
1401 __ ldr(temp1, Address(arg, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001402 // Check if `count` fields are equal, return false if they're not.
jessicahandojo05765752016-09-09 19:01:32 -07001403 // Also compares the compression style, if differs return false.
Agi Csaki289cd552015-08-18 17:10:38 -07001404 __ cmp(temp, ShifterOperand(temp1));
1405 __ b(&return_false, NE);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001406 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
1407 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1408 "Expecting 0=compressed, 1=uncompressed");
Agi Csaki289cd552015-08-18 17:10:38 -07001409 __ cbz(temp, &return_true);
Agi Csaki289cd552015-08-18 17:10:38 -07001410
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001411 // Assertions that must hold in order to compare strings 4 bytes at a time.
Agi Csaki289cd552015-08-18 17:10:38 -07001412 DCHECK_ALIGNED(value_offset, 4);
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001413 static_assert(IsAligned<4>(kObjectAlignment), "String data must be aligned for fast compare.");
Agi Csaki289cd552015-08-18 17:10:38 -07001414
jessicahandojo05765752016-09-09 19:01:32 -07001415 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001416 // For string compression, calculate the number of bytes to compare (not chars).
1417 // This could in theory exceed INT32_MAX, so treat temp as unsigned.
1418 __ Lsrs(temp, temp, 1u); // Extract length and check compression flag.
1419 __ it(CS); // If uncompressed,
1420 __ add(temp, temp, ShifterOperand(temp), CS); // double the byte count.
jessicahandojo05765752016-09-09 19:01:32 -07001421 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001422
1423 // Store offset of string value in preparation for comparison loop.
jessicahandojo05765752016-09-09 19:01:32 -07001424 __ LoadImmediate(temp1, value_offset);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001425
1426 // Loop to compare strings 4 bytes at a time starting at the front of the string.
1427 // Ok to do this because strings are zero-padded to kObjectAlignment.
Agi Csaki289cd552015-08-18 17:10:38 -07001428 __ Bind(&loop);
1429 __ ldr(out, Address(str, temp1));
1430 __ ldr(temp2, Address(arg, temp1));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001431 __ add(temp1, temp1, ShifterOperand(sizeof(uint32_t)));
Agi Csaki289cd552015-08-18 17:10:38 -07001432 __ cmp(out, ShifterOperand(temp2));
1433 __ b(&return_false, NE);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001434 // With string compression, we have compared 4 bytes, otherwise 2 chars.
1435 __ subs(temp, temp, ShifterOperand(mirror::kUseStringCompression ? 4 : 2));
1436 __ b(&loop, HI);
Agi Csaki289cd552015-08-18 17:10:38 -07001437
1438 // Return true and exit the function.
1439 // If loop does not result in returning false, we return true.
1440 __ Bind(&return_true);
1441 __ LoadImmediate(out, 1);
Anton Kirilov6f644202017-02-27 18:29:45 +00001442 __ b(final_label);
Agi Csaki289cd552015-08-18 17:10:38 -07001443
1444 // Return false and exit the function.
1445 __ Bind(&return_false);
1446 __ LoadImmediate(out, 0);
Anton Kirilov6f644202017-02-27 18:29:45 +00001447
1448 if (end.IsLinked()) {
1449 __ Bind(&end);
1450 }
Agi Csaki289cd552015-08-18 17:10:38 -07001451}
1452
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001453static void GenerateVisitStringIndexOf(HInvoke* invoke,
1454 ArmAssembler* assembler,
1455 CodeGeneratorARM* codegen,
1456 ArenaAllocator* allocator,
1457 bool start_at_zero) {
1458 LocationSummary* locations = invoke->GetLocations();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001459
1460 // Note that the null check must have been done earlier.
1461 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1462
1463 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001464 // 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 -07001465 SlowPathCode* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001466 HInstruction* code_point = invoke->InputAt(1);
1467 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01001468 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) >
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001469 std::numeric_limits<uint16_t>::max()) {
1470 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1471 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1472 slow_path = new (allocator) IntrinsicSlowPathARM(invoke);
1473 codegen->AddSlowPath(slow_path);
1474 __ b(slow_path->GetEntryLabel());
1475 __ Bind(slow_path->GetExitLabel());
1476 return;
1477 }
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001478 } else if (code_point->GetType() != Primitive::kPrimChar) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001479 Register char_reg = locations->InAt(1).AsRegister<Register>();
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001480 // 0xffff is not modified immediate but 0x10000 is, so use `>= 0x10000` instead of `> 0xffff`.
1481 __ cmp(char_reg,
1482 ShifterOperand(static_cast<uint32_t>(std::numeric_limits<uint16_t>::max()) + 1));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001483 slow_path = new (allocator) IntrinsicSlowPathARM(invoke);
1484 codegen->AddSlowPath(slow_path);
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001485 __ b(slow_path->GetEntryLabel(), HS);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001486 }
1487
1488 if (start_at_zero) {
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001489 Register tmp_reg = locations->GetTemp(0).AsRegister<Register>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001490 DCHECK_EQ(tmp_reg, R2);
1491 // Start-index = 0.
1492 __ LoadImmediate(tmp_reg, 0);
1493 }
1494
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01001495 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
Roland Levillain42ad2882016-02-29 18:26:54 +00001496 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001497
1498 if (slow_path != nullptr) {
1499 __ Bind(slow_path->GetExitLabel());
1500 }
1501}
1502
1503void IntrinsicLocationsBuilderARM::VisitStringIndexOf(HInvoke* invoke) {
1504 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001505 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001506 kIntrinsified);
1507 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1508 // best to align the inputs accordingly.
1509 InvokeRuntimeCallingConvention calling_convention;
1510 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1511 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1512 locations->SetOut(Location::RegisterLocation(R0));
1513
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001514 // Need to send start-index=0.
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001515 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1516}
1517
1518void IntrinsicCodeGeneratorARM::VisitStringIndexOf(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001519 GenerateVisitStringIndexOf(
1520 invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001521}
1522
1523void IntrinsicLocationsBuilderARM::VisitStringIndexOfAfter(HInvoke* invoke) {
1524 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001525 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001526 kIntrinsified);
1527 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1528 // best to align the inputs accordingly.
1529 InvokeRuntimeCallingConvention calling_convention;
1530 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1531 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1532 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1533 locations->SetOut(Location::RegisterLocation(R0));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001534}
1535
1536void IntrinsicCodeGeneratorARM::VisitStringIndexOfAfter(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001537 GenerateVisitStringIndexOf(
1538 invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001539}
1540
Jeff Hao848f70a2014-01-15 13:49:50 -08001541void IntrinsicLocationsBuilderARM::VisitStringNewStringFromBytes(HInvoke* invoke) {
1542 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001543 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001544 kIntrinsified);
1545 InvokeRuntimeCallingConvention calling_convention;
1546 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1547 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1548 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1549 locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
1550 locations->SetOut(Location::RegisterLocation(R0));
1551}
1552
1553void IntrinsicCodeGeneratorARM::VisitStringNewStringFromBytes(HInvoke* invoke) {
1554 ArmAssembler* assembler = GetAssembler();
1555 LocationSummary* locations = invoke->GetLocations();
1556
1557 Register byte_array = locations->InAt(0).AsRegister<Register>();
1558 __ cmp(byte_array, ShifterOperand(0));
Andreas Gampe85b62f22015-09-09 13:15:38 -07001559 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
Jeff Hao848f70a2014-01-15 13:49:50 -08001560 codegen_->AddSlowPath(slow_path);
1561 __ b(slow_path->GetEntryLabel(), EQ);
1562
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01001563 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001564 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001565 __ Bind(slow_path->GetExitLabel());
1566}
1567
1568void IntrinsicLocationsBuilderARM::VisitStringNewStringFromChars(HInvoke* invoke) {
1569 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001570 LocationSummary::kCallOnMainOnly,
Jeff Hao848f70a2014-01-15 13:49:50 -08001571 kIntrinsified);
1572 InvokeRuntimeCallingConvention calling_convention;
1573 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1574 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1575 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1576 locations->SetOut(Location::RegisterLocation(R0));
1577}
1578
1579void IntrinsicCodeGeneratorARM::VisitStringNewStringFromChars(HInvoke* invoke) {
Roland Levillaincc3839c2016-02-29 16:23:48 +00001580 // No need to emit code checking whether `locations->InAt(2)` is a null
1581 // pointer, as callers of the native method
1582 //
1583 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1584 //
1585 // all include a null check on `data` before calling that method.
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01001586 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
Roland Levillainf969a202016-03-09 16:14:00 +00001587 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001588}
1589
1590void IntrinsicLocationsBuilderARM::VisitStringNewStringFromString(HInvoke* invoke) {
1591 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001592 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001593 kIntrinsified);
1594 InvokeRuntimeCallingConvention calling_convention;
1595 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1596 locations->SetOut(Location::RegisterLocation(R0));
1597}
1598
1599void IntrinsicCodeGeneratorARM::VisitStringNewStringFromString(HInvoke* invoke) {
1600 ArmAssembler* assembler = GetAssembler();
1601 LocationSummary* locations = invoke->GetLocations();
1602
1603 Register string_to_copy = locations->InAt(0).AsRegister<Register>();
1604 __ cmp(string_to_copy, ShifterOperand(0));
Andreas Gampe85b62f22015-09-09 13:15:38 -07001605 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
Jeff Hao848f70a2014-01-15 13:49:50 -08001606 codegen_->AddSlowPath(slow_path);
1607 __ b(slow_path->GetEntryLabel(), EQ);
1608
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01001609 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001610 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01001611
Jeff Hao848f70a2014-01-15 13:49:50 -08001612 __ Bind(slow_path->GetExitLabel());
1613}
1614
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001615void IntrinsicLocationsBuilderARM::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01001616 // The only read barrier implementation supporting the
1617 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1618 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain3d312422016-06-23 13:53:42 +01001619 return;
1620 }
1621
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001622 CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
1623 LocationSummary* locations = invoke->GetLocations();
1624 if (locations == nullptr) {
1625 return;
1626 }
1627
1628 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1629 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
1630 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1631
1632 if (src_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(src_pos->GetValue())) {
1633 locations->SetInAt(1, Location::RequiresRegister());
1634 }
1635 if (dest_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(dest_pos->GetValue())) {
1636 locations->SetInAt(3, Location::RequiresRegister());
1637 }
1638 if (length != nullptr && !assembler_->ShifterOperandCanAlwaysHold(length->GetValue())) {
1639 locations->SetInAt(4, Location::RequiresRegister());
1640 }
Roland Levillain0b671c02016-08-19 12:02:34 +01001641 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1642 // Temporary register IP cannot be used in
Roland Levillain16d9f942016-08-25 17:27:56 +01001643 // ReadBarrierSystemArrayCopySlowPathARM (because that register
Roland Levillain0b671c02016-08-19 12:02:34 +01001644 // is clobbered by ReadBarrierMarkRegX entry points). Get an extra
1645 // temporary register from the register allocator.
1646 locations->AddTemp(Location::RequiresRegister());
1647 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001648}
1649
1650static void CheckPosition(ArmAssembler* assembler,
1651 Location pos,
1652 Register input,
1653 Location length,
1654 SlowPathCode* slow_path,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001655 Register temp,
1656 bool length_is_input_length = false) {
1657 // Where is the length in the Array?
1658 const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
1659
1660 if (pos.IsConstant()) {
1661 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
1662 if (pos_const == 0) {
1663 if (!length_is_input_length) {
1664 // Check that length(input) >= length.
1665 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
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 {
1674 // Check that length(input) >= pos.
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01001675 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
1676 __ subs(temp, temp, ShifterOperand(pos_const));
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001677 __ b(slow_path->GetEntryLabel(), LT);
1678
1679 // Check that (length(input) - pos) >= length.
1680 if (length.IsConstant()) {
1681 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1682 } else {
1683 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1684 }
1685 __ b(slow_path->GetEntryLabel(), LT);
1686 }
1687 } else if (length_is_input_length) {
1688 // The only way the copy can succeed is if pos is zero.
1689 Register pos_reg = pos.AsRegister<Register>();
1690 __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
1691 } else {
1692 // Check that pos >= 0.
1693 Register pos_reg = pos.AsRegister<Register>();
1694 __ cmp(pos_reg, ShifterOperand(0));
1695 __ b(slow_path->GetEntryLabel(), LT);
1696
1697 // Check that pos <= length(input).
1698 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
1699 __ subs(temp, temp, ShifterOperand(pos_reg));
1700 __ b(slow_path->GetEntryLabel(), LT);
1701
1702 // Check that (length(input) - pos) >= length.
1703 if (length.IsConstant()) {
1704 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1705 } else {
1706 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1707 }
1708 __ b(slow_path->GetEntryLabel(), LT);
1709 }
1710}
1711
1712void IntrinsicCodeGeneratorARM::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01001713 // The only read barrier implementation supporting the
1714 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1715 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01001716
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001717 ArmAssembler* assembler = GetAssembler();
1718 LocationSummary* locations = invoke->GetLocations();
1719
1720 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1721 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
1722 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
1723 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Roland Levillain0b671c02016-08-19 12:02:34 +01001724 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001725
1726 Register src = locations->InAt(0).AsRegister<Register>();
1727 Location src_pos = locations->InAt(1);
1728 Register dest = locations->InAt(2).AsRegister<Register>();
1729 Location dest_pos = locations->InAt(3);
1730 Location length = locations->InAt(4);
Roland Levillain0b671c02016-08-19 12:02:34 +01001731 Location temp1_loc = locations->GetTemp(0);
1732 Register temp1 = temp1_loc.AsRegister<Register>();
1733 Location temp2_loc = locations->GetTemp(1);
1734 Register temp2 = temp2_loc.AsRegister<Register>();
1735 Location temp3_loc = locations->GetTemp(2);
1736 Register temp3 = temp3_loc.AsRegister<Register>();
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001737
Roland Levillain0b671c02016-08-19 12:02:34 +01001738 SlowPathCode* intrinsic_slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
1739 codegen_->AddSlowPath(intrinsic_slow_path);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001740
Roland Levillainebea3d22016-04-12 15:42:57 +01001741 Label conditions_on_positions_validated;
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001742 SystemArrayCopyOptimizations optimizations(invoke);
1743
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001744 // If source and destination are the same, we go to slow path if we need to do
1745 // forward copying.
1746 if (src_pos.IsConstant()) {
1747 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
1748 if (dest_pos.IsConstant()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001749 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
1750 if (optimizations.GetDestinationIsSource()) {
1751 // Checked when building locations.
1752 DCHECK_GE(src_pos_constant, dest_pos_constant);
1753 } else if (src_pos_constant < dest_pos_constant) {
1754 __ cmp(src, ShifterOperand(dest));
Roland Levillain0b671c02016-08-19 12:02:34 +01001755 __ b(intrinsic_slow_path->GetEntryLabel(), EQ);
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001756 }
1757
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001758 // Checked when building locations.
1759 DCHECK(!optimizations.GetDestinationIsSource()
1760 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
1761 } else {
1762 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001763 __ cmp(src, ShifterOperand(dest));
Roland Levillainebea3d22016-04-12 15:42:57 +01001764 __ b(&conditions_on_positions_validated, NE);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001765 }
1766 __ cmp(dest_pos.AsRegister<Register>(), ShifterOperand(src_pos_constant));
Roland Levillain0b671c02016-08-19 12:02:34 +01001767 __ b(intrinsic_slow_path->GetEntryLabel(), GT);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001768 }
1769 } else {
1770 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001771 __ cmp(src, ShifterOperand(dest));
Roland Levillainebea3d22016-04-12 15:42:57 +01001772 __ b(&conditions_on_positions_validated, NE);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001773 }
1774 if (dest_pos.IsConstant()) {
1775 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
1776 __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos_constant));
1777 } else {
1778 __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos.AsRegister<Register>()));
1779 }
Roland Levillain0b671c02016-08-19 12:02:34 +01001780 __ b(intrinsic_slow_path->GetEntryLabel(), LT);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001781 }
1782
Roland Levillainebea3d22016-04-12 15:42:57 +01001783 __ Bind(&conditions_on_positions_validated);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001784
1785 if (!optimizations.GetSourceIsNotNull()) {
1786 // Bail out if the source is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01001787 __ CompareAndBranchIfZero(src, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001788 }
1789
1790 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
1791 // Bail out if the destination is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01001792 __ CompareAndBranchIfZero(dest, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001793 }
1794
1795 // If the length is negative, bail out.
1796 // We have already checked in the LocationsBuilder for the constant case.
1797 if (!length.IsConstant() &&
1798 !optimizations.GetCountIsSourceLength() &&
1799 !optimizations.GetCountIsDestinationLength()) {
1800 __ cmp(length.AsRegister<Register>(), ShifterOperand(0));
Roland Levillain0b671c02016-08-19 12:02:34 +01001801 __ b(intrinsic_slow_path->GetEntryLabel(), LT);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001802 }
1803
1804 // Validity checks: source.
1805 CheckPosition(assembler,
1806 src_pos,
1807 src,
1808 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01001809 intrinsic_slow_path,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001810 temp1,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001811 optimizations.GetCountIsSourceLength());
1812
1813 // Validity checks: dest.
1814 CheckPosition(assembler,
1815 dest_pos,
1816 dest,
1817 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01001818 intrinsic_slow_path,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001819 temp1,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001820 optimizations.GetCountIsDestinationLength());
1821
1822 if (!optimizations.GetDoesNotNeedTypeCheck()) {
1823 // Check whether all elements of the source array are assignable to the component
1824 // type of the destination array. We do two checks: the classes are the same,
1825 // or the destination is Object[]. If none of these checks succeed, we go to the
1826 // slow path.
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001827
Roland Levillain0b671c02016-08-19 12:02:34 +01001828 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1829 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1830 // /* HeapReference<Class> */ temp1 = src->klass_
1831 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1832 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
1833 // Bail out if the source is not a non primitive array.
1834 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1835 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1836 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
1837 __ CompareAndBranchIfZero(temp1, intrinsic_slow_path->GetEntryLabel());
1838 // If heap poisoning is enabled, `temp1` has been unpoisoned
1839 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1840 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
1841 __ LoadFromOffset(kLoadUnsignedHalfword, temp1, temp1, primitive_offset);
1842 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1843 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001844 }
Roland Levillain0b671c02016-08-19 12:02:34 +01001845
1846 // /* HeapReference<Class> */ temp1 = dest->klass_
1847 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1848 invoke, temp1_loc, dest, class_offset, temp2_loc, /* needs_null_check */ false);
1849
1850 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
1851 // Bail out if the destination is not a non primitive array.
1852 //
1853 // Register `temp1` is not trashed by the read barrier emitted
1854 // by GenerateFieldLoadWithBakerReadBarrier below, as that
1855 // method produces a call to a ReadBarrierMarkRegX entry point,
1856 // which saves all potentially live registers, including
1857 // temporaries such a `temp1`.
1858 // /* HeapReference<Class> */ temp2 = temp1->component_type_
1859 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1860 invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
1861 __ CompareAndBranchIfZero(temp2, intrinsic_slow_path->GetEntryLabel());
1862 // If heap poisoning is enabled, `temp2` has been unpoisoned
1863 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1864 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
1865 __ LoadFromOffset(kLoadUnsignedHalfword, temp2, temp2, primitive_offset);
1866 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1867 __ CompareAndBranchIfNonZero(temp2, intrinsic_slow_path->GetEntryLabel());
1868 }
1869
1870 // For the same reason given earlier, `temp1` is not trashed by the
1871 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
1872 // /* HeapReference<Class> */ temp2 = src->klass_
1873 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1874 invoke, temp2_loc, src, class_offset, temp3_loc, /* needs_null_check */ false);
1875 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
1876 __ cmp(temp1, ShifterOperand(temp2));
1877
1878 if (optimizations.GetDestinationIsTypedObjectArray()) {
1879 Label do_copy;
1880 __ b(&do_copy, EQ);
1881 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1882 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1883 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
1884 // /* HeapReference<Class> */ temp1 = temp1->super_class_
1885 // We do not need to emit a read barrier for the following
1886 // heap reference load, as `temp1` is only used in a
1887 // comparison with null below, and this reference is not
1888 // kept afterwards.
1889 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
1890 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
1891 __ Bind(&do_copy);
1892 } else {
1893 __ b(intrinsic_slow_path->GetEntryLabel(), NE);
1894 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001895 } else {
Roland Levillain0b671c02016-08-19 12:02:34 +01001896 // Non read barrier code.
1897
1898 // /* HeapReference<Class> */ temp1 = dest->klass_
1899 __ LoadFromOffset(kLoadWord, temp1, dest, class_offset);
1900 // /* HeapReference<Class> */ temp2 = src->klass_
1901 __ LoadFromOffset(kLoadWord, temp2, src, class_offset);
1902 bool did_unpoison = false;
1903 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
1904 !optimizations.GetSourceIsNonPrimitiveArray()) {
1905 // One or two of the references need to be unpoisoned. Unpoison them
1906 // both to make the identity check valid.
1907 __ MaybeUnpoisonHeapReference(temp1);
1908 __ MaybeUnpoisonHeapReference(temp2);
1909 did_unpoison = true;
1910 }
1911
1912 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
1913 // Bail out if the destination is not a non primitive array.
1914 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1915 __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
1916 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1917 __ MaybeUnpoisonHeapReference(temp3);
1918 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
1919 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1920 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1921 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
1922 }
1923
1924 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1925 // Bail out if the source is not a non primitive array.
1926 // /* HeapReference<Class> */ temp3 = temp2->component_type_
1927 __ LoadFromOffset(kLoadWord, temp3, temp2, component_offset);
1928 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1929 __ MaybeUnpoisonHeapReference(temp3);
1930 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
1931 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1932 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1933 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
1934 }
1935
1936 __ cmp(temp1, ShifterOperand(temp2));
1937
1938 if (optimizations.GetDestinationIsTypedObjectArray()) {
1939 Label do_copy;
1940 __ b(&do_copy, EQ);
1941 if (!did_unpoison) {
1942 __ MaybeUnpoisonHeapReference(temp1);
1943 }
1944 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1945 __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
1946 __ MaybeUnpoisonHeapReference(temp1);
1947 // /* HeapReference<Class> */ temp1 = temp1->super_class_
1948 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
1949 // No need to unpoison the result, we're comparing against null.
1950 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
1951 __ Bind(&do_copy);
1952 } else {
1953 __ b(intrinsic_slow_path->GetEntryLabel(), NE);
1954 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001955 }
1956 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1957 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
1958 // Bail out if the source is not a non primitive array.
Roland Levillain0b671c02016-08-19 12:02:34 +01001959 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1960 // /* HeapReference<Class> */ temp1 = src->klass_
1961 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1962 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
1963 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1964 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1965 invoke, temp3_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
1966 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1967 // If heap poisoning is enabled, `temp3` has been unpoisoned
1968 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1969 } else {
1970 // /* HeapReference<Class> */ temp1 = src->klass_
1971 __ LoadFromOffset(kLoadWord, temp1, src, class_offset);
1972 __ MaybeUnpoisonHeapReference(temp1);
1973 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1974 __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
1975 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1976 __ MaybeUnpoisonHeapReference(temp3);
1977 }
1978 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001979 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1980 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Roland Levillain0b671c02016-08-19 12:02:34 +01001981 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001982 }
1983
Roland Levillain1663d162017-03-17 15:15:21 +00001984 if (length.IsConstant() && length.GetConstant()->AsIntConstant()->GetValue() == 0) {
1985 // Null constant length: not need to emit the loop code at all.
Roland Levillain0b671c02016-08-19 12:02:34 +01001986 } else {
Roland Levillain1663d162017-03-17 15:15:21 +00001987 Label done;
1988 const Primitive::Type type = Primitive::kPrimNot;
1989 const int32_t element_size = Primitive::ComponentSize(type);
1990
1991 if (length.IsRegister()) {
1992 // Don't enter the copy loop if the length is null.
1993 __ CompareAndBranchIfZero(length.AsRegister<Register>(), &done);
1994 }
1995
1996 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1997 // TODO: Also convert this intrinsic to the IsGcMarking strategy?
1998
1999 // SystemArrayCopy implementation for Baker read barriers (see
2000 // also CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier):
2001 //
2002 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
2003 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
2004 // bool is_gray = (rb_state == ReadBarrier::GrayState());
2005 // if (is_gray) {
2006 // // Slow-path copy.
2007 // do {
2008 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
2009 // } while (src_ptr != end_ptr)
2010 // } else {
2011 // // Fast-path copy.
2012 // do {
2013 // *dest_ptr++ = *src_ptr++;
2014 // } while (src_ptr != end_ptr)
2015 // }
2016
2017 // /* int32_t */ monitor = src->monitor_
2018 __ LoadFromOffset(kLoadWord, temp2, src, monitor_offset);
2019 // /* LockWord */ lock_word = LockWord(monitor)
2020 static_assert(sizeof(LockWord) == sizeof(int32_t),
2021 "art::LockWord and int32_t have different sizes.");
2022
2023 // Introduce a dependency on the lock_word including the rb_state,
2024 // which shall prevent load-load reordering without using
2025 // a memory barrier (which would be more expensive).
2026 // `src` is unchanged by this operation, but its value now depends
2027 // on `temp2`.
2028 __ add(src, src, ShifterOperand(temp2, LSR, 32));
2029
2030 // Compute the base source address in `temp1`.
2031 // Note that `temp1` (the base source address) is computed from
2032 // `src` (and `src_pos`) here, and thus honors the artificial
2033 // dependency of `src` on `temp2`.
2034 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2035 // Compute the end source address in `temp3`.
2036 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2037 // The base destination address is computed later, as `temp2` is
2038 // used for intermediate computations.
2039
2040 // Slow path used to copy array when `src` is gray.
2041 // Note that the base destination address is computed in `temp2`
2042 // by the slow path code.
2043 SlowPathCode* read_barrier_slow_path =
2044 new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathARM(invoke);
2045 codegen_->AddSlowPath(read_barrier_slow_path);
2046
2047 // Given the numeric representation, it's enough to check the low bit of the
2048 // rb_state. We do that by shifting the bit out of the lock word with LSRS
2049 // which can be a 16-bit instruction unlike the TST immediate.
2050 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
2051 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
2052 __ Lsrs(temp2, temp2, LockWord::kReadBarrierStateShift + 1);
2053 // Carry flag is the last bit shifted out by LSRS.
2054 __ b(read_barrier_slow_path->GetEntryLabel(), CS);
2055
2056 // Fast-path copy.
2057 // Compute the base destination address in `temp2`.
2058 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2059 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2060 // poison/unpoison.
2061 Label loop;
2062 __ Bind(&loop);
2063 __ ldr(IP, Address(temp1, element_size, Address::PostIndex));
2064 __ str(IP, Address(temp2, element_size, Address::PostIndex));
2065 __ cmp(temp1, ShifterOperand(temp3));
2066 __ b(&loop, NE);
2067
2068 __ Bind(read_barrier_slow_path->GetExitLabel());
2069 } else {
2070 // Non read barrier code.
2071 // Compute the base source address in `temp1`.
2072 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2073 // Compute the base destination address in `temp2`.
2074 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2075 // Compute the end source address in `temp3`.
2076 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2077 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2078 // poison/unpoison.
2079 Label loop;
2080 __ Bind(&loop);
2081 __ ldr(IP, Address(temp1, element_size, Address::PostIndex));
2082 __ str(IP, Address(temp2, element_size, Address::PostIndex));
2083 __ cmp(temp1, ShifterOperand(temp3));
2084 __ b(&loop, NE);
2085 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002086 __ Bind(&done);
2087 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01002088
2089 // We only need one card marking on the destination array.
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002090 codegen_->MarkGCCard(temp1, temp2, dest, Register(kNoRegister), /* value_can_be_null */ false);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01002091
Roland Levillain0b671c02016-08-19 12:02:34 +01002092 __ Bind(intrinsic_slow_path->GetExitLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01002093}
2094
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002095static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
2096 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2097 // the code generator. Furthermore, the register allocator creates fixed live intervals
2098 // for all caller-saved registers because we are doing a function call. As a result, if
2099 // the input and output locations are unallocated, the register allocator runs out of
2100 // registers and fails; however, a debuggable graph is not the common case.
2101 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2102 return;
2103 }
2104
2105 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2106 DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
2107 DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
2108
2109 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01002110 LocationSummary::kCallOnMainOnly,
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002111 kIntrinsified);
2112 const InvokeRuntimeCallingConvention calling_convention;
2113
2114 locations->SetInAt(0, Location::RequiresFpuRegister());
2115 locations->SetOut(Location::RequiresFpuRegister());
2116 // Native code uses the soft float ABI.
2117 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2118 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2119}
2120
2121static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
2122 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2123 // the code generator. Furthermore, the register allocator creates fixed live intervals
2124 // for all caller-saved registers because we are doing a function call. As a result, if
2125 // the input and output locations are unallocated, the register allocator runs out of
2126 // registers and fails; however, a debuggable graph is not the common case.
2127 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2128 return;
2129 }
2130
2131 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2132 DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
2133 DCHECK_EQ(invoke->InputAt(1)->GetType(), Primitive::kPrimDouble);
2134 DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
2135
2136 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01002137 LocationSummary::kCallOnMainOnly,
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002138 kIntrinsified);
2139 const InvokeRuntimeCallingConvention calling_convention;
2140
2141 locations->SetInAt(0, Location::RequiresFpuRegister());
2142 locations->SetInAt(1, Location::RequiresFpuRegister());
2143 locations->SetOut(Location::RequiresFpuRegister());
2144 // Native code uses the soft float ABI.
2145 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2146 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2147 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
2148 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
2149}
2150
2151static void GenFPToFPCall(HInvoke* invoke,
2152 ArmAssembler* assembler,
2153 CodeGeneratorARM* codegen,
2154 QuickEntrypointEnum entry) {
2155 LocationSummary* const locations = invoke->GetLocations();
2156 const InvokeRuntimeCallingConvention calling_convention;
2157
2158 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2159 DCHECK(locations->WillCall() && locations->Intrinsified());
2160 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
2161 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
2162
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002163 // Native code uses the soft float ABI.
2164 __ vmovrrd(calling_convention.GetRegisterAt(0),
2165 calling_convention.GetRegisterAt(1),
2166 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01002167 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002168 __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
2169 calling_convention.GetRegisterAt(0),
2170 calling_convention.GetRegisterAt(1));
2171}
2172
2173static void GenFPFPToFPCall(HInvoke* invoke,
2174 ArmAssembler* assembler,
2175 CodeGeneratorARM* codegen,
2176 QuickEntrypointEnum entry) {
2177 LocationSummary* const locations = invoke->GetLocations();
2178 const InvokeRuntimeCallingConvention calling_convention;
2179
2180 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2181 DCHECK(locations->WillCall() && locations->Intrinsified());
2182 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
2183 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
2184 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(2)));
2185 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(3)));
2186
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002187 // Native code uses the soft float ABI.
2188 __ vmovrrd(calling_convention.GetRegisterAt(0),
2189 calling_convention.GetRegisterAt(1),
2190 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
2191 __ vmovrrd(calling_convention.GetRegisterAt(2),
2192 calling_convention.GetRegisterAt(3),
2193 FromLowSToD(locations->InAt(1).AsFpuRegisterPairLow<SRegister>()));
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01002194 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002195 __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
2196 calling_convention.GetRegisterAt(0),
2197 calling_convention.GetRegisterAt(1));
2198}
2199
2200void IntrinsicLocationsBuilderARM::VisitMathCos(HInvoke* invoke) {
2201 CreateFPToFPCallLocations(arena_, invoke);
2202}
2203
2204void IntrinsicCodeGeneratorARM::VisitMathCos(HInvoke* invoke) {
2205 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCos);
2206}
2207
2208void IntrinsicLocationsBuilderARM::VisitMathSin(HInvoke* invoke) {
2209 CreateFPToFPCallLocations(arena_, invoke);
2210}
2211
2212void IntrinsicCodeGeneratorARM::VisitMathSin(HInvoke* invoke) {
2213 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSin);
2214}
2215
2216void IntrinsicLocationsBuilderARM::VisitMathAcos(HInvoke* invoke) {
2217 CreateFPToFPCallLocations(arena_, invoke);
2218}
2219
2220void IntrinsicCodeGeneratorARM::VisitMathAcos(HInvoke* invoke) {
2221 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAcos);
2222}
2223
2224void IntrinsicLocationsBuilderARM::VisitMathAsin(HInvoke* invoke) {
2225 CreateFPToFPCallLocations(arena_, invoke);
2226}
2227
2228void IntrinsicCodeGeneratorARM::VisitMathAsin(HInvoke* invoke) {
2229 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAsin);
2230}
2231
2232void IntrinsicLocationsBuilderARM::VisitMathAtan(HInvoke* invoke) {
2233 CreateFPToFPCallLocations(arena_, invoke);
2234}
2235
2236void IntrinsicCodeGeneratorARM::VisitMathAtan(HInvoke* invoke) {
2237 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan);
2238}
2239
2240void IntrinsicLocationsBuilderARM::VisitMathCbrt(HInvoke* invoke) {
2241 CreateFPToFPCallLocations(arena_, invoke);
2242}
2243
2244void IntrinsicCodeGeneratorARM::VisitMathCbrt(HInvoke* invoke) {
2245 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCbrt);
2246}
2247
2248void IntrinsicLocationsBuilderARM::VisitMathCosh(HInvoke* invoke) {
2249 CreateFPToFPCallLocations(arena_, invoke);
2250}
2251
2252void IntrinsicCodeGeneratorARM::VisitMathCosh(HInvoke* invoke) {
2253 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCosh);
2254}
2255
2256void IntrinsicLocationsBuilderARM::VisitMathExp(HInvoke* invoke) {
2257 CreateFPToFPCallLocations(arena_, invoke);
2258}
2259
2260void IntrinsicCodeGeneratorARM::VisitMathExp(HInvoke* invoke) {
2261 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExp);
2262}
2263
2264void IntrinsicLocationsBuilderARM::VisitMathExpm1(HInvoke* invoke) {
2265 CreateFPToFPCallLocations(arena_, invoke);
2266}
2267
2268void IntrinsicCodeGeneratorARM::VisitMathExpm1(HInvoke* invoke) {
2269 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExpm1);
2270}
2271
2272void IntrinsicLocationsBuilderARM::VisitMathLog(HInvoke* invoke) {
2273 CreateFPToFPCallLocations(arena_, invoke);
2274}
2275
2276void IntrinsicCodeGeneratorARM::VisitMathLog(HInvoke* invoke) {
2277 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog);
2278}
2279
2280void IntrinsicLocationsBuilderARM::VisitMathLog10(HInvoke* invoke) {
2281 CreateFPToFPCallLocations(arena_, invoke);
2282}
2283
2284void IntrinsicCodeGeneratorARM::VisitMathLog10(HInvoke* invoke) {
2285 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog10);
2286}
2287
2288void IntrinsicLocationsBuilderARM::VisitMathSinh(HInvoke* invoke) {
2289 CreateFPToFPCallLocations(arena_, invoke);
2290}
2291
2292void IntrinsicCodeGeneratorARM::VisitMathSinh(HInvoke* invoke) {
2293 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSinh);
2294}
2295
2296void IntrinsicLocationsBuilderARM::VisitMathTan(HInvoke* invoke) {
2297 CreateFPToFPCallLocations(arena_, invoke);
2298}
2299
2300void IntrinsicCodeGeneratorARM::VisitMathTan(HInvoke* invoke) {
2301 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTan);
2302}
2303
2304void IntrinsicLocationsBuilderARM::VisitMathTanh(HInvoke* invoke) {
2305 CreateFPToFPCallLocations(arena_, invoke);
2306}
2307
2308void IntrinsicCodeGeneratorARM::VisitMathTanh(HInvoke* invoke) {
2309 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTanh);
2310}
2311
2312void IntrinsicLocationsBuilderARM::VisitMathAtan2(HInvoke* invoke) {
2313 CreateFPFPToFPCallLocations(arena_, invoke);
2314}
2315
2316void IntrinsicCodeGeneratorARM::VisitMathAtan2(HInvoke* invoke) {
2317 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan2);
2318}
2319
2320void IntrinsicLocationsBuilderARM::VisitMathHypot(HInvoke* invoke) {
2321 CreateFPFPToFPCallLocations(arena_, invoke);
2322}
2323
2324void IntrinsicCodeGeneratorARM::VisitMathHypot(HInvoke* invoke) {
2325 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickHypot);
2326}
2327
2328void IntrinsicLocationsBuilderARM::VisitMathNextAfter(HInvoke* invoke) {
2329 CreateFPFPToFPCallLocations(arena_, invoke);
2330}
2331
2332void IntrinsicCodeGeneratorARM::VisitMathNextAfter(HInvoke* invoke) {
2333 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter);
2334}
2335
Artem Serovc257da72016-02-02 13:49:43 +00002336void IntrinsicLocationsBuilderARM::VisitIntegerReverse(HInvoke* invoke) {
2337 CreateIntToIntLocations(arena_, invoke);
2338}
2339
2340void IntrinsicCodeGeneratorARM::VisitIntegerReverse(HInvoke* invoke) {
2341 ArmAssembler* assembler = GetAssembler();
2342 LocationSummary* locations = invoke->GetLocations();
2343
2344 Register out = locations->Out().AsRegister<Register>();
2345 Register in = locations->InAt(0).AsRegister<Register>();
2346
2347 __ rbit(out, in);
2348}
2349
2350void IntrinsicLocationsBuilderARM::VisitLongReverse(HInvoke* invoke) {
2351 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2352 LocationSummary::kNoCall,
2353 kIntrinsified);
2354 locations->SetInAt(0, Location::RequiresRegister());
2355 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2356}
2357
2358void IntrinsicCodeGeneratorARM::VisitLongReverse(HInvoke* invoke) {
2359 ArmAssembler* assembler = GetAssembler();
2360 LocationSummary* locations = invoke->GetLocations();
2361
2362 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
2363 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
2364 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
2365 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
2366
2367 __ rbit(out_reg_lo, in_reg_hi);
2368 __ rbit(out_reg_hi, in_reg_lo);
2369}
2370
2371void IntrinsicLocationsBuilderARM::VisitIntegerReverseBytes(HInvoke* invoke) {
2372 CreateIntToIntLocations(arena_, invoke);
2373}
2374
2375void IntrinsicCodeGeneratorARM::VisitIntegerReverseBytes(HInvoke* invoke) {
2376 ArmAssembler* assembler = GetAssembler();
2377 LocationSummary* locations = invoke->GetLocations();
2378
2379 Register out = locations->Out().AsRegister<Register>();
2380 Register in = locations->InAt(0).AsRegister<Register>();
2381
2382 __ rev(out, in);
2383}
2384
2385void IntrinsicLocationsBuilderARM::VisitLongReverseBytes(HInvoke* invoke) {
2386 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2387 LocationSummary::kNoCall,
2388 kIntrinsified);
2389 locations->SetInAt(0, Location::RequiresRegister());
2390 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2391}
2392
2393void IntrinsicCodeGeneratorARM::VisitLongReverseBytes(HInvoke* invoke) {
2394 ArmAssembler* assembler = GetAssembler();
2395 LocationSummary* locations = invoke->GetLocations();
2396
2397 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
2398 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
2399 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
2400 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
2401
2402 __ rev(out_reg_lo, in_reg_hi);
2403 __ rev(out_reg_hi, in_reg_lo);
2404}
2405
2406void IntrinsicLocationsBuilderARM::VisitShortReverseBytes(HInvoke* invoke) {
2407 CreateIntToIntLocations(arena_, invoke);
2408}
2409
2410void IntrinsicCodeGeneratorARM::VisitShortReverseBytes(HInvoke* invoke) {
2411 ArmAssembler* assembler = GetAssembler();
2412 LocationSummary* locations = invoke->GetLocations();
2413
2414 Register out = locations->Out().AsRegister<Register>();
2415 Register in = locations->InAt(0).AsRegister<Register>();
2416
2417 __ revsh(out, in);
2418}
2419
xueliang.zhongf1073c82016-07-05 15:28:19 +01002420static void GenBitCount(HInvoke* instr, Primitive::Type type, ArmAssembler* assembler) {
2421 DCHECK(Primitive::IsIntOrLongType(type)) << type;
2422 DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
2423 DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
2424
2425 bool is_long = type == Primitive::kPrimLong;
2426 LocationSummary* locations = instr->GetLocations();
2427 Location in = locations->InAt(0);
2428 Register src_0 = is_long ? in.AsRegisterPairLow<Register>() : in.AsRegister<Register>();
2429 Register src_1 = is_long ? in.AsRegisterPairHigh<Register>() : src_0;
2430 SRegister tmp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
2431 DRegister tmp_d = FromLowSToD(tmp_s);
2432 Register out_r = locations->Out().AsRegister<Register>();
2433
2434 // Move data from core register(s) to temp D-reg for bit count calculation, then move back.
2435 // According to Cortex A57 and A72 optimization guides, compared to transferring to full D-reg,
2436 // transferring data from core reg to upper or lower half of vfp D-reg requires extra latency,
2437 // That's why for integer bit count, we use 'vmov d0, r0, r0' instead of 'vmov d0[0], r0'.
2438 __ vmovdrr(tmp_d, src_1, src_0); // Temp DReg |--src_1|--src_0|
2439 __ vcntd(tmp_d, tmp_d); // Temp DReg |c|c|c|c|c|c|c|c|
2440 __ vpaddld(tmp_d, tmp_d, 8, /* is_unsigned */ true); // Temp DReg |--c|--c|--c|--c|
2441 __ vpaddld(tmp_d, tmp_d, 16, /* is_unsigned */ true); // Temp DReg |------c|------c|
2442 if (is_long) {
2443 __ vpaddld(tmp_d, tmp_d, 32, /* is_unsigned */ true); // Temp DReg |--------------c|
2444 }
2445 __ vmovrs(out_r, tmp_s);
2446}
2447
2448void IntrinsicLocationsBuilderARM::VisitIntegerBitCount(HInvoke* invoke) {
2449 CreateIntToIntLocations(arena_, invoke);
2450 invoke->GetLocations()->AddTemp(Location::RequiresFpuRegister());
2451}
2452
2453void IntrinsicCodeGeneratorARM::VisitIntegerBitCount(HInvoke* invoke) {
2454 GenBitCount(invoke, Primitive::kPrimInt, GetAssembler());
2455}
2456
2457void IntrinsicLocationsBuilderARM::VisitLongBitCount(HInvoke* invoke) {
2458 VisitIntegerBitCount(invoke);
2459}
2460
2461void IntrinsicCodeGeneratorARM::VisitLongBitCount(HInvoke* invoke) {
2462 GenBitCount(invoke, Primitive::kPrimLong, GetAssembler());
2463}
2464
Tim Zhang25abd6c2016-01-19 23:39:24 +08002465void IntrinsicLocationsBuilderARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2466 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2467 LocationSummary::kNoCall,
2468 kIntrinsified);
2469 locations->SetInAt(0, Location::RequiresRegister());
2470 locations->SetInAt(1, Location::RequiresRegister());
2471 locations->SetInAt(2, Location::RequiresRegister());
2472 locations->SetInAt(3, Location::RequiresRegister());
2473 locations->SetInAt(4, Location::RequiresRegister());
2474
Scott Wakeling3fdab772016-04-25 11:32:37 +01002475 // Temporary registers to store lengths of strings and for calculations.
Tim Zhang25abd6c2016-01-19 23:39:24 +08002476 locations->AddTemp(Location::RequiresRegister());
2477 locations->AddTemp(Location::RequiresRegister());
2478 locations->AddTemp(Location::RequiresRegister());
2479}
2480
2481void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2482 ArmAssembler* assembler = GetAssembler();
2483 LocationSummary* locations = invoke->GetLocations();
2484
2485 // Check assumption that sizeof(Char) is 2 (used in scaling below).
2486 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2487 DCHECK_EQ(char_size, 2u);
2488
2489 // Location of data in char array buffer.
2490 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2491
2492 // Location of char array data in string.
2493 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2494
2495 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2496 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2497 Register srcObj = locations->InAt(0).AsRegister<Register>();
2498 Register srcBegin = locations->InAt(1).AsRegister<Register>();
2499 Register srcEnd = locations->InAt(2).AsRegister<Register>();
2500 Register dstObj = locations->InAt(3).AsRegister<Register>();
2501 Register dstBegin = locations->InAt(4).AsRegister<Register>();
2502
Scott Wakeling3fdab772016-04-25 11:32:37 +01002503 Register num_chr = locations->GetTemp(0).AsRegister<Register>();
2504 Register src_ptr = locations->GetTemp(1).AsRegister<Register>();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002505 Register dst_ptr = locations->GetTemp(2).AsRegister<Register>();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002506
jessicahandojo05765752016-09-09 19:01:32 -07002507 Label done, compressed_string_loop;
Anton Kirilov6f644202017-02-27 18:29:45 +00002508 Label* final_label = codegen_->GetFinalLabel(invoke, &done);
Tim Zhang25abd6c2016-01-19 23:39:24 +08002509 // dst to be copied.
2510 __ add(dst_ptr, dstObj, ShifterOperand(data_offset));
2511 __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1));
2512
Scott Wakeling3fdab772016-04-25 11:32:37 +01002513 __ subs(num_chr, srcEnd, ShifterOperand(srcBegin));
Scott Wakeling3fdab772016-04-25 11:32:37 +01002514 // Early out for valid zero-length retrievals.
Anton Kirilov6f644202017-02-27 18:29:45 +00002515 __ b(final_label, EQ);
Scott Wakeling3fdab772016-04-25 11:32:37 +01002516
jessicahandojo05765752016-09-09 19:01:32 -07002517 // src range to copy.
2518 __ add(src_ptr, srcObj, ShifterOperand(value_offset));
2519 Label compressed_string_preloop;
2520 if (mirror::kUseStringCompression) {
2521 // Location of count in string.
2522 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2523 // String's length.
2524 __ ldr(IP, Address(srcObj, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002525 __ tst(IP, ShifterOperand(1));
2526 __ b(&compressed_string_preloop, EQ);
jessicahandojo05765752016-09-09 19:01:32 -07002527 }
2528 __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1));
2529
2530 // Do the copy.
2531 Label loop, remainder;
2532
Scott Wakeling3fdab772016-04-25 11:32:37 +01002533 // Save repairing the value of num_chr on the < 4 character path.
2534 __ subs(IP, num_chr, ShifterOperand(4));
2535 __ b(&remainder, LT);
2536
2537 // Keep the result of the earlier subs, we are going to fetch at least 4 characters.
2538 __ mov(num_chr, ShifterOperand(IP));
2539
2540 // Main loop used for longer fetches loads and stores 4x16-bit characters at a time.
2541 // (LDRD/STRD fault on unaligned addresses and it's not worth inlining extra code
2542 // to rectify these everywhere this intrinsic applies.)
2543 __ Bind(&loop);
2544 __ ldr(IP, Address(src_ptr, char_size * 2));
2545 __ subs(num_chr, num_chr, ShifterOperand(4));
2546 __ str(IP, Address(dst_ptr, char_size * 2));
2547 __ ldr(IP, Address(src_ptr, char_size * 4, Address::PostIndex));
2548 __ str(IP, Address(dst_ptr, char_size * 4, Address::PostIndex));
2549 __ b(&loop, GE);
2550
2551 __ adds(num_chr, num_chr, ShifterOperand(4));
Anton Kirilov6f644202017-02-27 18:29:45 +00002552 __ b(final_label, EQ);
Scott Wakeling3fdab772016-04-25 11:32:37 +01002553
2554 // Main loop for < 4 character case and remainder handling. Loads and stores one
2555 // 16-bit Java character at a time.
2556 __ Bind(&remainder);
2557 __ ldrh(IP, Address(src_ptr, char_size, Address::PostIndex));
2558 __ subs(num_chr, num_chr, ShifterOperand(1));
2559 __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex));
2560 __ b(&remainder, GT);
jessicahandojo05765752016-09-09 19:01:32 -07002561
2562 if (mirror::kUseStringCompression) {
Anton Kirilov6f644202017-02-27 18:29:45 +00002563 __ b(final_label);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002564
jessicahandojo05765752016-09-09 19:01:32 -07002565 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
2566 DCHECK_EQ(c_char_size, 1u);
2567 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2568 __ Bind(&compressed_string_preloop);
2569 __ add(src_ptr, src_ptr, ShifterOperand(srcBegin));
2570 __ Bind(&compressed_string_loop);
2571 __ ldrb(IP, Address(src_ptr, c_char_size, Address::PostIndex));
2572 __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex));
2573 __ subs(num_chr, num_chr, ShifterOperand(1));
2574 __ b(&compressed_string_loop, GT);
2575 }
Scott Wakeling3fdab772016-04-25 11:32:37 +01002576
Anton Kirilov6f644202017-02-27 18:29:45 +00002577 if (done.IsLinked()) {
2578 __ Bind(&done);
2579 }
Tim Zhang25abd6c2016-01-19 23:39:24 +08002580}
2581
Anton Kirilova3ffea22016-04-07 17:02:37 +01002582void IntrinsicLocationsBuilderARM::VisitFloatIsInfinite(HInvoke* invoke) {
2583 CreateFPToIntLocations(arena_, invoke);
2584}
2585
2586void IntrinsicCodeGeneratorARM::VisitFloatIsInfinite(HInvoke* invoke) {
2587 ArmAssembler* const assembler = GetAssembler();
2588 LocationSummary* const locations = invoke->GetLocations();
2589 const Register out = locations->Out().AsRegister<Register>();
2590 // Shifting left by 1 bit makes the value encodable as an immediate operand;
2591 // we don't care about the sign bit anyway.
2592 constexpr uint32_t infinity = kPositiveInfinityFloat << 1U;
2593
2594 __ vmovrs(out, locations->InAt(0).AsFpuRegister<SRegister>());
2595 // We don't care about the sign bit, so shift left.
2596 __ Lsl(out, out, 1);
2597 __ eor(out, out, ShifterOperand(infinity));
2598 // If the result is 0, then it has 32 leading zeros, and less than that otherwise.
2599 __ clz(out, out);
2600 // Any number less than 32 logically shifted right by 5 bits results in 0;
2601 // the same operation on 32 yields 1.
2602 __ Lsr(out, out, 5);
2603}
2604
2605void IntrinsicLocationsBuilderARM::VisitDoubleIsInfinite(HInvoke* invoke) {
2606 CreateFPToIntLocations(arena_, invoke);
2607}
2608
2609void IntrinsicCodeGeneratorARM::VisitDoubleIsInfinite(HInvoke* invoke) {
2610 ArmAssembler* const assembler = GetAssembler();
2611 LocationSummary* const locations = invoke->GetLocations();
2612 const Register out = locations->Out().AsRegister<Register>();
2613 // The highest 32 bits of double precision positive infinity separated into
2614 // two constants encodable as immediate operands.
2615 constexpr uint32_t infinity_high = 0x7f000000U;
2616 constexpr uint32_t infinity_high2 = 0x00f00000U;
2617
2618 static_assert((infinity_high | infinity_high2) == static_cast<uint32_t>(kPositiveInfinityDouble >> 32U),
2619 "The constants do not add up to the high 32 bits of double precision positive infinity.");
2620 __ vmovrrd(IP, out, FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
2621 __ eor(out, out, ShifterOperand(infinity_high));
2622 __ eor(out, out, ShifterOperand(infinity_high2));
2623 // We don't care about the sign bit, so shift left.
2624 __ orr(out, IP, ShifterOperand(out, LSL, 1));
2625 // If the result is 0, then it has 32 leading zeros, and less than that otherwise.
2626 __ clz(out, out);
2627 // Any number less than 32 logically shifted right by 5 bits results in 0;
2628 // the same operation on 32 yields 1.
2629 __ Lsr(out, out, 5);
2630}
2631
TatWai Chongd8c052a2016-11-02 16:12:48 +08002632void IntrinsicLocationsBuilderARM::VisitReferenceGetReferent(HInvoke* invoke) {
2633 if (kEmitCompilerReadBarrier) {
2634 // Do not intrinsify this call with the read barrier configuration.
2635 return;
2636 }
2637 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2638 LocationSummary::kCallOnSlowPath,
2639 kIntrinsified);
2640 locations->SetInAt(0, Location::RequiresRegister());
2641 locations->SetOut(Location::SameAsFirstInput());
2642 locations->AddTemp(Location::RequiresRegister());
2643}
2644
2645void IntrinsicCodeGeneratorARM::VisitReferenceGetReferent(HInvoke* invoke) {
2646 DCHECK(!kEmitCompilerReadBarrier);
2647 ArmAssembler* const assembler = GetAssembler();
2648 LocationSummary* locations = invoke->GetLocations();
2649
2650 Register obj = locations->InAt(0).AsRegister<Register>();
2651 Register out = locations->Out().AsRegister<Register>();
2652
2653 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
2654 codegen_->AddSlowPath(slow_path);
2655
2656 // Load ArtMethod first.
2657 HInvokeStaticOrDirect* invoke_direct = invoke->AsInvokeStaticOrDirect();
2658 DCHECK(invoke_direct != nullptr);
2659 Register temp = codegen_->GenerateCalleeMethodStaticOrDirectCall(
2660 invoke_direct, locations->GetTemp(0)).AsRegister<Register>();
2661
2662 // Now get declaring class.
2663 __ ldr(temp, Address(temp, ArtMethod::DeclaringClassOffset().Int32Value()));
2664
2665 uint32_t slow_path_flag_offset = codegen_->GetReferenceSlowFlagOffset();
2666 uint32_t disable_flag_offset = codegen_->GetReferenceDisableFlagOffset();
2667 DCHECK_NE(slow_path_flag_offset, 0u);
2668 DCHECK_NE(disable_flag_offset, 0u);
2669 DCHECK_NE(slow_path_flag_offset, disable_flag_offset);
2670
2671 // Check static flags that prevent using intrinsic.
2672 __ ldr(IP, Address(temp, disable_flag_offset));
2673 __ ldr(temp, Address(temp, slow_path_flag_offset));
2674 __ orr(IP, IP, ShifterOperand(temp));
2675 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel());
2676
2677 // Fast path.
2678 __ ldr(out, Address(obj, mirror::Reference::ReferentOffset().Int32Value()));
2679 codegen_->MaybeRecordImplicitNullCheck(invoke);
2680 __ MaybeUnpoisonHeapReference(out);
2681 __ Bind(slow_path->GetExitLabel());
2682}
2683
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002684void IntrinsicLocationsBuilderARM::VisitIntegerValueOf(HInvoke* invoke) {
2685 InvokeRuntimeCallingConvention calling_convention;
2686 IntrinsicVisitor::ComputeIntegerValueOfLocations(
2687 invoke,
2688 codegen_,
2689 Location::RegisterLocation(R0),
2690 Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2691}
2692
2693void IntrinsicCodeGeneratorARM::VisitIntegerValueOf(HInvoke* invoke) {
2694 IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo();
2695 LocationSummary* locations = invoke->GetLocations();
2696 ArmAssembler* const assembler = GetAssembler();
2697
2698 Register out = locations->Out().AsRegister<Register>();
2699 InvokeRuntimeCallingConvention calling_convention;
2700 Register argument = calling_convention.GetRegisterAt(0);
2701 if (invoke->InputAt(0)->IsConstant()) {
2702 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
2703 if (value >= info.low && value <= info.high) {
2704 // Just embed the j.l.Integer in the code.
2705 ScopedObjectAccess soa(Thread::Current());
2706 mirror::Object* boxed = info.cache->Get(value + (-info.low));
2707 DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed));
2708 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(boxed));
2709 __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
2710 } else {
2711 // Allocate and initialize a new j.l.Integer.
2712 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
2713 // JIT object table.
2714 uint32_t address =
2715 dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2716 __ LoadLiteral(argument, codegen_->DeduplicateBootImageAddressLiteral(address));
2717 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2718 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2719 __ LoadImmediate(IP, value);
2720 __ StoreToOffset(kStoreWord, IP, out, info.value_offset);
2721 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2722 // one.
2723 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2724 }
2725 } else {
2726 Register in = locations->InAt(0).AsRegister<Register>();
2727 // Check bounds of our cache.
2728 __ AddConstant(out, in, -info.low);
2729 __ CmpConstant(out, info.high - info.low + 1);
2730 Label allocate, done;
2731 __ b(&allocate, HS);
2732 // If the value is within the bounds, load the j.l.Integer directly from the array.
2733 uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
2734 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.cache));
2735 __ LoadLiteral(IP, codegen_->DeduplicateBootImageAddressLiteral(data_offset + address));
2736 codegen_->LoadFromShiftedRegOffset(Primitive::kPrimNot, locations->Out(), IP, out);
2737 __ MaybeUnpoisonHeapReference(out);
2738 __ b(&done);
2739 __ Bind(&allocate);
2740 // Otherwise allocate and initialize a new j.l.Integer.
2741 address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2742 __ LoadLiteral(argument, codegen_->DeduplicateBootImageAddressLiteral(address));
2743 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2744 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2745 __ StoreToOffset(kStoreWord, in, out, info.value_offset);
2746 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2747 // one.
2748 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2749 __ Bind(&done);
2750 }
2751}
2752
Aart Bik2f9fcc92016-03-01 15:16:54 -08002753UNIMPLEMENTED_INTRINSIC(ARM, MathMinDoubleDouble)
2754UNIMPLEMENTED_INTRINSIC(ARM, MathMinFloatFloat)
2755UNIMPLEMENTED_INTRINSIC(ARM, MathMaxDoubleDouble)
2756UNIMPLEMENTED_INTRINSIC(ARM, MathMaxFloatFloat)
2757UNIMPLEMENTED_INTRINSIC(ARM, MathMinLongLong)
2758UNIMPLEMENTED_INTRINSIC(ARM, MathMaxLongLong)
2759UNIMPLEMENTED_INTRINSIC(ARM, MathCeil) // Could be done by changing rounding mode, maybe?
2760UNIMPLEMENTED_INTRINSIC(ARM, MathFloor) // Could be done by changing rounding mode, maybe?
2761UNIMPLEMENTED_INTRINSIC(ARM, MathRint)
2762UNIMPLEMENTED_INTRINSIC(ARM, MathRoundDouble) // Could be done by changing rounding mode, maybe?
2763UNIMPLEMENTED_INTRINSIC(ARM, MathRoundFloat) // Could be done by changing rounding mode, maybe?
2764UNIMPLEMENTED_INTRINSIC(ARM, UnsafeCASLong) // High register pressure.
2765UNIMPLEMENTED_INTRINSIC(ARM, SystemArrayCopyChar)
Aart Bik2f9fcc92016-03-01 15:16:54 -08002766UNIMPLEMENTED_INTRINSIC(ARM, IntegerHighestOneBit)
2767UNIMPLEMENTED_INTRINSIC(ARM, LongHighestOneBit)
2768UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit)
2769UNIMPLEMENTED_INTRINSIC(ARM, LongLowestOneBit)
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08002770
Aart Bikff7d89c2016-11-07 08:49:28 -08002771UNIMPLEMENTED_INTRINSIC(ARM, StringStringIndexOf);
2772UNIMPLEMENTED_INTRINSIC(ARM, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08002773UNIMPLEMENTED_INTRINSIC(ARM, StringBufferAppend);
2774UNIMPLEMENTED_INTRINSIC(ARM, StringBufferLength);
2775UNIMPLEMENTED_INTRINSIC(ARM, StringBufferToString);
2776UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderAppend);
2777UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderLength);
2778UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08002779
Aart Bik0e54c012016-03-04 12:08:31 -08002780// 1.8.
2781UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddInt)
2782UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddLong)
2783UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetInt)
2784UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetLong)
2785UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08002786
Aart Bik2f9fcc92016-03-01 15:16:54 -08002787UNREACHABLE_INTRINSICS(ARM)
Roland Levillain4d027112015-07-01 15:41:14 +01002788
2789#undef __
2790
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08002791} // namespace arm
2792} // namespace art