blob: ae5f8d176095234d3a4fedd61f7f65a6528abe3a [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"
Andreas Gampeb486a982017-06-01 13:45:54 -070031#include "thread-current-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());
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01001648 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen_);
1649 arm_codegen->MaybeAddBakerCcEntrypointTempForFields(locations);
Roland Levillain0b671c02016-08-19 12:02:34 +01001650 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001651}
1652
1653static void CheckPosition(ArmAssembler* assembler,
1654 Location pos,
1655 Register input,
1656 Location length,
1657 SlowPathCode* slow_path,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001658 Register temp,
1659 bool length_is_input_length = false) {
1660 // Where is the length in the Array?
1661 const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
1662
1663 if (pos.IsConstant()) {
1664 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
1665 if (pos_const == 0) {
1666 if (!length_is_input_length) {
1667 // Check that length(input) >= length.
1668 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
1669 if (length.IsConstant()) {
1670 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1671 } else {
1672 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1673 }
1674 __ b(slow_path->GetEntryLabel(), LT);
1675 }
1676 } else {
1677 // Check that length(input) >= pos.
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01001678 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
1679 __ subs(temp, temp, ShifterOperand(pos_const));
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001680 __ b(slow_path->GetEntryLabel(), LT);
1681
1682 // Check that (length(input) - pos) >= length.
1683 if (length.IsConstant()) {
1684 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1685 } else {
1686 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1687 }
1688 __ b(slow_path->GetEntryLabel(), LT);
1689 }
1690 } else if (length_is_input_length) {
1691 // The only way the copy can succeed is if pos is zero.
1692 Register pos_reg = pos.AsRegister<Register>();
1693 __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
1694 } else {
1695 // Check that pos >= 0.
1696 Register pos_reg = pos.AsRegister<Register>();
1697 __ cmp(pos_reg, ShifterOperand(0));
1698 __ b(slow_path->GetEntryLabel(), LT);
1699
1700 // Check that pos <= length(input).
1701 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
1702 __ subs(temp, temp, ShifterOperand(pos_reg));
1703 __ b(slow_path->GetEntryLabel(), LT);
1704
1705 // Check that (length(input) - pos) >= length.
1706 if (length.IsConstant()) {
1707 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1708 } else {
1709 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1710 }
1711 __ b(slow_path->GetEntryLabel(), LT);
1712 }
1713}
1714
1715void IntrinsicCodeGeneratorARM::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01001716 // The only read barrier implementation supporting the
1717 // SystemArrayCopy intrinsic is the Baker-style read barriers.
1718 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01001719
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001720 ArmAssembler* assembler = GetAssembler();
1721 LocationSummary* locations = invoke->GetLocations();
1722
1723 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1724 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
1725 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
1726 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Roland Levillain0b671c02016-08-19 12:02:34 +01001727 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001728
1729 Register src = locations->InAt(0).AsRegister<Register>();
1730 Location src_pos = locations->InAt(1);
1731 Register dest = locations->InAt(2).AsRegister<Register>();
1732 Location dest_pos = locations->InAt(3);
1733 Location length = locations->InAt(4);
Roland Levillain0b671c02016-08-19 12:02:34 +01001734 Location temp1_loc = locations->GetTemp(0);
1735 Register temp1 = temp1_loc.AsRegister<Register>();
1736 Location temp2_loc = locations->GetTemp(1);
1737 Register temp2 = temp2_loc.AsRegister<Register>();
1738 Location temp3_loc = locations->GetTemp(2);
1739 Register temp3 = temp3_loc.AsRegister<Register>();
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001740
Roland Levillain0b671c02016-08-19 12:02:34 +01001741 SlowPathCode* intrinsic_slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
1742 codegen_->AddSlowPath(intrinsic_slow_path);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001743
Roland Levillainebea3d22016-04-12 15:42:57 +01001744 Label conditions_on_positions_validated;
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001745 SystemArrayCopyOptimizations optimizations(invoke);
1746
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001747 // If source and destination are the same, we go to slow path if we need to do
1748 // forward copying.
1749 if (src_pos.IsConstant()) {
1750 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
1751 if (dest_pos.IsConstant()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001752 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
1753 if (optimizations.GetDestinationIsSource()) {
1754 // Checked when building locations.
1755 DCHECK_GE(src_pos_constant, dest_pos_constant);
1756 } else if (src_pos_constant < dest_pos_constant) {
1757 __ cmp(src, ShifterOperand(dest));
Roland Levillain0b671c02016-08-19 12:02:34 +01001758 __ b(intrinsic_slow_path->GetEntryLabel(), EQ);
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001759 }
1760
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001761 // Checked when building locations.
1762 DCHECK(!optimizations.GetDestinationIsSource()
1763 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
1764 } else {
1765 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001766 __ cmp(src, ShifterOperand(dest));
Roland Levillainebea3d22016-04-12 15:42:57 +01001767 __ b(&conditions_on_positions_validated, NE);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001768 }
1769 __ cmp(dest_pos.AsRegister<Register>(), ShifterOperand(src_pos_constant));
Roland Levillain0b671c02016-08-19 12:02:34 +01001770 __ b(intrinsic_slow_path->GetEntryLabel(), GT);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001771 }
1772 } else {
1773 if (!optimizations.GetDestinationIsSource()) {
Nicolas Geoffray9f65db82016-07-07 12:07:42 +01001774 __ cmp(src, ShifterOperand(dest));
Roland Levillainebea3d22016-04-12 15:42:57 +01001775 __ b(&conditions_on_positions_validated, NE);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001776 }
1777 if (dest_pos.IsConstant()) {
1778 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
1779 __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos_constant));
1780 } else {
1781 __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos.AsRegister<Register>()));
1782 }
Roland Levillain0b671c02016-08-19 12:02:34 +01001783 __ b(intrinsic_slow_path->GetEntryLabel(), LT);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001784 }
1785
Roland Levillainebea3d22016-04-12 15:42:57 +01001786 __ Bind(&conditions_on_positions_validated);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001787
1788 if (!optimizations.GetSourceIsNotNull()) {
1789 // Bail out if the source is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01001790 __ CompareAndBranchIfZero(src, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001791 }
1792
1793 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
1794 // Bail out if the destination is null.
Roland Levillain0b671c02016-08-19 12:02:34 +01001795 __ CompareAndBranchIfZero(dest, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001796 }
1797
1798 // If the length is negative, bail out.
1799 // We have already checked in the LocationsBuilder for the constant case.
1800 if (!length.IsConstant() &&
1801 !optimizations.GetCountIsSourceLength() &&
1802 !optimizations.GetCountIsDestinationLength()) {
1803 __ cmp(length.AsRegister<Register>(), ShifterOperand(0));
Roland Levillain0b671c02016-08-19 12:02:34 +01001804 __ b(intrinsic_slow_path->GetEntryLabel(), LT);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001805 }
1806
1807 // Validity checks: source.
1808 CheckPosition(assembler,
1809 src_pos,
1810 src,
1811 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01001812 intrinsic_slow_path,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001813 temp1,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001814 optimizations.GetCountIsSourceLength());
1815
1816 // Validity checks: dest.
1817 CheckPosition(assembler,
1818 dest_pos,
1819 dest,
1820 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01001821 intrinsic_slow_path,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001822 temp1,
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001823 optimizations.GetCountIsDestinationLength());
1824
1825 if (!optimizations.GetDoesNotNeedTypeCheck()) {
1826 // Check whether all elements of the source array are assignable to the component
1827 // type of the destination array. We do two checks: the classes are the same,
1828 // or the destination is Object[]. If none of these checks succeed, we go to the
1829 // slow path.
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001830
Roland Levillain0b671c02016-08-19 12:02:34 +01001831 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1832 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1833 // /* HeapReference<Class> */ temp1 = src->klass_
1834 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1835 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
1836 // Bail out if the source is not a non primitive array.
1837 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1838 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1839 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
1840 __ CompareAndBranchIfZero(temp1, intrinsic_slow_path->GetEntryLabel());
1841 // If heap poisoning is enabled, `temp1` has been unpoisoned
1842 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1843 // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
1844 __ LoadFromOffset(kLoadUnsignedHalfword, temp1, temp1, primitive_offset);
1845 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1846 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001847 }
Roland Levillain0b671c02016-08-19 12:02:34 +01001848
1849 // /* HeapReference<Class> */ temp1 = dest->klass_
1850 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1851 invoke, temp1_loc, dest, class_offset, temp2_loc, /* needs_null_check */ false);
1852
1853 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
1854 // Bail out if the destination is not a non primitive array.
1855 //
1856 // Register `temp1` is not trashed by the read barrier emitted
1857 // by GenerateFieldLoadWithBakerReadBarrier below, as that
1858 // method produces a call to a ReadBarrierMarkRegX entry point,
1859 // which saves all potentially live registers, including
1860 // temporaries such a `temp1`.
1861 // /* HeapReference<Class> */ temp2 = temp1->component_type_
1862 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1863 invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
1864 __ CompareAndBranchIfZero(temp2, intrinsic_slow_path->GetEntryLabel());
1865 // If heap poisoning is enabled, `temp2` has been unpoisoned
1866 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1867 // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
1868 __ LoadFromOffset(kLoadUnsignedHalfword, temp2, temp2, primitive_offset);
1869 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1870 __ CompareAndBranchIfNonZero(temp2, intrinsic_slow_path->GetEntryLabel());
1871 }
1872
1873 // For the same reason given earlier, `temp1` is not trashed by the
1874 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
1875 // /* HeapReference<Class> */ temp2 = src->klass_
1876 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1877 invoke, temp2_loc, src, class_offset, temp3_loc, /* needs_null_check */ false);
1878 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
1879 __ cmp(temp1, ShifterOperand(temp2));
1880
1881 if (optimizations.GetDestinationIsTypedObjectArray()) {
1882 Label do_copy;
1883 __ b(&do_copy, EQ);
1884 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1885 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1886 invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
1887 // /* HeapReference<Class> */ temp1 = temp1->super_class_
1888 // We do not need to emit a read barrier for the following
1889 // heap reference load, as `temp1` is only used in a
1890 // comparison with null below, and this reference is not
1891 // kept afterwards.
1892 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
1893 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
1894 __ Bind(&do_copy);
1895 } else {
1896 __ b(intrinsic_slow_path->GetEntryLabel(), NE);
1897 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001898 } else {
Roland Levillain0b671c02016-08-19 12:02:34 +01001899 // Non read barrier code.
1900
1901 // /* HeapReference<Class> */ temp1 = dest->klass_
1902 __ LoadFromOffset(kLoadWord, temp1, dest, class_offset);
1903 // /* HeapReference<Class> */ temp2 = src->klass_
1904 __ LoadFromOffset(kLoadWord, temp2, src, class_offset);
1905 bool did_unpoison = false;
1906 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
1907 !optimizations.GetSourceIsNonPrimitiveArray()) {
1908 // One or two of the references need to be unpoisoned. Unpoison them
1909 // both to make the identity check valid.
1910 __ MaybeUnpoisonHeapReference(temp1);
1911 __ MaybeUnpoisonHeapReference(temp2);
1912 did_unpoison = true;
1913 }
1914
1915 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
1916 // Bail out if the destination is not a non primitive array.
1917 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1918 __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
1919 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1920 __ MaybeUnpoisonHeapReference(temp3);
1921 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
1922 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1923 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1924 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
1925 }
1926
1927 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1928 // Bail out if the source is not a non primitive array.
1929 // /* HeapReference<Class> */ temp3 = temp2->component_type_
1930 __ LoadFromOffset(kLoadWord, temp3, temp2, component_offset);
1931 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1932 __ MaybeUnpoisonHeapReference(temp3);
1933 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
1934 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1935 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1936 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
1937 }
1938
1939 __ cmp(temp1, ShifterOperand(temp2));
1940
1941 if (optimizations.GetDestinationIsTypedObjectArray()) {
1942 Label do_copy;
1943 __ b(&do_copy, EQ);
1944 if (!did_unpoison) {
1945 __ MaybeUnpoisonHeapReference(temp1);
1946 }
1947 // /* HeapReference<Class> */ temp1 = temp1->component_type_
1948 __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
1949 __ MaybeUnpoisonHeapReference(temp1);
1950 // /* HeapReference<Class> */ temp1 = temp1->super_class_
1951 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
1952 // No need to unpoison the result, we're comparing against null.
1953 __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
1954 __ Bind(&do_copy);
1955 } else {
1956 __ b(intrinsic_slow_path->GetEntryLabel(), NE);
1957 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001958 }
1959 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1960 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
1961 // Bail out if the source is not a non primitive array.
Roland Levillain0b671c02016-08-19 12:02:34 +01001962 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1963 // /* HeapReference<Class> */ temp1 = src->klass_
1964 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1965 invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
1966 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1967 codegen_->GenerateFieldLoadWithBakerReadBarrier(
1968 invoke, temp3_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
1969 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1970 // If heap poisoning is enabled, `temp3` has been unpoisoned
1971 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
1972 } else {
1973 // /* HeapReference<Class> */ temp1 = src->klass_
1974 __ LoadFromOffset(kLoadWord, temp1, src, class_offset);
1975 __ MaybeUnpoisonHeapReference(temp1);
1976 // /* HeapReference<Class> */ temp3 = temp1->component_type_
1977 __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
1978 __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
1979 __ MaybeUnpoisonHeapReference(temp3);
1980 }
1981 // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001982 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1983 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Roland Levillain0b671c02016-08-19 12:02:34 +01001984 __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001985 }
1986
Roland Levillain1663d162017-03-17 15:15:21 +00001987 if (length.IsConstant() && length.GetConstant()->AsIntConstant()->GetValue() == 0) {
1988 // Null constant length: not need to emit the loop code at all.
Roland Levillain0b671c02016-08-19 12:02:34 +01001989 } else {
Roland Levillain1663d162017-03-17 15:15:21 +00001990 Label done;
1991 const Primitive::Type type = Primitive::kPrimNot;
1992 const int32_t element_size = Primitive::ComponentSize(type);
1993
1994 if (length.IsRegister()) {
1995 // Don't enter the copy loop if the length is null.
1996 __ CompareAndBranchIfZero(length.AsRegister<Register>(), &done);
1997 }
1998
1999 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2000 // TODO: Also convert this intrinsic to the IsGcMarking strategy?
2001
2002 // SystemArrayCopy implementation for Baker read barriers (see
2003 // also CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier):
2004 //
2005 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
2006 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
2007 // bool is_gray = (rb_state == ReadBarrier::GrayState());
2008 // if (is_gray) {
2009 // // Slow-path copy.
2010 // do {
2011 // *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
2012 // } while (src_ptr != end_ptr)
2013 // } else {
2014 // // Fast-path copy.
2015 // do {
2016 // *dest_ptr++ = *src_ptr++;
2017 // } while (src_ptr != end_ptr)
2018 // }
2019
2020 // /* int32_t */ monitor = src->monitor_
2021 __ LoadFromOffset(kLoadWord, temp2, src, monitor_offset);
2022 // /* LockWord */ lock_word = LockWord(monitor)
2023 static_assert(sizeof(LockWord) == sizeof(int32_t),
2024 "art::LockWord and int32_t have different sizes.");
2025
2026 // Introduce a dependency on the lock_word including the rb_state,
2027 // which shall prevent load-load reordering without using
2028 // a memory barrier (which would be more expensive).
2029 // `src` is unchanged by this operation, but its value now depends
2030 // on `temp2`.
2031 __ add(src, src, ShifterOperand(temp2, LSR, 32));
2032
2033 // Compute the base source address in `temp1`.
2034 // Note that `temp1` (the base source address) is computed from
2035 // `src` (and `src_pos`) here, and thus honors the artificial
2036 // dependency of `src` on `temp2`.
2037 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2038 // Compute the end source address in `temp3`.
2039 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2040 // The base destination address is computed later, as `temp2` is
2041 // used for intermediate computations.
2042
2043 // Slow path used to copy array when `src` is gray.
2044 // Note that the base destination address is computed in `temp2`
2045 // by the slow path code.
2046 SlowPathCode* read_barrier_slow_path =
2047 new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathARM(invoke);
2048 codegen_->AddSlowPath(read_barrier_slow_path);
2049
2050 // Given the numeric representation, it's enough to check the low bit of the
2051 // rb_state. We do that by shifting the bit out of the lock word with LSRS
2052 // which can be a 16-bit instruction unlike the TST immediate.
2053 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
2054 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
2055 __ Lsrs(temp2, temp2, LockWord::kReadBarrierStateShift + 1);
2056 // Carry flag is the last bit shifted out by LSRS.
2057 __ b(read_barrier_slow_path->GetEntryLabel(), CS);
2058
2059 // Fast-path copy.
2060 // Compute the base destination address in `temp2`.
2061 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2062 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2063 // poison/unpoison.
2064 Label loop;
2065 __ Bind(&loop);
2066 __ ldr(IP, Address(temp1, element_size, Address::PostIndex));
2067 __ str(IP, Address(temp2, element_size, Address::PostIndex));
2068 __ cmp(temp1, ShifterOperand(temp3));
2069 __ b(&loop, NE);
2070
2071 __ Bind(read_barrier_slow_path->GetExitLabel());
2072 } else {
2073 // Non read barrier code.
2074 // Compute the base source address in `temp1`.
2075 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
2076 // Compute the base destination address in `temp2`.
2077 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
2078 // Compute the end source address in `temp3`.
2079 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
2080 // Iterate over the arrays and do a raw copy of the objects. We don't need to
2081 // poison/unpoison.
2082 Label loop;
2083 __ Bind(&loop);
2084 __ ldr(IP, Address(temp1, element_size, Address::PostIndex));
2085 __ str(IP, Address(temp2, element_size, Address::PostIndex));
2086 __ cmp(temp1, ShifterOperand(temp3));
2087 __ b(&loop, NE);
2088 }
Roland Levillain0b671c02016-08-19 12:02:34 +01002089 __ Bind(&done);
2090 }
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01002091
2092 // We only need one card marking on the destination array.
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002093 codegen_->MarkGCCard(temp1, temp2, dest, Register(kNoRegister), /* value_can_be_null */ false);
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01002094
Roland Levillain0b671c02016-08-19 12:02:34 +01002095 __ Bind(intrinsic_slow_path->GetExitLabel());
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01002096}
2097
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002098static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
2099 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2100 // the code generator. Furthermore, the register allocator creates fixed live intervals
2101 // for all caller-saved registers because we are doing a function call. As a result, if
2102 // the input and output locations are unallocated, the register allocator runs out of
2103 // registers and fails; however, a debuggable graph is not the common case.
2104 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2105 return;
2106 }
2107
2108 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2109 DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
2110 DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
2111
2112 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01002113 LocationSummary::kCallOnMainOnly,
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002114 kIntrinsified);
2115 const InvokeRuntimeCallingConvention calling_convention;
2116
2117 locations->SetInAt(0, Location::RequiresFpuRegister());
2118 locations->SetOut(Location::RequiresFpuRegister());
2119 // Native code uses the soft float ABI.
2120 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2121 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2122}
2123
2124static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
2125 // If the graph is debuggable, all callee-saved floating-point registers are blocked by
2126 // the code generator. Furthermore, the register allocator creates fixed live intervals
2127 // for all caller-saved registers because we are doing a function call. As a result, if
2128 // the input and output locations are unallocated, the register allocator runs out of
2129 // registers and fails; however, a debuggable graph is not the common case.
2130 if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
2131 return;
2132 }
2133
2134 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2135 DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
2136 DCHECK_EQ(invoke->InputAt(1)->GetType(), Primitive::kPrimDouble);
2137 DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
2138
2139 LocationSummary* const locations = new (arena) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01002140 LocationSummary::kCallOnMainOnly,
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002141 kIntrinsified);
2142 const InvokeRuntimeCallingConvention calling_convention;
2143
2144 locations->SetInAt(0, Location::RequiresFpuRegister());
2145 locations->SetInAt(1, Location::RequiresFpuRegister());
2146 locations->SetOut(Location::RequiresFpuRegister());
2147 // Native code uses the soft float ABI.
2148 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2149 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2150 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
2151 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
2152}
2153
2154static void GenFPToFPCall(HInvoke* invoke,
2155 ArmAssembler* assembler,
2156 CodeGeneratorARM* codegen,
2157 QuickEntrypointEnum entry) {
2158 LocationSummary* const locations = invoke->GetLocations();
2159 const InvokeRuntimeCallingConvention calling_convention;
2160
2161 DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
2162 DCHECK(locations->WillCall() && locations->Intrinsified());
2163 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
2164 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
2165
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002166 // Native code uses the soft float ABI.
2167 __ vmovrrd(calling_convention.GetRegisterAt(0),
2168 calling_convention.GetRegisterAt(1),
2169 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01002170 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002171 __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
2172 calling_convention.GetRegisterAt(0),
2173 calling_convention.GetRegisterAt(1));
2174}
2175
2176static void GenFPFPToFPCall(HInvoke* invoke,
2177 ArmAssembler* assembler,
2178 CodeGeneratorARM* codegen,
2179 QuickEntrypointEnum entry) {
2180 LocationSummary* const locations = invoke->GetLocations();
2181 const InvokeRuntimeCallingConvention calling_convention;
2182
2183 DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
2184 DCHECK(locations->WillCall() && locations->Intrinsified());
2185 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
2186 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
2187 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(2)));
2188 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(3)));
2189
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002190 // Native code uses the soft float ABI.
2191 __ vmovrrd(calling_convention.GetRegisterAt(0),
2192 calling_convention.GetRegisterAt(1),
2193 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
2194 __ vmovrrd(calling_convention.GetRegisterAt(2),
2195 calling_convention.GetRegisterAt(3),
2196 FromLowSToD(locations->InAt(1).AsFpuRegisterPairLow<SRegister>()));
Serban Constantinescu4bb30ac2016-06-22 17:04:45 +01002197 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Anton Kirilovd70dc9d2016-02-04 14:59:04 +00002198 __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
2199 calling_convention.GetRegisterAt(0),
2200 calling_convention.GetRegisterAt(1));
2201}
2202
2203void IntrinsicLocationsBuilderARM::VisitMathCos(HInvoke* invoke) {
2204 CreateFPToFPCallLocations(arena_, invoke);
2205}
2206
2207void IntrinsicCodeGeneratorARM::VisitMathCos(HInvoke* invoke) {
2208 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCos);
2209}
2210
2211void IntrinsicLocationsBuilderARM::VisitMathSin(HInvoke* invoke) {
2212 CreateFPToFPCallLocations(arena_, invoke);
2213}
2214
2215void IntrinsicCodeGeneratorARM::VisitMathSin(HInvoke* invoke) {
2216 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSin);
2217}
2218
2219void IntrinsicLocationsBuilderARM::VisitMathAcos(HInvoke* invoke) {
2220 CreateFPToFPCallLocations(arena_, invoke);
2221}
2222
2223void IntrinsicCodeGeneratorARM::VisitMathAcos(HInvoke* invoke) {
2224 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAcos);
2225}
2226
2227void IntrinsicLocationsBuilderARM::VisitMathAsin(HInvoke* invoke) {
2228 CreateFPToFPCallLocations(arena_, invoke);
2229}
2230
2231void IntrinsicCodeGeneratorARM::VisitMathAsin(HInvoke* invoke) {
2232 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAsin);
2233}
2234
2235void IntrinsicLocationsBuilderARM::VisitMathAtan(HInvoke* invoke) {
2236 CreateFPToFPCallLocations(arena_, invoke);
2237}
2238
2239void IntrinsicCodeGeneratorARM::VisitMathAtan(HInvoke* invoke) {
2240 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan);
2241}
2242
2243void IntrinsicLocationsBuilderARM::VisitMathCbrt(HInvoke* invoke) {
2244 CreateFPToFPCallLocations(arena_, invoke);
2245}
2246
2247void IntrinsicCodeGeneratorARM::VisitMathCbrt(HInvoke* invoke) {
2248 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCbrt);
2249}
2250
2251void IntrinsicLocationsBuilderARM::VisitMathCosh(HInvoke* invoke) {
2252 CreateFPToFPCallLocations(arena_, invoke);
2253}
2254
2255void IntrinsicCodeGeneratorARM::VisitMathCosh(HInvoke* invoke) {
2256 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCosh);
2257}
2258
2259void IntrinsicLocationsBuilderARM::VisitMathExp(HInvoke* invoke) {
2260 CreateFPToFPCallLocations(arena_, invoke);
2261}
2262
2263void IntrinsicCodeGeneratorARM::VisitMathExp(HInvoke* invoke) {
2264 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExp);
2265}
2266
2267void IntrinsicLocationsBuilderARM::VisitMathExpm1(HInvoke* invoke) {
2268 CreateFPToFPCallLocations(arena_, invoke);
2269}
2270
2271void IntrinsicCodeGeneratorARM::VisitMathExpm1(HInvoke* invoke) {
2272 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExpm1);
2273}
2274
2275void IntrinsicLocationsBuilderARM::VisitMathLog(HInvoke* invoke) {
2276 CreateFPToFPCallLocations(arena_, invoke);
2277}
2278
2279void IntrinsicCodeGeneratorARM::VisitMathLog(HInvoke* invoke) {
2280 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog);
2281}
2282
2283void IntrinsicLocationsBuilderARM::VisitMathLog10(HInvoke* invoke) {
2284 CreateFPToFPCallLocations(arena_, invoke);
2285}
2286
2287void IntrinsicCodeGeneratorARM::VisitMathLog10(HInvoke* invoke) {
2288 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog10);
2289}
2290
2291void IntrinsicLocationsBuilderARM::VisitMathSinh(HInvoke* invoke) {
2292 CreateFPToFPCallLocations(arena_, invoke);
2293}
2294
2295void IntrinsicCodeGeneratorARM::VisitMathSinh(HInvoke* invoke) {
2296 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSinh);
2297}
2298
2299void IntrinsicLocationsBuilderARM::VisitMathTan(HInvoke* invoke) {
2300 CreateFPToFPCallLocations(arena_, invoke);
2301}
2302
2303void IntrinsicCodeGeneratorARM::VisitMathTan(HInvoke* invoke) {
2304 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTan);
2305}
2306
2307void IntrinsicLocationsBuilderARM::VisitMathTanh(HInvoke* invoke) {
2308 CreateFPToFPCallLocations(arena_, invoke);
2309}
2310
2311void IntrinsicCodeGeneratorARM::VisitMathTanh(HInvoke* invoke) {
2312 GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTanh);
2313}
2314
2315void IntrinsicLocationsBuilderARM::VisitMathAtan2(HInvoke* invoke) {
2316 CreateFPFPToFPCallLocations(arena_, invoke);
2317}
2318
2319void IntrinsicCodeGeneratorARM::VisitMathAtan2(HInvoke* invoke) {
2320 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan2);
2321}
2322
2323void IntrinsicLocationsBuilderARM::VisitMathHypot(HInvoke* invoke) {
2324 CreateFPFPToFPCallLocations(arena_, invoke);
2325}
2326
2327void IntrinsicCodeGeneratorARM::VisitMathHypot(HInvoke* invoke) {
2328 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickHypot);
2329}
2330
2331void IntrinsicLocationsBuilderARM::VisitMathNextAfter(HInvoke* invoke) {
2332 CreateFPFPToFPCallLocations(arena_, invoke);
2333}
2334
2335void IntrinsicCodeGeneratorARM::VisitMathNextAfter(HInvoke* invoke) {
2336 GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter);
2337}
2338
Artem Serovc257da72016-02-02 13:49:43 +00002339void IntrinsicLocationsBuilderARM::VisitIntegerReverse(HInvoke* invoke) {
2340 CreateIntToIntLocations(arena_, invoke);
2341}
2342
2343void IntrinsicCodeGeneratorARM::VisitIntegerReverse(HInvoke* invoke) {
2344 ArmAssembler* assembler = GetAssembler();
2345 LocationSummary* locations = invoke->GetLocations();
2346
2347 Register out = locations->Out().AsRegister<Register>();
2348 Register in = locations->InAt(0).AsRegister<Register>();
2349
2350 __ rbit(out, in);
2351}
2352
2353void IntrinsicLocationsBuilderARM::VisitLongReverse(HInvoke* invoke) {
2354 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2355 LocationSummary::kNoCall,
2356 kIntrinsified);
2357 locations->SetInAt(0, Location::RequiresRegister());
2358 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2359}
2360
2361void IntrinsicCodeGeneratorARM::VisitLongReverse(HInvoke* invoke) {
2362 ArmAssembler* assembler = GetAssembler();
2363 LocationSummary* locations = invoke->GetLocations();
2364
2365 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
2366 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
2367 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
2368 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
2369
2370 __ rbit(out_reg_lo, in_reg_hi);
2371 __ rbit(out_reg_hi, in_reg_lo);
2372}
2373
2374void IntrinsicLocationsBuilderARM::VisitIntegerReverseBytes(HInvoke* invoke) {
2375 CreateIntToIntLocations(arena_, invoke);
2376}
2377
2378void IntrinsicCodeGeneratorARM::VisitIntegerReverseBytes(HInvoke* invoke) {
2379 ArmAssembler* assembler = GetAssembler();
2380 LocationSummary* locations = invoke->GetLocations();
2381
2382 Register out = locations->Out().AsRegister<Register>();
2383 Register in = locations->InAt(0).AsRegister<Register>();
2384
2385 __ rev(out, in);
2386}
2387
2388void IntrinsicLocationsBuilderARM::VisitLongReverseBytes(HInvoke* invoke) {
2389 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2390 LocationSummary::kNoCall,
2391 kIntrinsified);
2392 locations->SetInAt(0, Location::RequiresRegister());
2393 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2394}
2395
2396void IntrinsicCodeGeneratorARM::VisitLongReverseBytes(HInvoke* invoke) {
2397 ArmAssembler* assembler = GetAssembler();
2398 LocationSummary* locations = invoke->GetLocations();
2399
2400 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
2401 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
2402 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
2403 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
2404
2405 __ rev(out_reg_lo, in_reg_hi);
2406 __ rev(out_reg_hi, in_reg_lo);
2407}
2408
2409void IntrinsicLocationsBuilderARM::VisitShortReverseBytes(HInvoke* invoke) {
2410 CreateIntToIntLocations(arena_, invoke);
2411}
2412
2413void IntrinsicCodeGeneratorARM::VisitShortReverseBytes(HInvoke* invoke) {
2414 ArmAssembler* assembler = GetAssembler();
2415 LocationSummary* locations = invoke->GetLocations();
2416
2417 Register out = locations->Out().AsRegister<Register>();
2418 Register in = locations->InAt(0).AsRegister<Register>();
2419
2420 __ revsh(out, in);
2421}
2422
xueliang.zhongf1073c82016-07-05 15:28:19 +01002423static void GenBitCount(HInvoke* instr, Primitive::Type type, ArmAssembler* assembler) {
2424 DCHECK(Primitive::IsIntOrLongType(type)) << type;
2425 DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
2426 DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
2427
2428 bool is_long = type == Primitive::kPrimLong;
2429 LocationSummary* locations = instr->GetLocations();
2430 Location in = locations->InAt(0);
2431 Register src_0 = is_long ? in.AsRegisterPairLow<Register>() : in.AsRegister<Register>();
2432 Register src_1 = is_long ? in.AsRegisterPairHigh<Register>() : src_0;
2433 SRegister tmp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
2434 DRegister tmp_d = FromLowSToD(tmp_s);
2435 Register out_r = locations->Out().AsRegister<Register>();
2436
2437 // Move data from core register(s) to temp D-reg for bit count calculation, then move back.
2438 // According to Cortex A57 and A72 optimization guides, compared to transferring to full D-reg,
2439 // transferring data from core reg to upper or lower half of vfp D-reg requires extra latency,
2440 // That's why for integer bit count, we use 'vmov d0, r0, r0' instead of 'vmov d0[0], r0'.
2441 __ vmovdrr(tmp_d, src_1, src_0); // Temp DReg |--src_1|--src_0|
2442 __ vcntd(tmp_d, tmp_d); // Temp DReg |c|c|c|c|c|c|c|c|
2443 __ vpaddld(tmp_d, tmp_d, 8, /* is_unsigned */ true); // Temp DReg |--c|--c|--c|--c|
2444 __ vpaddld(tmp_d, tmp_d, 16, /* is_unsigned */ true); // Temp DReg |------c|------c|
2445 if (is_long) {
2446 __ vpaddld(tmp_d, tmp_d, 32, /* is_unsigned */ true); // Temp DReg |--------------c|
2447 }
2448 __ vmovrs(out_r, tmp_s);
2449}
2450
2451void IntrinsicLocationsBuilderARM::VisitIntegerBitCount(HInvoke* invoke) {
2452 CreateIntToIntLocations(arena_, invoke);
2453 invoke->GetLocations()->AddTemp(Location::RequiresFpuRegister());
2454}
2455
2456void IntrinsicCodeGeneratorARM::VisitIntegerBitCount(HInvoke* invoke) {
2457 GenBitCount(invoke, Primitive::kPrimInt, GetAssembler());
2458}
2459
2460void IntrinsicLocationsBuilderARM::VisitLongBitCount(HInvoke* invoke) {
2461 VisitIntegerBitCount(invoke);
2462}
2463
2464void IntrinsicCodeGeneratorARM::VisitLongBitCount(HInvoke* invoke) {
2465 GenBitCount(invoke, Primitive::kPrimLong, GetAssembler());
2466}
2467
Tim Zhang25abd6c2016-01-19 23:39:24 +08002468void IntrinsicLocationsBuilderARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2469 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2470 LocationSummary::kNoCall,
2471 kIntrinsified);
2472 locations->SetInAt(0, Location::RequiresRegister());
2473 locations->SetInAt(1, Location::RequiresRegister());
2474 locations->SetInAt(2, Location::RequiresRegister());
2475 locations->SetInAt(3, Location::RequiresRegister());
2476 locations->SetInAt(4, Location::RequiresRegister());
2477
Scott Wakeling3fdab772016-04-25 11:32:37 +01002478 // Temporary registers to store lengths of strings and for calculations.
Tim Zhang25abd6c2016-01-19 23:39:24 +08002479 locations->AddTemp(Location::RequiresRegister());
2480 locations->AddTemp(Location::RequiresRegister());
2481 locations->AddTemp(Location::RequiresRegister());
2482}
2483
2484void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2485 ArmAssembler* assembler = GetAssembler();
2486 LocationSummary* locations = invoke->GetLocations();
2487
2488 // Check assumption that sizeof(Char) is 2 (used in scaling below).
2489 const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
2490 DCHECK_EQ(char_size, 2u);
2491
2492 // Location of data in char array buffer.
2493 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2494
2495 // Location of char array data in string.
2496 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2497
2498 // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
2499 // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
2500 Register srcObj = locations->InAt(0).AsRegister<Register>();
2501 Register srcBegin = locations->InAt(1).AsRegister<Register>();
2502 Register srcEnd = locations->InAt(2).AsRegister<Register>();
2503 Register dstObj = locations->InAt(3).AsRegister<Register>();
2504 Register dstBegin = locations->InAt(4).AsRegister<Register>();
2505
Scott Wakeling3fdab772016-04-25 11:32:37 +01002506 Register num_chr = locations->GetTemp(0).AsRegister<Register>();
2507 Register src_ptr = locations->GetTemp(1).AsRegister<Register>();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002508 Register dst_ptr = locations->GetTemp(2).AsRegister<Register>();
Tim Zhang25abd6c2016-01-19 23:39:24 +08002509
jessicahandojo05765752016-09-09 19:01:32 -07002510 Label done, compressed_string_loop;
Anton Kirilov6f644202017-02-27 18:29:45 +00002511 Label* final_label = codegen_->GetFinalLabel(invoke, &done);
Tim Zhang25abd6c2016-01-19 23:39:24 +08002512 // dst to be copied.
2513 __ add(dst_ptr, dstObj, ShifterOperand(data_offset));
2514 __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1));
2515
Scott Wakeling3fdab772016-04-25 11:32:37 +01002516 __ subs(num_chr, srcEnd, ShifterOperand(srcBegin));
Scott Wakeling3fdab772016-04-25 11:32:37 +01002517 // Early out for valid zero-length retrievals.
Anton Kirilov6f644202017-02-27 18:29:45 +00002518 __ b(final_label, EQ);
Scott Wakeling3fdab772016-04-25 11:32:37 +01002519
jessicahandojo05765752016-09-09 19:01:32 -07002520 // src range to copy.
2521 __ add(src_ptr, srcObj, ShifterOperand(value_offset));
2522 Label compressed_string_preloop;
2523 if (mirror::kUseStringCompression) {
2524 // Location of count in string.
2525 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2526 // String's length.
2527 __ ldr(IP, Address(srcObj, count_offset));
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002528 __ tst(IP, ShifterOperand(1));
2529 __ b(&compressed_string_preloop, EQ);
jessicahandojo05765752016-09-09 19:01:32 -07002530 }
2531 __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1));
2532
2533 // Do the copy.
2534 Label loop, remainder;
2535
Scott Wakeling3fdab772016-04-25 11:32:37 +01002536 // Save repairing the value of num_chr on the < 4 character path.
2537 __ subs(IP, num_chr, ShifterOperand(4));
2538 __ b(&remainder, LT);
2539
2540 // Keep the result of the earlier subs, we are going to fetch at least 4 characters.
2541 __ mov(num_chr, ShifterOperand(IP));
2542
2543 // Main loop used for longer fetches loads and stores 4x16-bit characters at a time.
2544 // (LDRD/STRD fault on unaligned addresses and it's not worth inlining extra code
2545 // to rectify these everywhere this intrinsic applies.)
2546 __ Bind(&loop);
2547 __ ldr(IP, Address(src_ptr, char_size * 2));
2548 __ subs(num_chr, num_chr, ShifterOperand(4));
2549 __ str(IP, Address(dst_ptr, char_size * 2));
2550 __ ldr(IP, Address(src_ptr, char_size * 4, Address::PostIndex));
2551 __ str(IP, Address(dst_ptr, char_size * 4, Address::PostIndex));
2552 __ b(&loop, GE);
2553
2554 __ adds(num_chr, num_chr, ShifterOperand(4));
Anton Kirilov6f644202017-02-27 18:29:45 +00002555 __ b(final_label, EQ);
Scott Wakeling3fdab772016-04-25 11:32:37 +01002556
2557 // Main loop for < 4 character case and remainder handling. Loads and stores one
2558 // 16-bit Java character at a time.
2559 __ Bind(&remainder);
2560 __ ldrh(IP, Address(src_ptr, char_size, Address::PostIndex));
2561 __ subs(num_chr, num_chr, ShifterOperand(1));
2562 __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex));
2563 __ b(&remainder, GT);
jessicahandojo05765752016-09-09 19:01:32 -07002564
2565 if (mirror::kUseStringCompression) {
Anton Kirilov6f644202017-02-27 18:29:45 +00002566 __ b(final_label);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002567
jessicahandojo05765752016-09-09 19:01:32 -07002568 const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
2569 DCHECK_EQ(c_char_size, 1u);
2570 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2571 __ Bind(&compressed_string_preloop);
2572 __ add(src_ptr, src_ptr, ShifterOperand(srcBegin));
2573 __ Bind(&compressed_string_loop);
2574 __ ldrb(IP, Address(src_ptr, c_char_size, Address::PostIndex));
2575 __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex));
2576 __ subs(num_chr, num_chr, ShifterOperand(1));
2577 __ b(&compressed_string_loop, GT);
2578 }
Scott Wakeling3fdab772016-04-25 11:32:37 +01002579
Anton Kirilov6f644202017-02-27 18:29:45 +00002580 if (done.IsLinked()) {
2581 __ Bind(&done);
2582 }
Tim Zhang25abd6c2016-01-19 23:39:24 +08002583}
2584
Anton Kirilova3ffea22016-04-07 17:02:37 +01002585void IntrinsicLocationsBuilderARM::VisitFloatIsInfinite(HInvoke* invoke) {
2586 CreateFPToIntLocations(arena_, invoke);
2587}
2588
2589void IntrinsicCodeGeneratorARM::VisitFloatIsInfinite(HInvoke* invoke) {
2590 ArmAssembler* const assembler = GetAssembler();
2591 LocationSummary* const locations = invoke->GetLocations();
2592 const Register out = locations->Out().AsRegister<Register>();
2593 // Shifting left by 1 bit makes the value encodable as an immediate operand;
2594 // we don't care about the sign bit anyway.
2595 constexpr uint32_t infinity = kPositiveInfinityFloat << 1U;
2596
2597 __ vmovrs(out, locations->InAt(0).AsFpuRegister<SRegister>());
2598 // We don't care about the sign bit, so shift left.
2599 __ Lsl(out, out, 1);
2600 __ eor(out, out, ShifterOperand(infinity));
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002601 codegen_->GenerateConditionWithZero(kCondEQ, out, out);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002602}
2603
2604void IntrinsicLocationsBuilderARM::VisitDoubleIsInfinite(HInvoke* invoke) {
2605 CreateFPToIntLocations(arena_, invoke);
2606}
2607
2608void IntrinsicCodeGeneratorARM::VisitDoubleIsInfinite(HInvoke* invoke) {
2609 ArmAssembler* const assembler = GetAssembler();
2610 LocationSummary* const locations = invoke->GetLocations();
2611 const Register out = locations->Out().AsRegister<Register>();
2612 // The highest 32 bits of double precision positive infinity separated into
2613 // two constants encodable as immediate operands.
2614 constexpr uint32_t infinity_high = 0x7f000000U;
2615 constexpr uint32_t infinity_high2 = 0x00f00000U;
2616
2617 static_assert((infinity_high | infinity_high2) == static_cast<uint32_t>(kPositiveInfinityDouble >> 32U),
2618 "The constants do not add up to the high 32 bits of double precision positive infinity.");
2619 __ vmovrrd(IP, out, FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
2620 __ eor(out, out, ShifterOperand(infinity_high));
2621 __ eor(out, out, ShifterOperand(infinity_high2));
2622 // We don't care about the sign bit, so shift left.
2623 __ orr(out, IP, ShifterOperand(out, LSL, 1));
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002624 codegen_->GenerateConditionWithZero(kCondEQ, out, out);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002625}
2626
Nicolas Geoffray331605a2017-03-01 11:01:41 +00002627void IntrinsicLocationsBuilderARM::VisitIntegerValueOf(HInvoke* invoke) {
2628 InvokeRuntimeCallingConvention calling_convention;
2629 IntrinsicVisitor::ComputeIntegerValueOfLocations(
2630 invoke,
2631 codegen_,
2632 Location::RegisterLocation(R0),
2633 Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2634}
2635
2636void IntrinsicCodeGeneratorARM::VisitIntegerValueOf(HInvoke* invoke) {
2637 IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo();
2638 LocationSummary* locations = invoke->GetLocations();
2639 ArmAssembler* const assembler = GetAssembler();
2640
2641 Register out = locations->Out().AsRegister<Register>();
2642 InvokeRuntimeCallingConvention calling_convention;
2643 Register argument = calling_convention.GetRegisterAt(0);
2644 if (invoke->InputAt(0)->IsConstant()) {
2645 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
2646 if (value >= info.low && value <= info.high) {
2647 // Just embed the j.l.Integer in the code.
2648 ScopedObjectAccess soa(Thread::Current());
2649 mirror::Object* boxed = info.cache->Get(value + (-info.low));
2650 DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed));
2651 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(boxed));
2652 __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
2653 } else {
2654 // Allocate and initialize a new j.l.Integer.
2655 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
2656 // JIT object table.
2657 uint32_t address =
2658 dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2659 __ LoadLiteral(argument, codegen_->DeduplicateBootImageAddressLiteral(address));
2660 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2661 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2662 __ LoadImmediate(IP, value);
2663 __ StoreToOffset(kStoreWord, IP, out, info.value_offset);
2664 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2665 // one.
2666 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2667 }
2668 } else {
2669 Register in = locations->InAt(0).AsRegister<Register>();
2670 // Check bounds of our cache.
2671 __ AddConstant(out, in, -info.low);
2672 __ CmpConstant(out, info.high - info.low + 1);
2673 Label allocate, done;
2674 __ b(&allocate, HS);
2675 // If the value is within the bounds, load the j.l.Integer directly from the array.
2676 uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
2677 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.cache));
2678 __ LoadLiteral(IP, codegen_->DeduplicateBootImageAddressLiteral(data_offset + address));
2679 codegen_->LoadFromShiftedRegOffset(Primitive::kPrimNot, locations->Out(), IP, out);
2680 __ MaybeUnpoisonHeapReference(out);
2681 __ b(&done);
2682 __ Bind(&allocate);
2683 // Otherwise allocate and initialize a new j.l.Integer.
2684 address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
2685 __ LoadLiteral(argument, codegen_->DeduplicateBootImageAddressLiteral(address));
2686 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
2687 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
2688 __ StoreToOffset(kStoreWord, in, out, info.value_offset);
2689 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
2690 // one.
2691 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
2692 __ Bind(&done);
2693 }
2694}
2695
Nicolas Geoffray365719c2017-03-08 13:11:50 +00002696void IntrinsicLocationsBuilderARM::VisitThreadInterrupted(HInvoke* invoke) {
2697 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2698 LocationSummary::kNoCall,
2699 kIntrinsified);
2700 locations->SetOut(Location::RequiresRegister());
2701}
2702
2703void IntrinsicCodeGeneratorARM::VisitThreadInterrupted(HInvoke* invoke) {
2704 ArmAssembler* assembler = GetAssembler();
2705 Register out = invoke->GetLocations()->Out().AsRegister<Register>();
2706 int32_t offset = Thread::InterruptedOffset<kArmPointerSize>().Int32Value();
2707 __ LoadFromOffset(kLoadWord, out, TR, offset);
2708 Label done;
Anton Kirilovfd522532017-05-10 12:46:57 +01002709 Label* const final_label = codegen_->GetFinalLabel(invoke, &done);
2710 __ CompareAndBranchIfZero(out, final_label);
Nicolas Geoffray365719c2017-03-08 13:11:50 +00002711 __ dmb(ISH);
2712 __ LoadImmediate(IP, 0);
2713 __ StoreToOffset(kStoreWord, IP, TR, offset);
2714 __ dmb(ISH);
Anton Kirilovfd522532017-05-10 12:46:57 +01002715 if (done.IsLinked()) {
2716 __ Bind(&done);
2717 }
Nicolas Geoffray365719c2017-03-08 13:11:50 +00002718}
2719
Aart Bik2f9fcc92016-03-01 15:16:54 -08002720UNIMPLEMENTED_INTRINSIC(ARM, MathMinDoubleDouble)
2721UNIMPLEMENTED_INTRINSIC(ARM, MathMinFloatFloat)
2722UNIMPLEMENTED_INTRINSIC(ARM, MathMaxDoubleDouble)
2723UNIMPLEMENTED_INTRINSIC(ARM, MathMaxFloatFloat)
2724UNIMPLEMENTED_INTRINSIC(ARM, MathMinLongLong)
2725UNIMPLEMENTED_INTRINSIC(ARM, MathMaxLongLong)
2726UNIMPLEMENTED_INTRINSIC(ARM, MathCeil) // Could be done by changing rounding mode, maybe?
2727UNIMPLEMENTED_INTRINSIC(ARM, MathFloor) // Could be done by changing rounding mode, maybe?
2728UNIMPLEMENTED_INTRINSIC(ARM, MathRint)
2729UNIMPLEMENTED_INTRINSIC(ARM, MathRoundDouble) // Could be done by changing rounding mode, maybe?
2730UNIMPLEMENTED_INTRINSIC(ARM, MathRoundFloat) // Could be done by changing rounding mode, maybe?
2731UNIMPLEMENTED_INTRINSIC(ARM, UnsafeCASLong) // High register pressure.
2732UNIMPLEMENTED_INTRINSIC(ARM, SystemArrayCopyChar)
Vladimir Markod254f5c2017-06-02 15:18:36 +00002733UNIMPLEMENTED_INTRINSIC(ARM, ReferenceGetReferent)
Aart Bik2f9fcc92016-03-01 15:16:54 -08002734UNIMPLEMENTED_INTRINSIC(ARM, IntegerHighestOneBit)
2735UNIMPLEMENTED_INTRINSIC(ARM, LongHighestOneBit)
2736UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit)
2737UNIMPLEMENTED_INTRINSIC(ARM, LongLowestOneBit)
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08002738
Aart Bikff7d89c2016-11-07 08:49:28 -08002739UNIMPLEMENTED_INTRINSIC(ARM, StringStringIndexOf);
2740UNIMPLEMENTED_INTRINSIC(ARM, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08002741UNIMPLEMENTED_INTRINSIC(ARM, StringBufferAppend);
2742UNIMPLEMENTED_INTRINSIC(ARM, StringBufferLength);
2743UNIMPLEMENTED_INTRINSIC(ARM, StringBufferToString);
2744UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderAppend);
2745UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderLength);
2746UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08002747
Aart Bik0e54c012016-03-04 12:08:31 -08002748// 1.8.
2749UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddInt)
2750UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddLong)
2751UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetInt)
2752UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetLong)
2753UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08002754
Aart Bik2f9fcc92016-03-01 15:16:54 -08002755UNREACHABLE_INTRINSICS(ARM)
Roland Levillain4d027112015-07-01 15:41:14 +01002756
2757#undef __
2758
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08002759} // namespace arm
2760} // namespace art