blob: c369020c64f35ab7a8d52b0679adc1851374b12c [file] [log] [blame]
Andreas Gampe71fb52f2014-12-29 17:43:08 -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_x86_64.h"
18
Mark Mendellfb8d2792015-03-31 22:16:59 -040019#include "arch/x86_64/instruction_set_features_x86_64.h"
Andreas Gampe71fb52f2014-12-29 17:43:08 -080020#include "code_generator_x86_64.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/x86_64/assembler_x86_64.h"
28#include "utils/x86_64/constants_x86_64.h"
29
30namespace art {
31
32namespace x86_64 {
33
Mark Mendellfb8d2792015-03-31 22:16:59 -040034IntrinsicLocationsBuilderX86_64::IntrinsicLocationsBuilderX86_64(CodeGeneratorX86_64* codegen)
35 : arena_(codegen->GetGraph()->GetArena()), codegen_(codegen) {
36}
37
38
Andreas Gampe71fb52f2014-12-29 17:43:08 -080039X86_64Assembler* IntrinsicCodeGeneratorX86_64::GetAssembler() {
40 return reinterpret_cast<X86_64Assembler*>(codegen_->GetAssembler());
41}
42
Andreas Gampe878d58c2015-01-15 23:24:00 -080043ArenaAllocator* IntrinsicCodeGeneratorX86_64::GetAllocator() {
Andreas Gampe71fb52f2014-12-29 17:43:08 -080044 return codegen_->GetGraph()->GetArena();
45}
46
47bool IntrinsicLocationsBuilderX86_64::TryDispatch(HInvoke* invoke) {
48 Dispatch(invoke);
49 const LocationSummary* res = invoke->GetLocations();
50 return res != nullptr && res->Intrinsified();
51}
52
53#define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())->
54
55// TODO: trg as memory.
56static void MoveFromReturnRegister(Location trg,
57 Primitive::Type type,
58 CodeGeneratorX86_64* codegen) {
59 if (!trg.IsValid()) {
60 DCHECK(type == Primitive::kPrimVoid);
61 return;
62 }
63
64 switch (type) {
65 case Primitive::kPrimBoolean:
66 case Primitive::kPrimByte:
67 case Primitive::kPrimChar:
68 case Primitive::kPrimShort:
69 case Primitive::kPrimInt:
70 case Primitive::kPrimNot: {
71 CpuRegister trg_reg = trg.AsRegister<CpuRegister>();
72 if (trg_reg.AsRegister() != RAX) {
73 __ movl(trg_reg, CpuRegister(RAX));
74 }
75 break;
76 }
77 case Primitive::kPrimLong: {
78 CpuRegister trg_reg = trg.AsRegister<CpuRegister>();
79 if (trg_reg.AsRegister() != RAX) {
80 __ movq(trg_reg, CpuRegister(RAX));
81 }
82 break;
83 }
84
85 case Primitive::kPrimVoid:
86 LOG(FATAL) << "Unexpected void type for valid location " << trg;
87 UNREACHABLE();
88
89 case Primitive::kPrimDouble: {
90 XmmRegister trg_reg = trg.AsFpuRegister<XmmRegister>();
91 if (trg_reg.AsFloatRegister() != XMM0) {
92 __ movsd(trg_reg, XmmRegister(XMM0));
93 }
94 break;
95 }
96 case Primitive::kPrimFloat: {
97 XmmRegister trg_reg = trg.AsFpuRegister<XmmRegister>();
98 if (trg_reg.AsFloatRegister() != XMM0) {
99 __ movss(trg_reg, XmmRegister(XMM0));
100 }
101 break;
102 }
103 }
104}
105
106static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX86_64* codegen) {
107 if (invoke->InputCount() == 0) {
108 return;
109 }
110
111 LocationSummary* locations = invoke->GetLocations();
112 InvokeDexCallingConventionVisitor calling_convention_visitor;
113
114 // We're moving potentially two or more locations to locations that could overlap, so we need
115 // a parallel move resolver.
116 HParallelMove parallel_move(arena);
117
118 for (size_t i = 0; i < invoke->InputCount(); i++) {
119 HInstruction* input = invoke->InputAt(i);
120 Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType());
121 Location actual_loc = locations->InAt(i);
122
Nicolas Geoffray90218252015-04-15 11:56:51 +0100123 parallel_move.AddMove(actual_loc, cc_loc, input->GetType(), nullptr);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800124 }
125
126 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
127}
128
129// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
130// call. This will copy the arguments into the positions for a regular call.
131//
132// Note: The actual parameters are required to be in the locations given by the invoke's location
133// summary. If an intrinsic modifies those locations before a slowpath call, they must be
134// restored!
135class IntrinsicSlowPathX86_64 : public SlowPathCodeX86_64 {
136 public:
137 explicit IntrinsicSlowPathX86_64(HInvoke* invoke) : invoke_(invoke) { }
138
139 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
140 CodeGeneratorX86_64* codegen = down_cast<CodeGeneratorX86_64*>(codegen_in);
141 __ Bind(GetEntryLabel());
142
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000143 SaveLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800144
145 MoveArguments(invoke_, codegen->GetGraph()->GetArena(), codegen);
146
147 if (invoke_->IsInvokeStaticOrDirect()) {
148 codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), CpuRegister(RDI));
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000149 RecordPcInfo(codegen, invoke_, invoke_->GetDexPc());
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800150 } else {
151 UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
152 UNREACHABLE();
153 }
154
155 // Copy the result back to the expected output.
156 Location out = invoke_->GetLocations()->Out();
157 if (out.IsValid()) {
158 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
159 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
160 MoveFromReturnRegister(out, invoke_->GetType(), codegen);
161 }
162
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000163 RestoreLiveRegisters(codegen, invoke_->GetLocations());
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800164 __ jmp(GetExitLabel());
165 }
166
167 private:
168 // The instruction where this slow path is happening.
169 HInvoke* const invoke_;
170
171 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathX86_64);
172};
173
174#undef __
175#define __ assembler->
176
177static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
178 LocationSummary* locations = new (arena) LocationSummary(invoke,
179 LocationSummary::kNoCall,
180 kIntrinsified);
181 locations->SetInAt(0, Location::RequiresFpuRegister());
182 locations->SetOut(Location::RequiresRegister());
183}
184
185static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
186 LocationSummary* locations = new (arena) LocationSummary(invoke,
187 LocationSummary::kNoCall,
188 kIntrinsified);
189 locations->SetInAt(0, Location::RequiresRegister());
190 locations->SetOut(Location::RequiresFpuRegister());
191}
192
193static void MoveFPToInt(LocationSummary* locations, bool is64bit, X86_64Assembler* assembler) {
194 Location input = locations->InAt(0);
195 Location output = locations->Out();
196 __ movd(output.AsRegister<CpuRegister>(), input.AsFpuRegister<XmmRegister>(), is64bit);
197}
198
199static void MoveIntToFP(LocationSummary* locations, bool is64bit, X86_64Assembler* assembler) {
200 Location input = locations->InAt(0);
201 Location output = locations->Out();
202 __ movd(output.AsFpuRegister<XmmRegister>(), input.AsRegister<CpuRegister>(), is64bit);
203}
204
205void IntrinsicLocationsBuilderX86_64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
206 CreateFPToIntLocations(arena_, invoke);
207}
208void IntrinsicLocationsBuilderX86_64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
209 CreateIntToFPLocations(arena_, invoke);
210}
211
212void IntrinsicCodeGeneratorX86_64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
213 MoveFPToInt(invoke->GetLocations(), true, GetAssembler());
214}
215void IntrinsicCodeGeneratorX86_64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
216 MoveIntToFP(invoke->GetLocations(), true, GetAssembler());
217}
218
219void IntrinsicLocationsBuilderX86_64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
220 CreateFPToIntLocations(arena_, invoke);
221}
222void IntrinsicLocationsBuilderX86_64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
223 CreateIntToFPLocations(arena_, invoke);
224}
225
226void IntrinsicCodeGeneratorX86_64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
227 MoveFPToInt(invoke->GetLocations(), false, GetAssembler());
228}
229void IntrinsicCodeGeneratorX86_64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
230 MoveIntToFP(invoke->GetLocations(), false, GetAssembler());
231}
232
233static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
234 LocationSummary* locations = new (arena) LocationSummary(invoke,
235 LocationSummary::kNoCall,
236 kIntrinsified);
237 locations->SetInAt(0, Location::RequiresRegister());
238 locations->SetOut(Location::SameAsFirstInput());
239}
240
241static void GenReverseBytes(LocationSummary* locations,
242 Primitive::Type size,
243 X86_64Assembler* assembler) {
244 CpuRegister out = locations->Out().AsRegister<CpuRegister>();
245
246 switch (size) {
247 case Primitive::kPrimShort:
248 // TODO: Can be done with an xchg of 8b registers. This is straight from Quick.
249 __ bswapl(out);
250 __ sarl(out, Immediate(16));
251 break;
252 case Primitive::kPrimInt:
253 __ bswapl(out);
254 break;
255 case Primitive::kPrimLong:
256 __ bswapq(out);
257 break;
258 default:
259 LOG(FATAL) << "Unexpected size for reverse-bytes: " << size;
260 UNREACHABLE();
261 }
262}
263
264void IntrinsicLocationsBuilderX86_64::VisitIntegerReverseBytes(HInvoke* invoke) {
265 CreateIntToIntLocations(arena_, invoke);
266}
267
268void IntrinsicCodeGeneratorX86_64::VisitIntegerReverseBytes(HInvoke* invoke) {
269 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
270}
271
272void IntrinsicLocationsBuilderX86_64::VisitLongReverseBytes(HInvoke* invoke) {
273 CreateIntToIntLocations(arena_, invoke);
274}
275
276void IntrinsicCodeGeneratorX86_64::VisitLongReverseBytes(HInvoke* invoke) {
277 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
278}
279
280void IntrinsicLocationsBuilderX86_64::VisitShortReverseBytes(HInvoke* invoke) {
281 CreateIntToIntLocations(arena_, invoke);
282}
283
284void IntrinsicCodeGeneratorX86_64::VisitShortReverseBytes(HInvoke* invoke) {
285 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler());
286}
287
288
289// TODO: Consider Quick's way of doing Double abs through integer operations, as the immediate we
290// need is 64b.
291
292static void CreateFloatToFloatPlusTemps(ArenaAllocator* arena, HInvoke* invoke) {
293 // TODO: Enable memory operations when the assembler supports them.
294 LocationSummary* locations = new (arena) LocationSummary(invoke,
295 LocationSummary::kNoCall,
296 kIntrinsified);
297 locations->SetInAt(0, Location::RequiresFpuRegister());
298 // TODO: Allow x86 to work with memory. This requires assembler support, see below.
299 // locations->SetInAt(0, Location::Any()); // X86 can work on memory directly.
300 locations->SetOut(Location::SameAsFirstInput());
Mark Mendellf55c3e02015-03-26 21:07:46 -0400301 locations->AddTemp(Location::RequiresFpuRegister()); // FP reg to hold mask.
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800302}
303
Mark Mendell39dcf552015-04-09 20:42:42 -0400304static void MathAbsFP(LocationSummary* locations,
305 bool is64bit,
306 X86_64Assembler* assembler,
307 CodeGeneratorX86_64* codegen) {
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800308 Location output = locations->Out();
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800309
310 if (output.IsFpuRegister()) {
311 // In-register
Mark Mendellf55c3e02015-03-26 21:07:46 -0400312 XmmRegister xmm_temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800313
Mark Mendell39dcf552015-04-09 20:42:42 -0400314 // TODO: Can mask directly with constant area using pand if we can guarantee
315 // that the literal is aligned on a 16 byte boundary. This will avoid a
316 // temporary.
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800317 if (is64bit) {
Mark Mendellf55c3e02015-03-26 21:07:46 -0400318 __ movsd(xmm_temp, codegen->LiteralInt64Address(INT64_C(0x7FFFFFFFFFFFFFFF)));
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800319 __ andpd(output.AsFpuRegister<XmmRegister>(), xmm_temp);
320 } else {
Mark Mendellf55c3e02015-03-26 21:07:46 -0400321 __ movss(xmm_temp, codegen->LiteralInt32Address(INT32_C(0x7FFFFFFF)));
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800322 __ andps(output.AsFpuRegister<XmmRegister>(), xmm_temp);
323 }
324 } else {
325 // TODO: update when assember support is available.
326 UNIMPLEMENTED(FATAL) << "Needs assembler support.";
327// Once assembler support is available, in-memory operations look like this:
328// if (is64bit) {
329// DCHECK(output.IsDoubleStackSlot());
330// // No 64b and with literal.
331// __ movq(cpu_temp, Immediate(INT64_C(0x7FFFFFFFFFFFFFFF)));
332// __ andq(Address(CpuRegister(RSP), output.GetStackIndex()), cpu_temp);
333// } else {
334// DCHECK(output.IsStackSlot());
335// // Can use and with a literal directly.
336// __ andl(Address(CpuRegister(RSP), output.GetStackIndex()), Immediate(INT64_C(0x7FFFFFFF)));
337// }
338 }
339}
340
341void IntrinsicLocationsBuilderX86_64::VisitMathAbsDouble(HInvoke* invoke) {
342 CreateFloatToFloatPlusTemps(arena_, invoke);
343}
344
345void IntrinsicCodeGeneratorX86_64::VisitMathAbsDouble(HInvoke* invoke) {
Mark Mendellf55c3e02015-03-26 21:07:46 -0400346 MathAbsFP(invoke->GetLocations(), true, GetAssembler(), codegen_);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800347}
348
349void IntrinsicLocationsBuilderX86_64::VisitMathAbsFloat(HInvoke* invoke) {
350 CreateFloatToFloatPlusTemps(arena_, invoke);
351}
352
353void IntrinsicCodeGeneratorX86_64::VisitMathAbsFloat(HInvoke* invoke) {
Mark Mendellf55c3e02015-03-26 21:07:46 -0400354 MathAbsFP(invoke->GetLocations(), false, GetAssembler(), codegen_);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800355}
356
357static void CreateIntToIntPlusTemp(ArenaAllocator* arena, HInvoke* invoke) {
358 LocationSummary* locations = new (arena) LocationSummary(invoke,
359 LocationSummary::kNoCall,
360 kIntrinsified);
361 locations->SetInAt(0, Location::RequiresRegister());
362 locations->SetOut(Location::SameAsFirstInput());
363 locations->AddTemp(Location::RequiresRegister());
364}
365
366static void GenAbsInteger(LocationSummary* locations, bool is64bit, X86_64Assembler* assembler) {
367 Location output = locations->Out();
368 CpuRegister out = output.AsRegister<CpuRegister>();
369 CpuRegister mask = locations->GetTemp(0).AsRegister<CpuRegister>();
370
371 if (is64bit) {
372 // Create mask.
373 __ movq(mask, out);
374 __ sarq(mask, Immediate(63));
375 // Add mask.
376 __ addq(out, mask);
377 __ xorq(out, mask);
378 } else {
379 // Create mask.
380 __ movl(mask, out);
381 __ sarl(mask, Immediate(31));
382 // Add mask.
383 __ addl(out, mask);
384 __ xorl(out, mask);
385 }
386}
387
388void IntrinsicLocationsBuilderX86_64::VisitMathAbsInt(HInvoke* invoke) {
389 CreateIntToIntPlusTemp(arena_, invoke);
390}
391
392void IntrinsicCodeGeneratorX86_64::VisitMathAbsInt(HInvoke* invoke) {
393 GenAbsInteger(invoke->GetLocations(), false, GetAssembler());
394}
395
396void IntrinsicLocationsBuilderX86_64::VisitMathAbsLong(HInvoke* invoke) {
397 CreateIntToIntPlusTemp(arena_, invoke);
398}
399
400void IntrinsicCodeGeneratorX86_64::VisitMathAbsLong(HInvoke* invoke) {
401 GenAbsInteger(invoke->GetLocations(), true, GetAssembler());
402}
403
Mark Mendell39dcf552015-04-09 20:42:42 -0400404static void GenMinMaxFP(LocationSummary* locations,
405 bool is_min,
406 bool is_double,
407 X86_64Assembler* assembler,
408 CodeGeneratorX86_64* codegen) {
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800409 Location op1_loc = locations->InAt(0);
410 Location op2_loc = locations->InAt(1);
411 Location out_loc = locations->Out();
412 XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
413
414 // Shortcut for same input locations.
415 if (op1_loc.Equals(op2_loc)) {
416 DCHECK(out_loc.Equals(op1_loc));
417 return;
418 }
419
420 // (out := op1)
421 // out <=? op2
422 // if Nan jmp Nan_label
423 // if out is min jmp done
424 // if op2 is min jmp op2_label
425 // handle -0/+0
426 // jmp done
427 // Nan_label:
428 // out := NaN
429 // op2_label:
430 // out := op2
431 // done:
432 //
433 // This removes one jmp, but needs to copy one input (op1) to out.
434 //
Mark Mendellf55c3e02015-03-26 21:07:46 -0400435 // TODO: This is straight from Quick. Make NaN an out-of-line slowpath?
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800436
437 XmmRegister op2 = op2_loc.AsFpuRegister<XmmRegister>();
438
439 Label nan, done, op2_label;
440 if (is_double) {
441 __ ucomisd(out, op2);
442 } else {
443 __ ucomiss(out, op2);
444 }
445
446 __ j(Condition::kParityEven, &nan);
447
448 __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label);
449 __ j(is_min ? Condition::kBelow : Condition::kAbove, &done);
450
451 // Handle 0.0/-0.0.
452 if (is_min) {
453 if (is_double) {
454 __ orpd(out, op2);
455 } else {
456 __ orps(out, op2);
457 }
458 } else {
459 if (is_double) {
460 __ andpd(out, op2);
461 } else {
462 __ andps(out, op2);
463 }
464 }
465 __ jmp(&done);
466
467 // NaN handling.
468 __ Bind(&nan);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800469 if (is_double) {
Mark Mendellf55c3e02015-03-26 21:07:46 -0400470 __ movsd(out, codegen->LiteralInt64Address(INT64_C(0x7FF8000000000000)));
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800471 } else {
Mark Mendellf55c3e02015-03-26 21:07:46 -0400472 __ movss(out, codegen->LiteralInt32Address(INT32_C(0x7FC00000)));
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800473 }
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800474 __ jmp(&done);
475
476 // out := op2;
477 __ Bind(&op2_label);
478 if (is_double) {
479 __ movsd(out, op2);
480 } else {
481 __ movss(out, op2);
482 }
483
484 // Done.
485 __ Bind(&done);
486}
487
Mark Mendellf55c3e02015-03-26 21:07:46 -0400488static void CreateFPFPToFP(ArenaAllocator* arena, HInvoke* invoke) {
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800489 LocationSummary* locations = new (arena) LocationSummary(invoke,
490 LocationSummary::kNoCall,
491 kIntrinsified);
492 locations->SetInAt(0, Location::RequiresFpuRegister());
493 locations->SetInAt(1, Location::RequiresFpuRegister());
494 // The following is sub-optimal, but all we can do for now. It would be fine to also accept
495 // the second input to be the output (we can simply swap inputs).
496 locations->SetOut(Location::SameAsFirstInput());
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800497}
498
499void IntrinsicLocationsBuilderX86_64::VisitMathMinDoubleDouble(HInvoke* invoke) {
Mark Mendellf55c3e02015-03-26 21:07:46 -0400500 CreateFPFPToFP(arena_, invoke);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800501}
502
503void IntrinsicCodeGeneratorX86_64::VisitMathMinDoubleDouble(HInvoke* invoke) {
Mark Mendellf55c3e02015-03-26 21:07:46 -0400504 GenMinMaxFP(invoke->GetLocations(), true, true, GetAssembler(), codegen_);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800505}
506
507void IntrinsicLocationsBuilderX86_64::VisitMathMinFloatFloat(HInvoke* invoke) {
Mark Mendellf55c3e02015-03-26 21:07:46 -0400508 CreateFPFPToFP(arena_, invoke);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800509}
510
511void IntrinsicCodeGeneratorX86_64::VisitMathMinFloatFloat(HInvoke* invoke) {
Mark Mendellf55c3e02015-03-26 21:07:46 -0400512 GenMinMaxFP(invoke->GetLocations(), true, false, GetAssembler(), codegen_);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800513}
514
515void IntrinsicLocationsBuilderX86_64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
Mark Mendellf55c3e02015-03-26 21:07:46 -0400516 CreateFPFPToFP(arena_, invoke);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800517}
518
519void IntrinsicCodeGeneratorX86_64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
Mark Mendellf55c3e02015-03-26 21:07:46 -0400520 GenMinMaxFP(invoke->GetLocations(), false, true, GetAssembler(), codegen_);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800521}
522
523void IntrinsicLocationsBuilderX86_64::VisitMathMaxFloatFloat(HInvoke* invoke) {
Mark Mendellf55c3e02015-03-26 21:07:46 -0400524 CreateFPFPToFP(arena_, invoke);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800525}
526
527void IntrinsicCodeGeneratorX86_64::VisitMathMaxFloatFloat(HInvoke* invoke) {
Mark Mendellf55c3e02015-03-26 21:07:46 -0400528 GenMinMaxFP(invoke->GetLocations(), false, false, GetAssembler(), codegen_);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800529}
530
531static void GenMinMax(LocationSummary* locations, bool is_min, bool is_long,
532 X86_64Assembler* assembler) {
533 Location op1_loc = locations->InAt(0);
534 Location op2_loc = locations->InAt(1);
535
536 // Shortcut for same input locations.
537 if (op1_loc.Equals(op2_loc)) {
538 // Can return immediately, as op1_loc == out_loc.
539 // Note: if we ever support separate registers, e.g., output into memory, we need to check for
540 // a copy here.
541 DCHECK(locations->Out().Equals(op1_loc));
542 return;
543 }
544
545 CpuRegister out = locations->Out().AsRegister<CpuRegister>();
546 CpuRegister op2 = op2_loc.AsRegister<CpuRegister>();
547
548 // (out := op1)
549 // out <=? op2
550 // if out is min jmp done
551 // out := op2
552 // done:
553
554 if (is_long) {
555 __ cmpq(out, op2);
556 } else {
557 __ cmpl(out, op2);
558 }
559
560 __ cmov(is_min ? Condition::kGreater : Condition::kLess, out, op2, is_long);
561}
562
563static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
564 LocationSummary* locations = new (arena) LocationSummary(invoke,
565 LocationSummary::kNoCall,
566 kIntrinsified);
567 locations->SetInAt(0, Location::RequiresRegister());
568 locations->SetInAt(1, Location::RequiresRegister());
569 locations->SetOut(Location::SameAsFirstInput());
570}
571
572void IntrinsicLocationsBuilderX86_64::VisitMathMinIntInt(HInvoke* invoke) {
573 CreateIntIntToIntLocations(arena_, invoke);
574}
575
576void IntrinsicCodeGeneratorX86_64::VisitMathMinIntInt(HInvoke* invoke) {
577 GenMinMax(invoke->GetLocations(), true, false, GetAssembler());
578}
579
580void IntrinsicLocationsBuilderX86_64::VisitMathMinLongLong(HInvoke* invoke) {
581 CreateIntIntToIntLocations(arena_, invoke);
582}
583
584void IntrinsicCodeGeneratorX86_64::VisitMathMinLongLong(HInvoke* invoke) {
585 GenMinMax(invoke->GetLocations(), true, true, GetAssembler());
586}
587
588void IntrinsicLocationsBuilderX86_64::VisitMathMaxIntInt(HInvoke* invoke) {
589 CreateIntIntToIntLocations(arena_, invoke);
590}
591
592void IntrinsicCodeGeneratorX86_64::VisitMathMaxIntInt(HInvoke* invoke) {
593 GenMinMax(invoke->GetLocations(), false, false, GetAssembler());
594}
595
596void IntrinsicLocationsBuilderX86_64::VisitMathMaxLongLong(HInvoke* invoke) {
597 CreateIntIntToIntLocations(arena_, invoke);
598}
599
600void IntrinsicCodeGeneratorX86_64::VisitMathMaxLongLong(HInvoke* invoke) {
601 GenMinMax(invoke->GetLocations(), false, true, GetAssembler());
602}
603
604static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
605 LocationSummary* locations = new (arena) LocationSummary(invoke,
606 LocationSummary::kNoCall,
607 kIntrinsified);
608 locations->SetInAt(0, Location::RequiresFpuRegister());
609 locations->SetOut(Location::RequiresFpuRegister());
610}
611
612void IntrinsicLocationsBuilderX86_64::VisitMathSqrt(HInvoke* invoke) {
613 CreateFPToFPLocations(arena_, invoke);
614}
615
616void IntrinsicCodeGeneratorX86_64::VisitMathSqrt(HInvoke* invoke) {
617 LocationSummary* locations = invoke->GetLocations();
618 XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>();
619 XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
620
621 GetAssembler()->sqrtsd(out, in);
622}
623
Mark Mendellfb8d2792015-03-31 22:16:59 -0400624static void InvokeOutOfLineIntrinsic(CodeGeneratorX86_64* codegen, HInvoke* invoke) {
625 MoveArguments(invoke, codegen->GetGraph()->GetArena(), codegen);
626
627 DCHECK(invoke->IsInvokeStaticOrDirect());
628 codegen->GenerateStaticOrDirectCall(invoke->AsInvokeStaticOrDirect(), CpuRegister(RDI));
629 codegen->RecordPcInfo(invoke, invoke->GetDexPc());
630
631 // Copy the result back to the expected output.
632 Location out = invoke->GetLocations()->Out();
633 if (out.IsValid()) {
634 DCHECK(out.IsRegister());
635 MoveFromReturnRegister(out, invoke->GetType(), codegen);
636 }
637}
638
639static void CreateSSE41FPToFPLocations(ArenaAllocator* arena,
640 HInvoke* invoke,
641 CodeGeneratorX86_64* codegen) {
642 // Do we have instruction support?
643 if (codegen->GetInstructionSetFeatures().HasSSE4_1()) {
644 CreateFPToFPLocations(arena, invoke);
645 return;
646 }
647
648 // We have to fall back to a call to the intrinsic.
649 LocationSummary* locations = new (arena) LocationSummary(invoke,
650 LocationSummary::kCall);
651 InvokeRuntimeCallingConvention calling_convention;
652 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetFpuRegisterAt(0)));
653 locations->SetOut(Location::FpuRegisterLocation(XMM0));
654 // Needs to be RDI for the invoke.
655 locations->AddTemp(Location::RegisterLocation(RDI));
656}
657
658static void GenSSE41FPToFPIntrinsic(CodeGeneratorX86_64* codegen,
659 HInvoke* invoke,
660 X86_64Assembler* assembler,
661 int round_mode) {
662 LocationSummary* locations = invoke->GetLocations();
663 if (locations->WillCall()) {
664 InvokeOutOfLineIntrinsic(codegen, invoke);
665 } else {
666 XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>();
667 XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
668 __ roundsd(out, in, Immediate(round_mode));
669 }
670}
671
672void IntrinsicLocationsBuilderX86_64::VisitMathCeil(HInvoke* invoke) {
673 CreateSSE41FPToFPLocations(arena_, invoke, codegen_);
674}
675
676void IntrinsicCodeGeneratorX86_64::VisitMathCeil(HInvoke* invoke) {
677 GenSSE41FPToFPIntrinsic(codegen_, invoke, GetAssembler(), 2);
678}
679
680void IntrinsicLocationsBuilderX86_64::VisitMathFloor(HInvoke* invoke) {
681 CreateSSE41FPToFPLocations(arena_, invoke, codegen_);
682}
683
684void IntrinsicCodeGeneratorX86_64::VisitMathFloor(HInvoke* invoke) {
685 GenSSE41FPToFPIntrinsic(codegen_, invoke, GetAssembler(), 1);
686}
687
688void IntrinsicLocationsBuilderX86_64::VisitMathRint(HInvoke* invoke) {
689 CreateSSE41FPToFPLocations(arena_, invoke, codegen_);
690}
691
692void IntrinsicCodeGeneratorX86_64::VisitMathRint(HInvoke* invoke) {
693 GenSSE41FPToFPIntrinsic(codegen_, invoke, GetAssembler(), 0);
694}
695
696static void CreateSSE41FPToIntLocations(ArenaAllocator* arena,
697 HInvoke* invoke,
698 CodeGeneratorX86_64* codegen) {
699 // Do we have instruction support?
700 if (codegen->GetInstructionSetFeatures().HasSSE4_1()) {
701 LocationSummary* locations = new (arena) LocationSummary(invoke,
702 LocationSummary::kNoCall,
703 kIntrinsified);
704 locations->SetInAt(0, Location::RequiresFpuRegister());
705 locations->SetOut(Location::RequiresFpuRegister());
706 locations->AddTemp(Location::RequiresFpuRegister());
Mark Mendellfb8d2792015-03-31 22:16:59 -0400707 return;
708 }
709
710 // We have to fall back to a call to the intrinsic.
711 LocationSummary* locations = new (arena) LocationSummary(invoke,
712 LocationSummary::kCall);
713 InvokeRuntimeCallingConvention calling_convention;
714 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetFpuRegisterAt(0)));
715 locations->SetOut(Location::RegisterLocation(RAX));
716 // Needs to be RDI for the invoke.
717 locations->AddTemp(Location::RegisterLocation(RDI));
718}
719
720void IntrinsicLocationsBuilderX86_64::VisitMathRoundFloat(HInvoke* invoke) {
721 CreateSSE41FPToIntLocations(arena_, invoke, codegen_);
722}
723
724void IntrinsicCodeGeneratorX86_64::VisitMathRoundFloat(HInvoke* invoke) {
725 LocationSummary* locations = invoke->GetLocations();
726 if (locations->WillCall()) {
727 InvokeOutOfLineIntrinsic(codegen_, invoke);
728 return;
729 }
730
731 // Implement RoundFloat as t1 = floor(input + 0.5f); convert to int.
732 XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>();
733 CpuRegister out = locations->Out().AsRegister<CpuRegister>();
Mark Mendell40741f32015-04-20 22:10:34 -0400734 XmmRegister inPlusPointFive = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
Mark Mendellfb8d2792015-03-31 22:16:59 -0400735 Label done, nan;
736 X86_64Assembler* assembler = GetAssembler();
737
Mark Mendell40741f32015-04-20 22:10:34 -0400738 // Load 0.5 into inPlusPointFive.
739 __ movss(inPlusPointFive, codegen_->LiteralFloatAddress(0.5f));
Mark Mendellfb8d2792015-03-31 22:16:59 -0400740
741 // Add in the input.
742 __ addss(inPlusPointFive, in);
743
744 // And truncate to an integer.
745 __ roundss(inPlusPointFive, inPlusPointFive, Immediate(1));
746
Mark Mendellfb8d2792015-03-31 22:16:59 -0400747 // if inPlusPointFive >= maxInt goto done
Mark Mendell40741f32015-04-20 22:10:34 -0400748 __ comiss(inPlusPointFive, codegen_->LiteralFloatAddress(static_cast<float>(kPrimIntMax)));
Mark Mendellfb8d2792015-03-31 22:16:59 -0400749 __ j(kAboveEqual, &done);
750
751 // if input == NaN goto nan
752 __ j(kUnordered, &nan);
753
754 // output = float-to-int-truncate(input)
755 __ cvttss2si(out, inPlusPointFive);
756 __ jmp(&done);
757 __ Bind(&nan);
758
759 // output = 0
760 __ xorl(out, out);
761 __ Bind(&done);
762}
763
764void IntrinsicLocationsBuilderX86_64::VisitMathRoundDouble(HInvoke* invoke) {
765 CreateSSE41FPToIntLocations(arena_, invoke, codegen_);
766}
767
768void IntrinsicCodeGeneratorX86_64::VisitMathRoundDouble(HInvoke* invoke) {
769 LocationSummary* locations = invoke->GetLocations();
770 if (locations->WillCall()) {
771 InvokeOutOfLineIntrinsic(codegen_, invoke);
772 return;
773 }
774
775 // Implement RoundDouble as t1 = floor(input + 0.5); convert to long.
776 XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>();
777 CpuRegister out = locations->Out().AsRegister<CpuRegister>();
Mark Mendell40741f32015-04-20 22:10:34 -0400778 XmmRegister inPlusPointFive = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
Mark Mendellfb8d2792015-03-31 22:16:59 -0400779 Label done, nan;
780 X86_64Assembler* assembler = GetAssembler();
781
Mark Mendell40741f32015-04-20 22:10:34 -0400782 // Load 0.5 into inPlusPointFive.
783 __ movsd(inPlusPointFive, codegen_->LiteralDoubleAddress(0.5));
Mark Mendellfb8d2792015-03-31 22:16:59 -0400784
785 // Add in the input.
786 __ addsd(inPlusPointFive, in);
787
788 // And truncate to an integer.
789 __ roundsd(inPlusPointFive, inPlusPointFive, Immediate(1));
790
Mark Mendellfb8d2792015-03-31 22:16:59 -0400791 // if inPlusPointFive >= maxLong goto done
Mark Mendell40741f32015-04-20 22:10:34 -0400792 __ comisd(inPlusPointFive, codegen_->LiteralDoubleAddress(static_cast<double>(kPrimLongMax)));
Mark Mendellfb8d2792015-03-31 22:16:59 -0400793 __ j(kAboveEqual, &done);
794
795 // if input == NaN goto nan
796 __ j(kUnordered, &nan);
797
798 // output = double-to-long-truncate(input)
799 __ cvttsd2si(out, inPlusPointFive, true);
800 __ jmp(&done);
801 __ Bind(&nan);
802
803 // output = 0
804 __ xorq(out, out);
805 __ Bind(&done);
806}
807
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800808void IntrinsicLocationsBuilderX86_64::VisitStringCharAt(HInvoke* invoke) {
809 // The inputs plus one temp.
810 LocationSummary* locations = new (arena_) LocationSummary(invoke,
811 LocationSummary::kCallOnSlowPath,
812 kIntrinsified);
813 locations->SetInAt(0, Location::RequiresRegister());
814 locations->SetInAt(1, Location::RequiresRegister());
815 locations->SetOut(Location::SameAsFirstInput());
816 locations->AddTemp(Location::RequiresRegister());
817}
818
819void IntrinsicCodeGeneratorX86_64::VisitStringCharAt(HInvoke* invoke) {
820 LocationSummary* locations = invoke->GetLocations();
821
822 // Location of reference to data array
823 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
824 // Location of count
825 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
826 // Starting offset within data array
827 const int32_t offset_offset = mirror::String::OffsetOffset().Int32Value();
828 // Start of char data with array_
829 const int32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value();
830
831 CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>();
832 CpuRegister idx = locations->InAt(1).AsRegister<CpuRegister>();
833 CpuRegister out = locations->Out().AsRegister<CpuRegister>();
834 Location temp_loc = locations->GetTemp(0);
835 CpuRegister temp = temp_loc.AsRegister<CpuRegister>();
836
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800837 // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
838 // the cost.
839 // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
840 // we will not optimize the code for constants (which would save a register).
841
Andreas Gampe878d58c2015-01-15 23:24:00 -0800842 SlowPathCodeX86_64* slow_path = new (GetAllocator()) IntrinsicSlowPathX86_64(invoke);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800843 codegen_->AddSlowPath(slow_path);
844
845 X86_64Assembler* assembler = GetAssembler();
846
847 __ cmpl(idx, Address(obj, count_offset));
Andreas Gampe878d58c2015-01-15 23:24:00 -0800848 codegen_->MaybeRecordImplicitNullCheck(invoke);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800849 __ j(kAboveEqual, slow_path->GetEntryLabel());
850
851 // Get the actual element.
852 __ movl(temp, idx); // temp := idx.
853 __ addl(temp, Address(obj, offset_offset)); // temp := offset + idx.
854 __ movl(out, Address(obj, value_offset)); // obj := obj.array.
855 // out = out[2*temp].
856 __ movzxw(out, Address(out, temp, ScaleFactor::TIMES_2, data_offset));
857
858 __ Bind(slow_path->GetExitLabel());
859}
860
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +0000861void IntrinsicLocationsBuilderX86_64::VisitStringCompareTo(HInvoke* invoke) {
862 LocationSummary* locations = new (arena_) LocationSummary(invoke,
863 LocationSummary::kCall,
864 kIntrinsified);
865 InvokeRuntimeCallingConvention calling_convention;
866 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
867 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
868 locations->SetOut(Location::RegisterLocation(RAX));
869}
870
871void IntrinsicCodeGeneratorX86_64::VisitStringCompareTo(HInvoke* invoke) {
872 X86_64Assembler* assembler = GetAssembler();
873 LocationSummary* locations = invoke->GetLocations();
874
Nicolas Geoffray512e04d2015-03-27 17:21:24 +0000875 // Note that the null check must have been done earlier.
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +0000876 DCHECK(!invoke->CanDoImplicitNullCheck());
877
878 CpuRegister argument = locations->InAt(1).AsRegister<CpuRegister>();
879 __ testl(argument, argument);
880 SlowPathCodeX86_64* slow_path = new (GetAllocator()) IntrinsicSlowPathX86_64(invoke);
881 codegen_->AddSlowPath(slow_path);
882 __ j(kEqual, slow_path->GetEntryLabel());
883
884 __ gs()->call(Address::Absolute(
885 QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pStringCompareTo), true));
886 __ Bind(slow_path->GetExitLabel());
887}
888
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800889static void GenPeek(LocationSummary* locations, Primitive::Type size, X86_64Assembler* assembler) {
890 CpuRegister address = locations->InAt(0).AsRegister<CpuRegister>();
891 CpuRegister out = locations->Out().AsRegister<CpuRegister>(); // == address, here for clarity.
892 // x86 allows unaligned access. We do not have to check the input or use specific instructions
893 // to avoid a SIGBUS.
894 switch (size) {
895 case Primitive::kPrimByte:
896 __ movsxb(out, Address(address, 0));
897 break;
898 case Primitive::kPrimShort:
899 __ movsxw(out, Address(address, 0));
900 break;
901 case Primitive::kPrimInt:
902 __ movl(out, Address(address, 0));
903 break;
904 case Primitive::kPrimLong:
905 __ movq(out, Address(address, 0));
906 break;
907 default:
908 LOG(FATAL) << "Type not recognized for peek: " << size;
909 UNREACHABLE();
910 }
911}
912
913void IntrinsicLocationsBuilderX86_64::VisitMemoryPeekByte(HInvoke* invoke) {
914 CreateIntToIntLocations(arena_, invoke);
915}
916
917void IntrinsicCodeGeneratorX86_64::VisitMemoryPeekByte(HInvoke* invoke) {
918 GenPeek(invoke->GetLocations(), Primitive::kPrimByte, GetAssembler());
919}
920
921void IntrinsicLocationsBuilderX86_64::VisitMemoryPeekIntNative(HInvoke* invoke) {
922 CreateIntToIntLocations(arena_, invoke);
923}
924
925void IntrinsicCodeGeneratorX86_64::VisitMemoryPeekIntNative(HInvoke* invoke) {
926 GenPeek(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
927}
928
929void IntrinsicLocationsBuilderX86_64::VisitMemoryPeekLongNative(HInvoke* invoke) {
930 CreateIntToIntLocations(arena_, invoke);
931}
932
933void IntrinsicCodeGeneratorX86_64::VisitMemoryPeekLongNative(HInvoke* invoke) {
934 GenPeek(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
935}
936
937void IntrinsicLocationsBuilderX86_64::VisitMemoryPeekShortNative(HInvoke* invoke) {
938 CreateIntToIntLocations(arena_, invoke);
939}
940
941void IntrinsicCodeGeneratorX86_64::VisitMemoryPeekShortNative(HInvoke* invoke) {
942 GenPeek(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler());
943}
944
945static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
946 LocationSummary* locations = new (arena) LocationSummary(invoke,
947 LocationSummary::kNoCall,
948 kIntrinsified);
949 locations->SetInAt(0, Location::RequiresRegister());
Mark Mendell40741f32015-04-20 22:10:34 -0400950 locations->SetInAt(1, Location::RegisterOrInt32LongConstant(invoke->InputAt(1)));
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800951}
952
953static void GenPoke(LocationSummary* locations, Primitive::Type size, X86_64Assembler* assembler) {
954 CpuRegister address = locations->InAt(0).AsRegister<CpuRegister>();
Mark Mendell40741f32015-04-20 22:10:34 -0400955 Location value = locations->InAt(1);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800956 // x86 allows unaligned access. We do not have to check the input or use specific instructions
957 // to avoid a SIGBUS.
958 switch (size) {
959 case Primitive::kPrimByte:
Mark Mendell40741f32015-04-20 22:10:34 -0400960 if (value.IsConstant()) {
961 __ movb(Address(address, 0),
962 Immediate(CodeGenerator::GetInt32ValueOf(value.GetConstant())));
963 } else {
964 __ movb(Address(address, 0), value.AsRegister<CpuRegister>());
965 }
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800966 break;
967 case Primitive::kPrimShort:
Mark Mendell40741f32015-04-20 22:10:34 -0400968 if (value.IsConstant()) {
969 __ movw(Address(address, 0),
970 Immediate(CodeGenerator::GetInt32ValueOf(value.GetConstant())));
971 } else {
972 __ movw(Address(address, 0), value.AsRegister<CpuRegister>());
973 }
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800974 break;
975 case Primitive::kPrimInt:
Mark Mendell40741f32015-04-20 22:10:34 -0400976 if (value.IsConstant()) {
977 __ movl(Address(address, 0),
978 Immediate(CodeGenerator::GetInt32ValueOf(value.GetConstant())));
979 } else {
980 __ movl(Address(address, 0), value.AsRegister<CpuRegister>());
981 }
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800982 break;
983 case Primitive::kPrimLong:
Mark Mendell40741f32015-04-20 22:10:34 -0400984 if (value.IsConstant()) {
985 int64_t v = value.GetConstant()->AsLongConstant()->GetValue();
986 DCHECK(IsInt<32>(v));
987 int32_t v_32 = v;
988 __ movq(Address(address, 0), Immediate(v_32));
989 } else {
990 __ movq(Address(address, 0), value.AsRegister<CpuRegister>());
991 }
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800992 break;
993 default:
994 LOG(FATAL) << "Type not recognized for poke: " << size;
995 UNREACHABLE();
996 }
997}
998
999void IntrinsicLocationsBuilderX86_64::VisitMemoryPokeByte(HInvoke* invoke) {
1000 CreateIntIntToVoidLocations(arena_, invoke);
1001}
1002
1003void IntrinsicCodeGeneratorX86_64::VisitMemoryPokeByte(HInvoke* invoke) {
1004 GenPoke(invoke->GetLocations(), Primitive::kPrimByte, GetAssembler());
1005}
1006
1007void IntrinsicLocationsBuilderX86_64::VisitMemoryPokeIntNative(HInvoke* invoke) {
1008 CreateIntIntToVoidLocations(arena_, invoke);
1009}
1010
1011void IntrinsicCodeGeneratorX86_64::VisitMemoryPokeIntNative(HInvoke* invoke) {
1012 GenPoke(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
1013}
1014
1015void IntrinsicLocationsBuilderX86_64::VisitMemoryPokeLongNative(HInvoke* invoke) {
1016 CreateIntIntToVoidLocations(arena_, invoke);
1017}
1018
1019void IntrinsicCodeGeneratorX86_64::VisitMemoryPokeLongNative(HInvoke* invoke) {
1020 GenPoke(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
1021}
1022
1023void IntrinsicLocationsBuilderX86_64::VisitMemoryPokeShortNative(HInvoke* invoke) {
1024 CreateIntIntToVoidLocations(arena_, invoke);
1025}
1026
1027void IntrinsicCodeGeneratorX86_64::VisitMemoryPokeShortNative(HInvoke* invoke) {
1028 GenPoke(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler());
1029}
1030
1031void IntrinsicLocationsBuilderX86_64::VisitThreadCurrentThread(HInvoke* invoke) {
1032 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1033 LocationSummary::kNoCall,
1034 kIntrinsified);
1035 locations->SetOut(Location::RequiresRegister());
1036}
1037
1038void IntrinsicCodeGeneratorX86_64::VisitThreadCurrentThread(HInvoke* invoke) {
1039 CpuRegister out = invoke->GetLocations()->Out().AsRegister<CpuRegister>();
1040 GetAssembler()->gs()->movl(out, Address::Absolute(Thread::PeerOffset<kX86_64WordSize>(), true));
1041}
1042
Andreas Gampe878d58c2015-01-15 23:24:00 -08001043static void GenUnsafeGet(LocationSummary* locations, Primitive::Type type,
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001044 bool is_volatile ATTRIBUTE_UNUSED, X86_64Assembler* assembler) {
1045 CpuRegister base = locations->InAt(1).AsRegister<CpuRegister>();
1046 CpuRegister offset = locations->InAt(2).AsRegister<CpuRegister>();
1047 CpuRegister trg = locations->Out().AsRegister<CpuRegister>();
1048
Andreas Gampe878d58c2015-01-15 23:24:00 -08001049 switch (type) {
1050 case Primitive::kPrimInt:
1051 case Primitive::kPrimNot:
1052 __ movl(trg, Address(base, offset, ScaleFactor::TIMES_1, 0));
1053 break;
1054
1055 case Primitive::kPrimLong:
1056 __ movq(trg, Address(base, offset, ScaleFactor::TIMES_1, 0));
1057 break;
1058
1059 default:
1060 LOG(FATAL) << "Unsupported op size " << type;
1061 UNREACHABLE();
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001062 }
1063}
1064
1065static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
1066 LocationSummary* locations = new (arena) LocationSummary(invoke,
1067 LocationSummary::kNoCall,
1068 kIntrinsified);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001069 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001070 locations->SetInAt(1, Location::RequiresRegister());
1071 locations->SetInAt(2, Location::RequiresRegister());
Andreas Gampe878d58c2015-01-15 23:24:00 -08001072 locations->SetOut(Location::RequiresRegister());
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001073}
1074
1075void IntrinsicLocationsBuilderX86_64::VisitUnsafeGet(HInvoke* invoke) {
1076 CreateIntIntIntToIntLocations(arena_, invoke);
1077}
1078void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetVolatile(HInvoke* invoke) {
1079 CreateIntIntIntToIntLocations(arena_, invoke);
1080}
1081void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetLong(HInvoke* invoke) {
1082 CreateIntIntIntToIntLocations(arena_, invoke);
1083}
1084void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
1085 CreateIntIntIntToIntLocations(arena_, invoke);
1086}
Andreas Gampe878d58c2015-01-15 23:24:00 -08001087void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetObject(HInvoke* invoke) {
1088 CreateIntIntIntToIntLocations(arena_, invoke);
1089}
1090void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
1091 CreateIntIntIntToIntLocations(arena_, invoke);
1092}
1093
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001094
1095void IntrinsicCodeGeneratorX86_64::VisitUnsafeGet(HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08001096 GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimInt, false, GetAssembler());
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001097}
1098void IntrinsicCodeGeneratorX86_64::VisitUnsafeGetVolatile(HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08001099 GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimInt, true, GetAssembler());
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001100}
1101void IntrinsicCodeGeneratorX86_64::VisitUnsafeGetLong(HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08001102 GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimLong, false, GetAssembler());
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001103}
1104void IntrinsicCodeGeneratorX86_64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08001105 GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimLong, true, GetAssembler());
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001106}
Andreas Gampe878d58c2015-01-15 23:24:00 -08001107void IntrinsicCodeGeneratorX86_64::VisitUnsafeGetObject(HInvoke* invoke) {
1108 GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimNot, false, GetAssembler());
1109}
1110void IntrinsicCodeGeneratorX86_64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
1111 GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimNot, true, GetAssembler());
1112}
1113
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001114
1115static void CreateIntIntIntIntToVoidPlusTempsLocations(ArenaAllocator* arena,
1116 Primitive::Type type,
1117 HInvoke* invoke) {
1118 LocationSummary* locations = new (arena) LocationSummary(invoke,
1119 LocationSummary::kNoCall,
1120 kIntrinsified);
Andreas Gampe878d58c2015-01-15 23:24:00 -08001121 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001122 locations->SetInAt(1, Location::RequiresRegister());
1123 locations->SetInAt(2, Location::RequiresRegister());
1124 locations->SetInAt(3, Location::RequiresRegister());
1125 if (type == Primitive::kPrimNot) {
1126 // Need temp registers for card-marking.
1127 locations->AddTemp(Location::RequiresRegister());
1128 locations->AddTemp(Location::RequiresRegister());
1129 }
1130}
1131
1132void IntrinsicLocationsBuilderX86_64::VisitUnsafePut(HInvoke* invoke) {
1133 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimInt, invoke);
1134}
1135void IntrinsicLocationsBuilderX86_64::VisitUnsafePutOrdered(HInvoke* invoke) {
1136 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimInt, invoke);
1137}
1138void IntrinsicLocationsBuilderX86_64::VisitUnsafePutVolatile(HInvoke* invoke) {
1139 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimInt, invoke);
1140}
1141void IntrinsicLocationsBuilderX86_64::VisitUnsafePutObject(HInvoke* invoke) {
1142 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimNot, invoke);
1143}
1144void IntrinsicLocationsBuilderX86_64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
1145 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimNot, invoke);
1146}
1147void IntrinsicLocationsBuilderX86_64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
1148 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimNot, invoke);
1149}
1150void IntrinsicLocationsBuilderX86_64::VisitUnsafePutLong(HInvoke* invoke) {
1151 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimLong, invoke);
1152}
1153void IntrinsicLocationsBuilderX86_64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
1154 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimLong, invoke);
1155}
1156void IntrinsicLocationsBuilderX86_64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
1157 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimLong, invoke);
1158}
1159
1160// We don't care for ordered: it requires an AnyStore barrier, which is already given by the x86
1161// memory model.
1162static void GenUnsafePut(LocationSummary* locations, Primitive::Type type, bool is_volatile,
1163 CodeGeneratorX86_64* codegen) {
1164 X86_64Assembler* assembler = reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler());
1165 CpuRegister base = locations->InAt(1).AsRegister<CpuRegister>();
1166 CpuRegister offset = locations->InAt(2).AsRegister<CpuRegister>();
1167 CpuRegister value = locations->InAt(3).AsRegister<CpuRegister>();
1168
1169 if (type == Primitive::kPrimLong) {
1170 __ movq(Address(base, offset, ScaleFactor::TIMES_1, 0), value);
1171 } else {
1172 __ movl(Address(base, offset, ScaleFactor::TIMES_1, 0), value);
1173 }
1174
1175 if (is_volatile) {
1176 __ mfence();
1177 }
1178
1179 if (type == Primitive::kPrimNot) {
1180 codegen->MarkGCCard(locations->GetTemp(0).AsRegister<CpuRegister>(),
1181 locations->GetTemp(1).AsRegister<CpuRegister>(),
1182 base,
1183 value);
1184 }
1185}
1186
1187void IntrinsicCodeGeneratorX86_64::VisitUnsafePut(HInvoke* invoke) {
1188 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, codegen_);
1189}
1190void IntrinsicCodeGeneratorX86_64::VisitUnsafePutOrdered(HInvoke* invoke) {
1191 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, codegen_);
1192}
1193void IntrinsicCodeGeneratorX86_64::VisitUnsafePutVolatile(HInvoke* invoke) {
1194 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, true, codegen_);
1195}
1196void IntrinsicCodeGeneratorX86_64::VisitUnsafePutObject(HInvoke* invoke) {
1197 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, codegen_);
1198}
1199void IntrinsicCodeGeneratorX86_64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
1200 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, codegen_);
1201}
1202void IntrinsicCodeGeneratorX86_64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
1203 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, true, codegen_);
1204}
1205void IntrinsicCodeGeneratorX86_64::VisitUnsafePutLong(HInvoke* invoke) {
1206 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, codegen_);
1207}
1208void IntrinsicCodeGeneratorX86_64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
1209 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, codegen_);
1210}
1211void IntrinsicCodeGeneratorX86_64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
1212 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, codegen_);
1213}
1214
Mark Mendell58d25fd2015-04-03 14:52:31 -04001215static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, Primitive::Type type,
1216 HInvoke* invoke) {
1217 LocationSummary* locations = new (arena) LocationSummary(invoke,
1218 LocationSummary::kNoCall,
1219 kIntrinsified);
1220 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1221 locations->SetInAt(1, Location::RequiresRegister());
1222 locations->SetInAt(2, Location::RequiresRegister());
1223 // expected value must be in EAX/RAX.
1224 locations->SetInAt(3, Location::RegisterLocation(RAX));
1225 locations->SetInAt(4, Location::RequiresRegister());
1226
1227 locations->SetOut(Location::RequiresRegister());
1228 if (type == Primitive::kPrimNot) {
1229 // Need temp registers for card-marking.
1230 locations->AddTemp(Location::RequiresRegister());
1231 locations->AddTemp(Location::RequiresRegister());
1232 }
1233}
1234
1235void IntrinsicLocationsBuilderX86_64::VisitUnsafeCASInt(HInvoke* invoke) {
1236 CreateIntIntIntIntIntToInt(arena_, Primitive::kPrimInt, invoke);
1237}
1238
1239void IntrinsicLocationsBuilderX86_64::VisitUnsafeCASLong(HInvoke* invoke) {
1240 CreateIntIntIntIntIntToInt(arena_, Primitive::kPrimLong, invoke);
1241}
1242
1243void IntrinsicLocationsBuilderX86_64::VisitUnsafeCASObject(HInvoke* invoke) {
1244 CreateIntIntIntIntIntToInt(arena_, Primitive::kPrimNot, invoke);
1245}
1246
1247static void GenCAS(Primitive::Type type, HInvoke* invoke, CodeGeneratorX86_64* codegen) {
1248 X86_64Assembler* assembler =
1249 reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler());
1250 LocationSummary* locations = invoke->GetLocations();
1251
1252 CpuRegister base = locations->InAt(1).AsRegister<CpuRegister>();
1253 CpuRegister offset = locations->InAt(2).AsRegister<CpuRegister>();
1254 CpuRegister expected = locations->InAt(3).AsRegister<CpuRegister>();
1255 DCHECK_EQ(expected.AsRegister(), RAX);
1256 CpuRegister value = locations->InAt(4).AsRegister<CpuRegister>();
1257 CpuRegister out = locations->Out().AsRegister<CpuRegister>();
1258
1259 if (type == Primitive::kPrimLong) {
1260 __ LockCmpxchgq(Address(base, offset, TIMES_1, 0), value);
1261 } else {
1262 // Integer or object.
1263 if (type == Primitive::kPrimNot) {
1264 // Mark card for object assuming new value is stored.
1265 codegen->MarkGCCard(locations->GetTemp(0).AsRegister<CpuRegister>(),
1266 locations->GetTemp(1).AsRegister<CpuRegister>(),
1267 base,
1268 value);
1269 }
1270
1271 __ LockCmpxchgl(Address(base, offset, TIMES_1, 0), value);
1272 }
1273
1274 // locked cmpxchg has full barrier semantics, and we don't need scheduling
1275 // barriers at this time.
1276
1277 // Convert ZF into the boolean result.
1278 __ setcc(kZero, out);
1279 __ movzxb(out, out);
1280}
1281
1282void IntrinsicCodeGeneratorX86_64::VisitUnsafeCASInt(HInvoke* invoke) {
1283 GenCAS(Primitive::kPrimInt, invoke, codegen_);
1284}
1285
1286void IntrinsicCodeGeneratorX86_64::VisitUnsafeCASLong(HInvoke* invoke) {
1287 GenCAS(Primitive::kPrimLong, invoke, codegen_);
1288}
1289
1290void IntrinsicCodeGeneratorX86_64::VisitUnsafeCASObject(HInvoke* invoke) {
1291 GenCAS(Primitive::kPrimNot, invoke, codegen_);
1292}
1293
1294void IntrinsicLocationsBuilderX86_64::VisitIntegerReverse(HInvoke* invoke) {
1295 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1296 LocationSummary::kNoCall,
1297 kIntrinsified);
1298 locations->SetInAt(0, Location::RequiresRegister());
1299 locations->SetOut(Location::SameAsFirstInput());
1300 locations->AddTemp(Location::RequiresRegister());
1301}
1302
1303static void SwapBits(CpuRegister reg, CpuRegister temp, int32_t shift, int32_t mask,
1304 X86_64Assembler* assembler) {
1305 Immediate imm_shift(shift);
1306 Immediate imm_mask(mask);
1307 __ movl(temp, reg);
1308 __ shrl(reg, imm_shift);
1309 __ andl(temp, imm_mask);
1310 __ andl(reg, imm_mask);
1311 __ shll(temp, imm_shift);
1312 __ orl(reg, temp);
1313}
1314
1315void IntrinsicCodeGeneratorX86_64::VisitIntegerReverse(HInvoke* invoke) {
1316 X86_64Assembler* assembler =
1317 reinterpret_cast<X86_64Assembler*>(codegen_->GetAssembler());
1318 LocationSummary* locations = invoke->GetLocations();
1319
1320 CpuRegister reg = locations->InAt(0).AsRegister<CpuRegister>();
1321 CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
1322
1323 /*
1324 * Use one bswap instruction to reverse byte order first and then use 3 rounds of
1325 * swapping bits to reverse bits in a number x. Using bswap to save instructions
1326 * compared to generic luni implementation which has 5 rounds of swapping bits.
1327 * x = bswap x
1328 * x = (x & 0x55555555) << 1 | (x >> 1) & 0x55555555;
1329 * x = (x & 0x33333333) << 2 | (x >> 2) & 0x33333333;
1330 * x = (x & 0x0F0F0F0F) << 4 | (x >> 4) & 0x0F0F0F0F;
1331 */
1332 __ bswapl(reg);
1333 SwapBits(reg, temp, 1, 0x55555555, assembler);
1334 SwapBits(reg, temp, 2, 0x33333333, assembler);
1335 SwapBits(reg, temp, 4, 0x0f0f0f0f, assembler);
1336}
1337
1338void IntrinsicLocationsBuilderX86_64::VisitLongReverse(HInvoke* invoke) {
1339 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1340 LocationSummary::kNoCall,
1341 kIntrinsified);
1342 locations->SetInAt(0, Location::RequiresRegister());
1343 locations->SetOut(Location::SameAsFirstInput());
1344 locations->AddTemp(Location::RequiresRegister());
1345 locations->AddTemp(Location::RequiresRegister());
1346}
1347
1348static void SwapBits64(CpuRegister reg, CpuRegister temp, CpuRegister temp_mask,
1349 int32_t shift, int64_t mask, X86_64Assembler* assembler) {
1350 Immediate imm_shift(shift);
1351 __ movq(temp_mask, Immediate(mask));
1352 __ movq(temp, reg);
1353 __ shrq(reg, imm_shift);
1354 __ andq(temp, temp_mask);
1355 __ andq(reg, temp_mask);
1356 __ shlq(temp, imm_shift);
1357 __ orq(reg, temp);
1358}
1359
1360void IntrinsicCodeGeneratorX86_64::VisitLongReverse(HInvoke* invoke) {
1361 X86_64Assembler* assembler =
1362 reinterpret_cast<X86_64Assembler*>(codegen_->GetAssembler());
1363 LocationSummary* locations = invoke->GetLocations();
1364
1365 CpuRegister reg = locations->InAt(0).AsRegister<CpuRegister>();
1366 CpuRegister temp1 = locations->GetTemp(0).AsRegister<CpuRegister>();
1367 CpuRegister temp2 = locations->GetTemp(1).AsRegister<CpuRegister>();
1368
1369 /*
1370 * Use one bswap instruction to reverse byte order first and then use 3 rounds of
1371 * swapping bits to reverse bits in a long number x. Using bswap to save instructions
1372 * compared to generic luni implementation which has 5 rounds of swapping bits.
1373 * x = bswap x
1374 * x = (x & 0x5555555555555555) << 1 | (x >> 1) & 0x5555555555555555;
1375 * x = (x & 0x3333333333333333) << 2 | (x >> 2) & 0x3333333333333333;
1376 * x = (x & 0x0F0F0F0F0F0F0F0F) << 4 | (x >> 4) & 0x0F0F0F0F0F0F0F0F;
1377 */
1378 __ bswapq(reg);
1379 SwapBits64(reg, temp1, temp2, 1, INT64_C(0x5555555555555555), assembler);
1380 SwapBits64(reg, temp1, temp2, 2, INT64_C(0x3333333333333333), assembler);
1381 SwapBits64(reg, temp1, temp2, 4, INT64_C(0x0f0f0f0f0f0f0f0f), assembler);
1382}
1383
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001384// Unimplemented intrinsics.
1385
1386#define UNIMPLEMENTED_INTRINSIC(Name) \
1387void IntrinsicLocationsBuilderX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
1388} \
1389void IntrinsicCodeGeneratorX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
1390}
1391
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001392UNIMPLEMENTED_INTRINSIC(StringIndexOf)
1393UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)
1394UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001395UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
1396
1397} // namespace x86_64
1398} // namespace art