blob: b18741dbfc4011ef9a0d5dcce34dd5bfd3658070 [file] [log] [blame]
Mark Mendell09ed1a32015-03-25 08:30:06 -04001/*
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.h"
18
Mark Mendellfb8d2792015-03-31 22:16:59 -040019#include "arch/x86/instruction_set_features_x86.h"
Mark Mendell09ed1a32015-03-25 08:30:06 -040020#include "code_generator_x86.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/assembler_x86.h"
28#include "utils/x86/constants_x86.h"
29
30namespace art {
31
32namespace x86 {
33
34static constexpr int kDoubleNaNHigh = 0x7FF80000;
35static constexpr int kDoubleNaNLow = 0x00000000;
36static constexpr int kFloatNaN = 0x7FC00000;
37
Mark Mendellfb8d2792015-03-31 22:16:59 -040038IntrinsicLocationsBuilderX86::IntrinsicLocationsBuilderX86(CodeGeneratorX86* codegen)
39 : arena_(codegen->GetGraph()->GetArena()), codegen_(codegen) {
40}
41
42
Mark Mendell09ed1a32015-03-25 08:30:06 -040043X86Assembler* IntrinsicCodeGeneratorX86::GetAssembler() {
44 return reinterpret_cast<X86Assembler*>(codegen_->GetAssembler());
45}
46
47ArenaAllocator* IntrinsicCodeGeneratorX86::GetAllocator() {
48 return codegen_->GetGraph()->GetArena();
49}
50
51bool IntrinsicLocationsBuilderX86::TryDispatch(HInvoke* invoke) {
52 Dispatch(invoke);
53 LocationSummary* res = invoke->GetLocations();
54 return res != nullptr && res->Intrinsified();
55}
56
57#define __ reinterpret_cast<X86Assembler*>(codegen->GetAssembler())->
58
59// TODO: target as memory.
60static void MoveFromReturnRegister(Location target,
61 Primitive::Type type,
62 CodeGeneratorX86* codegen) {
63 if (!target.IsValid()) {
64 DCHECK(type == Primitive::kPrimVoid);
65 return;
66 }
67
68 switch (type) {
69 case Primitive::kPrimBoolean:
70 case Primitive::kPrimByte:
71 case Primitive::kPrimChar:
72 case Primitive::kPrimShort:
73 case Primitive::kPrimInt:
74 case Primitive::kPrimNot: {
75 Register target_reg = target.AsRegister<Register>();
76 if (target_reg != EAX) {
77 __ movl(target_reg, EAX);
78 }
79 break;
80 }
81 case Primitive::kPrimLong: {
82 Register target_reg_lo = target.AsRegisterPairLow<Register>();
83 Register target_reg_hi = target.AsRegisterPairHigh<Register>();
84 if (target_reg_lo != EAX) {
85 __ movl(target_reg_lo, EAX);
86 }
87 if (target_reg_hi != EDX) {
88 __ movl(target_reg_hi, EDX);
89 }
90 break;
91 }
92
93 case Primitive::kPrimVoid:
94 LOG(FATAL) << "Unexpected void type for valid location " << target;
95 UNREACHABLE();
96
97 case Primitive::kPrimDouble: {
98 XmmRegister target_reg = target.AsFpuRegister<XmmRegister>();
99 if (target_reg != XMM0) {
100 __ movsd(target_reg, XMM0);
101 }
102 break;
103 }
104 case Primitive::kPrimFloat: {
105 XmmRegister target_reg = target.AsFpuRegister<XmmRegister>();
106 if (target_reg != XMM0) {
107 __ movss(target_reg, XMM0);
108 }
109 break;
110 }
111 }
112}
113
114static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX86* codegen) {
Roland Levillain3e3d7332015-04-28 11:00:54 +0100115 if (invoke->GetNumberOfArguments() == 0) {
Roland Levillain4c0eb422015-04-24 16:43:49 +0100116 // No argument to move.
Mark Mendell09ed1a32015-03-25 08:30:06 -0400117 return;
118 }
119
120 LocationSummary* locations = invoke->GetLocations();
121 InvokeDexCallingConventionVisitor calling_convention_visitor;
122
123 // We're moving potentially two or more locations to locations that could overlap, so we need
124 // a parallel move resolver.
125 HParallelMove parallel_move(arena);
126
Roland Levillain3e3d7332015-04-28 11:00:54 +0100127 for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) {
Mark Mendell09ed1a32015-03-25 08:30:06 -0400128 HInstruction* input = invoke->InputAt(i);
129 Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType());
130 Location actual_loc = locations->InAt(i);
131
Nicolas Geoffray90218252015-04-15 11:56:51 +0100132 parallel_move.AddMove(actual_loc, cc_loc, input->GetType(), nullptr);
Mark Mendell09ed1a32015-03-25 08:30:06 -0400133 }
134
135 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
136}
137
138// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
139// call. This will copy the arguments into the positions for a regular call.
140//
141// Note: The actual parameters are required to be in the locations given by the invoke's location
142// summary. If an intrinsic modifies those locations before a slowpath call, they must be
143// restored!
144class IntrinsicSlowPathX86 : public SlowPathCodeX86 {
145 public:
146 explicit IntrinsicSlowPathX86(HInvoke* invoke, Register temp)
147 : invoke_(invoke) {
148 // The temporary register has to be EAX for x86 invokes.
149 DCHECK_EQ(temp, EAX);
150 }
151
152 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
153 CodeGeneratorX86* codegen = down_cast<CodeGeneratorX86*>(codegen_in);
154 __ Bind(GetEntryLabel());
155
156 SaveLiveRegisters(codegen, invoke_->GetLocations());
157
158 MoveArguments(invoke_, codegen->GetGraph()->GetArena(), codegen);
159
160 if (invoke_->IsInvokeStaticOrDirect()) {
161 codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), EAX);
Mingyao Yange90db122015-04-03 17:56:54 -0700162 RecordPcInfo(codegen, invoke_, invoke_->GetDexPc());
Mark Mendell09ed1a32015-03-25 08:30:06 -0400163 } else {
164 UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
165 UNREACHABLE();
166 }
167
168 // Copy the result back to the expected output.
169 Location out = invoke_->GetLocations()->Out();
170 if (out.IsValid()) {
171 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
172 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
173 MoveFromReturnRegister(out, invoke_->GetType(), codegen);
174 }
175
176 RestoreLiveRegisters(codegen, invoke_->GetLocations());
177 __ jmp(GetExitLabel());
178 }
179
180 private:
181 // The instruction where this slow path is happening.
182 HInvoke* const invoke_;
183
184 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathX86);
185};
186
187#undef __
188#define __ assembler->
189
190static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke, bool is64bit) {
191 LocationSummary* locations = new (arena) LocationSummary(invoke,
192 LocationSummary::kNoCall,
193 kIntrinsified);
194 locations->SetInAt(0, Location::RequiresFpuRegister());
195 locations->SetOut(Location::RequiresRegister());
196 if (is64bit) {
197 locations->AddTemp(Location::RequiresFpuRegister());
198 }
199}
200
201static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke, bool is64bit) {
202 LocationSummary* locations = new (arena) LocationSummary(invoke,
203 LocationSummary::kNoCall,
204 kIntrinsified);
205 locations->SetInAt(0, Location::RequiresRegister());
206 locations->SetOut(Location::RequiresFpuRegister());
207 if (is64bit) {
208 locations->AddTemp(Location::RequiresFpuRegister());
209 locations->AddTemp(Location::RequiresFpuRegister());
210 }
211}
212
213static void MoveFPToInt(LocationSummary* locations, bool is64bit, X86Assembler* assembler) {
214 Location input = locations->InAt(0);
215 Location output = locations->Out();
216 if (is64bit) {
217 // Need to use the temporary.
218 XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
219 __ movsd(temp, input.AsFpuRegister<XmmRegister>());
220 __ movd(output.AsRegisterPairLow<Register>(), temp);
221 __ psrlq(temp, Immediate(32));
222 __ movd(output.AsRegisterPairHigh<Register>(), temp);
223 } else {
224 __ movd(output.AsRegister<Register>(), input.AsFpuRegister<XmmRegister>());
225 }
226}
227
228static void MoveIntToFP(LocationSummary* locations, bool is64bit, X86Assembler* assembler) {
229 Location input = locations->InAt(0);
230 Location output = locations->Out();
231 if (is64bit) {
232 // Need to use the temporary.
233 XmmRegister temp1 = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
234 XmmRegister temp2 = locations->GetTemp(1).AsFpuRegister<XmmRegister>();
235 __ movd(temp1, input.AsRegisterPairLow<Register>());
236 __ movd(temp2, input.AsRegisterPairHigh<Register>());
237 __ punpckldq(temp1, temp2);
238 __ movsd(output.AsFpuRegister<XmmRegister>(), temp1);
239 } else {
240 __ movd(output.AsFpuRegister<XmmRegister>(), input.AsRegister<Register>());
241 }
242}
243
244void IntrinsicLocationsBuilderX86::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
245 CreateFPToIntLocations(arena_, invoke, true);
246}
247void IntrinsicLocationsBuilderX86::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
248 CreateIntToFPLocations(arena_, invoke, true);
249}
250
251void IntrinsicCodeGeneratorX86::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
252 MoveFPToInt(invoke->GetLocations(), true, GetAssembler());
253}
254void IntrinsicCodeGeneratorX86::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
255 MoveIntToFP(invoke->GetLocations(), true, GetAssembler());
256}
257
258void IntrinsicLocationsBuilderX86::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
259 CreateFPToIntLocations(arena_, invoke, false);
260}
261void IntrinsicLocationsBuilderX86::VisitFloatIntBitsToFloat(HInvoke* invoke) {
262 CreateIntToFPLocations(arena_, invoke, false);
263}
264
265void IntrinsicCodeGeneratorX86::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
266 MoveFPToInt(invoke->GetLocations(), false, GetAssembler());
267}
268void IntrinsicCodeGeneratorX86::VisitFloatIntBitsToFloat(HInvoke* invoke) {
269 MoveIntToFP(invoke->GetLocations(), false, GetAssembler());
270}
271
272static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
273 LocationSummary* locations = new (arena) LocationSummary(invoke,
274 LocationSummary::kNoCall,
275 kIntrinsified);
276 locations->SetInAt(0, Location::RequiresRegister());
277 locations->SetOut(Location::SameAsFirstInput());
278}
279
280static void CreateLongToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
281 LocationSummary* locations = new (arena) LocationSummary(invoke,
282 LocationSummary::kNoCall,
283 kIntrinsified);
284 locations->SetInAt(0, Location::RequiresRegister());
285 locations->SetOut(Location::RequiresRegister());
286}
287
288static void CreateLongToLongLocations(ArenaAllocator* arena, HInvoke* invoke) {
289 LocationSummary* locations = new (arena) LocationSummary(invoke,
290 LocationSummary::kNoCall,
291 kIntrinsified);
292 locations->SetInAt(0, Location::RequiresRegister());
293 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
294}
295
296static void GenReverseBytes(LocationSummary* locations,
297 Primitive::Type size,
298 X86Assembler* assembler) {
299 Register out = locations->Out().AsRegister<Register>();
300
301 switch (size) {
302 case Primitive::kPrimShort:
303 // TODO: Can be done with an xchg of 8b registers. This is straight from Quick.
304 __ bswapl(out);
305 __ sarl(out, Immediate(16));
306 break;
307 case Primitive::kPrimInt:
308 __ bswapl(out);
309 break;
310 default:
311 LOG(FATAL) << "Unexpected size for reverse-bytes: " << size;
312 UNREACHABLE();
313 }
314}
315
316void IntrinsicLocationsBuilderX86::VisitIntegerReverseBytes(HInvoke* invoke) {
317 CreateIntToIntLocations(arena_, invoke);
318}
319
320void IntrinsicCodeGeneratorX86::VisitIntegerReverseBytes(HInvoke* invoke) {
321 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
322}
323
Mark Mendell58d25fd2015-04-03 14:52:31 -0400324void IntrinsicLocationsBuilderX86::VisitLongReverseBytes(HInvoke* invoke) {
325 CreateLongToLongLocations(arena_, invoke);
326}
327
328void IntrinsicCodeGeneratorX86::VisitLongReverseBytes(HInvoke* invoke) {
329 LocationSummary* locations = invoke->GetLocations();
330 Location input = locations->InAt(0);
331 Register input_lo = input.AsRegisterPairLow<Register>();
332 Register input_hi = input.AsRegisterPairHigh<Register>();
333 Location output = locations->Out();
334 Register output_lo = output.AsRegisterPairLow<Register>();
335 Register output_hi = output.AsRegisterPairHigh<Register>();
336
337 X86Assembler* assembler = GetAssembler();
338 // Assign the inputs to the outputs, mixing low/high.
339 __ movl(output_lo, input_hi);
340 __ movl(output_hi, input_lo);
341 __ bswapl(output_lo);
342 __ bswapl(output_hi);
343}
344
Mark Mendell09ed1a32015-03-25 08:30:06 -0400345void IntrinsicLocationsBuilderX86::VisitShortReverseBytes(HInvoke* invoke) {
346 CreateIntToIntLocations(arena_, invoke);
347}
348
349void IntrinsicCodeGeneratorX86::VisitShortReverseBytes(HInvoke* invoke) {
350 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler());
351}
352
353
354// TODO: Consider Quick's way of doing Double abs through integer operations, as the immediate we
355// need is 64b.
356
357static void CreateFloatToFloat(ArenaAllocator* arena, HInvoke* invoke) {
358 // TODO: Enable memory operations when the assembler supports them.
359 LocationSummary* locations = new (arena) LocationSummary(invoke,
360 LocationSummary::kNoCall,
361 kIntrinsified);
362 locations->SetInAt(0, Location::RequiresFpuRegister());
363 // TODO: Allow x86 to work with memory. This requires assembler support, see below.
364 // locations->SetInAt(0, Location::Any()); // X86 can work on memory directly.
365 locations->SetOut(Location::SameAsFirstInput());
366}
367
368static void MathAbsFP(LocationSummary* locations, bool is64bit, X86Assembler* assembler) {
369 Location output = locations->Out();
370
371 if (output.IsFpuRegister()) {
372 // Create the right constant on an aligned stack.
373 if (is64bit) {
374 __ subl(ESP, Immediate(8));
375 __ pushl(Immediate(0x7FFFFFFF));
376 __ pushl(Immediate(0xFFFFFFFF));
377 __ andpd(output.AsFpuRegister<XmmRegister>(), Address(ESP, 0));
378 } else {
379 __ subl(ESP, Immediate(12));
380 __ pushl(Immediate(0x7FFFFFFF));
381 __ andps(output.AsFpuRegister<XmmRegister>(), Address(ESP, 0));
382 }
383 __ addl(ESP, Immediate(16));
384 } else {
385 // TODO: update when assember support is available.
386 UNIMPLEMENTED(FATAL) << "Needs assembler support.";
387// Once assembler support is available, in-memory operations look like this:
388// if (is64bit) {
389// DCHECK(output.IsDoubleStackSlot());
390// __ andl(Address(Register(RSP), output.GetHighStackIndex(kX86WordSize)),
391// Immediate(0x7FFFFFFF));
392// } else {
393// DCHECK(output.IsStackSlot());
394// // Can use and with a literal directly.
395// __ andl(Address(Register(RSP), output.GetStackIndex()), Immediate(0x7FFFFFFF));
396// }
397 }
398}
399
400void IntrinsicLocationsBuilderX86::VisitMathAbsDouble(HInvoke* invoke) {
401 CreateFloatToFloat(arena_, invoke);
402}
403
404void IntrinsicCodeGeneratorX86::VisitMathAbsDouble(HInvoke* invoke) {
405 MathAbsFP(invoke->GetLocations(), true, GetAssembler());
406}
407
408void IntrinsicLocationsBuilderX86::VisitMathAbsFloat(HInvoke* invoke) {
409 CreateFloatToFloat(arena_, invoke);
410}
411
412void IntrinsicCodeGeneratorX86::VisitMathAbsFloat(HInvoke* invoke) {
413 MathAbsFP(invoke->GetLocations(), false, GetAssembler());
414}
415
416static void CreateAbsIntLocation(ArenaAllocator* arena, HInvoke* invoke) {
417 LocationSummary* locations = new (arena) LocationSummary(invoke,
418 LocationSummary::kNoCall,
419 kIntrinsified);
420 locations->SetInAt(0, Location::RegisterLocation(EAX));
421 locations->SetOut(Location::SameAsFirstInput());
422 locations->AddTemp(Location::RegisterLocation(EDX));
423}
424
425static void GenAbsInteger(LocationSummary* locations, X86Assembler* assembler) {
426 Location output = locations->Out();
427 Register out = output.AsRegister<Register>();
428 DCHECK_EQ(out, EAX);
429 Register temp = locations->GetTemp(0).AsRegister<Register>();
430 DCHECK_EQ(temp, EDX);
431
432 // Sign extend EAX into EDX.
433 __ cdq();
434
435 // XOR EAX with sign.
436 __ xorl(EAX, EDX);
437
438 // Subtract out sign to correct.
439 __ subl(EAX, EDX);
440
441 // The result is in EAX.
442}
443
444static void CreateAbsLongLocation(ArenaAllocator* arena, HInvoke* invoke) {
445 LocationSummary* locations = new (arena) LocationSummary(invoke,
446 LocationSummary::kNoCall,
447 kIntrinsified);
448 locations->SetInAt(0, Location::RequiresRegister());
449 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
450 locations->AddTemp(Location::RequiresRegister());
451}
452
453static void GenAbsLong(LocationSummary* locations, X86Assembler* assembler) {
454 Location input = locations->InAt(0);
455 Register input_lo = input.AsRegisterPairLow<Register>();
456 Register input_hi = input.AsRegisterPairHigh<Register>();
457 Location output = locations->Out();
458 Register output_lo = output.AsRegisterPairLow<Register>();
459 Register output_hi = output.AsRegisterPairHigh<Register>();
460 Register temp = locations->GetTemp(0).AsRegister<Register>();
461
462 // Compute the sign into the temporary.
463 __ movl(temp, input_hi);
464 __ sarl(temp, Immediate(31));
465
466 // Store the sign into the output.
467 __ movl(output_lo, temp);
468 __ movl(output_hi, temp);
469
470 // XOR the input to the output.
471 __ xorl(output_lo, input_lo);
472 __ xorl(output_hi, input_hi);
473
474 // Subtract the sign.
475 __ subl(output_lo, temp);
476 __ sbbl(output_hi, temp);
477}
478
479void IntrinsicLocationsBuilderX86::VisitMathAbsInt(HInvoke* invoke) {
480 CreateAbsIntLocation(arena_, invoke);
481}
482
483void IntrinsicCodeGeneratorX86::VisitMathAbsInt(HInvoke* invoke) {
484 GenAbsInteger(invoke->GetLocations(), GetAssembler());
485}
486
487void IntrinsicLocationsBuilderX86::VisitMathAbsLong(HInvoke* invoke) {
488 CreateAbsLongLocation(arena_, invoke);
489}
490
491void IntrinsicCodeGeneratorX86::VisitMathAbsLong(HInvoke* invoke) {
492 GenAbsLong(invoke->GetLocations(), GetAssembler());
493}
494
495static void GenMinMaxFP(LocationSummary* locations, bool is_min, bool is_double,
496 X86Assembler* assembler) {
497 Location op1_loc = locations->InAt(0);
498 Location op2_loc = locations->InAt(1);
499 Location out_loc = locations->Out();
500 XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
501
502 // Shortcut for same input locations.
503 if (op1_loc.Equals(op2_loc)) {
504 DCHECK(out_loc.Equals(op1_loc));
505 return;
506 }
507
508 // (out := op1)
509 // out <=? op2
510 // if Nan jmp Nan_label
511 // if out is min jmp done
512 // if op2 is min jmp op2_label
513 // handle -0/+0
514 // jmp done
515 // Nan_label:
516 // out := NaN
517 // op2_label:
518 // out := op2
519 // done:
520 //
521 // This removes one jmp, but needs to copy one input (op1) to out.
522 //
523 // TODO: This is straight from Quick (except literal pool). Make NaN an out-of-line slowpath?
524
525 XmmRegister op2 = op2_loc.AsFpuRegister<XmmRegister>();
526
527 Label nan, done, op2_label;
528 if (is_double) {
529 __ ucomisd(out, op2);
530 } else {
531 __ ucomiss(out, op2);
532 }
533
534 __ j(Condition::kParityEven, &nan);
535
536 __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label);
537 __ j(is_min ? Condition::kBelow : Condition::kAbove, &done);
538
539 // Handle 0.0/-0.0.
540 if (is_min) {
541 if (is_double) {
542 __ orpd(out, op2);
543 } else {
544 __ orps(out, op2);
545 }
546 } else {
547 if (is_double) {
548 __ andpd(out, op2);
549 } else {
550 __ andps(out, op2);
551 }
552 }
553 __ jmp(&done);
554
555 // NaN handling.
556 __ Bind(&nan);
557 if (is_double) {
558 __ pushl(Immediate(kDoubleNaNHigh));
559 __ pushl(Immediate(kDoubleNaNLow));
560 __ movsd(out, Address(ESP, 0));
561 __ addl(ESP, Immediate(8));
562 } else {
563 __ pushl(Immediate(kFloatNaN));
564 __ movss(out, Address(ESP, 0));
565 __ addl(ESP, Immediate(4));
566 }
567 __ jmp(&done);
568
569 // out := op2;
570 __ Bind(&op2_label);
571 if (is_double) {
572 __ movsd(out, op2);
573 } else {
574 __ movss(out, op2);
575 }
576
577 // Done.
578 __ Bind(&done);
579}
580
581static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
582 LocationSummary* locations = new (arena) LocationSummary(invoke,
583 LocationSummary::kNoCall,
584 kIntrinsified);
585 locations->SetInAt(0, Location::RequiresFpuRegister());
586 locations->SetInAt(1, Location::RequiresFpuRegister());
587 // The following is sub-optimal, but all we can do for now. It would be fine to also accept
588 // the second input to be the output (we can simply swap inputs).
589 locations->SetOut(Location::SameAsFirstInput());
590}
591
592void IntrinsicLocationsBuilderX86::VisitMathMinDoubleDouble(HInvoke* invoke) {
593 CreateFPFPToFPLocations(arena_, invoke);
594}
595
596void IntrinsicCodeGeneratorX86::VisitMathMinDoubleDouble(HInvoke* invoke) {
597 GenMinMaxFP(invoke->GetLocations(), true, true, GetAssembler());
598}
599
600void IntrinsicLocationsBuilderX86::VisitMathMinFloatFloat(HInvoke* invoke) {
601 CreateFPFPToFPLocations(arena_, invoke);
602}
603
604void IntrinsicCodeGeneratorX86::VisitMathMinFloatFloat(HInvoke* invoke) {
605 GenMinMaxFP(invoke->GetLocations(), true, false, GetAssembler());
606}
607
608void IntrinsicLocationsBuilderX86::VisitMathMaxDoubleDouble(HInvoke* invoke) {
609 CreateFPFPToFPLocations(arena_, invoke);
610}
611
612void IntrinsicCodeGeneratorX86::VisitMathMaxDoubleDouble(HInvoke* invoke) {
613 GenMinMaxFP(invoke->GetLocations(), false, true, GetAssembler());
614}
615
616void IntrinsicLocationsBuilderX86::VisitMathMaxFloatFloat(HInvoke* invoke) {
617 CreateFPFPToFPLocations(arena_, invoke);
618}
619
620void IntrinsicCodeGeneratorX86::VisitMathMaxFloatFloat(HInvoke* invoke) {
621 GenMinMaxFP(invoke->GetLocations(), false, false, GetAssembler());
622}
623
624static void GenMinMax(LocationSummary* locations, bool is_min, bool is_long,
625 X86Assembler* assembler) {
626 Location op1_loc = locations->InAt(0);
627 Location op2_loc = locations->InAt(1);
628
629 // Shortcut for same input locations.
630 if (op1_loc.Equals(op2_loc)) {
631 // Can return immediately, as op1_loc == out_loc.
632 // Note: if we ever support separate registers, e.g., output into memory, we need to check for
633 // a copy here.
634 DCHECK(locations->Out().Equals(op1_loc));
635 return;
636 }
637
638 if (is_long) {
639 // Need to perform a subtract to get the sign right.
640 // op1 is already in the same location as the output.
641 Location output = locations->Out();
642 Register output_lo = output.AsRegisterPairLow<Register>();
643 Register output_hi = output.AsRegisterPairHigh<Register>();
644
645 Register op2_lo = op2_loc.AsRegisterPairLow<Register>();
646 Register op2_hi = op2_loc.AsRegisterPairHigh<Register>();
647
648 // Spare register to compute the subtraction to set condition code.
649 Register temp = locations->GetTemp(0).AsRegister<Register>();
650
651 // Subtract off op2_low.
652 __ movl(temp, output_lo);
653 __ subl(temp, op2_lo);
654
655 // Now use the same tempo and the borrow to finish the subtraction of op2_hi.
656 __ movl(temp, output_hi);
657 __ sbbl(temp, op2_hi);
658
659 // Now the condition code is correct.
660 Condition cond = is_min ? Condition::kGreaterEqual : Condition::kLess;
661 __ cmovl(cond, output_lo, op2_lo);
662 __ cmovl(cond, output_hi, op2_hi);
663 } else {
664 Register out = locations->Out().AsRegister<Register>();
665 Register op2 = op2_loc.AsRegister<Register>();
666
667 // (out := op1)
668 // out <=? op2
669 // if out is min jmp done
670 // out := op2
671 // done:
672
673 __ cmpl(out, op2);
674 Condition cond = is_min ? Condition::kGreater : Condition::kLess;
675 __ cmovl(cond, out, op2);
676 }
677}
678
679static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
680 LocationSummary* locations = new (arena) LocationSummary(invoke,
681 LocationSummary::kNoCall,
682 kIntrinsified);
683 locations->SetInAt(0, Location::RequiresRegister());
684 locations->SetInAt(1, Location::RequiresRegister());
685 locations->SetOut(Location::SameAsFirstInput());
686}
687
688static void CreateLongLongToLongLocations(ArenaAllocator* arena, HInvoke* invoke) {
689 LocationSummary* locations = new (arena) LocationSummary(invoke,
690 LocationSummary::kNoCall,
691 kIntrinsified);
692 locations->SetInAt(0, Location::RequiresRegister());
693 locations->SetInAt(1, Location::RequiresRegister());
694 locations->SetOut(Location::SameAsFirstInput());
695 // Register to use to perform a long subtract to set cc.
696 locations->AddTemp(Location::RequiresRegister());
697}
698
699void IntrinsicLocationsBuilderX86::VisitMathMinIntInt(HInvoke* invoke) {
700 CreateIntIntToIntLocations(arena_, invoke);
701}
702
703void IntrinsicCodeGeneratorX86::VisitMathMinIntInt(HInvoke* invoke) {
704 GenMinMax(invoke->GetLocations(), true, false, GetAssembler());
705}
706
707void IntrinsicLocationsBuilderX86::VisitMathMinLongLong(HInvoke* invoke) {
708 CreateLongLongToLongLocations(arena_, invoke);
709}
710
711void IntrinsicCodeGeneratorX86::VisitMathMinLongLong(HInvoke* invoke) {
712 GenMinMax(invoke->GetLocations(), true, true, GetAssembler());
713}
714
715void IntrinsicLocationsBuilderX86::VisitMathMaxIntInt(HInvoke* invoke) {
716 CreateIntIntToIntLocations(arena_, invoke);
717}
718
719void IntrinsicCodeGeneratorX86::VisitMathMaxIntInt(HInvoke* invoke) {
720 GenMinMax(invoke->GetLocations(), false, false, GetAssembler());
721}
722
723void IntrinsicLocationsBuilderX86::VisitMathMaxLongLong(HInvoke* invoke) {
724 CreateLongLongToLongLocations(arena_, invoke);
725}
726
727void IntrinsicCodeGeneratorX86::VisitMathMaxLongLong(HInvoke* invoke) {
728 GenMinMax(invoke->GetLocations(), false, true, GetAssembler());
729}
730
731static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
732 LocationSummary* locations = new (arena) LocationSummary(invoke,
733 LocationSummary::kNoCall,
734 kIntrinsified);
735 locations->SetInAt(0, Location::RequiresFpuRegister());
736 locations->SetOut(Location::RequiresFpuRegister());
737}
738
739void IntrinsicLocationsBuilderX86::VisitMathSqrt(HInvoke* invoke) {
740 CreateFPToFPLocations(arena_, invoke);
741}
742
743void IntrinsicCodeGeneratorX86::VisitMathSqrt(HInvoke* invoke) {
744 LocationSummary* locations = invoke->GetLocations();
745 XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>();
746 XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
747
748 GetAssembler()->sqrtsd(out, in);
749}
750
Mark Mendellfb8d2792015-03-31 22:16:59 -0400751static void InvokeOutOfLineIntrinsic(CodeGeneratorX86* codegen, HInvoke* invoke) {
752 MoveArguments(invoke, codegen->GetGraph()->GetArena(), codegen);
753
754 DCHECK(invoke->IsInvokeStaticOrDirect());
755 codegen->GenerateStaticOrDirectCall(invoke->AsInvokeStaticOrDirect(), EAX);
Mingyao Yange90db122015-04-03 17:56:54 -0700756 codegen->RecordPcInfo(invoke, invoke->GetDexPc());
Mark Mendellfb8d2792015-03-31 22:16:59 -0400757
758 // Copy the result back to the expected output.
759 Location out = invoke->GetLocations()->Out();
760 if (out.IsValid()) {
761 DCHECK(out.IsRegister());
762 MoveFromReturnRegister(out, invoke->GetType(), codegen);
763 }
764}
765
766static void CreateSSE41FPToFPLocations(ArenaAllocator* arena,
767 HInvoke* invoke,
768 CodeGeneratorX86* codegen) {
769 // Do we have instruction support?
770 if (codegen->GetInstructionSetFeatures().HasSSE4_1()) {
771 CreateFPToFPLocations(arena, invoke);
772 return;
773 }
774
775 // We have to fall back to a call to the intrinsic.
776 LocationSummary* locations = new (arena) LocationSummary(invoke,
777 LocationSummary::kCall);
778 InvokeRuntimeCallingConvention calling_convention;
779 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetFpuRegisterAt(0)));
780 locations->SetOut(Location::FpuRegisterLocation(XMM0));
781 // Needs to be EAX for the invoke.
782 locations->AddTemp(Location::RegisterLocation(EAX));
783}
784
785static void GenSSE41FPToFPIntrinsic(CodeGeneratorX86* codegen,
786 HInvoke* invoke,
787 X86Assembler* assembler,
788 int round_mode) {
789 LocationSummary* locations = invoke->GetLocations();
790 if (locations->WillCall()) {
791 InvokeOutOfLineIntrinsic(codegen, invoke);
792 } else {
793 XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>();
794 XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
795 __ roundsd(out, in, Immediate(round_mode));
796 }
797}
798
799void IntrinsicLocationsBuilderX86::VisitMathCeil(HInvoke* invoke) {
800 CreateSSE41FPToFPLocations(arena_, invoke, codegen_);
801}
802
803void IntrinsicCodeGeneratorX86::VisitMathCeil(HInvoke* invoke) {
804 GenSSE41FPToFPIntrinsic(codegen_, invoke, GetAssembler(), 2);
805}
806
807void IntrinsicLocationsBuilderX86::VisitMathFloor(HInvoke* invoke) {
808 CreateSSE41FPToFPLocations(arena_, invoke, codegen_);
809}
810
811void IntrinsicCodeGeneratorX86::VisitMathFloor(HInvoke* invoke) {
812 GenSSE41FPToFPIntrinsic(codegen_, invoke, GetAssembler(), 1);
813}
814
815void IntrinsicLocationsBuilderX86::VisitMathRint(HInvoke* invoke) {
816 CreateSSE41FPToFPLocations(arena_, invoke, codegen_);
817}
818
819void IntrinsicCodeGeneratorX86::VisitMathRint(HInvoke* invoke) {
820 GenSSE41FPToFPIntrinsic(codegen_, invoke, GetAssembler(), 0);
821}
822
823// Note that 32 bit x86 doesn't have the capability to inline MathRoundDouble,
824// as it needs 64 bit instructions.
825void IntrinsicLocationsBuilderX86::VisitMathRoundFloat(HInvoke* invoke) {
826 // Do we have instruction support?
827 if (codegen_->GetInstructionSetFeatures().HasSSE4_1()) {
828 LocationSummary* locations = new (arena_) LocationSummary(invoke,
829 LocationSummary::kNoCall,
830 kIntrinsified);
831 locations->SetInAt(0, Location::RequiresFpuRegister());
Nicolas Geoffrayd9b92402015-04-21 10:02:22 +0100832 locations->SetOut(Location::RequiresRegister());
Mark Mendellfb8d2792015-03-31 22:16:59 -0400833 locations->AddTemp(Location::RequiresFpuRegister());
834 locations->AddTemp(Location::RequiresFpuRegister());
835 return;
836 }
837
838 // We have to fall back to a call to the intrinsic.
839 LocationSummary* locations = new (arena_) LocationSummary(invoke,
840 LocationSummary::kCall);
841 InvokeRuntimeCallingConvention calling_convention;
842 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetFpuRegisterAt(0)));
843 locations->SetOut(Location::RegisterLocation(EAX));
844 // Needs to be EAX for the invoke.
845 locations->AddTemp(Location::RegisterLocation(EAX));
846}
847
848void IntrinsicCodeGeneratorX86::VisitMathRoundFloat(HInvoke* invoke) {
849 LocationSummary* locations = invoke->GetLocations();
850 if (locations->WillCall()) {
851 InvokeOutOfLineIntrinsic(codegen_, invoke);
852 return;
853 }
854
855 // Implement RoundFloat as t1 = floor(input + 0.5f); convert to int.
856 XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>();
857 Register out = locations->Out().AsRegister<Register>();
858 XmmRegister maxInt = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
859 XmmRegister inPlusPointFive = locations->GetTemp(1).AsFpuRegister<XmmRegister>();
860 Label done, nan;
861 X86Assembler* assembler = GetAssembler();
862
863 // Generate 0.5 into inPlusPointFive.
864 __ movl(out, Immediate(bit_cast<int32_t, float>(0.5f)));
865 __ movd(inPlusPointFive, out);
866
867 // Add in the input.
868 __ addss(inPlusPointFive, in);
869
870 // And truncate to an integer.
871 __ roundss(inPlusPointFive, inPlusPointFive, Immediate(1));
872
873 __ movl(out, Immediate(kPrimIntMax));
874 // maxInt = int-to-float(out)
875 __ cvtsi2ss(maxInt, out);
876
877 // if inPlusPointFive >= maxInt goto done
878 __ comiss(inPlusPointFive, maxInt);
879 __ j(kAboveEqual, &done);
880
881 // if input == NaN goto nan
882 __ j(kUnordered, &nan);
883
884 // output = float-to-int-truncate(input)
885 __ cvttss2si(out, inPlusPointFive);
886 __ jmp(&done);
887 __ Bind(&nan);
888
889 // output = 0
890 __ xorl(out, out);
891 __ Bind(&done);
892}
893
Mark Mendell09ed1a32015-03-25 08:30:06 -0400894void IntrinsicLocationsBuilderX86::VisitStringCharAt(HInvoke* invoke) {
895 // The inputs plus one temp.
896 LocationSummary* locations = new (arena_) LocationSummary(invoke,
897 LocationSummary::kCallOnSlowPath,
898 kIntrinsified);
899 locations->SetInAt(0, Location::RequiresRegister());
900 locations->SetInAt(1, Location::RequiresRegister());
901 locations->SetOut(Location::SameAsFirstInput());
902 // Needs to be EAX for the invoke.
903 locations->AddTemp(Location::RegisterLocation(EAX));
904}
905
906void IntrinsicCodeGeneratorX86::VisitStringCharAt(HInvoke* invoke) {
907 LocationSummary* locations = invoke->GetLocations();
908
909 // Location of reference to data array
910 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
911 // Location of count
912 const int32_t count_offset = mirror::String::CountOffset().Int32Value();
913 // Starting offset within data array
914 const int32_t offset_offset = mirror::String::OffsetOffset().Int32Value();
915 // Start of char data with array_
916 const int32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value();
917
918 Register obj = locations->InAt(0).AsRegister<Register>();
919 Register idx = locations->InAt(1).AsRegister<Register>();
920 Register out = locations->Out().AsRegister<Register>();
921 Location temp_loc = locations->GetTemp(0);
922 Register temp = temp_loc.AsRegister<Register>();
923
924 // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
925 // the cost.
926 // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
927 // we will not optimize the code for constants (which would save a register).
928
929 SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke, temp);
930 codegen_->AddSlowPath(slow_path);
931
932 X86Assembler* assembler = GetAssembler();
933
934 __ cmpl(idx, Address(obj, count_offset));
935 codegen_->MaybeRecordImplicitNullCheck(invoke);
936 __ j(kAboveEqual, slow_path->GetEntryLabel());
937
938 // Get the actual element.
939 __ movl(temp, idx); // temp := idx.
940 __ addl(temp, Address(obj, offset_offset)); // temp := offset + idx.
941 __ movl(out, Address(obj, value_offset)); // obj := obj.array.
942 // out = out[2*temp].
943 __ movzxw(out, Address(out, temp, ScaleFactor::TIMES_2, data_offset));
944
945 __ Bind(slow_path->GetExitLabel());
946}
947
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +0000948void IntrinsicLocationsBuilderX86::VisitStringCompareTo(HInvoke* invoke) {
949 // The inputs plus one temp.
950 LocationSummary* locations = new (arena_) LocationSummary(invoke,
951 LocationSummary::kCall,
952 kIntrinsified);
953 InvokeRuntimeCallingConvention calling_convention;
954 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
955 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
956 locations->SetOut(Location::RegisterLocation(EAX));
957 // Needs to be EAX for the invoke.
958 locations->AddTemp(Location::RegisterLocation(EAX));
959}
960
961void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) {
962 X86Assembler* assembler = GetAssembler();
963 LocationSummary* locations = invoke->GetLocations();
964
Nicolas Geoffray512e04d2015-03-27 17:21:24 +0000965 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +0100966 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +0000967
968 Register argument = locations->InAt(1).AsRegister<Register>();
969 __ testl(argument, argument);
970 SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(
971 invoke, locations->GetTemp(0).AsRegister<Register>());
972 codegen_->AddSlowPath(slow_path);
973 __ j(kEqual, slow_path->GetEntryLabel());
974
975 __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pStringCompareTo)));
976 __ Bind(slow_path->GetExitLabel());
977}
978
Mark Mendell09ed1a32015-03-25 08:30:06 -0400979static void GenPeek(LocationSummary* locations, Primitive::Type size, X86Assembler* assembler) {
980 Register address = locations->InAt(0).AsRegisterPairLow<Register>();
981 Location out_loc = locations->Out();
982 // x86 allows unaligned access. We do not have to check the input or use specific instructions
983 // to avoid a SIGBUS.
984 switch (size) {
985 case Primitive::kPrimByte:
986 __ movsxb(out_loc.AsRegister<Register>(), Address(address, 0));
987 break;
988 case Primitive::kPrimShort:
989 __ movsxw(out_loc.AsRegister<Register>(), Address(address, 0));
990 break;
991 case Primitive::kPrimInt:
992 __ movl(out_loc.AsRegister<Register>(), Address(address, 0));
993 break;
994 case Primitive::kPrimLong:
995 __ movl(out_loc.AsRegisterPairLow<Register>(), Address(address, 0));
996 __ movl(out_loc.AsRegisterPairHigh<Register>(), Address(address, 4));
997 break;
998 default:
999 LOG(FATAL) << "Type not recognized for peek: " << size;
1000 UNREACHABLE();
1001 }
1002}
1003
1004void IntrinsicLocationsBuilderX86::VisitMemoryPeekByte(HInvoke* invoke) {
1005 CreateLongToIntLocations(arena_, invoke);
1006}
1007
1008void IntrinsicCodeGeneratorX86::VisitMemoryPeekByte(HInvoke* invoke) {
1009 GenPeek(invoke->GetLocations(), Primitive::kPrimByte, GetAssembler());
1010}
1011
1012void IntrinsicLocationsBuilderX86::VisitMemoryPeekIntNative(HInvoke* invoke) {
1013 CreateLongToIntLocations(arena_, invoke);
1014}
1015
1016void IntrinsicCodeGeneratorX86::VisitMemoryPeekIntNative(HInvoke* invoke) {
1017 GenPeek(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
1018}
1019
1020void IntrinsicLocationsBuilderX86::VisitMemoryPeekLongNative(HInvoke* invoke) {
1021 CreateLongToLongLocations(arena_, invoke);
1022}
1023
1024void IntrinsicCodeGeneratorX86::VisitMemoryPeekLongNative(HInvoke* invoke) {
1025 GenPeek(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
1026}
1027
1028void IntrinsicLocationsBuilderX86::VisitMemoryPeekShortNative(HInvoke* invoke) {
1029 CreateLongToIntLocations(arena_, invoke);
1030}
1031
1032void IntrinsicCodeGeneratorX86::VisitMemoryPeekShortNative(HInvoke* invoke) {
1033 GenPeek(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler());
1034}
1035
1036static void CreateLongIntToVoidLocations(ArenaAllocator* arena, Primitive::Type size,
1037 HInvoke* invoke) {
1038 LocationSummary* locations = new (arena) LocationSummary(invoke,
1039 LocationSummary::kNoCall,
1040 kIntrinsified);
1041 locations->SetInAt(0, Location::RequiresRegister());
Roland Levillain4c0eb422015-04-24 16:43:49 +01001042 HInstruction* value = invoke->InputAt(1);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001043 if (size == Primitive::kPrimByte) {
1044 locations->SetInAt(1, Location::ByteRegisterOrConstant(EDX, value));
1045 } else {
1046 locations->SetInAt(1, Location::RegisterOrConstant(value));
1047 }
1048}
1049
1050static void GenPoke(LocationSummary* locations, Primitive::Type size, X86Assembler* assembler) {
1051 Register address = locations->InAt(0).AsRegisterPairLow<Register>();
1052 Location value_loc = locations->InAt(1);
1053 // x86 allows unaligned access. We do not have to check the input or use specific instructions
1054 // to avoid a SIGBUS.
1055 switch (size) {
1056 case Primitive::kPrimByte:
1057 if (value_loc.IsConstant()) {
1058 __ movb(Address(address, 0),
1059 Immediate(value_loc.GetConstant()->AsIntConstant()->GetValue()));
1060 } else {
1061 __ movb(Address(address, 0), value_loc.AsRegister<ByteRegister>());
1062 }
1063 break;
1064 case Primitive::kPrimShort:
1065 if (value_loc.IsConstant()) {
1066 __ movw(Address(address, 0),
1067 Immediate(value_loc.GetConstant()->AsIntConstant()->GetValue()));
1068 } else {
1069 __ movw(Address(address, 0), value_loc.AsRegister<Register>());
1070 }
1071 break;
1072 case Primitive::kPrimInt:
1073 if (value_loc.IsConstant()) {
1074 __ movl(Address(address, 0),
1075 Immediate(value_loc.GetConstant()->AsIntConstant()->GetValue()));
1076 } else {
1077 __ movl(Address(address, 0), value_loc.AsRegister<Register>());
1078 }
1079 break;
1080 case Primitive::kPrimLong:
1081 if (value_loc.IsConstant()) {
1082 int64_t value = value_loc.GetConstant()->AsLongConstant()->GetValue();
1083 __ movl(Address(address, 0), Immediate(Low32Bits(value)));
1084 __ movl(Address(address, 4), Immediate(High32Bits(value)));
1085 } else {
1086 __ movl(Address(address, 0), value_loc.AsRegisterPairLow<Register>());
1087 __ movl(Address(address, 4), value_loc.AsRegisterPairHigh<Register>());
1088 }
1089 break;
1090 default:
1091 LOG(FATAL) << "Type not recognized for poke: " << size;
1092 UNREACHABLE();
1093 }
1094}
1095
1096void IntrinsicLocationsBuilderX86::VisitMemoryPokeByte(HInvoke* invoke) {
1097 CreateLongIntToVoidLocations(arena_, Primitive::kPrimByte, invoke);
1098}
1099
1100void IntrinsicCodeGeneratorX86::VisitMemoryPokeByte(HInvoke* invoke) {
1101 GenPoke(invoke->GetLocations(), Primitive::kPrimByte, GetAssembler());
1102}
1103
1104void IntrinsicLocationsBuilderX86::VisitMemoryPokeIntNative(HInvoke* invoke) {
1105 CreateLongIntToVoidLocations(arena_, Primitive::kPrimInt, invoke);
1106}
1107
1108void IntrinsicCodeGeneratorX86::VisitMemoryPokeIntNative(HInvoke* invoke) {
1109 GenPoke(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
1110}
1111
1112void IntrinsicLocationsBuilderX86::VisitMemoryPokeLongNative(HInvoke* invoke) {
1113 CreateLongIntToVoidLocations(arena_, Primitive::kPrimLong, invoke);
1114}
1115
1116void IntrinsicCodeGeneratorX86::VisitMemoryPokeLongNative(HInvoke* invoke) {
1117 GenPoke(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
1118}
1119
1120void IntrinsicLocationsBuilderX86::VisitMemoryPokeShortNative(HInvoke* invoke) {
1121 CreateLongIntToVoidLocations(arena_, Primitive::kPrimShort, invoke);
1122}
1123
1124void IntrinsicCodeGeneratorX86::VisitMemoryPokeShortNative(HInvoke* invoke) {
1125 GenPoke(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler());
1126}
1127
1128void IntrinsicLocationsBuilderX86::VisitThreadCurrentThread(HInvoke* invoke) {
1129 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1130 LocationSummary::kNoCall,
1131 kIntrinsified);
1132 locations->SetOut(Location::RequiresRegister());
1133}
1134
1135void IntrinsicCodeGeneratorX86::VisitThreadCurrentThread(HInvoke* invoke) {
1136 Register out = invoke->GetLocations()->Out().AsRegister<Register>();
1137 GetAssembler()->fs()->movl(out, Address::Absolute(Thread::PeerOffset<kX86WordSize>()));
1138}
1139
1140static void GenUnsafeGet(LocationSummary* locations, Primitive::Type type,
1141 bool is_volatile, X86Assembler* assembler) {
1142 Register base = locations->InAt(1).AsRegister<Register>();
1143 Register offset = locations->InAt(2).AsRegisterPairLow<Register>();
1144 Location output = locations->Out();
1145
1146 switch (type) {
1147 case Primitive::kPrimInt:
1148 case Primitive::kPrimNot:
1149 __ movl(output.AsRegister<Register>(), Address(base, offset, ScaleFactor::TIMES_1, 0));
1150 break;
1151
1152 case Primitive::kPrimLong: {
1153 Register output_lo = output.AsRegisterPairLow<Register>();
1154 Register output_hi = output.AsRegisterPairHigh<Register>();
1155 if (is_volatile) {
1156 // Need to use a XMM to read atomically.
1157 XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
1158 __ movsd(temp, Address(base, offset, ScaleFactor::TIMES_1, 0));
1159 __ movd(output_lo, temp);
1160 __ psrlq(temp, Immediate(32));
1161 __ movd(output_hi, temp);
1162 } else {
1163 __ movl(output_lo, Address(base, offset, ScaleFactor::TIMES_1, 0));
1164 __ movl(output_hi, Address(base, offset, ScaleFactor::TIMES_1, 4));
1165 }
1166 }
1167 break;
1168
1169 default:
1170 LOG(FATAL) << "Unsupported op size " << type;
1171 UNREACHABLE();
1172 }
1173}
1174
1175static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke,
1176 bool is_long, bool is_volatile) {
1177 LocationSummary* locations = new (arena) LocationSummary(invoke,
1178 LocationSummary::kNoCall,
1179 kIntrinsified);
1180 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1181 locations->SetInAt(1, Location::RequiresRegister());
1182 locations->SetInAt(2, Location::RequiresRegister());
1183 if (is_long) {
1184 if (is_volatile) {
1185 // Need to use XMM to read volatile.
1186 locations->AddTemp(Location::RequiresFpuRegister());
1187 locations->SetOut(Location::RequiresRegister());
1188 } else {
1189 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1190 }
1191 } else {
1192 locations->SetOut(Location::RequiresRegister());
1193 }
1194}
1195
1196void IntrinsicLocationsBuilderX86::VisitUnsafeGet(HInvoke* invoke) {
1197 CreateIntIntIntToIntLocations(arena_, invoke, false, false);
1198}
1199void IntrinsicLocationsBuilderX86::VisitUnsafeGetVolatile(HInvoke* invoke) {
1200 CreateIntIntIntToIntLocations(arena_, invoke, false, true);
1201}
1202void IntrinsicLocationsBuilderX86::VisitUnsafeGetLong(HInvoke* invoke) {
1203 CreateIntIntIntToIntLocations(arena_, invoke, false, false);
1204}
1205void IntrinsicLocationsBuilderX86::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
1206 CreateIntIntIntToIntLocations(arena_, invoke, true, true);
1207}
1208void IntrinsicLocationsBuilderX86::VisitUnsafeGetObject(HInvoke* invoke) {
1209 CreateIntIntIntToIntLocations(arena_, invoke, false, false);
1210}
1211void IntrinsicLocationsBuilderX86::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
1212 CreateIntIntIntToIntLocations(arena_, invoke, false, true);
1213}
1214
1215
1216void IntrinsicCodeGeneratorX86::VisitUnsafeGet(HInvoke* invoke) {
1217 GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimInt, false, GetAssembler());
1218}
1219void IntrinsicCodeGeneratorX86::VisitUnsafeGetVolatile(HInvoke* invoke) {
1220 GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimInt, true, GetAssembler());
1221}
1222void IntrinsicCodeGeneratorX86::VisitUnsafeGetLong(HInvoke* invoke) {
1223 GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimLong, false, GetAssembler());
1224}
1225void IntrinsicCodeGeneratorX86::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
1226 GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimLong, true, GetAssembler());
1227}
1228void IntrinsicCodeGeneratorX86::VisitUnsafeGetObject(HInvoke* invoke) {
1229 GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimNot, false, GetAssembler());
1230}
1231void IntrinsicCodeGeneratorX86::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
1232 GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimNot, true, GetAssembler());
1233}
1234
1235
1236static void CreateIntIntIntIntToVoidPlusTempsLocations(ArenaAllocator* arena,
1237 Primitive::Type type,
1238 HInvoke* invoke,
1239 bool is_volatile) {
1240 LocationSummary* locations = new (arena) LocationSummary(invoke,
1241 LocationSummary::kNoCall,
1242 kIntrinsified);
1243 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1244 locations->SetInAt(1, Location::RequiresRegister());
1245 locations->SetInAt(2, Location::RequiresRegister());
1246 locations->SetInAt(3, Location::RequiresRegister());
1247 if (type == Primitive::kPrimNot) {
1248 // Need temp registers for card-marking.
1249 locations->AddTemp(Location::RequiresRegister());
1250 // Ensure the value is in a byte register.
1251 locations->AddTemp(Location::RegisterLocation(ECX));
1252 } else if (type == Primitive::kPrimLong && is_volatile) {
1253 locations->AddTemp(Location::RequiresFpuRegister());
1254 locations->AddTemp(Location::RequiresFpuRegister());
1255 }
1256}
1257
1258void IntrinsicLocationsBuilderX86::VisitUnsafePut(HInvoke* invoke) {
1259 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimInt, invoke, false);
1260}
1261void IntrinsicLocationsBuilderX86::VisitUnsafePutOrdered(HInvoke* invoke) {
1262 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimInt, invoke, false);
1263}
1264void IntrinsicLocationsBuilderX86::VisitUnsafePutVolatile(HInvoke* invoke) {
1265 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimInt, invoke, true);
1266}
1267void IntrinsicLocationsBuilderX86::VisitUnsafePutObject(HInvoke* invoke) {
1268 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimNot, invoke, false);
1269}
1270void IntrinsicLocationsBuilderX86::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
1271 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimNot, invoke, false);
1272}
1273void IntrinsicLocationsBuilderX86::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
1274 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimNot, invoke, true);
1275}
1276void IntrinsicLocationsBuilderX86::VisitUnsafePutLong(HInvoke* invoke) {
1277 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimLong, invoke, false);
1278}
1279void IntrinsicLocationsBuilderX86::VisitUnsafePutLongOrdered(HInvoke* invoke) {
1280 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimLong, invoke, false);
1281}
1282void IntrinsicLocationsBuilderX86::VisitUnsafePutLongVolatile(HInvoke* invoke) {
1283 CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimLong, invoke, true);
1284}
1285
1286// We don't care for ordered: it requires an AnyStore barrier, which is already given by the x86
1287// memory model.
1288static void GenUnsafePut(LocationSummary* locations,
1289 Primitive::Type type,
1290 bool is_volatile,
1291 CodeGeneratorX86* codegen) {
1292 X86Assembler* assembler = reinterpret_cast<X86Assembler*>(codegen->GetAssembler());
1293 Register base = locations->InAt(1).AsRegister<Register>();
1294 Register offset = locations->InAt(2).AsRegisterPairLow<Register>();
1295 Location value_loc = locations->InAt(3);
1296
1297 if (type == Primitive::kPrimLong) {
1298 Register value_lo = value_loc.AsRegisterPairLow<Register>();
1299 Register value_hi = value_loc.AsRegisterPairHigh<Register>();
1300 if (is_volatile) {
1301 XmmRegister temp1 = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
1302 XmmRegister temp2 = locations->GetTemp(1).AsFpuRegister<XmmRegister>();
1303 __ movd(temp1, value_lo);
1304 __ movd(temp2, value_hi);
1305 __ punpckldq(temp1, temp2);
1306 __ movsd(Address(base, offset, ScaleFactor::TIMES_1, 0), temp1);
1307 } else {
1308 __ movl(Address(base, offset, ScaleFactor::TIMES_1, 0), value_lo);
1309 __ movl(Address(base, offset, ScaleFactor::TIMES_1, 4), value_hi);
1310 }
1311 } else {
1312 __ movl(Address(base, offset, ScaleFactor::TIMES_1, 0), value_loc.AsRegister<Register>());
1313 }
1314
1315 if (is_volatile) {
1316 __ mfence();
1317 }
1318
1319 if (type == Primitive::kPrimNot) {
1320 codegen->MarkGCCard(locations->GetTemp(0).AsRegister<Register>(),
1321 locations->GetTemp(1).AsRegister<Register>(),
1322 base,
1323 value_loc.AsRegister<Register>());
1324 }
1325}
1326
1327void IntrinsicCodeGeneratorX86::VisitUnsafePut(HInvoke* invoke) {
1328 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, codegen_);
1329}
1330void IntrinsicCodeGeneratorX86::VisitUnsafePutOrdered(HInvoke* invoke) {
1331 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, codegen_);
1332}
1333void IntrinsicCodeGeneratorX86::VisitUnsafePutVolatile(HInvoke* invoke) {
1334 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, true, codegen_);
1335}
1336void IntrinsicCodeGeneratorX86::VisitUnsafePutObject(HInvoke* invoke) {
1337 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, codegen_);
1338}
1339void IntrinsicCodeGeneratorX86::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
1340 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, codegen_);
1341}
1342void IntrinsicCodeGeneratorX86::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
1343 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, true, codegen_);
1344}
1345void IntrinsicCodeGeneratorX86::VisitUnsafePutLong(HInvoke* invoke) {
1346 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, codegen_);
1347}
1348void IntrinsicCodeGeneratorX86::VisitUnsafePutLongOrdered(HInvoke* invoke) {
1349 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, codegen_);
1350}
1351void IntrinsicCodeGeneratorX86::VisitUnsafePutLongVolatile(HInvoke* invoke) {
1352 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, codegen_);
1353}
1354
Mark Mendell58d25fd2015-04-03 14:52:31 -04001355static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, Primitive::Type type,
1356 HInvoke* invoke) {
1357 LocationSummary* locations = new (arena) LocationSummary(invoke,
1358 LocationSummary::kNoCall,
1359 kIntrinsified);
1360 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1361 locations->SetInAt(1, Location::RequiresRegister());
1362 // Offset is a long, but in 32 bit mode, we only need the low word.
1363 // Can we update the invoke here to remove a TypeConvert to Long?
1364 locations->SetInAt(2, Location::RequiresRegister());
1365 // Expected value must be in EAX or EDX:EAX.
1366 // For long, new value must be in ECX:EBX.
1367 if (type == Primitive::kPrimLong) {
1368 locations->SetInAt(3, Location::RegisterPairLocation(EAX, EDX));
1369 locations->SetInAt(4, Location::RegisterPairLocation(EBX, ECX));
1370 } else {
1371 locations->SetInAt(3, Location::RegisterLocation(EAX));
1372 locations->SetInAt(4, Location::RequiresRegister());
1373 }
1374
1375 // Force a byte register for the output.
1376 locations->SetOut(Location::RegisterLocation(EAX));
1377 if (type == Primitive::kPrimNot) {
1378 // Need temp registers for card-marking.
1379 locations->AddTemp(Location::RequiresRegister());
1380 // Need a byte register for marking.
1381 locations->AddTemp(Location::RegisterLocation(ECX));
1382 }
1383}
1384
1385void IntrinsicLocationsBuilderX86::VisitUnsafeCASInt(HInvoke* invoke) {
1386 CreateIntIntIntIntIntToInt(arena_, Primitive::kPrimInt, invoke);
1387}
1388
1389void IntrinsicLocationsBuilderX86::VisitUnsafeCASLong(HInvoke* invoke) {
1390 CreateIntIntIntIntIntToInt(arena_, Primitive::kPrimLong, invoke);
1391}
1392
1393void IntrinsicLocationsBuilderX86::VisitUnsafeCASObject(HInvoke* invoke) {
1394 CreateIntIntIntIntIntToInt(arena_, Primitive::kPrimNot, invoke);
1395}
1396
1397static void GenCAS(Primitive::Type type, HInvoke* invoke, CodeGeneratorX86* codegen) {
1398 X86Assembler* assembler =
1399 reinterpret_cast<X86Assembler*>(codegen->GetAssembler());
1400 LocationSummary* locations = invoke->GetLocations();
1401
1402 Register base = locations->InAt(1).AsRegister<Register>();
1403 Register offset = locations->InAt(2).AsRegisterPairLow<Register>();
1404 Location out = locations->Out();
1405 DCHECK_EQ(out.AsRegister<Register>(), EAX);
1406
1407 if (type == Primitive::kPrimLong) {
1408 DCHECK_EQ(locations->InAt(3).AsRegisterPairLow<Register>(), EAX);
1409 DCHECK_EQ(locations->InAt(3).AsRegisterPairHigh<Register>(), EDX);
1410 DCHECK_EQ(locations->InAt(4).AsRegisterPairLow<Register>(), EBX);
1411 DCHECK_EQ(locations->InAt(4).AsRegisterPairHigh<Register>(), ECX);
1412 __ LockCmpxchg8b(Address(base, offset, TIMES_1, 0));
1413 } else {
1414 // Integer or object.
1415 DCHECK_EQ(locations->InAt(3).AsRegister<Register>(), EAX);
1416 Register value = locations->InAt(4).AsRegister<Register>();
1417 if (type == Primitive::kPrimNot) {
1418 // Mark card for object assuming new value is stored.
1419 codegen->MarkGCCard(locations->GetTemp(0).AsRegister<Register>(),
1420 locations->GetTemp(1).AsRegister<Register>(),
1421 base,
1422 value);
1423 }
1424
1425 __ LockCmpxchgl(Address(base, offset, TIMES_1, 0), value);
1426 }
1427
1428 // locked cmpxchg has full barrier semantics, and we don't need scheduling
1429 // barriers at this time.
1430
1431 // Convert ZF into the boolean result.
1432 __ setb(kZero, out.AsRegister<Register>());
1433 __ movzxb(out.AsRegister<Register>(), out.AsRegister<ByteRegister>());
1434}
1435
1436void IntrinsicCodeGeneratorX86::VisitUnsafeCASInt(HInvoke* invoke) {
1437 GenCAS(Primitive::kPrimInt, invoke, codegen_);
1438}
1439
1440void IntrinsicCodeGeneratorX86::VisitUnsafeCASLong(HInvoke* invoke) {
1441 GenCAS(Primitive::kPrimLong, invoke, codegen_);
1442}
1443
1444void IntrinsicCodeGeneratorX86::VisitUnsafeCASObject(HInvoke* invoke) {
1445 GenCAS(Primitive::kPrimNot, invoke, codegen_);
1446}
1447
1448void IntrinsicLocationsBuilderX86::VisitIntegerReverse(HInvoke* invoke) {
1449 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1450 LocationSummary::kNoCall,
1451 kIntrinsified);
1452 locations->SetInAt(0, Location::RequiresRegister());
1453 locations->SetOut(Location::SameAsFirstInput());
1454 locations->AddTemp(Location::RequiresRegister());
1455}
1456
1457static void SwapBits(Register reg, Register temp, int32_t shift, int32_t mask,
1458 X86Assembler* assembler) {
1459 Immediate imm_shift(shift);
1460 Immediate imm_mask(mask);
1461 __ movl(temp, reg);
1462 __ shrl(reg, imm_shift);
1463 __ andl(temp, imm_mask);
1464 __ andl(reg, imm_mask);
1465 __ shll(temp, imm_shift);
1466 __ orl(reg, temp);
1467}
1468
1469void IntrinsicCodeGeneratorX86::VisitIntegerReverse(HInvoke* invoke) {
1470 X86Assembler* assembler =
1471 reinterpret_cast<X86Assembler*>(codegen_->GetAssembler());
1472 LocationSummary* locations = invoke->GetLocations();
1473
1474 Register reg = locations->InAt(0).AsRegister<Register>();
1475 Register temp = locations->GetTemp(0).AsRegister<Register>();
1476
1477 /*
1478 * Use one bswap instruction to reverse byte order first and then use 3 rounds of
1479 * swapping bits to reverse bits in a number x. Using bswap to save instructions
1480 * compared to generic luni implementation which has 5 rounds of swapping bits.
1481 * x = bswap x
1482 * x = (x & 0x55555555) << 1 | (x >> 1) & 0x55555555;
1483 * x = (x & 0x33333333) << 2 | (x >> 2) & 0x33333333;
1484 * x = (x & 0x0F0F0F0F) << 4 | (x >> 4) & 0x0F0F0F0F;
1485 */
1486 __ bswapl(reg);
1487 SwapBits(reg, temp, 1, 0x55555555, assembler);
1488 SwapBits(reg, temp, 2, 0x33333333, assembler);
1489 SwapBits(reg, temp, 4, 0x0f0f0f0f, assembler);
1490}
1491
1492void IntrinsicLocationsBuilderX86::VisitLongReverse(HInvoke* invoke) {
1493 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1494 LocationSummary::kNoCall,
1495 kIntrinsified);
1496 locations->SetInAt(0, Location::RequiresRegister());
1497 locations->SetOut(Location::SameAsFirstInput());
1498 locations->AddTemp(Location::RequiresRegister());
1499}
1500
1501void IntrinsicCodeGeneratorX86::VisitLongReverse(HInvoke* invoke) {
1502 X86Assembler* assembler =
1503 reinterpret_cast<X86Assembler*>(codegen_->GetAssembler());
1504 LocationSummary* locations = invoke->GetLocations();
1505
1506 Register reg_low = locations->InAt(0).AsRegisterPairLow<Register>();
1507 Register reg_high = locations->InAt(0).AsRegisterPairHigh<Register>();
1508 Register temp = locations->GetTemp(0).AsRegister<Register>();
1509
1510 // We want to swap high/low, then bswap each one, and then do the same
1511 // as a 32 bit reverse.
1512 // Exchange high and low.
1513 __ movl(temp, reg_low);
1514 __ movl(reg_low, reg_high);
1515 __ movl(reg_high, temp);
1516
1517 // bit-reverse low
1518 __ bswapl(reg_low);
1519 SwapBits(reg_low, temp, 1, 0x55555555, assembler);
1520 SwapBits(reg_low, temp, 2, 0x33333333, assembler);
1521 SwapBits(reg_low, temp, 4, 0x0f0f0f0f, assembler);
1522
1523 // bit-reverse high
1524 __ bswapl(reg_high);
1525 SwapBits(reg_high, temp, 1, 0x55555555, assembler);
1526 SwapBits(reg_high, temp, 2, 0x33333333, assembler);
1527 SwapBits(reg_high, temp, 4, 0x0f0f0f0f, assembler);
1528}
1529
Mark Mendell09ed1a32015-03-25 08:30:06 -04001530// Unimplemented intrinsics.
1531
1532#define UNIMPLEMENTED_INTRINSIC(Name) \
1533void IntrinsicLocationsBuilderX86::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
1534} \
1535void IntrinsicCodeGeneratorX86::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
1536}
1537
Mark Mendell09ed1a32015-03-25 08:30:06 -04001538UNIMPLEMENTED_INTRINSIC(MathRoundDouble)
Mark Mendell09ed1a32015-03-25 08:30:06 -04001539UNIMPLEMENTED_INTRINSIC(StringIndexOf)
1540UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)
1541UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
Mark Mendell09ed1a32015-03-25 08:30:06 -04001542UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
1543
1544} // namespace x86
1545} // namespace art