1// Copyright 2012 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#if V8_TARGET_ARCH_IA32
6
7#include <stdint.h>
8
9#include "include/v8-internal.h"
10#include "src/base/bits.h"
11#include "src/base/logging.h"
12#include "src/base/macros.h"
13#include "src/base/platform/platform.h"
14#include "src/builtins/builtins.h"
15#include "src/codegen/assembler.h"
16#include "src/codegen/bailout-reason.h"
17#include "src/codegen/code-factory.h"
18#include "src/codegen/cpu-features.h"
19#include "src/codegen/external-reference.h"
20#include "src/codegen/ia32/assembler-ia32.h"
21#include "src/codegen/ia32/register-ia32.h"
22#include "src/codegen/interface-descriptors-inl.h"
23#include "src/codegen/label.h"
24#include "src/codegen/macro-assembler.h"
25#include "src/codegen/register.h"
26#include "src/codegen/reglist.h"
27#include "src/codegen/reloc-info.h"
28#include "src/codegen/turbo-assembler.h"
29#include "src/common/globals.h"
30#include "src/deoptimizer/deoptimizer.h"
31#include "src/execution/frame-constants.h"
32#include "src/execution/frames.h"
33#include "src/execution/isolate-data.h"
34#include "src/execution/isolate.h"
35#include "src/flags/flags.h"
36#include "src/handles/handles-inl.h"
37#include "src/handles/handles.h"
38#include "src/heap/basic-memory-chunk.h"
39#include "src/heap/factory-inl.h"
40#include "src/heap/factory.h"
41#include "src/heap/memory-chunk.h"
42#include "src/logging/counters.h"
43#include "src/objects/code.h"
44#include "src/objects/contexts.h"
45#include "src/objects/fixed-array.h"
46#include "src/objects/heap-object.h"
47#include "src/objects/js-function.h"
48#include "src/objects/map.h"
49#include "src/objects/objects.h"
50#include "src/objects/oddball.h"
51#include "src/objects/shared-function-info.h"
52#include "src/objects/slots-inl.h"
53#include "src/objects/smi.h"
54#include "src/roots/roots-inl.h"
55#include "src/roots/roots.h"
56#include "src/runtime/runtime.h"
57#include "src/utils/utils.h"
58
59// Satisfy cpplint check, but don't include platform-specific header. It is
60// included recursively via macro-assembler.h.
61#if 0
62#include "src/codegen/ia32/macro-assembler-ia32.h"
63#endif
64
65namespace v8 {
66namespace internal {
67
68Operand StackArgumentsAccessor::GetArgumentOperand(int index) const {
69  DCHECK_GE(index, 0);
70  // arg[0] = esp + kPCOnStackSize;
71  // arg[i] = arg[0] + i * kSystemPointerSize;
72  return Operand(esp, kPCOnStackSize + index * kSystemPointerSize);
73}
74
75// -------------------------------------------------------------------------
76// MacroAssembler implementation.
77
78void TurboAssembler::InitializeRootRegister() {
79  ASM_CODE_COMMENT(this);
80  ExternalReference isolate_root = ExternalReference::isolate_root(isolate());
81  Move(kRootRegister, Immediate(isolate_root));
82}
83
84Operand TurboAssembler::RootAsOperand(RootIndex index) {
85  DCHECK(root_array_available());
86  return Operand(kRootRegister, RootRegisterOffsetForRootIndex(index));
87}
88
89void TurboAssembler::LoadRoot(Register destination, RootIndex index) {
90  ASM_CODE_COMMENT(this);
91  if (root_array_available()) {
92    mov(destination, RootAsOperand(index));
93    return;
94  }
95
96  if (RootsTable::IsImmortalImmovable(index)) {
97    Handle<Object> object = isolate()->root_handle(index);
98    if (object->IsSmi()) {
99      mov(destination, Immediate(Smi::cast(*object)));
100      return;
101    } else {
102      DCHECK(object->IsHeapObject());
103      mov(destination, Handle<HeapObject>::cast(object));
104      return;
105    }
106  }
107
108  ExternalReference isolate_root = ExternalReference::isolate_root(isolate());
109  lea(destination,
110      Operand(isolate_root.address(), RelocInfo::EXTERNAL_REFERENCE));
111  mov(destination, Operand(destination, RootRegisterOffsetForRootIndex(index)));
112}
113
114void TurboAssembler::CompareRoot(Register with, Register scratch,
115                                 RootIndex index) {
116  ASM_CODE_COMMENT(this);
117  if (root_array_available()) {
118    CompareRoot(with, index);
119  } else {
120    ExternalReference isolate_root = ExternalReference::isolate_root(isolate());
121    lea(scratch,
122        Operand(isolate_root.address(), RelocInfo::EXTERNAL_REFERENCE));
123    cmp(with, Operand(scratch, RootRegisterOffsetForRootIndex(index)));
124  }
125}
126
127void TurboAssembler::CompareRoot(Register with, RootIndex index) {
128  ASM_CODE_COMMENT(this);
129  if (root_array_available()) {
130    cmp(with, RootAsOperand(index));
131    return;
132  }
133
134  DCHECK(RootsTable::IsImmortalImmovable(index));
135  Handle<Object> object = isolate()->root_handle(index);
136  if (object->IsHeapObject()) {
137    cmp(with, Handle<HeapObject>::cast(object));
138  } else {
139    cmp(with, Immediate(Smi::cast(*object)));
140  }
141}
142
143void MacroAssembler::PushRoot(RootIndex index) {
144  ASM_CODE_COMMENT(this);
145  if (root_array_available()) {
146    DCHECK(RootsTable::IsImmortalImmovable(index));
147    push(RootAsOperand(index));
148    return;
149  }
150
151  // TODO(v8:6666): Add a scratch register or remove all uses.
152  DCHECK(RootsTable::IsImmortalImmovable(index));
153  Handle<Object> object = isolate()->root_handle(index);
154  if (object->IsHeapObject()) {
155    Push(Handle<HeapObject>::cast(object));
156  } else {
157    Push(Smi::cast(*object));
158  }
159}
160
161void MacroAssembler::CompareRange(Register value, unsigned lower_limit,
162                                  unsigned higher_limit, Register scratch) {
163  ASM_CODE_COMMENT(this);
164  DCHECK_LT(lower_limit, higher_limit);
165  if (lower_limit != 0) {
166    lea(scratch, Operand(value, 0u - lower_limit));
167    cmp(scratch, Immediate(higher_limit - lower_limit));
168  } else {
169    cmp(value, Immediate(higher_limit));
170  }
171}
172
173void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit,
174                                     unsigned higher_limit, Register scratch,
175                                     Label* on_in_range,
176                                     Label::Distance near_jump) {
177  CompareRange(value, lower_limit, higher_limit, scratch);
178  j(below_equal, on_in_range, near_jump);
179}
180
181void TurboAssembler::PushArray(Register array, Register size, Register scratch,
182                               PushArrayOrder order) {
183  ASM_CODE_COMMENT(this);
184  DCHECK(!AreAliased(array, size, scratch));
185  Register counter = scratch;
186  Label loop, entry;
187  if (order == PushArrayOrder::kReverse) {
188    mov(counter, 0);
189    jmp(&entry);
190    bind(&loop);
191    Push(Operand(array, counter, times_system_pointer_size, 0));
192    inc(counter);
193    bind(&entry);
194    cmp(counter, size);
195    j(less, &loop, Label::kNear);
196  } else {
197    mov(counter, size);
198    jmp(&entry);
199    bind(&loop);
200    Push(Operand(array, counter, times_system_pointer_size, 0));
201    bind(&entry);
202    dec(counter);
203    j(greater_equal, &loop, Label::kNear);
204  }
205}
206
207Operand TurboAssembler::ExternalReferenceAsOperand(ExternalReference reference,
208                                                   Register scratch) {
209  if (root_array_available() && options().enable_root_relative_access) {
210    intptr_t delta =
211        RootRegisterOffsetForExternalReference(isolate(), reference);
212    return Operand(kRootRegister, delta);
213  }
214  if (root_array_available() && options().isolate_independent_code) {
215    if (IsAddressableThroughRootRegister(isolate(), reference)) {
216      // Some external references can be efficiently loaded as an offset from
217      // kRootRegister.
218      intptr_t offset =
219          RootRegisterOffsetForExternalReference(isolate(), reference);
220      return Operand(kRootRegister, offset);
221    } else {
222      // Otherwise, do a memory load from the external reference table.
223      mov(scratch, Operand(kRootRegister,
224                           RootRegisterOffsetForExternalReferenceTableEntry(
225                               isolate(), reference)));
226      return Operand(scratch, 0);
227    }
228  }
229  Move(scratch, Immediate(reference));
230  return Operand(scratch, 0);
231}
232
233// TODO(v8:6666): If possible, refactor into a platform-independent function in
234// TurboAssembler.
235Operand TurboAssembler::ExternalReferenceAddressAsOperand(
236    ExternalReference reference) {
237  DCHECK(root_array_available());
238  DCHECK(options().isolate_independent_code);
239  return Operand(
240      kRootRegister,
241      RootRegisterOffsetForExternalReferenceTableEntry(isolate(), reference));
242}
243
244// TODO(v8:6666): If possible, refactor into a platform-independent function in
245// TurboAssembler.
246Operand TurboAssembler::HeapObjectAsOperand(Handle<HeapObject> object) {
247  DCHECK(root_array_available());
248
249  Builtin builtin;
250  RootIndex root_index;
251  if (isolate()->roots_table().IsRootHandle(object, &root_index)) {
252    return RootAsOperand(root_index);
253  } else if (isolate()->builtins()->IsBuiltinHandle(object, &builtin)) {
254    return Operand(kRootRegister, RootRegisterOffsetForBuiltin(builtin));
255  } else if (object.is_identical_to(code_object_) &&
256             Builtins::IsBuiltinId(maybe_builtin_)) {
257    return Operand(kRootRegister, RootRegisterOffsetForBuiltin(maybe_builtin_));
258  } else {
259    // Objects in the constants table need an additional indirection, which
260    // cannot be represented as a single Operand.
261    UNREACHABLE();
262  }
263}
264
265void TurboAssembler::LoadFromConstantsTable(Register destination,
266                                            int constant_index) {
267  ASM_CODE_COMMENT(this);
268  DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kBuiltinsConstantsTable));
269  LoadRoot(destination, RootIndex::kBuiltinsConstantsTable);
270  mov(destination,
271      FieldOperand(destination, FixedArray::OffsetOfElementAt(constant_index)));
272}
273
274void TurboAssembler::LoadRootRegisterOffset(Register destination,
275                                            intptr_t offset) {
276  ASM_CODE_COMMENT(this);
277  DCHECK(is_int32(offset));
278  DCHECK(root_array_available());
279  if (offset == 0) {
280    mov(destination, kRootRegister);
281  } else {
282    lea(destination, Operand(kRootRegister, static_cast<int32_t>(offset)));
283  }
284}
285
286void TurboAssembler::LoadRootRelative(Register destination, int32_t offset) {
287  ASM_CODE_COMMENT(this);
288  DCHECK(root_array_available());
289  mov(destination, Operand(kRootRegister, offset));
290}
291
292void TurboAssembler::LoadAddress(Register destination,
293                                 ExternalReference source) {
294  // TODO(jgruber): Add support for enable_root_relative_access.
295  if (root_array_available() && options().isolate_independent_code) {
296    IndirectLoadExternalReference(destination, source);
297    return;
298  }
299  mov(destination, Immediate(source));
300}
301
302static constexpr Register saved_regs[] = {eax, ecx, edx};
303
304static constexpr int kNumberOfSavedRegs = sizeof(saved_regs) / sizeof(Register);
305
306int TurboAssembler::RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode,
307                                                    Register exclusion1,
308                                                    Register exclusion2,
309                                                    Register exclusion3) const {
310  int bytes = 0;
311  for (int i = 0; i < kNumberOfSavedRegs; i++) {
312    Register reg = saved_regs[i];
313    if (reg != exclusion1 && reg != exclusion2 && reg != exclusion3) {
314      bytes += kSystemPointerSize;
315    }
316  }
317
318  if (fp_mode == SaveFPRegsMode::kSave) {
319    // Count all XMM registers except XMM0.
320    bytes += kStackSavedSavedFPSize * (XMMRegister::kNumRegisters - 1);
321  }
322
323  return bytes;
324}
325
326int TurboAssembler::PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1,
327                                    Register exclusion2, Register exclusion3) {
328  ASM_CODE_COMMENT(this);
329  // We don't allow a GC in a write barrier slow path so there is no need to
330  // store the registers in any particular way, but we do have to store and
331  // restore them.
332  int bytes = 0;
333  for (int i = 0; i < kNumberOfSavedRegs; i++) {
334    Register reg = saved_regs[i];
335    if (reg != exclusion1 && reg != exclusion2 && reg != exclusion3) {
336      push(reg);
337      bytes += kSystemPointerSize;
338    }
339  }
340
341  if (fp_mode == SaveFPRegsMode::kSave) {
342    // Save all XMM registers except XMM0.
343    const int delta = kStackSavedSavedFPSize * (XMMRegister::kNumRegisters - 1);
344    AllocateStackSpace(delta);
345    for (int i = XMMRegister::kNumRegisters - 1; i > 0; i--) {
346      XMMRegister reg = XMMRegister::from_code(i);
347#if V8_ENABLE_WEBASSEMBLY
348      Movdqu(Operand(esp, (i - 1) * kStackSavedSavedFPSize), reg);
349#else
350      Movsd(Operand(esp, (i - 1) * kStackSavedSavedFPSize), reg);
351#endif  // V8_ENABLE_WEBASSEMBLY
352    }
353    bytes += delta;
354  }
355
356  return bytes;
357}
358
359int TurboAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1,
360                                   Register exclusion2, Register exclusion3) {
361  ASM_CODE_COMMENT(this);
362  int bytes = 0;
363  if (fp_mode == SaveFPRegsMode::kSave) {
364    // Restore all XMM registers except XMM0.
365    const int delta = kStackSavedSavedFPSize * (XMMRegister::kNumRegisters - 1);
366    for (int i = XMMRegister::kNumRegisters - 1; i > 0; i--) {
367      XMMRegister reg = XMMRegister::from_code(i);
368#if V8_ENABLE_WEBASSEMBLY
369      Movdqu(reg, Operand(esp, (i - 1) * kStackSavedSavedFPSize));
370#else
371      Movsd(reg, Operand(esp, (i - 1) * kStackSavedSavedFPSize));
372#endif  // V8_ENABLE_WEBASSEMBLY
373    }
374    add(esp, Immediate(delta));
375    bytes += delta;
376  }
377
378  for (int i = kNumberOfSavedRegs - 1; i >= 0; i--) {
379    Register reg = saved_regs[i];
380    if (reg != exclusion1 && reg != exclusion2 && reg != exclusion3) {
381      pop(reg);
382      bytes += kSystemPointerSize;
383    }
384  }
385
386  return bytes;
387}
388
389void MacroAssembler::RecordWriteField(Register object, int offset,
390                                      Register value, Register slot_address,
391                                      SaveFPRegsMode save_fp,
392                                      RememberedSetAction remembered_set_action,
393                                      SmiCheck smi_check) {
394  ASM_CODE_COMMENT(this);
395  // First, check if a write barrier is even needed. The tests below
396  // catch stores of Smis.
397  Label done;
398
399  // Skip barrier if writing a smi.
400  if (smi_check == SmiCheck::kInline) {
401    JumpIfSmi(value, &done);
402  }
403
404  // Although the object register is tagged, the offset is relative to the start
405  // of the object, so so offset must be a multiple of kTaggedSize.
406  DCHECK(IsAligned(offset, kTaggedSize));
407
408  lea(slot_address, FieldOperand(object, offset));
409  if (FLAG_debug_code) {
410    Label ok;
411    test_b(slot_address, Immediate(kTaggedSize - 1));
412    j(zero, &ok, Label::kNear);
413    int3();
414    bind(&ok);
415  }
416
417  RecordWrite(object, slot_address, value, save_fp, remembered_set_action,
418              SmiCheck::kOmit);
419
420  bind(&done);
421
422  // Clobber clobbered input registers when running with the debug-code flag
423  // turned on to provoke errors.
424  if (FLAG_debug_code) {
425    mov(value, Immediate(bit_cast<int32_t>(kZapValue)));
426    mov(slot_address, Immediate(bit_cast<int32_t>(kZapValue)));
427  }
428}
429
430void TurboAssembler::MaybeSaveRegisters(RegList registers) {
431  for (Register reg : registers) {
432    push(reg);
433  }
434}
435
436void TurboAssembler::MaybeRestoreRegisters(RegList registers) {
437  for (Register reg : base::Reversed(registers)) {
438    pop(reg);
439  }
440}
441
442void TurboAssembler::CallEphemeronKeyBarrier(Register object,
443                                             Register slot_address,
444                                             SaveFPRegsMode fp_mode) {
445  ASM_CODE_COMMENT(this);
446  DCHECK(!AreAliased(object, slot_address));
447  RegList registers =
448      WriteBarrierDescriptor::ComputeSavedRegisters(object, slot_address);
449  MaybeSaveRegisters(registers);
450
451  Register object_parameter = WriteBarrierDescriptor::ObjectRegister();
452  Register slot_address_parameter =
453      WriteBarrierDescriptor::SlotAddressRegister();
454
455  push(object);
456  push(slot_address);
457  pop(slot_address_parameter);
458  pop(object_parameter);
459
460  Call(isolate()->builtins()->code_handle(
461           Builtins::GetEphemeronKeyBarrierStub(fp_mode)),
462       RelocInfo::CODE_TARGET);
463
464  MaybeRestoreRegisters(registers);
465}
466
467void TurboAssembler::CallRecordWriteStubSaveRegisters(
468    Register object, Register slot_address,
469    RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode,
470    StubCallMode mode) {
471  ASM_CODE_COMMENT(this);
472  DCHECK(!AreAliased(object, slot_address));
473  RegList registers =
474      WriteBarrierDescriptor::ComputeSavedRegisters(object, slot_address);
475  MaybeSaveRegisters(registers);
476
477  Register object_parameter = WriteBarrierDescriptor::ObjectRegister();
478  Register slot_address_parameter =
479      WriteBarrierDescriptor::SlotAddressRegister();
480
481  push(object);
482  push(slot_address);
483  pop(slot_address_parameter);
484  pop(object_parameter);
485
486  CallRecordWriteStub(object_parameter, slot_address_parameter,
487                      remembered_set_action, fp_mode, mode);
488
489  MaybeRestoreRegisters(registers);
490}
491
492void TurboAssembler::CallRecordWriteStub(
493    Register object, Register slot_address,
494    RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode,
495    StubCallMode mode) {
496  ASM_CODE_COMMENT(this);
497  // Use CallRecordWriteStubSaveRegisters if the object and slot registers
498  // need to be caller saved.
499  DCHECK_EQ(WriteBarrierDescriptor::ObjectRegister(), object);
500  DCHECK_EQ(WriteBarrierDescriptor::SlotAddressRegister(), slot_address);
501#if V8_ENABLE_WEBASSEMBLY
502  if (mode == StubCallMode::kCallWasmRuntimeStub) {
503    // Use {wasm_call} for direct Wasm call within a module.
504    auto wasm_target =
505        wasm::WasmCode::GetRecordWriteStub(remembered_set_action, fp_mode);
506    wasm_call(wasm_target, RelocInfo::WASM_STUB_CALL);
507#else
508  if (false) {
509#endif
510  } else {
511    Builtin builtin =
512        Builtins::GetRecordWriteStub(remembered_set_action, fp_mode);
513    if (options().inline_offheap_trampolines) {
514      CallBuiltin(builtin);
515    } else {
516      Handle<Code> code_target = isolate()->builtins()->code_handle(builtin);
517      Call(code_target, RelocInfo::CODE_TARGET);
518    }
519  }
520}
521
522void MacroAssembler::RecordWrite(Register object, Register slot_address,
523                                 Register value, SaveFPRegsMode fp_mode,
524                                 RememberedSetAction remembered_set_action,
525                                 SmiCheck smi_check) {
526  ASM_CODE_COMMENT(this);
527  DCHECK(!AreAliased(object, value, slot_address));
528  AssertNotSmi(object);
529
530  if ((remembered_set_action == RememberedSetAction::kOmit &&
531       !FLAG_incremental_marking) ||
532      FLAG_disable_write_barriers) {
533    return;
534  }
535
536  if (FLAG_debug_code) {
537    ASM_CODE_COMMENT_STRING(this, "Verify slot_address");
538    Label ok;
539    cmp(value, Operand(slot_address, 0));
540    j(equal, &ok, Label::kNear);
541    int3();
542    bind(&ok);
543  }
544
545  // First, check if a write barrier is even needed. The tests below
546  // catch stores of Smis and stores into young gen.
547  Label done;
548
549  if (smi_check == SmiCheck::kInline) {
550    // Skip barrier if writing a smi.
551    JumpIfSmi(value, &done, Label::kNear);
552  }
553
554  CheckPageFlag(value,
555                value,  // Used as scratch.
556                MemoryChunk::kPointersToHereAreInterestingMask, zero, &done,
557                Label::kNear);
558  CheckPageFlag(object,
559                value,  // Used as scratch.
560                MemoryChunk::kPointersFromHereAreInterestingMask, zero, &done,
561                Label::kNear);
562  RecordComment("CheckPageFlag]");
563
564  CallRecordWriteStub(object, slot_address, remembered_set_action, fp_mode);
565
566  bind(&done);
567
568  // Clobber clobbered registers when running with the debug-code flag
569  // turned on to provoke errors.
570  if (FLAG_debug_code) {
571    ASM_CODE_COMMENT_STRING(this, "Clobber slot_address and value");
572    mov(slot_address, Immediate(bit_cast<int32_t>(kZapValue)));
573    mov(value, Immediate(bit_cast<int32_t>(kZapValue)));
574  }
575}
576
577void TurboAssembler::Cvtsi2ss(XMMRegister dst, Operand src) {
578  xorps(dst, dst);
579  cvtsi2ss(dst, src);
580}
581
582void TurboAssembler::Cvtsi2sd(XMMRegister dst, Operand src) {
583  xorpd(dst, dst);
584  cvtsi2sd(dst, src);
585}
586
587void TurboAssembler::Cvtui2ss(XMMRegister dst, Operand src, Register tmp) {
588  Label done;
589  Register src_reg = src.is_reg_only() ? src.reg() : tmp;
590  if (src_reg == tmp) mov(tmp, src);
591  cvtsi2ss(dst, src_reg);
592  test(src_reg, src_reg);
593  j(positive, &done, Label::kNear);
594
595  // Compute {src/2 | (src&1)} (retain the LSB to avoid rounding errors).
596  if (src_reg != tmp) mov(tmp, src_reg);
597  shr(tmp, 1);
598  // The LSB is shifted into CF. If it is set, set the LSB in {tmp}.
599  Label msb_not_set;
600  j(not_carry, &msb_not_set, Label::kNear);
601  or_(tmp, Immediate(1));
602  bind(&msb_not_set);
603  cvtsi2ss(dst, tmp);
604  addss(dst, dst);
605  bind(&done);
606}
607
608void TurboAssembler::Cvttss2ui(Register dst, Operand src, XMMRegister tmp) {
609  Label done;
610  cvttss2si(dst, src);
611  test(dst, dst);
612  j(positive, &done);
613  Move(tmp, static_cast<float>(INT32_MIN));
614  addss(tmp, src);
615  cvttss2si(dst, tmp);
616  or_(dst, Immediate(0x80000000));
617  bind(&done);
618}
619
620void TurboAssembler::Cvtui2sd(XMMRegister dst, Operand src, Register scratch) {
621  Label done;
622  cmp(src, Immediate(0));
623  ExternalReference uint32_bias = ExternalReference::address_of_uint32_bias();
624  Cvtsi2sd(dst, src);
625  j(not_sign, &done, Label::kNear);
626  addsd(dst, ExternalReferenceAsOperand(uint32_bias, scratch));
627  bind(&done);
628}
629
630void TurboAssembler::Cvttsd2ui(Register dst, Operand src, XMMRegister tmp) {
631  Move(tmp, -2147483648.0);
632  addsd(tmp, src);
633  cvttsd2si(dst, tmp);
634  add(dst, Immediate(0x80000000));
635}
636
637void TurboAssembler::ShlPair(Register high, Register low, uint8_t shift) {
638  DCHECK_GE(63, shift);
639  if (shift >= 32) {
640    mov(high, low);
641    if (shift != 32) shl(high, shift - 32);
642    xor_(low, low);
643  } else {
644    shld(high, low, shift);
645    shl(low, shift);
646  }
647}
648
649void TurboAssembler::ShlPair_cl(Register high, Register low) {
650  ASM_CODE_COMMENT(this);
651  shld_cl(high, low);
652  shl_cl(low);
653  Label done;
654  test(ecx, Immediate(0x20));
655  j(equal, &done, Label::kNear);
656  mov(high, low);
657  xor_(low, low);
658  bind(&done);
659}
660
661void TurboAssembler::ShrPair(Register high, Register low, uint8_t shift) {
662  DCHECK_GE(63, shift);
663  if (shift >= 32) {
664    mov(low, high);
665    if (shift != 32) shr(low, shift - 32);
666    xor_(high, high);
667  } else {
668    shrd(low, high, shift);
669    shr(high, shift);
670  }
671}
672
673void TurboAssembler::ShrPair_cl(Register high, Register low) {
674  ASM_CODE_COMMENT(this);
675  shrd_cl(low, high);
676  shr_cl(high);
677  Label done;
678  test(ecx, Immediate(0x20));
679  j(equal, &done, Label::kNear);
680  mov(low, high);
681  xor_(high, high);
682  bind(&done);
683}
684
685void TurboAssembler::SarPair(Register high, Register low, uint8_t shift) {
686  ASM_CODE_COMMENT(this);
687  DCHECK_GE(63, shift);
688  if (shift >= 32) {
689    mov(low, high);
690    if (shift != 32) sar(low, shift - 32);
691    sar(high, 31);
692  } else {
693    shrd(low, high, shift);
694    sar(high, shift);
695  }
696}
697
698void TurboAssembler::SarPair_cl(Register high, Register low) {
699  ASM_CODE_COMMENT(this);
700  shrd_cl(low, high);
701  sar_cl(high);
702  Label done;
703  test(ecx, Immediate(0x20));
704  j(equal, &done, Label::kNear);
705  mov(low, high);
706  sar(high, 31);
707  bind(&done);
708}
709
710void TurboAssembler::LoadMap(Register destination, Register object) {
711  mov(destination, FieldOperand(object, HeapObject::kMapOffset));
712}
713
714void MacroAssembler::CmpObjectType(Register heap_object, InstanceType type,
715                                   Register map) {
716  ASM_CODE_COMMENT(this);
717  LoadMap(map, heap_object);
718  CmpInstanceType(map, type);
719}
720
721void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
722  cmpw(FieldOperand(map, Map::kInstanceTypeOffset), Immediate(type));
723}
724
725void MacroAssembler::CmpInstanceTypeRange(Register map,
726                                          Register instance_type_out,
727                                          Register scratch,
728                                          InstanceType lower_limit,
729                                          InstanceType higher_limit) {
730  ASM_CODE_COMMENT(this);
731  DCHECK_LT(lower_limit, higher_limit);
732  movzx_w(instance_type_out, FieldOperand(map, Map::kInstanceTypeOffset));
733  CompareRange(instance_type_out, lower_limit, higher_limit, scratch);
734}
735
736void MacroAssembler::AssertSmi(Register object) {
737  if (FLAG_debug_code) {
738    ASM_CODE_COMMENT(this);
739    test(object, Immediate(kSmiTagMask));
740    Check(equal, AbortReason::kOperandIsNotASmi);
741  }
742}
743
744void MacroAssembler::AssertConstructor(Register object) {
745  if (FLAG_debug_code) {
746    ASM_CODE_COMMENT(this);
747    test(object, Immediate(kSmiTagMask));
748    Check(not_equal, AbortReason::kOperandIsASmiAndNotAConstructor);
749    Push(object);
750    LoadMap(object, object);
751    test_b(FieldOperand(object, Map::kBitFieldOffset),
752           Immediate(Map::Bits1::IsConstructorBit::kMask));
753    Pop(object);
754    Check(not_zero, AbortReason::kOperandIsNotAConstructor);
755  }
756}
757
758void MacroAssembler::AssertFunction(Register object, Register scratch) {
759  if (FLAG_debug_code) {
760    ASM_CODE_COMMENT(this);
761    test(object, Immediate(kSmiTagMask));
762    Check(not_equal, AbortReason::kOperandIsASmiAndNotAFunction);
763    Push(object);
764    LoadMap(object, object);
765    CmpInstanceTypeRange(object, scratch, scratch, FIRST_JS_FUNCTION_TYPE,
766                         LAST_JS_FUNCTION_TYPE);
767    Pop(object);
768    Check(below_equal, AbortReason::kOperandIsNotAFunction);
769  }
770}
771
772void MacroAssembler::AssertCallableFunction(Register object, Register scratch) {
773  if (FLAG_debug_code) {
774    ASM_CODE_COMMENT(this);
775    test(object, Immediate(kSmiTagMask));
776    Check(not_equal, AbortReason::kOperandIsASmiAndNotAFunction);
777    Push(object);
778    LoadMap(object, object);
779    CmpInstanceTypeRange(object, scratch, scratch,
780                         FIRST_CALLABLE_JS_FUNCTION_TYPE,
781                         LAST_CALLABLE_JS_FUNCTION_TYPE);
782    Pop(object);
783    Check(below_equal, AbortReason::kOperandIsNotACallableFunction);
784  }
785}
786
787void MacroAssembler::AssertBoundFunction(Register object) {
788  if (FLAG_debug_code) {
789    ASM_CODE_COMMENT(this);
790    test(object, Immediate(kSmiTagMask));
791    Check(not_equal, AbortReason::kOperandIsASmiAndNotABoundFunction);
792    Push(object);
793    CmpObjectType(object, JS_BOUND_FUNCTION_TYPE, object);
794    Pop(object);
795    Check(equal, AbortReason::kOperandIsNotABoundFunction);
796  }
797}
798
799void MacroAssembler::AssertGeneratorObject(Register object) {
800  if (!FLAG_debug_code) return;
801  ASM_CODE_COMMENT(this);
802
803  test(object, Immediate(kSmiTagMask));
804  Check(not_equal, AbortReason::kOperandIsASmiAndNotAGeneratorObject);
805
806  {
807    Push(object);
808    Register map = object;
809
810    LoadMap(map, object);
811
812    Label do_check;
813    // Check if JSGeneratorObject
814    CmpInstanceType(map, JS_GENERATOR_OBJECT_TYPE);
815    j(equal, &do_check, Label::kNear);
816
817    // Check if JSAsyncFunctionObject.
818    CmpInstanceType(map, JS_ASYNC_FUNCTION_OBJECT_TYPE);
819    j(equal, &do_check, Label::kNear);
820
821    // Check if JSAsyncGeneratorObject
822    CmpInstanceType(map, JS_ASYNC_GENERATOR_OBJECT_TYPE);
823
824    bind(&do_check);
825    Pop(object);
826  }
827
828  Check(equal, AbortReason::kOperandIsNotAGeneratorObject);
829}
830
831void MacroAssembler::AssertUndefinedOrAllocationSite(Register object,
832                                                     Register scratch) {
833  if (FLAG_debug_code) {
834    ASM_CODE_COMMENT(this);
835    Label done_checking;
836    AssertNotSmi(object);
837    CompareRoot(object, scratch, RootIndex::kUndefinedValue);
838    j(equal, &done_checking);
839    LoadRoot(scratch, RootIndex::kAllocationSiteWithWeakNextMap);
840    cmp(FieldOperand(object, 0), scratch);
841    Assert(equal, AbortReason::kExpectedUndefinedOrCell);
842    bind(&done_checking);
843  }
844}
845
846void MacroAssembler::AssertNotSmi(Register object) {
847  if (FLAG_debug_code) {
848    ASM_CODE_COMMENT(this);
849    test(object, Immediate(kSmiTagMask));
850    Check(not_equal, AbortReason::kOperandIsASmi);
851  }
852}
853
854void TurboAssembler::StubPrologue(StackFrame::Type type) {
855  ASM_CODE_COMMENT(this);
856  push(ebp);  // Caller's frame pointer.
857  mov(ebp, esp);
858  push(Immediate(StackFrame::TypeToMarker(type)));
859}
860
861void TurboAssembler::Prologue() {
862  ASM_CODE_COMMENT(this);
863  push(ebp);  // Caller's frame pointer.
864  mov(ebp, esp);
865  push(kContextRegister);                 // Callee's context.
866  push(kJSFunctionRegister);              // Callee's JS function.
867  push(kJavaScriptCallArgCountRegister);  // Actual argument count.
868}
869
870void TurboAssembler::DropArguments(Register count, ArgumentsCountType type,
871                                   ArgumentsCountMode mode) {
872  int receiver_bytes =
873      (mode == kCountExcludesReceiver) ? kSystemPointerSize : 0;
874  switch (type) {
875    case kCountIsInteger: {
876      lea(esp, Operand(esp, count, times_system_pointer_size, receiver_bytes));
877      break;
878    }
879    case kCountIsSmi: {
880      STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
881      // SMIs are stored shifted left by 1 byte with the tag being 0.
882      // This is equivalent to multiplying by 2. To convert SMIs to bytes we
883      // can therefore just multiply the stored value by half the system pointer
884      // size.
885      lea(esp,
886          Operand(esp, count, times_half_system_pointer_size, receiver_bytes));
887      break;
888    }
889    case kCountIsBytes: {
890      if (receiver_bytes == 0) {
891        add(esp, count);
892      } else {
893        lea(esp, Operand(esp, count, times_1, receiver_bytes));
894      }
895      break;
896    }
897  }
898}
899
900void TurboAssembler::DropArguments(Register count, Register scratch,
901                                   ArgumentsCountType type,
902                                   ArgumentsCountMode mode) {
903  DCHECK(!AreAliased(count, scratch));
904  PopReturnAddressTo(scratch);
905  DropArguments(count, type, mode);
906  PushReturnAddressFrom(scratch);
907}
908
909void TurboAssembler::DropArgumentsAndPushNewReceiver(Register argc,
910                                                     Register receiver,
911                                                     Register scratch,
912                                                     ArgumentsCountType type,
913                                                     ArgumentsCountMode mode) {
914  DCHECK(!AreAliased(argc, receiver, scratch));
915  PopReturnAddressTo(scratch);
916  DropArguments(argc, type, mode);
917  Push(receiver);
918  PushReturnAddressFrom(scratch);
919}
920
921void TurboAssembler::DropArgumentsAndPushNewReceiver(Register argc,
922                                                     Operand receiver,
923                                                     Register scratch,
924                                                     ArgumentsCountType type,
925                                                     ArgumentsCountMode mode) {
926  DCHECK(!AreAliased(argc, scratch));
927  DCHECK(!receiver.is_reg(scratch));
928  PopReturnAddressTo(scratch);
929  DropArguments(argc, type, mode);
930  Push(receiver);
931  PushReturnAddressFrom(scratch);
932}
933
934void TurboAssembler::EnterFrame(StackFrame::Type type) {
935  ASM_CODE_COMMENT(this);
936  push(ebp);
937  mov(ebp, esp);
938  if (!StackFrame::IsJavaScript(type)) {
939    Push(Immediate(StackFrame::TypeToMarker(type)));
940  }
941#if V8_ENABLE_WEBASSEMBLY
942  if (type == StackFrame::WASM) Push(kWasmInstanceRegister);
943#endif  // V8_ENABLE_WEBASSEMBLY
944}
945
946void TurboAssembler::LeaveFrame(StackFrame::Type type) {
947  ASM_CODE_COMMENT(this);
948  if (FLAG_debug_code && !StackFrame::IsJavaScript(type)) {
949    cmp(Operand(ebp, CommonFrameConstants::kContextOrFrameTypeOffset),
950        Immediate(StackFrame::TypeToMarker(type)));
951    Check(equal, AbortReason::kStackFrameTypesMustMatch);
952  }
953  leave();
954}
955
956#ifdef V8_OS_WIN
957void TurboAssembler::AllocateStackSpace(Register bytes_scratch) {
958  ASM_CODE_COMMENT(this);
959  // In windows, we cannot increment the stack size by more than one page
960  // (minimum page size is 4KB) without accessing at least one byte on the
961  // page. Check this:
962  // https://msdn.microsoft.com/en-us/library/aa227153(v=vs.60).aspx.
963  Label check_offset;
964  Label touch_next_page;
965  jmp(&check_offset);
966  bind(&touch_next_page);
967  sub(esp, Immediate(kStackPageSize));
968  // Just to touch the page, before we increment further.
969  mov(Operand(esp, 0), Immediate(0));
970  sub(bytes_scratch, Immediate(kStackPageSize));
971
972  bind(&check_offset);
973  cmp(bytes_scratch, kStackPageSize);
974  j(greater_equal, &touch_next_page);
975
976  sub(esp, bytes_scratch);
977}
978
979void TurboAssembler::AllocateStackSpace(int bytes) {
980  ASM_CODE_COMMENT(this);
981  DCHECK_GE(bytes, 0);
982  while (bytes >= kStackPageSize) {
983    sub(esp, Immediate(kStackPageSize));
984    mov(Operand(esp, 0), Immediate(0));
985    bytes -= kStackPageSize;
986  }
987  if (bytes == 0) return;
988  sub(esp, Immediate(bytes));
989}
990#endif
991
992void MacroAssembler::EnterExitFramePrologue(StackFrame::Type frame_type,
993                                            Register scratch) {
994  ASM_CODE_COMMENT(this);
995  DCHECK(frame_type == StackFrame::EXIT ||
996         frame_type == StackFrame::BUILTIN_EXIT);
997
998  // Set up the frame structure on the stack.
999  DCHECK_EQ(+2 * kSystemPointerSize, ExitFrameConstants::kCallerSPDisplacement);
1000  DCHECK_EQ(+1 * kSystemPointerSize, ExitFrameConstants::kCallerPCOffset);
1001  DCHECK_EQ(0 * kSystemPointerSize, ExitFrameConstants::kCallerFPOffset);
1002  push(ebp);
1003  mov(ebp, esp);
1004
1005  // Reserve room for entry stack pointer.
1006  push(Immediate(StackFrame::TypeToMarker(frame_type)));
1007  DCHECK_EQ(-2 * kSystemPointerSize, ExitFrameConstants::kSPOffset);
1008  push(Immediate(0));  // Saved entry sp, patched before call.
1009
1010  STATIC_ASSERT(edx == kRuntimeCallFunctionRegister);
1011  STATIC_ASSERT(esi == kContextRegister);
1012
1013  // Save the frame pointer and the context in top.
1014  ExternalReference c_entry_fp_address =
1015      ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate());
1016  ExternalReference context_address =
1017      ExternalReference::Create(IsolateAddressId::kContextAddress, isolate());
1018  ExternalReference c_function_address =
1019      ExternalReference::Create(IsolateAddressId::kCFunctionAddress, isolate());
1020
1021  DCHECK(!AreAliased(scratch, ebp, esi, edx));
1022  mov(ExternalReferenceAsOperand(c_entry_fp_address, scratch), ebp);
1023  mov(ExternalReferenceAsOperand(context_address, scratch), esi);
1024  mov(ExternalReferenceAsOperand(c_function_address, scratch), edx);
1025}
1026
1027void MacroAssembler::EnterExitFrameEpilogue(int argc, bool save_doubles) {
1028  ASM_CODE_COMMENT(this);
1029  // Optionally save all XMM registers.
1030  if (save_doubles) {
1031    int space =
1032        XMMRegister::kNumRegisters * kDoubleSize + argc * kSystemPointerSize;
1033    AllocateStackSpace(space);
1034    const int offset = -ExitFrameConstants::kFixedFrameSizeFromFp;
1035    for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
1036      XMMRegister reg = XMMRegister::from_code(i);
1037      movsd(Operand(ebp, offset - ((i + 1) * kDoubleSize)), reg);
1038    }
1039  } else {
1040    AllocateStackSpace(argc * kSystemPointerSize);
1041  }
1042
1043  // Get the required frame alignment for the OS.
1044  const int kFrameAlignment = base::OS::ActivationFrameAlignment();
1045  if (kFrameAlignment > 0) {
1046    DCHECK(base::bits::IsPowerOfTwo(kFrameAlignment));
1047    and_(esp, -kFrameAlignment);
1048  }
1049
1050  // Patch the saved entry sp.
1051  mov(Operand(ebp, ExitFrameConstants::kSPOffset), esp);
1052}
1053
1054void MacroAssembler::EnterExitFrame(int argc, bool save_doubles,
1055                                    StackFrame::Type frame_type) {
1056  ASM_CODE_COMMENT(this);
1057  EnterExitFramePrologue(frame_type, edi);
1058
1059  // Set up argc and argv in callee-saved registers.
1060  int offset = StandardFrameConstants::kCallerSPOffset - kSystemPointerSize;
1061  mov(edi, eax);
1062  lea(esi, Operand(ebp, eax, times_system_pointer_size, offset));
1063
1064  // Reserve space for argc, argv and isolate.
1065  EnterExitFrameEpilogue(argc, save_doubles);
1066}
1067
1068void MacroAssembler::EnterApiExitFrame(int argc, Register scratch) {
1069  EnterExitFramePrologue(StackFrame::EXIT, scratch);
1070  EnterExitFrameEpilogue(argc, false);
1071}
1072
1073void MacroAssembler::LeaveExitFrame(bool save_doubles, bool pop_arguments) {
1074  ASM_CODE_COMMENT(this);
1075  // Optionally restore all XMM registers.
1076  if (save_doubles) {
1077    const int offset = -ExitFrameConstants::kFixedFrameSizeFromFp;
1078    for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
1079      XMMRegister reg = XMMRegister::from_code(i);
1080      movsd(reg, Operand(ebp, offset - ((i + 1) * kDoubleSize)));
1081    }
1082  }
1083
1084  if (pop_arguments) {
1085    // Get the return address from the stack and restore the frame pointer.
1086    mov(ecx, Operand(ebp, 1 * kSystemPointerSize));
1087    mov(ebp, Operand(ebp, 0 * kSystemPointerSize));
1088
1089    // Pop the arguments and the receiver from the caller stack.
1090    lea(esp, Operand(esi, 1 * kSystemPointerSize));
1091
1092    // Push the return address to get ready to return.
1093    push(ecx);
1094  } else {
1095    // Otherwise just leave the exit frame.
1096    leave();
1097  }
1098
1099  LeaveExitFrameEpilogue();
1100}
1101
1102void MacroAssembler::LeaveExitFrameEpilogue() {
1103  ASM_CODE_COMMENT(this);
1104  // Clear the top frame.
1105  ExternalReference c_entry_fp_address =
1106      ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate());
1107  mov(ExternalReferenceAsOperand(c_entry_fp_address, esi), Immediate(0));
1108
1109  // Restore current context from top and clear it in debug mode.
1110  ExternalReference context_address =
1111      ExternalReference::Create(IsolateAddressId::kContextAddress, isolate());
1112  mov(esi, ExternalReferenceAsOperand(context_address, esi));
1113#ifdef DEBUG
1114  push(eax);
1115  mov(ExternalReferenceAsOperand(context_address, eax),
1116      Immediate(Context::kInvalidContext));
1117  pop(eax);
1118#endif
1119}
1120
1121void MacroAssembler::LeaveApiExitFrame() {
1122  ASM_CODE_COMMENT(this);
1123  mov(esp, ebp);
1124  pop(ebp);
1125
1126  LeaveExitFrameEpilogue();
1127}
1128
1129void MacroAssembler::PushStackHandler(Register scratch) {
1130  ASM_CODE_COMMENT(this);
1131  // Adjust this code if not the case.
1132  STATIC_ASSERT(StackHandlerConstants::kSize == 2 * kSystemPointerSize);
1133  STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
1134
1135  push(Immediate(0));  // Padding.
1136
1137  // Link the current handler as the next handler.
1138  ExternalReference handler_address =
1139      ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate());
1140  push(ExternalReferenceAsOperand(handler_address, scratch));
1141
1142  // Set this new handler as the current one.
1143  mov(ExternalReferenceAsOperand(handler_address, scratch), esp);
1144}
1145
1146void MacroAssembler::PopStackHandler(Register scratch) {
1147  ASM_CODE_COMMENT(this);
1148  STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
1149  ExternalReference handler_address =
1150      ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate());
1151  pop(ExternalReferenceAsOperand(handler_address, scratch));
1152  add(esp, Immediate(StackHandlerConstants::kSize - kSystemPointerSize));
1153}
1154
1155void MacroAssembler::CallRuntime(const Runtime::Function* f, int num_arguments,
1156                                 SaveFPRegsMode save_doubles) {
1157  ASM_CODE_COMMENT(this);
1158  // If the expected number of arguments of the runtime function is
1159  // constant, we check that the actual number of arguments match the
1160  // expectation.
1161  CHECK(f->nargs < 0 || f->nargs == num_arguments);
1162
1163  // TODO(1236192): Most runtime routines don't need the number of
1164  // arguments passed in because it is constant. At some point we
1165  // should remove this need and make the runtime routine entry code
1166  // smarter.
1167  Move(kRuntimeCallArgCountRegister, Immediate(num_arguments));
1168  Move(kRuntimeCallFunctionRegister, Immediate(ExternalReference::Create(f)));
1169  Handle<Code> code =
1170      CodeFactory::CEntry(isolate(), f->result_size, save_doubles);
1171  Call(code, RelocInfo::CODE_TARGET);
1172}
1173
1174void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid) {
1175  // ----------- S t a t e -------------
1176  //  -- esp[0]                 : return address
1177  //  -- esp[8]                 : argument num_arguments - 1
1178  //  ...
1179  //  -- esp[8 * num_arguments] : argument 0 (receiver)
1180  //
1181  //  For runtime functions with variable arguments:
1182  //  -- eax                    : number of  arguments
1183  // -----------------------------------
1184  ASM_CODE_COMMENT(this);
1185  const Runtime::Function* function = Runtime::FunctionForId(fid);
1186  DCHECK_EQ(1, function->result_size);
1187  if (function->nargs >= 0) {
1188    // TODO(1236192): Most runtime routines don't need the number of
1189    // arguments passed in because it is constant. At some point we
1190    // should remove this need and make the runtime routine entry code
1191    // smarter.
1192    Move(kRuntimeCallArgCountRegister, Immediate(function->nargs));
1193  }
1194  JumpToExternalReference(ExternalReference::Create(fid));
1195}
1196
1197void MacroAssembler::JumpToExternalReference(const ExternalReference& ext,
1198                                             bool builtin_exit_frame) {
1199  ASM_CODE_COMMENT(this);
1200  // Set the entry point and jump to the C entry runtime stub.
1201  Move(kRuntimeCallFunctionRegister, Immediate(ext));
1202  Handle<Code> code = CodeFactory::CEntry(isolate(), 1, SaveFPRegsMode::kIgnore,
1203                                          ArgvMode::kStack, builtin_exit_frame);
1204  Jump(code, RelocInfo::CODE_TARGET);
1205}
1206
1207void MacroAssembler::JumpToOffHeapInstructionStream(Address entry) {
1208  jmp(entry, RelocInfo::OFF_HEAP_TARGET);
1209}
1210
1211void MacroAssembler::CompareStackLimit(Register with, StackLimitKind kind) {
1212  ASM_CODE_COMMENT(this);
1213  DCHECK(root_array_available());
1214  Isolate* isolate = this->isolate();
1215  // Address through the root register. No load is needed.
1216  ExternalReference limit =
1217      kind == StackLimitKind::kRealStackLimit
1218          ? ExternalReference::address_of_real_jslimit(isolate)
1219          : ExternalReference::address_of_jslimit(isolate);
1220  DCHECK(TurboAssembler::IsAddressableThroughRootRegister(isolate, limit));
1221
1222  intptr_t offset =
1223      TurboAssembler::RootRegisterOffsetForExternalReference(isolate, limit);
1224  cmp(with, Operand(kRootRegister, offset));
1225}
1226
1227void MacroAssembler::StackOverflowCheck(Register num_args, Register scratch,
1228                                        Label* stack_overflow,
1229                                        bool include_receiver) {
1230  ASM_CODE_COMMENT(this);
1231  DCHECK_NE(num_args, scratch);
1232  // Check the stack for overflow. We are not trying to catch
1233  // interruptions (e.g. debug break and preemption) here, so the "real stack
1234  // limit" is checked.
1235  ExternalReference real_stack_limit =
1236      ExternalReference::address_of_real_jslimit(isolate());
1237  // Compute the space that is left as a negative number in scratch. If
1238  // we already overflowed, this will be a positive number.
1239  mov(scratch, ExternalReferenceAsOperand(real_stack_limit, scratch));
1240  sub(scratch, esp);
1241  // TODO(victorgomes): Remove {include_receiver} and always require one extra
1242  // word of the stack space.
1243  lea(scratch, Operand(scratch, num_args, times_system_pointer_size, 0));
1244  if (include_receiver) {
1245    add(scratch, Immediate(kSystemPointerSize));
1246  }
1247  // See if we overflowed, i.e. scratch is positive.
1248  cmp(scratch, Immediate(0));
1249  // TODO(victorgomes):  Save some bytes in the builtins that use stack checks
1250  // by jumping to a builtin that throws the exception.
1251  j(greater, stack_overflow);  // Signed comparison.
1252}
1253
1254void MacroAssembler::InvokePrologue(Register expected_parameter_count,
1255                                    Register actual_parameter_count,
1256                                    Label* done, InvokeType type) {
1257  if (expected_parameter_count == actual_parameter_count) return;
1258  ASM_CODE_COMMENT(this);
1259  DCHECK_EQ(actual_parameter_count, eax);
1260  DCHECK_EQ(expected_parameter_count, ecx);
1261  Label regular_invoke;
1262
1263  // If the expected parameter count is equal to the adaptor sentinel, no need
1264  // to push undefined value as arguments.
1265  if (kDontAdaptArgumentsSentinel != 0) {
1266    cmp(expected_parameter_count, Immediate(kDontAdaptArgumentsSentinel));
1267    j(equal, &regular_invoke, Label::kFar);
1268  }
1269
1270  // If overapplication or if the actual argument count is equal to the
1271  // formal parameter count, no need to push extra undefined values.
1272  sub(expected_parameter_count, actual_parameter_count);
1273  j(less_equal, &regular_invoke, Label::kFar);
1274
1275  // We need to preserve edx, edi, esi and ebx.
1276  movd(xmm0, edx);
1277  movd(xmm1, edi);
1278  movd(xmm2, esi);
1279  movd(xmm3, ebx);
1280
1281  Label stack_overflow;
1282  StackOverflowCheck(expected_parameter_count, edx, &stack_overflow);
1283
1284  Register scratch = esi;
1285
1286  // Underapplication. Move the arguments already in the stack, including the
1287  // receiver and the return address.
1288  {
1289    Label copy, check;
1290    Register src = edx, dest = esp, num = edi, current = ebx;
1291    mov(src, esp);
1292    lea(scratch,
1293        Operand(expected_parameter_count, times_system_pointer_size, 0));
1294    AllocateStackSpace(scratch);
1295    // Extra words are the receiver (if not already included in argc) and the
1296    // return address (if a jump).
1297    int extra_words = type == InvokeType::kCall ? 0 : 1;
1298    lea(num, Operand(eax, extra_words));  // Number of words to copy.
1299    Move(current, 0);
1300    // Fall-through to the loop body because there are non-zero words to copy.
1301    bind(&copy);
1302    mov(scratch, Operand(src, current, times_system_pointer_size, 0));
1303    mov(Operand(dest, current, times_system_pointer_size, 0), scratch);
1304    inc(current);
1305    bind(&check);
1306    cmp(current, num);
1307    j(less, &copy);
1308    lea(edx, Operand(esp, num, times_system_pointer_size, 0));
1309  }
1310
1311    // Fill remaining expected arguments with undefined values.
1312    movd(ebx, xmm3);  // Restore root.
1313    LoadRoot(scratch, RootIndex::kUndefinedValue);
1314    {
1315      Label loop;
1316      bind(&loop);
1317      dec(expected_parameter_count);
1318      mov(Operand(edx, expected_parameter_count, times_system_pointer_size, 0),
1319          scratch);
1320      j(greater, &loop, Label::kNear);
1321    }
1322
1323    // Restore remaining registers.
1324    movd(esi, xmm2);
1325    movd(edi, xmm1);
1326    movd(edx, xmm0);
1327
1328    jmp(&regular_invoke);
1329
1330    bind(&stack_overflow);
1331    {
1332      FrameScope frame(
1333          this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL);
1334      CallRuntime(Runtime::kThrowStackOverflow);
1335      int3();  // This should be unreachable.
1336    }
1337
1338    bind(&regular_invoke);
1339}
1340
1341void MacroAssembler::CallDebugOnFunctionCall(Register fun, Register new_target,
1342                                             Register expected_parameter_count,
1343                                             Register actual_parameter_count) {
1344  ASM_CODE_COMMENT(this);
1345  FrameScope frame(
1346      this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL);
1347  SmiTag(expected_parameter_count);
1348  Push(expected_parameter_count);
1349
1350  SmiTag(actual_parameter_count);
1351  Push(actual_parameter_count);
1352  SmiUntag(actual_parameter_count);
1353
1354  if (new_target.is_valid()) {
1355    Push(new_target);
1356  }
1357  Push(fun);
1358  Push(fun);
1359  // Arguments are located 2 words below the base pointer.
1360  Operand receiver_op = Operand(ebp, kSystemPointerSize * 2);
1361  Push(receiver_op);
1362  CallRuntime(Runtime::kDebugOnFunctionCall);
1363  Pop(fun);
1364  if (new_target.is_valid()) {
1365    Pop(new_target);
1366  }
1367  Pop(actual_parameter_count);
1368  SmiUntag(actual_parameter_count);
1369
1370  Pop(expected_parameter_count);
1371  SmiUntag(expected_parameter_count);
1372}
1373
1374void MacroAssembler::InvokeFunctionCode(Register function, Register new_target,
1375                                        Register expected_parameter_count,
1376                                        Register actual_parameter_count,
1377                                        InvokeType type) {
1378  ASM_CODE_COMMENT(this);
1379  // You can't call a function without a valid frame.
1380  DCHECK_IMPLIES(type == InvokeType::kCall, has_frame());
1381  DCHECK_EQ(function, edi);
1382  DCHECK_IMPLIES(new_target.is_valid(), new_target == edx);
1383  DCHECK(expected_parameter_count == ecx || expected_parameter_count == eax);
1384  DCHECK_EQ(actual_parameter_count, eax);
1385
1386  // On function call, call into the debugger if necessary.
1387  Label debug_hook, continue_after_hook;
1388  {
1389    ExternalReference debug_hook_active =
1390        ExternalReference::debug_hook_on_function_call_address(isolate());
1391    push(eax);
1392    cmpb(ExternalReferenceAsOperand(debug_hook_active, eax), Immediate(0));
1393    pop(eax);
1394    j(not_equal, &debug_hook);
1395  }
1396  bind(&continue_after_hook);
1397
1398  // Clear the new.target register if not given.
1399  if (!new_target.is_valid()) {
1400    Move(edx, isolate()->factory()->undefined_value());
1401  }
1402
1403  Label done;
1404  InvokePrologue(expected_parameter_count, actual_parameter_count, &done, type);
1405  // We call indirectly through the code field in the function to
1406  // allow recompilation to take effect without changing any of the
1407  // call sites.
1408  static_assert(kJavaScriptCallCodeStartRegister == ecx, "ABI mismatch");
1409  mov(ecx, FieldOperand(function, JSFunction::kCodeOffset));
1410  switch (type) {
1411    case InvokeType::kCall:
1412      CallCodeObject(ecx);
1413      break;
1414    case InvokeType::kJump:
1415      JumpCodeObject(ecx);
1416      break;
1417  }
1418  jmp(&done, Label::kNear);
1419
1420  // Deferred debug hook.
1421  bind(&debug_hook);
1422  CallDebugOnFunctionCall(function, new_target, expected_parameter_count,
1423                          actual_parameter_count);
1424  jmp(&continue_after_hook);
1425
1426  bind(&done);
1427}
1428
1429void MacroAssembler::InvokeFunction(Register fun, Register new_target,
1430                                    Register actual_parameter_count,
1431                                    InvokeType type) {
1432  ASM_CODE_COMMENT(this);
1433  // You can't call a function without a valid frame.
1434  DCHECK(type == InvokeType::kJump || has_frame());
1435
1436  DCHECK(fun == edi);
1437  mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
1438  mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
1439  movzx_w(ecx,
1440          FieldOperand(ecx, SharedFunctionInfo::kFormalParameterCountOffset));
1441
1442  InvokeFunctionCode(edi, new_target, ecx, actual_parameter_count, type);
1443}
1444
1445void MacroAssembler::LoadGlobalProxy(Register dst) {
1446  LoadNativeContextSlot(dst, Context::GLOBAL_PROXY_INDEX);
1447}
1448
1449void MacroAssembler::LoadNativeContextSlot(Register destination, int index) {
1450  ASM_CODE_COMMENT(this);
1451  // Load the native context from the current context.
1452  LoadMap(destination, esi);
1453  mov(destination,
1454      FieldOperand(destination,
1455                   Map::kConstructorOrBackPointerOrNativeContextOffset));
1456  // Load the function from the native context.
1457  mov(destination, Operand(destination, Context::SlotOffset(index)));
1458}
1459
1460void TurboAssembler::Ret() { ret(0); }
1461
1462void TurboAssembler::Ret(int bytes_dropped, Register scratch) {
1463  if (is_uint16(bytes_dropped)) {
1464    ret(bytes_dropped);
1465  } else {
1466    pop(scratch);
1467    add(esp, Immediate(bytes_dropped));
1468    push(scratch);
1469    ret(0);
1470  }
1471}
1472
1473void TurboAssembler::Push(Immediate value) {
1474  if (root_array_available() && options().isolate_independent_code) {
1475    if (value.is_embedded_object()) {
1476      Push(HeapObjectAsOperand(value.embedded_object()));
1477      return;
1478    } else if (value.is_external_reference()) {
1479      Push(ExternalReferenceAddressAsOperand(value.external_reference()));
1480      return;
1481    }
1482  }
1483  push(value);
1484}
1485
1486void MacroAssembler::Drop(int stack_elements) {
1487  if (stack_elements > 0) {
1488    add(esp, Immediate(stack_elements * kSystemPointerSize));
1489  }
1490}
1491
1492void TurboAssembler::Move(Register dst, Register src) {
1493  if (dst != src) {
1494    mov(dst, src);
1495  }
1496}
1497
1498void TurboAssembler::Move(Register dst, const Immediate& src) {
1499  if (!src.is_heap_object_request() && src.is_zero()) {
1500    xor_(dst, dst);  // Shorter than mov of 32-bit immediate 0.
1501  } else if (src.is_external_reference()) {
1502    LoadAddress(dst, src.external_reference());
1503  } else {
1504    mov(dst, src);
1505  }
1506}
1507
1508void TurboAssembler::Move(Operand dst, const Immediate& src) {
1509  // Since there's no scratch register available, take a detour through the
1510  // stack.
1511  if (root_array_available() && options().isolate_independent_code) {
1512    if (src.is_embedded_object() || src.is_external_reference() ||
1513        src.is_heap_object_request()) {
1514      Push(src);
1515      pop(dst);
1516      return;
1517    }
1518  }
1519
1520  if (src.is_embedded_object()) {
1521    mov(dst, src.embedded_object());
1522  } else {
1523    mov(dst, src);
1524  }
1525}
1526
1527void TurboAssembler::Move(Register dst, Operand src) { mov(dst, src); }
1528
1529void TurboAssembler::Move(Register dst, Handle<HeapObject> src) {
1530  if (root_array_available() && options().isolate_independent_code) {
1531    IndirectLoadConstant(dst, src);
1532    return;
1533  }
1534  mov(dst, src);
1535}
1536
1537void TurboAssembler::Move(XMMRegister dst, uint32_t src) {
1538  if (src == 0) {
1539    pxor(dst, dst);
1540  } else {
1541    unsigned cnt = base::bits::CountPopulation(src);
1542    unsigned nlz = base::bits::CountLeadingZeros32(src);
1543    unsigned ntz = base::bits::CountTrailingZeros32(src);
1544    if (nlz + cnt + ntz == 32) {
1545      pcmpeqd(dst, dst);
1546      if (ntz == 0) {
1547        psrld(dst, 32 - cnt);
1548      } else {
1549        pslld(dst, 32 - cnt);
1550        if (nlz != 0) psrld(dst, nlz);
1551      }
1552    } else {
1553      push(eax);
1554      mov(eax, Immediate(src));
1555      movd(dst, Operand(eax));
1556      pop(eax);
1557    }
1558  }
1559}
1560
1561void TurboAssembler::Move(XMMRegister dst, uint64_t src) {
1562  if (src == 0) {
1563    pxor(dst, dst);
1564  } else {
1565    uint32_t lower = static_cast<uint32_t>(src);
1566    uint32_t upper = static_cast<uint32_t>(src >> 32);
1567    unsigned cnt = base::bits::CountPopulation(src);
1568    unsigned nlz = base::bits::CountLeadingZeros64(src);
1569    unsigned ntz = base::bits::CountTrailingZeros64(src);
1570    if (nlz + cnt + ntz == 64) {
1571      pcmpeqd(dst, dst);
1572      if (ntz == 0) {
1573        psrlq(dst, 64 - cnt);
1574      } else {
1575        psllq(dst, 64 - cnt);
1576        if (nlz != 0) psrlq(dst, nlz);
1577      }
1578    } else if (lower == 0) {
1579      Move(dst, upper);
1580      psllq(dst, 32);
1581    } else if (CpuFeatures::IsSupported(SSE4_1)) {
1582      CpuFeatureScope scope(this, SSE4_1);
1583      push(eax);
1584      Move(eax, Immediate(lower));
1585      movd(dst, Operand(eax));
1586      if (upper != lower) {
1587        Move(eax, Immediate(upper));
1588      }
1589      pinsrd(dst, Operand(eax), 1);
1590      pop(eax);
1591    } else {
1592      push(Immediate(upper));
1593      push(Immediate(lower));
1594      movsd(dst, Operand(esp, 0));
1595      add(esp, Immediate(kDoubleSize));
1596    }
1597  }
1598}
1599
1600void TurboAssembler::PextrdPreSse41(Register dst, XMMRegister src,
1601                                    uint8_t imm8) {
1602  if (imm8 == 0) {
1603    Movd(dst, src);
1604    return;
1605  }
1606  // Without AVX or SSE, we can only have 64-bit values in xmm registers.
1607  // We don't have an xmm scratch register, so move the data via the stack. This
1608  // path is rarely required, so it's acceptable to be slow.
1609  DCHECK_LT(imm8, 2);
1610  AllocateStackSpace(kDoubleSize);
1611  movsd(Operand(esp, 0), src);
1612  mov(dst, Operand(esp, imm8 * kUInt32Size));
1613  add(esp, Immediate(kDoubleSize));
1614}
1615
1616void TurboAssembler::PinsrdPreSse41(XMMRegister dst, Operand src, uint8_t imm8,
1617                                    uint32_t* load_pc_offset) {
1618  // Without AVX or SSE, we can only have 64-bit values in xmm registers.
1619  // We don't have an xmm scratch register, so move the data via the stack. This
1620  // path is rarely required, so it's acceptable to be slow.
1621  DCHECK_LT(imm8, 2);
1622  AllocateStackSpace(kDoubleSize);
1623  // Write original content of {dst} to the stack.
1624  movsd(Operand(esp, 0), dst);
1625  // Overwrite the portion specified in {imm8}.
1626  if (src.is_reg_only()) {
1627    mov(Operand(esp, imm8 * kUInt32Size), src.reg());
1628  } else {
1629    movss(dst, src);
1630    movss(Operand(esp, imm8 * kUInt32Size), dst);
1631  }
1632  // Load back the full value into {dst}.
1633  movsd(dst, Operand(esp, 0));
1634  add(esp, Immediate(kDoubleSize));
1635}
1636
1637void TurboAssembler::Lzcnt(Register dst, Operand src) {
1638  if (CpuFeatures::IsSupported(LZCNT)) {
1639    CpuFeatureScope scope(this, LZCNT);
1640    lzcnt(dst, src);
1641    return;
1642  }
1643  Label not_zero_src;
1644  bsr(dst, src);
1645  j(not_zero, &not_zero_src, Label::kNear);
1646  mov(dst, 63);  // 63^31 == 32
1647  bind(&not_zero_src);
1648  xor_(dst, Immediate(31));  // for x in [0..31], 31^x == 31-x.
1649}
1650
1651void TurboAssembler::Tzcnt(Register dst, Operand src) {
1652  if (CpuFeatures::IsSupported(BMI1)) {
1653    CpuFeatureScope scope(this, BMI1);
1654    tzcnt(dst, src);
1655    return;
1656  }
1657  Label not_zero_src;
1658  bsf(dst, src);
1659  j(not_zero, &not_zero_src, Label::kNear);
1660  mov(dst, 32);  // The result of tzcnt is 32 if src = 0.
1661  bind(&not_zero_src);
1662}
1663
1664void TurboAssembler::Popcnt(Register dst, Operand src) {
1665  if (CpuFeatures::IsSupported(POPCNT)) {
1666    CpuFeatureScope scope(this, POPCNT);
1667    popcnt(dst, src);
1668    return;
1669  }
1670  FATAL("no POPCNT support");
1671}
1672
1673void MacroAssembler::LoadWeakValue(Register in_out, Label* target_if_cleared) {
1674  ASM_CODE_COMMENT(this);
1675  cmp(in_out, Immediate(kClearedWeakHeapObjectLower32));
1676  j(equal, target_if_cleared);
1677
1678  and_(in_out, Immediate(~kWeakHeapObjectMask));
1679}
1680
1681void MacroAssembler::EmitIncrementCounter(StatsCounter* counter, int value,
1682                                          Register scratch) {
1683  DCHECK_GT(value, 0);
1684  if (FLAG_native_code_counters && counter->Enabled()) {
1685    ASM_CODE_COMMENT(this);
1686    Operand operand =
1687        ExternalReferenceAsOperand(ExternalReference::Create(counter), scratch);
1688    if (value == 1) {
1689      inc(operand);
1690    } else {
1691      add(operand, Immediate(value));
1692    }
1693  }
1694}
1695
1696void MacroAssembler::EmitDecrementCounter(StatsCounter* counter, int value,
1697                                          Register scratch) {
1698  DCHECK_GT(value, 0);
1699  if (FLAG_native_code_counters && counter->Enabled()) {
1700    ASM_CODE_COMMENT(this);
1701    Operand operand =
1702        ExternalReferenceAsOperand(ExternalReference::Create(counter), scratch);
1703    if (value == 1) {
1704      dec(operand);
1705    } else {
1706      sub(operand, Immediate(value));
1707    }
1708  }
1709}
1710
1711void TurboAssembler::Assert(Condition cc, AbortReason reason) {
1712  if (FLAG_debug_code) Check(cc, reason);
1713}
1714
1715void TurboAssembler::AssertUnreachable(AbortReason reason) {
1716  if (FLAG_debug_code) Abort(reason);
1717}
1718
1719void TurboAssembler::Check(Condition cc, AbortReason reason) {
1720  Label L;
1721  j(cc, &L);
1722  Abort(reason);
1723  // will not return here
1724  bind(&L);
1725}
1726
1727void TurboAssembler::CheckStackAlignment() {
1728  ASM_CODE_COMMENT(this);
1729  int frame_alignment = base::OS::ActivationFrameAlignment();
1730  int frame_alignment_mask = frame_alignment - 1;
1731  if (frame_alignment > kSystemPointerSize) {
1732    DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
1733    Label alignment_as_expected;
1734    test(esp, Immediate(frame_alignment_mask));
1735    j(zero, &alignment_as_expected);
1736    // Abort if stack is not aligned.
1737    int3();
1738    bind(&alignment_as_expected);
1739  }
1740}
1741
1742void TurboAssembler::Abort(AbortReason reason) {
1743  if (FLAG_code_comments) {
1744    const char* msg = GetAbortReason(reason);
1745    RecordComment("Abort message: ");
1746    RecordComment(msg);
1747  }
1748
1749  // Avoid emitting call to builtin if requested.
1750  if (trap_on_abort()) {
1751    int3();
1752    return;
1753  }
1754
1755  if (should_abort_hard()) {
1756    // We don't care if we constructed a frame. Just pretend we did.
1757    FrameScope assume_frame(this, StackFrame::NO_FRAME_TYPE);
1758    PrepareCallCFunction(1, eax);
1759    mov(Operand(esp, 0), Immediate(static_cast<int>(reason)));
1760    CallCFunction(ExternalReference::abort_with_reason(), 1);
1761    return;
1762  }
1763
1764  Move(edx, Smi::FromInt(static_cast<int>(reason)));
1765
1766  // Disable stub call restrictions to always allow calls to abort.
1767  if (!has_frame()) {
1768    // We don't actually want to generate a pile of code for this, so just
1769    // claim there is a stack frame, without generating one.
1770    FrameScope scope(this, StackFrame::NO_FRAME_TYPE);
1771    Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET);
1772  } else {
1773    Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET);
1774  }
1775  // will not return here
1776  int3();
1777}
1778
1779void TurboAssembler::PrepareCallCFunction(int num_arguments, Register scratch) {
1780  ASM_CODE_COMMENT(this);
1781  int frame_alignment = base::OS::ActivationFrameAlignment();
1782  if (frame_alignment != 0) {
1783    // Make stack end at alignment and make room for num_arguments words
1784    // and the original value of esp.
1785    mov(scratch, esp);
1786    AllocateStackSpace((num_arguments + 1) * kSystemPointerSize);
1787    DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
1788    and_(esp, -frame_alignment);
1789    mov(Operand(esp, num_arguments * kSystemPointerSize), scratch);
1790  } else {
1791    AllocateStackSpace(num_arguments * kSystemPointerSize);
1792  }
1793}
1794
1795void TurboAssembler::CallCFunction(ExternalReference function,
1796                                   int num_arguments) {
1797  // Trashing eax is ok as it will be the return value.
1798  Move(eax, Immediate(function));
1799  CallCFunction(eax, num_arguments);
1800}
1801
1802void TurboAssembler::CallCFunction(Register function, int num_arguments) {
1803  ASM_CODE_COMMENT(this);
1804  DCHECK_LE(num_arguments, kMaxCParameters);
1805  DCHECK(has_frame());
1806  // Check stack alignment.
1807  if (FLAG_debug_code) {
1808    CheckStackAlignment();
1809  }
1810
1811  // Save the frame pointer and PC so that the stack layout remains iterable,
1812  // even without an ExitFrame which normally exists between JS and C frames.
1813  // Find two caller-saved scratch registers.
1814  Register pc_scratch = eax;
1815  Register scratch = ecx;
1816  if (function == eax) pc_scratch = edx;
1817  if (function == ecx) scratch = edx;
1818  PushPC();
1819  pop(pc_scratch);
1820
1821  // See x64 code for reasoning about how to address the isolate data fields.
1822  DCHECK_IMPLIES(!root_array_available(), isolate() != nullptr);
1823  mov(root_array_available()
1824          ? Operand(kRootRegister, IsolateData::fast_c_call_caller_pc_offset())
1825          : ExternalReferenceAsOperand(
1826                ExternalReference::fast_c_call_caller_pc_address(isolate()),
1827                scratch),
1828      pc_scratch);
1829  mov(root_array_available()
1830          ? Operand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset())
1831          : ExternalReferenceAsOperand(
1832                ExternalReference::fast_c_call_caller_fp_address(isolate()),
1833                scratch),
1834      ebp);
1835
1836  call(function);
1837
1838  // We don't unset the PC; the FP is the source of truth.
1839  mov(root_array_available()
1840          ? Operand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset())
1841          : ExternalReferenceAsOperand(
1842                ExternalReference::fast_c_call_caller_fp_address(isolate()),
1843                scratch),
1844      Immediate(0));
1845
1846  if (base::OS::ActivationFrameAlignment() != 0) {
1847    mov(esp, Operand(esp, num_arguments * kSystemPointerSize));
1848  } else {
1849    add(esp, Immediate(num_arguments * kSystemPointerSize));
1850  }
1851}
1852
1853void TurboAssembler::PushPC() {
1854  // Push the current PC onto the stack as "return address" via calling
1855  // the next instruction.
1856  Label get_pc;
1857  call(&get_pc);
1858  bind(&get_pc);
1859}
1860
1861void TurboAssembler::Call(Handle<Code> code_object, RelocInfo::Mode rmode) {
1862  ASM_CODE_COMMENT(this);
1863  DCHECK_IMPLIES(options().isolate_independent_code,
1864                 Builtins::IsIsolateIndependentBuiltin(*code_object));
1865  if (options().inline_offheap_trampolines) {
1866    Builtin builtin = Builtin::kNoBuiltinId;
1867    if (isolate()->builtins()->IsBuiltinHandle(code_object, &builtin)) {
1868      // Inline the trampoline.
1869      CallBuiltin(builtin);
1870      return;
1871    }
1872  }
1873  DCHECK(RelocInfo::IsCodeTarget(rmode));
1874  call(code_object, rmode);
1875}
1876
1877void TurboAssembler::LoadEntryFromBuiltinIndex(Register builtin_index) {
1878  ASM_CODE_COMMENT(this);
1879  STATIC_ASSERT(kSystemPointerSize == 4);
1880  STATIC_ASSERT(kSmiShiftSize == 0);
1881  STATIC_ASSERT(kSmiTagSize == 1);
1882  STATIC_ASSERT(kSmiTag == 0);
1883
1884  // The builtin_index register contains the builtin index as a Smi.
1885  // Untagging is folded into the indexing operand below (we use
1886  // times_half_system_pointer_size instead of times_system_pointer_size since
1887  // smis are already shifted by one).
1888  mov(builtin_index,
1889      Operand(kRootRegister, builtin_index, times_half_system_pointer_size,
1890              IsolateData::builtin_entry_table_offset()));
1891}
1892
1893void TurboAssembler::CallBuiltinByIndex(Register builtin_index) {
1894  ASM_CODE_COMMENT(this);
1895  LoadEntryFromBuiltinIndex(builtin_index);
1896  call(builtin_index);
1897}
1898
1899void TurboAssembler::CallBuiltin(Builtin builtin) {
1900  ASM_CODE_COMMENT_STRING(this, CommentForOffHeapTrampoline("call", builtin));
1901  DCHECK(Builtins::IsBuiltinId(builtin));
1902  call(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET);
1903}
1904
1905Operand TurboAssembler::EntryFromBuiltinAsOperand(Builtin builtin) {
1906  ASM_CODE_COMMENT(this);
1907  return Operand(kRootRegister, IsolateData::BuiltinEntrySlotOffset(builtin));
1908}
1909
1910void TurboAssembler::LoadCodeObjectEntry(Register destination,
1911                                         Register code_object) {
1912  ASM_CODE_COMMENT(this);
1913  // Code objects are called differently depending on whether we are generating
1914  // builtin code (which will later be embedded into the binary) or compiling
1915  // user JS code at runtime.
1916  // * Builtin code runs in --jitless mode and thus must not call into on-heap
1917  //   Code targets. Instead, we dispatch through the builtins entry table.
1918  // * Codegen at runtime does not have this restriction and we can use the
1919  //   shorter, branchless instruction sequence. The assumption here is that
1920  //   targets are usually generated code and not builtin Code objects.
1921
1922  if (options().isolate_independent_code) {
1923    DCHECK(root_array_available());
1924    Label if_code_is_off_heap, out;
1925
1926    // Check whether the Code object is an off-heap trampoline. If so, call its
1927    // (off-heap) entry point directly without going through the (on-heap)
1928    // trampoline.  Otherwise, just call the Code object as always.
1929    test(FieldOperand(code_object, Code::kFlagsOffset),
1930         Immediate(Code::IsOffHeapTrampoline::kMask));
1931    j(not_equal, &if_code_is_off_heap);
1932
1933    // Not an off-heap trampoline, the entry point is at
1934    // Code::raw_instruction_start().
1935    Move(destination, code_object);
1936    add(destination, Immediate(Code::kHeaderSize - kHeapObjectTag));
1937    jmp(&out);
1938
1939    // An off-heap trampoline, the entry point is loaded from the builtin entry
1940    // table.
1941    bind(&if_code_is_off_heap);
1942    mov(destination, FieldOperand(code_object, Code::kBuiltinIndexOffset));
1943    mov(destination,
1944        Operand(kRootRegister, destination, times_system_pointer_size,
1945                IsolateData::builtin_entry_table_offset()));
1946
1947    bind(&out);
1948  } else {
1949    Move(destination, code_object);
1950    add(destination, Immediate(Code::kHeaderSize - kHeapObjectTag));
1951  }
1952}
1953
1954void TurboAssembler::CallCodeObject(Register code_object) {
1955  ASM_CODE_COMMENT(this);
1956  LoadCodeObjectEntry(code_object, code_object);
1957  call(code_object);
1958}
1959
1960void TurboAssembler::JumpCodeObject(Register code_object, JumpMode jump_mode) {
1961  ASM_CODE_COMMENT(this);
1962  LoadCodeObjectEntry(code_object, code_object);
1963  switch (jump_mode) {
1964    case JumpMode::kJump:
1965      jmp(code_object);
1966      return;
1967    case JumpMode::kPushAndReturn:
1968      push(code_object);
1969      ret(0);
1970      return;
1971  }
1972}
1973
1974void TurboAssembler::Jump(const ExternalReference& reference) {
1975  DCHECK(root_array_available());
1976  jmp(Operand(kRootRegister, RootRegisterOffsetForExternalReferenceTableEntry(
1977                                 isolate(), reference)));
1978}
1979
1980void TurboAssembler::Jump(Handle<Code> code_object, RelocInfo::Mode rmode) {
1981  DCHECK_IMPLIES(options().isolate_independent_code,
1982                 Builtins::IsIsolateIndependentBuiltin(*code_object));
1983  if (options().inline_offheap_trampolines) {
1984    Builtin builtin = Builtin::kNoBuiltinId;
1985    if (isolate()->builtins()->IsBuiltinHandle(code_object, &builtin)) {
1986      // Inline the trampoline.
1987      RecordCommentForOffHeapTrampoline(builtin);
1988      jmp(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET);
1989      return;
1990    }
1991  }
1992  DCHECK(RelocInfo::IsCodeTarget(rmode));
1993  jmp(code_object, rmode);
1994}
1995
1996void TurboAssembler::CheckPageFlag(Register object, Register scratch, int mask,
1997                                   Condition cc, Label* condition_met,
1998                                   Label::Distance condition_met_distance) {
1999  ASM_CODE_COMMENT(this);
2000  DCHECK(cc == zero || cc == not_zero);
2001  if (scratch == object) {
2002    and_(scratch, Immediate(~kPageAlignmentMask));
2003  } else {
2004    mov(scratch, Immediate(~kPageAlignmentMask));
2005    and_(scratch, object);
2006  }
2007  if (mask < (1 << kBitsPerByte)) {
2008    test_b(Operand(scratch, BasicMemoryChunk::kFlagsOffset), Immediate(mask));
2009  } else {
2010    test(Operand(scratch, BasicMemoryChunk::kFlagsOffset), Immediate(mask));
2011  }
2012  j(cc, condition_met, condition_met_distance);
2013}
2014
2015void TurboAssembler::ComputeCodeStartAddress(Register dst) {
2016  ASM_CODE_COMMENT(this);
2017  // In order to get the address of the current instruction, we first need
2018  // to use a call and then use a pop, thus pushing the return address to
2019  // the stack and then popping it into the register.
2020  Label current;
2021  call(&current);
2022  int pc = pc_offset();
2023  bind(&current);
2024  pop(dst);
2025  if (pc != 0) {
2026    sub(dst, Immediate(pc));
2027  }
2028}
2029
2030void TurboAssembler::CallForDeoptimization(Builtin target, int, Label* exit,
2031                                           DeoptimizeKind kind, Label* ret,
2032                                           Label*) {
2033  ASM_CODE_COMMENT(this);
2034  CallBuiltin(target);
2035  DCHECK_EQ(SizeOfCodeGeneratedSince(exit),
2036            (kind == DeoptimizeKind::kLazy) ? Deoptimizer::kLazyDeoptExitSize
2037                                            : Deoptimizer::kEagerDeoptExitSize);
2038}
2039
2040void TurboAssembler::Trap() { int3(); }
2041void TurboAssembler::DebugBreak() { int3(); }
2042
2043}  // namespace internal
2044}  // namespace v8
2045
2046#endif  // V8_TARGET_ARCH_IA32
2047