blob: e067b01f45f24630c1739a4a7896558a00c5d80f [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 Gampec6ea7d02017-02-01 16:46:28 -080025#include "lock_word.h"
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -080026#include "mirror/array-inl.h"
Andreas Gampec15a2f42017-04-21 12:09:39 -070027#include "mirror/object_array-inl.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080028#include "mirror/reference.h"
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -080029#include "mirror/string.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080030#include "scoped_thread_state_change-inl.h"
31#include "thread-inl.h"
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -080032#include "utils/arm/assembler_arm.h"
33
34namespace art {
35
36namespace arm {
37
38ArmAssembler* IntrinsicCodeGeneratorARM::GetAssembler() {
39 return codegen_->GetAssembler();
40}
41
42ArenaAllocator* IntrinsicCodeGeneratorARM::GetAllocator() {
43 return codegen_->GetGraph()->GetArena();
44}
45
Andreas Gampe85b62f22015-09-09 13:15:38 -070046using IntrinsicSlowPathARM = IntrinsicSlowPath<InvokeDexCallingConventionVisitorARM>;
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -080047
Roland Levillain9cc0ea82017-03-16 11:25:59 +000048#define __ assembler->
49
50// Compute base address for the System.arraycopy intrinsic in `base`.
51static void GenSystemArrayCopyBaseAddress(ArmAssembler* assembler,
52 Primitive::Type type,
53 const Register& array,
54 const Location& pos,
55 const Register& base) {
56 // This routine is only used by the SystemArrayCopy intrinsic at the
57 // moment. We can allow Primitive::kPrimNot as `type` to implement
58 // the SystemArrayCopyChar intrinsic.
59 DCHECK_EQ(type, Primitive::kPrimNot);
60 const int32_t element_size = Primitive::ComponentSize(type);
61 const uint32_t element_size_shift = Primitive::ComponentSizeShift(type);
62 const uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
63
64 if (pos.IsConstant()) {
65 int32_t constant = pos.GetConstant()->AsIntConstant()->GetValue();
66 __ AddConstant(base, array, element_size * constant + data_offset);
67 } else {
68 __ add(base, array, ShifterOperand(pos.AsRegister<Register>(), LSL, element_size_shift));
69 __ AddConstant(base, data_offset);
70 }
71}
72
73// Compute end address for the System.arraycopy intrinsic in `end`.
74static void GenSystemArrayCopyEndAddress(ArmAssembler* assembler,
75 Primitive::Type type,
76 const Location& copy_length,
77 const Register& base,
78 const Register& end) {
79 // This routine is only used by the SystemArrayCopy intrinsic at the
80 // moment. We can allow Primitive::kPrimNot as `type` to implement
81 // the SystemArrayCopyChar intrinsic.
82 DCHECK_EQ(type, Primitive::kPrimNot);
83 const int32_t element_size = Primitive::ComponentSize(type);
84 const uint32_t element_size_shift = Primitive::ComponentSizeShift(type);
85
86 if (copy_length.IsConstant()) {
87 int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
88 __ AddConstant(end, base, element_size * constant);
89 } else {
90 __ add(end, base, ShifterOperand(copy_length.AsRegister<Register>(), LSL, element_size_shift));
91 }
92}
93
94#undef __
95
Roland Levillain0b671c02016-08-19 12:02:34 +010096// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
97#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())-> // NOLINT
98
99// Slow path implementing the SystemArrayCopy intrinsic copy loop with read barriers.
100class ReadBarrierSystemArrayCopySlowPathARM : public SlowPathCode {
101 public:
102 explicit ReadBarrierSystemArrayCopySlowPathARM(HInstruction* instruction)
103 : SlowPathCode(instruction) {
104 DCHECK(kEmitCompilerReadBarrier);
105 DCHECK(kUseBakerReadBarrier);
106 }
107
108 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
109 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000110 ArmAssembler* assembler = arm_codegen->GetAssembler();
Roland Levillain0b671c02016-08-19 12:02:34 +0100111 LocationSummary* locations = instruction_->GetLocations();
112 DCHECK(locations->CanCall());
113 DCHECK(instruction_->IsInvokeStaticOrDirect())
114 << "Unexpected instruction in read barrier arraycopy slow path: "
115 << instruction_->DebugName();
116 DCHECK(instruction_->GetLocations()->Intrinsified());
117 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kSystemArrayCopy);
118
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000119 Primitive::Type type = Primitive::kPrimNot;
120 const int32_t element_size = Primitive::ComponentSize(type);
Roland Levillain0b671c02016-08-19 12:02:34 +0100121
122 Register dest = locations->InAt(2).AsRegister<Register>();
123 Location dest_pos = locations->InAt(3);
124 Register src_curr_addr = locations->GetTemp(0).AsRegister<Register>();
125 Register dst_curr_addr = locations->GetTemp(1).AsRegister<Register>();
126 Register src_stop_addr = locations->GetTemp(2).AsRegister<Register>();
127 Register tmp = locations->GetTemp(3).AsRegister<Register>();
128
129 __ Bind(GetEntryLabel());
130 // Compute the base destination address in `dst_curr_addr`.
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000131 GenSystemArrayCopyBaseAddress(assembler, type, dest, dest_pos, dst_curr_addr);
Roland Levillain0b671c02016-08-19 12:02:34 +0100132
133 Label loop;
134 __ Bind(&loop);
135 __ ldr(tmp, Address(src_curr_addr, element_size, Address::PostIndex));
136 __ MaybeUnpoisonHeapReference(tmp);
137 // TODO: Inline the mark bit check before calling the runtime?
138 // tmp = ReadBarrier::Mark(tmp);
139 // No need to save live registers; it's taken care of by the
140 // entrypoint. Also, there is no need to update the stack mask,
141 // as this runtime call will not trigger a garbage collection.
142 // (See ReadBarrierMarkSlowPathARM::EmitNativeCode for more
143 // explanations.)
144 DCHECK_NE(tmp, SP);
145 DCHECK_NE(tmp, LR);
146 DCHECK_NE(tmp, PC);
147 // IP is used internally by the ReadBarrierMarkRegX entry point
148 // as a temporary (and not preserved). It thus cannot be used by
149 // any live register in this slow path.
150 DCHECK_NE(src_curr_addr, IP);
151 DCHECK_NE(dst_curr_addr, IP);
152 DCHECK_NE(src_stop_addr, IP);
153 DCHECK_NE(tmp, IP);
154 DCHECK(0 <= tmp && tmp < kNumberOfCoreRegisters) << tmp;
Roland Levillain9cc0ea82017-03-16 11:25:59 +0000155 // TODO: Load the entrypoint once before the loop, instead of
156 // loading it at every iteration.
Roland Levillain0b671c02016-08-19 12:02:34 +0100157 int32_t entry_point_offset =
158 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(tmp);
159 // This runtime call does not require a stack map.
160 arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
161 __ MaybePoisonHeapReference(tmp);
162 __ str(tmp, Address(dst_curr_addr, element_size, Address::PostIndex));
163 __ cmp(src_curr_addr, ShifterOperand(src_stop_addr));
164 __ b(&loop, NE);
165 __ b(GetExitLabel());
166 }
167
168 const char* GetDescription() const OVERRIDE { return "ReadBarrierSystemArrayCopySlowPathARM"; }
169
170 private:
171 DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathARM);
172};
173
174#undef __
175
Vladimir Marko68c981f2016-08-26 13:13:33 +0100176IntrinsicLocationsBuilderARM::IntrinsicLocationsBuilderARM(CodeGeneratorARM* codegen)
177 : arena_(codegen->GetGraph()->GetArena()),
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000178 codegen_(codegen),
Vladimir Marko68c981f2016-08-26 13:13:33 +0100179 assembler_(codegen->GetAssembler()),
180 features_(codegen->GetInstructionSetFeatures()) {}
181
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800182bool IntrinsicLocationsBuilderARM::TryDispatch(HInvoke* invoke) {
183 Dispatch(invoke);
184 LocationSummary* res = invoke->GetLocations();
Roland Levillain3b359c72015-11-17 19:35:12 +0000185 if (res == nullptr) {
186 return false;
187 }
Roland Levillain3b359c72015-11-17 19:35:12 +0000188 return res->Intrinsified();
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800189}
190
191#define __ assembler->
192
193static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
194 LocationSummary* locations = new (arena) LocationSummary(invoke,
195 LocationSummary::kNoCall,
196 kIntrinsified);
197 locations->SetInAt(0, Location::RequiresFpuRegister());
198 locations->SetOut(Location::RequiresRegister());
199}
200
201static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
202 LocationSummary* locations = new (arena) LocationSummary(invoke,
203 LocationSummary::kNoCall,
204 kIntrinsified);
205 locations->SetInAt(0, Location::RequiresRegister());
206 locations->SetOut(Location::RequiresFpuRegister());
207}
208
209static void MoveFPToInt(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
210 Location input = locations->InAt(0);
211 Location output = locations->Out();
212 if (is64bit) {
213 __ vmovrrd(output.AsRegisterPairLow<Register>(),
214 output.AsRegisterPairHigh<Register>(),
215 FromLowSToD(input.AsFpuRegisterPairLow<SRegister>()));
216 } else {
217 __ vmovrs(output.AsRegister<Register>(), input.AsFpuRegister<SRegister>());
218 }
219}
220
221static void MoveIntToFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
222 Location input = locations->InAt(0);
223 Location output = locations->Out();
224 if (is64bit) {
225 __ vmovdrr(FromLowSToD(output.AsFpuRegisterPairLow<SRegister>()),
226 input.AsRegisterPairLow<Register>(),
227 input.AsRegisterPairHigh<Register>());
228 } else {
229 __ vmovsr(output.AsFpuRegister<SRegister>(), input.AsRegister<Register>());
230 }
231}
232
233void IntrinsicLocationsBuilderARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
234 CreateFPToIntLocations(arena_, invoke);
235}
236void IntrinsicLocationsBuilderARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
237 CreateIntToFPLocations(arena_, invoke);
238}
239
240void IntrinsicCodeGeneratorARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000241 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800242}
243void IntrinsicCodeGeneratorARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000244 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800245}
246
247void IntrinsicLocationsBuilderARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
248 CreateFPToIntLocations(arena_, invoke);
249}
250void IntrinsicLocationsBuilderARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
251 CreateIntToFPLocations(arena_, invoke);
252}
253
254void IntrinsicCodeGeneratorARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000255 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800256}
257void IntrinsicCodeGeneratorARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000258 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800259}
260
261static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
262 LocationSummary* locations = new (arena) LocationSummary(invoke,
263 LocationSummary::kNoCall,
264 kIntrinsified);
265 locations->SetInAt(0, Location::RequiresRegister());
266 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
267}
268
269static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
270 LocationSummary* locations = new (arena) LocationSummary(invoke,
271 LocationSummary::kNoCall,
272 kIntrinsified);
273 locations->SetInAt(0, Location::RequiresFpuRegister());
274 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
275}
276
Anton Kirilov6f644202017-02-27 18:29:45 +0000277static void GenNumberOfLeadingZeros(HInvoke* invoke,
Scott Wakeling611d3392015-07-10 11:42:06 +0100278 Primitive::Type type,
Anton Kirilov6f644202017-02-27 18:29:45 +0000279 CodeGeneratorARM* codegen) {
280 ArmAssembler* assembler = codegen->GetAssembler();
281 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling611d3392015-07-10 11:42:06 +0100282 Location in = locations->InAt(0);
283 Register out = locations->Out().AsRegister<Register>();
284
285 DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
286
287 if (type == Primitive::kPrimLong) {
288 Register in_reg_lo = in.AsRegisterPairLow<Register>();
289 Register in_reg_hi = in.AsRegisterPairHigh<Register>();
290 Label end;
Anton Kirilov6f644202017-02-27 18:29:45 +0000291 Label* final_label = codegen->GetFinalLabel(invoke, &end);
Scott Wakeling611d3392015-07-10 11:42:06 +0100292 __ clz(out, in_reg_hi);
Anton Kirilov6f644202017-02-27 18:29:45 +0000293 __ CompareAndBranchIfNonZero(in_reg_hi, final_label);
Scott Wakeling611d3392015-07-10 11:42:06 +0100294 __ clz(out, in_reg_lo);
295 __ AddConstant(out, 32);
Anton Kirilov6f644202017-02-27 18:29:45 +0000296 if (end.IsLinked()) {
297 __ Bind(&end);
298 }
Scott Wakeling611d3392015-07-10 11:42:06 +0100299 } else {
300 __ clz(out, in.AsRegister<Register>());
301 }
302}
303
304void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
305 CreateIntToIntLocations(arena_, invoke);
306}
307
308void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
Anton Kirilov6f644202017-02-27 18:29:45 +0000309 GenNumberOfLeadingZeros(invoke, Primitive::kPrimInt, codegen_);
Scott Wakeling611d3392015-07-10 11:42:06 +0100310}
311
312void IntrinsicLocationsBuilderARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
313 LocationSummary* locations = new (arena_) LocationSummary(invoke,
314 LocationSummary::kNoCall,
315 kIntrinsified);
316 locations->SetInAt(0, Location::RequiresRegister());
317 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
318}
319
320void IntrinsicCodeGeneratorARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
Anton Kirilov6f644202017-02-27 18:29:45 +0000321 GenNumberOfLeadingZeros(invoke, Primitive::kPrimLong, codegen_);
Scott Wakeling611d3392015-07-10 11:42:06 +0100322}
323
Anton Kirilov6f644202017-02-27 18:29:45 +0000324static void GenNumberOfTrailingZeros(HInvoke* invoke,
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100325 Primitive::Type type,
Anton Kirilov6f644202017-02-27 18:29:45 +0000326 CodeGeneratorARM* codegen) {
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100327 DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
328
Anton Kirilov6f644202017-02-27 18:29:45 +0000329 ArmAssembler* assembler = codegen->GetAssembler();
330 LocationSummary* locations = invoke->GetLocations();
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100331 Register out = locations->Out().AsRegister<Register>();
332
333 if (type == Primitive::kPrimLong) {
334 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
335 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
336 Label end;
Anton Kirilov6f644202017-02-27 18:29:45 +0000337 Label* final_label = codegen->GetFinalLabel(invoke, &end);
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100338 __ rbit(out, in_reg_lo);
339 __ clz(out, out);
Anton Kirilov6f644202017-02-27 18:29:45 +0000340 __ CompareAndBranchIfNonZero(in_reg_lo, final_label);
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100341 __ rbit(out, in_reg_hi);
342 __ clz(out, out);
343 __ AddConstant(out, 32);
Anton Kirilov6f644202017-02-27 18:29:45 +0000344 if (end.IsLinked()) {
345 __ Bind(&end);
346 }
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100347 } else {
348 Register in = locations->InAt(0).AsRegister<Register>();
349 __ rbit(out, in);
350 __ clz(out, out);
351 }
352}
353
354void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
355 LocationSummary* locations = new (arena_) LocationSummary(invoke,
356 LocationSummary::kNoCall,
357 kIntrinsified);
358 locations->SetInAt(0, Location::RequiresRegister());
359 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
360}
361
362void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
Anton Kirilov6f644202017-02-27 18:29:45 +0000363 GenNumberOfTrailingZeros(invoke, Primitive::kPrimInt, codegen_);
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100364}
365
366void IntrinsicLocationsBuilderARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
367 LocationSummary* locations = new (arena_) LocationSummary(invoke,
368 LocationSummary::kNoCall,
369 kIntrinsified);
370 locations->SetInAt(0, Location::RequiresRegister());
371 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
372}
373
374void IntrinsicCodeGeneratorARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
Anton Kirilov6f644202017-02-27 18:29:45 +0000375 GenNumberOfTrailingZeros(invoke, Primitive::kPrimLong, codegen_);
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100376}
377
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800378static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
379 Location in = locations->InAt(0);
380 Location out = locations->Out();
381
382 if (is64bit) {
383 __ vabsd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
384 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
385 } else {
386 __ vabss(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>());
387 }
388}
389
390void IntrinsicLocationsBuilderARM::VisitMathAbsDouble(HInvoke* invoke) {
391 CreateFPToFPLocations(arena_, invoke);
392}
393
394void IntrinsicCodeGeneratorARM::VisitMathAbsDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000395 MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800396}
397
398void IntrinsicLocationsBuilderARM::VisitMathAbsFloat(HInvoke* invoke) {
399 CreateFPToFPLocations(arena_, invoke);
400}
401
402void IntrinsicCodeGeneratorARM::VisitMathAbsFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000403 MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800404}
405
406static void CreateIntToIntPlusTemp(ArenaAllocator* arena, HInvoke* invoke) {
407 LocationSummary* locations = new (arena) LocationSummary(invoke,
408 LocationSummary::kNoCall,
409 kIntrinsified);
410 locations->SetInAt(0, Location::RequiresRegister());
411 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
412
413 locations->AddTemp(Location::RequiresRegister());
414}
415
416static void GenAbsInteger(LocationSummary* locations,
417 bool is64bit,
418 ArmAssembler* assembler) {
419 Location in = locations->InAt(0);
420 Location output = locations->Out();
421
422 Register mask = locations->GetTemp(0).AsRegister<Register>();
423
424 if (is64bit) {
425 Register in_reg_lo = in.AsRegisterPairLow<Register>();
426 Register in_reg_hi = in.AsRegisterPairHigh<Register>();
427 Register out_reg_lo = output.AsRegisterPairLow<Register>();
428 Register out_reg_hi = output.AsRegisterPairHigh<Register>();
429
430 DCHECK_NE(out_reg_lo, in_reg_hi) << "Diagonal overlap unexpected.";
431
432 __ Asr(mask, in_reg_hi, 31);
433 __ adds(out_reg_lo, in_reg_lo, ShifterOperand(mask));
434 __ adc(out_reg_hi, in_reg_hi, ShifterOperand(mask));
435 __ eor(out_reg_lo, mask, ShifterOperand(out_reg_lo));
436 __ eor(out_reg_hi, mask, ShifterOperand(out_reg_hi));
437 } else {
438 Register in_reg = in.AsRegister<Register>();
439 Register out_reg = output.AsRegister<Register>();
440
441 __ Asr(mask, in_reg, 31);
442 __ add(out_reg, in_reg, ShifterOperand(mask));
443 __ eor(out_reg, mask, ShifterOperand(out_reg));
444 }
445}
446
447void IntrinsicLocationsBuilderARM::VisitMathAbsInt(HInvoke* invoke) {
448 CreateIntToIntPlusTemp(arena_, invoke);
449}
450
451void IntrinsicCodeGeneratorARM::VisitMathAbsInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000452 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800453}
454
455
456void IntrinsicLocationsBuilderARM::VisitMathAbsLong(HInvoke* invoke) {
457 CreateIntToIntPlusTemp(arena_, invoke);
458}
459
460void IntrinsicCodeGeneratorARM::VisitMathAbsLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000461 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800462}
463
464static void GenMinMax(LocationSummary* locations,
465 bool is_min,
466 ArmAssembler* assembler) {
467 Register op1 = locations->InAt(0).AsRegister<Register>();
468 Register op2 = locations->InAt(1).AsRegister<Register>();
469 Register out = locations->Out().AsRegister<Register>();
470
471 __ cmp(op1, ShifterOperand(op2));
472
473 __ it((is_min) ? Condition::LT : Condition::GT, kItElse);
474 __ mov(out, ShifterOperand(op1), is_min ? Condition::LT : Condition::GT);
475 __ mov(out, ShifterOperand(op2), is_min ? Condition::GE : Condition::LE);
476}
477
478static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
479 LocationSummary* locations = new (arena) LocationSummary(invoke,
480 LocationSummary::kNoCall,
481 kIntrinsified);
482 locations->SetInAt(0, Location::RequiresRegister());
483 locations->SetInAt(1, Location::RequiresRegister());
484 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
485}
486
487void IntrinsicLocationsBuilderARM::VisitMathMinIntInt(HInvoke* invoke) {
488 CreateIntIntToIntLocations(arena_, invoke);
489}
490
491void IntrinsicCodeGeneratorARM::VisitMathMinIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000492 GenMinMax(invoke->GetLocations(), /* is_min */ true, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800493}
494
495void IntrinsicLocationsBuilderARM::VisitMathMaxIntInt(HInvoke* invoke) {
496 CreateIntIntToIntLocations(arena_, invoke);
497}
498
499void IntrinsicCodeGeneratorARM::VisitMathMaxIntInt(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000500 GenMinMax(invoke->GetLocations(), /* is_min */ false, GetAssembler());
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800501}
502
503void IntrinsicLocationsBuilderARM::VisitMathSqrt(HInvoke* invoke) {
504 CreateFPToFPLocations(arena_, invoke);
505}
506
507void IntrinsicCodeGeneratorARM::VisitMathSqrt(HInvoke* invoke) {
508 LocationSummary* locations = invoke->GetLocations();
509 ArmAssembler* assembler = GetAssembler();
510 __ vsqrtd(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
511 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
512}
513
514void IntrinsicLocationsBuilderARM::VisitMemoryPeekByte(HInvoke* invoke) {
515 CreateIntToIntLocations(arena_, invoke);
516}
517
518void IntrinsicCodeGeneratorARM::VisitMemoryPeekByte(HInvoke* invoke) {
519 ArmAssembler* assembler = GetAssembler();
520 // Ignore upper 4B of long address.
521 __ ldrsb(invoke->GetLocations()->Out().AsRegister<Register>(),
522 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
523}
524
525void IntrinsicLocationsBuilderARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
526 CreateIntToIntLocations(arena_, invoke);
527}
528
529void IntrinsicCodeGeneratorARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
530 ArmAssembler* assembler = GetAssembler();
531 // Ignore upper 4B of long address.
532 __ ldr(invoke->GetLocations()->Out().AsRegister<Register>(),
533 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
534}
535
536void IntrinsicLocationsBuilderARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
537 CreateIntToIntLocations(arena_, invoke);
538}
539
540void IntrinsicCodeGeneratorARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
541 ArmAssembler* assembler = GetAssembler();
542 // Ignore upper 4B of long address.
543 Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
544 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
545 // exception. So we can't use ldrd as addr may be unaligned.
546 Register lo = invoke->GetLocations()->Out().AsRegisterPairLow<Register>();
547 Register hi = invoke->GetLocations()->Out().AsRegisterPairHigh<Register>();
548 if (addr == lo) {
549 __ ldr(hi, Address(addr, 4));
550 __ ldr(lo, Address(addr, 0));
551 } else {
552 __ ldr(lo, Address(addr, 0));
553 __ ldr(hi, Address(addr, 4));
554 }
555}
556
557void IntrinsicLocationsBuilderARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
558 CreateIntToIntLocations(arena_, invoke);
559}
560
561void IntrinsicCodeGeneratorARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
562 ArmAssembler* assembler = GetAssembler();
563 // Ignore upper 4B of long address.
564 __ ldrsh(invoke->GetLocations()->Out().AsRegister<Register>(),
565 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
566}
567
568static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
569 LocationSummary* locations = new (arena) LocationSummary(invoke,
570 LocationSummary::kNoCall,
571 kIntrinsified);
572 locations->SetInAt(0, Location::RequiresRegister());
573 locations->SetInAt(1, Location::RequiresRegister());
574}
575
576void IntrinsicLocationsBuilderARM::VisitMemoryPokeByte(HInvoke* invoke) {
577 CreateIntIntToVoidLocations(arena_, invoke);
578}
579
580void IntrinsicCodeGeneratorARM::VisitMemoryPokeByte(HInvoke* invoke) {
581 ArmAssembler* assembler = GetAssembler();
582 __ strb(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
583 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
584}
585
586void IntrinsicLocationsBuilderARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
587 CreateIntIntToVoidLocations(arena_, invoke);
588}
589
590void IntrinsicCodeGeneratorARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
591 ArmAssembler* assembler = GetAssembler();
592 __ str(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
593 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
594}
595
596void IntrinsicLocationsBuilderARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
597 CreateIntIntToVoidLocations(arena_, invoke);
598}
599
600void IntrinsicCodeGeneratorARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
601 ArmAssembler* assembler = GetAssembler();
602 // Ignore upper 4B of long address.
603 Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
604 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
605 // exception. So we can't use ldrd as addr may be unaligned.
606 __ str(invoke->GetLocations()->InAt(1).AsRegisterPairLow<Register>(), Address(addr, 0));
607 __ str(invoke->GetLocations()->InAt(1).AsRegisterPairHigh<Register>(), Address(addr, 4));
608}
609
610void IntrinsicLocationsBuilderARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
611 CreateIntIntToVoidLocations(arena_, invoke);
612}
613
614void IntrinsicCodeGeneratorARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
615 ArmAssembler* assembler = GetAssembler();
616 __ strh(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
617 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
618}
619
620void IntrinsicLocationsBuilderARM::VisitThreadCurrentThread(HInvoke* invoke) {
621 LocationSummary* locations = new (arena_) LocationSummary(invoke,
622 LocationSummary::kNoCall,
623 kIntrinsified);
624 locations->SetOut(Location::RequiresRegister());
625}
626
627void IntrinsicCodeGeneratorARM::VisitThreadCurrentThread(HInvoke* invoke) {
628 ArmAssembler* assembler = GetAssembler();
629 __ LoadFromOffset(kLoadWord,
630 invoke->GetLocations()->Out().AsRegister<Register>(),
631 TR,
632 Thread::PeerOffset<kArmPointerSize>().Int32Value());
633}
634
635static void GenUnsafeGet(HInvoke* invoke,
636 Primitive::Type type,
637 bool is_volatile,
638 CodeGeneratorARM* codegen) {
639 LocationSummary* locations = invoke->GetLocations();
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800640 ArmAssembler* assembler = codegen->GetAssembler();
Roland Levillain3b359c72015-11-17 19:35:12 +0000641 Location base_loc = locations->InAt(1);
642 Register base = base_loc.AsRegister<Register>(); // Object pointer.
643 Location offset_loc = locations->InAt(2);
644 Register offset = offset_loc.AsRegisterPairLow<Register>(); // Long offset, lo part only.
645 Location trg_loc = locations->Out();
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800646
Roland Levillainc9285912015-12-18 10:38:42 +0000647 switch (type) {
648 case Primitive::kPrimInt: {
649 Register trg = trg_loc.AsRegister<Register>();
650 __ ldr(trg, Address(base, offset));
651 if (is_volatile) {
652 __ dmb(ISH);
653 }
654 break;
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800655 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800656
Roland Levillainc9285912015-12-18 10:38:42 +0000657 case Primitive::kPrimNot: {
658 Register trg = trg_loc.AsRegister<Register>();
659 if (kEmitCompilerReadBarrier) {
660 if (kUseBakerReadBarrier) {
661 Location temp = locations->GetTemp(0);
Roland Levillainbfea3352016-06-23 13:48:47 +0100662 codegen->GenerateReferenceLoadWithBakerReadBarrier(
663 invoke, trg_loc, base, 0U, offset_loc, TIMES_1, temp, /* needs_null_check */ false);
Roland Levillainc9285912015-12-18 10:38:42 +0000664 if (is_volatile) {
665 __ dmb(ISH);
666 }
667 } else {
668 __ ldr(trg, Address(base, offset));
669 if (is_volatile) {
670 __ dmb(ISH);
671 }
672 codegen->GenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
673 }
674 } else {
675 __ ldr(trg, Address(base, offset));
676 if (is_volatile) {
677 __ dmb(ISH);
678 }
679 __ MaybeUnpoisonHeapReference(trg);
680 }
681 break;
682 }
Roland Levillain4d027112015-07-01 15:41:14 +0100683
Roland Levillainc9285912015-12-18 10:38:42 +0000684 case Primitive::kPrimLong: {
685 Register trg_lo = trg_loc.AsRegisterPairLow<Register>();
686 __ add(IP, base, ShifterOperand(offset));
687 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
688 Register trg_hi = trg_loc.AsRegisterPairHigh<Register>();
689 __ ldrexd(trg_lo, trg_hi, IP);
690 } else {
691 __ ldrd(trg_lo, Address(IP));
692 }
693 if (is_volatile) {
694 __ dmb(ISH);
695 }
696 break;
697 }
698
699 default:
700 LOG(FATAL) << "Unexpected type " << type;
701 UNREACHABLE();
Roland Levillain4d027112015-07-01 15:41:14 +0100702 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800703}
704
Roland Levillainc9285912015-12-18 10:38:42 +0000705static void CreateIntIntIntToIntLocations(ArenaAllocator* arena,
706 HInvoke* invoke,
707 Primitive::Type type) {
Roland Levillain3b359c72015-11-17 19:35:12 +0000708 bool can_call = kEmitCompilerReadBarrier &&
709 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
710 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800711 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100712 (can_call
713 ? LocationSummary::kCallOnSlowPath
714 : LocationSummary::kNoCall),
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800715 kIntrinsified);
Vladimir Marko70e97462016-08-09 11:04:26 +0100716 if (can_call && kUseBakerReadBarrier) {
Vladimir Marko804b03f2016-09-14 16:26:36 +0100717 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Vladimir Marko70e97462016-08-09 11:04:26 +0100718 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800719 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
720 locations->SetInAt(1, Location::RequiresRegister());
721 locations->SetInAt(2, Location::RequiresRegister());
Roland Levillainbfea3352016-06-23 13:48:47 +0100722 locations->SetOut(Location::RequiresRegister(),
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100723 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Roland Levillainc9285912015-12-18 10:38:42 +0000724 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
725 // We need a temporary register for the read barrier marking slow
Roland Levillainbfea3352016-06-23 13:48:47 +0100726 // path in InstructionCodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier.
Roland Levillainc9285912015-12-18 10:38:42 +0000727 locations->AddTemp(Location::RequiresRegister());
728 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800729}
730
731void IntrinsicLocationsBuilderARM::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000732 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800733}
734void IntrinsicLocationsBuilderARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000735 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800736}
737void IntrinsicLocationsBuilderARM::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000738 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800739}
740void IntrinsicLocationsBuilderARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000741 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800742}
743void IntrinsicLocationsBuilderARM::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000744 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800745}
746void IntrinsicLocationsBuilderARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainc9285912015-12-18 10:38:42 +0000747 CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800748}
749
750void IntrinsicCodeGeneratorARM::VisitUnsafeGet(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000751 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800752}
753void IntrinsicCodeGeneratorARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000754 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800755}
756void IntrinsicCodeGeneratorARM::VisitUnsafeGetLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000757 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800758}
759void IntrinsicCodeGeneratorARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000760 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800761}
762void IntrinsicCodeGeneratorARM::VisitUnsafeGetObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000763 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800764}
765void IntrinsicCodeGeneratorARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000766 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800767}
768
769static void CreateIntIntIntIntToVoid(ArenaAllocator* arena,
770 const ArmInstructionSetFeatures& features,
771 Primitive::Type type,
772 bool is_volatile,
773 HInvoke* invoke) {
774 LocationSummary* locations = new (arena) LocationSummary(invoke,
775 LocationSummary::kNoCall,
776 kIntrinsified);
777 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
778 locations->SetInAt(1, Location::RequiresRegister());
779 locations->SetInAt(2, Location::RequiresRegister());
780 locations->SetInAt(3, Location::RequiresRegister());
781
782 if (type == Primitive::kPrimLong) {
783 // Potentially need temps for ldrexd-strexd loop.
784 if (is_volatile && !features.HasAtomicLdrdAndStrd()) {
785 locations->AddTemp(Location::RequiresRegister()); // Temp_lo.
786 locations->AddTemp(Location::RequiresRegister()); // Temp_hi.
787 }
788 } else if (type == Primitive::kPrimNot) {
789 // Temps for card-marking.
790 locations->AddTemp(Location::RequiresRegister()); // Temp.
791 locations->AddTemp(Location::RequiresRegister()); // Card.
792 }
793}
794
795void IntrinsicLocationsBuilderARM::VisitUnsafePut(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000796 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800797}
798void IntrinsicLocationsBuilderARM::VisitUnsafePutOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000799 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800800}
801void IntrinsicLocationsBuilderARM::VisitUnsafePutVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000802 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ true, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800803}
804void IntrinsicLocationsBuilderARM::VisitUnsafePutObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000805 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800806}
807void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000808 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800809}
810void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000811 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ true, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800812}
813void IntrinsicLocationsBuilderARM::VisitUnsafePutLong(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::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000818 CreateIntIntIntIntToVoid(
819 arena_, features_, Primitive::kPrimLong, /* is_volatile */ false, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800820}
821void IntrinsicLocationsBuilderARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000822 CreateIntIntIntIntToVoid(
823 arena_, features_, Primitive::kPrimLong, /* is_volatile */ true, invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800824}
825
826static void GenUnsafePut(LocationSummary* locations,
827 Primitive::Type type,
828 bool is_volatile,
829 bool is_ordered,
830 CodeGeneratorARM* codegen) {
831 ArmAssembler* assembler = codegen->GetAssembler();
832
833 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
834 Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Long offset, lo part only.
835 Register value;
836
837 if (is_volatile || is_ordered) {
838 __ dmb(ISH);
839 }
840
841 if (type == Primitive::kPrimLong) {
842 Register value_lo = locations->InAt(3).AsRegisterPairLow<Register>();
843 value = value_lo;
844 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
845 Register temp_lo = locations->GetTemp(0).AsRegister<Register>();
846 Register temp_hi = locations->GetTemp(1).AsRegister<Register>();
847 Register value_hi = locations->InAt(3).AsRegisterPairHigh<Register>();
848
849 __ add(IP, base, ShifterOperand(offset));
850 Label loop_head;
851 __ Bind(&loop_head);
852 __ ldrexd(temp_lo, temp_hi, IP);
853 __ strexd(temp_lo, value_lo, value_hi, IP);
854 __ cmp(temp_lo, ShifterOperand(0));
855 __ b(&loop_head, NE);
856 } else {
857 __ add(IP, base, ShifterOperand(offset));
858 __ strd(value_lo, Address(IP));
859 }
860 } else {
Roland Levillain4d027112015-07-01 15:41:14 +0100861 value = locations->InAt(3).AsRegister<Register>();
862 Register source = value;
863 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
864 Register temp = locations->GetTemp(0).AsRegister<Register>();
865 __ Mov(temp, value);
866 __ PoisonHeapReference(temp);
867 source = temp;
868 }
869 __ str(source, Address(base, offset));
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800870 }
871
872 if (is_volatile) {
873 __ dmb(ISH);
874 }
875
876 if (type == Primitive::kPrimNot) {
877 Register temp = locations->GetTemp(0).AsRegister<Register>();
878 Register card = locations->GetTemp(1).AsRegister<Register>();
Nicolas Geoffray07276db2015-05-18 14:22:09 +0100879 bool value_can_be_null = true; // TODO: Worth finding out this information?
880 codegen->MarkGCCard(temp, card, base, value, value_can_be_null);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800881 }
882}
883
884void IntrinsicCodeGeneratorARM::VisitUnsafePut(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000885 GenUnsafePut(invoke->GetLocations(),
886 Primitive::kPrimInt,
887 /* is_volatile */ false,
888 /* is_ordered */ false,
889 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800890}
891void IntrinsicCodeGeneratorARM::VisitUnsafePutOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000892 GenUnsafePut(invoke->GetLocations(),
893 Primitive::kPrimInt,
894 /* is_volatile */ false,
895 /* is_ordered */ true,
896 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800897}
898void IntrinsicCodeGeneratorARM::VisitUnsafePutVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000899 GenUnsafePut(invoke->GetLocations(),
900 Primitive::kPrimInt,
901 /* is_volatile */ true,
902 /* is_ordered */ false,
903 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800904}
905void IntrinsicCodeGeneratorARM::VisitUnsafePutObject(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000906 GenUnsafePut(invoke->GetLocations(),
907 Primitive::kPrimNot,
908 /* is_volatile */ false,
909 /* is_ordered */ false,
910 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800911}
912void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000913 GenUnsafePut(invoke->GetLocations(),
914 Primitive::kPrimNot,
915 /* is_volatile */ false,
916 /* is_ordered */ true,
917 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800918}
919void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000920 GenUnsafePut(invoke->GetLocations(),
921 Primitive::kPrimNot,
922 /* is_volatile */ true,
923 /* is_ordered */ false,
924 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800925}
926void IntrinsicCodeGeneratorARM::VisitUnsafePutLong(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000927 GenUnsafePut(invoke->GetLocations(),
928 Primitive::kPrimLong,
929 /* is_volatile */ false,
930 /* is_ordered */ false,
931 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800932}
933void IntrinsicCodeGeneratorARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000934 GenUnsafePut(invoke->GetLocations(),
935 Primitive::kPrimLong,
936 /* is_volatile */ false,
937 /* is_ordered */ true,
938 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800939}
940void IntrinsicCodeGeneratorARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000941 GenUnsafePut(invoke->GetLocations(),
942 Primitive::kPrimLong,
943 /* is_volatile */ true,
944 /* is_ordered */ false,
945 codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800946}
947
948static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena,
Roland Levillain2e50ecb2016-01-27 14:08:33 +0000949 HInvoke* invoke,
950 Primitive::Type type) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100951 bool can_call = kEmitCompilerReadBarrier &&
952 kUseBakerReadBarrier &&
953 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800954 LocationSummary* locations = new (arena) LocationSummary(invoke,
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100955 (can_call
956 ? LocationSummary::kCallOnSlowPath
957 : LocationSummary::kNoCall),
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800958 kIntrinsified);
959 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
960 locations->SetInAt(1, Location::RequiresRegister());
961 locations->SetInAt(2, Location::RequiresRegister());
962 locations->SetInAt(3, Location::RequiresRegister());
963 locations->SetInAt(4, Location::RequiresRegister());
964
Roland Levillain2e50ecb2016-01-27 14:08:33 +0000965 // If heap poisoning is enabled, we don't want the unpoisoning
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100966 // operations to potentially clobber the output. Likewise when
967 // emitting a (Baker) read barrier, which may call.
968 Location::OutputOverlap overlaps =
969 ((kPoisonHeapReferences && type == Primitive::kPrimNot) || can_call)
Roland Levillain2e50ecb2016-01-27 14:08:33 +0000970 ? Location::kOutputOverlap
971 : Location::kNoOutputOverlap;
972 locations->SetOut(Location::RequiresRegister(), overlaps);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800973
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100974 // Temporary registers used in CAS. In the object case
975 // (UnsafeCASObject intrinsic), these are also used for
976 // card-marking, and possibly for (Baker) read barrier.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800977 locations->AddTemp(Location::RequiresRegister()); // Pointer.
978 locations->AddTemp(Location::RequiresRegister()); // Temp 1.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800979}
980
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100981static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARM* codegen) {
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800982 DCHECK_NE(type, Primitive::kPrimLong);
983
984 ArmAssembler* assembler = codegen->GetAssembler();
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100985 LocationSummary* locations = invoke->GetLocations();
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800986
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100987 Location out_loc = locations->Out();
988 Register out = out_loc.AsRegister<Register>(); // Boolean result.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800989
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100990 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
991 Location offset_loc = locations->InAt(2);
992 Register offset = offset_loc.AsRegisterPairLow<Register>(); // Offset (discard high 4B).
993 Register expected = locations->InAt(3).AsRegister<Register>(); // Expected.
994 Register value = locations->InAt(4).AsRegister<Register>(); // Value.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800995
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100996 Location tmp_ptr_loc = locations->GetTemp(0);
997 Register tmp_ptr = tmp_ptr_loc.AsRegister<Register>(); // Pointer to actual memory.
998 Register tmp = locations->GetTemp(1).AsRegister<Register>(); // Value in memory.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800999
1000 if (type == Primitive::kPrimNot) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001001 // The only read barrier implementation supporting the
1002 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1003 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1004
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001005 // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
1006 // object and scan the receiver at the next GC for nothing.
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001007 bool value_can_be_null = true; // TODO: Worth finding out this information?
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001008 codegen->MarkGCCard(tmp_ptr, tmp, base, value, value_can_be_null);
1009
1010 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1011 // Need to make sure the reference stored in the field is a to-space
1012 // one before attempting the CAS or the CAS could fail incorrectly.
Roland Levillainff487002017-03-07 16:50:01 +00001013 codegen->UpdateReferenceFieldWithBakerReadBarrier(
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001014 invoke,
1015 out_loc, // Unused, used only as a "temporary" within the read barrier.
1016 base,
Roland Levillainff487002017-03-07 16:50:01 +00001017 /* field_offset */ offset_loc,
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001018 tmp_ptr_loc,
1019 /* needs_null_check */ false,
Roland Levillainff487002017-03-07 16:50:01 +00001020 tmp);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001021 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001022 }
1023
1024 // Prevent reordering with prior memory operations.
Roland Levillain4bedb382016-01-12 12:01:04 +00001025 // Emit a DMB ISH instruction instead of an DMB ISHST one, as the
1026 // latter allows a preceding load to be delayed past the STXR
1027 // instruction below.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001028 __ dmb(ISH);
1029
1030 __ add(tmp_ptr, base, ShifterOperand(offset));
1031
Roland Levillain4d027112015-07-01 15:41:14 +01001032 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001033 __ PoisonHeapReference(expected);
1034 if (value == expected) {
1035 // Do not poison `value`, as it is the same register as
1036 // `expected`, which has just been poisoned.
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001037 } else {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001038 __ PoisonHeapReference(value);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001039 }
Roland Levillain4d027112015-07-01 15:41:14 +01001040 }
1041
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001042 // do {
1043 // tmp = [r_ptr] - expected;
1044 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
1045 // result = tmp != 0;
1046
1047 Label loop_head;
1048 __ Bind(&loop_head);
1049
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001050 __ ldrex(tmp, tmp_ptr);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001051
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001052 __ subs(tmp, tmp, ShifterOperand(expected));
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001053
1054 __ it(EQ, ItState::kItT);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001055 __ strex(tmp, value, tmp_ptr, EQ);
1056 __ cmp(tmp, ShifterOperand(1), EQ);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001057
1058 __ b(&loop_head, EQ);
1059
1060 __ dmb(ISH);
1061
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001062 __ rsbs(out, tmp, ShifterOperand(1));
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001063 __ it(CC);
1064 __ mov(out, ShifterOperand(0), CC);
Roland Levillain4d027112015-07-01 15:41:14 +01001065
1066 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001067 __ UnpoisonHeapReference(expected);
1068 if (value == expected) {
1069 // Do not unpoison `value`, as it is the same register as
1070 // `expected`, which has just been unpoisoned.
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001071 } else {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001072 __ UnpoisonHeapReference(value);
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001073 }
Roland Levillain4d027112015-07-01 15:41:14 +01001074 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001075}
1076
Andreas Gampeca714582015-04-03 19:41:34 -07001077void IntrinsicLocationsBuilderARM::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001078 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimInt);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001079}
Andreas Gampeca714582015-04-03 19:41:34 -07001080void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001081 // The only read barrier implementation supporting the
1082 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1083 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain985ff702015-10-23 13:25:35 +01001084 return;
1085 }
1086
Roland Levillain2e50ecb2016-01-27 14:08:33 +00001087 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimNot);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001088}
1089void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001090 GenCas(invoke, Primitive::kPrimInt, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001091}
1092void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001093 // The only read barrier implementation supporting the
1094 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1095 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01001096
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001097 GenCas(invoke, Primitive::kPrimNot, codegen_);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001098}
1099
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001100void IntrinsicLocationsBuilderARM::VisitStringCompareTo(HInvoke* invoke) {
1101 // The inputs plus one temp.
1102 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001103 invoke->InputAt(1)->CanBeNull()
1104 ? LocationSummary::kCallOnSlowPath
1105 : LocationSummary::kNoCall,
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001106 kIntrinsified);
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001107 locations->SetInAt(0, Location::RequiresRegister());
1108 locations->SetInAt(1, Location::RequiresRegister());
1109 locations->AddTemp(Location::RequiresRegister());
1110 locations->AddTemp(Location::RequiresRegister());
1111 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001112 // Need temporary registers for String compression's feature.
1113 if (mirror::kUseStringCompression) {
1114 locations->AddTemp(Location::RequiresRegister());
jessicahandojo05765752016-09-09 19:01:32 -07001115 }
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001116 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001117}
1118
1119void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) {
1120 ArmAssembler* assembler = GetAssembler();
1121 LocationSummary* locations = invoke->GetLocations();
1122
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001123 Register str = locations->InAt(0).AsRegister<Register>();
1124 Register arg = locations->InAt(1).AsRegister<Register>();
1125 Register out = locations->Out().AsRegister<Register>();
1126
1127 Register temp0 = locations->GetTemp(0).AsRegister<Register>();
1128 Register temp1 = locations->GetTemp(1).AsRegister<Register>();
1129 Register temp2 = locations->GetTemp(2).AsRegister<Register>();
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001130 Register temp3;
jessicahandojo05765752016-09-09 19:01:32 -07001131 if (mirror::kUseStringCompression) {
1132 temp3 = locations->GetTemp(3).AsRegister<Register>();
jessicahandojo05765752016-09-09 19:01:32 -07001133 }
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001134
1135 Label loop;
1136 Label find_char_diff;
1137 Label end;
jessicahandojo05765752016-09-09 19:01:32 -07001138 Label different_compression;
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001139
1140 // Get offsets of count and value fields within a string object.
1141 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1142 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1143
Nicolas Geoffray512e04d2015-03-27 17:21:24 +00001144 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +01001145 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001146
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001147 // Take slow path and throw if input can be and is null.
1148 SlowPathCode* slow_path = nullptr;
1149 const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
1150 if (can_slow_path) {
1151 slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
1152 codegen_->AddSlowPath(slow_path);
1153 __ CompareAndBranchIfZero(arg, slow_path->GetEntryLabel());
1154 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001155
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001156 // Reference equality check, return 0 if same reference.
1157 __ subs(out, str, ShifterOperand(arg));
1158 __ b(&end, EQ);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001159
jessicahandojo05765752016-09-09 19:01:32 -07001160 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001161 // Load `count` fields of this and argument strings.
jessicahandojo05765752016-09-09 19:01:32 -07001162 __ ldr(temp3, Address(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001163 __ ldr(temp2, Address(arg, count_offset));
1164 // Extract lengths from the `count` fields.
1165 __ Lsr(temp0, temp3, 1u);
1166 __ Lsr(temp1, temp2, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001167 } else {
1168 // Load lengths of this and argument strings.
1169 __ ldr(temp0, Address(str, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001170 __ ldr(temp1, Address(arg, count_offset));
jessicahandojo05765752016-09-09 19:01:32 -07001171 }
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001172 // out = length diff.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001173 __ subs(out, temp0, ShifterOperand(temp1));
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001174 // temp0 = min(len(str), len(arg)).
jessicahandojo05765752016-09-09 19:01:32 -07001175 __ it(GT);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001176 __ mov(temp0, ShifterOperand(temp1), GT);
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001177 // Shorter string is empty?
1178 __ CompareAndBranchIfZero(temp0, &end);
1179
jessicahandojo05765752016-09-09 19:01:32 -07001180 if (mirror::kUseStringCompression) {
1181 // Check if both strings using same compression style to use this comparison loop.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001182 __ eor(temp2, temp2, ShifterOperand(temp3));
1183 __ Lsrs(temp2, temp2, 1u);
1184 __ b(&different_compression, CS);
jessicahandojo05765752016-09-09 19:01:32 -07001185 // For string compression, calculate the number of bytes to compare (not chars).
1186 // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001187 __ Lsls(temp3, temp3, 31u); // Extract purely the compression flag.
1188 __ it(NE);
1189 __ add(temp0, temp0, ShifterOperand(temp0), NE);
jessicahandojo05765752016-09-09 19:01:32 -07001190 }
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001191
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001192 // Store offset of string value in preparation for comparison loop.
1193 __ mov(temp1, ShifterOperand(value_offset));
1194
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001195 // Assertions that must hold in order to compare multiple characters at a time.
1196 CHECK_ALIGNED(value_offset, 8);
1197 static_assert(IsAligned<8>(kObjectAlignment),
1198 "String data must be 8-byte aligned for unrolled CompareTo loop.");
1199
1200 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
1201 DCHECK_EQ(char_size, 2u);
1202
jessicahandojo05765752016-09-09 19:01:32 -07001203 Label find_char_diff_2nd_cmp;
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001204 // Unrolled loop comparing 4x16-bit chars per iteration (ok because of string data alignment).
1205 __ Bind(&loop);
1206 __ ldr(IP, Address(str, temp1));
1207 __ ldr(temp2, Address(arg, temp1));
1208 __ cmp(IP, ShifterOperand(temp2));
1209 __ b(&find_char_diff, NE);
1210 __ add(temp1, temp1, ShifterOperand(char_size * 2));
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001211
1212 __ ldr(IP, Address(str, temp1));
1213 __ ldr(temp2, Address(arg, temp1));
1214 __ cmp(IP, ShifterOperand(temp2));
jessicahandojo05765752016-09-09 19:01:32 -07001215 __ b(&find_char_diff_2nd_cmp, NE);
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001216 __ add(temp1, temp1, ShifterOperand(char_size * 2));
jessicahandojo05765752016-09-09 19:01:32 -07001217 // With string compression, we have compared 8 bytes, otherwise 4 chars.
1218 __ subs(temp0, temp0, ShifterOperand(mirror::kUseStringCompression ? 8 : 4));
1219 __ b(&loop, HI);
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001220 __ b(&end);
1221
jessicahandojo05765752016-09-09 19:01:32 -07001222 __ Bind(&find_char_diff_2nd_cmp);
1223 if (mirror::kUseStringCompression) {
1224 __ subs(temp0, temp0, ShifterOperand(4)); // 4 bytes previously compared.
1225 __ b(&end, LS); // Was the second comparison fully beyond the end?
1226 } else {
1227 // Without string compression, we can start treating temp0 as signed
1228 // and rely on the signed comparison below.
1229 __ sub(temp0, temp0, ShifterOperand(2));
1230 }
1231
1232 // Find the single character difference.
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001233 __ Bind(&find_char_diff);
1234 // Get the bit position of the first character that differs.
1235 __ eor(temp1, temp2, ShifterOperand(IP));
1236 __ rbit(temp1, temp1);
1237 __ clz(temp1, temp1);
1238
jessicahandojo05765752016-09-09 19:01:32 -07001239 // temp0 = number of characters remaining to compare.
1240 // (Without string compression, it could be < 1 if a difference is found by the second CMP
1241 // in the comparison loop, and after the end of the shorter string data).
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001242
jessicahandojo05765752016-09-09 19:01:32 -07001243 // Without string compression (temp1 >> 4) = character where difference occurs between the last
1244 // two words compared, in the interval [0,1].
1245 // (0 for low half-word different, 1 for high half-word different).
1246 // With string compression, (temp1 << 3) = byte where the difference occurs,
1247 // in the interval [0,3].
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001248
jessicahandojo05765752016-09-09 19:01:32 -07001249 // If temp0 <= (temp1 >> (kUseStringCompression ? 3 : 4)), the difference occurs outside
1250 // the remaining string data, so just return length diff (out).
1251 // The comparison is unsigned for string compression, otherwise signed.
1252 __ cmp(temp0, ShifterOperand(temp1, LSR, mirror::kUseStringCompression ? 3 : 4));
1253 __ b(&end, mirror::kUseStringCompression ? LS : LE);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001254
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001255 // Extract the characters and calculate the difference.
jessicahandojo05765752016-09-09 19:01:32 -07001256 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001257 // For compressed strings we need to clear 0x7 from temp1, for uncompressed we need to clear
1258 // 0xf. We also need to prepare the character extraction mask `uncompressed ? 0xffffu : 0xffu`.
1259 // The compression flag is now in the highest bit of temp3, so let's play some tricks.
1260 __ orr(temp3, temp3, ShifterOperand(0xffu << 23)); // uncompressed ? 0xff800000u : 0x7ff80000u
1261 __ bic(temp1, temp1, ShifterOperand(temp3, LSR, 31 - 3)); // &= ~(uncompressed ? 0xfu : 0x7u)
1262 __ Asr(temp3, temp3, 7u); // uncompressed ? 0xffff0000u : 0xff0000u.
1263 __ Lsr(temp2, temp2, temp1); // Extract second character.
1264 __ Lsr(temp3, temp3, 16u); // uncompressed ? 0xffffu : 0xffu
1265 __ Lsr(out, IP, temp1); // Extract first character.
1266 __ and_(temp2, temp2, ShifterOperand(temp3));
1267 __ and_(out, out, ShifterOperand(temp3));
1268 } else {
1269 __ bic(temp1, temp1, ShifterOperand(0xf));
1270 __ Lsr(temp2, temp2, temp1);
1271 __ Lsr(out, IP, temp1);
1272 __ movt(temp2, 0);
1273 __ movt(out, 0);
jessicahandojo05765752016-09-09 19:01:32 -07001274 }
jessicahandojo05765752016-09-09 19:01:32 -07001275
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001276 __ sub(out, out, ShifterOperand(temp2));
jessicahandojo05765752016-09-09 19:01:32 -07001277
1278 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001279 __ b(&end);
1280 __ Bind(&different_compression);
1281
1282 // Comparison for different compression style.
jessicahandojo05765752016-09-09 19:01:32 -07001283 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
1284 DCHECK_EQ(c_char_size, 1u);
jessicahandojo05765752016-09-09 19:01:32 -07001285
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001286 // We want to free up the temp3, currently holding `str.count`, for comparison.
1287 // So, we move it to the bottom bit of the iteration count `temp0` which we tnen
1288 // need to treat as unsigned. Start by freeing the bit with an ADD and continue
1289 // further down by a LSRS+SBC which will flip the meaning of the flag but allow
1290 // `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
1291 __ add(temp0, temp0, ShifterOperand(temp0)); // Unlike LSL, this ADD is always 16-bit.
1292 // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
1293 __ mov(temp1, ShifterOperand(str));
1294 __ mov(temp2, ShifterOperand(arg));
1295 __ Lsrs(temp3, temp3, 1u); // Continue the move of the compression flag.
1296 __ it(CS, kItThen); // Interleave with selection of temp1 and temp2.
1297 __ mov(temp1, ShifterOperand(arg), CS); // Preserves flags.
1298 __ mov(temp2, ShifterOperand(str), CS); // Preserves flags.
1299 __ sbc(temp0, temp0, ShifterOperand(0)); // Complete the move of the compression flag.
jessicahandojo05765752016-09-09 19:01:32 -07001300
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001301 // Adjust temp1 and temp2 from string pointers to data pointers.
1302 __ add(temp1, temp1, ShifterOperand(value_offset));
1303 __ add(temp2, temp2, ShifterOperand(value_offset));
1304
1305 Label different_compression_loop;
1306 Label different_compression_diff;
1307
1308 // Main loop for different compression.
1309 __ Bind(&different_compression_loop);
1310 __ ldrb(IP, Address(temp1, c_char_size, Address::PostIndex));
1311 __ ldrh(temp3, Address(temp2, char_size, Address::PostIndex));
1312 __ cmp(IP, ShifterOperand(temp3));
1313 __ b(&different_compression_diff, NE);
1314 __ subs(temp0, temp0, ShifterOperand(2));
1315 __ b(&different_compression_loop, HI);
jessicahandojo05765752016-09-09 19:01:32 -07001316 __ b(&end);
1317
1318 // Calculate the difference.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001319 __ Bind(&different_compression_diff);
1320 __ sub(out, IP, ShifterOperand(temp3));
1321 // Flip the difference if the `arg` is compressed.
1322 // `temp0` contains inverted `str` compression flag, i.e the same as `arg` compression flag.
1323 __ Lsrs(temp0, temp0, 1u);
1324 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1325 "Expecting 0=compressed, 1=uncompressed");
1326 __ it(CC);
1327 __ rsb(out, out, ShifterOperand(0), CC);
jessicahandojo05765752016-09-09 19:01:32 -07001328 }
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001329
1330 __ Bind(&end);
1331
1332 if (can_slow_path) {
1333 __ Bind(slow_path->GetExitLabel());
1334 }
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001335}
1336
Agi Csaki289cd552015-08-18 17:10:38 -07001337void IntrinsicLocationsBuilderARM::VisitStringEquals(HInvoke* invoke) {
1338 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1339 LocationSummary::kNoCall,
1340 kIntrinsified);
1341 InvokeRuntimeCallingConvention calling_convention;
1342 locations->SetInAt(0, Location::RequiresRegister());
1343 locations->SetInAt(1, Location::RequiresRegister());
1344 // Temporary registers to store lengths of strings and for calculations.
1345 // Using instruction cbz requires a low register, so explicitly set a temp to be R0.
1346 locations->AddTemp(Location::RegisterLocation(R0));
1347 locations->AddTemp(Location::RequiresRegister());
1348 locations->AddTemp(Location::RequiresRegister());
1349
1350 locations->SetOut(Location::RequiresRegister());
1351}
1352
1353void IntrinsicCodeGeneratorARM::VisitStringEquals(HInvoke* invoke) {
1354 ArmAssembler* assembler = GetAssembler();
1355 LocationSummary* locations = invoke->GetLocations();
1356
1357 Register str = locations->InAt(0).AsRegister<Register>();
1358 Register arg = locations->InAt(1).AsRegister<Register>();
1359 Register out = locations->Out().AsRegister<Register>();
1360
1361 Register temp = locations->GetTemp(0).AsRegister<Register>();
1362 Register temp1 = locations->GetTemp(1).AsRegister<Register>();
1363 Register temp2 = locations->GetTemp(2).AsRegister<Register>();
1364
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001365 Label loop;
Agi Csaki289cd552015-08-18 17:10:38 -07001366 Label end;
1367 Label return_true;
1368 Label return_false;
Anton Kirilov6f644202017-02-27 18:29:45 +00001369 Label* final_label = codegen_->GetFinalLabel(invoke, &end);
Agi Csaki289cd552015-08-18 17:10:38 -07001370
1371 // Get offsets of count, value, and class fields within a string object.
1372 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
1373 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1374 const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
1375
1376 // Note that the null check must have been done earlier.
1377 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1378
Vladimir Marko53b52002016-05-24 19:30:45 +01001379 StringEqualsOptimizations optimizations(invoke);
1380 if (!optimizations.GetArgumentNotNull()) {
1381 // Check if input is null, return false if it is.
1382 __ CompareAndBranchIfZero(arg, &return_false);
1383 }
Agi Csaki289cd552015-08-18 17:10:38 -07001384
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001385 // Reference equality check, return true if same reference.
1386 __ cmp(str, ShifterOperand(arg));
1387 __ b(&return_true, EQ);
1388
Vladimir Marko53b52002016-05-24 19:30:45 +01001389 if (!optimizations.GetArgumentIsString()) {
1390 // Instanceof check for the argument by comparing class fields.
1391 // All string objects must have the same type since String cannot be subclassed.
1392 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1393 // If the argument is a string object, its class field must be equal to receiver's class field.
1394 __ ldr(temp, Address(str, class_offset));
1395 __ ldr(temp1, Address(arg, class_offset));
1396 __ cmp(temp, ShifterOperand(temp1));
1397 __ b(&return_false, NE);
1398 }
Agi Csaki289cd552015-08-18 17:10:38 -07001399
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001400 // Load `count` fields of this and argument strings.
Agi Csaki289cd552015-08-18 17:10:38 -07001401 __ ldr(temp, Address(str, count_offset));
1402 __ ldr(temp1, Address(arg, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001403 // Check if `count` fields are equal, return false if they're not.
jessicahandojo05765752016-09-09 19:01:32 -07001404 // Also compares the compression style, if differs return false.
Agi Csaki289cd552015-08-18 17:10:38 -07001405 __ cmp(temp, ShifterOperand(temp1));
1406 __ b(&return_false, NE);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001407 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
1408 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1409 "Expecting 0=compressed, 1=uncompressed");
Agi Csaki289cd552015-08-18 17:10:38 -07001410 __ cbz(temp, &return_true);
Agi Csaki289cd552015-08-18 17:10:38 -07001411
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001412 // Assertions that must hold in order to compare strings 4 bytes at a time.
Agi Csaki289cd552015-08-18 17:10:38 -07001413 DCHECK_ALIGNED(value_offset, 4);
Scott Wakelingc25cbf12016-04-18 09:00:11 +01001414 static_assert(IsAligned<4>(kObjectAlignment), "String data must be aligned for fast compare.");
Agi Csaki289cd552015-08-18 17:10:38 -07001415
jessicahandojo05765752016-09-09 19:01:32 -07001416 if (mirror::kUseStringCompression) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001417 // For string compression, calculate the number of bytes to compare (not chars).
1418 // This could in theory exceed INT32_MAX, so treat temp as unsigned.
1419 __ Lsrs(temp, temp, 1u); // Extract length and check compression flag.
1420 __ it(CS); // If uncompressed,
1421 __ add(temp, temp, ShifterOperand(temp), CS); // double the byte count.
jessicahandojo05765752016-09-09 19:01:32 -07001422 }
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001423
1424 // Store offset of string value in preparation for comparison loop.
jessicahandojo05765752016-09-09 19:01:32 -07001425 __ LoadImmediate(temp1, value_offset);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001426
1427 // Loop to compare strings 4 bytes at a time starting at the front of the string.
1428 // Ok to do this because strings are zero-padded to kObjectAlignment.
Agi Csaki289cd552015-08-18 17:10:38 -07001429 __ Bind(&loop);
1430 __ ldr(out, Address(str, temp1));
1431 __ ldr(temp2, Address(arg, temp1));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001432 __ add(temp1, temp1, ShifterOperand(sizeof(uint32_t)));
Agi Csaki289cd552015-08-18 17:10:38 -07001433 __ cmp(out, ShifterOperand(temp2));
1434 __ b(&return_false, NE);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001435 // With string compression, we have compared 4 bytes, otherwise 2 chars.
1436 __ subs(temp, temp, ShifterOperand(mirror::kUseStringCompression ? 4 : 2));
1437 __ b(&loop, HI);
Agi Csaki289cd552015-08-18 17:10:38 -07001438
1439 // Return true and exit the function.
1440 // If loop does not result in returning false, we return true.
1441 __ Bind(&return_true);
1442 __ LoadImmediate(out, 1);
Anton Kirilov6f644202017-02-27 18:29:45 +00001443 __ b(final_label);
Agi Csaki289cd552015-08-18 17:10:38 -07001444
1445 // Return false and exit the function.
1446 __ Bind(&return_false);
1447 __ LoadImmediate(out, 0);
Anton Kirilov6f644202017-02-27 18:29:45 +00001448
1449 if (end.IsLinked()) {
1450 __ Bind(&end);
1451 }
Agi Csaki289cd552015-08-18 17:10:38 -07001452}
1453
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001454static void GenerateVisitStringIndexOf(HInvoke* invoke,
1455 ArmAssembler* assembler,
1456 CodeGeneratorARM* codegen,
1457 ArenaAllocator* allocator,
1458 bool start_at_zero) {
1459 LocationSummary* locations = invoke->GetLocations();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001460
1461 // Note that the null check must have been done earlier.
1462 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1463
1464 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001465 // 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 -07001466 SlowPathCode* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001467 HInstruction* code_point = invoke->InputAt(1);
1468 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01001469 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) >
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001470 std::numeric_limits<uint16_t>::max()) {
1471 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1472 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1473 slow_path = new (allocator) IntrinsicSlowPathARM(invoke);
1474 codegen->AddSlowPath(slow_path);
1475 __ b(slow_path->GetEntryLabel());
1476 __ Bind(slow_path->GetExitLabel());
1477 return;
1478 }
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001479 } else if (code_point->GetType() != Primitive::kPrimChar) {
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001480 Register char_reg = locations->InAt(1).AsRegister<Register>();
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001481 // 0xffff is not modified immediate but 0x10000 is, so use `>= 0x10000` instead of `> 0xffff`.
1482 __ cmp(char_reg,
1483 ShifterOperand(static_cast<uint32_t>(std::numeric_limits<uint16_t>::max()) + 1));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001484 slow_path = new (allocator) IntrinsicSlowPathARM(invoke);
1485 codegen->AddSlowPath(slow_path);
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001486 __ b(slow_path->GetEntryLabel(), HS);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001487 }
1488
1489 if (start_at_zero) {
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001490 Register tmp_reg = locations->GetTemp(0).AsRegister<Register>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001491 DCHECK_EQ(tmp_reg, R2);
1492 // Start-index = 0.
1493 __ LoadImmediate(tmp_reg, 0);
1494 }
1495
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01001496 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
Roland Levillain42ad2882016-02-29 18:26:54 +00001497 CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001498
1499 if (slow_path != nullptr) {
1500 __ Bind(slow_path->GetExitLabel());
1501 }
1502}
1503
1504void IntrinsicLocationsBuilderARM::VisitStringIndexOf(HInvoke* invoke) {
1505 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001506 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001507 kIntrinsified);
1508 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1509 // best to align the inputs accordingly.
1510 InvokeRuntimeCallingConvention calling_convention;
1511 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1512 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1513 locations->SetOut(Location::RegisterLocation(R0));
1514
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001515 // Need to send start-index=0.
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001516 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1517}
1518
1519void IntrinsicCodeGeneratorARM::VisitStringIndexOf(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001520 GenerateVisitStringIndexOf(
1521 invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001522}
1523
1524void IntrinsicLocationsBuilderARM::VisitStringIndexOfAfter(HInvoke* invoke) {
1525 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001526 LocationSummary::kCallOnMainAndSlowPath,
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001527 kIntrinsified);
1528 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1529 // best to align the inputs accordingly.
1530 InvokeRuntimeCallingConvention calling_convention;
1531 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1532 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1533 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1534 locations->SetOut(Location::RegisterLocation(R0));
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001535}
1536
1537void IntrinsicCodeGeneratorARM::VisitStringIndexOfAfter(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001538 GenerateVisitStringIndexOf(
1539 invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001540}
1541
Jeff Hao848f70a2014-01-15 13:49:50 -08001542void IntrinsicLocationsBuilderARM::VisitStringNewStringFromBytes(HInvoke* invoke) {
1543 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001544 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001545 kIntrinsified);
1546 InvokeRuntimeCallingConvention calling_convention;
1547 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1548 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1549 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1550 locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
1551 locations->SetOut(Location::RegisterLocation(R0));
1552}
1553
1554void IntrinsicCodeGeneratorARM::VisitStringNewStringFromBytes(HInvoke* invoke) {
1555 ArmAssembler* assembler = GetAssembler();
1556 LocationSummary* locations = invoke->GetLocations();
1557
1558 Register byte_array = locations->InAt(0).AsRegister<Register>();
1559 __ cmp(byte_array, ShifterOperand(0));
Andreas Gampe85b62f22015-09-09 13:15:38 -07001560 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
Jeff Hao848f70a2014-01-15 13:49:50 -08001561 codegen_->AddSlowPath(slow_path);
1562 __ b(slow_path->GetEntryLabel(), EQ);
1563
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01001564 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001565 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001566 __ Bind(slow_path->GetExitLabel());
1567}
1568
1569void IntrinsicLocationsBuilderARM::VisitStringNewStringFromChars(HInvoke* invoke) {
1570 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01001571 LocationSummary::kCallOnMainOnly,
Jeff Hao848f70a2014-01-15 13:49:50 -08001572 kIntrinsified);
1573 InvokeRuntimeCallingConvention calling_convention;
1574 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1575 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1576 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1577 locations->SetOut(Location::RegisterLocation(R0));
1578}
1579
1580void IntrinsicCodeGeneratorARM::VisitStringNewStringFromChars(HInvoke* invoke) {
Roland Levillaincc3839c2016-02-29 16:23:48 +00001581 // No need to emit code checking whether `locations->InAt(2)` is a null
1582 // pointer, as callers of the native method
1583 //
1584 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1585 //
1586 // all include a null check on `data` before calling that method.
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01001587 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
Roland Levillainf969a202016-03-09 16:14:00 +00001588 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001589}
1590
1591void IntrinsicLocationsBuilderARM::VisitStringNewStringFromString(HInvoke* invoke) {
1592 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00001593 LocationSummary::kCallOnMainAndSlowPath,
Jeff Hao848f70a2014-01-15 13:49:50 -08001594 kIntrinsified);
1595 InvokeRuntimeCallingConvention calling_convention;
1596 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1597 locations->SetOut(Location::RegisterLocation(R0));
1598}
1599
1600void IntrinsicCodeGeneratorARM::VisitStringNewStringFromString(HInvoke* invoke) {
1601 ArmAssembler* assembler = GetAssembler();
1602 LocationSummary* locations = invoke->GetLocations();
1603
1604 Register string_to_copy = locations->InAt(0).AsRegister<Register>();
1605 __ cmp(string_to_copy, ShifterOperand(0));
Andreas Gampe85b62f22015-09-09 13:15:38 -07001606 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
Jeff Hao848f70a2014-01-15 13:49:50 -08001607 codegen_->AddSlowPath(slow_path);
1608 __ b(slow_path->GetEntryLabel(), EQ);
1609
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01001610 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
Roland Levillainf969a202016-03-09 16:14:00 +00001611 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01001612
Jeff Hao848f70a2014-01-15 13:49:50 -08001613 __ Bind(slow_path->GetExitLabel());
1614}
1615
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001616void IntrinsicLocationsBuilderARM::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01001617 // The only read barrier implementation supporting the
1618 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1619 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain3d312422016-06-23 13:53:42 +01001620 return;
1621 }
1622
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001623 CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
1624 LocationSummary* locations = invoke->GetLocations();
1625 if (locations == nullptr) {
1626 return;
1627 }
1628
1629 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1630 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
1631 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1632
1633 if (src_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(src_pos->GetValue())) {
1634 locations->SetInAt(1, Location::RequiresRegister());
1635 }
1636 if (dest_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(dest_pos->GetValue())) {
1637 locations->SetInAt(3, Location::RequiresRegister());
1638 }
1639 if (length != nullptr && !assembler_->ShifterOperandCanAlwaysHold(length->GetValue())) {
1640 locations->SetInAt(4, Location::RequiresRegister());
1641 }
Roland Levillain0b671c02016-08-19 12:02:34 +01001642 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1643 // Temporary register IP cannot be used in
Roland Levillain16d9f942016-08-25 17:27:56 +01001644 // ReadBarrierSystemArrayCopySlowPathARM (because that register
Roland Levillain0b671c02016-08-19 12:02:34 +01001645 // is clobbered by ReadBarrierMarkRegX entry points). Get an extra
1646 // temporary register from the register allocator.
1647 locations->AddTemp(Location::RequiresRegister());
1648 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001649}
1650
1651static void CheckPosition(ArmAssembler* assembler,
1652 Location pos,
1653 Register input,
1654 Location length,
1655 SlowPathCode* slow_path,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001656 Register temp,
1657 bool length_is_input_length = false) {
1658 // Where is the length in the Array?
1659 const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
1660
1661 if (pos.IsConstant()) {
1662 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
1663 if (pos_const == 0) {
1664 if (!length_is_input_length) {
1665 // Check that length(input) >= length.
1666 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
1667 if (length.IsConstant()) {
1668 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1669 } else {
1670 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1671 }
1672 __ b(slow_path->GetEntryLabel(), LT);
1673 }
1674 } else {
1675 // Check that length(input) >= pos.
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01001676 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
1677 __ subs(temp, temp, ShifterOperand(pos_const));
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001678 __ b(slow_path->GetEntryLabel(), LT);
1679
1680 // Check that (length(input) - pos) >= length.
1681 if (length.IsConstant()) {
1682 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1683 } else {
1684 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1685 }
1686 __ b(slow_path->GetEntryLabel(), LT);
1687 }
1688 } else if (length_is_input_length) {
1689 // The only way the copy can succeed is if pos is zero.
1690 Register pos_reg = pos.AsRegister<Register>();
1691 __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
1692 } else {
1693 // Check that pos >= 0.
1694 Register pos_reg = pos.AsRegister<Register>();
1695 __ cmp(pos_reg, ShifterOperand(0));
1696 __ b(slow_path->GetEntryLabel(), LT);
1697
1698 // Check that pos <= length(input).
1699 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
1700 __ subs(temp, temp, ShifterOperand(pos_reg));
1701 __ b(slow_path->GetEntryLabel(), LT);
1702
1703 // Check that (length(input) - pos) >= length.
1704 if (length.IsConstant()) {
1705 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1706 } else {
1707 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1708 }
1709 __ b(slow_path->GetEntryLabel(), LT);
1710 }
1711}
1712
1713void IntrinsicCodeGeneratorARM::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01001714 // The only read barrier implementation supporting the
1715 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1716 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01001717
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001718 ArmAssembler* assembler = GetAssembler();
1719 LocationSummary* locations = invoke->GetLocations();
1720
1721 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1722 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
1723 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
1724 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Roland Levillain0b671c02016-08-19 12:02:34 +01001725 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001726
1727 Register src = locations->InAt(0).AsRegister<Register>();
1728 Location src_pos = locations->InAt(1);
1729 Register dest = locations->InAt(2).AsRegister<Register>();
1730 Location dest_pos = locations->InAt(3);
1731 Location length = locations->InAt(4);
Roland Levillain0b671c02016-08-19 12:02:34 +01001732 Location temp1_loc = locations->GetTemp(0);
1733 Register temp1 = temp1_loc.AsRegister<Register>();
1734 Location temp2_loc = locations->GetTemp(1);
1735 Register temp2 = temp2_loc.AsRegister<Register>();
1736 Location temp3_loc = locations->GetTemp(2);
1737 Register temp3 = temp3_loc.AsRegister<Register>();
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001738
Roland Levillain0b671c02016-08-19 12:02:34 +01001739 SlowPathCode* intrinsic_slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
1740 codegen_->AddSlowPath(intrinsic_slow_path);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001741
Roland Levillainebea3d22016-04-12 15:42:57 +01001742 Label conditions_on_positions_validated;
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001743 SystemArrayCopyOptimizations optimizations(invoke);
1744
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001745 // If source and destination are the same, we go to slow path if we need to do
1746 // forward copying.
1747 if (src_pos.IsConstant()) {
1748 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
1749 if (dest_pos.IsConstant()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001750 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
1751 if (optimizations.GetDestinationIsSource()) {
1752 // Checked when building locations.
1753 DCHECK_GE(src_pos_constant, dest_pos_constant);
1754 } else if (src_pos_constant < dest_pos_constant) {
1755 __ cmp(src, ShifterOperand(dest));
Roland Levillain0b671c02016-08-19 12:02:34 +01001756 __ b(intrinsic_slow_path->GetEntryLabel(), EQ);
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001757 }
1758
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001759 // Checked when building locations.
1760 DCHECK(!optimizations.GetDestinationIsSource()
1761 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
1762 } else {
1763 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001764 __ cmp(src, ShifterOperand(dest));
Roland Levillainebea3d22016-04-12 15:42:57 +01001765 __ b(&conditions_on_positions_validated, NE);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001766 }
1767 __ cmp(dest_pos.AsRegister<Register>(), ShifterOperand(src_pos_constant));
Roland Levillain0b671c02016-08-19 12:02:34 +01001768 __ b(intrinsic_slow_path->GetEntryLabel(), GT);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001769 }
1770 } else {
1771 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001772 __ cmp(src, ShifterOperand(dest));
Roland Levillainebea3d22016-04-12 15:42:57 +01001773 __ b(&conditions_on_positions_validated, NE);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001774 }
1775 if (dest_pos.IsConstant()) {
1776 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
1777 __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos_constant));
1778 } else {
1779 __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos.AsRegister<Register>()));
1780 }
Roland Levillain0b671c02016-08-19 12:02:34 +01001781 __ b(intrinsic_slow_path->GetEntryLabel(), LT);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001782 }
1783
Roland Levillainebea3d22016-04-12 15:42:57 +01001784 __ Bind(&conditions_on_positions_validated);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001785
1786 if (!optimizations.GetSourceIsNotNull()) {
1787 // Bail out if the source is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01001788 __ CompareAndBranchIfZero(src, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001789 }
1790
1791 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
1792 // Bail out if the destination is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01001793 __ CompareAndBranchIfZero(dest, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001794 }
1795
1796 // If the length is negative, bail out.
1797 // We have already checked in the LocationsBuilder for the constant case.
1798 if (!length.IsConstant() &&
1799 !optimizations.GetCountIsSourceLength() &&
1800 !optimizations.GetCountIsDestinationLength()) {
1801 __ cmp(length.AsRegister<Register>(), ShifterOperand(0));
Roland Levillain0b671c02016-08-19 12:02:34 +01001802 __ b(intrinsic_slow_path->GetEntryLabel(), LT);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001803 }
1804
1805 // Validity checks: source.
1806 CheckPosition(assembler,
1807 src_pos,
1808 src,
1809 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01001810 intrinsic_slow_path,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001811 temp1,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001812 optimizations.GetCountIsSourceLength());
1813
1814 // Validity checks: dest.
1815 CheckPosition(assembler,
1816 dest_pos,
1817 dest,
1818 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01001819 intrinsic_slow_path,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001820 temp1,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001821 optimizations.GetCountIsDestinationLength());
1822
1823 if (!optimizations.GetDoesNotNeedTypeCheck()) {
1824 // Check whether all elements of the source array are assignable to the component
1825 // type of the destination array. We do two checks: the classes are the same,
1826 // or the destination is Object[]. If none of these checks succeed, we go to the
1827 // slow path.
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001828
Roland Levillain0b671c02016-08-19 12:02:34 +01001829 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1830 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1831 // /* HeapReference<Class> */ temp1 = src->klass_
1832 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1833 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
1834 // Bail out if the source is not a non primitive array.
1835 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1836 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1837 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
1838 __ CompareAndBranchIfZero(temp1, intrinsic_slow_path->GetEntryLabel());
1839 // If heap poisoning is enabled, `temp1` has been unpoisoned
1840 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1841 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
1842 __ LoadFromOffset(kLoadUnsignedHalfword, temp1, temp1, primitive_offset);
1843 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1844 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001845 }
Roland Levillain0b671c02016-08-19 12:02:34 +01001846
1847 // /* HeapReference<Class> */ temp1 = dest->klass_
1848 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1849 invoke, temp1_loc, dest, class_offset, temp2_loc, /* needs_null_check */ false);
1850
1851 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
1852 // Bail out if the destination is not a non primitive array.
1853 //
1854 // Register `temp1` is not trashed by the read barrier emitted
1855 // by GenerateFieldLoadWithBakerReadBarrier below, as that
1856 // method produces a call to a ReadBarrierMarkRegX entry point,
1857 // which saves all potentially live registers, including
1858 // temporaries such a `temp1`.
1859 // /* HeapReference<Class> */ temp2 = temp1->component_type_
1860 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1861 invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
1862 __ CompareAndBranchIfZero(temp2, intrinsic_slow_path->GetEntryLabel());
1863 // If heap poisoning is enabled, `temp2` has been unpoisoned
1864 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1865 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
1866 __ LoadFromOffset(kLoadUnsignedHalfword, temp2, temp2, primitive_offset);
1867 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1868 __ CompareAndBranchIfNonZero(temp2, intrinsic_slow_path->GetEntryLabel());
1869 }
1870
1871 // For the same reason given earlier, `temp1` is not trashed by the
1872 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
1873 // /* HeapReference<Class> */ temp2 = src->klass_
1874 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1875 invoke, temp2_loc, src, class_offset, temp3_loc, /* needs_null_check */ false);
1876 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
1877 __ cmp(temp1, ShifterOperand(temp2));
1878
1879 if (optimizations.GetDestinationIsTypedObjectArray()) {
1880 Label do_copy;
1881 __ b(&do_copy, EQ);
1882 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1883 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1884 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
1885 // /* HeapReference<Class> */ temp1 = temp1->super_class_
1886 // We do not need to emit a read barrier for the following
1887 // heap reference load, as `temp1` is only used in a
1888 // comparison with null below, and this reference is not
1889 // kept afterwards.
1890 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
1891 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
1892 __ Bind(&do_copy);
1893 } else {
1894 __ b(intrinsic_slow_path->GetEntryLabel(), NE);
1895 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001896 } else {
Roland Levillain0b671c02016-08-19 12:02:34 +01001897 // Non read barrier code.
1898
1899 // /* HeapReference<Class> */ temp1 = dest->klass_
1900 __ LoadFromOffset(kLoadWord, temp1, dest, class_offset);
1901 // /* HeapReference<Class> */ temp2 = src->klass_
1902 __ LoadFromOffset(kLoadWord, temp2, src, class_offset);
1903 bool did_unpoison = false;
1904 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
1905 !optimizations.GetSourceIsNonPrimitiveArray()) {
1906 // One or two of the references need to be unpoisoned. Unpoison them
1907 // both to make the identity check valid.
1908 __ MaybeUnpoisonHeapReference(temp1);
1909 __ MaybeUnpoisonHeapReference(temp2);
1910 did_unpoison = true;
1911 }
1912
1913 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
1914 // Bail out if the destination is not a non primitive array.
1915 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1916 __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
1917 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1918 __ MaybeUnpoisonHeapReference(temp3);
1919 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
1920 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1921 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1922 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
1923 }
1924
1925 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1926 // Bail out if the source is not a non primitive array.
1927 // /* HeapReference<Class> */ temp3 = temp2->component_type_
1928 __ LoadFromOffset(kLoadWord, temp3, temp2, component_offset);
1929 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1930 __ MaybeUnpoisonHeapReference(temp3);
1931 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
1932 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1933 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1934 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
1935 }
1936
1937 __ cmp(temp1, ShifterOperand(temp2));
1938
1939 if (optimizations.GetDestinationIsTypedObjectArray()) {
1940 Label do_copy;
1941 __ b(&do_copy, EQ);
1942 if (!did_unpoison) {
1943 __ MaybeUnpoisonHeapReference(temp1);
1944 }
1945 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1946 __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
1947 __ MaybeUnpoisonHeapReference(temp1);
1948 // /* HeapReference<Class> */ temp1 = temp1->super_class_
1949 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
1950 // No need to unpoison the result, we're comparing against null.
1951 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
1952 __ Bind(&do_copy);
1953 } else {
1954 __ b(intrinsic_slow_path->GetEntryLabel(), NE);
1955 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001956 }
1957 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1958 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
1959 // Bail out if the source is not a non primitive array.
Roland Levillain0b671c02016-08-19 12:02:34 +01001960 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1961 // /* HeapReference<Class> */ temp1 = src->klass_
1962 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1963 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
1964 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1965 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1966 invoke, temp3_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
1967 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1968 // If heap poisoning is enabled, `temp3` has been unpoisoned
1969 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1970 } else {
1971 // /* HeapReference<Class> */ temp1 = src->klass_
1972 __ LoadFromOffset(kLoadWord, temp1, src, class_offset);
1973 __ MaybeUnpoisonHeapReference(temp1);
1974 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1975 __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
1976 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1977 __ MaybeUnpoisonHeapReference(temp3);
1978 }
1979 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001980 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1981 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Roland Levillain0b671c02016-08-19 12:02:34 +01001982 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001983 }
1984
Roland Levillain1663d162017-03-17 15:15:21 +00001985 if (length.IsConstant() && length.GetConstant()->AsIntConstant()->GetValue() == 0) {
1986 // Null constant length: not need to emit the loop code at all.
Roland Levillain0b671c02016-08-19 12:02:34 +01001987 } else {
Roland Levillain1663d162017-03-17 15:15:21 +00001988 Label done;
1989 const Primitive::Type type = Primitive::kPrimNot;
1990 const int32_t element_size = Primitive::ComponentSize(type);
1991
1992 if (length.IsRegister()) {
1993 // Don't enter the copy loop if the length is null.
1994 __ CompareAndBranchIfZero(length.AsRegister<Register>(), &done);
1995 }
1996
1997 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1998 // TODO: Also convert this intrinsic to the IsGcMarking strategy?
1999
2000 // SystemArrayCopy implementation for Baker read barriers (see
2001 // also CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier):
2002 //
2003 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
2004 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
2005 // bool is_gray = (rb_state == ReadBarrier::GrayState());
2006 // if (is_gray) {
2007 // // Slow-path copy.
2008 // do {
2009 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
2010 // } while (src_ptr != end_ptr)
2011 // } else {
2012 // // Fast-path copy.
2013 // do {
2014 // *dest_ptr++ = *src_ptr++;
2015 // } while (src_ptr != end_ptr)
2016 // }
2017
2018 // /* int32_t */ monitor = src->monitor_
2019 __ LoadFromOffset(kLoadWord, temp2, src, monitor_offset);
2020 // /* LockWord */ lock_word = LockWord(monitor)
2021 static_assert(sizeof(LockWord) == sizeof(int32_t),
2022 "art::LockWord and int32_t have different sizes.");
2023
2024 // Introduce a dependency on the lock_word including the rb_state,
2025 // which shall prevent load-load reordering without using
2026 // a memory barrier (which would be more expensive).
2027 // `src` is unchanged by this operation, but its value now depends
2028 // on `temp2`.
2029 __ add(src, src, ShifterOperand(temp2, LSR, 32));
2030
2031 // Compute the base source address in `temp1`.
2032 // Note that `temp1` (the base source address) is computed from
2033 // `src` (and `src_pos`) here, and thus honors the artificial
2034 // dependency of `src` on `temp2`.
2035 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2036 // Compute the end source address in `temp3`.
2037 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2038 // The base destination address is computed later, as `temp2` is
2039 // used for intermediate computations.
2040
2041 // Slow path used to copy array when `src` is gray.
2042 // Note that the base destination address is computed in `temp2`
2043 // by the slow path code.
2044 SlowPathCode* read_barrier_slow_path =
2045 new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathARM(invoke);
2046 codegen_->AddSlowPath(read_barrier_slow_path);
2047
2048 // Given the numeric representation, it's enough to check the low bit of the
2049 // rb_state. We do that by shifting the bit out of the lock word with LSRS
2050 // which can be a 16-bit instruction unlike the TST immediate.
2051 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
2052 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
2053 __ Lsrs(temp2, temp2, LockWord::kReadBarrierStateShift + 1);
2054 // Carry flag is the last bit shifted out by LSRS.
2055 __ b(read_barrier_slow_path->GetEntryLabel(), CS);
2056
2057 // Fast-path copy.
2058 // Compute the base destination address in `temp2`.
2059 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2060 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2061 // poison/unpoison.
2062 Label loop;
2063 __ Bind(&loop);
2064 __ ldr(IP, Address(temp1, element_size, Address::PostIndex));
2065 __ str(IP, Address(temp2, element_size, Address::PostIndex));
2066 __ cmp(temp1, ShifterOperand(temp3));
2067 __ b(&loop, NE);
2068
2069 __ Bind(read_barrier_slow_path->GetExitLabel());
2070 } else {
2071 // Non read barrier code.
2072 // Compute the base source address in `temp1`.
2073 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2074 // Compute the base destination address in `temp2`.
2075 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2076 // Compute the end source address in `temp3`.
2077 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2078 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2079 // poison/unpoison.
2080 Label loop;
2081 __ Bind(&loop);
2082 __ ldr(IP, Address(temp1, element_size, Address::PostIndex));
2083 __ str(IP, Address(temp2, element_size, Address::PostIndex));
2084 __ cmp(temp1, ShifterOperand(temp3));
2085 __ b(&loop, NE);
2086 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002087 __ Bind(&done);
2088 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01002089
2090 // We only need one card marking on the destination array.
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002091 codegen_->MarkGCCard(temp1, temp2, dest, Register(kNoRegister), /* value_can_be_null */ false);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01002092
Roland Levillain0b671c02016-08-19 12:02:34 +01002093 __ Bind(intrinsic_slow_path->GetExitLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01002094}
2095
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002096static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
2097 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2098 // the code generator. Furthermore, the register allocator creates fixed live intervals
2099 // for all caller-saved registers because we are doing a function call. As a result, if
2100 // the input and output locations are unallocated, the register allocator runs out of
2101 // registers and fails; however, a debuggable graph is not the common case.
2102 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2103 return;
2104 }
2105
2106 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2107 DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
2108 DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
2109
2110 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01002111 LocationSummary::kCallOnMainOnly,
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002112 kIntrinsified);
2113 const InvokeRuntimeCallingConvention calling_convention;
2114
2115 locations->SetInAt(0, Location::RequiresFpuRegister());
2116 locations->SetOut(Location::RequiresFpuRegister());
2117 // Native code uses the soft float ABI.
2118 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2119 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2120}
2121
2122static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
2123 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2124 // the code generator. Furthermore, the register allocator creates fixed live intervals
2125 // for all caller-saved registers because we are doing a function call. As a result, if
2126 // the input and output locations are unallocated, the register allocator runs out of
2127 // registers and fails; however, a debuggable graph is not the common case.
2128 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2129 return;
2130 }
2131
2132 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2133 DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
2134 DCHECK_EQ(invoke->InputAt(1)->GetType(), Primitive::kPrimDouble);
2135 DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
2136
2137 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01002138 LocationSummary::kCallOnMainOnly,
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002139 kIntrinsified);
2140 const InvokeRuntimeCallingConvention calling_convention;
2141
2142 locations->SetInAt(0, Location::RequiresFpuRegister());
2143 locations->SetInAt(1, Location::RequiresFpuRegister());
2144 locations->SetOut(Location::RequiresFpuRegister());
2145 // Native code uses the soft float ABI.
2146 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2147 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2148 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
2149 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
2150}
2151
2152static void GenFPToFPCall(HInvoke* invoke,
2153 ArmAssembler* assembler,
2154 CodeGeneratorARM* codegen,
2155 QuickEntrypointEnum entry) {
2156 LocationSummary* const locations = invoke->GetLocations();
2157 const InvokeRuntimeCallingConvention calling_convention;
2158
2159 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2160 DCHECK(locations->WillCall() && locations->Intrinsified());
2161 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
2162 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
2163
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002164 // Native code uses the soft float ABI.
2165 __ vmovrrd(calling_convention.GetRegisterAt(0),
2166 calling_convention.GetRegisterAt(1),
2167 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01002168 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002169 __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
2170 calling_convention.GetRegisterAt(0),
2171 calling_convention.GetRegisterAt(1));
2172}
2173
2174static void GenFPFPToFPCall(HInvoke* invoke,
2175 ArmAssembler* assembler,
2176 CodeGeneratorARM* codegen,
2177 QuickEntrypointEnum entry) {
2178 LocationSummary* const locations = invoke->GetLocations();
2179 const InvokeRuntimeCallingConvention calling_convention;
2180
2181 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2182 DCHECK(locations->WillCall() && locations->Intrinsified());
2183 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
2184 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
2185 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(2)));
2186 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(3)));
2187
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002188 // Native code uses the soft float ABI.
2189 __ vmovrrd(calling_convention.GetRegisterAt(0),
2190 calling_convention.GetRegisterAt(1),
2191 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
2192 __ vmovrrd(calling_convention.GetRegisterAt(2),
2193 calling_convention.GetRegisterAt(3),
2194 FromLowSToD(locations->InAt(1).AsFpuRegisterPairLow<SRegister>()));
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01002195 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002196 __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
2197 calling_convention.GetRegisterAt(0),
2198 calling_convention.GetRegisterAt(1));
2199}
2200
2201void IntrinsicLocationsBuilderARM::VisitMathCos(HInvoke* invoke) {
2202 CreateFPToFPCallLocations(arena_, invoke);
2203}
2204
2205void IntrinsicCodeGeneratorARM::VisitMathCos(HInvoke* invoke) {
2206 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCos);
2207}
2208
2209void IntrinsicLocationsBuilderARM::VisitMathSin(HInvoke* invoke) {
2210 CreateFPToFPCallLocations(arena_, invoke);
2211}
2212
2213void IntrinsicCodeGeneratorARM::VisitMathSin(HInvoke* invoke) {
2214 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSin);
2215}
2216
2217void IntrinsicLocationsBuilderARM::VisitMathAcos(HInvoke* invoke) {
2218 CreateFPToFPCallLocations(arena_, invoke);
2219}
2220
2221void IntrinsicCodeGeneratorARM::VisitMathAcos(HInvoke* invoke) {
2222 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAcos);
2223}
2224
2225void IntrinsicLocationsBuilderARM::VisitMathAsin(HInvoke* invoke) {
2226 CreateFPToFPCallLocations(arena_, invoke);
2227}
2228
2229void IntrinsicCodeGeneratorARM::VisitMathAsin(HInvoke* invoke) {
2230 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAsin);
2231}
2232
2233void IntrinsicLocationsBuilderARM::VisitMathAtan(HInvoke* invoke) {
2234 CreateFPToFPCallLocations(arena_, invoke);
2235}
2236
2237void IntrinsicCodeGeneratorARM::VisitMathAtan(HInvoke* invoke) {
2238 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan);
2239}
2240
2241void IntrinsicLocationsBuilderARM::VisitMathCbrt(HInvoke* invoke) {
2242 CreateFPToFPCallLocations(arena_, invoke);
2243}
2244
2245void IntrinsicCodeGeneratorARM::VisitMathCbrt(HInvoke* invoke) {
2246 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCbrt);
2247}
2248
2249void IntrinsicLocationsBuilderARM::VisitMathCosh(HInvoke* invoke) {
2250 CreateFPToFPCallLocations(arena_, invoke);
2251}
2252
2253void IntrinsicCodeGeneratorARM::VisitMathCosh(HInvoke* invoke) {
2254 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCosh);
2255}
2256
2257void IntrinsicLocationsBuilderARM::VisitMathExp(HInvoke* invoke) {
2258 CreateFPToFPCallLocations(arena_, invoke);
2259}
2260
2261void IntrinsicCodeGeneratorARM::VisitMathExp(HInvoke* invoke) {
2262 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExp);
2263}
2264
2265void IntrinsicLocationsBuilderARM::VisitMathExpm1(HInvoke* invoke) {
2266 CreateFPToFPCallLocations(arena_, invoke);
2267}
2268
2269void IntrinsicCodeGeneratorARM::VisitMathExpm1(HInvoke* invoke) {
2270 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExpm1);
2271}
2272
2273void IntrinsicLocationsBuilderARM::VisitMathLog(HInvoke* invoke) {
2274 CreateFPToFPCallLocations(arena_, invoke);
2275}
2276
2277void IntrinsicCodeGeneratorARM::VisitMathLog(HInvoke* invoke) {
2278 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog);
2279}
2280
2281void IntrinsicLocationsBuilderARM::VisitMathLog10(HInvoke* invoke) {
2282 CreateFPToFPCallLocations(arena_, invoke);
2283}
2284
2285void IntrinsicCodeGeneratorARM::VisitMathLog10(HInvoke* invoke) {
2286 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog10);
2287}
2288
2289void IntrinsicLocationsBuilderARM::VisitMathSinh(HInvoke* invoke) {
2290 CreateFPToFPCallLocations(arena_, invoke);
2291}
2292
2293void IntrinsicCodeGeneratorARM::VisitMathSinh(HInvoke* invoke) {
2294 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSinh);
2295}
2296
2297void IntrinsicLocationsBuilderARM::VisitMathTan(HInvoke* invoke) {
2298 CreateFPToFPCallLocations(arena_, invoke);
2299}
2300
2301void IntrinsicCodeGeneratorARM::VisitMathTan(HInvoke* invoke) {
2302 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTan);
2303}
2304
2305void IntrinsicLocationsBuilderARM::VisitMathTanh(HInvoke* invoke) {
2306 CreateFPToFPCallLocations(arena_, invoke);
2307}
2308
2309void IntrinsicCodeGeneratorARM::VisitMathTanh(HInvoke* invoke) {
2310 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTanh);
2311}
2312
2313void IntrinsicLocationsBuilderARM::VisitMathAtan2(HInvoke* invoke) {
2314 CreateFPFPToFPCallLocations(arena_, invoke);
2315}
2316
2317void IntrinsicCodeGeneratorARM::VisitMathAtan2(HInvoke* invoke) {
2318 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan2);
2319}
2320
2321void IntrinsicLocationsBuilderARM::VisitMathHypot(HInvoke* invoke) {
2322 CreateFPFPToFPCallLocations(arena_, invoke);
2323}
2324
2325void IntrinsicCodeGeneratorARM::VisitMathHypot(HInvoke* invoke) {
2326 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickHypot);
2327}
2328
2329void IntrinsicLocationsBuilderARM::VisitMathNextAfter(HInvoke* invoke) {
2330 CreateFPFPToFPCallLocations(arena_, invoke);
2331}
2332
2333void IntrinsicCodeGeneratorARM::VisitMathNextAfter(HInvoke* invoke) {
2334 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter);
2335}
2336
Artem Serovc257da72016-02-02 13:49:43 +00002337void IntrinsicLocationsBuilderARM::VisitIntegerReverse(HInvoke* invoke) {
2338 CreateIntToIntLocations(arena_, invoke);
2339}
2340
2341void IntrinsicCodeGeneratorARM::VisitIntegerReverse(HInvoke* invoke) {
2342 ArmAssembler* assembler = GetAssembler();
2343 LocationSummary* locations = invoke->GetLocations();
2344
2345 Register out = locations->Out().AsRegister<Register>();
2346 Register in = locations->InAt(0).AsRegister<Register>();
2347
2348 __ rbit(out, in);
2349}
2350
2351void IntrinsicLocationsBuilderARM::VisitLongReverse(HInvoke* invoke) {
2352 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2353 LocationSummary::kNoCall,
2354 kIntrinsified);
2355 locations->SetInAt(0, Location::RequiresRegister());
2356 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2357}
2358
2359void IntrinsicCodeGeneratorARM::VisitLongReverse(HInvoke* invoke) {
2360 ArmAssembler* assembler = GetAssembler();
2361 LocationSummary* locations = invoke->GetLocations();
2362
2363 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
2364 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
2365 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
2366 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
2367
2368 __ rbit(out_reg_lo, in_reg_hi);
2369 __ rbit(out_reg_hi, in_reg_lo);
2370}
2371
2372void IntrinsicLocationsBuilderARM::VisitIntegerReverseBytes(HInvoke* invoke) {
2373 CreateIntToIntLocations(arena_, invoke);
2374}
2375
2376void IntrinsicCodeGeneratorARM::VisitIntegerReverseBytes(HInvoke* invoke) {
2377 ArmAssembler* assembler = GetAssembler();
2378 LocationSummary* locations = invoke->GetLocations();
2379
2380 Register out = locations->Out().AsRegister<Register>();
2381 Register in = locations->InAt(0).AsRegister<Register>();
2382
2383 __ rev(out, in);
2384}
2385
2386void IntrinsicLocationsBuilderARM::VisitLongReverseBytes(HInvoke* invoke) {
2387 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2388 LocationSummary::kNoCall,
2389 kIntrinsified);
2390 locations->SetInAt(0, Location::RequiresRegister());
2391 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2392}
2393
2394void IntrinsicCodeGeneratorARM::VisitLongReverseBytes(HInvoke* invoke) {
2395 ArmAssembler* assembler = GetAssembler();
2396 LocationSummary* locations = invoke->GetLocations();
2397
2398 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
2399 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
2400 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
2401 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
2402
2403 __ rev(out_reg_lo, in_reg_hi);
2404 __ rev(out_reg_hi, in_reg_lo);
2405}
2406
2407void IntrinsicLocationsBuilderARM::VisitShortReverseBytes(HInvoke* invoke) {
2408 CreateIntToIntLocations(arena_, invoke);
2409}
2410
2411void IntrinsicCodeGeneratorARM::VisitShortReverseBytes(HInvoke* invoke) {
2412 ArmAssembler* assembler = GetAssembler();
2413 LocationSummary* locations = invoke->GetLocations();
2414
2415 Register out = locations->Out().AsRegister<Register>();
2416 Register in = locations->InAt(0).AsRegister<Register>();
2417
2418 __ revsh(out, in);
2419}
2420
xueliang.zhongf1073c82016-07-05 15:28:19 +01002421static void GenBitCount(HInvoke* instr, Primitive::Type type, ArmAssembler* assembler) {
2422 DCHECK(Primitive::IsIntOrLongType(type)) << type;
2423 DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
2424 DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
2425
2426 bool is_long = type == Primitive::kPrimLong;
2427 LocationSummary* locations = instr->GetLocations();
2428 Location in = locations->InAt(0);
2429 Register src_0 = is_long ? in.AsRegisterPairLow<Register>() : in.AsRegister<Register>();
2430 Register src_1 = is_long ? in.AsRegisterPairHigh<Register>() : src_0;
2431 SRegister tmp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
2432 DRegister tmp_d = FromLowSToD(tmp_s);
2433 Register out_r = locations->Out().AsRegister<Register>();
2434
2435 // Move data from core register(s) to temp D-reg for bit count calculation, then move back.
2436 // According to Cortex A57 and A72 optimization guides, compared to transferring to full D-reg,
2437 // transferring data from core reg to upper or lower half of vfp D-reg requires extra latency,
2438 // That's why for integer bit count, we use 'vmov d0, r0, r0' instead of 'vmov d0[0], r0'.
2439 __ vmovdrr(tmp_d, src_1, src_0); // Temp DReg |--src_1|--src_0|
2440 __ vcntd(tmp_d, tmp_d); // Temp DReg |c|c|c|c|c|c|c|c|
2441 __ vpaddld(tmp_d, tmp_d, 8, /* is_unsigned */ true); // Temp DReg |--c|--c|--c|--c|
2442 __ vpaddld(tmp_d, tmp_d, 16, /* is_unsigned */ true); // Temp DReg |------c|------c|
2443 if (is_long) {
2444 __ vpaddld(tmp_d, tmp_d, 32, /* is_unsigned */ true); // Temp DReg |--------------c|
2445 }
2446 __ vmovrs(out_r, tmp_s);
2447}
2448
2449void IntrinsicLocationsBuilderARM::VisitIntegerBitCount(HInvoke* invoke) {
2450 CreateIntToIntLocations(arena_, invoke);
2451 invoke->GetLocations()->AddTemp(Location::RequiresFpuRegister());
2452}
2453
2454void IntrinsicCodeGeneratorARM::VisitIntegerBitCount(HInvoke* invoke) {
2455 GenBitCount(invoke, Primitive::kPrimInt, GetAssembler());
2456}
2457
2458void IntrinsicLocationsBuilderARM::VisitLongBitCount(HInvoke* invoke) {
2459 VisitIntegerBitCount(invoke);
2460}
2461
2462void IntrinsicCodeGeneratorARM::VisitLongBitCount(HInvoke* invoke) {
2463 GenBitCount(invoke, Primitive::kPrimLong, GetAssembler());
2464}
2465
Tim Zhang25abd6c2016-01-19 23:39:24 +08002466void IntrinsicLocationsBuilderARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2467 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2468 LocationSummary::kNoCall,
2469 kIntrinsified);
2470 locations->SetInAt(0, Location::RequiresRegister());
2471 locations->SetInAt(1, Location::RequiresRegister());
2472 locations->SetInAt(2, Location::RequiresRegister());
2473 locations->SetInAt(3, Location::RequiresRegister());
2474 locations->SetInAt(4, Location::RequiresRegister());
2475
Scott Wakeling3fdab772016-04-25 11:32:37 +01002476 // Temporary registers to store lengths of strings and for calculations.
Tim Zhang25abd6c2016-01-19 23:39:24 +08002477 locations->AddTemp(Location::RequiresRegister());
2478 locations->AddTemp(Location::RequiresRegister());
2479 locations->AddTemp(Location::RequiresRegister());
2480}
2481
2482void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2483 ArmAssembler* assembler = GetAssembler();
2484 LocationSummary* locations = invoke->GetLocations();
2485
2486 // Check assumption that sizeof(Char) is 2 (used in scaling below).
2487 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2488 DCHECK_EQ(char_size, 2u);
2489
2490 // Location of data in char array buffer.
2491 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2492
2493 // Location of char array data in string.
2494 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2495
2496 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2497 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2498 Register srcObj = locations->InAt(0).AsRegister<Register>();
2499 Register srcBegin = locations->InAt(1).AsRegister<Register>();
2500 Register srcEnd = locations->InAt(2).AsRegister<Register>();
2501 Register dstObj = locations->InAt(3).AsRegister<Register>();
2502 Register dstBegin = locations->InAt(4).AsRegister<Register>();
2503
Scott Wakeling3fdab772016-04-25 11:32:37 +01002504 Register num_chr = locations->GetTemp(0).AsRegister<Register>();
2505 Register src_ptr = locations->GetTemp(1).AsRegister<Register>();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002506 Register dst_ptr = locations->GetTemp(2).AsRegister<Register>();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002507
jessicahandojo05765752016-09-09 19:01:32 -07002508 Label done, compressed_string_loop;
Anton Kirilov6f644202017-02-27 18:29:45 +00002509 Label* final_label = codegen_->GetFinalLabel(invoke, &done);
Tim Zhang25abd6c2016-01-19 23:39:24 +08002510 // dst to be copied.
2511 __ add(dst_ptr, dstObj, ShifterOperand(data_offset));
2512 __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1));
2513
Scott Wakeling3fdab772016-04-25 11:32:37 +01002514 __ subs(num_chr, srcEnd, ShifterOperand(srcBegin));
Scott Wakeling3fdab772016-04-25 11:32:37 +01002515 // Early out for valid zero-length retrievals.
Anton Kirilov6f644202017-02-27 18:29:45 +00002516 __ b(final_label, EQ);
Scott Wakeling3fdab772016-04-25 11:32:37 +01002517
jessicahandojo05765752016-09-09 19:01:32 -07002518 // src range to copy.
2519 __ add(src_ptr, srcObj, ShifterOperand(value_offset));
2520 Label compressed_string_preloop;
2521 if (mirror::kUseStringCompression) {
2522 // Location of count in string.
2523 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2524 // String's length.
2525 __ ldr(IP, Address(srcObj, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002526 __ tst(IP, ShifterOperand(1));
2527 __ b(&compressed_string_preloop, EQ);
jessicahandojo05765752016-09-09 19:01:32 -07002528 }
2529 __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1));
2530
2531 // Do the copy.
2532 Label loop, remainder;
2533
Scott Wakeling3fdab772016-04-25 11:32:37 +01002534 // Save repairing the value of num_chr on the < 4 character path.
2535 __ subs(IP, num_chr, ShifterOperand(4));
2536 __ b(&remainder, LT);
2537
2538 // Keep the result of the earlier subs, we are going to fetch at least 4 characters.
2539 __ mov(num_chr, ShifterOperand(IP));
2540
2541 // Main loop used for longer fetches loads and stores 4x16-bit characters at a time.
2542 // (LDRD/STRD fault on unaligned addresses and it's not worth inlining extra code
2543 // to rectify these everywhere this intrinsic applies.)
2544 __ Bind(&loop);
2545 __ ldr(IP, Address(src_ptr, char_size * 2));
2546 __ subs(num_chr, num_chr, ShifterOperand(4));
2547 __ str(IP, Address(dst_ptr, char_size * 2));
2548 __ ldr(IP, Address(src_ptr, char_size * 4, Address::PostIndex));
2549 __ str(IP, Address(dst_ptr, char_size * 4, Address::PostIndex));
2550 __ b(&loop, GE);
2551
2552 __ adds(num_chr, num_chr, ShifterOperand(4));
Anton Kirilov6f644202017-02-27 18:29:45 +00002553 __ b(final_label, EQ);
Scott Wakeling3fdab772016-04-25 11:32:37 +01002554
2555 // Main loop for < 4 character case and remainder handling. Loads and stores one
2556 // 16-bit Java character at a time.
2557 __ Bind(&remainder);
2558 __ ldrh(IP, Address(src_ptr, char_size, Address::PostIndex));
2559 __ subs(num_chr, num_chr, ShifterOperand(1));
2560 __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex));
2561 __ b(&remainder, GT);
jessicahandojo05765752016-09-09 19:01:32 -07002562
2563 if (mirror::kUseStringCompression) {
Anton Kirilov6f644202017-02-27 18:29:45 +00002564 __ b(final_label);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002565
jessicahandojo05765752016-09-09 19:01:32 -07002566 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
2567 DCHECK_EQ(c_char_size, 1u);
2568 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2569 __ Bind(&compressed_string_preloop);
2570 __ add(src_ptr, src_ptr, ShifterOperand(srcBegin));
2571 __ Bind(&compressed_string_loop);
2572 __ ldrb(IP, Address(src_ptr, c_char_size, Address::PostIndex));
2573 __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex));
2574 __ subs(num_chr, num_chr, ShifterOperand(1));
2575 __ b(&compressed_string_loop, GT);
2576 }
Scott Wakeling3fdab772016-04-25 11:32:37 +01002577
Anton Kirilov6f644202017-02-27 18:29:45 +00002578 if (done.IsLinked()) {
2579 __ Bind(&done);
2580 }
Tim Zhang25abd6c2016-01-19 23:39:24 +08002581}
2582
Anton Kirilova3ffea22016-04-07 17:02:37 +01002583void IntrinsicLocationsBuilderARM::VisitFloatIsInfinite(HInvoke* invoke) {
2584 CreateFPToIntLocations(arena_, invoke);
2585}
2586
2587void IntrinsicCodeGeneratorARM::VisitFloatIsInfinite(HInvoke* invoke) {
2588 ArmAssembler* const assembler = GetAssembler();
2589 LocationSummary* const locations = invoke->GetLocations();
2590 const Register out = locations->Out().AsRegister<Register>();
2591 // Shifting left by 1 bit makes the value encodable as an immediate operand;
2592 // we don't care about the sign bit anyway.
2593 constexpr uint32_t infinity = kPositiveInfinityFloat << 1U;
2594
2595 __ vmovrs(out, locations->InAt(0).AsFpuRegister<SRegister>());
2596 // We don't care about the sign bit, so shift left.
2597 __ Lsl(out, out, 1);
2598 __ eor(out, out, ShifterOperand(infinity));
2599 // If the result is 0, then it has 32 leading zeros, and less than that otherwise.
2600 __ clz(out, out);
2601 // Any number less than 32 logically shifted right by 5 bits results in 0;
2602 // the same operation on 32 yields 1.
2603 __ Lsr(out, out, 5);
2604}
2605
2606void IntrinsicLocationsBuilderARM::VisitDoubleIsInfinite(HInvoke* invoke) {
2607 CreateFPToIntLocations(arena_, invoke);
2608}
2609
2610void IntrinsicCodeGeneratorARM::VisitDoubleIsInfinite(HInvoke* invoke) {
2611 ArmAssembler* const assembler = GetAssembler();
2612 LocationSummary* const locations = invoke->GetLocations();
2613 const Register out = locations->Out().AsRegister<Register>();
2614 // The highest 32 bits of double precision positive infinity separated into
2615 // two constants encodable as immediate operands.
2616 constexpr uint32_t infinity_high = 0x7f000000U;
2617 constexpr uint32_t infinity_high2 = 0x00f00000U;
2618
2619 static_assert((infinity_high | infinity_high2) == static_cast<uint32_t>(kPositiveInfinityDouble >> 32U),
2620 "The constants do not add up to the high 32 bits of double precision positive infinity.");
2621 __ vmovrrd(IP, out, FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
2622 __ eor(out, out, ShifterOperand(infinity_high));
2623 __ eor(out, out, ShifterOperand(infinity_high2));
2624 // We don't care about the sign bit, so shift left.
2625 __ orr(out, IP, ShifterOperand(out, LSL, 1));
2626 // If the result is 0, then it has 32 leading zeros, and less than that otherwise.
2627 __ clz(out, out);
2628 // Any number less than 32 logically shifted right by 5 bits results in 0;
2629 // the same operation on 32 yields 1.
2630 __ Lsr(out, out, 5);
2631}
2632
TatWai Chongd8c052a2016-11-02 16:12:48 +08002633void IntrinsicLocationsBuilderARM::VisitReferenceGetReferent(HInvoke* invoke) {
2634 if (kEmitCompilerReadBarrier) {
2635 // Do not intrinsify this call with the read barrier configuration.
2636 return;
2637 }
2638 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2639 LocationSummary::kCallOnSlowPath,
2640 kIntrinsified);
2641 locations->SetInAt(0, Location::RequiresRegister());
2642 locations->SetOut(Location::SameAsFirstInput());
2643 locations->AddTemp(Location::RequiresRegister());
2644}
2645
2646void IntrinsicCodeGeneratorARM::VisitReferenceGetReferent(HInvoke* invoke) {
2647 DCHECK(!kEmitCompilerReadBarrier);
2648 ArmAssembler* const assembler = GetAssembler();
2649 LocationSummary* locations = invoke->GetLocations();
2650
2651 Register obj = locations->InAt(0).AsRegister<Register>();
2652 Register out = locations->Out().AsRegister<Register>();
2653
2654 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
2655 codegen_->AddSlowPath(slow_path);
2656
2657 // Load ArtMethod first.
2658 HInvokeStaticOrDirect* invoke_direct = invoke->AsInvokeStaticOrDirect();
2659 DCHECK(invoke_direct != nullptr);
2660 Register temp = codegen_->GenerateCalleeMethodStaticOrDirectCall(
2661 invoke_direct, locations->GetTemp(0)).AsRegister<Register>();
2662
2663 // Now get declaring class.
2664 __ ldr(temp, Address(temp, ArtMethod::DeclaringClassOffset().Int32Value()));
2665
2666 uint32_t slow_path_flag_offset = codegen_->GetReferenceSlowFlagOffset();
2667 uint32_t disable_flag_offset = codegen_->GetReferenceDisableFlagOffset();
2668 DCHECK_NE(slow_path_flag_offset, 0u);
2669 DCHECK_NE(disable_flag_offset, 0u);
2670 DCHECK_NE(slow_path_flag_offset, disable_flag_offset);
2671
2672 // Check static flags that prevent using intrinsic.
2673 __ ldr(IP, Address(temp, disable_flag_offset));
2674 __ ldr(temp, Address(temp, slow_path_flag_offset));
2675 __ orr(IP, IP, ShifterOperand(temp));
2676 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel());
2677
2678 // Fast path.
2679 __ ldr(out, Address(obj, mirror::Reference::ReferentOffset().Int32Value()));
2680 codegen_->MaybeRecordImplicitNullCheck(invoke);
2681 __ MaybeUnpoisonHeapReference(out);
2682 __ Bind(slow_path->GetExitLabel());
2683}
2684
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002685void IntrinsicLocationsBuilderARM::VisitIntegerValueOf(HInvoke* invoke) {
2686 InvokeRuntimeCallingConvention calling_convention;
2687 IntrinsicVisitor::ComputeIntegerValueOfLocations(
2688 invoke,
2689 codegen_,
2690 Location::RegisterLocation(R0),
2691 Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2692}
2693
2694void IntrinsicCodeGeneratorARM::VisitIntegerValueOf(HInvoke* invoke) {
2695 IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo();
2696 LocationSummary* locations = invoke->GetLocations();
2697 ArmAssembler* const assembler = GetAssembler();
2698
2699 Register out = locations->Out().AsRegister<Register>();
2700 InvokeRuntimeCallingConvention calling_convention;
2701 Register argument = calling_convention.GetRegisterAt(0);
2702 if (invoke->InputAt(0)->IsConstant()) {
2703 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
2704 if (value >= info.low && value <= info.high) {
2705 // Just embed the j.l.Integer in the code.
2706 ScopedObjectAccess soa(Thread::Current());
2707 mirror::Object* boxed = info.cache->Get(value + (-info.low));
2708 DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed));
2709 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(boxed));
2710 __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
2711 } else {
2712 // Allocate and initialize a new j.l.Integer.
2713 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
2714 // JIT object table.
2715 uint32_t address =
2716 dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2717 __ LoadLiteral(argument, codegen_->DeduplicateBootImageAddressLiteral(address));
2718 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2719 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2720 __ LoadImmediate(IP, value);
2721 __ StoreToOffset(kStoreWord, IP, out, info.value_offset);
2722 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2723 // one.
2724 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2725 }
2726 } else {
2727 Register in = locations->InAt(0).AsRegister<Register>();
2728 // Check bounds of our cache.
2729 __ AddConstant(out, in, -info.low);
2730 __ CmpConstant(out, info.high - info.low + 1);
2731 Label allocate, done;
2732 __ b(&allocate, HS);
2733 // If the value is within the bounds, load the j.l.Integer directly from the array.
2734 uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
2735 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.cache));
2736 __ LoadLiteral(IP, codegen_->DeduplicateBootImageAddressLiteral(data_offset + address));
2737 codegen_->LoadFromShiftedRegOffset(Primitive::kPrimNot, locations->Out(), IP, out);
2738 __ MaybeUnpoisonHeapReference(out);
2739 __ b(&done);
2740 __ Bind(&allocate);
2741 // Otherwise allocate and initialize a new j.l.Integer.
2742 address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2743 __ LoadLiteral(argument, codegen_->DeduplicateBootImageAddressLiteral(address));
2744 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2745 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2746 __ StoreToOffset(kStoreWord, in, out, info.value_offset);
2747 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2748 // one.
2749 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2750 __ Bind(&done);
2751 }
2752}
2753
Aart Bik2f9fcc92016-03-01 15:16:54 -08002754UNIMPLEMENTED_INTRINSIC(ARM, MathMinDoubleDouble)
2755UNIMPLEMENTED_INTRINSIC(ARM, MathMinFloatFloat)
2756UNIMPLEMENTED_INTRINSIC(ARM, MathMaxDoubleDouble)
2757UNIMPLEMENTED_INTRINSIC(ARM, MathMaxFloatFloat)
2758UNIMPLEMENTED_INTRINSIC(ARM, MathMinLongLong)
2759UNIMPLEMENTED_INTRINSIC(ARM, MathMaxLongLong)
2760UNIMPLEMENTED_INTRINSIC(ARM, MathCeil) // Could be done by changing rounding mode, maybe?
2761UNIMPLEMENTED_INTRINSIC(ARM, MathFloor) // Could be done by changing rounding mode, maybe?
2762UNIMPLEMENTED_INTRINSIC(ARM, MathRint)
2763UNIMPLEMENTED_INTRINSIC(ARM, MathRoundDouble) // Could be done by changing rounding mode, maybe?
2764UNIMPLEMENTED_INTRINSIC(ARM, MathRoundFloat) // Could be done by changing rounding mode, maybe?
2765UNIMPLEMENTED_INTRINSIC(ARM, UnsafeCASLong) // High register pressure.
2766UNIMPLEMENTED_INTRINSIC(ARM, SystemArrayCopyChar)
Aart Bik2f9fcc92016-03-01 15:16:54 -08002767UNIMPLEMENTED_INTRINSIC(ARM, IntegerHighestOneBit)
2768UNIMPLEMENTED_INTRINSIC(ARM, LongHighestOneBit)
2769UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit)
2770UNIMPLEMENTED_INTRINSIC(ARM, LongLowestOneBit)
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08002771
Aart Bikff7d89c2016-11-07 08:49:28 -08002772UNIMPLEMENTED_INTRINSIC(ARM, StringStringIndexOf);
2773UNIMPLEMENTED_INTRINSIC(ARM, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08002774UNIMPLEMENTED_INTRINSIC(ARM, StringBufferAppend);
2775UNIMPLEMENTED_INTRINSIC(ARM, StringBufferLength);
2776UNIMPLEMENTED_INTRINSIC(ARM, StringBufferToString);
2777UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderAppend);
2778UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderLength);
2779UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08002780
Aart Bik0e54c012016-03-04 12:08:31 -08002781// 1.8.
2782UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddInt)
2783UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddLong)
2784UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetInt)
2785UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetLong)
2786UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08002787
Aart Bik2f9fcc92016-03-01 15:16:54 -08002788UNREACHABLE_INTRINSICS(ARM)
Roland Levillain4d027112015-07-01 15:41:14 +01002789
2790#undef __
2791
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08002792} // namespace arm
2793} // namespace art