blob: 0a5acc3e64ae37b38825cdf4349aaf83a569587e [file] [log] [blame]
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "intrinsics_arm.h"
18
19#include "arch/arm/instruction_set_features_arm.h"
Mathieu Chartiere401d142015-04-22 13:56:20 -070020#include "art_method.h"
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -080021#include "code_generator_arm.h"
22#include "entrypoints/quick/quick_entrypoints.h"
23#include "intrinsics.h"
Andreas Gampe85b62f22015-09-09 13:15:38 -070024#include "intrinsics_utils.h"
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -080025#include "mirror/array-inl.h"
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -080026#include "mirror/string.h"
27#include "thread.h"
28#include "utils/arm/assembler_arm.h"
29
30namespace art {
31
32namespace arm {
33
34ArmAssembler* IntrinsicCodeGeneratorARM::GetAssembler() {
35 return codegen_->GetAssembler();
36}
37
38ArenaAllocator* IntrinsicCodeGeneratorARM::GetAllocator() {
39 return codegen_->GetGraph()->GetArena();
40}
41
Andreas Gampe85b62f22015-09-09 13:15:38 -070042using IntrinsicSlowPathARM = IntrinsicSlowPath<InvokeDexCallingConventionVisitorARM>;
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -080043
44bool IntrinsicLocationsBuilderARM::TryDispatch(HInvoke* invoke) {
45 Dispatch(invoke);
46 LocationSummary* res = invoke->GetLocations();
47 return res != nullptr && res->Intrinsified();
48}
49
50#define __ assembler->
51
52static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
53 LocationSummary* locations = new (arena) LocationSummary(invoke,
54 LocationSummary::kNoCall,
55 kIntrinsified);
56 locations->SetInAt(0, Location::RequiresFpuRegister());
57 locations->SetOut(Location::RequiresRegister());
58}
59
60static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
61 LocationSummary* locations = new (arena) LocationSummary(invoke,
62 LocationSummary::kNoCall,
63 kIntrinsified);
64 locations->SetInAt(0, Location::RequiresRegister());
65 locations->SetOut(Location::RequiresFpuRegister());
66}
67
68static void MoveFPToInt(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
69 Location input = locations->InAt(0);
70 Location output = locations->Out();
71 if (is64bit) {
72 __ vmovrrd(output.AsRegisterPairLow<Register>(),
73 output.AsRegisterPairHigh<Register>(),
74 FromLowSToD(input.AsFpuRegisterPairLow<SRegister>()));
75 } else {
76 __ vmovrs(output.AsRegister<Register>(), input.AsFpuRegister<SRegister>());
77 }
78}
79
80static void MoveIntToFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
81 Location input = locations->InAt(0);
82 Location output = locations->Out();
83 if (is64bit) {
84 __ vmovdrr(FromLowSToD(output.AsFpuRegisterPairLow<SRegister>()),
85 input.AsRegisterPairLow<Register>(),
86 input.AsRegisterPairHigh<Register>());
87 } else {
88 __ vmovsr(output.AsFpuRegister<SRegister>(), input.AsRegister<Register>());
89 }
90}
91
92void IntrinsicLocationsBuilderARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
93 CreateFPToIntLocations(arena_, invoke);
94}
95void IntrinsicLocationsBuilderARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
96 CreateIntToFPLocations(arena_, invoke);
97}
98
99void IntrinsicCodeGeneratorARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
100 MoveFPToInt(invoke->GetLocations(), true, GetAssembler());
101}
102void IntrinsicCodeGeneratorARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
103 MoveIntToFP(invoke->GetLocations(), true, GetAssembler());
104}
105
106void IntrinsicLocationsBuilderARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
107 CreateFPToIntLocations(arena_, invoke);
108}
109void IntrinsicLocationsBuilderARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
110 CreateIntToFPLocations(arena_, invoke);
111}
112
113void IntrinsicCodeGeneratorARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
114 MoveFPToInt(invoke->GetLocations(), false, GetAssembler());
115}
116void IntrinsicCodeGeneratorARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
117 MoveIntToFP(invoke->GetLocations(), false, GetAssembler());
118}
119
120static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
121 LocationSummary* locations = new (arena) LocationSummary(invoke,
122 LocationSummary::kNoCall,
123 kIntrinsified);
124 locations->SetInAt(0, Location::RequiresRegister());
125 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
126}
127
128static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
129 LocationSummary* locations = new (arena) LocationSummary(invoke,
130 LocationSummary::kNoCall,
131 kIntrinsified);
132 locations->SetInAt(0, Location::RequiresFpuRegister());
133 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
134}
135
Scott Wakeling611d3392015-07-10 11:42:06 +0100136static void GenNumberOfLeadingZeros(LocationSummary* locations,
137 Primitive::Type type,
138 ArmAssembler* assembler) {
139 Location in = locations->InAt(0);
140 Register out = locations->Out().AsRegister<Register>();
141
142 DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
143
144 if (type == Primitive::kPrimLong) {
145 Register in_reg_lo = in.AsRegisterPairLow<Register>();
146 Register in_reg_hi = in.AsRegisterPairHigh<Register>();
147 Label end;
148 __ clz(out, in_reg_hi);
149 __ CompareAndBranchIfNonZero(in_reg_hi, &end);
150 __ clz(out, in_reg_lo);
151 __ AddConstant(out, 32);
152 __ Bind(&end);
153 } else {
154 __ clz(out, in.AsRegister<Register>());
155 }
156}
157
158void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
159 CreateIntToIntLocations(arena_, invoke);
160}
161
162void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
163 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
164}
165
166void IntrinsicLocationsBuilderARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
167 LocationSummary* locations = new (arena_) LocationSummary(invoke,
168 LocationSummary::kNoCall,
169 kIntrinsified);
170 locations->SetInAt(0, Location::RequiresRegister());
171 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
172}
173
174void IntrinsicCodeGeneratorARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
175 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
176}
177
Scott Wakeling9ee23f42015-07-23 10:44:35 +0100178static void GenNumberOfTrailingZeros(LocationSummary* locations,
179 Primitive::Type type,
180 ArmAssembler* assembler) {
181 DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
182
183 Register out = locations->Out().AsRegister<Register>();
184
185 if (type == Primitive::kPrimLong) {
186 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
187 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
188 Label end;
189 __ rbit(out, in_reg_lo);
190 __ clz(out, out);
191 __ CompareAndBranchIfNonZero(in_reg_lo, &end);
192 __ rbit(out, in_reg_hi);
193 __ clz(out, out);
194 __ AddConstant(out, 32);
195 __ Bind(&end);
196 } else {
197 Register in = locations->InAt(0).AsRegister<Register>();
198 __ rbit(out, in);
199 __ clz(out, out);
200 }
201}
202
203void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
204 LocationSummary* locations = new (arena_) LocationSummary(invoke,
205 LocationSummary::kNoCall,
206 kIntrinsified);
207 locations->SetInAt(0, Location::RequiresRegister());
208 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
209}
210
211void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
212 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
213}
214
215void IntrinsicLocationsBuilderARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
216 LocationSummary* locations = new (arena_) LocationSummary(invoke,
217 LocationSummary::kNoCall,
218 kIntrinsified);
219 locations->SetInAt(0, Location::RequiresRegister());
220 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
221}
222
223void IntrinsicCodeGeneratorARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
224 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
225}
226
227static void GenIntegerRotate(LocationSummary* locations,
228 ArmAssembler* assembler,
229 bool is_left) {
230 Register in = locations->InAt(0).AsRegister<Register>();
231 Location rhs = locations->InAt(1);
232 Register out = locations->Out().AsRegister<Register>();
233
234 if (rhs.IsConstant()) {
235 // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
236 // so map all rotations to a +ve. equivalent in that range.
237 // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
238 uint32_t rot = rhs.GetConstant()->AsIntConstant()->GetValue() & 0x1F;
239 if (rot) {
240 // Rotate, mapping left rotations to right equivalents if necessary.
241 // (e.g. left by 2 bits == right by 30.)
242 __ Ror(out, in, is_left ? (0x20 - rot) : rot);
243 } else if (out != in) {
244 __ Mov(out, in);
245 }
246 } else {
247 if (is_left) {
248 __ rsb(out, rhs.AsRegister<Register>(), ShifterOperand(0));
249 __ Ror(out, in, out);
250 } else {
251 __ Ror(out, in, rhs.AsRegister<Register>());
252 }
253 }
254}
255
256// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
257// rotates by swapping input regs (effectively rotating by the first 32-bits of
258// a larger rotation) or flipping direction (thus treating larger right/left
259// rotations as sub-word sized rotations in the other direction) as appropriate.
260static void GenLongRotate(LocationSummary* locations,
261 ArmAssembler* assembler,
262 bool is_left) {
263 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
264 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
265 Location rhs = locations->InAt(1);
266 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
267 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
268
269 if (rhs.IsConstant()) {
270 uint32_t rot = rhs.GetConstant()->AsIntConstant()->GetValue();
271 // Map all left rotations to right equivalents.
272 if (is_left) {
273 rot = 0x40 - rot;
274 }
275 // Map all rotations to +ve. equivalents on the interval [0,63].
276 rot &= 0x3F;
277 // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
278 // logic below to a simple pair of binary orr.
279 // (e.g. 34 bits == in_reg swap + 2 bits right.)
280 if (rot >= 0x20) {
281 rot -= 0x20;
282 std::swap(in_reg_hi, in_reg_lo);
283 }
284 // Rotate, or mov to out for zero or word size rotations.
285 if (rot) {
286 __ Lsr(out_reg_hi, in_reg_hi, rot);
287 __ orr(out_reg_hi, out_reg_hi, ShifterOperand(in_reg_lo, arm::LSL, 0x20 - rot));
288 __ Lsr(out_reg_lo, in_reg_lo, rot);
289 __ orr(out_reg_lo, out_reg_lo, ShifterOperand(in_reg_hi, arm::LSL, 0x20 - rot));
290 } else {
291 __ Mov(out_reg_lo, in_reg_lo);
292 __ Mov(out_reg_hi, in_reg_hi);
293 }
294 } else {
295 Register shift_left = locations->GetTemp(0).AsRegister<Register>();
296 Register shift_right = locations->GetTemp(1).AsRegister<Register>();
297 Label end;
298 Label right;
299
300 __ and_(shift_left, rhs.AsRegister<Register>(), ShifterOperand(0x1F));
301 __ Lsrs(shift_right, rhs.AsRegister<Register>(), 6);
302 __ rsb(shift_right, shift_left, ShifterOperand(0x20), AL, kCcKeep);
303
304 if (is_left) {
305 __ b(&right, CS);
306 } else {
307 __ b(&right, CC);
308 std::swap(shift_left, shift_right);
309 }
310
311 // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
312 // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
313 __ Lsl(out_reg_hi, in_reg_hi, shift_left);
314 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
315 __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo));
316 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
317 __ Lsr(shift_left, in_reg_hi, shift_right);
318 __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_left));
319 __ b(&end);
320
321 // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
322 // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
323 __ Bind(&right);
324 __ Lsr(out_reg_hi, in_reg_hi, shift_right);
325 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
326 __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo));
327 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
328 __ Lsl(shift_right, in_reg_hi, shift_left);
329 __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_right));
330
331 __ Bind(&end);
332 }
333}
334
335void IntrinsicLocationsBuilderARM::VisitIntegerRotateRight(HInvoke* invoke) {
336 LocationSummary* locations = new (arena_) LocationSummary(invoke,
337 LocationSummary::kNoCall,
338 kIntrinsified);
339 locations->SetInAt(0, Location::RequiresRegister());
340 locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
341 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
342}
343
344void IntrinsicCodeGeneratorARM::VisitIntegerRotateRight(HInvoke* invoke) {
345 GenIntegerRotate(invoke->GetLocations(), GetAssembler(), false /* is_left */);
346}
347
348void IntrinsicLocationsBuilderARM::VisitLongRotateRight(HInvoke* invoke) {
349 LocationSummary* locations = new (arena_) LocationSummary(invoke,
350 LocationSummary::kNoCall,
351 kIntrinsified);
352 locations->SetInAt(0, Location::RequiresRegister());
353 if (invoke->InputAt(1)->IsConstant()) {
354 locations->SetInAt(1, Location::ConstantLocation(invoke->InputAt(1)->AsConstant()));
355 } else {
356 locations->SetInAt(1, Location::RequiresRegister());
357 locations->AddTemp(Location::RequiresRegister());
358 locations->AddTemp(Location::RequiresRegister());
359 }
360 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
361}
362
363void IntrinsicCodeGeneratorARM::VisitLongRotateRight(HInvoke* invoke) {
364 GenLongRotate(invoke->GetLocations(), GetAssembler(), false /* is_left */);
365}
366
367void IntrinsicLocationsBuilderARM::VisitIntegerRotateLeft(HInvoke* invoke) {
368 LocationSummary* locations = new (arena_) LocationSummary(invoke,
369 LocationSummary::kNoCall,
370 kIntrinsified);
371 locations->SetInAt(0, Location::RequiresRegister());
372 locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
373 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
374}
375
376void IntrinsicCodeGeneratorARM::VisitIntegerRotateLeft(HInvoke* invoke) {
377 GenIntegerRotate(invoke->GetLocations(), GetAssembler(), true /* is_left */);
378}
379
380void IntrinsicLocationsBuilderARM::VisitLongRotateLeft(HInvoke* invoke) {
381 LocationSummary* locations = new (arena_) LocationSummary(invoke,
382 LocationSummary::kNoCall,
383 kIntrinsified);
384 locations->SetInAt(0, Location::RequiresRegister());
385 if (invoke->InputAt(1)->IsConstant()) {
386 locations->SetInAt(1, Location::ConstantLocation(invoke->InputAt(1)->AsConstant()));
387 } else {
388 locations->SetInAt(1, Location::RequiresRegister());
389 locations->AddTemp(Location::RequiresRegister());
390 locations->AddTemp(Location::RequiresRegister());
391 }
392 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
393}
394
395void IntrinsicCodeGeneratorARM::VisitLongRotateLeft(HInvoke* invoke) {
396 GenLongRotate(invoke->GetLocations(), GetAssembler(), true /* is_left */);
397}
398
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800399static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
400 Location in = locations->InAt(0);
401 Location out = locations->Out();
402
403 if (is64bit) {
404 __ vabsd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
405 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
406 } else {
407 __ vabss(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>());
408 }
409}
410
411void IntrinsicLocationsBuilderARM::VisitMathAbsDouble(HInvoke* invoke) {
412 CreateFPToFPLocations(arena_, invoke);
413}
414
415void IntrinsicCodeGeneratorARM::VisitMathAbsDouble(HInvoke* invoke) {
416 MathAbsFP(invoke->GetLocations(), true, GetAssembler());
417}
418
419void IntrinsicLocationsBuilderARM::VisitMathAbsFloat(HInvoke* invoke) {
420 CreateFPToFPLocations(arena_, invoke);
421}
422
423void IntrinsicCodeGeneratorARM::VisitMathAbsFloat(HInvoke* invoke) {
424 MathAbsFP(invoke->GetLocations(), false, GetAssembler());
425}
426
427static void CreateIntToIntPlusTemp(ArenaAllocator* arena, HInvoke* invoke) {
428 LocationSummary* locations = new (arena) LocationSummary(invoke,
429 LocationSummary::kNoCall,
430 kIntrinsified);
431 locations->SetInAt(0, Location::RequiresRegister());
432 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
433
434 locations->AddTemp(Location::RequiresRegister());
435}
436
437static void GenAbsInteger(LocationSummary* locations,
438 bool is64bit,
439 ArmAssembler* assembler) {
440 Location in = locations->InAt(0);
441 Location output = locations->Out();
442
443 Register mask = locations->GetTemp(0).AsRegister<Register>();
444
445 if (is64bit) {
446 Register in_reg_lo = in.AsRegisterPairLow<Register>();
447 Register in_reg_hi = in.AsRegisterPairHigh<Register>();
448 Register out_reg_lo = output.AsRegisterPairLow<Register>();
449 Register out_reg_hi = output.AsRegisterPairHigh<Register>();
450
451 DCHECK_NE(out_reg_lo, in_reg_hi) << "Diagonal overlap unexpected.";
452
453 __ Asr(mask, in_reg_hi, 31);
454 __ adds(out_reg_lo, in_reg_lo, ShifterOperand(mask));
455 __ adc(out_reg_hi, in_reg_hi, ShifterOperand(mask));
456 __ eor(out_reg_lo, mask, ShifterOperand(out_reg_lo));
457 __ eor(out_reg_hi, mask, ShifterOperand(out_reg_hi));
458 } else {
459 Register in_reg = in.AsRegister<Register>();
460 Register out_reg = output.AsRegister<Register>();
461
462 __ Asr(mask, in_reg, 31);
463 __ add(out_reg, in_reg, ShifterOperand(mask));
464 __ eor(out_reg, mask, ShifterOperand(out_reg));
465 }
466}
467
468void IntrinsicLocationsBuilderARM::VisitMathAbsInt(HInvoke* invoke) {
469 CreateIntToIntPlusTemp(arena_, invoke);
470}
471
472void IntrinsicCodeGeneratorARM::VisitMathAbsInt(HInvoke* invoke) {
473 GenAbsInteger(invoke->GetLocations(), false, GetAssembler());
474}
475
476
477void IntrinsicLocationsBuilderARM::VisitMathAbsLong(HInvoke* invoke) {
478 CreateIntToIntPlusTemp(arena_, invoke);
479}
480
481void IntrinsicCodeGeneratorARM::VisitMathAbsLong(HInvoke* invoke) {
482 GenAbsInteger(invoke->GetLocations(), true, GetAssembler());
483}
484
485static void GenMinMax(LocationSummary* locations,
486 bool is_min,
487 ArmAssembler* assembler) {
488 Register op1 = locations->InAt(0).AsRegister<Register>();
489 Register op2 = locations->InAt(1).AsRegister<Register>();
490 Register out = locations->Out().AsRegister<Register>();
491
492 __ cmp(op1, ShifterOperand(op2));
493
494 __ it((is_min) ? Condition::LT : Condition::GT, kItElse);
495 __ mov(out, ShifterOperand(op1), is_min ? Condition::LT : Condition::GT);
496 __ mov(out, ShifterOperand(op2), is_min ? Condition::GE : Condition::LE);
497}
498
499static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
500 LocationSummary* locations = new (arena) LocationSummary(invoke,
501 LocationSummary::kNoCall,
502 kIntrinsified);
503 locations->SetInAt(0, Location::RequiresRegister());
504 locations->SetInAt(1, Location::RequiresRegister());
505 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
506}
507
508void IntrinsicLocationsBuilderARM::VisitMathMinIntInt(HInvoke* invoke) {
509 CreateIntIntToIntLocations(arena_, invoke);
510}
511
512void IntrinsicCodeGeneratorARM::VisitMathMinIntInt(HInvoke* invoke) {
513 GenMinMax(invoke->GetLocations(), true, GetAssembler());
514}
515
516void IntrinsicLocationsBuilderARM::VisitMathMaxIntInt(HInvoke* invoke) {
517 CreateIntIntToIntLocations(arena_, invoke);
518}
519
520void IntrinsicCodeGeneratorARM::VisitMathMaxIntInt(HInvoke* invoke) {
521 GenMinMax(invoke->GetLocations(), false, GetAssembler());
522}
523
524void IntrinsicLocationsBuilderARM::VisitMathSqrt(HInvoke* invoke) {
525 CreateFPToFPLocations(arena_, invoke);
526}
527
528void IntrinsicCodeGeneratorARM::VisitMathSqrt(HInvoke* invoke) {
529 LocationSummary* locations = invoke->GetLocations();
530 ArmAssembler* assembler = GetAssembler();
531 __ vsqrtd(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
532 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
533}
534
535void IntrinsicLocationsBuilderARM::VisitMemoryPeekByte(HInvoke* invoke) {
536 CreateIntToIntLocations(arena_, invoke);
537}
538
539void IntrinsicCodeGeneratorARM::VisitMemoryPeekByte(HInvoke* invoke) {
540 ArmAssembler* assembler = GetAssembler();
541 // Ignore upper 4B of long address.
542 __ ldrsb(invoke->GetLocations()->Out().AsRegister<Register>(),
543 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
544}
545
546void IntrinsicLocationsBuilderARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
547 CreateIntToIntLocations(arena_, invoke);
548}
549
550void IntrinsicCodeGeneratorARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
551 ArmAssembler* assembler = GetAssembler();
552 // Ignore upper 4B of long address.
553 __ ldr(invoke->GetLocations()->Out().AsRegister<Register>(),
554 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
555}
556
557void IntrinsicLocationsBuilderARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
558 CreateIntToIntLocations(arena_, invoke);
559}
560
561void IntrinsicCodeGeneratorARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
562 ArmAssembler* assembler = GetAssembler();
563 // Ignore upper 4B of long address.
564 Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
565 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
566 // exception. So we can't use ldrd as addr may be unaligned.
567 Register lo = invoke->GetLocations()->Out().AsRegisterPairLow<Register>();
568 Register hi = invoke->GetLocations()->Out().AsRegisterPairHigh<Register>();
569 if (addr == lo) {
570 __ ldr(hi, Address(addr, 4));
571 __ ldr(lo, Address(addr, 0));
572 } else {
573 __ ldr(lo, Address(addr, 0));
574 __ ldr(hi, Address(addr, 4));
575 }
576}
577
578void IntrinsicLocationsBuilderARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
579 CreateIntToIntLocations(arena_, invoke);
580}
581
582void IntrinsicCodeGeneratorARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
583 ArmAssembler* assembler = GetAssembler();
584 // Ignore upper 4B of long address.
585 __ ldrsh(invoke->GetLocations()->Out().AsRegister<Register>(),
586 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
587}
588
589static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
590 LocationSummary* locations = new (arena) LocationSummary(invoke,
591 LocationSummary::kNoCall,
592 kIntrinsified);
593 locations->SetInAt(0, Location::RequiresRegister());
594 locations->SetInAt(1, Location::RequiresRegister());
595}
596
597void IntrinsicLocationsBuilderARM::VisitMemoryPokeByte(HInvoke* invoke) {
598 CreateIntIntToVoidLocations(arena_, invoke);
599}
600
601void IntrinsicCodeGeneratorARM::VisitMemoryPokeByte(HInvoke* invoke) {
602 ArmAssembler* assembler = GetAssembler();
603 __ strb(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
604 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
605}
606
607void IntrinsicLocationsBuilderARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
608 CreateIntIntToVoidLocations(arena_, invoke);
609}
610
611void IntrinsicCodeGeneratorARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
612 ArmAssembler* assembler = GetAssembler();
613 __ str(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
614 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
615}
616
617void IntrinsicLocationsBuilderARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
618 CreateIntIntToVoidLocations(arena_, invoke);
619}
620
621void IntrinsicCodeGeneratorARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
622 ArmAssembler* assembler = GetAssembler();
623 // Ignore upper 4B of long address.
624 Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
625 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
626 // exception. So we can't use ldrd as addr may be unaligned.
627 __ str(invoke->GetLocations()->InAt(1).AsRegisterPairLow<Register>(), Address(addr, 0));
628 __ str(invoke->GetLocations()->InAt(1).AsRegisterPairHigh<Register>(), Address(addr, 4));
629}
630
631void IntrinsicLocationsBuilderARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
632 CreateIntIntToVoidLocations(arena_, invoke);
633}
634
635void IntrinsicCodeGeneratorARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
636 ArmAssembler* assembler = GetAssembler();
637 __ strh(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
638 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
639}
640
641void IntrinsicLocationsBuilderARM::VisitThreadCurrentThread(HInvoke* invoke) {
642 LocationSummary* locations = new (arena_) LocationSummary(invoke,
643 LocationSummary::kNoCall,
644 kIntrinsified);
645 locations->SetOut(Location::RequiresRegister());
646}
647
648void IntrinsicCodeGeneratorARM::VisitThreadCurrentThread(HInvoke* invoke) {
649 ArmAssembler* assembler = GetAssembler();
650 __ LoadFromOffset(kLoadWord,
651 invoke->GetLocations()->Out().AsRegister<Register>(),
652 TR,
653 Thread::PeerOffset<kArmPointerSize>().Int32Value());
654}
655
656static void GenUnsafeGet(HInvoke* invoke,
657 Primitive::Type type,
658 bool is_volatile,
659 CodeGeneratorARM* codegen) {
660 LocationSummary* locations = invoke->GetLocations();
661 DCHECK((type == Primitive::kPrimInt) ||
662 (type == Primitive::kPrimLong) ||
663 (type == Primitive::kPrimNot));
664 ArmAssembler* assembler = codegen->GetAssembler();
665 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
666 Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Long offset, lo part only.
667
668 if (type == Primitive::kPrimLong) {
669 Register trg_lo = locations->Out().AsRegisterPairLow<Register>();
670 __ add(IP, base, ShifterOperand(offset));
671 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
672 Register trg_hi = locations->Out().AsRegisterPairHigh<Register>();
673 __ ldrexd(trg_lo, trg_hi, IP);
674 } else {
675 __ ldrd(trg_lo, Address(IP));
676 }
677 } else {
678 Register trg = locations->Out().AsRegister<Register>();
679 __ ldr(trg, Address(base, offset));
680 }
681
682 if (is_volatile) {
683 __ dmb(ISH);
684 }
Roland Levillain4d027112015-07-01 15:41:14 +0100685
686 if (type == Primitive::kPrimNot) {
687 Register trg = locations->Out().AsRegister<Register>();
688 __ MaybeUnpoisonHeapReference(trg);
689 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800690}
691
692static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
693 LocationSummary* locations = new (arena) LocationSummary(invoke,
694 LocationSummary::kNoCall,
695 kIntrinsified);
696 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
697 locations->SetInAt(1, Location::RequiresRegister());
698 locations->SetInAt(2, Location::RequiresRegister());
699 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
700}
701
702void IntrinsicLocationsBuilderARM::VisitUnsafeGet(HInvoke* invoke) {
703 CreateIntIntIntToIntLocations(arena_, invoke);
704}
705void IntrinsicLocationsBuilderARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
706 CreateIntIntIntToIntLocations(arena_, invoke);
707}
708void IntrinsicLocationsBuilderARM::VisitUnsafeGetLong(HInvoke* invoke) {
709 CreateIntIntIntToIntLocations(arena_, invoke);
710}
711void IntrinsicLocationsBuilderARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
712 CreateIntIntIntToIntLocations(arena_, invoke);
713}
714void IntrinsicLocationsBuilderARM::VisitUnsafeGetObject(HInvoke* invoke) {
715 CreateIntIntIntToIntLocations(arena_, invoke);
716}
717void IntrinsicLocationsBuilderARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
718 CreateIntIntIntToIntLocations(arena_, invoke);
719}
720
721void IntrinsicCodeGeneratorARM::VisitUnsafeGet(HInvoke* invoke) {
722 GenUnsafeGet(invoke, Primitive::kPrimInt, false, codegen_);
723}
724void IntrinsicCodeGeneratorARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
725 GenUnsafeGet(invoke, Primitive::kPrimInt, true, codegen_);
726}
727void IntrinsicCodeGeneratorARM::VisitUnsafeGetLong(HInvoke* invoke) {
728 GenUnsafeGet(invoke, Primitive::kPrimLong, false, codegen_);
729}
730void IntrinsicCodeGeneratorARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
731 GenUnsafeGet(invoke, Primitive::kPrimLong, true, codegen_);
732}
733void IntrinsicCodeGeneratorARM::VisitUnsafeGetObject(HInvoke* invoke) {
734 GenUnsafeGet(invoke, Primitive::kPrimNot, false, codegen_);
735}
736void IntrinsicCodeGeneratorARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
737 GenUnsafeGet(invoke, Primitive::kPrimNot, true, codegen_);
738}
739
740static void CreateIntIntIntIntToVoid(ArenaAllocator* arena,
741 const ArmInstructionSetFeatures& features,
742 Primitive::Type type,
743 bool is_volatile,
744 HInvoke* invoke) {
745 LocationSummary* locations = new (arena) LocationSummary(invoke,
746 LocationSummary::kNoCall,
747 kIntrinsified);
748 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
749 locations->SetInAt(1, Location::RequiresRegister());
750 locations->SetInAt(2, Location::RequiresRegister());
751 locations->SetInAt(3, Location::RequiresRegister());
752
753 if (type == Primitive::kPrimLong) {
754 // Potentially need temps for ldrexd-strexd loop.
755 if (is_volatile && !features.HasAtomicLdrdAndStrd()) {
756 locations->AddTemp(Location::RequiresRegister()); // Temp_lo.
757 locations->AddTemp(Location::RequiresRegister()); // Temp_hi.
758 }
759 } else if (type == Primitive::kPrimNot) {
760 // Temps for card-marking.
761 locations->AddTemp(Location::RequiresRegister()); // Temp.
762 locations->AddTemp(Location::RequiresRegister()); // Card.
763 }
764}
765
766void IntrinsicLocationsBuilderARM::VisitUnsafePut(HInvoke* invoke) {
767 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, false, invoke);
768}
769void IntrinsicLocationsBuilderARM::VisitUnsafePutOrdered(HInvoke* invoke) {
770 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, false, invoke);
771}
772void IntrinsicLocationsBuilderARM::VisitUnsafePutVolatile(HInvoke* invoke) {
773 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, true, invoke);
774}
775void IntrinsicLocationsBuilderARM::VisitUnsafePutObject(HInvoke* invoke) {
776 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, false, invoke);
777}
778void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
779 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, false, invoke);
780}
781void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
782 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, true, invoke);
783}
784void IntrinsicLocationsBuilderARM::VisitUnsafePutLong(HInvoke* invoke) {
785 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, false, invoke);
786}
787void IntrinsicLocationsBuilderARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
788 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, false, invoke);
789}
790void IntrinsicLocationsBuilderARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
791 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, true, invoke);
792}
793
794static void GenUnsafePut(LocationSummary* locations,
795 Primitive::Type type,
796 bool is_volatile,
797 bool is_ordered,
798 CodeGeneratorARM* codegen) {
799 ArmAssembler* assembler = codegen->GetAssembler();
800
801 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
802 Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Long offset, lo part only.
803 Register value;
804
805 if (is_volatile || is_ordered) {
806 __ dmb(ISH);
807 }
808
809 if (type == Primitive::kPrimLong) {
810 Register value_lo = locations->InAt(3).AsRegisterPairLow<Register>();
811 value = value_lo;
812 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
813 Register temp_lo = locations->GetTemp(0).AsRegister<Register>();
814 Register temp_hi = locations->GetTemp(1).AsRegister<Register>();
815 Register value_hi = locations->InAt(3).AsRegisterPairHigh<Register>();
816
817 __ add(IP, base, ShifterOperand(offset));
818 Label loop_head;
819 __ Bind(&loop_head);
820 __ ldrexd(temp_lo, temp_hi, IP);
821 __ strexd(temp_lo, value_lo, value_hi, IP);
822 __ cmp(temp_lo, ShifterOperand(0));
823 __ b(&loop_head, NE);
824 } else {
825 __ add(IP, base, ShifterOperand(offset));
826 __ strd(value_lo, Address(IP));
827 }
828 } else {
Roland Levillain4d027112015-07-01 15:41:14 +0100829 value = locations->InAt(3).AsRegister<Register>();
830 Register source = value;
831 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
832 Register temp = locations->GetTemp(0).AsRegister<Register>();
833 __ Mov(temp, value);
834 __ PoisonHeapReference(temp);
835 source = temp;
836 }
837 __ str(source, Address(base, offset));
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800838 }
839
840 if (is_volatile) {
841 __ dmb(ISH);
842 }
843
844 if (type == Primitive::kPrimNot) {
845 Register temp = locations->GetTemp(0).AsRegister<Register>();
846 Register card = locations->GetTemp(1).AsRegister<Register>();
Nicolas Geoffray07276db2015-05-18 14:22:09 +0100847 bool value_can_be_null = true; // TODO: Worth finding out this information?
848 codegen->MarkGCCard(temp, card, base, value, value_can_be_null);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800849 }
850}
851
852void IntrinsicCodeGeneratorARM::VisitUnsafePut(HInvoke* invoke) {
853 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, false, codegen_);
854}
855void IntrinsicCodeGeneratorARM::VisitUnsafePutOrdered(HInvoke* invoke) {
856 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, true, codegen_);
857}
858void IntrinsicCodeGeneratorARM::VisitUnsafePutVolatile(HInvoke* invoke) {
859 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, true, false, codegen_);
860}
861void IntrinsicCodeGeneratorARM::VisitUnsafePutObject(HInvoke* invoke) {
862 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, false, codegen_);
863}
864void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
865 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, true, codegen_);
866}
867void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
868 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, true, false, codegen_);
869}
870void IntrinsicCodeGeneratorARM::VisitUnsafePutLong(HInvoke* invoke) {
871 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, false, codegen_);
872}
873void IntrinsicCodeGeneratorARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
874 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, true, codegen_);
875}
876void IntrinsicCodeGeneratorARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
877 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, false, codegen_);
878}
879
880static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena,
881 HInvoke* invoke) {
882 LocationSummary* locations = new (arena) LocationSummary(invoke,
883 LocationSummary::kNoCall,
884 kIntrinsified);
885 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
886 locations->SetInAt(1, Location::RequiresRegister());
887 locations->SetInAt(2, Location::RequiresRegister());
888 locations->SetInAt(3, Location::RequiresRegister());
889 locations->SetInAt(4, Location::RequiresRegister());
890
891 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
892
893 locations->AddTemp(Location::RequiresRegister()); // Pointer.
894 locations->AddTemp(Location::RequiresRegister()); // Temp 1.
895 locations->AddTemp(Location::RequiresRegister()); // Temp 2.
896}
897
898static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM* codegen) {
899 DCHECK_NE(type, Primitive::kPrimLong);
900
901 ArmAssembler* assembler = codegen->GetAssembler();
902
903 Register out = locations->Out().AsRegister<Register>(); // Boolean result.
904
905 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
906 Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Offset (discard high 4B).
907 Register expected_lo = locations->InAt(3).AsRegister<Register>(); // Expected.
908 Register value_lo = locations->InAt(4).AsRegister<Register>(); // Value.
909
910 Register tmp_ptr = locations->GetTemp(0).AsRegister<Register>(); // Pointer to actual memory.
911 Register tmp_lo = locations->GetTemp(1).AsRegister<Register>(); // Value in memory.
912
913 if (type == Primitive::kPrimNot) {
914 // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
915 // object and scan the receiver at the next GC for nothing.
Nicolas Geoffray07276db2015-05-18 14:22:09 +0100916 bool value_can_be_null = true; // TODO: Worth finding out this information?
917 codegen->MarkGCCard(tmp_ptr, tmp_lo, base, value_lo, value_can_be_null);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800918 }
919
920 // Prevent reordering with prior memory operations.
921 __ dmb(ISH);
922
923 __ add(tmp_ptr, base, ShifterOperand(offset));
924
Roland Levillain4d027112015-07-01 15:41:14 +0100925 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
926 codegen->GetAssembler()->PoisonHeapReference(expected_lo);
927 codegen->GetAssembler()->PoisonHeapReference(value_lo);
928 }
929
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800930 // do {
931 // tmp = [r_ptr] - expected;
932 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
933 // result = tmp != 0;
934
935 Label loop_head;
936 __ Bind(&loop_head);
937
938 __ ldrex(tmp_lo, tmp_ptr);
939
940 __ subs(tmp_lo, tmp_lo, ShifterOperand(expected_lo));
941
942 __ it(EQ, ItState::kItT);
943 __ strex(tmp_lo, value_lo, tmp_ptr, EQ);
944 __ cmp(tmp_lo, ShifterOperand(1), EQ);
945
946 __ b(&loop_head, EQ);
947
948 __ dmb(ISH);
949
950 __ rsbs(out, tmp_lo, ShifterOperand(1));
951 __ it(CC);
952 __ mov(out, ShifterOperand(0), CC);
Roland Levillain4d027112015-07-01 15:41:14 +0100953
954 if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
955 codegen->GetAssembler()->UnpoisonHeapReference(value_lo);
956 codegen->GetAssembler()->UnpoisonHeapReference(expected_lo);
957 }
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800958}
959
Andreas Gampeca714582015-04-03 19:41:34 -0700960void IntrinsicLocationsBuilderARM::VisitUnsafeCASInt(HInvoke* invoke) {
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800961 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
962}
Andreas Gampeca714582015-04-03 19:41:34 -0700963void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillain985ff702015-10-23 13:25:35 +0100964 // The UnsafeCASObject intrinsic does not always work when heap
965 // poisoning is enabled (it breaks run-test 004-UnsafeTest); turn it
966 // off temporarily as a quick fix.
967 // TODO(rpl): Fix it and turn it back on.
968 if (kPoisonHeapReferences) {
969 return;
970 }
971
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800972 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
973}
974void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) {
975 GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
976}
977void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) {
978 GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
979}
980
981void IntrinsicLocationsBuilderARM::VisitStringCharAt(HInvoke* invoke) {
982 LocationSummary* locations = new (arena_) LocationSummary(invoke,
983 LocationSummary::kCallOnSlowPath,
984 kIntrinsified);
985 locations->SetInAt(0, Location::RequiresRegister());
986 locations->SetInAt(1, Location::RequiresRegister());
987 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
988
989 locations->AddTemp(Location::RequiresRegister());
990 locations->AddTemp(Location::RequiresRegister());
991}
992
993void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) {
994 ArmAssembler* assembler = GetAssembler();
995 LocationSummary* locations = invoke->GetLocations();
996
997 // Location of reference to data array
998 const MemberOffset value_offset = mirror::String::ValueOffset();
999 // Location of count
1000 const MemberOffset count_offset = mirror::String::CountOffset();
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001001
1002 Register obj = locations->InAt(0).AsRegister<Register>(); // String object pointer.
1003 Register idx = locations->InAt(1).AsRegister<Register>(); // Index of character.
1004 Register out = locations->Out().AsRegister<Register>(); // Result character.
1005
1006 Register temp = locations->GetTemp(0).AsRegister<Register>();
1007 Register array_temp = locations->GetTemp(1).AsRegister<Register>();
1008
1009 // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
1010 // the cost.
1011 // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
1012 // we will not optimize the code for constants (which would save a register).
1013
Andreas Gampe85b62f22015-09-09 13:15:38 -07001014 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001015 codegen_->AddSlowPath(slow_path);
1016
1017 __ ldr(temp, Address(obj, count_offset.Int32Value())); // temp = str.length.
1018 codegen_->MaybeRecordImplicitNullCheck(invoke);
1019 __ cmp(idx, ShifterOperand(temp));
1020 __ b(slow_path->GetEntryLabel(), CS);
1021
Jeff Hao848f70a2014-01-15 13:49:50 -08001022 __ add(array_temp, obj, ShifterOperand(value_offset.Int32Value())); // array_temp := str.value.
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001023
1024 // Load the value.
Jeff Hao848f70a2014-01-15 13:49:50 -08001025 __ ldrh(out, Address(array_temp, idx, LSL, 1)); // out := array_temp[idx].
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001026
1027 __ Bind(slow_path->GetExitLabel());
1028}
1029
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001030void IntrinsicLocationsBuilderARM::VisitStringCompareTo(HInvoke* invoke) {
1031 // The inputs plus one temp.
1032 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1033 LocationSummary::kCall,
1034 kIntrinsified);
1035 InvokeRuntimeCallingConvention calling_convention;
1036 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1037 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1038 locations->SetOut(Location::RegisterLocation(R0));
1039}
1040
1041void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) {
1042 ArmAssembler* assembler = GetAssembler();
1043 LocationSummary* locations = invoke->GetLocations();
1044
Nicolas Geoffray512e04d2015-03-27 17:21:24 +00001045 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +01001046 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001047
1048 Register argument = locations->InAt(1).AsRegister<Register>();
1049 __ cmp(argument, ShifterOperand(0));
Andreas Gampe85b62f22015-09-09 13:15:38 -07001050 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +00001051 codegen_->AddSlowPath(slow_path);
1052 __ b(slow_path->GetEntryLabel(), EQ);
1053
1054 __ LoadFromOffset(
1055 kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pStringCompareTo).Int32Value());
1056 __ blx(LR);
1057 __ Bind(slow_path->GetExitLabel());
1058}
1059
Agi Csaki289cd552015-08-18 17:10:38 -07001060void IntrinsicLocationsBuilderARM::VisitStringEquals(HInvoke* invoke) {
1061 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1062 LocationSummary::kNoCall,
1063 kIntrinsified);
1064 InvokeRuntimeCallingConvention calling_convention;
1065 locations->SetInAt(0, Location::RequiresRegister());
1066 locations->SetInAt(1, Location::RequiresRegister());
1067 // Temporary registers to store lengths of strings and for calculations.
1068 // Using instruction cbz requires a low register, so explicitly set a temp to be R0.
1069 locations->AddTemp(Location::RegisterLocation(R0));
1070 locations->AddTemp(Location::RequiresRegister());
1071 locations->AddTemp(Location::RequiresRegister());
1072
1073 locations->SetOut(Location::RequiresRegister());
1074}
1075
1076void IntrinsicCodeGeneratorARM::VisitStringEquals(HInvoke* invoke) {
1077 ArmAssembler* assembler = GetAssembler();
1078 LocationSummary* locations = invoke->GetLocations();
1079
1080 Register str = locations->InAt(0).AsRegister<Register>();
1081 Register arg = locations->InAt(1).AsRegister<Register>();
1082 Register out = locations->Out().AsRegister<Register>();
1083
1084 Register temp = locations->GetTemp(0).AsRegister<Register>();
1085 Register temp1 = locations->GetTemp(1).AsRegister<Register>();
1086 Register temp2 = locations->GetTemp(2).AsRegister<Register>();
1087
1088 Label loop;
1089 Label end;
1090 Label return_true;
1091 Label return_false;
1092
1093 // Get offsets of count, value, and class fields within a string object.
1094 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
1095 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1096 const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
1097
1098 // Note that the null check must have been done earlier.
1099 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1100
1101 // Check if input is null, return false if it is.
1102 __ CompareAndBranchIfZero(arg, &return_false);
1103
1104 // Instanceof check for the argument by comparing class fields.
1105 // All string objects must have the same type since String cannot be subclassed.
1106 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1107 // If the argument is a string object, its class field must be equal to receiver's class field.
1108 __ ldr(temp, Address(str, class_offset));
1109 __ ldr(temp1, Address(arg, class_offset));
1110 __ cmp(temp, ShifterOperand(temp1));
1111 __ b(&return_false, NE);
1112
1113 // Load lengths of this and argument strings.
1114 __ ldr(temp, Address(str, count_offset));
1115 __ ldr(temp1, Address(arg, count_offset));
1116 // Check if lengths are equal, return false if they're not.
1117 __ cmp(temp, ShifterOperand(temp1));
1118 __ b(&return_false, NE);
1119 // Return true if both strings are empty.
1120 __ cbz(temp, &return_true);
1121
1122 // Reference equality check, return true if same reference.
1123 __ cmp(str, ShifterOperand(arg));
1124 __ b(&return_true, EQ);
1125
1126 // Assertions that must hold in order to compare strings 2 characters at a time.
1127 DCHECK_ALIGNED(value_offset, 4);
1128 static_assert(IsAligned<4>(kObjectAlignment), "String of odd length is not zero padded");
1129
Agi Csaki289cd552015-08-18 17:10:38 -07001130 __ LoadImmediate(temp1, value_offset);
Agi Csaki289cd552015-08-18 17:10:38 -07001131
1132 // Loop to compare strings 2 characters at a time starting at the front of the string.
1133 // Ok to do this because strings with an odd length are zero-padded.
1134 __ Bind(&loop);
1135 __ ldr(out, Address(str, temp1));
1136 __ ldr(temp2, Address(arg, temp1));
1137 __ cmp(out, ShifterOperand(temp2));
1138 __ b(&return_false, NE);
1139 __ add(temp1, temp1, ShifterOperand(sizeof(uint32_t)));
Vladimir Markoa63f0d42015-09-01 13:36:35 +01001140 __ subs(temp, temp, ShifterOperand(sizeof(uint32_t) / sizeof(uint16_t)));
1141 __ b(&loop, GT);
Agi Csaki289cd552015-08-18 17:10:38 -07001142
1143 // Return true and exit the function.
1144 // If loop does not result in returning false, we return true.
1145 __ Bind(&return_true);
1146 __ LoadImmediate(out, 1);
1147 __ b(&end);
1148
1149 // Return false and exit the function.
1150 __ Bind(&return_false);
1151 __ LoadImmediate(out, 0);
1152 __ Bind(&end);
1153}
1154
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001155static void GenerateVisitStringIndexOf(HInvoke* invoke,
1156 ArmAssembler* assembler,
1157 CodeGeneratorARM* codegen,
1158 ArenaAllocator* allocator,
1159 bool start_at_zero) {
1160 LocationSummary* locations = invoke->GetLocations();
1161 Register tmp_reg = locations->GetTemp(0).AsRegister<Register>();
1162
1163 // Note that the null check must have been done earlier.
1164 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1165
1166 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
1167 // or directly dispatch if we have a constant.
Andreas Gampe85b62f22015-09-09 13:15:38 -07001168 SlowPathCode* slow_path = nullptr;
Andreas Gampeba6fdbc2015-05-07 22:31:55 -07001169 if (invoke->InputAt(1)->IsIntConstant()) {
1170 if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) >
1171 std::numeric_limits<uint16_t>::max()) {
1172 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1173 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
1174 slow_path = new (allocator) IntrinsicSlowPathARM(invoke);
1175 codegen->AddSlowPath(slow_path);
1176 __ b(slow_path->GetEntryLabel());
1177 __ Bind(slow_path->GetExitLabel());
1178 return;
1179 }
1180 } else {
1181 Register char_reg = locations->InAt(1).AsRegister<Register>();
1182 __ LoadImmediate(tmp_reg, std::numeric_limits<uint16_t>::max());
1183 __ cmp(char_reg, ShifterOperand(tmp_reg));
1184 slow_path = new (allocator) IntrinsicSlowPathARM(invoke);
1185 codegen->AddSlowPath(slow_path);
1186 __ b(slow_path->GetEntryLabel(), HI);
1187 }
1188
1189 if (start_at_zero) {
1190 DCHECK_EQ(tmp_reg, R2);
1191 // Start-index = 0.
1192 __ LoadImmediate(tmp_reg, 0);
1193 }
1194
1195 __ LoadFromOffset(kLoadWord, LR, TR,
1196 QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pIndexOf).Int32Value());
1197 __ blx(LR);
1198
1199 if (slow_path != nullptr) {
1200 __ Bind(slow_path->GetExitLabel());
1201 }
1202}
1203
1204void IntrinsicLocationsBuilderARM::VisitStringIndexOf(HInvoke* invoke) {
1205 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1206 LocationSummary::kCall,
1207 kIntrinsified);
1208 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1209 // best to align the inputs accordingly.
1210 InvokeRuntimeCallingConvention calling_convention;
1211 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1212 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1213 locations->SetOut(Location::RegisterLocation(R0));
1214
1215 // Need a temp for slow-path codepoint compare, and need to send start-index=0.
1216 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1217}
1218
1219void IntrinsicCodeGeneratorARM::VisitStringIndexOf(HInvoke* invoke) {
1220 GenerateVisitStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), true);
1221}
1222
1223void IntrinsicLocationsBuilderARM::VisitStringIndexOfAfter(HInvoke* invoke) {
1224 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1225 LocationSummary::kCall,
1226 kIntrinsified);
1227 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
1228 // best to align the inputs accordingly.
1229 InvokeRuntimeCallingConvention calling_convention;
1230 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1231 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1232 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1233 locations->SetOut(Location::RegisterLocation(R0));
1234
1235 // Need a temp for slow-path codepoint compare.
1236 locations->AddTemp(Location::RequiresRegister());
1237}
1238
1239void IntrinsicCodeGeneratorARM::VisitStringIndexOfAfter(HInvoke* invoke) {
1240 GenerateVisitStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), false);
1241}
1242
Jeff Hao848f70a2014-01-15 13:49:50 -08001243void IntrinsicLocationsBuilderARM::VisitStringNewStringFromBytes(HInvoke* invoke) {
1244 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1245 LocationSummary::kCall,
1246 kIntrinsified);
1247 InvokeRuntimeCallingConvention calling_convention;
1248 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1249 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1250 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1251 locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
1252 locations->SetOut(Location::RegisterLocation(R0));
1253}
1254
1255void IntrinsicCodeGeneratorARM::VisitStringNewStringFromBytes(HInvoke* invoke) {
1256 ArmAssembler* assembler = GetAssembler();
1257 LocationSummary* locations = invoke->GetLocations();
1258
1259 Register byte_array = locations->InAt(0).AsRegister<Register>();
1260 __ cmp(byte_array, ShifterOperand(0));
Andreas Gampe85b62f22015-09-09 13:15:38 -07001261 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
Jeff Hao848f70a2014-01-15 13:49:50 -08001262 codegen_->AddSlowPath(slow_path);
1263 __ b(slow_path->GetEntryLabel(), EQ);
1264
1265 __ LoadFromOffset(
1266 kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromBytes).Int32Value());
1267 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1268 __ blx(LR);
1269 __ Bind(slow_path->GetExitLabel());
1270}
1271
1272void IntrinsicLocationsBuilderARM::VisitStringNewStringFromChars(HInvoke* invoke) {
1273 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1274 LocationSummary::kCall,
1275 kIntrinsified);
1276 InvokeRuntimeCallingConvention calling_convention;
1277 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1278 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1279 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1280 locations->SetOut(Location::RegisterLocation(R0));
1281}
1282
1283void IntrinsicCodeGeneratorARM::VisitStringNewStringFromChars(HInvoke* invoke) {
1284 ArmAssembler* assembler = GetAssembler();
1285
1286 __ LoadFromOffset(
1287 kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromChars).Int32Value());
1288 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1289 __ blx(LR);
1290}
1291
1292void IntrinsicLocationsBuilderARM::VisitStringNewStringFromString(HInvoke* invoke) {
1293 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1294 LocationSummary::kCall,
1295 kIntrinsified);
1296 InvokeRuntimeCallingConvention calling_convention;
1297 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1298 locations->SetOut(Location::RegisterLocation(R0));
1299}
1300
1301void IntrinsicCodeGeneratorARM::VisitStringNewStringFromString(HInvoke* invoke) {
1302 ArmAssembler* assembler = GetAssembler();
1303 LocationSummary* locations = invoke->GetLocations();
1304
1305 Register string_to_copy = locations->InAt(0).AsRegister<Register>();
1306 __ cmp(string_to_copy, ShifterOperand(0));
Andreas Gampe85b62f22015-09-09 13:15:38 -07001307 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
Jeff Hao848f70a2014-01-15 13:49:50 -08001308 codegen_->AddSlowPath(slow_path);
1309 __ b(slow_path->GetEntryLabel(), EQ);
1310
1311 __ LoadFromOffset(kLoadWord,
1312 LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromString).Int32Value());
1313 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1314 __ blx(LR);
1315 __ Bind(slow_path->GetExitLabel());
1316}
1317
Nicolas Geoffray5bd05a52015-10-13 09:48:30 +01001318void IntrinsicLocationsBuilderARM::VisitSystemArrayCopy(HInvoke* invoke) {
1319 CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
1320 LocationSummary* locations = invoke->GetLocations();
1321 if (locations == nullptr) {
1322 return;
1323 }
1324
1325 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
1326 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
1327 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
1328
1329 if (src_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(src_pos->GetValue())) {
1330 locations->SetInAt(1, Location::RequiresRegister());
1331 }
1332 if (dest_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(dest_pos->GetValue())) {
1333 locations->SetInAt(3, Location::RequiresRegister());
1334 }
1335 if (length != nullptr && !assembler_->ShifterOperandCanAlwaysHold(length->GetValue())) {
1336 locations->SetInAt(4, Location::RequiresRegister());
1337 }
1338}
1339
1340static void CheckPosition(ArmAssembler* assembler,
1341 Location pos,
1342 Register input,
1343 Location length,
1344 SlowPathCode* slow_path,
1345 Register input_len,
1346 Register temp,
1347 bool length_is_input_length = false) {
1348 // Where is the length in the Array?
1349 const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
1350
1351 if (pos.IsConstant()) {
1352 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
1353 if (pos_const == 0) {
1354 if (!length_is_input_length) {
1355 // Check that length(input) >= length.
1356 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
1357 if (length.IsConstant()) {
1358 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1359 } else {
1360 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1361 }
1362 __ b(slow_path->GetEntryLabel(), LT);
1363 }
1364 } else {
1365 // Check that length(input) >= pos.
1366 __ LoadFromOffset(kLoadWord, input_len, input, length_offset);
1367 __ subs(temp, input_len, ShifterOperand(pos_const));
1368 __ b(slow_path->GetEntryLabel(), LT);
1369
1370 // Check that (length(input) - pos) >= length.
1371 if (length.IsConstant()) {
1372 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1373 } else {
1374 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1375 }
1376 __ b(slow_path->GetEntryLabel(), LT);
1377 }
1378 } else if (length_is_input_length) {
1379 // The only way the copy can succeed is if pos is zero.
1380 Register pos_reg = pos.AsRegister<Register>();
1381 __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
1382 } else {
1383 // Check that pos >= 0.
1384 Register pos_reg = pos.AsRegister<Register>();
1385 __ cmp(pos_reg, ShifterOperand(0));
1386 __ b(slow_path->GetEntryLabel(), LT);
1387
1388 // Check that pos <= length(input).
1389 __ LoadFromOffset(kLoadWord, temp, input, length_offset);
1390 __ subs(temp, temp, ShifterOperand(pos_reg));
1391 __ b(slow_path->GetEntryLabel(), LT);
1392
1393 // Check that (length(input) - pos) >= length.
1394 if (length.IsConstant()) {
1395 __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
1396 } else {
1397 __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
1398 }
1399 __ b(slow_path->GetEntryLabel(), LT);
1400 }
1401}
1402
1403void IntrinsicCodeGeneratorARM::VisitSystemArrayCopy(HInvoke* invoke) {
1404 ArmAssembler* assembler = GetAssembler();
1405 LocationSummary* locations = invoke->GetLocations();
1406
1407 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1408 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
1409 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
1410 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
1411
1412 Register src = locations->InAt(0).AsRegister<Register>();
1413 Location src_pos = locations->InAt(1);
1414 Register dest = locations->InAt(2).AsRegister<Register>();
1415 Location dest_pos = locations->InAt(3);
1416 Location length = locations->InAt(4);
1417 Register temp1 = locations->GetTemp(0).AsRegister<Register>();
1418 Register temp2 = locations->GetTemp(1).AsRegister<Register>();
1419 Register temp3 = locations->GetTemp(2).AsRegister<Register>();
1420
1421 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
1422 codegen_->AddSlowPath(slow_path);
1423
1424 Label ok;
1425 SystemArrayCopyOptimizations optimizations(invoke);
1426
1427 if (!optimizations.GetDestinationIsSource()) {
1428 if (!src_pos.IsConstant() || !dest_pos.IsConstant()) {
1429 __ cmp(src, ShifterOperand(dest));
1430 }
1431 }
1432
1433 // If source and destination are the same, we go to slow path if we need to do
1434 // forward copying.
1435 if (src_pos.IsConstant()) {
1436 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
1437 if (dest_pos.IsConstant()) {
1438 // Checked when building locations.
1439 DCHECK(!optimizations.GetDestinationIsSource()
1440 || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
1441 } else {
1442 if (!optimizations.GetDestinationIsSource()) {
1443 __ b(&ok, NE);
1444 }
1445 __ cmp(dest_pos.AsRegister<Register>(), ShifterOperand(src_pos_constant));
1446 __ b(slow_path->GetEntryLabel(), GT);
1447 }
1448 } else {
1449 if (!optimizations.GetDestinationIsSource()) {
1450 __ b(&ok, NE);
1451 }
1452 if (dest_pos.IsConstant()) {
1453 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
1454 __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos_constant));
1455 } else {
1456 __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos.AsRegister<Register>()));
1457 }
1458 __ b(slow_path->GetEntryLabel(), LT);
1459 }
1460
1461 __ Bind(&ok);
1462
1463 if (!optimizations.GetSourceIsNotNull()) {
1464 // Bail out if the source is null.
1465 __ CompareAndBranchIfZero(src, slow_path->GetEntryLabel());
1466 }
1467
1468 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
1469 // Bail out if the destination is null.
1470 __ CompareAndBranchIfZero(dest, slow_path->GetEntryLabel());
1471 }
1472
1473 // If the length is negative, bail out.
1474 // We have already checked in the LocationsBuilder for the constant case.
1475 if (!length.IsConstant() &&
1476 !optimizations.GetCountIsSourceLength() &&
1477 !optimizations.GetCountIsDestinationLength()) {
1478 __ cmp(length.AsRegister<Register>(), ShifterOperand(0));
1479 __ b(slow_path->GetEntryLabel(), LT);
1480 }
1481
1482 // Validity checks: source.
1483 CheckPosition(assembler,
1484 src_pos,
1485 src,
1486 length,
1487 slow_path,
1488 temp1,
1489 temp2,
1490 optimizations.GetCountIsSourceLength());
1491
1492 // Validity checks: dest.
1493 CheckPosition(assembler,
1494 dest_pos,
1495 dest,
1496 length,
1497 slow_path,
1498 temp1,
1499 temp2,
1500 optimizations.GetCountIsDestinationLength());
1501
1502 if (!optimizations.GetDoesNotNeedTypeCheck()) {
1503 // Check whether all elements of the source array are assignable to the component
1504 // type of the destination array. We do two checks: the classes are the same,
1505 // or the destination is Object[]. If none of these checks succeed, we go to the
1506 // slow path.
1507 __ LoadFromOffset(kLoadWord, temp1, dest, class_offset);
1508 __ LoadFromOffset(kLoadWord, temp2, src, class_offset);
1509 bool did_unpoison = false;
1510 if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
1511 !optimizations.GetSourceIsNonPrimitiveArray()) {
1512 // One or two of the references need to be unpoisoned. Unpoisoned them
1513 // both to make the identity check valid.
1514 __ MaybeUnpoisonHeapReference(temp1);
1515 __ MaybeUnpoisonHeapReference(temp2);
1516 did_unpoison = true;
1517 }
1518
1519 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
1520 // Bail out if the destination is not a non primitive array.
1521 __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
1522 __ CompareAndBranchIfZero(temp3, slow_path->GetEntryLabel());
1523 __ MaybeUnpoisonHeapReference(temp3);
1524 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1525 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1526 __ CompareAndBranchIfNonZero(temp3, slow_path->GetEntryLabel());
1527 }
1528
1529 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1530 // Bail out if the source is not a non primitive array.
1531 // Bail out if the destination is not a non primitive array.
1532 __ LoadFromOffset(kLoadWord, temp3, temp2, component_offset);
1533 __ CompareAndBranchIfZero(temp3, slow_path->GetEntryLabel());
1534 __ MaybeUnpoisonHeapReference(temp3);
1535 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1536 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1537 __ CompareAndBranchIfNonZero(temp3, slow_path->GetEntryLabel());
1538 }
1539
1540 __ cmp(temp1, ShifterOperand(temp2));
1541
1542 if (optimizations.GetDestinationIsTypedObjectArray()) {
1543 Label do_copy;
1544 __ b(&do_copy, EQ);
1545 if (!did_unpoison) {
1546 __ MaybeUnpoisonHeapReference(temp1);
1547 }
1548 __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
1549 __ MaybeUnpoisonHeapReference(temp1);
1550 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
1551 // No need to unpoison the result, we're comparing against null.
1552 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
1553 __ Bind(&do_copy);
1554 } else {
1555 __ b(slow_path->GetEntryLabel(), NE);
1556 }
1557 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
1558 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
1559 // Bail out if the source is not a non primitive array.
1560 __ LoadFromOffset(kLoadWord, temp1, src, class_offset);
1561 __ MaybeUnpoisonHeapReference(temp1);
1562 __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
1563 __ CompareAndBranchIfZero(temp3, slow_path->GetEntryLabel());
1564 __ MaybeUnpoisonHeapReference(temp3);
1565 __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
1566 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
1567 __ CompareAndBranchIfNonZero(temp3, slow_path->GetEntryLabel());
1568 }
1569
1570 // Compute base source address, base destination address, and end source address.
1571
1572 uint32_t element_size = sizeof(int32_t);
1573 uint32_t offset = mirror::Array::DataOffset(element_size).Uint32Value();
1574 if (src_pos.IsConstant()) {
1575 int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
1576 __ AddConstant(temp1, src, element_size * constant + offset);
1577 } else {
1578 __ add(temp1, src, ShifterOperand(src_pos.AsRegister<Register>(), LSL, 2));
1579 __ AddConstant(temp1, offset);
1580 }
1581
1582 if (dest_pos.IsConstant()) {
1583 int32_t constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
1584 __ AddConstant(temp2, dest, element_size * constant + offset);
1585 } else {
1586 __ add(temp2, dest, ShifterOperand(dest_pos.AsRegister<Register>(), LSL, 2));
1587 __ AddConstant(temp2, offset);
1588 }
1589
1590 if (length.IsConstant()) {
1591 int32_t constant = length.GetConstant()->AsIntConstant()->GetValue();
1592 __ AddConstant(temp3, temp1, element_size * constant);
1593 } else {
1594 __ add(temp3, temp1, ShifterOperand(length.AsRegister<Register>(), LSL, 2));
1595 }
1596
1597 // Iterate over the arrays and do a raw copy of the objects. We don't need to
1598 // poison/unpoison, nor do any read barrier as the next uses of the destination
1599 // array will do it.
1600 Label loop, done;
1601 __ cmp(temp1, ShifterOperand(temp3));
1602 __ b(&done, EQ);
1603 __ Bind(&loop);
1604 __ ldr(IP, Address(temp1, element_size, Address::PostIndex));
1605 __ str(IP, Address(temp2, element_size, Address::PostIndex));
1606 __ cmp(temp1, ShifterOperand(temp3));
1607 __ b(&loop, NE);
1608 __ Bind(&done);
1609
1610 // We only need one card marking on the destination array.
1611 codegen_->MarkGCCard(temp1,
1612 temp2,
1613 dest,
1614 Register(kNoRegister),
1615 false);
1616
1617 __ Bind(slow_path->GetExitLabel());
1618}
1619
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001620// Unimplemented intrinsics.
1621
1622#define UNIMPLEMENTED_INTRINSIC(Name) \
1623void IntrinsicLocationsBuilderARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
1624} \
1625void IntrinsicCodeGeneratorARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
1626}
1627
1628UNIMPLEMENTED_INTRINSIC(IntegerReverse)
1629UNIMPLEMENTED_INTRINSIC(IntegerReverseBytes)
1630UNIMPLEMENTED_INTRINSIC(LongReverse)
1631UNIMPLEMENTED_INTRINSIC(LongReverseBytes)
1632UNIMPLEMENTED_INTRINSIC(ShortReverseBytes)
1633UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble)
1634UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat)
1635UNIMPLEMENTED_INTRINSIC(MathMaxDoubleDouble)
1636UNIMPLEMENTED_INTRINSIC(MathMaxFloatFloat)
1637UNIMPLEMENTED_INTRINSIC(MathMinLongLong)
1638UNIMPLEMENTED_INTRINSIC(MathMaxLongLong)
1639UNIMPLEMENTED_INTRINSIC(MathCeil) // Could be done by changing rounding mode, maybe?
1640UNIMPLEMENTED_INTRINSIC(MathFloor) // Could be done by changing rounding mode, maybe?
1641UNIMPLEMENTED_INTRINSIC(MathRint)
1642UNIMPLEMENTED_INTRINSIC(MathRoundDouble) // Could be done by changing rounding mode, maybe?
1643UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding mode, maybe?
1644UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure.
1645UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001646UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
Jeff Hao848f70a2014-01-15 13:49:50 -08001647UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001648
Roland Levillain4d027112015-07-01 15:41:14 +01001649#undef UNIMPLEMENTED_INTRINSIC
1650
1651#undef __
1652
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -08001653} // namespace arm
1654} // namespace art