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