intrinsics_arm.cc revision d75948ac93a4a317feaf136cae78823071234ba5
1/*
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"
20#include "code_generator_arm.h"
21#include "entrypoints/quick/quick_entrypoints.h"
22#include "intrinsics.h"
23#include "mirror/array-inl.h"
24#include "mirror/art_method.h"
25#include "mirror/string.h"
26#include "thread.h"
27#include "utils/arm/assembler_arm.h"
28
29namespace art {
30
31namespace arm {
32
33ArmAssembler* IntrinsicCodeGeneratorARM::GetAssembler() {
34  return codegen_->GetAssembler();
35}
36
37ArenaAllocator* IntrinsicCodeGeneratorARM::GetAllocator() {
38  return codegen_->GetGraph()->GetArena();
39}
40
41#define __ codegen->GetAssembler()->
42
43static void MoveFromReturnRegister(Location trg, Primitive::Type type, CodeGeneratorARM* codegen) {
44  if (!trg.IsValid()) {
45    DCHECK(type == Primitive::kPrimVoid);
46    return;
47  }
48
49  DCHECK_NE(type, Primitive::kPrimVoid);
50
51  if (Primitive::IsIntegralType(type)) {
52    if (type == Primitive::kPrimLong) {
53      Register trg_reg_lo = trg.AsRegisterPairLow<Register>();
54      Register trg_reg_hi = trg.AsRegisterPairHigh<Register>();
55      Register res_reg_lo = R0;
56      Register res_reg_hi = R1;
57      if (trg_reg_lo != res_reg_hi) {
58        if (trg_reg_lo != res_reg_lo) {
59          __ mov(trg_reg_lo, ShifterOperand(res_reg_lo));
60          __ mov(trg_reg_hi, ShifterOperand(res_reg_hi));
61        } else {
62          DCHECK_EQ(trg_reg_lo + 1, trg_reg_hi);
63        }
64      } else {
65        __ mov(trg_reg_hi, ShifterOperand(res_reg_hi));
66        __ mov(trg_reg_lo, ShifterOperand(res_reg_lo));
67      }
68    } else {
69      Register trg_reg = trg.AsRegister<Register>();
70      Register res_reg = R0;
71      if (trg_reg != res_reg) {
72        __ mov(trg_reg, ShifterOperand(res_reg));
73      }
74    }
75  } else {
76    UNIMPLEMENTED(FATAL) << "Floating-point return.";
77  }
78}
79
80static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorARM* codegen) {
81  if (invoke->InputCount() == 0) {
82    return;
83  }
84
85  LocationSummary* locations = invoke->GetLocations();
86  InvokeDexCallingConventionVisitor calling_convention_visitor;
87
88  // We're moving potentially two or more locations to locations that could overlap, so we need
89  // a parallel move resolver.
90  HParallelMove parallel_move(arena);
91
92  for (size_t i = 0; i < invoke->InputCount(); i++) {
93    HInstruction* input = invoke->InputAt(i);
94    Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType());
95    Location actual_loc = locations->InAt(i);
96
97    parallel_move.AddMove(actual_loc, cc_loc, nullptr);
98  }
99
100  codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
101}
102
103// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
104// call. This will copy the arguments into the positions for a regular call.
105//
106// Note: The actual parameters are required to be in the locations given by the invoke's location
107//       summary. If an intrinsic modifies those locations before a slowpath call, they must be
108//       restored!
109class IntrinsicSlowPathARM : public SlowPathCodeARM {
110 public:
111  explicit IntrinsicSlowPathARM(HInvoke* invoke) : invoke_(invoke) { }
112
113  void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
114    CodeGeneratorARM* codegen = down_cast<CodeGeneratorARM*>(codegen_in);
115    __ Bind(GetEntryLabel());
116
117    SaveLiveRegisters(codegen, invoke_->GetLocations());
118
119    MoveArguments(invoke_, codegen->GetGraph()->GetArena(), codegen);
120
121    if (invoke_->IsInvokeStaticOrDirect()) {
122      codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), kArtMethodRegister);
123      RecordPcInfo(codegen, invoke_, invoke_->GetDexPc());
124    } else {
125      UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
126      UNREACHABLE();
127    }
128
129    // Copy the result back to the expected output.
130    Location out = invoke_->GetLocations()->Out();
131    if (out.IsValid()) {
132      DCHECK(out.IsRegister());  // TODO: Replace this when we support output in memory.
133      DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
134      MoveFromReturnRegister(out, invoke_->GetType(), codegen);
135    }
136
137    RestoreLiveRegisters(codegen, invoke_->GetLocations());
138    __ b(GetExitLabel());
139  }
140
141 private:
142  // The instruction where this slow path is happening.
143  HInvoke* const invoke_;
144
145  DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM);
146};
147
148#undef __
149
150bool IntrinsicLocationsBuilderARM::TryDispatch(HInvoke* invoke) {
151  Dispatch(invoke);
152  LocationSummary* res = invoke->GetLocations();
153  return res != nullptr && res->Intrinsified();
154}
155
156#define __ assembler->
157
158static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
159  LocationSummary* locations = new (arena) LocationSummary(invoke,
160                                                           LocationSummary::kNoCall,
161                                                           kIntrinsified);
162  locations->SetInAt(0, Location::RequiresFpuRegister());
163  locations->SetOut(Location::RequiresRegister());
164}
165
166static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
167  LocationSummary* locations = new (arena) LocationSummary(invoke,
168                                                           LocationSummary::kNoCall,
169                                                           kIntrinsified);
170  locations->SetInAt(0, Location::RequiresRegister());
171  locations->SetOut(Location::RequiresFpuRegister());
172}
173
174static void MoveFPToInt(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
175  Location input = locations->InAt(0);
176  Location output = locations->Out();
177  if (is64bit) {
178    __ vmovrrd(output.AsRegisterPairLow<Register>(),
179               output.AsRegisterPairHigh<Register>(),
180               FromLowSToD(input.AsFpuRegisterPairLow<SRegister>()));
181  } else {
182    __ vmovrs(output.AsRegister<Register>(), input.AsFpuRegister<SRegister>());
183  }
184}
185
186static void MoveIntToFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
187  Location input = locations->InAt(0);
188  Location output = locations->Out();
189  if (is64bit) {
190    __ vmovdrr(FromLowSToD(output.AsFpuRegisterPairLow<SRegister>()),
191               input.AsRegisterPairLow<Register>(),
192               input.AsRegisterPairHigh<Register>());
193  } else {
194    __ vmovsr(output.AsFpuRegister<SRegister>(), input.AsRegister<Register>());
195  }
196}
197
198void IntrinsicLocationsBuilderARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
199  CreateFPToIntLocations(arena_, invoke);
200}
201void IntrinsicLocationsBuilderARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
202  CreateIntToFPLocations(arena_, invoke);
203}
204
205void IntrinsicCodeGeneratorARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
206  MoveFPToInt(invoke->GetLocations(), true, GetAssembler());
207}
208void IntrinsicCodeGeneratorARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
209  MoveIntToFP(invoke->GetLocations(), true, GetAssembler());
210}
211
212void IntrinsicLocationsBuilderARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
213  CreateFPToIntLocations(arena_, invoke);
214}
215void IntrinsicLocationsBuilderARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
216  CreateIntToFPLocations(arena_, invoke);
217}
218
219void IntrinsicCodeGeneratorARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
220  MoveFPToInt(invoke->GetLocations(), false, GetAssembler());
221}
222void IntrinsicCodeGeneratorARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
223  MoveIntToFP(invoke->GetLocations(), false, GetAssembler());
224}
225
226static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
227  LocationSummary* locations = new (arena) LocationSummary(invoke,
228                                                           LocationSummary::kNoCall,
229                                                           kIntrinsified);
230  locations->SetInAt(0, Location::RequiresRegister());
231  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
232}
233
234static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
235  LocationSummary* locations = new (arena) LocationSummary(invoke,
236                                                           LocationSummary::kNoCall,
237                                                           kIntrinsified);
238  locations->SetInAt(0, Location::RequiresFpuRegister());
239  locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
240}
241
242static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
243  Location in = locations->InAt(0);
244  Location out = locations->Out();
245
246  if (is64bit) {
247    __ vabsd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
248             FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
249  } else {
250    __ vabss(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>());
251  }
252}
253
254void IntrinsicLocationsBuilderARM::VisitMathAbsDouble(HInvoke* invoke) {
255  CreateFPToFPLocations(arena_, invoke);
256}
257
258void IntrinsicCodeGeneratorARM::VisitMathAbsDouble(HInvoke* invoke) {
259  MathAbsFP(invoke->GetLocations(), true, GetAssembler());
260}
261
262void IntrinsicLocationsBuilderARM::VisitMathAbsFloat(HInvoke* invoke) {
263  CreateFPToFPLocations(arena_, invoke);
264}
265
266void IntrinsicCodeGeneratorARM::VisitMathAbsFloat(HInvoke* invoke) {
267  MathAbsFP(invoke->GetLocations(), false, GetAssembler());
268}
269
270static void CreateIntToIntPlusTemp(ArenaAllocator* arena, HInvoke* invoke) {
271  LocationSummary* locations = new (arena) LocationSummary(invoke,
272                                                           LocationSummary::kNoCall,
273                                                           kIntrinsified);
274  locations->SetInAt(0, Location::RequiresRegister());
275  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
276
277  locations->AddTemp(Location::RequiresRegister());
278}
279
280static void GenAbsInteger(LocationSummary* locations,
281                          bool is64bit,
282                          ArmAssembler* assembler) {
283  Location in = locations->InAt(0);
284  Location output = locations->Out();
285
286  Register mask = locations->GetTemp(0).AsRegister<Register>();
287
288  if (is64bit) {
289    Register in_reg_lo = in.AsRegisterPairLow<Register>();
290    Register in_reg_hi = in.AsRegisterPairHigh<Register>();
291    Register out_reg_lo = output.AsRegisterPairLow<Register>();
292    Register out_reg_hi = output.AsRegisterPairHigh<Register>();
293
294    DCHECK_NE(out_reg_lo, in_reg_hi) << "Diagonal overlap unexpected.";
295
296    __ Asr(mask, in_reg_hi, 31);
297    __ adds(out_reg_lo, in_reg_lo, ShifterOperand(mask));
298    __ adc(out_reg_hi, in_reg_hi, ShifterOperand(mask));
299    __ eor(out_reg_lo, mask, ShifterOperand(out_reg_lo));
300    __ eor(out_reg_hi, mask, ShifterOperand(out_reg_hi));
301  } else {
302    Register in_reg = in.AsRegister<Register>();
303    Register out_reg = output.AsRegister<Register>();
304
305    __ Asr(mask, in_reg, 31);
306    __ add(out_reg, in_reg, ShifterOperand(mask));
307    __ eor(out_reg, mask, ShifterOperand(out_reg));
308  }
309}
310
311void IntrinsicLocationsBuilderARM::VisitMathAbsInt(HInvoke* invoke) {
312  CreateIntToIntPlusTemp(arena_, invoke);
313}
314
315void IntrinsicCodeGeneratorARM::VisitMathAbsInt(HInvoke* invoke) {
316  GenAbsInteger(invoke->GetLocations(), false, GetAssembler());
317}
318
319
320void IntrinsicLocationsBuilderARM::VisitMathAbsLong(HInvoke* invoke) {
321  CreateIntToIntPlusTemp(arena_, invoke);
322}
323
324void IntrinsicCodeGeneratorARM::VisitMathAbsLong(HInvoke* invoke) {
325  GenAbsInteger(invoke->GetLocations(), true, GetAssembler());
326}
327
328static void GenMinMax(LocationSummary* locations,
329                      bool is_min,
330                      ArmAssembler* assembler) {
331  Register op1 = locations->InAt(0).AsRegister<Register>();
332  Register op2 = locations->InAt(1).AsRegister<Register>();
333  Register out = locations->Out().AsRegister<Register>();
334
335  __ cmp(op1, ShifterOperand(op2));
336
337  __ it((is_min) ? Condition::LT : Condition::GT, kItElse);
338  __ mov(out, ShifterOperand(op1), is_min ? Condition::LT : Condition::GT);
339  __ mov(out, ShifterOperand(op2), is_min ? Condition::GE : Condition::LE);
340}
341
342static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
343  LocationSummary* locations = new (arena) LocationSummary(invoke,
344                                                           LocationSummary::kNoCall,
345                                                           kIntrinsified);
346  locations->SetInAt(0, Location::RequiresRegister());
347  locations->SetInAt(1, Location::RequiresRegister());
348  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
349}
350
351void IntrinsicLocationsBuilderARM::VisitMathMinIntInt(HInvoke* invoke) {
352  CreateIntIntToIntLocations(arena_, invoke);
353}
354
355void IntrinsicCodeGeneratorARM::VisitMathMinIntInt(HInvoke* invoke) {
356  GenMinMax(invoke->GetLocations(), true, GetAssembler());
357}
358
359void IntrinsicLocationsBuilderARM::VisitMathMaxIntInt(HInvoke* invoke) {
360  CreateIntIntToIntLocations(arena_, invoke);
361}
362
363void IntrinsicCodeGeneratorARM::VisitMathMaxIntInt(HInvoke* invoke) {
364  GenMinMax(invoke->GetLocations(), false, GetAssembler());
365}
366
367void IntrinsicLocationsBuilderARM::VisitMathSqrt(HInvoke* invoke) {
368  CreateFPToFPLocations(arena_, invoke);
369}
370
371void IntrinsicCodeGeneratorARM::VisitMathSqrt(HInvoke* invoke) {
372  LocationSummary* locations = invoke->GetLocations();
373  ArmAssembler* assembler = GetAssembler();
374  __ vsqrtd(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
375            FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
376}
377
378void IntrinsicLocationsBuilderARM::VisitMemoryPeekByte(HInvoke* invoke) {
379  CreateIntToIntLocations(arena_, invoke);
380}
381
382void IntrinsicCodeGeneratorARM::VisitMemoryPeekByte(HInvoke* invoke) {
383  ArmAssembler* assembler = GetAssembler();
384  // Ignore upper 4B of long address.
385  __ ldrsb(invoke->GetLocations()->Out().AsRegister<Register>(),
386           Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
387}
388
389void IntrinsicLocationsBuilderARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
390  CreateIntToIntLocations(arena_, invoke);
391}
392
393void IntrinsicCodeGeneratorARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
394  ArmAssembler* assembler = GetAssembler();
395  // Ignore upper 4B of long address.
396  __ ldr(invoke->GetLocations()->Out().AsRegister<Register>(),
397         Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
398}
399
400void IntrinsicLocationsBuilderARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
401  CreateIntToIntLocations(arena_, invoke);
402}
403
404void IntrinsicCodeGeneratorARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
405  ArmAssembler* assembler = GetAssembler();
406  // Ignore upper 4B of long address.
407  Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
408  // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
409  // exception. So we can't use ldrd as addr may be unaligned.
410  Register lo = invoke->GetLocations()->Out().AsRegisterPairLow<Register>();
411  Register hi = invoke->GetLocations()->Out().AsRegisterPairHigh<Register>();
412  if (addr == lo) {
413    __ ldr(hi, Address(addr, 4));
414    __ ldr(lo, Address(addr, 0));
415  } else {
416    __ ldr(lo, Address(addr, 0));
417    __ ldr(hi, Address(addr, 4));
418  }
419}
420
421void IntrinsicLocationsBuilderARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
422  CreateIntToIntLocations(arena_, invoke);
423}
424
425void IntrinsicCodeGeneratorARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
426  ArmAssembler* assembler = GetAssembler();
427  // Ignore upper 4B of long address.
428  __ ldrsh(invoke->GetLocations()->Out().AsRegister<Register>(),
429           Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
430}
431
432static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
433  LocationSummary* locations = new (arena) LocationSummary(invoke,
434                                                           LocationSummary::kNoCall,
435                                                           kIntrinsified);
436  locations->SetInAt(0, Location::RequiresRegister());
437  locations->SetInAt(1, Location::RequiresRegister());
438}
439
440void IntrinsicLocationsBuilderARM::VisitMemoryPokeByte(HInvoke* invoke) {
441  CreateIntIntToVoidLocations(arena_, invoke);
442}
443
444void IntrinsicCodeGeneratorARM::VisitMemoryPokeByte(HInvoke* invoke) {
445  ArmAssembler* assembler = GetAssembler();
446  __ strb(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
447          Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
448}
449
450void IntrinsicLocationsBuilderARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
451  CreateIntIntToVoidLocations(arena_, invoke);
452}
453
454void IntrinsicCodeGeneratorARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
455  ArmAssembler* assembler = GetAssembler();
456  __ str(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
457         Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
458}
459
460void IntrinsicLocationsBuilderARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
461  CreateIntIntToVoidLocations(arena_, invoke);
462}
463
464void IntrinsicCodeGeneratorARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
465  ArmAssembler* assembler = GetAssembler();
466  // Ignore upper 4B of long address.
467  Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
468  // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
469  // exception. So we can't use ldrd as addr may be unaligned.
470  __ str(invoke->GetLocations()->InAt(1).AsRegisterPairLow<Register>(), Address(addr, 0));
471  __ str(invoke->GetLocations()->InAt(1).AsRegisterPairHigh<Register>(), Address(addr, 4));
472}
473
474void IntrinsicLocationsBuilderARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
475  CreateIntIntToVoidLocations(arena_, invoke);
476}
477
478void IntrinsicCodeGeneratorARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
479  ArmAssembler* assembler = GetAssembler();
480  __ strh(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
481          Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
482}
483
484void IntrinsicLocationsBuilderARM::VisitThreadCurrentThread(HInvoke* invoke) {
485  LocationSummary* locations = new (arena_) LocationSummary(invoke,
486                                                            LocationSummary::kNoCall,
487                                                            kIntrinsified);
488  locations->SetOut(Location::RequiresRegister());
489}
490
491void IntrinsicCodeGeneratorARM::VisitThreadCurrentThread(HInvoke* invoke) {
492  ArmAssembler* assembler = GetAssembler();
493  __ LoadFromOffset(kLoadWord,
494                    invoke->GetLocations()->Out().AsRegister<Register>(),
495                    TR,
496                    Thread::PeerOffset<kArmPointerSize>().Int32Value());
497}
498
499static void GenUnsafeGet(HInvoke* invoke,
500                         Primitive::Type type,
501                         bool is_volatile,
502                         CodeGeneratorARM* codegen) {
503  LocationSummary* locations = invoke->GetLocations();
504  DCHECK((type == Primitive::kPrimInt) ||
505         (type == Primitive::kPrimLong) ||
506         (type == Primitive::kPrimNot));
507  ArmAssembler* assembler = codegen->GetAssembler();
508  Register base = locations->InAt(1).AsRegister<Register>();           // Object pointer.
509  Register offset = locations->InAt(2).AsRegisterPairLow<Register>();  // Long offset, lo part only.
510
511  if (type == Primitive::kPrimLong) {
512    Register trg_lo = locations->Out().AsRegisterPairLow<Register>();
513    __ add(IP, base, ShifterOperand(offset));
514    if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
515      Register trg_hi = locations->Out().AsRegisterPairHigh<Register>();
516      __ ldrexd(trg_lo, trg_hi, IP);
517    } else {
518      __ ldrd(trg_lo, Address(IP));
519    }
520  } else {
521    Register trg = locations->Out().AsRegister<Register>();
522    __ ldr(trg, Address(base, offset));
523  }
524
525  if (is_volatile) {
526    __ dmb(ISH);
527  }
528}
529
530static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
531  LocationSummary* locations = new (arena) LocationSummary(invoke,
532                                                           LocationSummary::kNoCall,
533                                                           kIntrinsified);
534  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
535  locations->SetInAt(1, Location::RequiresRegister());
536  locations->SetInAt(2, Location::RequiresRegister());
537  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
538}
539
540void IntrinsicLocationsBuilderARM::VisitUnsafeGet(HInvoke* invoke) {
541  CreateIntIntIntToIntLocations(arena_, invoke);
542}
543void IntrinsicLocationsBuilderARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
544  CreateIntIntIntToIntLocations(arena_, invoke);
545}
546void IntrinsicLocationsBuilderARM::VisitUnsafeGetLong(HInvoke* invoke) {
547  CreateIntIntIntToIntLocations(arena_, invoke);
548}
549void IntrinsicLocationsBuilderARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
550  CreateIntIntIntToIntLocations(arena_, invoke);
551}
552void IntrinsicLocationsBuilderARM::VisitUnsafeGetObject(HInvoke* invoke) {
553  CreateIntIntIntToIntLocations(arena_, invoke);
554}
555void IntrinsicLocationsBuilderARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
556  CreateIntIntIntToIntLocations(arena_, invoke);
557}
558
559void IntrinsicCodeGeneratorARM::VisitUnsafeGet(HInvoke* invoke) {
560  GenUnsafeGet(invoke, Primitive::kPrimInt, false, codegen_);
561}
562void IntrinsicCodeGeneratorARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
563  GenUnsafeGet(invoke, Primitive::kPrimInt, true, codegen_);
564}
565void IntrinsicCodeGeneratorARM::VisitUnsafeGetLong(HInvoke* invoke) {
566  GenUnsafeGet(invoke, Primitive::kPrimLong, false, codegen_);
567}
568void IntrinsicCodeGeneratorARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
569  GenUnsafeGet(invoke, Primitive::kPrimLong, true, codegen_);
570}
571void IntrinsicCodeGeneratorARM::VisitUnsafeGetObject(HInvoke* invoke) {
572  GenUnsafeGet(invoke, Primitive::kPrimNot, false, codegen_);
573}
574void IntrinsicCodeGeneratorARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
575  GenUnsafeGet(invoke, Primitive::kPrimNot, true, codegen_);
576}
577
578static void CreateIntIntIntIntToVoid(ArenaAllocator* arena,
579                                     const ArmInstructionSetFeatures& features,
580                                     Primitive::Type type,
581                                     bool is_volatile,
582                                     HInvoke* invoke) {
583  LocationSummary* locations = new (arena) LocationSummary(invoke,
584                                                           LocationSummary::kNoCall,
585                                                           kIntrinsified);
586  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
587  locations->SetInAt(1, Location::RequiresRegister());
588  locations->SetInAt(2, Location::RequiresRegister());
589  locations->SetInAt(3, Location::RequiresRegister());
590
591  if (type == Primitive::kPrimLong) {
592    // Potentially need temps for ldrexd-strexd loop.
593    if (is_volatile && !features.HasAtomicLdrdAndStrd()) {
594      locations->AddTemp(Location::RequiresRegister());  // Temp_lo.
595      locations->AddTemp(Location::RequiresRegister());  // Temp_hi.
596    }
597  } else if (type == Primitive::kPrimNot) {
598    // Temps for card-marking.
599    locations->AddTemp(Location::RequiresRegister());  // Temp.
600    locations->AddTemp(Location::RequiresRegister());  // Card.
601  }
602}
603
604void IntrinsicLocationsBuilderARM::VisitUnsafePut(HInvoke* invoke) {
605  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, false, invoke);
606}
607void IntrinsicLocationsBuilderARM::VisitUnsafePutOrdered(HInvoke* invoke) {
608  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, false, invoke);
609}
610void IntrinsicLocationsBuilderARM::VisitUnsafePutVolatile(HInvoke* invoke) {
611  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, true, invoke);
612}
613void IntrinsicLocationsBuilderARM::VisitUnsafePutObject(HInvoke* invoke) {
614  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, false, invoke);
615}
616void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
617  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, false, invoke);
618}
619void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
620  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, true, invoke);
621}
622void IntrinsicLocationsBuilderARM::VisitUnsafePutLong(HInvoke* invoke) {
623  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, false, invoke);
624}
625void IntrinsicLocationsBuilderARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
626  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, false, invoke);
627}
628void IntrinsicLocationsBuilderARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
629  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, true, invoke);
630}
631
632static void GenUnsafePut(LocationSummary* locations,
633                         Primitive::Type type,
634                         bool is_volatile,
635                         bool is_ordered,
636                         CodeGeneratorARM* codegen) {
637  ArmAssembler* assembler = codegen->GetAssembler();
638
639  Register base = locations->InAt(1).AsRegister<Register>();           // Object pointer.
640  Register offset = locations->InAt(2).AsRegisterPairLow<Register>();  // Long offset, lo part only.
641  Register value;
642
643  if (is_volatile || is_ordered) {
644    __ dmb(ISH);
645  }
646
647  if (type == Primitive::kPrimLong) {
648    Register value_lo = locations->InAt(3).AsRegisterPairLow<Register>();
649    value = value_lo;
650    if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
651      Register temp_lo = locations->GetTemp(0).AsRegister<Register>();
652      Register temp_hi = locations->GetTemp(1).AsRegister<Register>();
653      Register value_hi = locations->InAt(3).AsRegisterPairHigh<Register>();
654
655      __ add(IP, base, ShifterOperand(offset));
656      Label loop_head;
657      __ Bind(&loop_head);
658      __ ldrexd(temp_lo, temp_hi, IP);
659      __ strexd(temp_lo, value_lo, value_hi, IP);
660      __ cmp(temp_lo, ShifterOperand(0));
661      __ b(&loop_head, NE);
662    } else {
663      __ add(IP, base, ShifterOperand(offset));
664      __ strd(value_lo, Address(IP));
665    }
666  } else {
667    value =  locations->InAt(3).AsRegister<Register>();
668    __ str(value, Address(base, offset));
669  }
670
671  if (is_volatile) {
672    __ dmb(ISH);
673  }
674
675  if (type == Primitive::kPrimNot) {
676    Register temp = locations->GetTemp(0).AsRegister<Register>();
677    Register card = locations->GetTemp(1).AsRegister<Register>();
678    codegen->MarkGCCard(temp, card, base, value);
679  }
680}
681
682void IntrinsicCodeGeneratorARM::VisitUnsafePut(HInvoke* invoke) {
683  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, false, codegen_);
684}
685void IntrinsicCodeGeneratorARM::VisitUnsafePutOrdered(HInvoke* invoke) {
686  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, true, codegen_);
687}
688void IntrinsicCodeGeneratorARM::VisitUnsafePutVolatile(HInvoke* invoke) {
689  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, true, false, codegen_);
690}
691void IntrinsicCodeGeneratorARM::VisitUnsafePutObject(HInvoke* invoke) {
692  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, false, codegen_);
693}
694void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
695  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, true, codegen_);
696}
697void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
698  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, true, false, codegen_);
699}
700void IntrinsicCodeGeneratorARM::VisitUnsafePutLong(HInvoke* invoke) {
701  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, false, codegen_);
702}
703void IntrinsicCodeGeneratorARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
704  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, true, codegen_);
705}
706void IntrinsicCodeGeneratorARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
707  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, false, codegen_);
708}
709
710static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena,
711                                                HInvoke* invoke) {
712  LocationSummary* locations = new (arena) LocationSummary(invoke,
713                                                           LocationSummary::kNoCall,
714                                                           kIntrinsified);
715  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
716  locations->SetInAt(1, Location::RequiresRegister());
717  locations->SetInAt(2, Location::RequiresRegister());
718  locations->SetInAt(3, Location::RequiresRegister());
719  locations->SetInAt(4, Location::RequiresRegister());
720
721  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
722
723  locations->AddTemp(Location::RequiresRegister());  // Pointer.
724  locations->AddTemp(Location::RequiresRegister());  // Temp 1.
725  locations->AddTemp(Location::RequiresRegister());  // Temp 2.
726}
727
728static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM* codegen) {
729  DCHECK_NE(type, Primitive::kPrimLong);
730
731  ArmAssembler* assembler = codegen->GetAssembler();
732
733  Register out = locations->Out().AsRegister<Register>();              // Boolean result.
734
735  Register base = locations->InAt(1).AsRegister<Register>();           // Object pointer.
736  Register offset = locations->InAt(2).AsRegisterPairLow<Register>();  // Offset (discard high 4B).
737  Register expected_lo = locations->InAt(3).AsRegister<Register>();    // Expected.
738  Register value_lo = locations->InAt(4).AsRegister<Register>();       // Value.
739
740  Register tmp_ptr = locations->GetTemp(0).AsRegister<Register>();     // Pointer to actual memory.
741  Register tmp_lo = locations->GetTemp(1).AsRegister<Register>();      // Value in memory.
742
743  if (type == Primitive::kPrimNot) {
744    // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
745    // object and scan the receiver at the next GC for nothing.
746    codegen->MarkGCCard(tmp_ptr, tmp_lo, base, value_lo);
747  }
748
749  // Prevent reordering with prior memory operations.
750  __ dmb(ISH);
751
752  __ add(tmp_ptr, base, ShifterOperand(offset));
753
754  // do {
755  //   tmp = [r_ptr] - expected;
756  // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
757  // result = tmp != 0;
758
759  Label loop_head;
760  __ Bind(&loop_head);
761
762  __ ldrex(tmp_lo, tmp_ptr);
763
764  __ subs(tmp_lo, tmp_lo, ShifterOperand(expected_lo));
765
766  __ it(EQ, ItState::kItT);
767  __ strex(tmp_lo, value_lo, tmp_ptr, EQ);
768  __ cmp(tmp_lo, ShifterOperand(1), EQ);
769
770  __ b(&loop_head, EQ);
771
772  __ dmb(ISH);
773
774  __ rsbs(out, tmp_lo, ShifterOperand(1));
775  __ it(CC);
776  __ mov(out, ShifterOperand(0), CC);
777}
778
779void IntrinsicLocationsBuilderARM::VisitUnsafeCASInt(HInvoke* invoke ATTRIBUTE_UNUSED) {
780  CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
781}
782void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke ATTRIBUTE_UNUSED) {
783  CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
784}
785void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) {
786  GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
787}
788void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) {
789  GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
790}
791
792void IntrinsicLocationsBuilderARM::VisitStringCharAt(HInvoke* invoke) {
793  LocationSummary* locations = new (arena_) LocationSummary(invoke,
794                                                            LocationSummary::kCallOnSlowPath,
795                                                            kIntrinsified);
796  locations->SetInAt(0, Location::RequiresRegister());
797  locations->SetInAt(1, Location::RequiresRegister());
798  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
799
800  locations->AddTemp(Location::RequiresRegister());
801  locations->AddTemp(Location::RequiresRegister());
802}
803
804void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) {
805  ArmAssembler* assembler = GetAssembler();
806  LocationSummary* locations = invoke->GetLocations();
807
808  // Location of reference to data array
809  const MemberOffset value_offset = mirror::String::ValueOffset();
810  // Location of count
811  const MemberOffset count_offset = mirror::String::CountOffset();
812  // Starting offset within data array
813  const MemberOffset offset_offset = mirror::String::OffsetOffset();
814  // Start of char data with array_
815  const MemberOffset data_offset = mirror::Array::DataOffset(sizeof(uint16_t));
816
817  Register obj = locations->InAt(0).AsRegister<Register>();  // String object pointer.
818  Register idx = locations->InAt(1).AsRegister<Register>();  // Index of character.
819  Register out = locations->Out().AsRegister<Register>();    // Result character.
820
821  Register temp = locations->GetTemp(0).AsRegister<Register>();
822  Register array_temp = locations->GetTemp(1).AsRegister<Register>();
823
824  // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
825  //       the cost.
826  // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
827  //       we will not optimize the code for constants (which would save a register).
828
829  SlowPathCodeARM* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
830  codegen_->AddSlowPath(slow_path);
831
832  __ ldr(temp, Address(obj, count_offset.Int32Value()));          // temp = str.length.
833  codegen_->MaybeRecordImplicitNullCheck(invoke);
834  __ cmp(idx, ShifterOperand(temp));
835  __ b(slow_path->GetEntryLabel(), CS);
836
837  // Index computation.
838  __ ldr(temp, Address(obj, offset_offset.Int32Value()));         // temp := str.offset.
839  __ ldr(array_temp, Address(obj, value_offset.Int32Value()));    // array_temp := str.offset.
840  __ add(temp, temp, ShifterOperand(idx));
841  DCHECK_EQ(data_offset.Int32Value() % 2, 0);                     // We'll compensate by shifting.
842  __ add(temp, temp, ShifterOperand(data_offset.Int32Value() / 2));
843
844  // Load the value.
845  __ ldrh(out, Address(array_temp, temp, LSL, 1));                // out := array_temp[temp].
846
847  __ Bind(slow_path->GetExitLabel());
848}
849
850void IntrinsicLocationsBuilderARM::VisitStringCompareTo(HInvoke* invoke) {
851  // The inputs plus one temp.
852  LocationSummary* locations = new (arena_) LocationSummary(invoke,
853                                                            LocationSummary::kCall,
854                                                            kIntrinsified);
855  InvokeRuntimeCallingConvention calling_convention;
856  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
857  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
858  locations->SetOut(Location::RegisterLocation(R0));
859}
860
861void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) {
862  ArmAssembler* assembler = GetAssembler();
863  LocationSummary* locations = invoke->GetLocations();
864
865  // Note that the null check must have be done earlier.
866  DCHECK(!invoke->CanDoImplicitNullCheck());
867
868  Register argument = locations->InAt(1).AsRegister<Register>();
869  __ cmp(argument, ShifterOperand(0));
870  SlowPathCodeARM* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
871  codegen_->AddSlowPath(slow_path);
872  __ b(slow_path->GetEntryLabel(), EQ);
873
874  __ LoadFromOffset(
875      kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pStringCompareTo).Int32Value());
876  __ blx(LR);
877  __ Bind(slow_path->GetExitLabel());
878}
879
880// Unimplemented intrinsics.
881
882#define UNIMPLEMENTED_INTRINSIC(Name)                                                  \
883void IntrinsicLocationsBuilderARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
884}                                                                                      \
885void IntrinsicCodeGeneratorARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) {    \
886}
887
888UNIMPLEMENTED_INTRINSIC(IntegerReverse)
889UNIMPLEMENTED_INTRINSIC(IntegerReverseBytes)
890UNIMPLEMENTED_INTRINSIC(LongReverse)
891UNIMPLEMENTED_INTRINSIC(LongReverseBytes)
892UNIMPLEMENTED_INTRINSIC(ShortReverseBytes)
893UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble)
894UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat)
895UNIMPLEMENTED_INTRINSIC(MathMaxDoubleDouble)
896UNIMPLEMENTED_INTRINSIC(MathMaxFloatFloat)
897UNIMPLEMENTED_INTRINSIC(MathMinLongLong)
898UNIMPLEMENTED_INTRINSIC(MathMaxLongLong)
899UNIMPLEMENTED_INTRINSIC(MathCeil)          // Could be done by changing rounding mode, maybe?
900UNIMPLEMENTED_INTRINSIC(MathFloor)         // Could be done by changing rounding mode, maybe?
901UNIMPLEMENTED_INTRINSIC(MathRint)
902UNIMPLEMENTED_INTRINSIC(MathRoundDouble)   // Could be done by changing rounding mode, maybe?
903UNIMPLEMENTED_INTRINSIC(MathRoundFloat)    // Could be done by changing rounding mode, maybe?
904UNIMPLEMENTED_INTRINSIC(UnsafeCASLong)     // High register pressure.
905UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
906UNIMPLEMENTED_INTRINSIC(StringIsEmpty)  // Might not want to do these two anyways, inlining should
907UNIMPLEMENTED_INTRINSIC(StringLength)   // be good enough here.
908UNIMPLEMENTED_INTRINSIC(StringIndexOf)
909UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)
910UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
911
912}  // namespace arm
913}  // namespace art
914