Flutter Engine
The Flutter Engine
il_x64.cc
Go to the documentation of this file.
1// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "platform/globals.h"
6#include "vm/globals.h" // Needed here to get TARGET_ARCH_X64.
7#if defined(TARGET_ARCH_X64)
8
10
11#include "platform/assert.h"
13#include "vm/class_id.h"
23#include "vm/dart_entry.h"
24#include "vm/instructions.h"
25#include "vm/object_store.h"
26#include "vm/parser.h"
27#include "vm/stack_frame.h"
28#include "vm/stub_code.h"
29#include "vm/symbols.h"
31
32#define __ compiler->assembler()->
33#define Z (compiler->zone())
34
35namespace dart {
36
37// Generic summary for call instructions that have all arguments pushed
38// on the stack and return the result in a fixed register RAX (or XMM0 if
39// the return type is double).
40LocationSummary* Instruction::MakeCallSummary(Zone* zone,
41 const Instruction* instr,
42 LocationSummary* locs) {
43 ASSERT(locs == nullptr || locs->always_calls());
44 LocationSummary* result =
45 ((locs == nullptr)
46 ? (new (zone) LocationSummary(zone, 0, 0, LocationSummary::kCall))
47 : locs);
48 const auto representation = instr->representation();
49 switch (representation) {
50 case kTagged:
51 case kUntagged:
52 case kUnboxedInt64:
53 result->set_out(
55 break;
56 case kPairOfTagged:
57 result->set_out(
62 break;
63 case kUnboxedDouble:
64 result->set_out(
66 break;
67 default:
69 break;
70 }
71 return result;
72}
73
74LocationSummary* LoadIndexedUnsafeInstr::MakeLocationSummary(Zone* zone,
75 bool opt) const {
76 const intptr_t kNumInputs = 1;
77 const intptr_t kNumTemps = 0;
78 LocationSummary* locs = new (zone)
79 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
80
82 switch (representation()) {
83 case kTagged:
84 case kUnboxedInt64:
86 break;
87 case kUnboxedDouble:
89 break;
90 default:
92 break;
93 }
94 return locs;
95}
96
97void LoadIndexedUnsafeInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
98 ASSERT(RequiredInputRepresentation(0) == kTagged); // It is a Smi.
99 ASSERT(kSmiTag == 0);
100 ASSERT(kSmiTagSize == 1);
101
102 const Register index = locs()->in(0).reg();
103#if defined(DART_COMPRESSED_POINTERS)
104 // No addressing mode will ignore the upper bits. Cannot use the shorter `orl`
105 // to clear the upper bits as this instructions uses negative indices as part
106 // of FP-relative loads.
107 // TODO(compressed-pointers): Can we guarantee the index is already
108 // sign-extended if always comes for an args-descriptor load?
109 __ movsxd(index, index);
110#endif
111
112 switch (representation()) {
113 case kTagged:
114 case kUnboxedInt64: {
115 const auto out = locs()->out(0).reg();
116 __ movq(out, compiler::Address(base_reg(), index, TIMES_4, offset()));
117 break;
118 }
119 case kUnboxedDouble: {
120 const auto out = locs()->out(0).fpu_reg();
121 __ movsd(out, compiler::Address(base_reg(), index, TIMES_4, offset()));
122 break;
123 }
124 default:
125 UNREACHABLE();
126 break;
127 }
128}
129
130DEFINE_BACKEND(StoreIndexedUnsafe,
131 (NoLocation, Register index, Register value)) {
132 ASSERT(instr->RequiredInputRepresentation(
133 StoreIndexedUnsafeInstr::kIndexPos) == kTagged); // It is a Smi.
134#if defined(DART_COMPRESSED_POINTERS)
135 // No addressing mode will ignore the upper bits. Cannot use the shorter `orl`
136 // to clear the upper bits as this instructions uses negative indices as part
137 // of FP-relative stores.
138 // TODO(compressed-pointers): Can we guarantee the index is already
139 // sign-extended if always comes for an args-descriptor load?
140 __ movsxd(index, index);
141#endif
142 __ movq(compiler::Address(instr->base_reg(), index, TIMES_4, instr->offset()),
143 value);
144
145 ASSERT(kSmiTag == 0);
146 ASSERT(kSmiTagSize == 1);
147}
148
149DEFINE_BACKEND(TailCall, (NoLocation, Fixed<Register, ARGS_DESC_REG>)) {
150 compiler->EmitTailCallToStub(instr->code());
151
152 // Even though the TailCallInstr will be the last instruction in a basic
153 // block, the flow graph compiler will emit native code for other blocks after
154 // the one containing this instruction and needs to be able to use the pool.
155 // (The `LeaveDartFrame` above disables usages of the pool.)
156 __ set_constant_pool_allowed(true);
157}
158
159LocationSummary* MemoryCopyInstr::MakeLocationSummary(Zone* zone,
160 bool opt) const {
161 // The compiler must optimize any function that includes a MemoryCopy
162 // instruction that uses typed data cids, since extracting the payload address
163 // from views is done in a compiler pass after all code motion has happened.
164 ASSERT((!IsTypedDataBaseClassId(src_cid_) &&
165 !IsTypedDataBaseClassId(dest_cid_)) ||
166 opt);
167 const intptr_t kNumInputs = 5;
168 const intptr_t kNumTemps = 2;
169 LocationSummary* locs = new (zone)
170 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
173 const bool needs_writable_inputs =
174 (((element_size_ == 1) && !unboxed_inputs_) ||
175 ((element_size_ == 16) && unboxed_inputs_));
177 needs_writable_inputs
181 needs_writable_inputs
184 if (length()->BindsToSmiConstant() && length()->BoundSmiConstant() <= 4) {
185 locs->set_in(
188 length()->definition()->OriginalDefinition()->AsConstant()));
189 } else {
191 }
192 // Used for the actual iteration.
195 return locs;
196}
197
198static inline intptr_t SizeOfMemoryCopyElements(intptr_t element_size) {
199 return Utils::Minimum<intptr_t>(element_size, compiler::target::kWordSize);
200}
201
203 Register length_reg,
204 compiler::Label* done) {
205 const intptr_t mov_size = SizeOfMemoryCopyElements(element_size_);
206
207 // We want to convert the value in length_reg to an unboxed length in
208 // terms of mov_size-sized elements.
209 const intptr_t shift = Utils::ShiftForPowerOfTwo(element_size_) -
210 Utils::ShiftForPowerOfTwo(mov_size) -
212 if (shift < 0) {
213 ASSERT_EQUAL(shift, -kSmiTagShift);
214 __ SmiUntag(length_reg);
215 } else if (shift > 0) {
216 __ OBJ(shl)(length_reg, compiler::Immediate(shift));
217 } else {
218 __ ExtendNonNegativeSmi(length_reg);
219 }
220}
221
222void MemoryCopyInstr::EmitLoopCopy(FlowGraphCompiler* compiler,
223 Register dest_reg,
224 Register src_reg,
225 Register length_reg,
226 compiler::Label* done,
227 compiler::Label* copy_forwards) {
228 const intptr_t mov_size = SizeOfMemoryCopyElements(element_size_);
229 const bool reversed = copy_forwards != nullptr;
230 if (reversed) {
231 // Avoid doing the extra work to prepare for the rep mov instructions
232 // if the length to copy is zero.
233 __ BranchIfZero(length_reg, done);
234 // Verify that the overlap actually exists by checking to see if
235 // the first element in dest <= the last element in src.
236 const ScaleFactor scale = ToScaleFactor(mov_size, /*index_unboxed=*/true);
237 __ leaq(TMP, compiler::Address(src_reg, length_reg, scale, -mov_size));
238 __ CompareRegisters(dest_reg, TMP);
239 const auto jump_distance = FLAG_target_memory_sanitizer
242 __ BranchIf(UNSIGNED_GREATER, copy_forwards, jump_distance);
243 // The backwards move must be performed, so move TMP -> src_reg and do the
244 // same adjustment for dest_reg.
245 __ movq(src_reg, TMP);
246 __ leaq(dest_reg,
247 compiler::Address(dest_reg, length_reg, scale, -mov_size));
248 __ std();
249 }
251 // For reversed, do the `rep` first. It sets `dest_reg` to the start again.
252 // For forward, do the unpoisining first, before `dest_reg` is modified.
253 __ movq(TMP, length_reg);
254 if (mov_size != 1) {
255 // Unpoison takes the length in bytes.
256 __ MulImmediate(TMP, mov_size);
257 }
258 if (!reversed) {
259 __ MsanUnpoison(dest_reg, TMP);
260 }
261 }
262 switch (mov_size) {
263 case 1:
264 __ rep_movsb();
265 break;
266 case 2:
267 __ rep_movsw();
268 break;
269 case 4:
270 __ rep_movsd();
271 break;
272 case 8:
273 __ rep_movsq();
274 break;
275 default:
276 UNREACHABLE();
277 }
278 if (reversed) {
279 __ cld();
280 }
281
283 if (reversed) {
284 __ MsanUnpoison(dest_reg, TMP);
285 }
286 }
287}
288
289void MemoryCopyInstr::EmitComputeStartPointer(FlowGraphCompiler* compiler,
290 classid_t array_cid,
291 Register array_reg,
292 Register payload_reg,
293 Representation array_rep,
294 Location start_loc) {
295 intptr_t offset = 0;
296 if (array_rep != kTagged) {
297 // Do nothing, array_reg already contains the payload address.
298 } else if (IsTypedDataBaseClassId(array_cid)) {
299 // The incoming array must have been proven to be an internal typed data
300 // object, where the payload is in the object and we can just offset.
301 ASSERT_EQUAL(array_rep, kTagged);
303 } else {
304 ASSERT_EQUAL(array_rep, kTagged);
305 ASSERT(!IsExternalPayloadClassId(array_cid));
306 switch (array_cid) {
307 case kOneByteStringCid:
308 offset =
310 break;
311 case kTwoByteStringCid:
312 offset =
314 break;
315 default:
316 UNREACHABLE();
317 break;
318 }
319 }
320 ASSERT(start_loc.IsRegister() || start_loc.IsConstant());
321 if (start_loc.IsConstant()) {
322 const auto& constant = start_loc.constant();
323 ASSERT(constant.IsInteger());
324 const int64_t start_value = Integer::Cast(constant).AsInt64Value();
325 const intptr_t add_value = Utils::AddWithWrapAround(
326 Utils::MulWithWrapAround<intptr_t>(start_value, element_size_), offset);
327 __ leaq(payload_reg, compiler::Address(array_reg, add_value));
328 return;
329 }
330 // Note that start_reg must be writable in the special cases below.
331 const Register start_reg = start_loc.reg();
332 bool index_unboxed = unboxed_inputs_;
333 // Both special cases below assume that Smis are only shifted one bit.
335 if (element_size_ == 1 && !index_unboxed) {
336 // Shift the value to the right by tagging it as a Smi.
337 __ SmiUntag(start_reg);
338 index_unboxed = true;
339 } else if (element_size_ == 16 && index_unboxed) {
340 // Can't use TIMES_16 on X86, so instead pre-shift the value to reduce the
341 // scaling needed in the leaq instruction.
342 __ SmiTag(start_reg);
343 index_unboxed = false;
344 } else if (!index_unboxed) {
345 __ ExtendNonNegativeSmi(start_reg);
346 }
347 auto const scale = ToScaleFactor(element_size_, index_unboxed);
348 __ leaq(payload_reg, compiler::Address(array_reg, start_reg, scale, offset));
349}
350
351LocationSummary* MoveArgumentInstr::MakeLocationSummary(Zone* zone,
352 bool opt) const {
353 const intptr_t kNumInputs = 1;
354 const intptr_t kNumTemps = 0;
355 LocationSummary* locs = new (zone)
356 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
357 if (representation() == kUnboxedDouble) {
359 } else if (representation() == kUnboxedInt64) {
361 } else {
363 }
364 return locs;
365}
366
367void MoveArgumentInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
368 ASSERT(compiler->is_optimizing());
369
370 const Location value = locs()->in(0);
372 if (value.IsRegister()) {
373 __ movq(dst, value.reg());
374 } else if (value.IsConstant()) {
375 __ StoreObject(dst, value.constant());
376 } else if (value.IsFpuRegister()) {
377 __ movsd(dst, value.fpu_reg());
378 } else {
379 ASSERT(value.IsStackSlot());
380 __ MoveMemoryToMemory(dst, LocationToStackSlotAddress(value));
381 }
382}
383
384LocationSummary* DartReturnInstr::MakeLocationSummary(Zone* zone,
385 bool opt) const {
386 const intptr_t kNumInputs = 1;
387 const intptr_t kNumTemps = 0;
388 LocationSummary* locs = new (zone)
389 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
390 switch (representation()) {
391 case kTagged:
392 case kUnboxedInt64:
393 locs->set_in(0,
395 break;
396 case kPairOfTagged:
397 locs->set_in(
402 break;
403 case kUnboxedDouble:
404 locs->set_in(
406 break;
407 default:
408 UNREACHABLE();
409 break;
410 }
411 return locs;
412}
413
414// Attempt optimized compilation at return instruction instead of at the entry.
415// The entry needs to be patchable, no inlined objects are allowed in the area
416// that will be overwritten by the patch instruction: a jump).
417void DartReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
418 if (locs()->in(0).IsRegister()) {
419 const Register result = locs()->in(0).reg();
421 } else if (locs()->in(0).IsPairLocation()) {
422 const Register result_lo = locs()->in(0).AsPairLocation()->At(0).reg();
423 const Register result_hi = locs()->in(0).AsPairLocation()->At(1).reg();
426 } else {
427 ASSERT(locs()->in(0).IsFpuRegister());
428 const FpuRegister result = locs()->in(0).fpu_reg();
430 }
431
432 if (compiler->parsed_function().function().IsAsyncFunction() ||
433 compiler->parsed_function().function().IsAsyncGenerator()) {
434 ASSERT(compiler->flow_graph().graph_entry()->NeedsFrame());
435 const Code& stub = GetReturnStub(compiler);
436 compiler->EmitJumpToStub(stub);
437 return;
438 }
439
440 if (!compiler->flow_graph().graph_entry()->NeedsFrame()) {
441 __ ret();
442 return;
443 }
444
445#if defined(DEBUG)
446 __ Comment("Stack Check");
447 compiler::Label done;
448 const intptr_t fp_sp_dist =
450 compiler->StackSize()) *
451 kWordSize;
452 ASSERT(fp_sp_dist <= 0);
453 __ movq(RDI, RSP);
454 __ subq(RDI, RBP);
455 __ CompareImmediate(RDI, compiler::Immediate(fp_sp_dist));
457 __ int3();
458 __ Bind(&done);
459#endif
460 ASSERT(__ constant_pool_allowed());
461 __ LeaveDartFrame(); // Disallows constant pool use.
462 __ ret();
463 // This DartReturnInstr may be emitted out of order by the optimizer. The next
464 // block may be a target expecting a properly set constant pool pointer.
465 __ set_constant_pool_allowed(true);
466}
467
468static const RegisterSet kCalleeSaveRegistersSet(
471
472// Keep in sync with NativeEntryInstr::EmitNativeCode.
473void NativeReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
474 EmitReturnMoves(compiler);
475
476 // Restore tag before the profiler's stack walker will no longer see the
477 // InvokeDartCode return address.
478 __ movq(TMP, compiler::Address(RBP, NativeEntryInstr::kVMTagOffsetFromFp));
480
481 __ LeaveDartFrame();
482
483 // Pop dummy return address.
484 __ popq(TMP);
485
486 // Anything besides the return register.
487 const Register vm_tag_reg = RBX;
488 const Register old_exit_frame_reg = RCX;
489 const Register old_exit_through_ffi_reg = RDI;
490
491 __ popq(old_exit_frame_reg);
492
493 __ popq(old_exit_through_ffi_reg);
494
495 // Restore top_resource.
496 __ popq(TMP);
497 __ movq(
499 TMP);
500
501 __ popq(vm_tag_reg);
502
503 // The trampoline that called us will enter the safepoint on our behalf.
504 __ TransitionGeneratedToNative(vm_tag_reg, old_exit_frame_reg,
505 old_exit_through_ffi_reg,
506 /*enter_safepoint=*/false);
507
508 // Restore C++ ABI callee-saved registers.
509 __ PopRegisters(kCalleeSaveRegistersSet);
510
511#if defined(DART_TARGET_OS_FUCHSIA) && defined(USING_SHADOW_CALL_STACK)
512#error Unimplemented
513#endif
514
515 // Leave the entry frame.
516 __ LeaveFrame();
517
518 // Leave the dummy frame holding the pushed arguments.
519 __ LeaveFrame();
520
521 __ ret();
522
523 // For following blocks.
524 __ set_constant_pool_allowed(true);
525}
526
527// Detect pattern when one value is zero and another is a power of 2.
528static bool IsPowerOfTwoKind(intptr_t v1, intptr_t v2) {
529 return (Utils::IsPowerOfTwo(v1) && (v2 == 0)) ||
530 (Utils::IsPowerOfTwo(v2) && (v1 == 0));
531}
532
533LocationSummary* IfThenElseInstr::MakeLocationSummary(Zone* zone,
534 bool opt) const {
536 // TODO(dartbug.com/30952) support conversion of Register to corresponding
537 // least significant byte register (e.g. RAX -> AL, RSI -> SIL, r15 -> r15b).
539 return comparison()->locs();
540}
541
542void IfThenElseInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
543 ASSERT(locs()->out(0).reg() == RDX);
544
545 // Clear upper part of the out register. We are going to use setcc on it
546 // which is a byte move.
547 __ xorq(RDX, RDX);
548
549 // Emit comparison code. This must not overwrite the result register.
550 // IfThenElseInstr::Supports() should prevent EmitComparisonCode from using
551 // the labels or returning an invalid condition.
552 BranchLabels labels = {nullptr, nullptr, nullptr};
553 Condition true_condition = comparison()->EmitComparisonCode(compiler, labels);
554 ASSERT(true_condition != kInvalidCondition);
555
556 const bool is_power_of_two_kind = IsPowerOfTwoKind(if_true_, if_false_);
557
558 intptr_t true_value = if_true_;
559 intptr_t false_value = if_false_;
560
561 if (is_power_of_two_kind) {
562 if (true_value == 0) {
563 // We need to have zero in RDX on true_condition.
564 true_condition = InvertCondition(true_condition);
565 }
566 } else {
567 if (true_value == 0) {
568 // Swap values so that false_value is zero.
569 intptr_t temp = true_value;
570 true_value = false_value;
571 false_value = temp;
572 } else {
573 true_condition = InvertCondition(true_condition);
574 }
575 }
576
577 __ setcc(true_condition, DL);
578
579 if (is_power_of_two_kind) {
580 const intptr_t shift =
581 Utils::ShiftForPowerOfTwo(Utils::Maximum(true_value, false_value));
582 __ shlq(RDX, compiler::Immediate(shift + kSmiTagSize));
583 } else {
584 __ decq(RDX);
585 __ AndImmediate(RDX, compiler::Immediate(Smi::RawValue(true_value) -
586 Smi::RawValue(false_value)));
587 if (false_value != 0) {
588 __ AddImmediate(RDX, compiler::Immediate(Smi::RawValue(false_value)));
589 }
590 }
591}
592
593LocationSummary* LoadLocalInstr::MakeLocationSummary(Zone* zone,
594 bool opt) const {
595 const intptr_t kNumInputs = 0;
596 const intptr_t stack_index =
598 return LocationSummary::Make(zone, kNumInputs,
599 Location::StackSlot(stack_index, FPREG),
601}
602
603void LoadLocalInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
604 ASSERT(!compiler->is_optimizing());
605 // Nothing to do.
606}
607
608LocationSummary* StoreLocalInstr::MakeLocationSummary(Zone* zone,
609 bool opt) const {
610 const intptr_t kNumInputs = 1;
611 return LocationSummary::Make(zone, kNumInputs, Location::SameAsFirstInput(),
613}
614
615void StoreLocalInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
616 Register value = locs()->in(0).reg();
617 Register result = locs()->out(0).reg();
618 ASSERT(result == value); // Assert that register assignment is correct.
619 __ movq(compiler::Address(
621 value);
622}
623
624LocationSummary* ConstantInstr::MakeLocationSummary(Zone* zone,
625 bool opt) const {
626 const intptr_t kNumInputs = 0;
627 return LocationSummary::Make(zone, kNumInputs,
629 ? Location::Constant(this)
632}
633
634void ConstantInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
635 // The register allocator drops constant definitions that have no uses.
636 Location out = locs()->out(0);
637 ASSERT(out.IsRegister() || out.IsConstant() || out.IsInvalid());
638 if (out.IsRegister()) {
639 Register result = out.reg();
640 __ LoadObject(result, value());
641 }
642}
643
644void ConstantInstr::EmitMoveToLocation(FlowGraphCompiler* compiler,
645 const Location& destination,
646 Register tmp,
647 intptr_t pair_index) {
648 ASSERT(pair_index == 0); // No pair representation needed on 64-bit.
649 if (destination.IsRegister()) {
651 const int64_t value = Integer::Cast(value_).AsInt64Value();
652 if (value == 0) {
653 __ xorl(destination.reg(), destination.reg());
654 } else {
655 __ movq(destination.reg(), compiler::Immediate(value));
656 }
657 } else {
658 ASSERT(representation() == kTagged);
659 __ LoadObject(destination.reg(), value_);
660 }
661 } else if (destination.IsFpuRegister()) {
662 switch (representation()) {
663 case kUnboxedFloat:
664 __ LoadSImmediate(destination.fpu_reg(), Double::Cast(value_).value());
665 break;
666 case kUnboxedDouble:
667 __ LoadDImmediate(destination.fpu_reg(), Double::Cast(value_).value());
668 break;
669 case kUnboxedFloat64x2:
670 __ LoadQImmediate(destination.fpu_reg(),
671 Float64x2::Cast(value_).value());
672 break;
673 case kUnboxedFloat32x4:
674 __ LoadQImmediate(destination.fpu_reg(),
675 Float32x4::Cast(value_).value());
676 break;
677 case kUnboxedInt32x4:
678 __ LoadQImmediate(destination.fpu_reg(), Int32x4::Cast(value_).value());
679 break;
680 default:
681 UNREACHABLE();
682 }
683 } else if (destination.IsDoubleStackSlot()) {
684 ASSERT(representation() == kUnboxedDouble);
685 __ LoadDImmediate(FpuTMP, Double::Cast(value_).value());
686 __ movsd(LocationToStackSlotAddress(destination), FpuTMP);
687 } else if (destination.IsQuadStackSlot()) {
688 switch (representation()) {
689 case kUnboxedFloat64x2:
690 __ LoadQImmediate(FpuTMP, Float64x2::Cast(value_).value());
691 break;
692 case kUnboxedFloat32x4:
693 __ LoadQImmediate(FpuTMP, Float32x4::Cast(value_).value());
694 break;
695 case kUnboxedInt32x4:
696 __ LoadQImmediate(FpuTMP, Int32x4::Cast(value_).value());
697 break;
698 default:
699 UNREACHABLE();
700 }
701 __ movups(LocationToStackSlotAddress(destination), FpuTMP);
702 } else {
703 ASSERT(destination.IsStackSlot());
705 const int64_t value = Integer::Cast(value_).AsInt64Value();
706 __ movq(LocationToStackSlotAddress(destination),
707 compiler::Immediate(value));
708 } else if (representation() == kUnboxedFloat) {
709 int32_t float_bits =
710 bit_cast<int32_t, float>(Double::Cast(value_).value());
711 __ movl(LocationToStackSlotAddress(destination),
712 compiler::Immediate(float_bits));
713 } else {
714 ASSERT(representation() == kTagged);
715 __ StoreObject(LocationToStackSlotAddress(destination), value_);
716 }
717 }
718}
719
720LocationSummary* UnboxedConstantInstr::MakeLocationSummary(Zone* zone,
721 bool opt) const {
722 const bool is_unboxed_int =
726 const intptr_t kNumInputs = 0;
727 const intptr_t kNumTemps = is_unboxed_int ? 0 : 1;
728 LocationSummary* locs = new (zone)
729 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
730 if (is_unboxed_int) {
732 } else {
733 switch (representation()) {
734 case kUnboxedDouble:
737 break;
738 default:
739 UNREACHABLE();
740 break;
741 }
742 }
743 return locs;
744}
745
746void UnboxedConstantInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
747 // The register allocator drops constant definitions that have no uses.
748 if (!locs()->out(0).IsInvalid()) {
749 const Register scratch =
752 : locs()->temp(0).reg();
753 EmitMoveToLocation(compiler, locs()->out(0), scratch);
754 }
755}
756
757LocationSummary* AssertAssignableInstr::MakeLocationSummary(Zone* zone,
758 bool opt) const {
759 auto const dst_type_loc =
761
762 // We want to prevent spilling of the inputs (e.g. function/instantiator tav),
763 // since TTS preserves them. So we make this a `kNoCall` summary,
764 // even though most other registers can be modified by the stub. To tell the
765 // register allocator about it, we reserve all the other registers as
766 // temporary registers.
767 // TODO(http://dartbug.com/32788): Simplify this.
768
769 const intptr_t kNonChangeableInputRegs =
771 ((dst_type_loc.IsRegister() ? 1 : 0) << TypeTestABI::kDstTypeReg) |
774
775 const intptr_t kNumInputs = 4;
776
777 // We invoke a stub that can potentially clobber any CPU register
778 // but can only clobber FPU registers on the slow path when
779 // entering runtime. Preserve all FPU registers that are
780 // not guaranteed to be preserved by the ABI.
781 const intptr_t kCpuRegistersToPreserve =
782 kDartAvailableCpuRegs & ~kNonChangeableInputRegs;
783 const intptr_t kFpuRegistersToPreserve =
785
786 const intptr_t kNumTemps = (Utils::CountOneBits64(kCpuRegistersToPreserve) +
787 Utils::CountOneBits64(kFpuRegistersToPreserve));
788
789 LocationSummary* summary = new (zone) LocationSummary(
791 summary->set_in(kInstancePos,
793 summary->set_in(kDstTypePos, dst_type_loc);
794 summary->set_in(
799 summary->set_out(0, Location::SameAsFirstInput());
800
801 // Let's reserve all registers except for the input ones.
802 intptr_t next_temp = 0;
803 for (intptr_t i = 0; i < kNumberOfCpuRegisters; ++i) {
804 const bool should_preserve = ((1 << i) & kCpuRegistersToPreserve) != 0;
805 if (should_preserve) {
806 summary->set_temp(next_temp++,
807 Location::RegisterLocation(static_cast<Register>(i)));
808 }
809 }
810
811 for (intptr_t i = 0; i < kNumberOfFpuRegisters; i++) {
812 const bool should_preserve = ((1 << i) & kFpuRegistersToPreserve) != 0;
813 if (should_preserve) {
814 summary->set_temp(next_temp++, Location::FpuRegisterLocation(
815 static_cast<FpuRegister>(i)));
816 }
817 }
818
819 return summary;
820}
821
822void AssertBooleanInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
823 ASSERT(locs()->always_calls());
824
825 auto object_store = compiler->isolate_group()->object_store();
826 const auto& assert_boolean_stub =
827 Code::ZoneHandle(compiler->zone(), object_store->assert_boolean_stub());
828
829 compiler::Label done;
830 __ testq(
834 compiler->GenerateStubCall(source(), assert_boolean_stub,
835 /*kind=*/UntaggedPcDescriptors::kOther, locs(),
836 deopt_id(), env());
837 __ Bind(&done);
838}
839
840static Condition TokenKindToIntCondition(Token::Kind kind) {
841 switch (kind) {
842 case Token::kEQ:
843 return EQUAL;
844 case Token::kNE:
845 return NOT_EQUAL;
846 case Token::kLT:
847 return LESS;
848 case Token::kGT:
849 return GREATER;
850 case Token::kLTE:
851 return LESS_EQUAL;
852 case Token::kGTE:
853 return GREATER_EQUAL;
854 default:
855 UNREACHABLE();
856 return OVERFLOW;
857 }
858}
859
860LocationSummary* EqualityCompareInstr::MakeLocationSummary(Zone* zone,
861 bool opt) const {
862 const intptr_t kNumInputs = 2;
863 if (operation_cid() == kDoubleCid) {
864 const intptr_t kNumTemps = 0;
865 LocationSummary* locs = new (zone)
866 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
870 return locs;
871 }
872 if (operation_cid() == kSmiCid || operation_cid() == kMintCid ||
873 operation_cid() == kIntegerCid) {
874 const intptr_t kNumTemps = 0;
875 LocationSummary* locs = new (zone)
876 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
877 if (is_null_aware()) {
880 } else {
882 // Only one input can be a constant operand. The case of two constant
883 // operands should be handled by constant propagation.
884 // Only right can be a stack slot.
885 locs->set_in(1, locs->in(0).IsConstant()
888 }
890 return locs;
891 }
892 UNREACHABLE();
893 return nullptr;
894}
895
896static void LoadValueCid(FlowGraphCompiler* compiler,
897 Register value_cid_reg,
898 Register value_reg,
899 compiler::Label* value_is_smi = nullptr) {
900 compiler::Label done;
901 if (value_is_smi == nullptr) {
902 __ LoadImmediate(value_cid_reg, compiler::Immediate(kSmiCid));
903 }
904 __ testq(value_reg, compiler::Immediate(kSmiTagMask));
905 if (value_is_smi == nullptr) {
907 } else {
908 __ j(ZERO, value_is_smi);
909 }
910 __ LoadClassId(value_cid_reg, value_reg);
911 __ Bind(&done);
912}
913
914static Condition FlipCondition(Condition condition) {
915 switch (condition) {
916 case EQUAL:
917 return EQUAL;
918 case NOT_EQUAL:
919 return NOT_EQUAL;
920 case LESS:
921 return GREATER;
922 case LESS_EQUAL:
923 return GREATER_EQUAL;
924 case GREATER:
925 return LESS;
926 case GREATER_EQUAL:
927 return LESS_EQUAL;
928 case BELOW:
929 return ABOVE;
930 case BELOW_EQUAL:
931 return ABOVE_EQUAL;
932 case ABOVE:
933 return BELOW;
934 case ABOVE_EQUAL:
935 return BELOW_EQUAL;
936 default:
938 return EQUAL;
939 }
940}
941
942static void EmitBranchOnCondition(
943 FlowGraphCompiler* compiler,
944 Condition true_condition,
945 BranchLabels labels,
948 if (labels.fall_through == labels.false_label) {
949 // If the next block is the false successor, fall through to it.
950 __ j(true_condition, labels.true_label, jump_distance);
951 } else {
952 // If the next block is not the false successor, branch to it.
953 Condition false_condition = InvertCondition(true_condition);
954 __ j(false_condition, labels.false_label, jump_distance);
955
956 // Fall through or jump to the true successor.
957 if (labels.fall_through != labels.true_label) {
958 __ jmp(labels.true_label, jump_distance);
959 }
960 }
961}
962
963static Condition EmitSmiComparisonOp(FlowGraphCompiler* compiler,
964 const LocationSummary& locs,
965 Token::Kind kind) {
966 Location left = locs.in(0);
967 Location right = locs.in(1);
968 ASSERT(!left.IsConstant() || !right.IsConstant());
969
970 Condition true_condition = TokenKindToIntCondition(kind);
971 if (left.IsConstant() || right.IsConstant()) {
972 // Ensure constant is on the right.
973 ConstantInstr* constant = nullptr;
974 if (left.IsConstant()) {
975 constant = left.constant_instruction();
976 Location tmp = right;
977 right = left;
978 left = tmp;
979 true_condition = FlipCondition(true_condition);
980 } else {
981 constant = right.constant_instruction();
982 }
983
984 if (RepresentationUtils::IsUnboxedInteger(constant->representation())) {
985 int64_t value;
986 const bool ok = compiler::HasIntegerValue(constant->value(), &value);
988 __ OBJ(cmp)(left.reg(), compiler::Immediate(value));
989 } else {
990 ASSERT(constant->representation() == kTagged);
991 __ CompareObject(left.reg(), right.constant());
992 }
993 } else if (right.IsStackSlot()) {
994 __ OBJ(cmp)(left.reg(), LocationToStackSlotAddress(right));
995 } else {
996 __ OBJ(cmp)(left.reg(), right.reg());
997 }
998 return true_condition;
999}
1000
1001static Condition EmitInt64ComparisonOp(FlowGraphCompiler* compiler,
1002 const LocationSummary& locs,
1003 Token::Kind kind) {
1004 Location left = locs.in(0);
1005 Location right = locs.in(1);
1006 ASSERT(!left.IsConstant() || !right.IsConstant());
1007
1008 Condition true_condition = TokenKindToIntCondition(kind);
1009 if (left.IsConstant() || right.IsConstant()) {
1010 // Ensure constant is on the right.
1011 ConstantInstr* constant = nullptr;
1012 if (left.IsConstant()) {
1013 constant = left.constant_instruction();
1014 Location tmp = right;
1015 right = left;
1016 left = tmp;
1017 true_condition = FlipCondition(true_condition);
1018 } else {
1019 constant = right.constant_instruction();
1020 }
1021
1022 if (RepresentationUtils::IsUnboxedInteger(constant->representation())) {
1023 int64_t value;
1024 const bool ok = compiler::HasIntegerValue(constant->value(), &value);
1026 __ cmpq(left.reg(), compiler::Immediate(value));
1027 } else {
1028 UNREACHABLE();
1029 }
1030 } else if (right.IsStackSlot()) {
1031 __ cmpq(left.reg(), LocationToStackSlotAddress(right));
1032 } else {
1033 __ cmpq(left.reg(), right.reg());
1034 }
1035 return true_condition;
1036}
1037
1038static Condition EmitNullAwareInt64ComparisonOp(FlowGraphCompiler* compiler,
1039 const LocationSummary& locs,
1040 Token::Kind kind,
1041 BranchLabels labels) {
1042 ASSERT((kind == Token::kEQ) || (kind == Token::kNE));
1043 const Register left = locs.in(0).reg();
1044 const Register right = locs.in(1).reg();
1045 const Condition true_condition = TokenKindToIntCondition(kind);
1046 compiler::Label* equal_result =
1047 (true_condition == EQUAL) ? labels.true_label : labels.false_label;
1048 compiler::Label* not_equal_result =
1049 (true_condition == EQUAL) ? labels.false_label : labels.true_label;
1050
1051 // Check if operands have the same value. If they don't, then they could
1052 // be equal only if both of them are Mints with the same value.
1053 __ OBJ(cmp)(left, right);
1054 __ j(EQUAL, equal_result);
1055 __ OBJ(mov)(TMP, left);
1056 __ OBJ (and)(TMP, right);
1057 __ BranchIfSmi(TMP, not_equal_result);
1058 __ CompareClassId(left, kMintCid);
1059 __ j(NOT_EQUAL, not_equal_result);
1060 __ CompareClassId(right, kMintCid);
1061 __ j(NOT_EQUAL, not_equal_result);
1062 __ movq(TMP, compiler::FieldAddress(left, Mint::value_offset()));
1063 __ cmpq(TMP, compiler::FieldAddress(right, Mint::value_offset()));
1064 return true_condition;
1065}
1066
1067static Condition TokenKindToDoubleCondition(Token::Kind kind) {
1068 switch (kind) {
1069 case Token::kEQ:
1070 return EQUAL;
1071 case Token::kNE:
1072 return NOT_EQUAL;
1073 case Token::kLT:
1074 return BELOW;
1075 case Token::kGT:
1076 return ABOVE;
1077 case Token::kLTE:
1078 return BELOW_EQUAL;
1079 case Token::kGTE:
1080 return ABOVE_EQUAL;
1081 default:
1082 UNREACHABLE();
1083 return OVERFLOW;
1084 }
1085}
1086
1087static Condition EmitDoubleComparisonOp(FlowGraphCompiler* compiler,
1088 const LocationSummary& locs,
1089 Token::Kind kind,
1090 BranchLabels labels) {
1091 XmmRegister left = locs.in(0).fpu_reg();
1092 XmmRegister right = locs.in(1).fpu_reg();
1093
1094 __ comisd(left, right);
1095
1096 Condition true_condition = TokenKindToDoubleCondition(kind);
1097 compiler::Label* nan_result =
1098 (true_condition == NOT_EQUAL) ? labels.true_label : labels.false_label;
1099 __ j(PARITY_EVEN, nan_result);
1100 return true_condition;
1101}
1102
1103Condition EqualityCompareInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
1104 BranchLabels labels) {
1105 if (is_null_aware()) {
1106 ASSERT(operation_cid() == kMintCid);
1107 return EmitNullAwareInt64ComparisonOp(compiler, *locs(), kind(), labels);
1108 }
1109 if (operation_cid() == kSmiCid) {
1110 return EmitSmiComparisonOp(compiler, *locs(), kind());
1111 } else if (operation_cid() == kMintCid || operation_cid() == kIntegerCid) {
1112 return EmitInt64ComparisonOp(compiler, *locs(), kind());
1113 } else {
1114 ASSERT(operation_cid() == kDoubleCid);
1115 return EmitDoubleComparisonOp(compiler, *locs(), kind(), labels);
1116 }
1117}
1118
1119void ComparisonInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
1120 compiler::Label is_true, is_false;
1121 BranchLabels labels = {&is_true, &is_false, &is_false};
1122 Condition true_condition = EmitComparisonCode(compiler, labels);
1123
1124 Register result = locs()->out(0).reg();
1125 if (true_condition != kInvalidCondition) {
1126 EmitBranchOnCondition(compiler, true_condition, labels,
1128 }
1129 // Note: We use branches instead of setcc or cmov even when the branch labels
1130 // are otherwise unused, as this runs faster for the x86 processors tested on
1131 // our benchmarking server.
1132 compiler::Label done;
1133 __ Bind(&is_false);
1134 __ LoadObject(result, Bool::False());
1136 __ Bind(&is_true);
1137 __ LoadObject(result, Bool::True());
1138 __ Bind(&done);
1139}
1140
1141void ComparisonInstr::EmitBranchCode(FlowGraphCompiler* compiler,
1142 BranchInstr* branch) {
1143 BranchLabels labels = compiler->CreateBranchLabels(branch);
1144 Condition true_condition = EmitComparisonCode(compiler, labels);
1145 if (true_condition != kInvalidCondition) {
1146 EmitBranchOnCondition(compiler, true_condition, labels);
1147 }
1148}
1149
1150LocationSummary* TestIntInstr::MakeLocationSummary(Zone* zone, bool opt) const {
1151 const intptr_t kNumInputs = 2;
1152 const intptr_t kNumTemps = 0;
1153 LocationSummary* locs = new (zone)
1154 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
1156 // Only one input can be a constant operand. The case of two constant
1157 // operands should be handled by constant propagation.
1160 return locs;
1161}
1162
1163Condition TestIntInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
1164 BranchLabels labels) {
1165 Register left_reg = locs()->in(0).reg();
1166 Location right = locs()->in(1);
1167 if (right.IsConstant()) {
1168 const auto operand_size = representation_ == kTagged
1171 __ TestImmediate(left_reg, compiler::Immediate(ComputeImmediateMask()),
1172 operand_size);
1173 } else {
1174 if (representation_ == kTagged) {
1175 __ OBJ(test)(left_reg, right.reg());
1176 } else {
1177 __ testq(left_reg, right.reg());
1178 }
1179 }
1180 Condition true_condition = (kind() == Token::kNE) ? NOT_ZERO : ZERO;
1181 return true_condition;
1182}
1183
1184LocationSummary* TestCidsInstr::MakeLocationSummary(Zone* zone,
1185 bool opt) const {
1186 const intptr_t kNumInputs = 1;
1187 const intptr_t kNumTemps = 1;
1188 LocationSummary* locs = new (zone)
1189 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
1193 return locs;
1194}
1195
1196Condition TestCidsInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
1197 BranchLabels labels) {
1198 ASSERT((kind() == Token::kIS) || (kind() == Token::kISNOT));
1199 Register val_reg = locs()->in(0).reg();
1200 Register cid_reg = locs()->temp(0).reg();
1201
1202 compiler::Label* deopt =
1204 ? compiler->AddDeoptStub(deopt_id(), ICData::kDeoptTestCids)
1205 : nullptr;
1206
1207 const intptr_t true_result = (kind() == Token::kIS) ? 1 : 0;
1208 const ZoneGrowableArray<intptr_t>& data = cid_results();
1209 ASSERT(data[0] == kSmiCid);
1210 bool result = data[1] == true_result;
1211 __ testq(val_reg, compiler::Immediate(kSmiTagMask));
1212 __ j(ZERO, result ? labels.true_label : labels.false_label);
1213 __ LoadClassId(cid_reg, val_reg);
1214 for (intptr_t i = 2; i < data.length(); i += 2) {
1215 const intptr_t test_cid = data[i];
1216 ASSERT(test_cid != kSmiCid);
1217 result = data[i + 1] == true_result;
1218 __ cmpq(cid_reg, compiler::Immediate(test_cid));
1219 __ j(EQUAL, result ? labels.true_label : labels.false_label);
1220 }
1221 // No match found, deoptimize or default action.
1222 if (deopt == nullptr) {
1223 // If the cid is not in the list, jump to the opposite label from the cids
1224 // that are in the list. These must be all the same (see asserts in the
1225 // constructor).
1226 compiler::Label* target = result ? labels.false_label : labels.true_label;
1227 if (target != labels.fall_through) {
1228 __ jmp(target);
1229 }
1230 } else {
1231 __ jmp(deopt);
1232 }
1233 // Dummy result as this method already did the jump, there's no need
1234 // for the caller to branch on a condition.
1235 return kInvalidCondition;
1236}
1237
1238LocationSummary* RelationalOpInstr::MakeLocationSummary(Zone* zone,
1239 bool opt) const {
1240 const intptr_t kNumInputs = 2;
1241 const intptr_t kNumTemps = 0;
1242 if (operation_cid() == kDoubleCid) {
1243 LocationSummary* summary = new (zone)
1244 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
1246 summary->set_in(1, Location::RequiresFpuRegister());
1247 summary->set_out(0, Location::RequiresRegister());
1248 return summary;
1249 }
1250 if (operation_cid() == kSmiCid || operation_cid() == kMintCid) {
1251 LocationSummary* summary = new (zone)
1252 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
1253 summary->set_in(0, LocationRegisterOrConstant(left()));
1254 // Only one input can be a constant operand. The case of two constant
1255 // operands should be handled by constant propagation.
1256 summary->set_in(1, summary->in(0).IsConstant()
1259 summary->set_out(0, Location::RequiresRegister());
1260 return summary;
1261 }
1262 UNREACHABLE();
1263 return nullptr;
1264}
1265
1266Condition RelationalOpInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
1267 BranchLabels labels) {
1268 if (operation_cid() == kSmiCid) {
1269 return EmitSmiComparisonOp(compiler, *locs(), kind());
1270 } else if (operation_cid() == kMintCid) {
1271 return EmitInt64ComparisonOp(compiler, *locs(), kind());
1272 } else {
1273 ASSERT(operation_cid() == kDoubleCid);
1274 return EmitDoubleComparisonOp(compiler, *locs(), kind(), labels);
1275 }
1276}
1277
1278void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
1279 SetupNative();
1280 Register result = locs()->out(0).reg();
1281 const intptr_t argc_tag = NativeArguments::ComputeArgcTag(function());
1282
1283 // Pass a pointer to the first argument in R13 (we avoid using RAX here to
1284 // simplify the stub code that will call native code).
1285 __ leaq(R13, compiler::Address(RSP, (ArgumentCount() - 1) * kWordSize));
1286
1287 __ LoadImmediate(R10, compiler::Immediate(argc_tag));
1288 const Code* stub;
1289 if (link_lazily()) {
1290 stub = &StubCode::CallBootstrapNative();
1291 compiler::ExternalLabel label(NativeEntry::LinkNativeCallEntry());
1292 __ LoadNativeEntry(RBX, &label,
1294 compiler->GeneratePatchableCall(
1295 source(), *stub, UntaggedPcDescriptors::kOther, locs(),
1297 } else {
1298 if (is_bootstrap_native()) {
1299 stub = &StubCode::CallBootstrapNative();
1300 } else if (is_auto_scope()) {
1301 stub = &StubCode::CallAutoScopeNative();
1302 } else {
1303 stub = &StubCode::CallNoScopeNative();
1304 }
1305 const compiler::ExternalLabel label(
1306 reinterpret_cast<uword>(native_c_function()));
1307 __ LoadNativeEntry(RBX, &label,
1309 // We can never lazy-deopt here because natives are never optimized.
1310 ASSERT(!compiler->is_optimizing());
1311 compiler->GenerateNonLazyDeoptableStubCall(
1312 source(), *stub, UntaggedPcDescriptors::kOther, locs(),
1314 }
1315 __ LoadFromOffset(result, RSP, 0);
1316 compiler->EmitDropArguments(ArgumentCount()); // Drop the arguments.
1317}
1318
1319#define R(r) (1 << r)
1320
1321LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
1322 bool is_optimizing) const {
1323 // Use R10 as a temp. register. We can't use RDI, RSI, RDX, R8, R9 as they are
1324 // argument registers, and R11 is TMP.
1325 return MakeLocationSummaryInternal(
1326 zone, is_optimizing,
1329}
1330
1331#undef R
1332
1333void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
1334 const Register target_address = locs()->in(TargetAddressIndex()).reg();
1335
1336 // The temps are indexed according to their register number.
1337 // For regular calls, this holds the FP for rebasing the original locations
1338 // during EmitParamMoves.
1339 const Register saved_fp = locs()->temp(0).reg();
1340 const Register temp = locs()->temp(1).reg();
1341 // For leaf calls, this holds the SP used to restore the pre-aligned SP after
1342 // the call.
1343 // Note: R12 doubles as CODE_REG, which gets clobbered during frame setup in
1344 // regular calls.
1345 const Register saved_sp = locs()->temp(2).reg();
1346
1347 // Ensure these are callee-saved register and are preserved across the call.
1348 ASSERT(IsCalleeSavedRegister(saved_sp));
1349 ASSERT(IsCalleeSavedRegister(saved_fp));
1350 // Other temps don't need to be preserved.
1351
1352 if (is_leaf_) {
1353 __ movq(saved_sp, SPREG);
1354 } else {
1355 __ movq(saved_fp, FPREG);
1356 // Make a space to put the return address.
1357 __ pushq(compiler::Immediate(0));
1358
1359 // We need to create a dummy "exit frame". It will share the same pool
1360 // pointer but have a null code object.
1361 __ LoadObject(CODE_REG, Code::null_object());
1362 __ set_constant_pool_allowed(false);
1363 __ EnterDartFrame(0, PP);
1364 }
1365
1366 // Reserve space for the arguments that go on the stack (if any), then align.
1367 intptr_t stack_space = marshaller_.RequiredStackSpaceInBytes();
1368 __ ReserveAlignedFrameSpace(stack_space);
1370 RegisterSet kVolatileRegisterSet(CallingConventions::kVolatileCpuRegisters,
1372 __ movq(temp, RSP);
1373 __ PushRegisters(kVolatileRegisterSet);
1374
1375 // Unpoison everything from SP to FP: this covers both space we have
1376 // reserved for outgoing arguments and the spills which might have
1377 // been generated by the register allocator. Some of these spill slots
1378 // can be used as handles passed down to the runtime.
1379 __ movq(RAX, is_leaf_ ? FPREG : saved_fp);
1380 __ subq(RAX, temp);
1381 __ MsanUnpoison(temp, RAX);
1382
1383 // Incoming Dart arguments to this trampoline are potentially used as local
1384 // handles.
1385 __ MsanUnpoison(is_leaf_ ? FPREG : saved_fp,
1387
1388 // Outgoing arguments passed by register to the foreign function.
1389 __ LoadImmediate(CallingConventions::kArg1Reg, InputCount());
1390 __ CallCFunction(compiler::Address(
1391 THR, kMsanUnpoisonParamRuntimeEntry.OffsetFromThread()));
1392
1393 __ PopRegisters(kVolatileRegisterSet);
1394 }
1395
1396 if (is_leaf_) {
1397 EmitParamMoves(compiler, FPREG, saved_fp, TMP);
1398 } else {
1399 EmitParamMoves(compiler, saved_fp, saved_sp, TMP);
1400 }
1401
1403 __ Comment(is_leaf_ ? "Leaf Call" : "Call");
1404 }
1405
1406 if (is_leaf_) {
1407#if !defined(PRODUCT)
1408 // Set the thread object's top_exit_frame_info and VMTag to enable the
1409 // profiler to determine that thread is no longer executing Dart code.
1410 __ movq(compiler::Address(
1412 FPREG);
1413 __ movq(compiler::Assembler::VMTagAddress(), target_address);
1414#endif
1415
1416 if (marshaller_.contains_varargs() &&
1418 // TODO(http://dartbug.com/38578): Use the number of used FPU registers.
1421 }
1422 __ CallCFunction(target_address, /*restore_rsp=*/true);
1423
1424#if !defined(PRODUCT)
1426 compiler::Immediate(compiler::target::Thread::vm_tag_dart_id()));
1427 __ movq(compiler::Address(
1429 compiler::Immediate(0));
1430#endif
1431 } else {
1432 // We need to copy a dummy return address up into the dummy stack frame so
1433 // the stack walker will know which safepoint to use. RIP points to the
1434 // *next* instruction, so 'AddressRIPRelative' loads the address of the
1435 // following 'movq'.
1437 compiler->EmitCallsiteMetadata(InstructionSource(), deopt_id(),
1438 UntaggedPcDescriptors::Kind::kOther, locs(),
1439 env());
1440 __ movq(compiler::Address(FPREG, kSavedCallerPcSlotFromFp * kWordSize),
1441 temp);
1442
1444 // Update information in the thread object and enter a safepoint.
1445 __ movq(temp, compiler::Immediate(
1447
1448 __ TransitionGeneratedToNative(target_address, FPREG, temp,
1449 /*enter_safepoint=*/true);
1450
1451 if (marshaller_.contains_varargs() &&
1454 }
1455 __ CallCFunction(target_address, /*restore_rsp=*/true);
1456
1457 // Update information in the thread object and leave the safepoint.
1458 __ TransitionNativeToGenerated(/*leave_safepoint=*/true);
1459 } else {
1460 // We cannot trust that this code will be executable within a safepoint.
1461 // Therefore we delegate the responsibility of entering/exiting the
1462 // safepoint to a stub which is in the VM isolate's heap, which will never
1463 // lose execute permission.
1464 __ movq(temp,
1465 compiler::Address(
1466 THR, compiler::target::Thread::
1467 call_native_through_safepoint_entry_point_offset()));
1468
1469 // Calls RBX within a safepoint. RBX and R12 are clobbered.
1470 __ movq(RBX, target_address);
1471 if (marshaller_.contains_varargs() &&
1474 }
1475 __ call(temp);
1476 }
1477
1478 if (marshaller_.IsHandleCType(compiler::ffi::kResultIndex)) {
1479 __ Comment("Check Dart_Handle for Error.");
1480 compiler::Label not_error;
1481 __ movq(temp,
1482 compiler::Address(CallingConventions::kReturnReg,
1484 __ BranchIfSmi(temp, &not_error);
1485 __ LoadClassId(temp, temp);
1486 __ RangeCheck(temp, kNoRegister, kFirstErrorCid, kLastErrorCid,
1488
1489 // Slow path, use the stub to propagate error, to save on code-size.
1490 __ Comment("Slow path: call Dart_PropagateError through stub.");
1491 __ movq(temp,
1492 compiler::Address(
1493 THR, compiler::target::Thread::
1494 call_native_through_safepoint_entry_point_offset()));
1495 __ movq(RBX, compiler::Address(
1496 THR, kPropagateErrorRuntimeEntry.OffsetFromThread()));
1498 __ call(temp);
1499#if defined(DEBUG)
1500 // We should never return with normal controlflow from this.
1501 __ int3();
1502#endif
1503
1504 __ Bind(&not_error);
1505 }
1506 }
1507
1508 // Pass the `saved_fp` reg. as a temp to clobber since we're done with it.
1509 EmitReturnMoves(compiler, temp, saved_fp);
1510
1511 if (is_leaf_) {
1512 // Restore the pre-aligned SP.
1513 __ movq(SPREG, saved_sp);
1514 } else {
1515 __ LeaveDartFrame();
1516 // Restore the global object pool after returning from runtime (old space is
1517 // moving, so the GOP could have been relocated).
1518 if (FLAG_precompiled_mode) {
1519 __ movq(PP, compiler::Address(THR, Thread::global_object_pool_offset()));
1520 }
1521 __ set_constant_pool_allowed(true);
1522
1523 // Instead of returning to the "fake" return address, we just pop it.
1524 __ popq(temp);
1525 }
1526}
1527
1528// Keep in sync with NativeReturnInstr::EmitNativeCode.
1529void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
1530 __ Bind(compiler->GetJumpLabel(this));
1531
1532 // Create a dummy frame holding the pushed arguments. This simplifies
1533 // NativeReturnInstr::EmitNativeCode.
1534 __ EnterFrame(0);
1535
1536#if defined(DART_TARGET_OS_FUCHSIA) && defined(USING_SHADOW_CALL_STACK)
1537#error Unimplemented
1538#endif
1539
1540 // Save the argument registers, in reverse order.
1541 SaveArguments(compiler);
1542
1543 // Enter the entry frame. Push a dummy return address for consistency with
1544 // EnterFrame on ARM(64). NativeParameterInstr expects this frame has size
1545 // -exit_link_slot_from_entry_fp, verified below.
1546 __ PushImmediate(compiler::Immediate(0));
1547 __ EnterFrame(0);
1548
1549 // Save a space for the code object.
1550 __ PushImmediate(compiler::Immediate(0));
1551
1552 // InvokeDartCodeStub saves the arguments descriptor here. We don't have one,
1553 // but we need to follow the same frame layout for the stack walker.
1554 __ PushImmediate(compiler::Immediate(0));
1555
1556 // Save ABI callee-saved registers.
1557 __ PushRegisters(kCalleeSaveRegistersSet);
1558
1559 // Save the current VMTag on the stack.
1561 __ pushq(RAX);
1563
1564 // Save top resource.
1565 __ pushq(
1567 __ movq(
1569 compiler::Immediate(0));
1570
1571 __ pushq(compiler::Address(
1573
1574 // Save top exit frame info. Stack walker expects it to be here.
1575 __ pushq(compiler::Address(
1577
1578 // In debug mode, verify that we've pushed the top exit frame info at the
1579 // correct offset from FP.
1580 __ EmitEntryFrameVerification();
1581
1582 // The callback trampoline (caller) has already left the safepoint for us.
1583 __ TransitionNativeToGenerated(/*exit_safepoint=*/false,
1584 /*ignore_unwind_in_progress=*/false,
1585 /*set_tag=*/false);
1586
1587 // Load the code object.
1588 const Function& target_function = marshaller_.dart_signature();
1589 const intptr_t callback_id = target_function.FfiCallbackId();
1590 __ movq(RAX, compiler::Address(
1592 __ movq(RAX, compiler::Address(
1594 __ movq(RAX,
1595 compiler::Address(
1597 __ LoadCompressed(
1598 RAX, compiler::FieldAddress(
1600 __ LoadCompressed(
1601 CODE_REG,
1602 compiler::FieldAddress(
1605
1606 // Put the code object in the reserved slot.
1607 __ movq(compiler::Address(FPREG,
1609 CODE_REG);
1610
1611 if (FLAG_precompiled_mode) {
1612 __ movq(PP,
1613 compiler::Address(
1615 } else {
1616 __ xorq(PP, PP); // GC-safe value into PP.
1617 }
1618
1619 // Load a GC-safe value for arguments descriptor (unused but tagged).
1621
1622 // Push a dummy return address which suggests that we are inside of
1623 // InvokeDartCodeStub. This is how the stack walker detects an entry frame.
1624 __ movq(RAX,
1625 compiler::Address(
1627 __ pushq(compiler::FieldAddress(
1629
1630 // Continue with Dart frame setup.
1632
1633 // Delay setting the tag until the profiler's stack walker will see the
1634 // InvokeDartCode return address.
1636 compiler::Immediate(compiler::target::Thread::vm_tag_dart_id()));
1637}
1638
1639#define R(r) (1 << r)
1640
1642 Zone* zone,
1643 bool is_optimizing) const {
1645 return MakeLocationSummaryInternal(zone, (R(saved_fp)));
1646}
1647
1648#undef R
1649
1650void LeafRuntimeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
1651 const Register saved_fp = locs()->temp(0).reg();
1652 const Register temp0 = TMP;
1653
1654 // TODO(http://dartbug.com/47778): If we knew whether the stack was aligned
1655 // at this point, we could omit having a frame.
1656 __ MoveRegister(saved_fp, FPREG);
1657
1658 const intptr_t frame_space = native_calling_convention_.StackTopInBytes();
1659 __ EnterCFrame(frame_space);
1660
1661 EmitParamMoves(compiler, saved_fp, temp0);
1662 const Register target_address = locs()->in(TargetAddressIndex()).reg();
1663 __ movq(compiler::Assembler::VMTagAddress(), target_address);
1664 __ CallCFunction(target_address);
1666 compiler::Immediate(VMTag::kDartTagId));
1667
1668 __ LeaveCFrame();
1669}
1670
1672 Zone* zone,
1673 bool opt) const {
1674 const intptr_t kNumInputs = 1;
1675 // TODO(fschneider): Allow immediate operands for the char code.
1676 return LocationSummary::Make(zone, kNumInputs, Location::RequiresRegister(),
1678}
1679
1681 FlowGraphCompiler* compiler) {
1682 ASSERT(compiler->is_optimizing());
1683 Register char_code = locs()->in(0).reg();
1684 Register result = locs()->out(0).reg();
1685
1686 // Note: we don't bother to ensure char_code is a writable input because any
1687 // other instructions using it must also not rely on the upper bits when
1688 // compressed.
1689 __ ExtendNonNegativeSmi(char_code);
1690 __ movq(result,
1691 compiler::Address(THR, Thread::predefined_symbols_address_offset()));
1692 __ movq(result,
1693 compiler::Address(result, char_code,
1694 TIMES_HALF_WORD_SIZE, // Char code is a smi.
1696}
1697
1698LocationSummary* StringToCharCodeInstr::MakeLocationSummary(Zone* zone,
1699 bool opt) const {
1700 const intptr_t kNumInputs = 1;
1701 return LocationSummary::Make(zone, kNumInputs, Location::RequiresRegister(),
1703}
1704
1705void StringToCharCodeInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
1706 ASSERT(cid_ == kOneByteStringCid);
1707 Register str = locs()->in(0).reg();
1708 Register result = locs()->out(0).reg();
1709 compiler::Label is_one, done;
1710 __ LoadCompressedSmi(result,
1711 compiler::FieldAddress(str, String::length_offset()));
1712 __ cmpq(result, compiler::Immediate(Smi::RawValue(1)));
1714 __ movq(result, compiler::Immediate(Smi::RawValue(-1)));
1715 __ jmp(&done);
1716 __ Bind(&is_one);
1717 __ movzxb(result, compiler::FieldAddress(str, OneByteString::data_offset()));
1718 __ SmiTag(result);
1719 __ Bind(&done);
1720}
1721
1722LocationSummary* Utf8ScanInstr::MakeLocationSummary(Zone* zone,
1723 bool opt) const {
1724 const intptr_t kNumInputs = 5;
1725 const intptr_t kNumTemps = 1;
1726 LocationSummary* summary = new (zone)
1727 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
1728 summary->set_in(0, Location::Any()); // decoder
1729 summary->set_in(1, Location::WritableRegister()); // bytes
1730 summary->set_in(2, Location::WritableRegister()); // start
1731 summary->set_in(3, Location::WritableRegister()); // end
1732 summary->set_in(4, Location::RequiresRegister()); // table
1733 summary->set_temp(0, Location::RequiresRegister());
1734 summary->set_out(0, Location::RequiresRegister());
1735 return summary;
1736}
1737
1738void Utf8ScanInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
1739 const Register bytes_reg = locs()->in(1).reg();
1740 const Register start_reg = locs()->in(2).reg();
1741 const Register end_reg = locs()->in(3).reg();
1742 const Register table_reg = locs()->in(4).reg();
1743 const Register size_reg = locs()->out(0).reg();
1744
1745 const Register bytes_ptr_reg = start_reg;
1746 const Register bytes_end_reg = end_reg;
1747 const Register bytes_end_minus_16_reg = bytes_reg;
1748 const Register flags_reg = locs()->temp(0).reg();
1749 const Register temp_reg = TMP;
1750 const XmmRegister vector_reg = FpuTMP;
1751
1752 const intptr_t kSizeMask = 0x03;
1753 const intptr_t kFlagsMask = 0x3C;
1754
1755 compiler::Label scan_ascii, ascii_loop, ascii_loop_in, nonascii_loop;
1756 compiler::Label rest, rest_loop, rest_loop_in, done;
1757
1758 // Address of input bytes.
1759 __ LoadFromSlot(bytes_reg, bytes_reg, Slot::PointerBase_data());
1760
1761 // Pointers to start, end and end-16.
1762 __ leaq(bytes_ptr_reg, compiler::Address(bytes_reg, start_reg, TIMES_1, 0));
1763 __ leaq(bytes_end_reg, compiler::Address(bytes_reg, end_reg, TIMES_1, 0));
1764 __ leaq(bytes_end_minus_16_reg, compiler::Address(bytes_end_reg, -16));
1765
1766 // Initialize size and flags.
1767 __ xorq(size_reg, size_reg);
1768 __ xorq(flags_reg, flags_reg);
1769
1770 __ jmp(&scan_ascii, compiler::Assembler::kNearJump);
1771
1772 // Loop scanning through ASCII bytes one 16-byte vector at a time.
1773 // While scanning, the size register contains the size as it was at the start
1774 // of the current block of ASCII bytes, minus the address of the start of the
1775 // block. After the block, the end address of the block is added to update the
1776 // size to include the bytes in the block.
1777 __ Bind(&ascii_loop);
1778 __ addq(bytes_ptr_reg, compiler::Immediate(16));
1779 __ Bind(&ascii_loop_in);
1780
1781 // Exit vectorized loop when there are less than 16 bytes left.
1782 __ cmpq(bytes_ptr_reg, bytes_end_minus_16_reg);
1784
1785 // Find next non-ASCII byte within the next 16 bytes.
1786 // Note: In principle, we should use MOVDQU here, since the loaded value is
1787 // used as input to an integer instruction. In practice, according to Agner
1788 // Fog, there is no penalty for using the wrong kind of load.
1789 __ movups(vector_reg, compiler::Address(bytes_ptr_reg, 0));
1790 __ pmovmskb(temp_reg, vector_reg);
1791 __ bsfq(temp_reg, temp_reg);
1792 __ j(EQUAL, &ascii_loop, compiler::Assembler::kNearJump);
1793
1794 // Point to non-ASCII byte and update size.
1795 __ addq(bytes_ptr_reg, temp_reg);
1796 __ addq(size_reg, bytes_ptr_reg);
1797
1798 // Read first non-ASCII byte.
1799 __ movzxb(temp_reg, compiler::Address(bytes_ptr_reg, 0));
1800
1801 // Loop over block of non-ASCII bytes.
1802 __ Bind(&nonascii_loop);
1803 __ addq(bytes_ptr_reg, compiler::Immediate(1));
1804
1805 // Update size and flags based on byte value.
1806 __ movzxb(temp_reg, compiler::FieldAddress(
1807 table_reg, temp_reg, TIMES_1,
1809 __ orq(flags_reg, temp_reg);
1810 __ andq(temp_reg, compiler::Immediate(kSizeMask));
1811 __ addq(size_reg, temp_reg);
1812
1813 // Stop if end is reached.
1814 __ cmpq(bytes_ptr_reg, bytes_end_reg);
1816
1817 // Go to ASCII scan if next byte is ASCII, otherwise loop.
1818 __ movzxb(temp_reg, compiler::Address(bytes_ptr_reg, 0));
1819 __ testq(temp_reg, compiler::Immediate(0x80));
1820 __ j(NOT_EQUAL, &nonascii_loop, compiler::Assembler::kNearJump);
1821
1822 // Enter the ASCII scanning loop.
1823 __ Bind(&scan_ascii);
1824 __ subq(size_reg, bytes_ptr_reg);
1825 __ jmp(&ascii_loop_in);
1826
1827 // Less than 16 bytes left. Process the remaining bytes individually.
1828 __ Bind(&rest);
1829
1830 // Update size after ASCII scanning loop.
1831 __ addq(size_reg, bytes_ptr_reg);
1832 __ jmp(&rest_loop_in, compiler::Assembler::kNearJump);
1833
1834 __ Bind(&rest_loop);
1835
1836 // Read byte and increment pointer.
1837 __ movzxb(temp_reg, compiler::Address(bytes_ptr_reg, 0));
1838 __ addq(bytes_ptr_reg, compiler::Immediate(1));
1839
1840 // Update size and flags based on byte value.
1841 __ movzxb(temp_reg, compiler::FieldAddress(
1842 table_reg, temp_reg, TIMES_1,
1844 __ orq(flags_reg, temp_reg);
1845 __ andq(temp_reg, compiler::Immediate(kSizeMask));
1846 __ addq(size_reg, temp_reg);
1847
1848 // Stop if end is reached.
1849 __ Bind(&rest_loop_in);
1850 __ cmpq(bytes_ptr_reg, bytes_end_reg);
1852 __ Bind(&done);
1853
1854 // Write flags to field.
1855 __ andq(flags_reg, compiler::Immediate(kFlagsMask));
1856 if (!IsScanFlagsUnboxed()) {
1857 __ SmiTag(flags_reg);
1858 }
1859 Register decoder_reg;
1860 const Location decoder_location = locs()->in(0);
1861 if (decoder_location.IsStackSlot()) {
1862 __ movq(temp_reg, LocationToStackSlotAddress(decoder_location));
1863 decoder_reg = temp_reg;
1864 } else {
1865 decoder_reg = decoder_location.reg();
1866 }
1867 const auto scan_flags_field_offset = scan_flags_field_.offset_in_bytes();
1868 if (scan_flags_field_.is_compressed() && !IsScanFlagsUnboxed()) {
1869 __ OBJ(or)(compiler::FieldAddress(decoder_reg, scan_flags_field_offset),
1870 flags_reg);
1871 } else {
1872 __ orq(compiler::FieldAddress(decoder_reg, scan_flags_field_offset),
1873 flags_reg);
1874 }
1875}
1876
1877LocationSummary* LoadIndexedInstr::MakeLocationSummary(Zone* zone,
1878 bool opt) const {
1879 // The compiler must optimize any function that includes a LoadIndexed
1880 // instruction that uses typed data cids, since extracting the payload address
1881 // from views is done in a compiler pass after all code motion has happened.
1883
1884 const intptr_t kNumInputs = 2;
1885 const intptr_t kNumTemps = 0;
1886 LocationSummary* locs = new (zone)
1887 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
1889 // For tagged index with index_scale=1 as well as untagged index with
1890 // index_scale=16 we need a writable register due to addressing mode
1891 // restrictions on X64.
1892 const bool need_writable_index_register =
1893 (index_scale() == 1 && !index_unboxed_) ||
1894 (index_scale() == 16 && index_unboxed_);
1895 const bool can_be_constant =
1896 index()->BindsToConstant() &&
1898 index()->BoundConstant(), IsUntagged(), class_id(), index_scale());
1899 locs->set_in(
1900 kIndexPos,
1901 can_be_constant
1902 ? Location::Constant(index()->definition()->AsConstant())
1903 : (need_writable_index_register ? Location::WritableRegister()
1905 auto const rep =
1909 } else if (RepresentationUtils::IsUnboxed(rep)) {
1911 } else {
1913 }
1914 return locs;
1915}
1916
1917void LoadIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
1918 // The array register points to the backing store for external arrays.
1919 const Register array = locs()->in(kArrayPos).reg();
1920 const Location index = locs()->in(kIndexPos);
1921
1922 bool index_unboxed = index_unboxed_;
1923 if (index.IsRegister()) {
1924 if (index_scale_ == 1 && !index_unboxed) {
1925 __ SmiUntag(index.reg());
1926 index_unboxed = true;
1927 } else if (index_scale_ == 16 && index_unboxed) {
1928 // X64 does not support addressing mode using TIMES_16.
1929 __ SmiTag(index.reg());
1930 index_unboxed = false;
1931 } else if (!index_unboxed) {
1932 // Note: we don't bother to ensure index is a writable input because any
1933 // other instructions using it must also not rely on the upper bits
1934 // when compressed.
1935 __ ExtendNonNegativeSmi(index.reg());
1936 }
1937 } else {
1938 ASSERT(index.IsConstant());
1939 }
1940
1941 compiler::Address element_address =
1943 IsUntagged(), class_id(), index_scale_,
1944 index_unboxed, array, index.reg())
1945 : compiler::Assembler::ElementAddressForIntIndex(
1946 IsUntagged(), class_id(), index_scale_, array,
1947 Smi::Cast(index.constant()).Value());
1948
1949 auto const rep =
1953 Register result = locs()->out(0).reg();
1954 __ Load(result, element_address, RepresentationUtils::OperandSize(rep));
1955 } else if (RepresentationUtils::IsUnboxed(rep)) {
1956 XmmRegister result = locs()->out(0).fpu_reg();
1957 if (rep == kUnboxedFloat) {
1958 // Load single precision float.
1959 __ movss(result, element_address);
1960 } else if (rep == kUnboxedDouble) {
1961 __ movsd(result, element_address);
1962 } else {
1963 ASSERT(rep == kUnboxedInt32x4 || rep == kUnboxedFloat32x4 ||
1964 rep == kUnboxedFloat64x2);
1965 __ movups(result, element_address);
1966 }
1967 } else {
1968 ASSERT(rep == kTagged);
1969 ASSERT((class_id() == kArrayCid) || (class_id() == kImmutableArrayCid) ||
1970 (class_id() == kTypeArgumentsCid) || (class_id() == kRecordCid));
1971 Register result = locs()->out(0).reg();
1972 __ LoadCompressed(result, element_address);
1973 }
1974}
1975
1976LocationSummary* LoadCodeUnitsInstr::MakeLocationSummary(Zone* zone,
1977 bool opt) const {
1978 const intptr_t kNumInputs = 2;
1979 const intptr_t kNumTemps = 0;
1980 LocationSummary* summary = new (zone)
1981 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
1982 summary->set_in(0, Location::RequiresRegister());
1983 // The smi index is either untagged (element size == 1), or it is left smi
1984 // tagged (for all element sizes > 1).
1985 summary->set_in(1, index_scale() == 1 ? Location::WritableRegister()
1986 : Location::RequiresRegister());
1987 summary->set_out(0, Location::RequiresRegister());
1988 return summary;
1989}
1990
1991void LoadCodeUnitsInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
1992 // The string register points to the backing store for external strings.
1993 const Register str = locs()->in(0).reg();
1994 const Register index = locs()->in(1).reg();
1995
1996 bool index_unboxed = false;
1997 if ((index_scale() == 1)) {
1998 __ SmiUntag(index);
1999 index_unboxed = true;
2000 } else {
2001 __ ExtendNonNegativeSmi(index);
2002 }
2003 compiler::Address element_address =
2005 IsExternal(), class_id(), index_scale(), index_unboxed, str, index);
2006
2007 Register result = locs()->out(0).reg();
2008 switch (class_id()) {
2009 case kOneByteStringCid:
2010 switch (element_count()) {
2011 case 1:
2012 __ movzxb(result, element_address);
2013 break;
2014 case 2:
2015 __ movzxw(result, element_address);
2016 break;
2017 case 4:
2018 __ movl(result, element_address);
2019 break;
2020 default:
2021 UNREACHABLE();
2022 }
2024 __ SmiTag(result);
2025 break;
2026 case kTwoByteStringCid:
2027 switch (element_count()) {
2028 case 1:
2029 __ movzxw(result, element_address);
2030 break;
2031 case 2:
2032 __ movl(result, element_address);
2033 break;
2034 default:
2035 UNREACHABLE();
2036 }
2038 __ SmiTag(result);
2039 break;
2040 default:
2041 UNREACHABLE();
2042 break;
2043 }
2044}
2045
2046LocationSummary* StoreIndexedInstr::MakeLocationSummary(Zone* zone,
2047 bool opt) const {
2048 // The compiler must optimize any function that includes a StoreIndexed
2049 // instruction that uses typed data cids, since extracting the payload address
2050 // from views is done in a compiler pass after all code motion has happened.
2052
2053 const intptr_t kNumInputs = 3;
2054 const intptr_t kNumTemps =
2055 class_id() == kArrayCid && ShouldEmitStoreBarrier() ? 1 : 0;
2056 LocationSummary* locs = new (zone)
2057 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
2059 // For tagged index with index_scale=1 as well as untagged index with
2060 // index_scale=16 we need a writable register due to addressing mode
2061 // restrictions on X64.
2062 const bool need_writable_index_register =
2063 (index_scale() == 1 && !index_unboxed_) ||
2064 (index_scale() == 16 && index_unboxed_);
2065 const bool can_be_constant =
2066 index()->BindsToConstant() &&
2068 index()->BoundConstant(), IsUntagged(), class_id(), index_scale());
2069 locs->set_in(
2070 1, can_be_constant
2071 ? Location::Constant(index()->definition()->AsConstant())
2072 : (need_writable_index_register ? Location::WritableRegister()
2074 auto const rep =
2077 if (rep == kUnboxedUint8 || rep == kUnboxedInt8) {
2078 // TODO(fschneider): Add location constraint for byte registers (RAX,
2079 // RBX, RCX, RDX) instead of using a fixed register.
2081 } else {
2083 }
2084 } else if (RepresentationUtils::IsUnboxed(rep)) {
2085 // TODO(srdjan): Support Float64 constants.
2087 } else if (class_id() == kArrayCid) {
2091 if (ShouldEmitStoreBarrier()) {
2094 }
2095 } else {
2096 UNREACHABLE();
2097 }
2098 return locs;
2099}
2100
2101void StoreIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
2102 // The array register points to the backing store for external arrays.
2103 const Register array = locs()->in(0).reg();
2104 const Location index = locs()->in(1);
2105
2106 bool index_unboxed = index_unboxed_;
2107 if (index.IsRegister()) {
2108 if (index_scale_ == 1 && !index_unboxed) {
2109 __ SmiUntag(index.reg());
2110 index_unboxed = true;
2111 } else if (index_scale_ == 16 && index_unboxed) {
2112 // X64 does not support addressing mode using TIMES_16.
2113 __ SmiTag(index.reg());
2114 index_unboxed = false;
2115 } else if (!index_unboxed) {
2116 // Note: we don't bother to ensure index is a writable input because any
2117 // other instructions using it must also not rely on the upper bits
2118 // when compressed.
2119 __ ExtendNonNegativeSmi(index.reg());
2120 }
2121 } else {
2122 ASSERT(index.IsConstant());
2123 }
2124
2125 compiler::Address element_address =
2127 IsUntagged(), class_id(), index_scale_,
2128 index_unboxed, array, index.reg())
2129 : compiler::Assembler::ElementAddressForIntIndex(
2130 IsUntagged(), class_id(), index_scale_, array,
2131 Smi::Cast(index.constant()).Value());
2132
2133 auto const rep =
2137 ASSERT(rep == kUnboxedUint8);
2138 if (locs()->in(2).IsConstant()) {
2139 const Smi& constant = Smi::Cast(locs()->in(2).constant());
2140 intptr_t value = constant.Value();
2141 // Clamp to 0x0 or 0xFF respectively.
2142 if (value > 0xFF) {
2143 value = 0xFF;
2144 } else if (value < 0) {
2145 value = 0;
2146 }
2147 __ movb(element_address, compiler::Immediate(static_cast<int8_t>(value)));
2148 } else {
2149 const Register storedValueReg = locs()->in(2).reg();
2150 compiler::Label store_value, store_0xff;
2151 __ CompareImmediate(storedValueReg, compiler::Immediate(0xFF));
2153 // Clamp to 0x0 or 0xFF respectively.
2154 __ j(GREATER, &store_0xff);
2155 __ xorq(storedValueReg, storedValueReg);
2156 __ jmp(&store_value, compiler::Assembler::kNearJump);
2157 __ Bind(&store_0xff);
2158 __ LoadImmediate(storedValueReg, compiler::Immediate(0xFF));
2159 __ Bind(&store_value);
2160 __ movb(element_address, ByteRegisterOf(storedValueReg));
2161 }
2162 } else if (RepresentationUtils::IsUnboxedInteger(rep)) {
2163 if (rep == kUnboxedUint8 || rep == kUnboxedInt8) {
2164 if (locs()->in(2).IsConstant()) {
2165 const Smi& constant = Smi::Cast(locs()->in(2).constant());
2166 __ movb(element_address,
2167 compiler::Immediate(static_cast<int8_t>(constant.Value())));
2168 } else {
2169 __ movb(element_address, ByteRegisterOf(locs()->in(2).reg()));
2170 }
2171 } else {
2172 Register value = locs()->in(2).reg();
2173 __ Store(value, element_address, RepresentationUtils::OperandSize(rep));
2174 }
2175 } else if (RepresentationUtils::IsUnboxed(rep)) {
2176 if (rep == kUnboxedFloat) {
2177 __ movss(element_address, locs()->in(2).fpu_reg());
2178 } else if (rep == kUnboxedDouble) {
2179 __ movsd(element_address, locs()->in(2).fpu_reg());
2180 } else {
2181 ASSERT(rep == kUnboxedInt32x4 || rep == kUnboxedFloat32x4 ||
2182 rep == kUnboxedFloat64x2);
2183 __ movups(element_address, locs()->in(2).fpu_reg());
2184 }
2185 } else if (class_id() == kArrayCid) {
2186 ASSERT(rep == kTagged);
2187 if (ShouldEmitStoreBarrier()) {
2188 Register value = locs()->in(2).reg();
2189 Register slot = locs()->temp(0).reg();
2190 __ leaq(slot, element_address);
2191 __ StoreCompressedIntoArray(array, slot, value, CanValueBeSmi());
2192 } else if (locs()->in(2).IsConstant()) {
2193 const Object& constant = locs()->in(2).constant();
2194 __ StoreCompressedObjectIntoObjectNoBarrier(array, element_address,
2195 constant);
2196 } else {
2197 Register value = locs()->in(2).reg();
2198 __ StoreCompressedIntoObjectNoBarrier(array, element_address, value);
2199 }
2200 } else {
2201 UNREACHABLE();
2202 }
2203
2205 __ leaq(TMP, element_address);
2206 const intptr_t length_in_bytes = RepresentationUtils::ValueSize(
2208 __ MsanUnpoison(TMP, length_in_bytes);
2209 }
2210}
2211
2212LocationSummary* GuardFieldClassInstr::MakeLocationSummary(Zone* zone,
2213 bool opt) const {
2214 const intptr_t kNumInputs = 1;
2215
2216 const intptr_t value_cid = value()->Type()->ToCid();
2217 const intptr_t field_cid = field().guarded_cid();
2218
2219 const bool emit_full_guard = !opt || (field_cid == kIllegalCid);
2220 const bool needs_value_cid_temp_reg =
2221 (value_cid == kDynamicCid) && (emit_full_guard || (field_cid != kSmiCid));
2222 const bool needs_field_temp_reg = emit_full_guard;
2223
2224 intptr_t num_temps = 0;
2225 if (needs_value_cid_temp_reg) {
2226 num_temps++;
2227 }
2228 if (needs_field_temp_reg) {
2229 num_temps++;
2230 }
2231
2232 LocationSummary* summary = new (zone)
2233 LocationSummary(zone, kNumInputs, num_temps, LocationSummary::kNoCall);
2234 summary->set_in(0, Location::RequiresRegister());
2235
2236 for (intptr_t i = 0; i < num_temps; i++) {
2237 summary->set_temp(i, Location::RequiresRegister());
2238 }
2239
2240 return summary;
2241}
2242
2243void GuardFieldClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
2245 ASSERT(sizeof(UntaggedField::guarded_cid_) == 4);
2246 ASSERT(sizeof(UntaggedField::is_nullable_) == 4);
2247
2248 const intptr_t value_cid = value()->Type()->ToCid();
2249 const intptr_t field_cid = field().guarded_cid();
2250 const intptr_t nullability = field().is_nullable() ? kNullCid : kIllegalCid;
2251
2252 if (field_cid == kDynamicCid) {
2253 return; // Nothing to emit.
2254 }
2255
2256 const bool emit_full_guard =
2257 !compiler->is_optimizing() || (field_cid == kIllegalCid);
2258
2259 const bool needs_value_cid_temp_reg =
2260 (value_cid == kDynamicCid) && (emit_full_guard || (field_cid != kSmiCid));
2261
2262 const bool needs_field_temp_reg = emit_full_guard;
2263
2264 const Register value_reg = locs()->in(0).reg();
2265
2266 const Register value_cid_reg =
2267 needs_value_cid_temp_reg ? locs()->temp(0).reg() : kNoRegister;
2268
2269 const Register field_reg = needs_field_temp_reg
2270 ? locs()->temp(locs()->temp_count() - 1).reg()
2271 : kNoRegister;
2272
2273 compiler::Label ok, fail_label;
2274
2275 compiler::Label* deopt = nullptr;
2276 if (compiler->is_optimizing()) {
2277 deopt = compiler->AddDeoptStub(deopt_id(), ICData::kDeoptGuardField);
2278 }
2279
2280 compiler::Label* fail = (deopt != nullptr) ? deopt : &fail_label;
2281
2282 if (emit_full_guard) {
2283 __ LoadObject(field_reg, Field::ZoneHandle(field().Original()));
2284
2285 compiler::FieldAddress field_cid_operand(field_reg,
2287 compiler::FieldAddress field_nullability_operand(
2288 field_reg, Field::is_nullable_offset());
2289
2290 if (value_cid == kDynamicCid) {
2291 LoadValueCid(compiler, value_cid_reg, value_reg);
2292
2293 __ cmpl(value_cid_reg, field_cid_operand);
2294 __ j(EQUAL, &ok);
2295 __ cmpl(value_cid_reg, field_nullability_operand);
2296 } else if (value_cid == kNullCid) {
2297 __ cmpl(field_nullability_operand, compiler::Immediate(value_cid));
2298 } else {
2299 __ cmpl(field_cid_operand, compiler::Immediate(value_cid));
2300 }
2301 __ j(EQUAL, &ok);
2302
2303 // Check if the tracked state of the guarded field can be initialized
2304 // inline. If the field needs length check or requires type arguments and
2305 // class hierarchy processing for exactness tracking then we fall through
2306 // into runtime which is responsible for computing offset of the length
2307 // field based on the class id.
2308 const bool is_complicated_field =
2311 if (!is_complicated_field) {
2312 // Uninitialized field can be handled inline. Check if the
2313 // field is still unitialized.
2314 __ cmpl(field_cid_operand, compiler::Immediate(kIllegalCid));
2315 __ j(NOT_EQUAL, fail);
2316
2317 if (value_cid == kDynamicCid) {
2318 __ movl(field_cid_operand, value_cid_reg);
2319 __ movl(field_nullability_operand, value_cid_reg);
2320 } else {
2321 ASSERT(field_reg != kNoRegister);
2322 __ movl(field_cid_operand, compiler::Immediate(value_cid));
2323 __ movl(field_nullability_operand, compiler::Immediate(value_cid));
2324 }
2325
2326 __ jmp(&ok);
2327 }
2328
2329 if (deopt == nullptr) {
2330 __ Bind(fail);
2331
2332 __ cmpl(compiler::FieldAddress(field_reg, Field::guarded_cid_offset()),
2333 compiler::Immediate(kDynamicCid));
2334 __ j(EQUAL, &ok);
2335
2336 __ pushq(field_reg);
2337 __ pushq(value_reg);
2338 ASSERT(!compiler->is_optimizing()); // No deopt info needed.
2339 __ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
2340 __ Drop(2); // Drop the field and the value.
2341 } else {
2342 __ jmp(fail);
2343 }
2344 } else {
2345 ASSERT(compiler->is_optimizing());
2346 ASSERT(deopt != nullptr);
2347
2348 // Field guard class has been initialized and is known.
2349 if (value_cid == kDynamicCid) {
2350 // Value's class id is not known.
2351 __ testq(value_reg, compiler::Immediate(kSmiTagMask));
2352
2353 if (field_cid != kSmiCid) {
2354 __ j(ZERO, fail);
2355 __ LoadClassId(value_cid_reg, value_reg);
2356 __ CompareImmediate(value_cid_reg, compiler::Immediate(field_cid));
2357 }
2358
2359 if (field().is_nullable() && (field_cid != kNullCid)) {
2360 __ j(EQUAL, &ok);
2361 __ CompareObject(value_reg, Object::null_object());
2362 }
2363
2364 __ j(NOT_EQUAL, fail);
2365 } else if (value_cid == field_cid) {
2366 // This would normally be caught by Canonicalize, but RemoveRedefinitions
2367 // may sometimes produce the situation after the last Canonicalize pass.
2368 } else {
2369 // Both value's and field's class id is known.
2370 ASSERT(value_cid != nullability);
2371 __ jmp(fail);
2372 }
2373 }
2374 __ Bind(&ok);
2375}
2376
2377LocationSummary* GuardFieldLengthInstr::MakeLocationSummary(Zone* zone,
2378 bool opt) const {
2379 const intptr_t kNumInputs = 1;
2380 if (!opt || (field().guarded_list_length() == Field::kUnknownFixedLength)) {
2381 const intptr_t kNumTemps = 3;
2382 LocationSummary* summary = new (zone)
2383 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
2384 summary->set_in(0, Location::RequiresRegister());
2385 // We need temporaries for field object, length offset and expected length.
2386 summary->set_temp(0, Location::RequiresRegister());
2387 summary->set_temp(1, Location::RequiresRegister());
2388 summary->set_temp(2, Location::RequiresRegister());
2389 return summary;
2390 } else {
2391 LocationSummary* summary = new (zone)
2392 LocationSummary(zone, kNumInputs, 0, LocationSummary::kNoCall);
2393 summary->set_in(0, Location::RequiresRegister());
2394 return summary;
2395 }
2396 UNREACHABLE();
2397}
2398
2399void GuardFieldLengthInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
2400 if (field().guarded_list_length() == Field::kNoFixedLength) {
2401 return; // Nothing to emit.
2402 }
2403
2404 compiler::Label* deopt =
2405 compiler->is_optimizing()
2406 ? compiler->AddDeoptStub(deopt_id(), ICData::kDeoptGuardField)
2407 : nullptr;
2408
2409 const Register value_reg = locs()->in(0).reg();
2410
2411 if (!compiler->is_optimizing() ||
2412 (field().guarded_list_length() == Field::kUnknownFixedLength)) {
2413 const Register field_reg = locs()->temp(0).reg();
2414 const Register offset_reg = locs()->temp(1).reg();
2415 const Register length_reg = locs()->temp(2).reg();
2416
2417 compiler::Label ok;
2418
2419 __ LoadObject(field_reg, Field::ZoneHandle(field().Original()));
2420
2421 __ movsxb(
2422 offset_reg,
2423 compiler::FieldAddress(
2425 __ LoadCompressedSmi(
2426 length_reg,
2427 compiler::FieldAddress(field_reg, Field::guarded_list_length_offset()));
2428
2429 __ cmpq(offset_reg, compiler::Immediate(0));
2430 __ j(NEGATIVE, &ok);
2431
2432 // Load the length from the value. GuardFieldClass already verified that
2433 // value's class matches guarded class id of the field.
2434 // offset_reg contains offset already corrected by -kHeapObjectTag that is
2435 // why we use Address instead of FieldAddress.
2436 __ OBJ(cmp)(length_reg,
2437 compiler::Address(value_reg, offset_reg, TIMES_1, 0));
2438
2439 if (deopt == nullptr) {
2440 __ j(EQUAL, &ok);
2441
2442 __ pushq(field_reg);
2443 __ pushq(value_reg);
2444 ASSERT(!compiler->is_optimizing()); // No deopt info needed.
2445 __ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
2446 __ Drop(2); // Drop the field and the value.
2447 } else {
2448 __ j(NOT_EQUAL, deopt);
2449 }
2450
2451 __ Bind(&ok);
2452 } else {
2453 ASSERT(compiler->is_optimizing());
2454 ASSERT(field().guarded_list_length() >= 0);
2455 ASSERT(field().guarded_list_length_in_object_offset() !=
2457
2458 __ CompareImmediate(
2459 compiler::FieldAddress(value_reg,
2460 field().guarded_list_length_in_object_offset()),
2461 compiler::Immediate(Smi::RawValue(field().guarded_list_length())));
2462 __ j(NOT_EQUAL, deopt);
2463 }
2464}
2465
2466LocationSummary* GuardFieldTypeInstr::MakeLocationSummary(Zone* zone,
2467 bool opt) const {
2468 const intptr_t kNumInputs = 1;
2469 const intptr_t kNumTemps = 1;
2470 LocationSummary* summary = new (zone)
2471 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
2472 summary->set_in(0, Location::RequiresRegister());
2473 summary->set_temp(0, Location::RequiresRegister());
2474 return summary;
2475}
2476
2477void GuardFieldTypeInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
2478 // Should never emit GuardFieldType for fields that are marked as NotTracking.
2479 ASSERT(field().static_type_exactness_state().IsTracking());
2480 if (!field().static_type_exactness_state().NeedsFieldGuard()) {
2481 // Nothing to do: we only need to perform checks for trivially invariant
2482 // fields. If optimizing Canonicalize pass should have removed
2483 // this instruction.
2484 return;
2485 }
2486
2487 compiler::Label* deopt =
2488 compiler->is_optimizing()
2489 ? compiler->AddDeoptStub(deopt_id(), ICData::kDeoptGuardField)
2490 : nullptr;
2491
2492 compiler::Label ok;
2493
2494 const Register value_reg = locs()->in(0).reg();
2495 const Register temp = locs()->temp(0).reg();
2496
2497 // Skip null values for nullable fields.
2498 if (!compiler->is_optimizing() || field().is_nullable()) {
2499 __ CompareObject(value_reg, Object::Handle());
2500 __ j(EQUAL, &ok);
2501 }
2502
2503 // Get the state.
2504 const Field& original =
2505 Field::ZoneHandle(compiler->zone(), field().Original());
2506 __ LoadObject(temp, original);
2507 __ movsxb(temp, compiler::FieldAddress(
2509
2510 if (!compiler->is_optimizing()) {
2511 // Check if field requires checking (it is in unitialized or trivially
2512 // exact state).
2513 __ cmpq(temp,
2514 compiler::Immediate(StaticTypeExactnessState::kUninitialized));
2515 __ j(LESS, &ok);
2516 }
2517
2518 compiler::Label call_runtime;
2519 if (field().static_type_exactness_state().IsUninitialized()) {
2520 // Can't initialize the field state inline in optimized code.
2521 __ cmpq(temp,
2522 compiler::Immediate(StaticTypeExactnessState::kUninitialized));
2523 __ j(EQUAL, compiler->is_optimizing() ? deopt : &call_runtime);
2524 }
2525
2526 // At this point temp is known to be type arguments offset in words.
2527 __ movq(temp, compiler::FieldAddress(value_reg, temp,
2529 __ CompareObject(
2530 temp,
2532 compiler->zone(), Type::Cast(AbstractType::Handle(field().type()))
2533 .GetInstanceTypeArguments(compiler->thread())));
2534 if (deopt != nullptr) {
2535 __ j(NOT_EQUAL, deopt);
2536 } else {
2537 __ j(EQUAL, &ok);
2538
2539 __ Bind(&call_runtime);
2540 __ PushObject(original);
2541 __ pushq(value_reg);
2542 ASSERT(!compiler->is_optimizing()); // No deopt info needed.
2543 __ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
2544 __ Drop(2);
2545 }
2546
2547 __ Bind(&ok);
2548}
2549
2550LocationSummary* StoreStaticFieldInstr::MakeLocationSummary(Zone* zone,
2551 bool opt) const {
2552 const intptr_t kNumInputs = 1;
2553 const intptr_t kNumTemps = 1;
2554 LocationSummary* locs = new (zone)
2555 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
2558 return locs;
2559}
2560
2561void StoreStaticFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
2562 Register value = locs()->in(0).reg();
2563 Register temp = locs()->temp(0).reg();
2564
2565 compiler->used_static_fields().Add(&field());
2566
2567 __ movq(temp,
2568 compiler::Address(
2569 THR,
2570 field().is_shared()
2573 // Note: static fields ids won't be changed by hot-reload.
2574 __ movq(
2575 compiler::Address(temp, compiler::target::FieldTable::OffsetOf(field())),
2576 value);
2577}
2578
2579LocationSummary* InstanceOfInstr::MakeLocationSummary(Zone* zone,
2580 bool opt) const {
2581 const intptr_t kNumInputs = 3;
2582 const intptr_t kNumTemps = 0;
2583 LocationSummary* summary = new (zone)
2584 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
2586 summary->set_in(1, Location::RegisterLocation(
2588 summary->set_in(
2590 summary->set_out(0, Location::RegisterLocation(RAX));
2591 return summary;
2592}
2593
2594void InstanceOfInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
2595 ASSERT(locs()->in(0).reg() == TypeTestABI::kInstanceReg);
2598
2599 compiler->GenerateInstanceOf(source(), deopt_id(), env(), type(), locs());
2600 ASSERT(locs()->out(0).reg() == RAX);
2601}
2602
2603// TODO(srdjan): In case of constant inputs make CreateArray kNoCall and
2604// use slow path stub.
2605LocationSummary* CreateArrayInstr::MakeLocationSummary(Zone* zone,
2606 bool opt) const {
2607 const intptr_t kNumInputs = 2;
2608 const intptr_t kNumTemps = 0;
2609 LocationSummary* locs = new (zone)
2610 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
2616 return locs;
2617}
2618
2619// Inlines array allocation for known constant values.
2620static void InlineArrayAllocation(FlowGraphCompiler* compiler,
2621 intptr_t num_elements,
2622 compiler::Label* slow_path,
2623 compiler::Label* done) {
2624 const int kInlineArraySize = 12; // Same as kInlineInstanceSize.
2625 const intptr_t instance_size = Array::InstanceSize(num_elements);
2626
2627 __ TryAllocateArray(kArrayCid, instance_size, slow_path,
2629 AllocateArrayABI::kResultReg, // instance
2630 RCX, // end address
2631 R13); // temp
2632
2633 // RAX: new object start as a tagged pointer.
2634 // Store the type argument field.
2635 __ StoreCompressedIntoObjectNoBarrier(
2637 compiler::FieldAddress(AllocateArrayABI::kResultReg,
2640
2641 // Set the length field.
2642 __ StoreCompressedIntoObjectNoBarrier(
2644 compiler::FieldAddress(AllocateArrayABI::kResultReg,
2647
2648 // Initialize all array elements to raw_null.
2649 // AllocateArrayABI::kResultReg: new object start as a tagged pointer.
2650 // RCX: new object end address.
2651 // RDI: iterator which initially points to the start of the variable
2652 // data area to be initialized.
2653 if (num_elements > 0) {
2654 const intptr_t array_size = instance_size - sizeof(UntaggedArray);
2655 __ LoadObject(R12, Object::null_object());
2656 __ leaq(RDI, compiler::FieldAddress(AllocateArrayABI::kResultReg,
2657 sizeof(UntaggedArray)));
2658 if (array_size < (kInlineArraySize * kCompressedWordSize)) {
2659 intptr_t current_offset = 0;
2660 while (current_offset < array_size) {
2661 __ StoreCompressedIntoObjectNoBarrier(
2663 compiler::Address(RDI, current_offset), R12);
2664 current_offset += kCompressedWordSize;
2665 }
2666 } else {
2667 compiler::Label init_loop;
2668 __ Bind(&init_loop);
2669 __ StoreCompressedIntoObjectNoBarrier(AllocateArrayABI::kResultReg,
2670 compiler::Address(RDI, 0), R12);
2671 __ addq(RDI, compiler::Immediate(kCompressedWordSize));
2672 __ cmpq(RDI, RCX);
2673 __ j(BELOW, &init_loop, compiler::Assembler::kNearJump);
2674 }
2675 }
2677}
2678
2679void CreateArrayInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
2680 TypeUsageInfo* type_usage_info = compiler->thread()->type_usage_info();
2681 if (type_usage_info != nullptr) {
2682 const Class& list_class =
2683 Class::Handle(compiler->isolate_group()->class_table()->At(kArrayCid));
2684 RegisterTypeArgumentsUse(compiler->function(), type_usage_info, list_class,
2685 type_arguments()->definition());
2686 }
2687
2688 compiler::Label slow_path, done;
2689 if (!FLAG_use_slow_path && FLAG_inline_alloc) {
2690 if (compiler->is_optimizing() && !FLAG_precompiled_mode &&
2691 num_elements()->BindsToConstant() &&
2692 num_elements()->BoundConstant().IsSmi()) {
2693 const intptr_t length =
2694 Smi::Cast(num_elements()->BoundConstant()).Value();
2696 InlineArrayAllocation(compiler, length, &slow_path, &done);
2697 }
2698 }
2699 }
2700
2701 __ Bind(&slow_path);
2702 auto object_store = compiler->isolate_group()->object_store();
2703 const auto& allocate_array_stub =
2704 Code::ZoneHandle(compiler->zone(), object_store->allocate_array_stub());
2705 compiler->GenerateStubCall(source(), allocate_array_stub,
2706 UntaggedPcDescriptors::kOther, locs(), deopt_id(),
2707 env());
2708 __ Bind(&done);
2709}
2710
2712 Zone* zone,
2713 bool opt) const {
2714 ASSERT(opt);
2715 const intptr_t kNumInputs = 0;
2716 const intptr_t kNumTemps = 2;
2717 LocationSummary* locs = new (zone) LocationSummary(
2718 zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
2722 return locs;
2723}
2724
2725class AllocateContextSlowPath
2726 : public TemplateSlowPathCode<AllocateUninitializedContextInstr> {
2727 public:
2728 explicit AllocateContextSlowPath(
2729 AllocateUninitializedContextInstr* instruction)
2730 : TemplateSlowPathCode(instruction) {}
2731
2732 virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
2733 __ Comment("AllocateContextSlowPath");
2734 __ Bind(entry_label());
2735
2736 LocationSummary* locs = instruction()->locs();
2737 locs->live_registers()->Remove(locs->out(0));
2738
2739 compiler->SaveLiveRegisters(locs);
2740
2741 auto slow_path_env = compiler->SlowPathEnvironmentFor(
2742 instruction(), /*num_slow_path_args=*/0);
2743 ASSERT(slow_path_env != nullptr);
2744
2745 auto object_store = compiler->isolate_group()->object_store();
2746 const auto& allocate_context_stub = Code::ZoneHandle(
2747 compiler->zone(), object_store->allocate_context_stub());
2748
2749 __ LoadImmediate(
2750 R10, compiler::Immediate(instruction()->num_context_variables()));
2751 compiler->GenerateStubCall(instruction()->source(), allocate_context_stub,
2752 UntaggedPcDescriptors::kOther, locs,
2753 instruction()->deopt_id(), slow_path_env);
2754 ASSERT(instruction()->locs()->out(0).reg() == RAX);
2755
2756 compiler->RestoreLiveRegisters(instruction()->locs());
2757 __ jmp(exit_label());
2758 }
2759};
2760
2762 FlowGraphCompiler* compiler) {
2763 ASSERT(compiler->is_optimizing());
2764 Register temp = locs()->temp(0).reg();
2765 Register result = locs()->out(0).reg();
2766 // Try allocate the object.
2767 AllocateContextSlowPath* slow_path = new AllocateContextSlowPath(this);
2768 compiler->AddSlowPathCode(slow_path);
2769 intptr_t instance_size = Context::InstanceSize(num_context_variables());
2770
2771 if (!FLAG_use_slow_path && FLAG_inline_alloc) {
2772 __ TryAllocateArray(kContextCid, instance_size, slow_path->entry_label(),
2774 result, // instance
2775 temp, // end address
2776 locs()->temp(1).reg());
2777
2778 // Setup up number of context variables field.
2779 __ movq(compiler::FieldAddress(result, Context::num_variables_offset()),
2780 compiler::Immediate(num_context_variables()));
2781 } else {
2782 __ Jump(slow_path->entry_label());
2783 }
2784
2785 __ Bind(slow_path->exit_label());
2786}
2787
2788LocationSummary* AllocateContextInstr::MakeLocationSummary(Zone* zone,
2789 bool opt) const {
2790 const intptr_t kNumInputs = 0;
2791 const intptr_t kNumTemps = 1;
2792 LocationSummary* locs = new (zone)
2793 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
2796 return locs;
2797}
2798
2799void AllocateContextInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
2800 ASSERT(locs()->temp(0).reg() == R10);
2801 ASSERT(locs()->out(0).reg() == RAX);
2802
2803 auto object_store = compiler->isolate_group()->object_store();
2804 const auto& allocate_context_stub =
2805 Code::ZoneHandle(compiler->zone(), object_store->allocate_context_stub());
2806
2807 __ LoadImmediate(R10, compiler::Immediate(num_context_variables()));
2808 compiler->GenerateStubCall(source(), allocate_context_stub,
2809 UntaggedPcDescriptors::kOther, locs(), deopt_id(),
2810 env());
2811}
2812
2813LocationSummary* CloneContextInstr::MakeLocationSummary(Zone* zone,
2814 bool opt) const {
2815 const intptr_t kNumInputs = 1;
2816 const intptr_t kNumTemps = 0;
2817 LocationSummary* locs = new (zone)
2818 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
2821 return locs;
2822}
2823
2824void CloneContextInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
2825 ASSERT(locs()->in(0).reg() == R9);
2826 ASSERT(locs()->out(0).reg() == RAX);
2827
2828 auto object_store = compiler->isolate_group()->object_store();
2829 const auto& clone_context_stub =
2830 Code::ZoneHandle(compiler->zone(), object_store->clone_context_stub());
2831 compiler->GenerateStubCall(source(), clone_context_stub,
2832 /*kind=*/UntaggedPcDescriptors::kOther, locs(),
2833 deopt_id(), env());
2834}
2835
2836LocationSummary* CatchBlockEntryInstr::MakeLocationSummary(Zone* zone,
2837 bool opt) const {
2838 return new (zone) LocationSummary(zone, 0, 0, LocationSummary::kCall);
2839}
2840
2841void CatchBlockEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
2842 __ Bind(compiler->GetJumpLabel(this));
2843 compiler->AddExceptionHandler(this);
2844 if (HasParallelMove()) {
2846 }
2847
2848 // Restore RSP from RBP as we are coming from a throw and the code for
2849 // popping arguments has not been run.
2850 const intptr_t fp_sp_dist =
2852 compiler->StackSize()) *
2853 kWordSize;
2854 ASSERT(fp_sp_dist <= 0);
2855 __ leaq(RSP, compiler::Address(RBP, fp_sp_dist));
2856
2857 if (!compiler->is_optimizing()) {
2858 if (raw_exception_var_ != nullptr) {
2859 __ movq(compiler::Address(RBP,
2861 raw_exception_var_)),
2863 }
2864 if (raw_stacktrace_var_ != nullptr) {
2865 __ movq(compiler::Address(RBP,
2867 raw_stacktrace_var_)),
2869 }
2870 }
2871}
2872
2873LocationSummary* CheckStackOverflowInstr::MakeLocationSummary(Zone* zone,
2874 bool opt) const {
2875 const intptr_t kNumInputs = 0;
2876 const intptr_t kNumTemps = 1;
2877 const bool using_shared_stub = UseSharedSlowPathStub(opt);
2878 LocationSummary* summary = new (zone)
2879 LocationSummary(zone, kNumInputs, kNumTemps,
2880 using_shared_stub ? LocationSummary::kCallOnSharedSlowPath
2882 summary->set_temp(0, Location::RequiresRegister());
2883 return summary;
2884}
2885
2886class CheckStackOverflowSlowPath
2887 : public TemplateSlowPathCode<CheckStackOverflowInstr> {
2888 public:
2889 static constexpr intptr_t kNumSlowPathArgs = 0;
2890
2891 explicit CheckStackOverflowSlowPath(CheckStackOverflowInstr* instruction)
2892 : TemplateSlowPathCode(instruction) {}
2893
2894 virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
2895 if (compiler->isolate_group()->use_osr() && osr_entry_label()->IsLinked()) {
2896 __ Comment("CheckStackOverflowSlowPathOsr");
2897 __ Bind(osr_entry_label());
2898 __ movq(compiler::Address(THR, Thread::stack_overflow_flags_offset()),
2899 compiler::Immediate(Thread::kOsrRequest));
2900 }
2901 __ Comment("CheckStackOverflowSlowPath");
2902 __ Bind(entry_label());
2903 const bool using_shared_stub =
2904 instruction()->locs()->call_on_shared_slow_path();
2905 if (!using_shared_stub) {
2906 compiler->SaveLiveRegisters(instruction()->locs());
2907 }
2908 // pending_deoptimization_env_ is needed to generate a runtime call that
2909 // may throw an exception.
2910 ASSERT(compiler->pending_deoptimization_env_ == nullptr);
2911 Environment* env =
2912 compiler->SlowPathEnvironmentFor(instruction(), kNumSlowPathArgs);
2913 compiler->pending_deoptimization_env_ = env;
2914
2915 const bool has_frame = compiler->flow_graph().graph_entry()->NeedsFrame();
2916 if (using_shared_stub) {
2917 if (!has_frame) {
2918 ASSERT(__ constant_pool_allowed());
2919 __ set_constant_pool_allowed(false);
2920 __ EnterDartFrame(0);
2921 }
2922 const uword entry_point_offset =
2924 instruction()->locs()->live_registers()->FpuRegisterCount() > 0);
2925 __ call(compiler::Address(THR, entry_point_offset));
2926 compiler->RecordSafepoint(instruction()->locs(), kNumSlowPathArgs);
2927 compiler->RecordCatchEntryMoves(env);
2928 compiler->AddCurrentDescriptor(UntaggedPcDescriptors::kOther,
2929 instruction()->deopt_id(),
2930 instruction()->source());
2931 if (!has_frame) {
2932 __ LeaveDartFrame();
2933 __ set_constant_pool_allowed(true);
2934 }
2935 } else {
2936 ASSERT(has_frame);
2937 __ CallRuntime(kInterruptOrStackOverflowRuntimeEntry, kNumSlowPathArgs);
2938 compiler->EmitCallsiteMetadata(
2939 instruction()->source(), instruction()->deopt_id(),
2940 UntaggedPcDescriptors::kOther, instruction()->locs(), env);
2941 }
2942
2943 if (compiler->isolate_group()->use_osr() && !compiler->is_optimizing() &&
2944 instruction()->in_loop()) {
2945 // In unoptimized code, record loop stack checks as possible OSR entries.
2946 compiler->AddCurrentDescriptor(UntaggedPcDescriptors::kOsrEntry,
2947 instruction()->deopt_id(),
2948 InstructionSource());
2949 }
2950 compiler->pending_deoptimization_env_ = nullptr;
2951 if (!using_shared_stub) {
2952 compiler->RestoreLiveRegisters(instruction()->locs());
2953 }
2954 __ jmp(exit_label());
2955 }
2956
2957 compiler::Label* osr_entry_label() {
2958 ASSERT(IsolateGroup::Current()->use_osr());
2959 return &osr_entry_label_;
2960 }
2961
2962 private:
2963 compiler::Label osr_entry_label_;
2964};
2965
2966void CheckStackOverflowInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
2967 CheckStackOverflowSlowPath* slow_path = new CheckStackOverflowSlowPath(this);
2968 compiler->AddSlowPathCode(slow_path);
2969
2970 Register temp = locs()->temp(0).reg();
2971 // Generate stack overflow check.
2972 __ cmpq(RSP, compiler::Address(THR, Thread::stack_limit_offset()));
2973 __ j(BELOW_EQUAL, slow_path->entry_label());
2974 if (compiler->CanOSRFunction() && in_loop()) {
2975 // In unoptimized code check the usage counter to trigger OSR at loop
2976 // stack checks. Use progressively higher thresholds for more deeply
2977 // nested loops to attempt to hit outer loops with OSR when possible.
2978 __ LoadObject(temp, compiler->parsed_function().function());
2979 const intptr_t configured_optimization_counter_threshold =
2980 compiler->thread()->isolate_group()->optimization_counter_threshold();
2981 const int32_t threshold =
2982 configured_optimization_counter_threshold * (loop_depth() + 1);
2983 __ incl(compiler::FieldAddress(temp, Function::usage_counter_offset()));
2984 __ cmpl(compiler::FieldAddress(temp, Function::usage_counter_offset()),
2985 compiler::Immediate(threshold));
2986 __ j(GREATER_EQUAL, slow_path->osr_entry_label());
2987 }
2988 if (compiler->ForceSlowPathForStackOverflow()) {
2989 __ jmp(slow_path->entry_label());
2990 }
2991 __ Bind(slow_path->exit_label());
2992}
2993
2994static void EmitSmiShiftLeft(FlowGraphCompiler* compiler,
2995 BinarySmiOpInstr* shift_left) {
2996 const LocationSummary& locs = *shift_left->locs();
2997 Register left = locs.in(0).reg();
2998 Register result = locs.out(0).reg();
2999 ASSERT(left == result);
3000 compiler::Label* deopt =
3001 shift_left->CanDeoptimize()
3002 ? compiler->AddDeoptStub(shift_left->deopt_id(),
3003 ICData::kDeoptBinarySmiOp)
3004 : nullptr;
3005 if (locs.in(1).IsConstant()) {
3006 const Object& constant = locs.in(1).constant();
3007 ASSERT(constant.IsSmi());
3008 // shlq operation masks the count to 6 bits.
3009#if !defined(DART_COMPRESSED_POINTERS)
3010 const intptr_t kCountLimit = 0x3F;
3011#else
3012 const intptr_t kCountLimit = 0x1F;
3013#endif
3014 const intptr_t value = Smi::Cast(constant).Value();
3015 ASSERT((0 < value) && (value < kCountLimit));
3016 if (shift_left->can_overflow()) {
3017 if (value == 1) {
3018 // Use overflow flag.
3019 __ OBJ(shl)(left, compiler::Immediate(1));
3020 __ j(OVERFLOW, deopt);
3021 return;
3022 }
3023 // Check for overflow.
3024 Register temp = locs.temp(0).reg();
3025 __ OBJ(mov)(temp, left);
3026 __ OBJ(shl)(left, compiler::Immediate(value));
3027 __ OBJ(sar)(left, compiler::Immediate(value));
3028 __ OBJ(cmp)(left, temp);
3029 __ j(NOT_EQUAL, deopt); // Overflow.
3030 }
3031 // Shift for result now we know there is no overflow.
3032 __ OBJ(shl)(left, compiler::Immediate(value));
3033 return;
3034 }
3035
3036 // Right (locs.in(1)) is not constant.
3037 Register right = locs.in(1).reg();
3038 Range* right_range = shift_left->right_range();
3039 if (shift_left->left()->BindsToConstant() && shift_left->can_overflow()) {
3040 // TODO(srdjan): Implement code below for is_truncating().
3041 // If left is constant, we know the maximal allowed size for right.
3042 const Object& obj = shift_left->left()->BoundConstant();
3043 if (obj.IsSmi()) {
3044 const intptr_t left_int = Smi::Cast(obj).Value();
3045 if (left_int == 0) {
3046 __ CompareImmediate(right, compiler::Immediate(0),
3048 __ j(NEGATIVE, deopt);
3049 return;
3050 }
3051 const intptr_t max_right = kSmiBits - Utils::HighestBit(left_int);
3052 const bool right_needs_check =
3053 !RangeUtils::IsWithin(right_range, 0, max_right - 1);
3054 if (right_needs_check) {
3055 __ CompareObject(right, Smi::ZoneHandle(Smi::New(max_right)));
3056 __ j(ABOVE_EQUAL, deopt);
3057 }
3058 __ SmiUntag(right);
3059 __ OBJ(shl)(left, right);
3060 }
3061 return;
3062 }
3063
3064 const bool right_needs_check =
3065 !RangeUtils::IsWithin(right_range, 0, (Smi::kBits - 1));
3066 ASSERT(right == RCX); // Count must be in RCX
3067 if (!shift_left->can_overflow()) {
3068 if (right_needs_check) {
3069 const bool right_may_be_negative =
3070 (right_range == nullptr) || !right_range->IsPositive();
3071 if (right_may_be_negative) {
3072 ASSERT(shift_left->CanDeoptimize());
3073 __ CompareImmediate(right, compiler::Immediate(0),
3075 __ j(NEGATIVE, deopt);
3076 }
3077 compiler::Label done, is_not_zero;
3078 __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
3079 __ j(BELOW, &is_not_zero, compiler::Assembler::kNearJump);
3080 __ xorq(left, left);
3082 __ Bind(&is_not_zero);
3083 __ SmiUntag(right);
3084 __ OBJ(shl)(left, right);
3085 __ Bind(&done);
3086 } else {
3087 __ SmiUntag(right);
3088 __ OBJ(shl)(left, right);
3089 }
3090 } else {
3091 if (right_needs_check) {
3092 ASSERT(shift_left->CanDeoptimize());
3093 __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
3094 __ j(ABOVE_EQUAL, deopt);
3095 }
3096 // Left is not a constant.
3097 Register temp = locs.temp(0).reg();
3098 // Check if count too large for handling it inlined.
3099 __ OBJ(mov)(temp, left);
3100 __ SmiUntag(right);
3101 // Overflow test (preserve temp and right);
3102 __ OBJ(shl)(left, right);
3103 __ OBJ(sar)(left, right);
3104 __ OBJ(cmp)(left, temp);
3105 __ j(NOT_EQUAL, deopt); // Overflow.
3106 // Shift for result now we know there is no overflow.
3107 __ OBJ(shl)(left, right);
3108 ASSERT(!shift_left->is_truncating());
3109 }
3110}
3111
3112static bool CanBeImmediate(const Object& constant) {
3113 return constant.IsSmi() &&
3114 compiler::Immediate(Smi::RawValue(Smi::Cast(constant).Value()))
3115 .is_int32();
3116}
3117
3118static bool IsSmiValue(const Object& constant, intptr_t value) {
3119 return constant.IsSmi() && (Smi::Cast(constant).Value() == value);
3120}
3121
3122LocationSummary* BinarySmiOpInstr::MakeLocationSummary(Zone* zone,
3123 bool opt) const {
3124 const intptr_t kNumInputs = 2;
3125
3126 ConstantInstr* right_constant = right()->definition()->AsConstant();
3127 if ((right_constant != nullptr) && (op_kind() != Token::kTRUNCDIV) &&
3128 (op_kind() != Token::kSHL) &&
3129#if defined(DART_COMPRESSED_POINTERS)
3130 (op_kind() != Token::kUSHR) &&
3131#endif
3132 (op_kind() != Token::kMUL) && (op_kind() != Token::kMOD) &&
3133 CanBeImmediate(right_constant->value())) {
3134 const intptr_t kNumTemps = 0;
3135 LocationSummary* summary = new (zone)
3136 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
3137 summary->set_in(0, Location::RequiresRegister());
3138 summary->set_in(1, Location::Constant(right_constant));
3139 summary->set_out(0, Location::SameAsFirstInput());
3140 return summary;
3141 }
3142
3143 if (op_kind() == Token::kTRUNCDIV) {
3144 const intptr_t kNumTemps = 1;
3145 LocationSummary* summary = new (zone)
3146 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
3148 summary->set_in(0, Location::RequiresRegister());
3149 ConstantInstr* right_constant = right()->definition()->AsConstant();
3150 summary->set_in(1, Location::Constant(right_constant));
3151 summary->set_temp(0, Location::RequiresRegister());
3152 summary->set_out(0, Location::SameAsFirstInput());
3153 } else {
3154 // Both inputs must be writable because they will be untagged.
3155 summary->set_in(0, Location::RegisterLocation(RAX));
3156 summary->set_in(1, Location::WritableRegister());
3157 summary->set_out(0, Location::SameAsFirstInput());
3158 // Will be used for sign extension and division.
3159 summary->set_temp(0, Location::RegisterLocation(RDX));
3160 }
3161 return summary;
3162 } else if (op_kind() == Token::kMOD) {
3163 const intptr_t kNumTemps = 1;
3164 LocationSummary* summary = new (zone)
3165 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
3166 // Both inputs must be writable because they will be untagged.
3167 summary->set_in(0, Location::RegisterLocation(RDX));
3168 summary->set_in(1, Location::WritableRegister());
3169 summary->set_out(0, Location::SameAsFirstInput());
3170 // Will be used for sign extension and division.
3171 summary->set_temp(0, Location::RegisterLocation(RAX));
3172 return summary;
3173 } else if ((op_kind() == Token::kSHR)
3174#if !defined(DART_COMPRESSED_POINTERS)
3175 || (op_kind() == Token::kUSHR)
3176#endif // !defined(DART_COMPRESSED_POINTERS)
3177 ) {
3178 const intptr_t kNumTemps = 0;
3179 LocationSummary* summary = new (zone)
3180 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
3181 summary->set_in(0, Location::RequiresRegister());
3182 summary->set_in(1, LocationFixedRegisterOrSmiConstant(right(), RCX));
3183 summary->set_out(0, Location::SameAsFirstInput());
3184 return summary;
3185#if defined(DART_COMPRESSED_POINTERS)
3186 } else if (op_kind() == Token::kUSHR) {
3187 const intptr_t kNumTemps = 1;
3188 LocationSummary* summary = new (zone)
3189 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
3190 summary->set_in(0, Location::RequiresRegister());
3191 if ((right_constant != nullptr) &&
3192 CanBeImmediate(right_constant->value())) {
3193 summary->set_in(1, Location::Constant(right_constant));
3194 } else {
3195 summary->set_in(1, LocationFixedRegisterOrSmiConstant(right(), RCX));
3196 }
3197 summary->set_out(0, Location::SameAsFirstInput());
3198 summary->set_temp(0, Location::RequiresRegister());
3199 return summary;
3200#endif // defined(DART_COMPRESSED_POINTERS)
3201 } else if (op_kind() == Token::kSHL) {
3202 // Shift-by-1 overflow checking can use flags, otherwise we need a temp.
3203 const bool shiftBy1 =
3204 (right_constant != nullptr) && IsSmiValue(right_constant->value(), 1);
3205 const intptr_t kNumTemps = (can_overflow() && !shiftBy1) ? 1 : 0;
3206 LocationSummary* summary = new (zone)
3207 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
3208 summary->set_in(0, Location::RequiresRegister());
3209 summary->set_in(1, LocationFixedRegisterOrSmiConstant(right(), RCX));
3210 if (kNumTemps == 1) {
3211 summary->set_temp(0, Location::RequiresRegister());
3212 }
3213 summary->set_out(0, Location::SameAsFirstInput());
3214 return summary;
3215 } else {
3216 const intptr_t kNumTemps = 0;
3217 LocationSummary* summary = new (zone)
3218 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
3219 summary->set_in(0, Location::RequiresRegister());
3220 ConstantInstr* constant = right()->definition()->AsConstant();
3221 if (constant != nullptr) {
3222 summary->set_in(1, LocationRegisterOrSmiConstant(right()));
3223 } else {
3224 summary->set_in(1, Location::PrefersRegister());
3225 }
3226 summary->set_out(0, Location::SameAsFirstInput());
3227 return summary;
3228 }
3229}
3230
3231void BinarySmiOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
3232 if (op_kind() == Token::kSHL) {
3233 EmitSmiShiftLeft(compiler, this);
3234 return;
3235 }
3236
3237 Register left = locs()->in(0).reg();
3238 Register result = locs()->out(0).reg();
3239 ASSERT(left == result);
3240 compiler::Label* deopt = nullptr;
3241 if (CanDeoptimize()) {
3242 deopt = compiler->AddDeoptStub(deopt_id(), ICData::kDeoptBinarySmiOp);
3243 }
3244
3245 if (locs()->in(1).IsConstant()) {
3246 const Object& constant = locs()->in(1).constant();
3247 ASSERT(constant.IsSmi());
3248 const int64_t imm = Smi::RawValue(Smi::Cast(constant).Value());
3249 switch (op_kind()) {
3250 case Token::kADD: {
3251 __ AddImmediate(left, compiler::Immediate(imm), compiler::kObjectBytes);
3252 if (deopt != nullptr) __ j(OVERFLOW, deopt);
3253 break;
3254 }
3255 case Token::kSUB: {
3256 __ SubImmediate(left, compiler::Immediate(imm), compiler::kObjectBytes);
3257 if (deopt != nullptr) __ j(OVERFLOW, deopt);
3258 break;
3259 }
3260 case Token::kMUL: {
3261 // Keep left value tagged and untag right value.
3262 const intptr_t value = Smi::Cast(constant).Value();
3263 __ MulImmediate(left, compiler::Immediate(value),
3265 if (deopt != nullptr) __ j(OVERFLOW, deopt);
3266 break;
3267 }
3268 case Token::kTRUNCDIV: {
3269 const intptr_t value = Smi::Cast(constant).Value();
3272 const intptr_t shift_count =
3274 ASSERT(kSmiTagSize == 1);
3275 Register temp = locs()->temp(0).reg();
3276 __ movq(temp, left);
3277#if !defined(DART_COMPRESSED_POINTERS)
3278 __ sarq(temp, compiler::Immediate(63));
3279#else
3280 __ sarl(temp, compiler::Immediate(31));
3281#endif
3282 ASSERT(shift_count > 1); // 1, -1 case handled above.
3283#if !defined(DART_COMPRESSED_POINTERS)
3284 __ shrq(temp, compiler::Immediate(64 - shift_count));
3285#else
3286 __ shrl(temp, compiler::Immediate(32 - shift_count));
3287#endif
3288 __ OBJ(add)(left, temp);
3289 ASSERT(shift_count > 0);
3290 __ OBJ(sar)(left, compiler::Immediate(shift_count));
3291 if (value < 0) {
3292 __ OBJ(neg)(left);
3293 }
3294 __ SmiTag(left);
3295 break;
3296 }
3297 case Token::kBIT_AND: {
3298 // No overflow check.
3299 __ AndImmediate(left, compiler::Immediate(imm));
3300 break;
3301 }
3302 case Token::kBIT_OR: {
3303 // No overflow check.
3304 __ OrImmediate(left, compiler::Immediate(imm));
3305 break;
3306 }
3307 case Token::kBIT_XOR: {
3308 // No overflow check.
3309 __ XorImmediate(left, compiler::Immediate(imm));
3310 break;
3311 }
3312
3313 case Token::kSHR: {
3314 // sarq/l operation masks the count to 6/5 bits.
3315#if !defined(DART_COMPRESSED_POINTERS)
3316 const intptr_t kCountLimit = 0x3F;
3317#else
3318 const intptr_t kCountLimit = 0x1F;
3319#endif
3320 const intptr_t value = Smi::Cast(constant).Value();
3321 __ OBJ(sar)(left, compiler::Immediate(Utils::Minimum(
3322 value + kSmiTagSize, kCountLimit)));
3323 __ SmiTag(left);
3324 break;
3325 }
3326
3327 case Token::kUSHR: {
3328 // shrq operation masks the count to 6 bits, but
3329 // unsigned shifts by >= kBitsPerInt64 are eliminated by
3330 // BinaryIntegerOpInstr::Canonicalize.
3331 const intptr_t kCountLimit = 0x3F;
3332 const intptr_t value = Smi::Cast(constant).Value();
3333 ASSERT((value >= 0) && (value <= kCountLimit));
3334 __ SmiUntagAndSignExtend(left);
3335 __ shrq(left, compiler::Immediate(value));
3336 __ shlq(left, compiler::Immediate(1)); // SmiTag, keep hi bits.
3337 if (deopt != nullptr) {
3338 __ j(OVERFLOW, deopt);
3339#if defined(DART_COMPRESSED_POINTERS)
3340 const Register temp = locs()->temp(0).reg();
3341 __ movsxd(temp, left);
3342 __ cmpq(temp, left);
3343 __ j(NOT_EQUAL, deopt);
3344#endif // defined(DART_COMPRESSED_POINTERS)
3345 }
3346 break;
3347 }
3348
3349 default:
3350 UNREACHABLE();
3351 break;
3352 }
3353 return;
3354 } // locs()->in(1).IsConstant().
3355
3356 if (locs()->in(1).IsStackSlot()) {
3357 const compiler::Address& right = LocationToStackSlotAddress(locs()->in(1));
3358 switch (op_kind()) {
3359 case Token::kADD: {
3360 __ OBJ(add)(left, right);
3361 if (deopt != nullptr) __ j(OVERFLOW, deopt);
3362 break;
3363 }
3364 case Token::kSUB: {
3365 __ OBJ(sub)(left, right);
3366 if (deopt != nullptr) __ j(OVERFLOW, deopt);
3367 break;
3368 }
3369 case Token::kMUL: {
3370 __ SmiUntag(left);
3371 __ OBJ(imul)(left, right);
3372 if (deopt != nullptr) __ j(OVERFLOW, deopt);
3373 break;
3374 }
3375 case Token::kBIT_AND: {
3376 // No overflow check.
3377 __ andq(left, right);
3378 break;
3379 }
3380 case Token::kBIT_OR: {
3381 // No overflow check.
3382 __ orq(left, right);
3383 break;
3384 }
3385 case Token::kBIT_XOR: {
3386 // No overflow check.
3387 __ xorq(left, right);
3388 break;
3389 }
3390 default:
3391 UNREACHABLE();
3392 break;
3393 }
3394 return;
3395 } // locs()->in(1).IsStackSlot().
3396
3397 // if locs()->in(1).IsRegister.
3398 Register right = locs()->in(1).reg();
3399 switch (op_kind()) {
3400 case Token::kADD: {
3401 __ OBJ(add)(left, right);
3402 if (deopt != nullptr) __ j(OVERFLOW, deopt);
3403 break;
3404 }
3405 case Token::kSUB: {
3406 __ OBJ(sub)(left, right);
3407 if (deopt != nullptr) __ j(OVERFLOW, deopt);
3408 break;
3409 }
3410 case Token::kMUL: {
3411 __ SmiUntag(left);
3412 __ OBJ(imul)(left, right);
3413 if (deopt != nullptr) __ j(OVERFLOW, deopt);
3414 break;
3415 }
3416 case Token::kBIT_AND: {
3417 // No overflow check.
3418 __ andq(left, right);
3419 break;
3420 }
3421 case Token::kBIT_OR: {
3422 // No overflow check.
3423 __ orq(left, right);
3424 break;
3425 }
3426 case Token::kBIT_XOR: {
3427 // No overflow check.
3428 __ xorq(left, right);
3429 break;
3430 }
3431 case Token::kTRUNCDIV: {
3432 compiler::Label not_32bit, done;
3433
3434 Register temp = locs()->temp(0).reg();
3435 ASSERT(left == RAX);
3436 ASSERT((right != RDX) && (right != RAX));
3437 ASSERT(temp == RDX);
3438 ASSERT(result == RAX);
3440 // Handle divide by zero in runtime.
3441 __ OBJ(test)(right, right);
3442 __ j(ZERO, deopt);
3443 }
3444#if !defined(DART_COMPRESSED_POINTERS)
3445 // Check if both operands fit into 32bits as idiv with 64bit operands
3446 // requires twice as many cycles and has much higher latency.
3447 // We are checking this before untagging them to avoid corner case
3448 // dividing INT_MAX by -1 that raises exception because quotient is
3449 // too large for 32bit register.
3450 __ movsxd(temp, left);
3451 __ cmpq(temp, left);
3452 __ j(NOT_EQUAL, &not_32bit);
3453 __ movsxd(temp, right);
3454 __ cmpq(temp, right);
3455 __ j(NOT_EQUAL, &not_32bit);
3456
3457 // Both operands are 31bit smis. Divide using 32bit idiv.
3458 __ SmiUntag(left);
3459 __ SmiUntag(right);
3460 __ cdq();
3461 __ idivl(right);
3462 __ movsxd(result, result);
3463 __ jmp(&done);
3464
3465 // Divide using 64bit idiv.
3466 __ Bind(&not_32bit);
3467 __ SmiUntag(left);
3468 __ SmiUntag(right);
3469 __ cqo(); // Sign extend RAX -> RDX:RAX.
3470 __ idivq(right); // RAX: quotient, RDX: remainder.
3471 if (RangeUtils::Overlaps(right_range(), -1, -1)) {
3472 // Check the corner case of dividing the 'MIN_SMI' with -1, in which
3473 // case we cannot tag the result.
3474 __ CompareImmediate(result, compiler::Immediate(0x4000000000000000));
3475 __ j(EQUAL, deopt);
3476 }
3477#else
3478 // Both operands are 31bit smis. Divide using 32bit idiv.
3479 __ SmiUntag(left);
3480 __ SmiUntag(right);
3481 __ cdq();
3482 __ idivl(right);
3483
3484 if (RangeUtils::Overlaps(right_range(), -1, -1)) {
3485 // Check the corner case of dividing the 'MIN_SMI' with -1, in which
3486 // case we cannot tag the result.
3487 __ cmpl(result, compiler::Immediate(0x40000000));
3488 __ j(EQUAL, deopt);
3489 }
3490 __ movsxd(result, result);
3491#endif
3492 __ Bind(&done);
3493 __ SmiTag(result);
3494 break;
3495 }
3496 case Token::kMOD: {
3497 compiler::Label not_32bit, div_done;
3498
3499 Register temp = locs()->temp(0).reg();
3500 ASSERT(left == RDX);
3501 ASSERT((right != RDX) && (right != RAX));
3502 ASSERT(temp == RAX);
3503 ASSERT(result == RDX);
3505 // Handle divide by zero in runtime.
3506 __ OBJ(test)(right, right);
3507 __ j(ZERO, deopt);
3508 }
3509#if !defined(DART_COMPRESSED_POINTERS)
3510 // Check if both operands fit into 32bits as idiv with 64bit operands
3511 // requires twice as many cycles and has much higher latency.
3512 // We are checking this before untagging them to avoid corner case
3513 // dividing INT_MAX by -1 that raises exception because quotient is
3514 // too large for 32bit register.
3515 __ movsxd(temp, left);
3516 __ cmpq(temp, left);
3517 __ j(NOT_EQUAL, &not_32bit);
3518 __ movsxd(temp, right);
3519 __ cmpq(temp, right);
3520 __ j(NOT_EQUAL, &not_32bit);
3521#endif
3522 // Both operands are 31bit smis. Divide using 32bit idiv.
3523 __ SmiUntag(left);
3524 __ SmiUntag(right);
3525 __ movq(RAX, RDX);
3526 __ cdq();
3527 __ idivl(right);
3528 __ movsxd(result, result);
3529#if !defined(DART_COMPRESSED_POINTERS)
3530 __ jmp(&div_done);
3531
3532 // Divide using 64bit idiv.
3533 __ Bind(&not_32bit);
3534 __ SmiUntag(left);
3535 __ SmiUntag(right);
3536 __ movq(RAX, RDX);
3537 __ cqo(); // Sign extend RAX -> RDX:RAX.
3538 __ idivq(right); // RAX: quotient, RDX: remainder.
3539 __ Bind(&div_done);
3540#endif
3541 // res = left % right;
3542 // if (res < 0) {
3543 // if (right < 0) {
3544 // res = res - right;
3545 // } else {
3546 // res = res + right;
3547 // }
3548 // }
3549 compiler::Label all_done;
3550 __ OBJ(cmp)(result, compiler::Immediate(0));
3552 // Result is negative, adjust it.
3553 if (RangeUtils::Overlaps(right_range(), -1, 1)) {
3554 compiler::Label subtract;
3555 __ OBJ(cmp)(right, compiler::Immediate(0));
3557 __ OBJ(add)(result, right);
3558 __ jmp(&all_done, compiler::Assembler::kNearJump);
3559 __ Bind(&subtract);
3560 __ OBJ(sub)(result, right);
3561 } else if (right_range()->IsPositive()) {
3562 // Right is positive.
3563 __ OBJ(add)(result, right);
3564 } else {
3565 // Right is negative.
3566 __ OBJ(sub)(result, right);
3567 }
3568 __ Bind(&all_done);
3569 __ SmiTag(result);
3570 break;
3571 }
3572 case Token::kSHR: {
3573 if (CanDeoptimize()) {
3574 __ CompareImmediate(right, compiler::Immediate(0),
3576 __ j(LESS, deopt);
3577 }
3578 __ SmiUntag(right);
3579 // sarq/l operation masks the count to 6/5 bits.
3580#if !defined(DART_COMPRESSED_POINTERS)
3581 const intptr_t kCountLimit = 0x3F;
3582#else
3583 const intptr_t kCountLimit = 0x1F;
3584#endif
3585 if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(), kCountLimit)) {
3586 __ CompareImmediate(right, compiler::Immediate(kCountLimit));
3587 compiler::Label count_ok;
3588 __ j(LESS, &count_ok, compiler::Assembler::kNearJump);
3589 __ LoadImmediate(right, compiler::Immediate(kCountLimit));
3590 __ Bind(&count_ok);
3591 }
3592 ASSERT(right == RCX); // Count must be in RCX
3593 __ SmiUntag(left);
3594 __ OBJ(sar)(left, right);
3595 __ SmiTag(left);
3596 break;
3597 }
3598 case Token::kUSHR: {
3599 if (deopt != nullptr) {
3600 __ CompareImmediate(right, compiler::Immediate(0),
3602 __ j(LESS, deopt);
3603 }
3604 __ SmiUntag(right);
3605 // shrq operation masks the count to 6 bits.
3606 const intptr_t kCountLimit = 0x3F;
3607 COMPILE_ASSERT(kCountLimit + 1 == kBitsPerInt64);
3608 compiler::Label done;
3609 if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(), kCountLimit)) {
3610 __ CompareImmediate(right, compiler::Immediate(kCountLimit),
3612 compiler::Label count_ok;
3614 __ xorq(left, left);
3616 __ Bind(&count_ok);
3617 }
3618 ASSERT(right == RCX); // Count must be in RCX
3619 __ SmiUntagAndSignExtend(left);
3620 __ shrq(left, right);
3621 __ shlq(left, compiler::Immediate(1)); // SmiTag, keep hi bits.
3622 if (deopt != nullptr) {
3623 __ j(OVERFLOW, deopt);
3624#if defined(DART_COMPRESSED_POINTERS)
3625 const Register temp = locs()->temp(0).reg();
3626 __ movsxd(temp, left);
3627 __ cmpq(temp, left);
3628 __ j(NOT_EQUAL, deopt);
3629#endif // defined(DART_COMPRESSED_POINTERS)
3630 }
3631 __ Bind(&done);
3632 break;
3633 }
3634 case Token::kDIV: {
3635 // Dispatches to 'Double./'.
3636 // TODO(srdjan): Implement as conversion to double and double division.
3637 UNREACHABLE();
3638 break;
3639 }
3640 case Token::kOR:
3641 case Token::kAND: {
3642 // Flow graph builder has dissected this operation to guarantee correct
3643 // behavior (short-circuit evaluation).
3644 UNREACHABLE();
3645 break;
3646 }
3647 default:
3648 UNREACHABLE();
3649 break;
3650 }
3651}
3652
3653LocationSummary* CheckEitherNonSmiInstr::MakeLocationSummary(Zone* zone,
3654 bool opt) const {
3655 intptr_t left_cid = left()->Type()->ToCid();
3656 intptr_t right_cid = right()->Type()->ToCid();
3657 ASSERT((left_cid != kDoubleCid) && (right_cid != kDoubleCid));
3658 const intptr_t kNumInputs = 2;
3659 const bool need_temp = (left()->definition() != right()->definition()) &&
3660 (left_cid != kSmiCid) && (right_cid != kSmiCid);
3661 const intptr_t kNumTemps = need_temp ? 1 : 0;
3662 LocationSummary* summary = new (zone)
3663 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
3664 summary->set_in(0, Location::RequiresRegister());
3665 summary->set_in(1, Location::RequiresRegister());
3666 if (need_temp) summary->set_temp(0, Location::RequiresRegister());
3667 return summary;
3668}
3669
3670void CheckEitherNonSmiInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
3671 compiler::Label* deopt =
3672 compiler->AddDeoptStub(deopt_id(), ICData::kDeoptBinaryDoubleOp);
3673 intptr_t left_cid = left()->Type()->ToCid();
3674 intptr_t right_cid = right()->Type()->ToCid();
3675 Register left = locs()->in(0).reg();
3676 Register right = locs()->in(1).reg();
3677 if (this->left()->definition() == this->right()->definition()) {
3678 __ testq(left, compiler::Immediate(kSmiTagMask));
3679 } else if (left_cid == kSmiCid) {
3680 __ testq(right, compiler::Immediate(kSmiTagMask));
3681 } else if (right_cid == kSmiCid) {
3682 __ testq(left, compiler::Immediate(kSmiTagMask));
3683 } else {
3684 Register temp = locs()->temp(0).reg();
3685 __ movq(temp, left);
3686 __ orq(temp, right);
3687 __ testq(temp, compiler::Immediate(kSmiTagMask));
3688 }
3689 __ j(ZERO, deopt);
3690}
3691
3692LocationSummary* BoxInstr::MakeLocationSummary(Zone* zone, bool opt) const {
3693 const intptr_t kNumInputs = 1;
3694 const intptr_t kNumTemps = 1;
3695 LocationSummary* summary = new (zone) LocationSummary(
3696 zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
3698 summary->set_temp(0, Location::RequiresRegister());
3699 summary->set_out(0, Location::RequiresRegister());
3700 return summary;
3701}
3702
3703void BoxInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
3704 Register out_reg = locs()->out(0).reg();
3705 Register temp = locs()->temp(0).reg();
3706 XmmRegister value = locs()->in(0).fpu_reg();
3707
3709 compiler->BoxClassFor(from_representation()),
3710 out_reg, temp);
3711
3712 switch (from_representation()) {
3713 case kUnboxedDouble:
3714 __ movsd(compiler::FieldAddress(out_reg, ValueOffset()), value);
3715 break;
3716 case kUnboxedFloat: {
3717 __ cvtss2sd(FpuTMP, value);
3718 __ movsd(compiler::FieldAddress(out_reg, ValueOffset()), FpuTMP);
3719 break;
3720 }
3721 case kUnboxedFloat32x4:
3722 case kUnboxedFloat64x2:
3723 case kUnboxedInt32x4:
3724 __ movups(compiler::FieldAddress(out_reg, ValueOffset()), value);
3725 break;
3726 default:
3727 UNREACHABLE();
3728 break;
3729 }
3730}
3731
3732LocationSummary* UnboxInstr::MakeLocationSummary(Zone* zone, bool opt) const {
3734 const intptr_t kNumInputs = 1;
3735 const intptr_t kNumTemps = 0;
3736 const bool needs_writable_input =
3737 (representation() != kUnboxedInt64) &&
3738 (value()->Type()->ToNullableCid() != BoxCid());
3739 LocationSummary* summary = new (zone)
3740 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
3741 summary->set_in(0, needs_writable_input ? Location::WritableRegister()
3744#if !defined(DART_COMPRESSED_POINTERS)
3745 summary->set_out(0, Location::SameAsFirstInput());
3746#else
3747 summary->set_out(0, Location::RequiresRegister());
3748#endif
3749 } else {
3750 summary->set_out(0, Location::RequiresFpuRegister());
3751 }
3752 return summary;
3753}
3754
3755void UnboxInstr::EmitLoadFromBox(FlowGraphCompiler* compiler) {
3756 const Register box = locs()->in(0).reg();
3757
3758 switch (representation()) {
3759 case kUnboxedInt64: {
3760 const Register result = locs()->out(0).reg();
3761 __ movq(result, compiler::FieldAddress(box, ValueOffset()));
3762 break;
3763 }
3764
3765 case kUnboxedDouble: {
3766 const FpuRegister result = locs()->out(0).fpu_reg();
3767 __ movsd(result, compiler::FieldAddress(box, ValueOffset()));
3768 break;
3769 }
3770
3771 case kUnboxedFloat: {
3772 const FpuRegister result = locs()->out(0).fpu_reg();
3773 __ movsd(result, compiler::FieldAddress(box, ValueOffset()));
3774 __ cvtsd2ss(result, result);
3775 break;
3776 }
3777
3778 case kUnboxedFloat32x4:
3779 case kUnboxedFloat64x2:
3780 case kUnboxedInt32x4: {
3781 const FpuRegister result = locs()->out(0).fpu_reg();
3782 __ movups(result, compiler::FieldAddress(box, ValueOffset()));
3783 break;
3784 }
3785
3786 default:
3787 UNREACHABLE();
3788 break;
3789 }
3790}
3791
3792void UnboxInstr::EmitSmiConversion(FlowGraphCompiler* compiler) {
3793 const Register box = locs()->in(0).reg();
3794
3795 switch (representation()) {
3796 case kUnboxedInt32: {
3797 const Register result = locs()->out(0).reg();
3798 __ SmiUntag(result, box);
3799 break;
3800 }
3801 case kUnboxedInt64: {
3802 const Register result = locs()->out(0).reg();
3803 __ SmiUntagAndSignExtend(result, box);
3804 break;
3805 }
3806 case kUnboxedDouble: {
3807 const FpuRegister result = locs()->out(0).fpu_reg();
3808 __ SmiUntag(box);
3809 __ OBJ(cvtsi2sd)(result, box);
3810 break;
3811 }
3812
3813 default:
3814 UNREACHABLE();
3815 break;
3816 }
3817}
3818
3819void UnboxInstr::EmitLoadInt32FromBoxOrSmi(FlowGraphCompiler* compiler) {
3820 const Register value = locs()->in(0).reg();
3821 const Register result = locs()->out(0).reg();
3822 __ LoadInt32FromBoxOrSmi(result, value);
3823}
3824
3825void UnboxInstr::EmitLoadInt64FromBoxOrSmi(FlowGraphCompiler* compiler) {
3826 const Register value = locs()->in(0).reg();
3827 const Register result = locs()->out(0).reg();
3828 __ LoadInt64FromBoxOrSmi(result, value);
3829}
3830
3831LocationSummary* UnboxInteger32Instr::MakeLocationSummary(Zone* zone,
3832 bool opt) const {
3833 const intptr_t kNumInputs = 1;
3834 const intptr_t kNumTemps = (!is_truncating() && CanDeoptimize()) ? 1 : 0;
3835 LocationSummary* summary = new (zone)
3836 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
3837 summary->set_in(0, Location::RequiresRegister());
3838 summary->set_out(0, Location::SameAsFirstInput());
3839 if (kNumTemps > 0) {
3840 summary->set_temp(0, Location::RequiresRegister());
3841 }
3842 return summary;
3843}
3844
3845void UnboxInteger32Instr::EmitNativeCode(FlowGraphCompiler* compiler) {
3846 const intptr_t value_cid = value()->Type()->ToCid();
3847 const Register value = locs()->in(0).reg();
3848 compiler::Label* deopt =
3850 ? compiler->AddDeoptStub(GetDeoptId(), ICData::kDeoptUnboxInteger)
3851 : nullptr;
3852 ASSERT(value == locs()->out(0).reg());
3853
3854 if (value_cid == kSmiCid) {
3855 __ SmiUntag(value);
3856 } else if (value_cid == kMintCid) {
3857 __ movq(value, compiler::FieldAddress(value, Mint::value_offset()));
3858 } else if (!CanDeoptimize()) {
3859 // Type information is not conclusive, but range analysis found
3860 // the value to be in int64 range. Therefore it must be a smi
3861 // or mint value.
3863 compiler::Label done;
3864#if !defined(DART_COMPRESSED_POINTERS)
3865 // Optimistically untag value.
3866 __ SmiUntag(value);
3868 // Undo untagging by multiplying value by 2.
3869 // [reg + reg + disp8] has a shorter encoding than [reg*2 + disp32]
3870 __ movq(value,
3871 compiler::Address(value, value, TIMES_1, Mint::value_offset()));
3872#else
3873 // Cannot speculatively untag because it erases the upper bits needed to
3874 // dereference when it is a Mint.
3875 compiler::Label not_smi;
3876 __ BranchIfNotSmi(value, &not_smi, compiler::Assembler::kNearJump);
3877 __ SmiUntagAndSignExtend(value);
3879 __ Bind(&not_smi);
3880 __ movq(value, compiler::FieldAddress(value, Mint::value_offset()));
3881#endif
3882 __ Bind(&done);
3883 return;
3884 } else {
3885 compiler::Label done;
3886#if !defined(DART_COMPRESSED_POINTERS)
3887 // Optimistically untag value.
3888 __ SmiUntagOrCheckClass(value, kMintCid, &done);
3889 __ j(NOT_EQUAL, deopt);
3890 // Undo untagging by multiplying value by 2.
3891 // [reg + reg + disp8] has a shorter encoding than [reg*2 + disp32]
3892 __ movq(value,
3893 compiler::Address(value, value, TIMES_1, Mint::value_offset()));
3894#else
3895 // Cannot speculatively untag because it erases the upper bits needed to
3896 // dereference when it is a Mint.
3897 compiler::Label not_smi;
3898 __ BranchIfNotSmi(value, &not_smi, compiler::Assembler::kNearJump);
3899 __ SmiUntagAndSignExtend(value);
3901 __ Bind(&not_smi);
3902 __ CompareClassId(value, kMintCid);
3903 __ j(NOT_EQUAL, deopt);
3904 __ movq(value, compiler::FieldAddress(value, Mint::value_offset()));
3905#endif
3906 __ Bind(&done);
3907 }
3908
3909 // TODO(vegorov): as it is implemented right now truncating unboxing would
3910 // leave "garbage" in the higher word.
3911 if (!is_truncating() && (deopt != nullptr)) {
3912 ASSERT(representation() == kUnboxedInt32);
3913 Register temp = locs()->temp(0).reg();
3914 __ movsxd(temp, value);
3915 __ cmpq(temp, value);
3916 __ j(NOT_EQUAL, deopt);
3917 }
3918}
3919
3920LocationSummary* BoxInteger32Instr::MakeLocationSummary(Zone* zone,
3921 bool opt) const {
3922 ASSERT((from_representation() == kUnboxedInt32) ||
3923 (from_representation() == kUnboxedUint32));
3924#if !defined(DART_COMPRESSED_POINTERS)
3925 // ValueFitsSmi() may be overly conservative and false because we only
3926 // perform range analysis during optimized compilation.
3927 const bool kMayAllocateMint = false;
3928#else
3929 const bool kMayAllocateMint = !ValueFitsSmi();
3930#endif
3931 const intptr_t kNumInputs = 1;
3932 const intptr_t kNumTemps = kMayAllocateMint ? 1 : 0;
3933 LocationSummary* summary = new (zone)
3934 LocationSummary(zone, kNumInputs, kNumTemps,
3935 kMayAllocateMint ? LocationSummary::kCallOnSlowPath
3937 summary->set_in(0, Location::RequiresRegister());
3938 summary->set_out(0, Location::RequiresRegister());
3939 if (kMayAllocateMint) {
3940 summary->set_temp(0, Location::RequiresRegister());
3941 }
3942 return summary;
3943}
3944
3945void BoxInteger32Instr::EmitNativeCode(FlowGraphCompiler* compiler) {
3946 const Register value = locs()->in(0).reg();
3947 const Register out = locs()->out(0).reg();
3948 ASSERT(value != out);
3949
3950#if !defined(DART_COMPRESSED_POINTERS)
3951 ASSERT(kSmiTagSize == 1);
3952 if (from_representation() == kUnboxedInt32) {
3953 __ movsxd(out, value);
3954 } else {
3955 ASSERT(from_representation() == kUnboxedUint32);
3956 __ movl(out, value);
3957 }
3958 __ SmiTag(out);
3959#else
3960 compiler::Label done;
3961 if (from_representation() == kUnboxedInt32) {
3962 __ MoveRegister(out, value);
3963 __ addl(out, out);
3964 __ movsxd(out, out); // Does not affect flags.
3965 if (ValueFitsSmi()) {
3966 return;
3967 }
3968 __ j(NO_OVERFLOW, &done);
3969 } else {
3970 __ movl(out, value);
3971 __ SmiTag(out);
3972 if (ValueFitsSmi()) {
3973 return;
3974 }
3975 __ TestImmediate(value, compiler::Immediate(0xC0000000LL));
3976 __ j(ZERO, &done);
3977 }
3978 // Allocate a mint.
3979 // Value input is a writable register and we have to inform the compiler of
3980 // the type so it can be preserved untagged on the slow path
3981 locs()->live_registers()->Add(locs()->in(0), from_representation());
3982 const Register temp = locs()->temp(0).reg();
3984 temp);
3985 if (from_representation() == kUnboxedInt32) {
3986 __ movsxd(temp, value); // Sign-extend.
3987 } else {
3988 __ movl(temp, value); // Zero-extend.
3989 }
3990 __ movq(compiler::FieldAddress(out, Mint::value_offset()), temp);
3991 __ Bind(&done);
3992#endif
3993}
3994
3995LocationSummary* BoxInt64Instr::MakeLocationSummary(Zone* zone,
3996 bool opt) const {
3997 const intptr_t kNumInputs = 1;
3998 const intptr_t kNumTemps = ValueFitsSmi() ? 0 : 1;
3999 // Shared slow path is used in BoxInt64Instr::EmitNativeCode in
4000 // precompiled mode and only after VM isolate stubs where
4001 // replaced with isolate-specific stubs.
4002 auto object_store = IsolateGroup::Current()->object_store();
4003 const bool stubs_in_vm_isolate =
4004 object_store->allocate_mint_with_fpu_regs_stub()
4005 ->untag()
4006 ->InVMIsolateHeap() ||
4007 object_store->allocate_mint_without_fpu_regs_stub()
4008 ->untag()
4009 ->InVMIsolateHeap();
4010 const bool shared_slow_path_call =
4011 SlowPathSharingSupported(opt) && !stubs_in_vm_isolate;
4012 LocationSummary* summary = new (zone) LocationSummary(
4013 zone, kNumInputs, kNumTemps,
4014 ValueFitsSmi()
4016 : ((shared_slow_path_call ? LocationSummary::kCallOnSharedSlowPath
4018 summary->set_in(0, Location::RequiresRegister());
4019 if (ValueFitsSmi()) {
4020 summary->set_out(0, Location::RequiresRegister());
4021 } else if (shared_slow_path_call) {
4022 summary->set_out(0,
4025 } else {
4026 summary->set_out(0, Location::RequiresRegister());
4027 summary->set_temp(0, Location::RequiresRegister());
4028 }
4029 return summary;
4030}
4031
4032void BoxInt64Instr::EmitNativeCode(FlowGraphCompiler* compiler) {
4033 const Register out = locs()->out(0).reg();
4034 const Register value = locs()->in(0).reg();
4035#if !defined(DART_COMPRESSED_POINTERS)
4036 __ MoveRegister(out, value);
4037 __ SmiTag(out);
4038 if (ValueFitsSmi()) {
4039 return;
4040 }
4041 // If the value doesn't fit in a smi, the tagging changes the sign,
4042 // which causes the overflow flag to be set.
4043 compiler::Label done;
4044 const Register temp = locs()->temp(0).reg();
4045 __ j(NO_OVERFLOW, &done);
4046#else
4047 __ leaq(out, compiler::Address(value, value, TIMES_1, 0));
4048 if (ValueFitsSmi()) {
4049 return;
4050 }
4051 compiler::Label done;
4052 const Register temp = locs()->temp(0).reg();
4053 __ movq(temp, value);
4054 __ sarq(temp, compiler::Immediate(30));
4055 __ addq(temp, compiler::Immediate(1));
4056 __ cmpq(temp, compiler::Immediate(2));
4057 __ j(BELOW, &done);
4058#endif
4059
4060 if (compiler->intrinsic_mode()) {
4061 __ TryAllocate(compiler->mint_class(),
4062 compiler->intrinsic_slow_path_label(),
4064 } else if (locs()->call_on_shared_slow_path()) {
4065 const bool has_frame = compiler->flow_graph().graph_entry()->NeedsFrame();
4066 if (!has_frame) {
4067 ASSERT(__ constant_pool_allowed());
4068 __ set_constant_pool_allowed(false);
4069 __ EnterDartFrame(0);
4070 }
4071 auto object_store = compiler->isolate_group()->object_store();
4072 const bool live_fpu_regs = locs()->live_registers()->FpuRegisterCount() > 0;
4073 const auto& stub = Code::ZoneHandle(
4074 compiler->zone(),
4075 live_fpu_regs ? object_store->allocate_mint_with_fpu_regs_stub()
4076 : object_store->allocate_mint_without_fpu_regs_stub());
4077
4078 ASSERT(!locs()->live_registers()->ContainsRegister(
4080 auto extended_env = compiler->SlowPathEnvironmentFor(this, 0);
4081 compiler->GenerateStubCall(source(), stub, UntaggedPcDescriptors::kOther,
4082 locs(), DeoptId::kNone, extended_env);
4083 if (!has_frame) {
4084 __ LeaveDartFrame();
4085 __ set_constant_pool_allowed(true);
4086 }
4087 } else {
4089 temp);
4090 }
4091
4092 __ movq(compiler::FieldAddress(out, Mint::value_offset()), value);
4093 __ Bind(&done);
4094}
4095
4096LocationSummary* BinaryDoubleOpInstr::MakeLocationSummary(Zone* zone,
4097 bool opt) const {
4098 const intptr_t kNumInputs = 2;
4099 const intptr_t kNumTemps = 0;
4100 LocationSummary* summary = new (zone)
4101 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
4103 summary->set_in(1, Location::RequiresFpuRegister());
4104 summary->set_out(0, Location::SameAsFirstInput());
4105 return summary;
4106}
4107
4108void BinaryDoubleOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
4109 XmmRegister left = locs()->in(0).fpu_reg();
4110 XmmRegister right = locs()->in(1).fpu_reg();
4111
4112 ASSERT(locs()->out(0).fpu_reg() == left);
4113
4114 switch (op_kind()) {
4115 case Token::kADD:
4116 __ addsd(left, right);
4117 break;
4118 case Token::kSUB:
4119 __ subsd(left, right);
4120 break;
4121 case Token::kMUL:
4122 __ mulsd(left, right);
4123 break;
4124 case Token::kDIV:
4125 __ divsd(left, right);
4126 break;
4127 default:
4128 UNREACHABLE();
4129 }
4130}
4131
4132LocationSummary* DoubleTestOpInstr::MakeLocationSummary(Zone* zone,
4133 bool opt) const {
4134 const intptr_t kNumInputs = 1;
4135 const intptr_t kNumTemps =
4136 op_kind() == MethodRecognizer::kDouble_getIsNegative
4137 ? 2
4138 : (op_kind() == MethodRecognizer::kDouble_getIsInfinite ? 1 : 0);
4139 LocationSummary* summary = new (zone)
4140 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
4141 summary->set_in(0, Location::RequiresFpuRegister());
4142 if (kNumTemps > 0) {
4143 summary->set_temp(0, Location::RequiresRegister());
4144 if (op_kind() == MethodRecognizer::kDouble_getIsNegative) {
4145 summary->set_temp(1, Location::RequiresFpuRegister());
4146 }
4147 }
4148 summary->set_out(0, Location::RequiresRegister());
4149 return summary;
4150}
4151
4152Condition DoubleTestOpInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
4153 BranchLabels labels) {
4154 ASSERT(compiler->is_optimizing());
4155 const XmmRegister value = locs()->in(0).fpu_reg();
4156 const bool is_negated = kind() != Token::kEQ;
4157
4158 switch (op_kind()) {
4159 case MethodRecognizer::kDouble_getIsNaN: {
4160 __ comisd(value, value);
4161 return is_negated ? PARITY_ODD : PARITY_EVEN;
4162 }
4163 case MethodRecognizer::kDouble_getIsInfinite: {
4164 const Register temp = locs()->temp(0).reg();
4165 __ AddImmediate(RSP, compiler::Immediate(-kDoubleSize));
4166 __ movsd(compiler::Address(RSP, 0), value);
4167 __ movq(temp, compiler::Address(RSP, 0));
4168 __ AddImmediate(RSP, compiler::Immediate(kDoubleSize));
4169 // Mask off the sign.
4170 __ AndImmediate(temp, compiler::Immediate(0x7FFFFFFFFFFFFFFFLL));
4171 // Compare with +infinity.
4172 __ CompareImmediate(temp, compiler::Immediate(0x7FF0000000000000LL));
4173 return is_negated ? NOT_EQUAL : EQUAL;
4174 }
4175 case MethodRecognizer::kDouble_getIsNegative: {
4176 const Register temp = locs()->temp(0).reg();
4177 const FpuRegister temp_fpu = locs()->temp(1).fpu_reg();
4178 compiler::Label not_zero;
4179 __ xorpd(temp_fpu, temp_fpu);
4180 __ comisd(value, temp_fpu);
4181 // If it's NaN, it's not negative.
4182 __ j(PARITY_EVEN, is_negated ? labels.true_label : labels.false_label);
4183 // Looking at the sign bit also takes care of signed zero.
4184 __ movmskpd(temp, value);
4185 __ TestImmediate(temp, compiler::Immediate(1));
4186 return is_negated ? EQUAL : NOT_EQUAL;
4187 }
4188 default:
4189 UNREACHABLE();
4190 }
4191}
4192
4193// SIMD
4194
4195#define DEFINE_EMIT(Name, Args) \
4196 static void Emit##Name(FlowGraphCompiler* compiler, SimdOpInstr* instr, \
4197 PP_APPLY(PP_UNPACK, Args))
4198
4199#define SIMD_OP_FLOAT_ARITH(V, Name, op) \
4200 V(Float32x4##Name, op##ps) \
4201 V(Float64x2##Name, op##pd)
4202
4203#define SIMD_OP_SIMPLE_BINARY(V) \
4204 SIMD_OP_FLOAT_ARITH(V, Add, add) \
4205 SIMD_OP_FLOAT_ARITH(V, Sub, sub) \
4206 SIMD_OP_FLOAT_ARITH(V, Mul, mul) \
4207 SIMD_OP_FLOAT_ARITH(V, Div, div) \
4208 SIMD_OP_FLOAT_ARITH(V, Min, min) \
4209 SIMD_OP_FLOAT_ARITH(V, Max, max) \
4210 V(Int32x4Add, addpl) \
4211 V(Int32x4Sub, subpl) \
4212 V(Int32x4BitAnd, andps) \
4213 V(Int32x4BitOr, orps) \
4214 V(Int32x4BitXor, xorps) \
4215 V(Float32x4Equal, cmppseq) \
4216 V(Float32x4NotEqual, cmppsneq) \
4217 V(Float32x4LessThan, cmppslt) \
4218 V(Float32x4LessThanOrEqual, cmppsle)
4219
4220DEFINE_EMIT(SimdBinaryOp,
4221 (SameAsFirstInput, XmmRegister left, XmmRegister right)) {
4222 switch (instr->kind()) {
4223#define EMIT(Name, op) \
4224 case SimdOpInstr::k##Name: \
4225 __ op(left, right); \
4226 break;
4227 SIMD_OP_SIMPLE_BINARY(EMIT)
4228#undef EMIT
4229 case SimdOpInstr::kFloat32x4Scale:
4230 __ cvtsd2ss(left, left);
4231 __ shufps(left, left, compiler::Immediate(0x00));
4232 __ mulps(left, right);
4233 break;
4234 case SimdOpInstr::kFloat32x4ShuffleMix:
4235 case SimdOpInstr::kInt32x4ShuffleMix:
4236 __ shufps(left, right, compiler::Immediate(instr->mask()));
4237 break;
4238 case SimdOpInstr::kFloat64x2FromDoubles:
4239 // shufpd mask 0x0 results in:
4240 // Lower 64-bits of left = Lower 64-bits of left.
4241 // Upper 64-bits of left = Lower 64-bits of right.
4242 __ shufpd(left, right, compiler::Immediate(0x0));
4243 break;
4244 case SimdOpInstr::kFloat64x2Scale:
4245 __ shufpd(right, right, compiler::Immediate(0x00));
4246 __ mulpd(left, right);
4247 break;
4248 case SimdOpInstr::kFloat64x2WithX:
4249 case SimdOpInstr::kFloat64x2WithY: {
4250 // TODO(dartbug.com/30949) avoid transfer through memory.
4251 COMPILE_ASSERT(SimdOpInstr::kFloat64x2WithY ==
4252 (SimdOpInstr::kFloat64x2WithX + 1));
4253 const intptr_t lane_index = instr->kind() - SimdOpInstr::kFloat64x2WithX;
4254 ASSERT(0 <= lane_index && lane_index < 2);
4255
4256 __ SubImmediate(RSP, compiler::Immediate(kSimd128Size));
4257 __ movups(compiler::Address(RSP, 0), left);
4258 __ movsd(compiler::Address(RSP, lane_index * kDoubleSize), right);
4259 __ movups(left, compiler::Address(RSP, 0));
4260 __ AddImmediate(RSP, compiler::Immediate(kSimd128Size));
4261 break;
4262 }
4263 case SimdOpInstr::kFloat32x4WithX:
4264 case SimdOpInstr::kFloat32x4WithY:
4265 case SimdOpInstr::kFloat32x4WithZ:
4266 case SimdOpInstr::kFloat32x4WithW: {
4267 // TODO(dartbug.com/30949) avoid transfer through memory. SSE4.1 has
4268 // insertps. SSE2 these instructions can be implemented via a combination
4269 // of shufps/movss/movlhps.
4271 SimdOpInstr::kFloat32x4WithY == (SimdOpInstr::kFloat32x4WithX + 1) &&
4272 SimdOpInstr::kFloat32x4WithZ == (SimdOpInstr::kFloat32x4WithX + 2) &&
4273 SimdOpInstr::kFloat32x4WithW == (SimdOpInstr::kFloat32x4WithX + 3));
4274 const intptr_t lane_index = instr->kind() - SimdOpInstr::kFloat32x4WithX;
4275 ASSERT(0 <= lane_index && lane_index < 4);
4276 __ cvtsd2ss(left, left);
4277 __ SubImmediate(RSP, compiler::Immediate(kSimd128Size));
4278 __ movups(compiler::Address(RSP, 0), right);
4279 __ movss(compiler::Address(RSP, lane_index * kFloatSize), left);
4280 __ movups(left, compiler::Address(RSP, 0));
4281 __ AddImmediate(RSP, compiler::Immediate(kSimd128Size));
4282 break;
4283 }
4284
4285 default:
4286 UNREACHABLE();
4287 }
4288}
4289
4290#define SIMD_OP_SIMPLE_UNARY(V) \
4291 SIMD_OP_FLOAT_ARITH(V, Sqrt, sqrt) \
4292 SIMD_OP_FLOAT_ARITH(V, Negate, negate) \
4293 SIMD_OP_FLOAT_ARITH(V, Abs, abs) \
4294 V(Float32x4Reciprocal, rcpps) \
4295 V(Float32x4ReciprocalSqrt, rsqrtps)
4296
4297DEFINE_EMIT(SimdUnaryOp, (SameAsFirstInput, XmmRegister value)) {
4298 // TODO(dartbug.com/30949) select better register constraints to avoid
4299 // redundant move of input into a different register.
4300 switch (instr->kind()) {
4301#define EMIT(Name, op) \
4302 case SimdOpInstr::k##Name: \
4303 __ op(value, value); \
4304 break;
4305 SIMD_OP_SIMPLE_UNARY(EMIT)
4306#undef EMIT
4307 case SimdOpInstr::kFloat32x4GetX:
4308 // Shuffle not necessary.
4309 __ cvtss2sd(value, value);
4310 break;
4311 case SimdOpInstr::kFloat32x4GetY:
4312 __ shufps(value, value, compiler::Immediate(0x55));
4313 __ cvtss2sd(value, value);
4314 break;
4315 case SimdOpInstr::kFloat32x4GetZ:
4316 __ shufps(value, value, compiler::Immediate(0xAA));
4317 __ cvtss2sd(value, value);
4318 break;
4319 case SimdOpInstr::kFloat32x4GetW:
4320 __ shufps(value, value, compiler::Immediate(0xFF));
4321 __ cvtss2sd(value, value);
4322 break;
4323 case SimdOpInstr::kFloat32x4Shuffle:
4324 case SimdOpInstr::kInt32x4Shuffle:
4325 __ shufps(value, value, compiler::Immediate(instr->mask()));
4326 break;
4327 case SimdOpInstr::kFloat32x4Splat:
4328 // Convert to Float32.
4329 __ cvtsd2ss(value, value);
4330 // Splat across all lanes.
4331 __ shufps(value, value, compiler::Immediate(0x00));
4332 break;
4333 case SimdOpInstr::kFloat32x4ToFloat64x2:
4334 __ cvtps2pd(value, value);
4335 break;
4336 case SimdOpInstr::kFloat64x2ToFloat32x4:
4337 __ cvtpd2ps(value, value);
4338 break;
4339 case SimdOpInstr::kInt32x4ToFloat32x4:
4340 case SimdOpInstr::kFloat32x4ToInt32x4:
4341 // TODO(dartbug.com/30949) these operations are essentially nop and should
4342 // not generate any code. They should be removed from the graph before
4343 // code generation.
4344 break;
4345 case SimdOpInstr::kFloat64x2GetX:
4346 // NOP.
4347 break;
4348 case SimdOpInstr::kFloat64x2GetY:
4349 __ shufpd(value, value, compiler::Immediate(0x33));
4350 break;
4351 case SimdOpInstr::kFloat64x2Splat:
4352 __ shufpd(value, value, compiler::Immediate(0x0));
4353 break;
4354 default:
4355 UNREACHABLE();
4356 break;
4357 }
4358}
4359
4360DEFINE_EMIT(SimdGetSignMask, (Register out, XmmRegister value)) {
4361 switch (instr->kind()) {
4362 case SimdOpInstr::kFloat32x4GetSignMask:
4363 case SimdOpInstr::kInt32x4GetSignMask:
4364 __ movmskps(out, value);
4365 break;
4366 case SimdOpInstr::kFloat64x2GetSignMask:
4367 __ movmskpd(out, value);
4368 break;
4369 default:
4370 UNREACHABLE();
4371 break;
4372 }
4373}
4374
4375DEFINE_EMIT(
4376 Float32x4FromDoubles,
4377 (SameAsFirstInput, XmmRegister v0, XmmRegister, XmmRegister, XmmRegister)) {
4378 // TODO(dartbug.com/30949) avoid transfer through memory. SSE4.1 has
4379 // insertps, with SSE2 this instruction can be implemented through unpcklps.
4380 const XmmRegister out = v0;
4381 __ SubImmediate(RSP, compiler::Immediate(kSimd128Size));
4382 for (intptr_t i = 0; i < 4; i++) {
4383 __ cvtsd2ss(out, instr->locs()->in(i).fpu_reg());
4384 __ movss(compiler::Address(RSP, i * kFloatSize), out);
4385 }
4386 __ movups(out, compiler::Address(RSP, 0));
4387 __ AddImmediate(RSP, compiler::Immediate(kSimd128Size));
4388}
4389
4390DEFINE_EMIT(Float32x4Zero, (XmmRegister value)) {
4391 __ xorps(value, value);
4392}
4393
4394DEFINE_EMIT(Float64x2Zero, (XmmRegister value)) {
4395 __ xorpd(value, value);
4396}
4397
4398DEFINE_EMIT(Float32x4Clamp,
4399 (SameAsFirstInput,
4402 XmmRegister upper)) {
4403 __ minps(value, upper);
4404 __ maxps(value, lower);
4405}
4406
4407DEFINE_EMIT(Float64x2Clamp,
4408 (SameAsFirstInput,
4411 XmmRegister upper)) {
4412 __ minpd(value, upper);
4413 __ maxpd(value, lower);
4414}
4415
4416DEFINE_EMIT(Int32x4FromInts,
4418 // TODO(dartbug.com/30949) avoid transfer through memory.
4419 __ SubImmediate(RSP, compiler::Immediate(kSimd128Size));
4420 for (intptr_t i = 0; i < 4; i++) {
4421 __ movl(compiler::Address(RSP, i * kInt32Size), instr->locs()->in(i).reg());
4422 }
4423 __ movups(result, compiler::Address(RSP, 0));
4424 __ AddImmediate(RSP, compiler::Immediate(kSimd128Size));
4425}
4426
4427DEFINE_EMIT(Int32x4FromBools,
4429 Register,
4430 Register,
4431 Register,
4432 Register,
4433 Temp<Register> temp)) {
4434 // TODO(dartbug.com/30949) avoid transfer through memory.
4435 __ SubImmediate(RSP, compiler::Immediate(kSimd128Size));
4436 for (intptr_t i = 0; i < 4; i++) {
4437 compiler::Label done, load_false;
4438 __ xorq(temp, temp);
4439 __ CompareObject(instr->locs()->in(i).reg(), Bool::True());
4440 __ setcc(EQUAL, ByteRegisterOf(temp));
4441 __ negl(temp); // temp = input ? -1 : 0
4442 __ movl(compiler::Address(RSP, kInt32Size * i), temp);
4443 }
4444 __ movups(result, compiler::Address(RSP, 0));
4445 __ AddImmediate(RSP, compiler::Immediate(kSimd128Size));
4446}
4447
4448static void EmitToBoolean(FlowGraphCompiler* compiler, Register out) {
4450 __ testl(out, out);
4451 __ setcc(ZERO, ByteRegisterOf(out));
4452 __ movzxb(out, out);
4453 __ movq(out,
4454 compiler::Address(THR, out, TIMES_8, Thread::bool_true_offset()));
4455}
4456
4457DEFINE_EMIT(Int32x4GetFlagZorW,
4458 (Register out, XmmRegister value, Temp<XmmRegister> temp)) {
4459 __ movhlps(temp, value); // extract upper half.
4460 __ movq(out, temp);
4461 if (instr->kind() == SimdOpInstr::kInt32x4GetFlagW) {
4462 __ shrq(out, compiler::Immediate(32)); // extract upper 32bits.
4463 }
4464 EmitToBoolean(compiler, out);
4465}
4466
4467DEFINE_EMIT(Int32x4GetFlagXorY, (Register out, XmmRegister value)) {
4468 __ movq(out, value);
4469 if (instr->kind() == SimdOpInstr::kInt32x4GetFlagY) {
4470 __ shrq(out, compiler::Immediate(32)); // extract upper 32bits.
4471 }
4472 EmitToBoolean(compiler, out);
4473}
4474
4475DEFINE_EMIT(
4476 Int32x4WithFlag,
4477 (SameAsFirstInput, XmmRegister mask, Register flag, Temp<Register> temp)) {
4478 // TODO(dartbug.com/30949) avoid transfer through memory.
4480 SimdOpInstr::kInt32x4WithFlagY == (SimdOpInstr::kInt32x4WithFlagX + 1) &&
4481 SimdOpInstr::kInt32x4WithFlagZ == (SimdOpInstr::kInt32x4WithFlagX + 2) &&
4482 SimdOpInstr::kInt32x4WithFlagW == (SimdOpInstr::kInt32x4WithFlagX + 3));
4483 const intptr_t lane_index = instr->kind() - SimdOpInstr::kInt32x4WithFlagX;
4484 ASSERT(0 <= lane_index && lane_index < 4);
4485 __ SubImmediate(RSP, compiler::Immediate(kSimd128Size));
4486 __ movups(compiler::Address(RSP, 0), mask);
4487
4488 // temp = flag == true ? -1 : 0
4489 __ xorq(temp, temp);
4490 __ CompareObject(flag, Bool::True());
4491 __ setcc(EQUAL, ByteRegisterOf(temp));
4492 __ negl(temp);
4493
4494 __ movl(compiler::Address(RSP, lane_index * kInt32Size), temp);
4495 __ movups(mask, compiler::Address(RSP, 0));
4496 __ AddImmediate(RSP, compiler::Immediate(kSimd128Size));
4497}
4498
4499DEFINE_EMIT(Int32x4Select,
4500 (SameAsFirstInput,
4501 XmmRegister mask,
4502 XmmRegister trueValue,
4503 XmmRegister falseValue,
4504 Temp<XmmRegister> temp)) {
4505 // Copy mask.
4506 __ movaps(temp, mask);
4507 // Invert it.
4508 __ notps(temp, temp);
4509 // mask = mask & trueValue.
4510 __ andps(mask, trueValue);
4511 // temp = temp & falseValue.
4512 __ andps(temp, falseValue);
4513 // out = mask | temp.
4514 __ orps(mask, temp);
4515}
4516
4517// Map SimdOpInstr::Kind-s to corresponding emit functions. Uses the following
4518// format:
4519//
4520// CASE(OpA) CASE(OpB) ____(Emitter) - Emitter is used to emit OpA and OpB.
4521// SIMPLE(OpA) - Emitter with name OpA is used to emit OpA.
4522//
4523#define SIMD_OP_VARIANTS(CASE, ____, SIMPLE) \
4524 SIMD_OP_SIMPLE_BINARY(CASE) \
4525 CASE(Float32x4Scale) \
4526 CASE(Float32x4ShuffleMix) \
4527 CASE(Int32x4ShuffleMix) \
4528 CASE(Float64x2FromDoubles) \
4529 CASE(Float64x2Scale) \
4530 CASE(Float64x2WithX) \
4531 CASE(Float64x2WithY) \
4532 CASE(Float32x4WithX) \
4533 CASE(Float32x4WithY) \
4534 CASE(Float32x4WithZ) \
4535 CASE(Float32x4WithW) \
4536 ____(SimdBinaryOp) \
4537 SIMD_OP_SIMPLE_UNARY(CASE) \
4538 CASE(Float32x4GetX) \
4539 CASE(Float32x4GetY) \
4540 CASE(Float32x4GetZ) \
4541 CASE(Float32x4GetW) \
4542 CASE(Float32x4Shuffle) \
4543 CASE(Int32x4Shuffle) \
4544 CASE(Float32x4Splat) \
4545 CASE(Float32x4ToFloat64x2) \
4546 CASE(Float64x2ToFloat32x4) \
4547 CASE(Int32x4ToFloat32x4) \
4548 CASE(Float32x4ToInt32x4) \
4549 CASE(Float64x2GetX) \
4550 CASE(Float64x2GetY) \
4551 CASE(Float64x2Splat) \
4552 ____(SimdUnaryOp) \
4553 CASE(Float32x4GetSignMask) \
4554 CASE(Int32x4GetSignMask) \
4555 CASE(Float64x2GetSignMask) \
4556 ____(SimdGetSignMask) \
4557 SIMPLE(Float32x4FromDoubles) \
4558 SIMPLE(Int32x4FromInts) \
4559 SIMPLE(Int32x4FromBools) \
4560 SIMPLE(Float32x4Zero) \
4561 SIMPLE(Float64x2Zero) \
4562 SIMPLE(Float32x4Clamp) \
4563 SIMPLE(Float64x2Clamp) \
4564 CASE(Int32x4GetFlagX) \
4565 CASE(Int32x4GetFlagY) \
4566 ____(Int32x4GetFlagXorY) \
4567 CASE(Int32x4GetFlagZ) \
4568 CASE(Int32x4GetFlagW) \
4569 ____(Int32x4GetFlagZorW) \
4570 CASE(Int32x4WithFlagX) \
4571 CASE(Int32x4WithFlagY) \
4572 CASE(Int32x4WithFlagZ) \
4573 CASE(Int32x4WithFlagW) \
4574 ____(Int32x4WithFlag) \
4575 SIMPLE(Int32x4Select)
4576
4577LocationSummary* SimdOpInstr::MakeLocationSummary(Zone* zone, bool opt) const {
4578 switch (kind()) {
4579#define CASE(Name, ...) case k##Name:
4580#define EMIT(Name) \
4581 return MakeLocationSummaryFromEmitter(zone, this, &Emit##Name);
4582#define SIMPLE(Name) CASE(Name) EMIT(Name)
4583 SIMD_OP_VARIANTS(CASE, EMIT, SIMPLE)
4584#undef CASE
4585#undef EMIT
4586#undef SIMPLE
4587 case SimdOpInstr::kFloat32x4GreaterThan:
4588 case SimdOpInstr::kFloat32x4GreaterThanOrEqual:
4589 case kIllegalSimdOp:
4590 break;
4591 }
4592 UNREACHABLE();
4593 return nullptr;
4594}
4595
4596void SimdOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
4597 switch (kind()) {
4598#define CASE(Name, ...) case k##Name:
4599#define EMIT(Name) \
4600 InvokeEmitter(compiler, this, &Emit##Name); \
4601 break;
4602#define SIMPLE(Name) CASE(Name) EMIT(Name)
4603 SIMD_OP_VARIANTS(CASE, EMIT, SIMPLE)
4604#undef CASE
4605#undef EMIT
4606#undef SIMPLE
4607 case SimdOpInstr::kFloat32x4GreaterThan:
4608 case SimdOpInstr::kFloat32x4GreaterThanOrEqual:
4609 case kIllegalSimdOp:
4610 UNREACHABLE();
4611 break;
4612 }
4613}
4614
4615#undef DEFINE_EMIT
4616
4618 Zone* zone,
4619 bool opt) const {
4620 const intptr_t kNumTemps = 0;
4621 LocationSummary* summary = new (zone)
4622 LocationSummary(zone, InputCount(), kNumTemps, LocationSummary::kCall);
4627 summary->set_out(0, Location::RegisterLocation(RAX));
4628 return summary;
4629}
4630
4632 compiler::LeafRuntimeScope rt(compiler->assembler(),
4633 /*frame_size=*/0,
4634 /*preserve_registers=*/false);
4635 // Call the function. Parameters are already in their correct spots.
4637}
4638
4639LocationSummary* UnarySmiOpInstr::MakeLocationSummary(Zone* zone,
4640 bool opt) const {
4641 const intptr_t kNumInputs = 1;
4642 return LocationSummary::Make(zone, kNumInputs, Location::SameAsFirstInput(),
4644}
4645
4646void UnarySmiOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
4647 Register value = locs()->in(0).reg();
4648 ASSERT(value == locs()->out(0).reg());
4649 switch (op_kind()) {
4650 case Token::kNEGATE: {
4651 compiler::Label* deopt =
4652 compiler->AddDeoptStub(deopt_id(), ICData::kDeoptUnaryOp);
4653 __ OBJ(neg)(value);
4654 __ j(OVERFLOW, deopt);
4655 break;
4656 }
4657 case Token::kBIT_NOT:
4658 __ notq(value);
4659 // Remove inverted smi-tag.
4660 __ AndImmediate(value, compiler::Immediate(~kSmiTagMask));
4661 break;
4662 default:
4663 UNREACHABLE();
4664 }
4665}
4666
4667LocationSummary* UnaryDoubleOpInstr::MakeLocationSummary(Zone* zone,
4668 bool opt) const {
4669 const intptr_t kNumInputs = 1;
4670 const intptr_t kNumTemps = 0;
4671 LocationSummary* summary = new (zone)
4672 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
4674 if (op_kind() == Token::kSQUARE) {
4675 summary->set_out(0, Location::SameAsFirstInput());
4676 } else {
4677 summary->set_out(0, Location::RequiresFpuRegister());
4678 }
4679 return summary;
4680}
4681
4682void UnaryDoubleOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
4683 ASSERT(representation() == kUnboxedDouble);
4684 XmmRegister result = locs()->out(0).fpu_reg();
4685 XmmRegister value = locs()->in(0).fpu_reg();
4686 switch (op_kind()) {
4687 case Token::kNEGATE:
4688 __ DoubleNegate(result, value);
4689 break;
4690 case Token::kSQRT:
4691 __ sqrtsd(result, value);
4692 break;
4693 case Token::kSQUARE:
4694 ASSERT(result == value);
4695 __ mulsd(result, value);
4696 break;
4697 case Token::kTRUNCATE:
4699 break;
4700 case Token::kFLOOR:
4702 break;
4703 case Token::kCEILING:
4705 break;
4706 default:
4707 UNREACHABLE();
4708 }
4709}
4710
4711LocationSummary* MathMinMaxInstr::MakeLocationSummary(Zone* zone,
4712 bool opt) const {
4713 if (result_cid() == kDoubleCid) {
4714 const intptr_t kNumInputs = 2;
4715 const intptr_t kNumTemps = 1;
4716 LocationSummary* summary = new (zone)
4717 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
4719 summary->set_in(1, Location::RequiresFpuRegister());
4720 // Reuse the left register so that code can be made shorter.
4721 summary->set_out(0, Location::SameAsFirstInput());
4722 summary->set_temp(0, Location::RequiresRegister());
4723 return summary;
4724 }
4725 ASSERT(result_cid() == kSmiCid);
4726 const intptr_t kNumInputs = 2;
4727 const intptr_t kNumTemps = 0;
4728 LocationSummary* summary = new (zone)
4729 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
4730 summary->set_in(0, Location::RequiresRegister());
4731 summary->set_in(1, Location::RequiresRegister());
4732 // Reuse the left register so that code can be made shorter.
4733 summary->set_out(0, Location::SameAsFirstInput());
4734 return summary;
4735}
4736
4737void MathMinMaxInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
4738 ASSERT((op_kind() == MethodRecognizer::kMathMin) ||
4739 (op_kind() == MethodRecognizer::kMathMax));
4740 const bool is_min = op_kind() == MethodRecognizer::kMathMin;
4741 if (result_cid() == kDoubleCid) {
4742 compiler::Label done, returns_nan, are_equal;
4743 XmmRegister left = locs()->in(0).fpu_reg();
4744 XmmRegister right = locs()->in(1).fpu_reg();
4745 XmmRegister result = locs()->out(0).fpu_reg();
4746 Register temp = locs()->temp(0).reg();
4747 __ comisd(left, right);
4750 const Condition double_condition =
4751 is_min ? TokenKindToDoubleCondition(Token::kLT)
4752 : TokenKindToDoubleCondition(Token::kGT);
4753 ASSERT(left == result);
4754 __ j(double_condition, &done, compiler::Assembler::kNearJump);
4755 __ movsd(result, right);
4757
4758 __ Bind(&returns_nan);
4759 __ movq(temp, compiler::Address(THR, Thread::double_nan_address_offset()));
4760 __ movsd(result, compiler::Address(temp, 0));
4762
4763 __ Bind(&are_equal);
4764 compiler::Label left_is_negative;
4765 // Check for negative zero: -0.0 is equal 0.0 but min or max must return
4766 // -0.0 or 0.0 respectively.
4767 // Check for negative left value (get the sign bit):
4768 // - min -> left is negative ? left : right.
4769 // - max -> left is negative ? right : left
4770 // Check the sign bit.
4771 __ movmskpd(temp, left);
4772 __ testq(temp, compiler::Immediate(1));
4773 if (is_min) {
4774 ASSERT(left == result);
4775 __ j(NOT_ZERO, &done,
4776 compiler::Assembler::kNearJump); // Negative -> return left.
4777 } else {
4778 ASSERT(left == result);
4779 __ j(ZERO, &done,
4780 compiler::Assembler::kNearJump); // Positive -> return left.
4781 }
4782 __ movsd(result, right);
4783 __ Bind(&done);
4784 return;
4785 }
4786
4787 ASSERT(result_cid() == kSmiCid);
4788 Register left = locs()->in(0).reg();
4789 Register right = locs()->in(1).reg();
4790 Register result = locs()->out(0).reg();
4791 __ OBJ(cmp)(left, right);
4792 ASSERT(result == left);
4793 if (is_min) {
4794 __ cmovgeq(result, right);
4795 } else {
4796 __ cmovlq(result, right);
4797 }
4798}
4799
4800LocationSummary* Int32ToDoubleInstr::MakeLocationSummary(Zone* zone,
4801 bool opt) const {
4802 const intptr_t kNumInputs = 1;
4803 const intptr_t kNumTemps = 0;
4804 LocationSummary* result = new (zone)
4805 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
4806 result->set_in(0, Location::RequiresRegister());
4808 return result;
4809}
4810
4811void Int32ToDoubleInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
4812 Register value = locs()->in(0).reg();
4813 FpuRegister result = locs()->out(0).fpu_reg();
4814 __ cvtsi2sdl(result, value);
4815}
4816
4817LocationSummary* SmiToDoubleInstr::MakeLocationSummary(Zone* zone,
4818 bool opt) const {
4819 const intptr_t kNumInputs = 1;
4820 const intptr_t kNumTemps = 0;
4821 LocationSummary* result = new (zone)
4822 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
4823 result->set_in(0, Location::WritableRegister());
4825 return result;
4826}
4827
4828void SmiToDoubleInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
4829 Register value = locs()->in(0).reg();
4830 FpuRegister result = locs()->out(0).fpu_reg();
4831 __ SmiUntag(value);
4832 __ OBJ(cvtsi2sd)(result, value);
4833}
4834
4835DEFINE_BACKEND(Int64ToDouble, (FpuRegister result, Register value)) {
4836 __ cvtsi2sdq(result, value);
4837}
4838
4839LocationSummary* DoubleToIntegerInstr::MakeLocationSummary(Zone* zone,
4840 bool opt) const {
4841 const intptr_t kNumInputs = 1;
4842 const intptr_t kNumTemps = 1;
4843 LocationSummary* result = new (zone) LocationSummary(
4844 zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
4846 result->set_out(0, Location::RequiresRegister());
4847 result->set_temp(0, Location::RequiresRegister());
4848 return result;
4849}
4850
4851void DoubleToIntegerInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
4852 const Register result = locs()->out(0).reg();
4853 const Register temp = locs()->temp(0).reg();
4854 XmmRegister value_double = locs()->in(0).fpu_reg();
4855 ASSERT(result != temp);
4856
4857 DoubleToIntegerSlowPath* slow_path =
4858 new DoubleToIntegerSlowPath(this, value_double);
4859 compiler->AddSlowPathCode(slow_path);
4860
4861 if (recognized_kind() != MethodRecognizer::kDoubleToInteger) {
4862 // In JIT mode without --target-unknown-cpu VM knows target CPU features
4863 // at compile time and can pick more optimal representation
4864 // for DoubleToDouble conversion. In AOT mode and with
4865 // --target-unknown-cpu we test if roundsd instruction is available
4866 // at run time and fall back to stub if it isn't.
4867 ASSERT(CompilerState::Current().is_aot() || FLAG_target_unknown_cpu);
4868 if (FLAG_use_slow_path) {
4869 __ jmp(slow_path->entry_label());
4870 __ Bind(slow_path->exit_label());
4871 return;
4872 }
4873 __ cmpb(
4874 compiler::Address(
4875 THR,
4877 compiler::Immediate(0));
4878 __ j(EQUAL, slow_path->entry_label());
4879
4880 __ xorps(FpuTMP, FpuTMP);
4881 switch (recognized_kind()) {
4882 case MethodRecognizer::kDoubleFloorToInt:
4883 __ roundsd(FpuTMP, value_double, compiler::Assembler::kRoundDown);
4884 break;
4885 case MethodRecognizer::kDoubleCeilToInt:
4886 __ roundsd(FpuTMP, value_double, compiler::Assembler::kRoundUp);
4887 break;
4888 default:
4889 UNREACHABLE();
4890 }
4891 value_double = FpuTMP;
4892 }
4893
4894 __ OBJ(cvttsd2si)(result, value_double);
4895 // Overflow is signalled with minint.
4896 // Check for overflow and that it fits into Smi.
4897 __ movq(temp, result);
4898 __ OBJ(shl)(temp, compiler::Immediate(1));
4899 __ j(OVERFLOW, slow_path->entry_label());
4900 __ SmiTag(result);
4901 __ Bind(slow_path->exit_label());
4902}
4903
4904LocationSummary* DoubleToSmiInstr::MakeLocationSummary(Zone* zone,
4905 bool opt) const {
4906 const intptr_t kNumInputs = 1;
4907 const intptr_t kNumTemps = 1;
4908 LocationSummary* result = new (zone)
4909 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
4911 result->set_out(0, Location::RequiresRegister());
4912 result->set_temp(0, Location::RequiresRegister());
4913 return result;
4914}
4915
4916void DoubleToSmiInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
4917 compiler::Label* deopt =
4918 compiler->AddDeoptStub(deopt_id(), ICData::kDeoptDoubleToSmi);
4919 Register result = locs()->out(0).reg();
4920 XmmRegister value = locs()->in(0).fpu_reg();
4921 Register temp = locs()->temp(0).reg();
4922
4923 __ OBJ(cvttsd2si)(result, value);
4924 // Overflow is signalled with minint.
4925 compiler::Label do_call, done;
4926 // Check for overflow and that it fits into Smi.
4927 __ movq(temp, result);
4928 __ OBJ(shl)(temp, compiler::Immediate(1));
4929 __ j(OVERFLOW, deopt);
4930 __ SmiTag(result);
4931}
4932
4933LocationSummary* DoubleToFloatInstr::MakeLocationSummary(Zone* zone,
4934 bool opt) const {
4935 const intptr_t kNumInputs = 1;
4936 const intptr_t kNumTemps = 0;
4937 LocationSummary* result = new (zone)
4938 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
4940 result->set_out(0, Location::SameAsFirstInput());
4941 return result;
4942}
4943
4944void DoubleToFloatInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
4945 __ cvtsd2ss(locs()->out(0).fpu_reg(), locs()->in(0).fpu_reg());
4946}
4947
4948LocationSummary* FloatToDoubleInstr::MakeLocationSummary(Zone* zone,
4949 bool opt) const {
4950 const intptr_t kNumInputs = 1;
4951 const intptr_t kNumTemps = 0;
4952 LocationSummary* result = new (zone)
4953 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
4955 result->set_out(0, Location::SameAsFirstInput());
4956 return result;
4957}
4958
4959void FloatToDoubleInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
4960 __ cvtss2sd(locs()->out(0).fpu_reg(), locs()->in(0).fpu_reg());
4961}
4962
4963LocationSummary* FloatCompareInstr::MakeLocationSummary(Zone* zone,
4964 bool opt) const {
4965 UNREACHABLE();
4966 return NULL;
4967}
4968
4969void FloatCompareInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
4970 UNREACHABLE();
4971}
4972
4973LocationSummary* InvokeMathCFunctionInstr::MakeLocationSummary(Zone* zone,
4974 bool opt) const {
4975 // Calling convention on x64 uses XMM0 and XMM1 to pass the first two
4976 // double arguments and XMM0 to return the result.
4979
4980 if (recognized_kind() == MethodRecognizer::kMathDoublePow) {
4981 ASSERT(InputCount() == 2);
4982 const intptr_t kNumTemps = 4;
4983 LocationSummary* result = new (zone)
4984 LocationSummary(zone, InputCount(), kNumTemps, LocationSummary::kCall);
4987 result->set_temp(0, Location::RegisterLocation(R13));
4988 // Temp index 1.
4989 result->set_temp(1, Location::RegisterLocation(RAX));
4990 // Temp index 2.
4992 // Block XMM0 for the calling convention.
4995 return result;
4996 }
4997 ASSERT((InputCount() == 1) || (InputCount() == 2));
4998 const intptr_t kNumTemps = 1;
4999 LocationSummary* result = new (zone)
5000 LocationSummary(zone, InputCount(), kNumTemps, LocationSummary::kCall);
5001 result->set_temp(0, Location::RegisterLocation(R13));
5003 if (InputCount() == 2) {
5005 }
5007 return result;
5008}
5009
5010// Pseudo code:
5011// if (exponent == 0.0) return 1.0;
5012// // Speed up simple cases.
5013// if (exponent == 1.0) return base;
5014// if (exponent == 2.0) return base * base;
5015// if (exponent == 3.0) return base * base * base;
5016// if (base == 1.0) return 1.0;
5017// if (base.isNaN || exponent.isNaN) {
5018// return double.NAN;
5019// }
5020// if (base != -Infinity && exponent == 0.5) {
5021// if (base == 0.0) return 0.0;
5022// return sqrt(value);
5023// }
5024// TODO(srdjan): Move into a stub?
5025static void InvokeDoublePow(FlowGraphCompiler* compiler,
5026 InvokeMathCFunctionInstr* instr) {
5027 ASSERT(instr->recognized_kind() == MethodRecognizer::kMathDoublePow);
5028 const intptr_t kInputCount = 2;
5029 ASSERT(instr->InputCount() == kInputCount);
5030 LocationSummary* locs = instr->locs();
5031
5032 XmmRegister base = locs->in(0).fpu_reg();
5033 XmmRegister exp = locs->in(1).fpu_reg();
5034 XmmRegister result = locs->out(0).fpu_reg();
5035 XmmRegister zero_temp =
5036 locs->temp(InvokeMathCFunctionInstr::kDoubleTempIndex).fpu_reg();
5037
5038 __ xorps(zero_temp, zero_temp);
5039 __ LoadDImmediate(result, 1.0);
5040
5041 compiler::Label check_base, skip_call;
5042 // exponent == 0.0 -> return 1.0;
5043 __ comisd(exp, zero_temp);
5045 __ j(EQUAL, &skip_call); // 'result' is 1.0.
5046
5047 // exponent == 1.0 ?
5048 __ comisd(exp, result);
5049 compiler::Label return_base;
5050 __ j(EQUAL, &return_base, compiler::Assembler::kNearJump);
5051
5052 // exponent == 2.0 ?
5053 __ LoadDImmediate(XMM0, 2.0);
5054 __ comisd(exp, XMM0);
5055 compiler::Label return_base_times_2;
5056 __ j(EQUAL, &return_base_times_2, compiler::Assembler::kNearJump);
5057
5058 // exponent == 3.0 ?
5059 __ LoadDImmediate(XMM0, 3.0);
5060 __ comisd(exp, XMM0);
5061 __ j(NOT_EQUAL, &check_base);
5062
5063 // Base times 3.
5064 __ movsd(result, base);
5065 __ mulsd(result, base);
5066 __ mulsd(result, base);
5067 __ jmp(&skip_call);
5068
5069 __ Bind(&return_base);
5070 __ movsd(result, base);
5071 __ jmp(&skip_call);
5072
5073 __ Bind(&return_base_times_2);
5074 __ movsd(result, base);
5075 __ mulsd(result, base);
5076 __ jmp(&skip_call);
5077
5078 __ Bind(&check_base);
5079 // Note: 'exp' could be NaN.
5080
5081 compiler::Label return_nan;
5082 // base == 1.0 -> return 1.0;
5083 __ comisd(base, result);
5085 __ j(EQUAL, &skip_call, compiler::Assembler::kNearJump);
5086 // Note: 'base' could be NaN.
5087 __ comisd(exp, base);
5088 // Neither 'exp' nor 'base' is NaN.
5089 compiler::Label try_sqrt;
5091 // Return NaN.
5092 __ Bind(&return_nan);
5093 __ LoadDImmediate(result, NAN);
5094 __ jmp(&skip_call);
5095
5096 compiler::Label do_pow, return_zero;
5097 __ Bind(&try_sqrt);
5098 // Before calling pow, check if we could use sqrt instead of pow.
5099 __ LoadDImmediate(result, kNegInfinity);
5100 // base == -Infinity -> call pow;
5101 __ comisd(base, result);
5103
5104 // exponent == 0.5 ?
5105 __ LoadDImmediate(result, 0.5);
5106 __ comisd(exp, result);
5108
5109 // base == 0 -> return 0;
5110 __ comisd(base, zero_temp);
5111 __ j(EQUAL, &return_zero, compiler::Assembler::kNearJump);
5112
5113 __ sqrtsd(result, base);
5114 __ jmp(&skip_call, compiler::Assembler::kNearJump);
5115
5116 __ Bind(&return_zero);
5117 __ movsd(result, zero_temp);
5118 __ jmp(&skip_call);
5119
5120 __ Bind(&do_pow);
5121 {
5122 compiler::LeafRuntimeScope rt(compiler->assembler(),
5123 /*frame_size=*/0,
5124 /*preserve_registers=*/false);
5125 __ movaps(XMM0, locs->in(0).fpu_reg());
5126 ASSERT(locs->in(1).fpu_reg() == XMM1);
5127 rt.Call(instr->TargetFunction(), kInputCount);
5128 __ movaps(locs->out(0).fpu_reg(), XMM0);
5129 }
5130 __ Bind(&skip_call);
5131}
5132
5133void InvokeMathCFunctionInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5134 if (recognized_kind() == MethodRecognizer::kMathDoublePow) {
5135 InvokeDoublePow(compiler, this);
5136 return;
5137 }
5138
5139 compiler::LeafRuntimeScope rt(compiler->assembler(),
5140 /*frame_size=*/0,
5141 /*preserve_registers=*/false);
5142 ASSERT(locs()->in(0).fpu_reg() == XMM0);
5143 if (InputCount() == 2) {
5144 ASSERT(locs()->in(1).fpu_reg() == XMM1);
5145 }
5146 rt.Call(TargetFunction(), InputCount());
5147 ASSERT(locs()->out(0).fpu_reg() == XMM0);
5148}
5149
5150LocationSummary* ExtractNthOutputInstr::MakeLocationSummary(Zone* zone,
5151 bool opt) const {
5152 // Only use this instruction in optimized code.
5153 ASSERT(opt);
5154 const intptr_t kNumInputs = 1;
5155 LocationSummary* summary =
5156 new (zone) LocationSummary(zone, kNumInputs, 0, LocationSummary::kNoCall);
5157 if (representation() == kUnboxedDouble) {
5158 if (index() == 0) {
5159 summary->set_in(
5161 } else {
5162 ASSERT(index() == 1);
5163 summary->set_in(
5165 }
5166 summary->set_out(0, Location::RequiresFpuRegister());
5167 } else {
5168 ASSERT(representation() == kTagged);
5169 if (index() == 0) {
5170 summary->set_in(
5172 } else {
5173 ASSERT(index() == 1);
5174 summary->set_in(
5176 }
5177 summary->set_out(0, Location::RequiresRegister());
5178 }
5179 return summary;
5180}
5181
5182void ExtractNthOutputInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5183 ASSERT(locs()->in(0).IsPairLocation());
5184 PairLocation* pair = locs()->in(0).AsPairLocation();
5185 Location in_loc = pair->At(index());
5186 if (representation() == kUnboxedDouble) {
5187 XmmRegister out = locs()->out(0).fpu_reg();
5188 XmmRegister in = in_loc.fpu_reg();
5189 __ movaps(out, in);
5190 } else {
5191 ASSERT(representation() == kTagged);
5192 Register out = locs()->out(0).reg();
5193 Register in = in_loc.reg();
5194 __ movq(out, in);
5195 }
5196}
5197
5198LocationSummary* UnboxLaneInstr::MakeLocationSummary(Zone* zone,
5199 bool opt) const {
5200 UNREACHABLE();
5201 return NULL;
5202}
5203
5204void UnboxLaneInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5205 UNREACHABLE();
5206}
5207
5208LocationSummary* BoxLanesInstr::MakeLocationSummary(Zone* zone,
5209 bool opt) const {
5210 UNREACHABLE();
5211 return NULL;
5212}
5213
5214void BoxLanesInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5215 UNREACHABLE();
5216}
5217
5218LocationSummary* TruncDivModInstr::MakeLocationSummary(Zone* zone,
5219 bool opt) const {
5220 const intptr_t kNumInputs = 2;
5221 const intptr_t kNumTemps = 0;
5222 LocationSummary* summary = new (zone)
5223 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
5224 // Both inputs must be writable because they will be untagged.
5226 summary->set_in(1, Location::WritableRegister());
5227 summary->set_out(0, Location::Pair(Location::RegisterLocation(RAX),
5229 return summary;
5230}
5231
5232void TruncDivModInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5234 compiler::Label* deopt =
5235 compiler->AddDeoptStub(deopt_id(), ICData::kDeoptBinarySmiOp);
5236 Register left = locs()->in(0).reg();
5237 Register right = locs()->in(1).reg();
5238 ASSERT(locs()->out(0).IsPairLocation());
5239 PairLocation* pair = locs()->out(0).AsPairLocation();
5240 Register result1 = pair->At(0).reg();
5241 Register result2 = pair->At(1).reg();
5242 compiler::Label not_32bit, done;
5243 Register temp = RDX;
5244 ASSERT(left == RAX);
5245 ASSERT((right != RDX) && (right != RAX));
5246 ASSERT(result1 == RAX);
5247 ASSERT(result2 == RDX);
5248 if (RangeUtils::CanBeZero(divisor_range())) {
5249 // Handle divide by zero in runtime.
5250 __ OBJ(test)(right, right);
5251 __ j(ZERO, deopt);
5252 }
5253#if !defined(DART_COMPRESSED_POINTERS)
5254 // Check if both operands fit into 32bits as idiv with 64bit operands
5255 // requires twice as many cycles and has much higher latency.
5256 // We are checking this before untagging them to avoid corner case
5257 // dividing INT_MAX by -1 that raises exception because quotient is
5258 // too large for 32bit register.
5259 __ movsxd(temp, left);
5260 __ cmpq(temp, left);
5261 __ j(NOT_EQUAL, &not_32bit);
5262 __ movsxd(temp, right);
5263 __ cmpq(temp, right);
5264 __ j(NOT_EQUAL, &not_32bit);
5265
5266 // Both operands are 31bit smis. Divide using 32bit idiv.
5267 __ SmiUntag(left);
5268 __ SmiUntag(right);
5269 __ cdq();
5270 __ idivl(right);
5271 __ movsxd(RAX, RAX);
5272 __ movsxd(RDX, RDX);
5273 __ jmp(&done);
5274
5275 // Divide using 64bit idiv.
5276 __ Bind(&not_32bit);
5277 __ SmiUntag(left);
5278 __ SmiUntag(right);
5279 __ cqo(); // Sign extend RAX -> RDX:RAX.
5280 __ idivq(right); // RAX: quotient, RDX: remainder.
5281 // Check the corner case of dividing the 'MIN_SMI' with -1, in which
5282 // case we cannot tag the result.
5283 __ CompareImmediate(RAX, compiler::Immediate(0x4000000000000000));
5284 __ j(EQUAL, deopt);
5285 __ Bind(&done);
5286#else
5287 USE(temp);
5288 // Both operands are 31bit smis. Divide using 32bit idiv.
5289 __ SmiUntag(left);
5290 __ SmiUntag(right);
5291 __ cdq();
5292 __ idivl(right);
5293
5294 // Check the corner case of dividing the 'MIN_SMI' with -1, in which
5295 // case we cannot tag the result.
5296 __ cmpl(RAX, compiler::Immediate(0x40000000));
5297 __ j(EQUAL, deopt);
5298 __ movsxd(RAX, RAX);
5299 __ movsxd(RDX, RDX);
5300#endif
5301
5302 // Modulo correction (RDX).
5303 // res = left % right;
5304 // if (res < 0) {
5305 // if (right < 0) {
5306 // res = res - right;
5307 // } else {
5308 // res = res + right;
5309 // }
5310 // }
5311 compiler::Label all_done;
5312 __ cmpq(RDX, compiler::Immediate(0));
5314 // Result is negative, adjust it.
5315 if ((divisor_range() == nullptr) || divisor_range()->Overlaps(-1, 1)) {
5316 compiler::Label subtract;
5317 __ cmpq(right, compiler::Immediate(0));
5319 __ addq(RDX, right);
5320 __ jmp(&all_done, compiler::Assembler::kNearJump);
5321 __ Bind(&subtract);
5322 __ subq(RDX, right);
5323 } else if (divisor_range()->IsPositive()) {
5324 // Right is positive.
5325 __ addq(RDX, right);
5326 } else {
5327 // Right is negative.
5328 __ subq(RDX, right);
5329 }
5330 __ Bind(&all_done);
5331
5332 __ SmiTag(RAX);
5333 __ SmiTag(RDX);
5334 // Note that the result of an integer division/modulo of two
5335 // in-range arguments, cannot create out-of-range result.
5336}
5337
5338// Should be kept in sync with integers.cc Multiply64Hash
5339static void EmitHashIntegerCodeSequence(FlowGraphCompiler* compiler) {
5340 __ movq(RDX, compiler::Immediate(0x2d51));
5341 __ mulq(RDX);
5342 __ xorq(RAX, RDX); // RAX = xor(hi64, lo64)
5343 __ movq(RDX, RAX);
5344 __ shrq(RDX, compiler::Immediate(32));
5345 __ xorq(RAX, RDX);
5346 __ andq(RAX, compiler::Immediate(0x3fffffff));
5347}
5348
5349LocationSummary* HashDoubleOpInstr::MakeLocationSummary(Zone* zone,
5350 bool opt) const {
5351 const intptr_t kNumInputs = 1;
5352 const intptr_t kNumTemps = 2;
5353 LocationSummary* summary = new (zone)
5354 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
5356 summary->set_temp(0, Location::RegisterLocation(RDX));
5357 summary->set_temp(1, Location::RequiresFpuRegister());
5358 summary->set_out(0, Location::RegisterLocation(RAX));
5359 return summary;
5360}
5361
5362void HashDoubleOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5363 const XmmRegister value = locs()->in(0).fpu_reg();
5364 ASSERT(locs()->out(0).reg() == RAX);
5365 ASSERT(locs()->temp(0).reg() == RDX);
5366 const FpuRegister temp_fpu_reg = locs()->temp(1).fpu_reg();
5367
5368 compiler::Label hash_double;
5369
5370 __ cvttsd2siq(RAX, value);
5371 __ cvtsi2sdq(temp_fpu_reg, RAX);
5372 __ comisd(value, temp_fpu_reg);
5373 __ j(PARITY_EVEN, &hash_double); // one of the arguments is NaN
5374 __ j(NOT_EQUAL, &hash_double);
5375
5376 // RAX has int64 value
5377 EmitHashIntegerCodeSequence(compiler);
5378
5379 compiler::Label done;
5380 __ jmp(&done);
5381
5382 __ Bind(&hash_double);
5383 // Convert the double bits to a hash code that fits in a Smi.
5384 __ movq(RAX, value);
5385 __ movq(RDX, RAX);
5386 __ shrq(RDX, compiler::Immediate(32));
5387 __ xorq(RAX, RDX);
5388 __ andq(RAX, compiler::Immediate(compiler::target::kSmiMax));
5389
5390 __ Bind(&done);
5391}
5392
5393LocationSummary* HashIntegerOpInstr::MakeLocationSummary(Zone* zone,
5394 bool opt) const {
5395 const intptr_t kNumInputs = 1;
5396 const intptr_t kNumTemps = 1;
5397 LocationSummary* summary = new (zone)
5398 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
5400 summary->set_out(0, Location::SameAsFirstInput());
5401 summary->set_temp(0, Location::RegisterLocation(RDX));
5402 return summary;
5403}
5404
5405void HashIntegerOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5406 Register value = locs()->in(0).reg();
5407 Register result = locs()->out(0).reg();
5408 Register temp = locs()->temp(0).reg();
5409 ASSERT(value == RAX);
5410 ASSERT(result == RAX);
5411 ASSERT(temp == RDX);
5412
5413 if (smi_) {
5414 __ SmiUntagAndSignExtend(RAX);
5415 } else {
5416 __ LoadFieldFromOffset(RAX, RAX, Mint::value_offset());
5417 }
5418
5419 EmitHashIntegerCodeSequence(compiler);
5420 __ SmiTag(RAX);
5421}
5422
5423LocationSummary* BranchInstr::MakeLocationSummary(Zone* zone, bool opt) const {
5425 // Branches don't produce a result.
5427 return comparison()->locs();
5428}
5429
5430void BranchInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5432}
5433
5434LocationSummary* CheckClassInstr::MakeLocationSummary(Zone* zone,
5435 bool opt) const {
5436 const intptr_t kNumInputs = 1;
5437 const bool need_mask_temp = IsBitTest();
5438 const intptr_t kNumTemps = !IsNullCheck() ? (need_mask_temp ? 2 : 1) : 0;
5439 LocationSummary* summary = new (zone)
5440 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
5441 summary->set_in(0, Location::RequiresRegister());
5442 if (!IsNullCheck()) {
5443 summary->set_temp(0, Location::RequiresRegister());
5444 if (need_mask_temp) {
5445 summary->set_temp(1, Location::RequiresRegister());
5446 }
5447 }
5448 return summary;
5449}
5450
5451void CheckClassInstr::EmitNullCheck(FlowGraphCompiler* compiler,
5452 compiler::Label* deopt) {
5453 __ CompareObject(locs()->in(0).reg(), Object::null_object());
5455 __ j(cond, deopt);
5456}
5457
5458void CheckClassInstr::EmitBitTest(FlowGraphCompiler* compiler,
5459 intptr_t min,
5460 intptr_t max,
5461 intptr_t mask,
5462 compiler::Label* deopt) {
5463 Register biased_cid = locs()->temp(0).reg();
5464 __ subq(biased_cid, compiler::Immediate(min));
5465 __ cmpq(biased_cid, compiler::Immediate(max - min));
5466 __ j(ABOVE, deopt);
5467
5468 Register mask_reg = locs()->temp(1).reg();
5469 __ movq(mask_reg, compiler::Immediate(mask));
5470 __ btq(mask_reg, biased_cid);
5471 __ j(NOT_CARRY, deopt);
5472}
5473
5474int CheckClassInstr::EmitCheckCid(FlowGraphCompiler* compiler,
5475 int bias,
5476 intptr_t cid_start,
5477 intptr_t cid_end,
5478 bool is_last,
5479 compiler::Label* is_ok,
5480 compiler::Label* deopt,
5481 bool use_near_jump) {
5482 Register biased_cid = locs()->temp(0).reg();
5483 Condition no_match, match;
5484 if (cid_start == cid_end) {
5485 __ cmpl(biased_cid, compiler::Immediate(cid_start - bias));
5486 no_match = NOT_EQUAL;
5487 match = EQUAL;
5488 } else {
5489 // For class ID ranges use a subtract followed by an unsigned
5490 // comparison to check both ends of the ranges with one comparison.
5491 __ addl(biased_cid, compiler::Immediate(bias - cid_start));
5492 bias = cid_start;
5493 __ cmpl(biased_cid, compiler::Immediate(cid_end - cid_start));
5494 no_match = ABOVE;
5496 }
5497
5498 if (is_last) {
5499 __ j(no_match, deopt);
5500 } else {
5501 if (use_near_jump) {
5503 } else {
5504 __ j(match, is_ok);
5505 }
5506 }
5507 return bias;
5508}
5509
5510LocationSummary* CheckSmiInstr::MakeLocationSummary(Zone* zone,
5511 bool opt) const {
5512 const intptr_t kNumInputs = 1;
5513 const intptr_t kNumTemps = 0;
5514 LocationSummary* summary = new (zone)
5515 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
5516 summary->set_in(0, Location::RequiresRegister());
5517 return summary;
5518}
5519
5520void CheckSmiInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5521 Register value = locs()->in(0).reg();
5522 compiler::Label* deopt =
5523 compiler->AddDeoptStub(deopt_id(), ICData::kDeoptCheckSmi);
5524 __ BranchIfNotSmi(value, deopt);
5525}
5526
5527void CheckNullInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5528 ThrowErrorSlowPathCode* slow_path = new NullErrorSlowPath(this);
5529 compiler->AddSlowPathCode(slow_path);
5530
5531 Register value_reg = locs()->in(0).reg();
5532 // TODO(dartbug.com/30480): Consider passing `null` literal as an argument
5533 // in order to be able to allocate it on register.
5534 __ CompareObject(value_reg, Object::null_object());
5535 __ BranchIf(EQUAL, slow_path->entry_label());
5536}
5537
5538LocationSummary* CheckClassIdInstr::MakeLocationSummary(Zone* zone,
5539 bool opt) const {
5540 const intptr_t kNumInputs = 1;
5541 const intptr_t kNumTemps = 0;
5542 LocationSummary* summary = new (zone)
5543 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
5544 summary->set_in(0, cids_.IsSingleCid() ? Location::RequiresRegister()
5545 : Location::WritableRegister());
5546 return summary;
5547}
5548
5549void CheckClassIdInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5550 Register value = locs()->in(0).reg();
5551 compiler::Label* deopt =
5552 compiler->AddDeoptStub(deopt_id(), ICData::kDeoptCheckClass);
5553 if (cids_.IsSingleCid()) {
5554 __ CompareImmediate(value,
5555 compiler::Immediate(Smi::RawValue(cids_.cid_start)));
5556 __ j(NOT_ZERO, deopt);
5557 } else {
5558 __ AddImmediate(value,
5559 compiler::Immediate(-Smi::RawValue(cids_.cid_start)));
5560 __ cmpq(value, compiler::Immediate(Smi::RawValue(cids_.Extent())));
5561 __ j(ABOVE, deopt);
5562 }
5563}
5564
5565LocationSummary* CheckArrayBoundInstr::MakeLocationSummary(Zone* zone,
5566 bool opt) const {
5567 const intptr_t kNumInputs = 2;
5568 const intptr_t kNumTemps = 0;
5569 LocationSummary* locs = new (zone)
5570 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
5573 return locs;
5574}
5575
5576void CheckArrayBoundInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5577 uint32_t flags = generalized_ ? ICData::kGeneralized : 0;
5578 compiler::Label* deopt =
5579 compiler->AddDeoptStub(deopt_id(), ICData::kDeoptCheckArrayBound, flags);
5580
5581 Location length_loc = locs()->in(kLengthPos);
5582 Location index_loc = locs()->in(kIndexPos);
5583
5584 if (length_loc.IsConstant() && index_loc.IsConstant()) {
5585 ASSERT((Smi::Cast(length_loc.constant()).Value() <=
5586 Smi::Cast(index_loc.constant()).Value()) ||
5587 (Smi::Cast(index_loc.constant()).Value() < 0));
5588 // Unconditionally deoptimize for constant bounds checks because they
5589 // only occur only when index is out-of-bounds.
5590 __ jmp(deopt);
5591 return;
5592 }
5593
5594 const intptr_t index_cid = index()->Type()->ToCid();
5595 if (index_loc.IsConstant()) {
5596 Register length = length_loc.reg();
5597 const Smi& index = Smi::Cast(index_loc.constant());
5598 __ CompareObject(length, index);
5599 __ j(BELOW_EQUAL, deopt);
5600 } else if (length_loc.IsConstant()) {
5601 const Smi& length = Smi::Cast(length_loc.constant());
5602 Register index = index_loc.reg();
5603 if (index_cid != kSmiCid) {
5604 __ BranchIfNotSmi(index, deopt);
5605 }
5606 if (length.Value() == Smi::kMaxValue) {
5607 __ OBJ(test)(index, index);
5608 __ j(NEGATIVE, deopt);
5609 } else {
5610 __ CompareObject(index, length);
5611 __ j(ABOVE_EQUAL, deopt);
5612 }
5613 } else {
5614 Register length = length_loc.reg();
5615 Register index = index_loc.reg();
5616 if (index_cid != kSmiCid) {
5617 __ BranchIfNotSmi(index, deopt);
5618 }
5619 __ OBJ(cmp)(index, length);
5620 __ j(ABOVE_EQUAL, deopt);
5621 }
5622}
5623
5624LocationSummary* CheckWritableInstr::MakeLocationSummary(Zone* zone,
5625 bool opt) const {
5626 const intptr_t kNumInputs = 1;
5627 const intptr_t kNumTemps = 0;
5628 LocationSummary* locs = new (zone) LocationSummary(
5629 zone, kNumInputs, kNumTemps,
5633 return locs;
5634}
5635
5636void CheckWritableInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5637 WriteErrorSlowPath* slow_path = new WriteErrorSlowPath(this);
5638 compiler->AddSlowPathCode(slow_path);
5639 __ movq(TMP, compiler::FieldAddress(locs()->in(0).reg(),
5641 __ testq(TMP, compiler::Immediate(
5643 __ j(NOT_ZERO, slow_path->entry_label());
5644}
5645
5646class Int64DivideSlowPath : public ThrowErrorSlowPathCode {
5647 public:
5648 Int64DivideSlowPath(BinaryInt64OpInstr* instruction,
5649 Register divisor,
5650 Range* divisor_range)
5651 : ThrowErrorSlowPathCode(instruction,
5652 kIntegerDivisionByZeroExceptionRuntimeEntry),
5653 is_mod_(instruction->op_kind() == Token::kMOD),
5654 divisor_(divisor),
5655 divisor_range_(divisor_range),
5656 div_by_minus_one_label_(),
5657 adjust_sign_label_() {}
5658
5659 void EmitNativeCode(FlowGraphCompiler* compiler) override {
5660 // Handle modulo/division by zero, if needed. Use superclass code.
5661 if (has_divide_by_zero()) {
5663 } else {
5664 __ Bind(entry_label()); // not used, but keeps destructor happy
5666 __ Comment("slow path %s operation (no throw)", name());
5667 }
5668 }
5669 // Handle modulo/division by minus one, if needed.
5670 // Note: an exact -1 divisor is best optimized prior to codegen.
5671 if (has_divide_by_minus_one()) {
5672 __ Bind(div_by_minus_one_label());
5673 if (is_mod_) {
5674 __ xorq(RDX, RDX); // x % -1 = 0
5675 } else {
5676 __ negq(RAX); // x / -1 = -x
5677 }
5678 __ jmp(exit_label());
5679 }
5680 // Adjust modulo for negative sign, optimized for known ranges.
5681 // if (divisor < 0)
5682 // out -= divisor;
5683 // else
5684 // out += divisor;
5685 if (has_adjust_sign()) {
5686 __ Bind(adjust_sign_label());
5687 if (RangeUtils::Overlaps(divisor_range_, -1, 1)) {
5688 // General case.
5689 compiler::Label subtract;
5690 __ testq(divisor_, divisor_);
5692 __ addq(RDX, divisor_);
5693 __ jmp(exit_label());
5694 __ Bind(&subtract);
5695 __ subq(RDX, divisor_);
5696 } else if (divisor_range_->IsPositive()) {
5697 // Always positive.
5698 __ addq(RDX, divisor_);
5699 } else {
5700 // Always negative.
5701 __ subq(RDX, divisor_);
5702 }
5703 __ jmp(exit_label());
5704 }
5705 }
5706
5707 const char* name() override { return "int64 divide"; }
5708
5709 bool has_divide_by_zero() { return RangeUtils::CanBeZero(divisor_range_); }
5710
5711 bool has_divide_by_minus_one() {
5712 return RangeUtils::Overlaps(divisor_range_, -1, -1);
5713 }
5714
5715 bool has_adjust_sign() { return is_mod_; }
5716
5717 bool is_needed() {
5718 return has_divide_by_zero() || has_divide_by_minus_one() ||
5719 has_adjust_sign();
5720 }
5721
5722 compiler::Label* div_by_minus_one_label() {
5723 ASSERT(has_divide_by_minus_one());
5724 return &div_by_minus_one_label_;
5725 }
5726
5727 compiler::Label* adjust_sign_label() {
5728 ASSERT(has_adjust_sign());
5729 return &adjust_sign_label_;
5730 }
5731
5732 private:
5733 bool is_mod_;
5734 Register divisor_;
5735 Range* divisor_range_;
5736 compiler::Label div_by_minus_one_label_;
5737 compiler::Label adjust_sign_label_;
5738};
5739
5740static void EmitInt64ModTruncDiv(FlowGraphCompiler* compiler,
5741 BinaryInt64OpInstr* instruction,
5742 Token::Kind op_kind,
5743 Register left,
5744 Register right,
5745 Register tmp,
5746 Register out) {
5747 ASSERT(op_kind == Token::kMOD || op_kind == Token::kTRUNCDIV);
5748
5749 // Special case 64-bit div/mod by compile-time constant. Note that various
5750 // special constants (such as powers of two) should have been optimized
5751 // earlier in the pipeline. Div or mod by zero falls into general code
5752 // to implement the exception.
5753 if (auto c = instruction->right()->definition()->AsConstant()) {
5754 if (c->value().IsInteger()) {
5755 const int64_t divisor = Integer::Cast(c->value()).AsInt64Value();
5756 if (divisor <= -2 || divisor >= 2) {
5757 // For x DIV c or x MOD c: use magic operations.
5758 compiler::Label pos;
5759 int64_t magic = 0;
5760 int64_t shift = 0;
5762 // RDX:RAX = magic * numerator.
5763 ASSERT(left == RAX);
5764 __ MoveRegister(TMP, RAX); // save numerator
5765 __ LoadImmediate(RAX, compiler::Immediate(magic));
5766 __ imulq(TMP);
5767 // RDX +/-= numerator.
5768 if (divisor > 0 && magic < 0) {
5769 __ addq(RDX, TMP);
5770 } else if (divisor < 0 && magic > 0) {
5771 __ subq(RDX, TMP);
5772 }
5773 // Shift if needed.
5774 if (shift != 0) {
5775 __ sarq(RDX, compiler::Immediate(shift));
5776 }
5777 // RDX += 1 if RDX < 0.
5778 __ movq(RAX, RDX);
5779 __ shrq(RDX, compiler::Immediate(63));
5780 __ addq(RDX, RAX);
5781 // Finalize DIV or MOD.
5782 if (op_kind == Token::kTRUNCDIV) {
5783 ASSERT(out == RAX && tmp == RDX);
5784 __ movq(RAX, RDX);
5785 } else {
5786 ASSERT(out == RDX && tmp == RAX);
5787 __ movq(RAX, TMP);
5788 __ LoadImmediate(TMP, compiler::Immediate(divisor));
5789 __ imulq(RDX, TMP);
5790 __ subq(RAX, RDX);
5791 // Compensate for Dart's Euclidean view of MOD.
5792 __ testq(RAX, RAX);
5793 __ j(GREATER_EQUAL, &pos);
5794 if (divisor > 0) {
5795 __ addq(RAX, TMP);
5796 } else {
5797 __ subq(RAX, TMP);
5798 }
5799 __ Bind(&pos);
5800 __ movq(RDX, RAX);
5801 }
5802 return;
5803 }
5804 }
5805 }
5806
5807 // Prepare a slow path.
5808 Range* right_range = instruction->right()->definition()->range();
5809 Int64DivideSlowPath* slow_path =
5810 new (Z) Int64DivideSlowPath(instruction, right, right_range);
5811
5812 // Handle modulo/division by zero exception on slow path.
5813 if (slow_path->has_divide_by_zero()) {
5814 __ testq(right, right);
5815 __ j(EQUAL, slow_path->entry_label());
5816 }
5817
5818 // Handle modulo/division by minus one explicitly on slow path
5819 // (to avoid arithmetic exception on 0x8000000000000000 / -1).
5820 if (slow_path->has_divide_by_minus_one()) {
5821 __ cmpq(right, compiler::Immediate(-1));
5822 __ j(EQUAL, slow_path->div_by_minus_one_label());
5823 }
5824
5825 // Perform actual operation
5826 // out = left % right
5827 // or
5828 // out = left / right.
5829 //
5830 // Note that since 64-bit division requires twice as many cycles
5831 // and has much higher latency compared to the 32-bit division,
5832 // even for this non-speculative 64-bit path we add a "fast path".
5833 // Integers are untagged at this stage, so testing if sign extending
5834 // the lower half of each operand equals the full operand, effectively
5835 // tests if the values fit in 32-bit operands (and the slightly
5836 // dangerous division by -1 has been handled above already).
5837 ASSERT(left == RAX);
5838 ASSERT(right != RDX); // available at this stage
5839 compiler::Label div_64;
5840 compiler::Label div_merge;
5841 __ movsxd(RDX, left);
5842 __ cmpq(RDX, left);
5844 __ movsxd(RDX, right);
5845 __ cmpq(RDX, right);
5847 __ cdq(); // sign-ext eax into edx:eax
5848 __ idivl(right); // quotient eax, remainder edx
5849 __ movsxd(out, out);
5850 __ jmp(&div_merge, compiler::Assembler::kNearJump);
5851 __ Bind(&div_64);
5852 __ cqo(); // sign-ext rax into rdx:rax
5853 __ idivq(right); // quotient rax, remainder rdx
5854 __ Bind(&div_merge);
5855 if (op_kind == Token::kMOD) {
5856 ASSERT(out == RDX);
5857 ASSERT(tmp == RAX);
5858 // For the % operator, again the idiv instruction does
5859 // not quite do what we want. Adjust for sign on slow path.
5860 __ testq(out, out);
5861 __ j(LESS, slow_path->adjust_sign_label());
5862 } else {
5863 ASSERT(out == RAX);
5864 ASSERT(tmp == RDX);
5865 }
5866
5867 if (slow_path->is_needed()) {
5868 __ Bind(slow_path->exit_label());
5869 compiler->AddSlowPathCode(slow_path);
5870 }
5871}
5872
5873template <typename OperandType>
5874static void EmitInt64Arithmetic(FlowGraphCompiler* compiler,
5875 Token::Kind op_kind,
5876 Register left,
5877 const OperandType& right) {
5878 switch (op_kind) {
5879 case Token::kADD:
5880 __ addq(left, right);
5881 break;
5882 case Token::kSUB:
5883 __ subq(left, right);
5884 break;
5885 case Token::kBIT_AND:
5886 __ andq(left, right);
5887 break;
5888 case Token::kBIT_OR:
5889 __ orq(left, right);
5890 break;
5891 case Token::kBIT_XOR:
5892 __ xorq(left, right);
5893 break;
5894 case Token::kMUL:
5895 __ imulq(left, right);
5896 break;
5897 default:
5898 UNREACHABLE();
5899 }
5900}
5901
5902LocationSummary* BinaryInt64OpInstr::MakeLocationSummary(Zone* zone,
5903 bool opt) const {
5904 switch (op_kind()) {
5905 case Token::kMOD:
5906 case Token::kTRUNCDIV: {
5907 const intptr_t kNumInputs = 2;
5908 const intptr_t kNumTemps = 1;
5909 LocationSummary* summary = new (zone) LocationSummary(
5910 zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
5912 summary->set_in(1, Location::RequiresRegister());
5913 // Intel uses rdx:rax with quotient rax and remainder rdx. Pick the
5914 // appropriate one for output and reserve the other as temp.
5915 summary->set_out(
5916 0, Location::RegisterLocation(op_kind() == Token::kMOD ? RDX : RAX));
5917 summary->set_temp(
5918 0, Location::RegisterLocation(op_kind() == Token::kMOD ? RAX : RDX));
5919 return summary;
5920 }
5921 default: {
5922 const intptr_t kNumInputs = 2;
5923 const intptr_t kNumTemps = 0;
5924 LocationSummary* summary = new (zone) LocationSummary(
5925 zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
5926 summary->set_in(0, Location::RequiresRegister());
5927 summary->set_in(1, LocationRegisterOrConstant(right()));
5928 summary->set_out(0, Location::SameAsFirstInput());
5929 return summary;
5930 }
5931 }
5932}
5933
5934void BinaryInt64OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5935 const Location left = locs()->in(0);
5936 const Location right = locs()->in(1);
5937 const Location out = locs()->out(0);
5938 ASSERT(!can_overflow());
5940
5941 if (op_kind() == Token::kMOD || op_kind() == Token::kTRUNCDIV) {
5942 const Location temp = locs()->temp(0);
5943 EmitInt64ModTruncDiv(compiler, this, op_kind(), left.reg(), right.reg(),
5944 temp.reg(), out.reg());
5945 } else if (right.IsConstant()) {
5946 ASSERT(out.reg() == left.reg());
5947 int64_t value;
5948 const bool ok = compiler::HasIntegerValue(right.constant(), &value);
5950 EmitInt64Arithmetic(compiler, op_kind(), left.reg(),
5951 compiler::Immediate(value));
5952 } else {
5953 ASSERT(out.reg() == left.reg());
5954 EmitInt64Arithmetic(compiler, op_kind(), left.reg(), right.reg());
5955 }
5956}
5957
5958LocationSummary* UnaryInt64OpInstr::MakeLocationSummary(Zone* zone,
5959 bool opt) const {
5960 const intptr_t kNumInputs = 1;
5961 const intptr_t kNumTemps = 0;
5962 LocationSummary* summary = new (zone)
5963 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
5964 summary->set_in(0, Location::RequiresRegister());
5965 summary->set_out(0, Location::SameAsFirstInput());
5966 return summary;
5967}
5968
5969void UnaryInt64OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5970 const Register left = locs()->in(0).reg();
5971 const Register out = locs()->out(0).reg();
5972 ASSERT(out == left);
5973 switch (op_kind()) {
5974 case Token::kBIT_NOT:
5975 __ notq(left);
5976 break;
5977 case Token::kNEGATE:
5978 __ negq(left);
5979 break;
5980 default:
5981 UNREACHABLE();
5982 }
5983}
5984
5985static void EmitShiftInt64ByConstant(FlowGraphCompiler* compiler,
5986 Token::Kind op_kind,
5987 Register left,
5988 const Object& right) {
5989 const int64_t shift = Integer::Cast(right).AsInt64Value();
5990 ASSERT(shift >= 0);
5991 switch (op_kind) {
5992 case Token::kSHR:
5993 __ sarq(left, compiler::Immediate(
5994 Utils::Minimum<int64_t>(shift, kBitsPerWord - 1)));
5995 break;
5996 case Token::kUSHR:
5997 ASSERT(shift < 64);
5998 __ shrq(left, compiler::Immediate(shift));
5999 break;
6000 case Token::kSHL: {
6001 ASSERT(shift < 64);
6002 __ shlq(left, compiler::Immediate(shift));
6003 break;
6004 }
6005 default:
6006 UNREACHABLE();
6007 }
6008}
6009
6010static void EmitShiftInt64ByRCX(FlowGraphCompiler* compiler,
6011 Token::Kind op_kind,
6012 Register left) {
6013 switch (op_kind) {
6014 case Token::kSHR: {
6015 __ sarq(left, RCX);
6016 break;
6017 }
6018 case Token::kUSHR: {
6019 __ shrq(left, RCX);
6020 break;
6021 }
6022 case Token::kSHL: {
6023 __ shlq(left, RCX);
6024 break;
6025 }
6026 default:
6027 UNREACHABLE();
6028 }
6029}
6030
6031static void EmitShiftUint32ByConstant(FlowGraphCompiler* compiler,
6032 Token::Kind op_kind,
6033 Register left,
6034 const Object& right) {
6035 const int64_t shift = Integer::Cast(right).AsInt64Value();
6036 ASSERT(shift >= 0);
6037 if (shift >= 32) {
6038 __ xorl(left, left);
6039 } else {
6040 switch (op_kind) {
6041 case Token::kSHR:
6042 case Token::kUSHR: {
6043 __ shrl(left, compiler::Immediate(shift));
6044 break;
6045 }
6046 case Token::kSHL: {
6047 __ shll(left, compiler::Immediate(shift));
6048 break;
6049 }
6050 default:
6051 UNREACHABLE();
6052 }
6053 }
6054}
6055
6056static void EmitShiftUint32ByRCX(FlowGraphCompiler* compiler,
6057 Token::Kind op_kind,
6058 Register left) {
6059 switch (op_kind) {
6060 case Token::kSHR:
6061 case Token::kUSHR: {
6062 __ shrl(left, RCX);
6063 break;
6064 }
6065 case Token::kSHL: {
6066 __ shll(left, RCX);
6067 break;
6068 }
6069 default:
6070 UNREACHABLE();
6071 }
6072}
6073
6074class ShiftInt64OpSlowPath : public ThrowErrorSlowPathCode {
6075 public:
6076 explicit ShiftInt64OpSlowPath(ShiftInt64OpInstr* instruction)
6077 : ThrowErrorSlowPathCode(instruction,
6078 kArgumentErrorUnboxedInt64RuntimeEntry) {}
6079
6080 const char* name() override { return "int64 shift"; }
6081
6082 void EmitCodeAtSlowPathEntry(FlowGraphCompiler* compiler) override {
6083 const Register out = instruction()->locs()->out(0).reg();
6084 ASSERT(out == instruction()->locs()->in(0).reg());
6085
6086 compiler::Label throw_error;
6087 __ testq(RCX, RCX);
6088 __ j(LESS, &throw_error);
6089
6090 switch (instruction()->AsShiftInt64Op()->op_kind()) {
6091 case Token::kSHR:
6092 __ sarq(out, compiler::Immediate(kBitsPerInt64 - 1));
6093 break;
6094 case Token::kUSHR:
6095 case Token::kSHL:
6096 __ xorq(out, out);
6097 break;
6098 default:
6099 UNREACHABLE();
6100 }
6101 __ jmp(exit_label());
6102
6103 __ Bind(&throw_error);
6104
6105 // Can't pass unboxed int64 value directly to runtime call, as all
6106 // arguments are expected to be tagged (boxed).
6107 // The unboxed int64 argument is passed through a dedicated slot in Thread.
6108 // TODO(dartbug.com/33549): Clean this up when unboxed values
6109 // could be passed as arguments.
6110 __ movq(compiler::Address(
6112 RCX);
6113 }
6114};
6115
6116LocationSummary* ShiftInt64OpInstr::MakeLocationSummary(Zone* zone,
6117 bool opt) const {
6118 const intptr_t kNumInputs = 2;
6119 const intptr_t kNumTemps = 0;
6120 LocationSummary* summary = new (zone) LocationSummary(
6121 zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
6122 summary->set_in(0, Location::RequiresRegister());
6123 summary->set_in(1, RangeUtils::IsPositive(shift_range())
6125 : Location::RegisterLocation(RCX));
6126 summary->set_out(0, Location::SameAsFirstInput());
6127 return summary;
6128}
6129
6130void ShiftInt64OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
6131 const Register left = locs()->in(0).reg();
6132 const Register out = locs()->out(0).reg();
6133 ASSERT(left == out);
6134 ASSERT(!can_overflow());
6135
6136 if (locs()->in(1).IsConstant()) {
6137 EmitShiftInt64ByConstant(compiler, op_kind(), left,
6138 locs()->in(1).constant());
6139 } else {
6140 // Code for a variable shift amount (or constant that throws).
6141 ASSERT(locs()->in(1).reg() == RCX);
6142
6143 // Jump to a slow path if shift count is > 63 or negative.
6144 ShiftInt64OpSlowPath* slow_path = nullptr;
6145 if (!IsShiftCountInRange()) {
6146 slow_path = new (Z) ShiftInt64OpSlowPath(this);
6147 compiler->AddSlowPathCode(slow_path);
6148
6149 __ cmpq(RCX, compiler::Immediate(kShiftCountLimit));
6150 __ j(ABOVE, slow_path->entry_label());
6151 }
6152
6153 EmitShiftInt64ByRCX(compiler, op_kind(), left);
6154
6155 if (slow_path != nullptr) {
6156 __ Bind(slow_path->exit_label());
6157 }
6158 }
6159}
6160
6162 Zone* zone,
6163 bool opt) const {
6164 const intptr_t kNumInputs = 2;
6165 const intptr_t kNumTemps = 0;
6166 LocationSummary* summary = new (zone)
6167 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
6168 summary->set_in(0, Location::RequiresRegister());
6169 summary->set_in(1, LocationFixedRegisterOrSmiConstant(right(), RCX));
6170 summary->set_out(0, Location::SameAsFirstInput());
6171 return summary;
6172}
6173
6175 const Register left = locs()->in(0).reg();
6176 const Register out = locs()->out(0).reg();
6177 ASSERT(left == out);
6178 ASSERT(!can_overflow());
6179
6180 if (locs()->in(1).IsConstant()) {
6181 EmitShiftInt64ByConstant(compiler, op_kind(), left,
6182 locs()->in(1).constant());
6183 } else {
6184 ASSERT(locs()->in(1).reg() == RCX);
6185 __ SmiUntag(RCX);
6186
6187 // Deoptimize if shift count is > 63 or negative (or not a smi).
6188 if (!IsShiftCountInRange()) {
6190 compiler::Label* deopt =
6191 compiler->AddDeoptStub(deopt_id(), ICData::kDeoptBinaryInt64Op);
6192
6193 __ cmpq(RCX, compiler::Immediate(kShiftCountLimit));
6194 __ j(ABOVE, deopt);
6195 }
6196
6197 EmitShiftInt64ByRCX(compiler, op_kind(), left);
6198 }
6199}
6200
6201class ShiftUint32OpSlowPath : public ThrowErrorSlowPathCode {
6202 public:
6203 explicit ShiftUint32OpSlowPath(ShiftUint32OpInstr* instruction)
6204 : ThrowErrorSlowPathCode(instruction,
6205 kArgumentErrorUnboxedInt64RuntimeEntry) {}
6206
6207 const char* name() override { return "uint32 shift"; }
6208
6209 void EmitCodeAtSlowPathEntry(FlowGraphCompiler* compiler) override {
6210 const Register out = instruction()->locs()->out(0).reg();
6211 ASSERT(out == instruction()->locs()->in(0).reg());
6212
6213 compiler::Label throw_error;
6214 __ testq(RCX, RCX);
6215 __ j(LESS, &throw_error);
6216
6217 __ xorl(out, out);
6218 __ jmp(exit_label());
6219
6220 __ Bind(&throw_error);
6221
6222 // Can't pass unboxed int64 value directly to runtime call, as all
6223 // arguments are expected to be tagged (boxed).
6224 // The unboxed int64 argument is passed through a dedicated slot in Thread.
6225 // TODO(dartbug.com/33549): Clean this up when unboxed values
6226 // could be passed as arguments.
6227 __ movq(compiler::Address(
6229 RCX);
6230 }
6231};
6232
6233LocationSummary* ShiftUint32OpInstr::MakeLocationSummary(Zone* zone,
6234 bool opt) const {
6235 const intptr_t kNumInputs = 2;
6236 const intptr_t kNumTemps = 0;
6237 LocationSummary* summary = new (zone) LocationSummary(
6238 zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
6239 summary->set_in(0, Location::RequiresRegister());
6240 summary->set_in(1, RangeUtils::IsPositive(shift_range())
6242 : Location::RegisterLocation(RCX));
6243 summary->set_out(0, Location::SameAsFirstInput());
6244 return summary;
6245}
6246
6247void ShiftUint32OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
6248 Register left = locs()->in(0).reg();
6249 Register out = locs()->out(0).reg();
6250 ASSERT(left == out);
6251
6252 if (locs()->in(1).IsConstant()) {
6253 EmitShiftUint32ByConstant(compiler, op_kind(), left,
6254 locs()->in(1).constant());
6255 } else {
6256 // Code for a variable shift amount (or constant that throws).
6257 ASSERT(locs()->in(1).reg() == RCX);
6258
6259 // Jump to a slow path if shift count is > 31 or negative.
6260 ShiftUint32OpSlowPath* slow_path = nullptr;
6261 if (!IsShiftCountInRange(kUint32ShiftCountLimit)) {
6262 slow_path = new (Z) ShiftUint32OpSlowPath(this);
6263 compiler->AddSlowPathCode(slow_path);
6264
6265 __ cmpq(RCX, compiler::Immediate(kUint32ShiftCountLimit));
6266 __ j(ABOVE, slow_path->entry_label());
6267 }
6268
6269 EmitShiftUint32ByRCX(compiler, op_kind(), left);
6270
6271 if (slow_path != nullptr) {
6272 __ Bind(slow_path->exit_label());
6273 }
6274 }
6275}
6276
6278 Zone* zone,
6279 bool opt) const {
6280 const intptr_t kNumInputs = 2;
6281 const intptr_t kNumTemps = 0;
6282 LocationSummary* summary = new (zone)
6283 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
6284 summary->set_in(0, Location::RequiresRegister());
6285 summary->set_in(1, LocationFixedRegisterOrSmiConstant(right(), RCX));
6286 summary->set_out(0, Location::SameAsFirstInput());
6287 return summary;
6288}
6289
6291 FlowGraphCompiler* compiler) {
6292 Register left = locs()->in(0).reg();
6293 Register out = locs()->out(0).reg();
6294 ASSERT(left == out);
6295
6296 if (locs()->in(1).IsConstant()) {
6297 EmitShiftUint32ByConstant(compiler, op_kind(), left,
6298 locs()->in(1).constant());
6299 } else {
6300 ASSERT(locs()->in(1).reg() == RCX);
6301 __ SmiUntag(RCX);
6302
6303 if (!IsShiftCountInRange(kUint32ShiftCountLimit)) {
6304 if (!IsShiftCountInRange()) {
6305 // Deoptimize if shift count is negative.
6307 compiler::Label* deopt =
6308 compiler->AddDeoptStub(deopt_id(), ICData::kDeoptBinaryInt64Op);
6309
6310 __ OBJ(test)(RCX, RCX);
6311 __ j(LESS, deopt);
6312 }
6313
6314 compiler::Label cont;
6315 __ OBJ(cmp)(RCX, compiler::Immediate(kUint32ShiftCountLimit));
6316 __ j(LESS_EQUAL, &cont);
6317
6318 __ xorl(left, left);
6319
6320 __ Bind(&cont);
6321 }
6322
6323 EmitShiftUint32ByRCX(compiler, op_kind(), left);
6324 }
6325}
6326
6327LocationSummary* BinaryUint32OpInstr::MakeLocationSummary(Zone* zone,
6328 bool opt) const {
6329 const intptr_t kNumInputs = 2;
6330 const intptr_t kNumTemps = 0;
6331 LocationSummary* summary = new (zone)
6332 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
6333 summary->set_in(0, Location::RequiresRegister());
6334 summary->set_in(1, Location::RequiresRegister());
6335 summary->set_out(0, Location::SameAsFirstInput());
6336 return summary;
6337}
6338
6339template <typename OperandType>
6340static void EmitIntegerArithmetic(FlowGraphCompiler* compiler,
6341 Token::Kind op_kind,
6342 Register left,
6343 const OperandType& right) {
6344 switch (op_kind) {
6345 case Token::kADD:
6346 __ addl(left, right);
6347 break;
6348 case Token::kSUB:
6349 __ subl(left, right);
6350 break;
6351 case Token::kBIT_AND:
6352 __ andl(left, right);
6353 break;
6354 case Token::kBIT_OR:
6355 __ orl(left, right);
6356 break;
6357 case Token::kBIT_XOR:
6358 __ xorl(left, right);
6359 break;
6360 case Token::kMUL:
6361 __ imull(left, right);
6362 break;
6363 default:
6364 UNREACHABLE();
6365 }
6366}
6367
6368void BinaryUint32OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
6369 Register left = locs()->in(0).reg();
6370 Register right = locs()->in(1).reg();
6371 Register out = locs()->out(0).reg();
6372 ASSERT(out == left);
6373 switch (op_kind()) {
6374 case Token::kBIT_AND:
6375 case Token::kBIT_OR:
6376 case Token::kBIT_XOR:
6377 case Token::kADD:
6378 case Token::kSUB:
6379 case Token::kMUL:
6380 EmitIntegerArithmetic(compiler, op_kind(), left, right);
6381 return;
6382 default:
6383 UNREACHABLE();
6384 }
6385}
6386
6387DEFINE_BACKEND(UnaryUint32Op, (SameAsFirstInput, Register value)) {
6388 ASSERT(instr->op_kind() == Token::kBIT_NOT);
6389 __ notl(value);
6390}
6391
6392DEFINE_UNIMPLEMENTED_INSTRUCTION(BinaryInt32OpInstr)
6393
6394LocationSummary* IntConverterInstr::MakeLocationSummary(Zone* zone,
6395 bool opt) const {
6396 const intptr_t kNumInputs = 1;
6397 const intptr_t kNumTemps = 0;
6398 LocationSummary* summary = new (zone)
6399 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
6400 if (from() == kUntagged || to() == kUntagged) {
6401 ASSERT((from() == kUntagged && to() == kUnboxedIntPtr) ||
6402 (from() == kUnboxedIntPtr && to() == kUntagged));
6404 } else if (from() == kUnboxedInt64) {
6405 ASSERT(to() == kUnboxedUint32 || to() == kUnboxedInt32);
6406 } else if (to() == kUnboxedInt64) {
6407 ASSERT(from() == kUnboxedInt32 || from() == kUnboxedUint32);
6408 } else {
6409 ASSERT(to() == kUnboxedUint32 || to() == kUnboxedInt32);
6410 ASSERT(from() == kUnboxedUint32 || from() == kUnboxedInt32);
6411 }
6412
6413 summary->set_in(0, Location::RequiresRegister());
6414 summary->set_out(0, Location::SameAsFirstInput());
6415
6416 return summary;
6417}
6418
6419void IntConverterInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
6420 const bool is_nop_conversion =
6421 (from() == kUntagged && to() == kUnboxedIntPtr) ||
6422 (from() == kUnboxedIntPtr && to() == kUntagged);
6423 if (is_nop_conversion) {
6424 ASSERT(locs()->in(0).reg() == locs()->out(0).reg());
6425 return;
6426 }
6427
6428 if (from() == kUnboxedInt32 && to() == kUnboxedUint32) {
6429 const Register value = locs()->in(0).reg();
6430 const Register out = locs()->out(0).reg();
6431 // Representations are bitwise equivalent but we want to normalize
6432 // upperbits for safety reasons.
6433 // TODO(vegorov) if we ensure that we never use upperbits we could
6434 // avoid this.
6435 __ movl(out, value);
6436 } else if (from() == kUnboxedUint32 && to() == kUnboxedInt32) {
6437 // Representations are bitwise equivalent.
6438 const Register value = locs()->in(0).reg();
6439 const Register out = locs()->out(0).reg();
6440 __ movsxd(out, value);
6441 if (CanDeoptimize()) {
6442 compiler::Label* deopt =
6443 compiler->AddDeoptStub(deopt_id(), ICData::kDeoptUnboxInteger);
6444 __ testl(out, out);
6445 __ j(NEGATIVE, deopt);
6446 }
6447 } else if (from() == kUnboxedInt64) {
6448 ASSERT(to() == kUnboxedUint32 || to() == kUnboxedInt32);
6449 const Register value = locs()->in(0).reg();
6450 const Register out = locs()->out(0).reg();
6451 if (!CanDeoptimize()) {
6452 // Copy low.
6453 __ movl(out, value);
6454 } else {
6455 compiler::Label* deopt =
6456 compiler->AddDeoptStub(deopt_id(), ICData::kDeoptUnboxInteger);
6457 // Sign extend.
6458 __ movsxd(out, value);
6459 // Compare with original value.
6460 __ cmpq(out, value);
6461 // Value cannot be held in Int32, deopt.
6462 __ j(NOT_EQUAL, deopt);
6463 }
6464 } else if (to() == kUnboxedInt64) {
6465 ASSERT((from() == kUnboxedUint32) || (from() == kUnboxedInt32));
6466 const Register value = locs()->in(0).reg();
6467 const Register out = locs()->out(0).reg();
6468 if (from() == kUnboxedUint32) {
6469 // Zero extend.
6470 __ movl(out, value);
6471 } else {
6472 // Sign extend.
6473 ASSERT(from() == kUnboxedInt32);
6474 __ movsxd(out, value);
6475 }
6476 } else {
6477 UNREACHABLE();
6478 }
6479}
6480
6481LocationSummary* StopInstr::MakeLocationSummary(Zone* zone, bool opt) const {
6482 return new (zone) LocationSummary(zone, 0, 0, LocationSummary::kNoCall);
6483}
6484
6485void StopInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
6486 __ Stop(message());
6487}
6488
6489void GraphEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
6490 BlockEntryInstr* entry = normal_entry();
6491 if (entry != nullptr) {
6492 if (!compiler->CanFallThroughTo(entry)) {
6493 FATAL("Checked function entry must have no offset");
6494 }
6495 } else {
6496 entry = osr_entry();
6497 if (!compiler->CanFallThroughTo(entry)) {
6498 __ jmp(compiler->GetJumpLabel(entry));
6499 }
6500 }
6501}
6502
6503LocationSummary* GotoInstr::MakeLocationSummary(Zone* zone, bool opt) const {
6504 return new (zone) LocationSummary(zone, 0, 0, LocationSummary::kNoCall);
6505}
6506
6507void GotoInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
6508 if (!compiler->is_optimizing()) {
6509 if (FLAG_reorder_basic_blocks) {
6510 compiler->EmitEdgeCounter(block()->preorder_number());
6511 }
6512 // Add a deoptimization descriptor for deoptimizing instructions that
6513 // may be inserted before this instruction.
6514 compiler->AddCurrentDescriptor(UntaggedPcDescriptors::kDeopt, GetDeoptId(),
6515 InstructionSource());
6516 }
6517 if (HasParallelMove()) {
6519 }
6520
6521 // We can fall through if the successor is the next block in the list.
6522 // Otherwise, we need a jump.
6523 if (!compiler->CanFallThroughTo(successor())) {
6524 __ jmp(compiler->GetJumpLabel(successor()));
6525 }
6526}
6527
6528LocationSummary* IndirectGotoInstr::MakeLocationSummary(Zone* zone,
6529 bool opt) const {
6530 const intptr_t kNumInputs = 1;
6531 const intptr_t kNumTemps = 1;
6532
6533 LocationSummary* summary = new (zone)
6534 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
6535
6536 summary->set_in(0, Location::RequiresRegister());
6537 summary->set_temp(0, Location::RequiresRegister());
6538
6539 return summary;
6540}
6541
6542void IndirectGotoInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
6543 Register index_reg = locs()->in(0).reg();
6544 Register offset_reg = locs()->temp(0).reg();
6545
6546 ASSERT(RequiredInputRepresentation(0) == kTagged);
6547 // Note: we don't bother to ensure index is a writable input because any
6548 // other instructions using it must also not rely on the upper bits
6549 // when compressed.
6550 __ ExtendNonNegativeSmi(index_reg);
6551 __ LoadObject(offset_reg, offsets_);
6553 /*is_external=*/false, kTypedDataInt32ArrayCid,
6554 /*index_scale=*/4,
6555 /*index_unboxed=*/false, offset_reg, index_reg));
6556
6557 {
6558 const intptr_t kRIPRelativeLeaqSize = 7;
6559 const intptr_t entry_to_rip_offset = __ CodeSize() + kRIPRelativeLeaqSize;
6560 __ leaq(TMP, compiler::Address::AddressRIPRelative(-entry_to_rip_offset));
6561 ASSERT(__ CodeSize() == entry_to_rip_offset);
6562 }
6563
6564 __ addq(TMP, offset_reg);
6565
6566 // Jump to the absolute address.
6567 __ jmp(TMP);
6568}
6569
6570LocationSummary* StrictCompareInstr::MakeLocationSummary(Zone* zone,
6571 bool opt) const {
6572 const intptr_t kNumInputs = 2;
6573 const intptr_t kNumTemps = 0;
6574 if (needs_number_check()) {
6575 LocationSummary* locs = new (zone)
6576 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
6580 return locs;
6581 }
6582 LocationSummary* locs = new (zone)
6583 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
6585 // Only one of the inputs can be a constant. Choose register if the first one
6586 // is a constant.
6587 locs->set_in(1, locs->in(0).IsConstant()
6591 return locs;
6592}
6593
6594Condition StrictCompareInstr::EmitComparisonCodeRegConstant(
6595 FlowGraphCompiler* compiler,
6596 BranchLabels labels,
6597 Register reg,
6598 const Object& obj) {
6599 return compiler->EmitEqualityRegConstCompare(reg, obj, needs_number_check(),
6600 source(), deopt_id());
6601}
6602
6603LocationSummary* ClosureCallInstr::MakeLocationSummary(Zone* zone,
6604 bool opt) const {
6605 const intptr_t kNumInputs = 1;
6606 const intptr_t kNumTemps = 0;
6607 LocationSummary* summary = new (zone)
6608 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
6610 FLAG_precompiled_mode ? RAX : FUNCTION_REG));
6611 return MakeCallSummary(zone, this, summary);
6612}
6613
6614void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
6615 // Arguments descriptor is expected in ARGS_DESC_REG.
6616 const intptr_t argument_count = ArgumentCount(); // Includes type args.
6617 const Array& arguments_descriptor =
6619 __ LoadObject(ARGS_DESC_REG, arguments_descriptor);
6620
6621 if (FLAG_precompiled_mode) {
6622 ASSERT(locs()->in(0).reg() == RAX);
6623 // RAX: Closure with cached entry point.
6624 __ movq(RCX, compiler::FieldAddress(
6626 } else {
6627 ASSERT(locs()->in(0).reg() == FUNCTION_REG);
6628 // FUNCTION_REG: Function.
6629 __ LoadCompressed(
6630 CODE_REG, compiler::FieldAddress(
6632 // Closure functions only have one entry point.
6633 __ movq(RCX, compiler::FieldAddress(
6636 }
6637
6638 // ARGS_DESC_REG: Arguments descriptor array.
6639 // RCX: instructions entry point.
6640 if (!FLAG_precompiled_mode) {
6641 // RBX: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
6642 __ xorq(IC_DATA_REG, IC_DATA_REG);
6643 }
6644 __ call(RCX);
6645 compiler->EmitCallsiteMetadata(source(), deopt_id(),
6646 UntaggedPcDescriptors::kOther, locs(), env());
6647 compiler->EmitDropArguments(argument_count);
6648}
6649
6650LocationSummary* BooleanNegateInstr::MakeLocationSummary(Zone* zone,
6651 bool opt) const {
6654}
6655
6656void BooleanNegateInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
6657 Register input = locs()->in(0).reg();
6658 Register result = locs()->out(0).reg();
6659 ASSERT(input == result);
6660 __ xorq(result, compiler::Immediate(
6662}
6663
6664LocationSummary* BoolToIntInstr::MakeLocationSummary(Zone* zone,
6665 bool opt) const {
6666 UNREACHABLE();
6667 return NULL;
6668}
6669
6670void BoolToIntInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
6671 UNREACHABLE();
6672}
6673
6674LocationSummary* IntToBoolInstr::MakeLocationSummary(Zone* zone,
6675 bool opt) const {
6676 UNREACHABLE();
6677 return NULL;
6678}
6679
6680void IntToBoolInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
6681 UNREACHABLE();
6682}
6683
6684LocationSummary* AllocateObjectInstr::MakeLocationSummary(Zone* zone,
6685 bool opt) const {
6686 const intptr_t kNumInputs = (type_arguments() != nullptr) ? 1 : 0;
6687 const intptr_t kNumTemps = 0;
6688 LocationSummary* locs = new (zone)
6689 LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
6690 if (type_arguments() != nullptr) {
6693 }
6695 return locs;
6696}
6697
6698void AllocateObjectInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
6699 if (type_arguments() != nullptr) {
6700 TypeUsageInfo* type_usage_info = compiler->thread()->type_usage_info();
6701 if (type_usage_info != nullptr) {
6702 RegisterTypeArgumentsUse(compiler->function(), type_usage_info, cls_,
6703 type_arguments()->definition());
6704 }
6705 }
6706 const Code& stub = Code::ZoneHandle(
6708 compiler->GenerateStubCall(source(), stub, UntaggedPcDescriptors::kOther,
6709 locs(), deopt_id(), env());
6710}
6711
6712void DebugStepCheckInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
6713#ifdef PRODUCT
6714 UNREACHABLE();
6715#else
6716 ASSERT(!compiler->is_optimizing());
6717 __ CallPatchable(StubCode::DebugStepCheck());
6718 compiler->AddCurrentDescriptor(stub_kind_, deopt_id_, source());
6719 compiler->RecordSafepoint(locs());
6720#endif
6721}
6722
6723} // namespace dart
6724
6725#undef __
6726
6727#endif // defined(TARGET_ARCH_X64)
static void done(const char *config, const char *src, const char *srcOptions, const char *name)
Definition: DM.cpp:263
static void fail(const SkString &err)
Definition: DM.cpp:234
SkPoint pos
static bool are_equal(skiatest::Reporter *reporter, const SkMatrix &a, const SkMatrix &b)
Definition: MatrixTest.cpp:50
static int float_bits(float f)
Definition: MatrixTest.cpp:44
static bool ok(int result)
static bool subtract(const R &a, const R &b, R *out)
Definition: SkRect.cpp:177
static size_t element_size(Layout layout, SkSLType type)
Vec2Value v2
#define __
#define SIMPLE(name,...)
#define OBJ(op)
#define UNREACHABLE()
Definition: assert.h:248
#define ASSERT_EQUAL(expected, actual)
Definition: assert.h:309
#define RELEASE_ASSERT(cond)
Definition: assert.h:327
#define Z
GLenum type
intptr_t num_context_variables() const
Definition: il.h:8392
Value * type_arguments() const
Definition: il.h:7436
const Class & cls() const
Definition: il.h:7435
intptr_t num_context_variables() const
Definition: il.h:7594
static intptr_t type_arguments_offset()
Definition: object.h:10928
static intptr_t InstanceSize()
Definition: object.h:10936
static constexpr bool IsValidLength(intptr_t len)
Definition: object.h:10932
static intptr_t length_offset()
Definition: object.h:10834
Value * dst_type() const
Definition: il.h:4423
Token::Kind op_kind() const
Definition: il.h:9038
Value * right() const
Definition: il.h:9036
Value * left() const
Definition: il.h:9035
bool can_overflow() const
Definition: il.h:9400
Value * right() const
Definition: il.h:9398
Token::Kind op_kind() const
Definition: il.h:9396
Value * left() const
Definition: il.h:9397
bool RightIsPowerOfTwoConstant() const
Definition: il.cc:2125
Range * right_range() const
Definition: il.h:9473
ParallelMoveInstr * parallel_move() const
Definition: il.h:1689
bool HasParallelMove() const
Definition: il.h:1691
static const Bool & False()
Definition: object.h:10799
static const Bool & True()
Definition: object.h:10797
static void Allocate(FlowGraphCompiler *compiler, Instruction *instruction, const Class &cls, Register result, Register temp)
Definition: il.cc:6309
Value * value() const
Definition: il.h:8528
Representation from_representation() const
Definition: il.h:8529
virtual bool ValueFitsSmi() const
Definition: il.cc:3253
ComparisonInstr * comparison() const
Definition: il.h:4021
static constexpr Register kSecondReturnReg
static constexpr Register kVarArgFpuRegisterCount
static constexpr intptr_t kCalleeSaveCpuRegisters
static constexpr RegList kVolatileXmmRegisters
static constexpr intptr_t kVolatileCpuRegisters
static constexpr intptr_t kFpuArgumentRegisters
static constexpr FpuRegister kReturnFpuReg
static constexpr Register kFfiAnyNonAbiRegister
static constexpr Register kArg3Reg
static constexpr Register kArg1Reg
static constexpr Register kReturnReg
static constexpr Register kSecondNonArgumentRegister
static constexpr Register kArg2Reg
static constexpr Register kArg4Reg
static constexpr intptr_t kCalleeSaveXmmRegisters
const RuntimeEntry & TargetFunction() const
Definition: il.cc:1101
Value * index() const
Definition: il.h:10797
Value * length() const
Definition: il.h:10796
Value * value() const
Definition: il.h:10755
bool IsDeoptIfNull() const
Definition: il.cc:863
DECLARE_INSTRUCTION_SERIALIZABLE_FIELDS(CheckClassInstr, TemplateInstruction, FIELD_LIST) private void EmitBitTest(FlowGraphCompiler *compiler, intptr_t min, intptr_t max, intptr_t mask, compiler::Label *deopt)
void EmitNullCheck(FlowGraphCompiler *compiler, compiler::Label *deopt)
bool IsNullCheck() const
Definition: il.h:10600
bool IsBitTest() const
Definition: il.cc:899
Value * right() const
Definition: il.h:8477
Value * left() const
Definition: il.h:8476
Value * value() const
Definition: il.h:10654
virtual bool UseSharedSlowPathStub(bool is_optimizing) const
Definition: il.h:9923
intptr_t loop_depth() const
Definition: il.h:9906
bool in_loop() const
Definition: il.h:9904
virtual void EmitBranchCode(FlowGraphCompiler *compiler, BranchInstr *branch)
virtual void EmitNativeCode(FlowGraphCompiler *compiler)
virtual Condition EmitComparisonCode(FlowGraphCompiler *compiler, BranchLabels labels)=0
intptr_t ToNullableCid()
static CompilerState & Current()
const Object & value() const
Definition: il.h:4230
void EmitMoveToLocation(FlowGraphCompiler *compiler, const Location &destination, Register tmp=kNoRegister, intptr_t pair_index=0)
static intptr_t num_variables_offset()
Definition: object.h:7415
static intptr_t InstanceSize()
Definition: object.h:7448
Value * type_arguments() const
Definition: il.h:7845
virtual Value * num_elements() const
Definition: il.h:7846
virtual Representation representation() const
Definition: il.h:3501
friend class Value
Definition: il.h:2690
static constexpr intptr_t kNone
Definition: deopt_id.h:27
Value * value() const
Definition: il.h:9101
MethodRecognizer::Kind op_kind() const
Definition: il.h:9103
MethodRecognizer::Kind recognized_kind() const
Definition: il.h:10060
Value * value() const
Definition: il.h:10111
bool is_null_aware() const
Definition: il.h:5341
virtual Representation representation() const
Definition: il.h:10337
intptr_t index() const
Definition: il.h:10335
void EmitReturnMoves(FlowGraphCompiler *compiler, const Register temp0, const Register temp1)
Definition: il.cc:7690
DECLARE_INSTRUCTION_SERIALIZABLE_FIELDS(FfiCallInstr, VariadicDefinition, FIELD_LIST) private void EmitParamMoves(FlowGraphCompiler *compiler, const Register saved_fp, const Register temp0, const Register temp1)
Definition: il.cc:7478
static bool CanExecuteGeneratedCodeInSafepoint()
Definition: il.h:6142
intptr_t TargetAddressIndex() const
Definition: il.h:6100
static intptr_t guarded_cid_offset()
Definition: object.h:4669
static intptr_t static_type_exactness_state_offset()
Definition: object.h:4651
bool is_nullable() const
Definition: object.cc:11770
@ kUnknownFixedLength
Definition: object.h:4728
@ kUnknownLengthOffset
Definition: object.h:4727
@ kNoFixedLength
Definition: object.h:4729
bool needs_length_check() const
Definition: object.h:4697
static intptr_t guarded_list_length_in_object_offset_offset()
Definition: object.h:4693
StaticTypeExactnessState static_type_exactness_state() const
Definition: object.h:4633
intptr_t guarded_cid() const
Definition: object.cc:11749
static intptr_t is_nullable_offset()
Definition: object.h:4766
static intptr_t guarded_list_length_offset()
Definition: object.h:4683
ParallelMoveInstr * parallel_move() const
Definition: il.h:3735
BlockEntryInstr * block() const
Definition: il.h:3710
bool HasParallelMove() const
Definition: il.h:3737
JoinEntryInstr * successor() const
Definition: il.h:3713
FunctionEntryInstr * normal_entry() const
Definition: il.h:2001
OsrEntryInstr * osr_entry() const
Definition: il.h:2007
const Field & field() const
Definition: il.h:6520
Value * value() const
Definition: il.h:6518
Value * value() const
Definition: il.h:9149
Value * value() const
Definition: il.h:9189
@ kGeneralized
Definition: object.h:2525
ComparisonInstr * comparison() const
Definition: il.h:5483
virtual Representation RequiredInputRepresentation(intptr_t idx) const
Definition: il.h:3807
const AbstractType & type() const
Definition: il.h:7284
intptr_t GetDeoptId() const
Definition: il.h:1409
virtual bool UseSharedSlowPathStub(bool is_optimizing) const
Definition: il.h:1377
Environment * env() const
Definition: il.h:1215
virtual LocationSummary * MakeLocationSummary(Zone *zone, bool is_optimizing) const =0
virtual void EmitNativeCode(FlowGraphCompiler *compiler)
Definition: il.h:1213
void InitializeLocationSummary(Zone *zone, bool optimizing)
Definition: il.h:1202
virtual Representation representation() const
Definition: il.h:1260
bool CanDeoptimize() const
Definition: il.h:1079
friend class BlockEntryInstr
Definition: il.h:1403
LocationSummary * locs()
Definition: il.h:1192
InstructionSource source() const
Definition: il.h:1008
intptr_t deopt_id() const
Definition: il.h:993
const T * Cast() const
Definition: il.h:1186
static bool SlowPathSharingSupported(bool is_optimizing)
Definition: il.h:1368
static LocationSummary * MakeCallSummary(Zone *zone, const Instruction *instr, LocationSummary *locs=nullptr)
Value * value() const
Definition: il.h:9978
Value * value() const
Definition: il.h:11044
Representation to() const
Definition: il.h:11047
Representation from() const
Definition: il.h:11046
const RuntimeEntry & TargetFunction() const
Definition: il.cc:7223
MethodRecognizer::Kind recognized_kind() const
Definition: il.h:10261
static constexpr intptr_t kDoubleTempIndex
Definition: il.h:10297
ObjectStore * object_store() const
Definition: isolate.h:510
static IsolateGroup * Current()
Definition: isolate.h:539
intptr_t TargetAddressIndex() const
Definition: il.h:6198
void EmitParamMoves(FlowGraphCompiler *compiler, Register saved_fp, Register temp0)
Definition: il.cc:8191
LocationSummary * MakeLocationSummaryInternal(Zone *zone, const RegList temps) const
Definition: il.cc:8113
intptr_t index_scale() const
Definition: il.h:6895
Value * index() const
Definition: il.h:6893
bool can_pack_into_smi() const
Definition: il.h:6902
intptr_t element_count() const
Definition: il.h:6900
bool IsExternal() const
Definition: il.h:6888
intptr_t class_id() const
Definition: il.h:6899
intptr_t class_id() const
Definition: il.h:6803
bool IsUntagged() const
Definition: il.h:6796
Value * array() const
Definition: il.h:6800
intptr_t index_scale() const
Definition: il.h:6802
Representation representation() const
Definition: il.h:6819
Value * index() const
Definition: il.h:6801
Value * index() const
Definition: il.h:3127
virtual Representation RequiredInputRepresentation(intptr_t index) const
Definition: il.h:3114
intptr_t offset() const
Definition: il.h:3129
Register base_reg() const
Definition: il.h:3128
virtual Representation representation() const
Definition: il.h:3125
const LocalVariable & local() const
Definition: il.h:5814
Location temp(intptr_t index) const
Definition: locations.h:882
Location out(intptr_t index) const
Definition: locations.h:903
static LocationSummary * Make(Zone *zone, intptr_t input_count, Location out, ContainsCall contains_call)
Definition: locations.cc:187
void set_temp(intptr_t index, Location loc)
Definition: locations.h:894
RegisterSet * live_registers()
Definition: locations.h:941
void set_out(intptr_t index, Location loc)
Definition: locations.cc:232
bool always_calls() const
Definition: locations.h:918
Location in(intptr_t index) const
Definition: locations.h:866
void set_in(intptr_t index, Location loc)
Definition: locations.cc:205
static Location StackSlot(intptr_t stack_index, Register base)
Definition: locations.h:447
static Location NoLocation()
Definition: locations.h:387
static Location SameAsFirstInput()
Definition: locations.h:382
static Location Pair(Location first, Location second)
Definition: locations.cc:271
Register reg() const
Definition: locations.h:404
static Location FpuRegisterLocation(FpuRegister reg)
Definition: locations.h:410
static Location WritableRegister()
Definition: locations.h:376
bool IsConstant() const
Definition: locations.h:292
static Location RegisterLocation(Register reg)
Definition: locations.h:398
static Location PrefersRegister()
Definition: locations.h:358
static Location Any()
Definition: locations.h:352
PairLocation * AsPairLocation() const
Definition: locations.cc:280
static Location RequiresRegister()
Definition: locations.h:365
static Location RequiresFpuRegister()
Definition: locations.h:369
FpuRegister fpu_reg() const
Definition: locations.h:416
const Object & constant() const
Definition: locations.cc:373
static Location Constant(const ConstantInstr *obj, int pair_index=0)
Definition: locations.h:294
Value * right() const
Definition: il.h:8970
intptr_t result_cid() const
Definition: il.h:8972
Value * left() const
Definition: il.h:8969
MethodRecognizer::Kind op_kind() const
Definition: il.h:8967
Value * length() const
Definition: il.h:3211
bool unboxed_inputs() const
Definition: il.h:3216
Value * src_start() const
Definition: il.h:3209
void EmitLoopCopy(FlowGraphCompiler *compiler, Register dest_reg, Register src_reg, Register length_reg, compiler::Label *done, compiler::Label *copy_forwards=nullptr)
void PrepareLengthRegForLoop(FlowGraphCompiler *compiler, Register length_reg, compiler::Label *done)
Value * dest_start() const
Definition: il.h:3210
static intptr_t value_offset()
Definition: object.h:10074
virtual Representation representation() const
Definition: il.h:3387
Value * value() const
Definition: il.h:3377
Location location() const
Definition: il.h:3374
static int ComputeArgcTag(const Function &function)
void SetupNative()
Definition: il.cc:7347
bool is_auto_scope() const
Definition: il.h:6026
bool is_bootstrap_native() const
Definition: il.h:6025
const Function & function() const
Definition: il.h:6023
NativeFunction native_c_function() const
Definition: il.h:6024
bool link_lazily() const
Definition: il.h:6027
static constexpr intptr_t kVMTagOffsetFromFp
Definition: il.h:2235
static uword LinkNativeCallEntry()
static Object & Handle()
Definition: object.h:407
static Object & ZoneHandle()
Definition: object.h:419
static intptr_t data_offset()
Definition: object.h:10554
Location At(intptr_t i) const
Definition: locations.h:618
static bool Overlaps(Range *range, intptr_t min, intptr_t max)
static bool OnlyLessThanOrEqualTo(Range *range, intptr_t value)
static bool IsWithin(const Range *range, int64_t min, int64_t max)
static bool IsPositive(Range *range)
static bool CanBeZero(Range *range)
bool IsPositive() const
intptr_t FpuRegisterCount() const
Definition: locations.h:809
void Add(Location loc, Representation rep=kTagged)
Definition: locations.h:754
DECLARE_INSTRUCTION_SERIALIZABLE_FIELDS(ShiftIntegerOpInstr, BinaryIntegerOpInstr, FIELD_LIST) protected bool IsShiftCountInRange(int64_t max=kShiftCountLimit) const
Definition: il.cc:2112
Range * shift_range() const
Definition: il.h:9655
Kind kind() const
Definition: il.h:11358
Value * value() const
Definition: il.h:9952
static constexpr intptr_t kBits
Definition: object.h:9986
static SmiPtr New(intptr_t value)
Definition: object.h:10006
static constexpr intptr_t kMaxValue
Definition: object.h:9987
static intptr_t RawValue(intptr_t value)
Definition: object.h:10022
const char * message() const
Definition: il.h:3681
bool ShouldEmitStoreBarrier() const
Definition: il.h:7089
virtual Representation RequiredInputRepresentation(intptr_t idx) const
Definition: il.cc:6925
Value * value() const
Definition: il.h:7083
Value * array() const
Definition: il.h:7081
intptr_t class_id() const
Definition: il.h:7086
bool IsUntagged() const
Definition: il.h:7114
intptr_t index_scale() const
Definition: il.h:7085
Value * index() const
Definition: il.h:7082
Value * value() const
Definition: il.h:5963
const LocalVariable & local() const
Definition: il.h:5962
const Field & field() const
Definition: il.h:6729
Value * value() const
Definition: il.h:6730
bool needs_number_check() const
Definition: il.h:5125
Value * str() const
Definition: il.h:6967
static intptr_t length_offset()
Definition: object.h:10214
static CodePtr GetAllocationStubForClass(const Class &cls)
Definition: stub_code.cc:174
static constexpr int kNullCharCodeSymbolOffset
Definition: symbols.h:605
intptr_t ArgumentCount() const
Definition: il.h:4586
ArrayPtr GetArgumentsDescriptor() const
Definition: il.h:4617
virtual intptr_t InputCount() const
Definition: il.h:2755
const ZoneGrowableArray< intptr_t > & cid_results() const
Definition: il.h:5234
static intptr_t stack_limit_offset()
Definition: thread.h:402
@ kOsrRequest
Definition: thread.h:425
static uword stack_overflow_shared_stub_entry_point_offset(bool fpu_regs)
Definition: thread.h:453
static intptr_t global_object_pool_offset()
Definition: thread.h:853
static intptr_t stack_overflow_flags_offset()
Definition: thread.h:443
virtual void EmitNativeCode(FlowGraphCompiler *compiler)
virtual Representation representation() const
Definition: il.h:9841
Value * value() const
Definition: il.h:9828
Token::Kind op_kind() const
Definition: il.h:9829
Value * value() const
Definition: il.h:9240
Token::Kind op_kind() const
Definition: il.h:9241
virtual Representation representation() const
Definition: il.h:8703
Value * value() const
Definition: il.h:8678
bool is_truncating() const
Definition: il.h:8772
virtual Representation representation() const
Definition: il.h:4288
bool IsScanFlagsUnboxed() const
Definition: il.cc:7181
static T Abs(T x)
Definition: utils.h:49
static void CalculateMagicAndShiftForDivRem(int64_t divisor, int64_t *magic, int64_t *shift)
Definition: utils.cc:39
static constexpr T Maximum(T x, T y)
Definition: utils.h:41
static constexpr int ShiftForPowerOfTwo(T x)
Definition: utils.h:81
static T Minimum(T x, T y)
Definition: utils.h:36
static T AddWithWrapAround(T a, T b)
Definition: utils.h:431
static constexpr int CountOneBits64(uint64_t x)
Definition: utils.h:148
static constexpr size_t HighestBit(int64_t v)
Definition: utils.h:185
static constexpr bool IsPowerOfTwo(T x)
Definition: utils.h:76
bool BindsToConstant() const
Definition: il.cc:1183
intptr_t BoundSmiConstant() const
Definition: il.cc:1212
bool BindsToSmiConstant() const
Definition: il.cc:1208
Definition * definition() const
Definition: il.h:103
CompileType * Type()
Value(Definition *definition)
Definition: il.h:95
intptr_t InputCount() const
Definition: il.h:2794
static Address AddressRIPRelative(int32_t disp)
void static bool EmittingComments()
Address ElementAddressForRegIndex(bool is_load, bool is_external, intptr_t cid, intptr_t index_scale, bool index_unboxed, Register array, Register index)
static Address VMTagAddress()
static bool AddressCanHoldConstantIndex(const Object &constant, bool is_load, bool is_external, intptr_t cid, intptr_t index_scale, bool *needs_base=nullptr)
static bool IsSafe(const Object &object)
static word entry_point_offset(CodeEntryKind kind=CodeEntryKind::kNormal)
static word OffsetOf(const dart::Field &field)
static word entry_point_offset(CodeEntryKind kind=CodeEntryKind::kNormal)
static word unboxed_runtime_arg_offset()
static word shared_field_table_values_offset()
static word field_table_values_offset()
static word exit_through_ffi_offset()
static word global_object_pool_offset()
static word invoke_dart_code_stub_offset()
static word top_exit_frame_info_offset()
static word double_truncate_round_supported_offset()
#define UNIMPLEMENTED
#define ASSERT(E)
SkBitmap source
Definition: examples.cpp:28
#define FATAL(error)
FlutterSemanticsFlag flag
FlutterSemanticsFlag flags
uint8_t value
GAsyncResult * result
uint32_t * target
int argument_count
Definition: fuchsia.cc:52
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
#define R(r)
#define CASE(Arity, Mask, Name, Args, Result)
#define DEFINE_UNIMPLEMENTED_INSTRUCTION(Name)
Definition: il.h:11867
size_t length
def match(bench, filt)
Definition: benchmark.py:23
const intptr_t kResultIndex
Definition: marshaller.h:28
intptr_t FrameOffsetInBytesForVariable(const LocalVariable *variable)
Definition: runtime_api.h:344
static constexpr intptr_t kWordSize
Definition: runtime_api.h:274
static constexpr intptr_t kCompressedWordSize
Definition: runtime_api.h:286
bool IsSmi(int64_t v)
Definition: runtime_api.cc:31
constexpr word kSmiMax
Definition: runtime_api.h:305
FrameLayout frame_layout
Definition: stack_frame.cc:76
bool HasIntegerValue(const dart::Object &object, int64_t *value)
Definition: runtime_api.cc:239
Definition: dart_vm.cc:33
Location LocationAnyOrConstant(Value *value)
Definition: locations.cc:357
Location LocationRegisterOrConstant(Value *value)
Definition: locations.cc:289
const intptr_t kSmiBits
Definition: globals.h:24
const Register kWriteBarrierSlotReg
@ TIMES_COMPRESSED_WORD_SIZE
const Register THR
const char *const name
static Condition InvertCondition(Condition c)
static constexpr int kSavedCallerPcSlotFromFp
bool IsTypedDataBaseClassId(intptr_t index)
Definition: class_id.h:429
constexpr intptr_t kBitsPerWord
Definition: globals.h:514
static bool IsSmiValue(Value *val, intptr_t *int_val)
const Register kExceptionObjectReg
const Register kWriteBarrierObjectReg
const Register kWriteBarrierValueReg
static constexpr bool IsCalleeSavedRegister(Register reg)
Definition: constants.h:85
constexpr intptr_t kIntptrMin
Definition: globals.h:556
int32_t classid_t
Definition: globals.h:524
static const ClassId kLastErrorCid
Definition: class_id.h:311
@ kIllegalCid
Definition: class_id.h:214
@ kNullCid
Definition: class_id.h:252
@ kDynamicCid
Definition: class_id.h:253
Representation
Definition: locations.h:66
constexpr intptr_t kSimd128Size
Definition: globals.h:459
const FpuRegister FpuTMP
const Register CALLEE_SAVED_TEMP
static const ClassId kFirstErrorCid
Definition: class_id.h:310
uintptr_t uword
Definition: globals.h:501
const Register CODE_REG
@ OVERFLOW
@ GREATER_EQUAL
@ UNSIGNED_GREATER
@ kInvalidCondition
@ UNSIGNED_GREATER_EQUAL
@ NOT_CARRY
Definition: constants_x86.h:35
@ NOT_ZERO
@ NO_OVERFLOW
@ NEGATIVE
Definition: constants_x86.h:32
@ LESS_EQUAL
@ BELOW_EQUAL
Definition: constants_x86.h:19
@ PARITY_ODD
Definition: constants_x86.h:24
@ UNSIGNED_LESS
@ NOT_EQUAL
@ ABOVE_EQUAL
Definition: constants_x86.h:16
@ PARITY_EVEN
Definition: constants_x86.h:23
void RegisterTypeArgumentsUse(const Function &function, TypeUsageInfo *type_usage_info, const Class &klass, Definition *type_arguments)
static constexpr int kParamEndSlotFromFp
const Register ARGS_DESC_REG
bool IsClampedTypedDataBaseClassId(intptr_t index)
Definition: class_id.h:461
@ kNumberOfCpuRegisters
Definition: constants_arm.h:98
@ kNoRegister
Definition: constants_arm.h:99
Location LocationFixedRegisterOrConstant(Value *value, Register reg)
Definition: locations.cc:339
const int kNumberOfFpuRegisters
bool IsExternalPayloadClassId(classid_t cid)
Definition: class_id.h:472
constexpr RegList kDartAvailableCpuRegs
static void USE(T &&)
Definition: globals.h:618
constexpr intptr_t kInt32Size
Definition: globals.h:450
const Register TMP
const Register FPREG
static constexpr intptr_t kCompressedWordSize
Definition: globals.h:42
DEFINE_BACKEND(LoadThread,(Register out))
Definition: il.cc:8109
static constexpr int kPcMarkerSlotFromFp
const Register FUNCTION_REG
const Register IC_DATA_REG
compiler::Address LocationToStackSlotAddress(Location loc)
Definition: locations.cc:365
constexpr intptr_t kWordSize
Definition: globals.h:509
Location LocationWritableRegisterOrConstant(Value *value)
Definition: locations.cc:314
static bool IsConstant(Definition *def, int64_t *val)
Definition: loops.cc:123
constexpr intptr_t kFloatSize
Definition: globals.h:457
static constexpr Representation kUnboxedIntPtr
Definition: locations.h:176
const Register PP
QRegister FpuRegister
constexpr bool FLAG_target_memory_sanitizer
Definition: flags.h:174
const Register kStackTraceObjectReg
static int8_t data[kExtLength]
constexpr intptr_t kDoubleSize
Definition: globals.h:456
Location LocationFixedRegisterOrSmiConstant(Value *value, Register reg)
Definition: locations.cc:348
@ kSmiTagSize
@ kHeapObjectTag
@ kSmiTagMask
@ kSmiTagShift
static ScaleFactor ToScaleFactor(intptr_t index_scale, bool index_unboxed)
Definition: constants.h:95
Location LocationRegisterOrSmiConstant(Value *value, intptr_t min_value, intptr_t max_value)
Definition: locations.cc:297
constexpr intptr_t kBitsPerInt64
Definition: globals.h:467
const Register SPREG
COMPILE_ASSERT(kUnreachableReference==WeakTable::kNoValue)
ByteRegister ByteRegisterOf(Register reg)
def call(args)
Definition: dom.py:159
Definition: __init__.py:1
dst
Definition: cp.py:12
static bool Bind(PassBindingsCacheMTL &pass, ShaderStage stage, size_t bind_index, const BufferView &view)
Definition: ref_ptr.h:256
const Scalar scale
SeparatedVector2 offset
static constexpr Register kResultReg
static constexpr Register kLengthReg
static constexpr Register kTypeArgumentsReg
static constexpr Register kResultReg
static constexpr Register kTempReg
static constexpr Register kTypeArgumentsReg
static constexpr Register kResultReg
static constexpr Register kObjectReg
static constexpr Representation NativeRepresentation(Representation rep)
Definition: il.h:8504
intptr_t first_local_from_fp
Definition: frame_layout.h:37
intptr_t FrameSlotForVariable(const LocalVariable *variable) const
Definition: stack_frame.cc:83
static constexpr intptr_t kBoolVsNullMask
static constexpr intptr_t kBoolValueMask
static constexpr size_t ValueSize(Representation rep)
Definition: locations.h:112
static constexpr bool IsUnboxedInteger(Representation rep)
Definition: locations.h:92
static compiler::OperandSize OperandSize(Representation rep)
Definition: locations.cc:16
static constexpr bool IsUnboxed(Representation rep)
Definition: locations.h:101
static bool IsUnsignedInteger(Representation rep)
Definition: locations.h:126
static Representation RepresentationOfArrayElement(classid_t cid)
Definition: locations.cc:79
static constexpr Register kDstTypeReg
static constexpr Register kInstanceReg
static constexpr Register kFunctionTypeArgumentsReg
static constexpr Register kInstantiatorTypeArgumentsReg
#define ASSERT_BOOL_FALSE_FOLLOWS_BOOL_TRUE()
Definition: thread.h:204
#define kNegInfinity
Definition: globals.h:66