Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
stub_code_compiler_x64.cc
Go to the documentation of this file.
1// Copyright (c) 2019, 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 <setjmp.h>
6
8#include "vm/globals.h"
9
10// For `AllocateObjectInstr::WillAllocateNewOrRemembered`
11// For `GenericCheckBoundInstr::UseUnboxedRepresentation`
13
14#define SHOULD_NOT_INCLUDE_RUNTIME
15
18
19#if defined(TARGET_ARCH_X64)
20
21#include "vm/class_id.h"
22#include "vm/code_entry_kind.h"
25#include "vm/constants.h"
27#include "vm/instructions.h"
29#include "vm/tags.h"
30
31#define __ assembler->
32
33namespace dart {
34namespace compiler {
35
36// Ensures that [RAX] is a new object, if not it will be added to the remembered
37// set via a leaf runtime call.
38//
39// WARNING: This might clobber all registers except for [RAX], [THR] and [FP].
40// The caller should simply call LeaveStubFrame() and return.
42 // If the object is not in an active TLAB, we call a leaf-runtime to add it to
43 // the remembered set and/or deferred marking worklist. This test assumes a
44 // Page's TLAB use is always ascending.
45 Label done;
46 __ AndImmediate(TMP, RAX, target::kPageMask);
47 __ LoadFromOffset(TMP, TMP, target::Page::original_top_offset());
48 __ CompareRegisters(RAX, TMP);
49 __ BranchIf(UNSIGNED_GREATER_EQUAL, &done);
50
51 {
52 LeafRuntimeScope rt(assembler, /*frame_size=*/0,
53 /*preserve_registers=*/false);
56 rt.Call(kEnsureRememberedAndMarkingDeferredRuntimeEntry, 2);
57 }
58
59 __ Bind(&done);
60}
61
62// In TSAN mode the runtime will throw an exception using an intermediary
63// longjmp() call to unwind the C frames in a way that TSAN can understand.
64//
65// This wrapper will setup a [jmp_buf] on the stack and initialize it to be a
66// target for a possible longjmp(). In the exceptional case we'll forward
67// control of execution to the usual JumpToFrame stub.
68//
69// In non-TSAN mode this will do nothing and the runtime will call the
70// JumpToFrame stub directly.
71//
72// The callback [fun] may be invoked with a modified [RSP] due to allocating
73// a [jmp_buf] allocating structure on the stack (as well as the saved old
74// [Thread::tsan_utils_->setjmp_buffer_]).
75static void WithExceptionCatchingTrampoline(Assembler* assembler,
76 std::function<void()> fun) {
77#if defined(TARGET_USES_THREAD_SANITIZER) && !defined(USING_SIMULATOR)
78 const Register kTsanUtilsReg = RAX;
79
80 // Reserve space for arguments and align frame before entering C++ world.
81 const intptr_t kJumpBufferSize = sizeof(jmp_buf);
82 // Save & Restore the volatile CPU registers across the setjmp() call.
83 const RegisterSet volatile_registers(
85 /*fpu_registers=*/0);
86
87 const Register kSavedRspReg = R12;
89 // We rely on THR being preserved across the setjmp() call.
91
92 Label do_native_call;
93
94 // Save old jmp_buf.
95 __ movq(kTsanUtilsReg, Address(THR, target::Thread::tsan_utils_offset()));
96 __ pushq(Address(kTsanUtilsReg, target::TsanUtils::setjmp_buffer_offset()));
97
98 // Allocate jmp_buf struct on stack & remember pointer to it on the
99 // [Thread::tsan_utils_->setjmp_buffer] (which exceptions.cc will longjmp()
100 // to)
101 __ AddImmediate(RSP, Immediate(-kJumpBufferSize));
102 __ movq(Address(kTsanUtilsReg, target::TsanUtils::setjmp_buffer_offset()),
103 RSP);
104
105 // Call setjmp() with a pointer to the allocated jmp_buf struct.
106 __ MoveRegister(CallingConventions::kArg1Reg, RSP);
107 __ PushRegisters(volatile_registers);
109 __ MoveRegister(kSavedRspReg, RSP);
110 __ andq(RSP, Immediate(~(OS::ActivationFrameAlignment() - 1)));
111 }
112 __ movq(kTsanUtilsReg, Address(THR, target::Thread::tsan_utils_offset()));
113 __ CallCFunction(
114 Address(kTsanUtilsReg, target::TsanUtils::setjmp_function_offset()),
115 /*restore_rsp=*/true);
117 __ MoveRegister(RSP, kSavedRspReg);
118 }
119 __ PopRegisters(volatile_registers);
120
121 // We are the target of a longjmp() iff setjmp() returns non-0.
122 __ CompareImmediate(RAX, 0);
123 __ BranchIf(EQUAL, &do_native_call);
124
125 // We are the target of a longjmp: Cleanup the stack and tail-call the
126 // JumpToFrame stub which will take care of unwinding the stack and hand
127 // execution to the catch entry.
128 __ AddImmediate(RSP, Immediate(kJumpBufferSize));
129 __ movq(kTsanUtilsReg, Address(THR, target::Thread::tsan_utils_offset()));
130 __ popq(Address(kTsanUtilsReg, target::TsanUtils::setjmp_buffer_offset()));
131
133 Address(kTsanUtilsReg, target::TsanUtils::exception_pc_offset()));
135 Address(kTsanUtilsReg, target::TsanUtils::exception_sp_offset()));
137 Address(kTsanUtilsReg, target::TsanUtils::exception_fp_offset()));
138 __ MoveRegister(CallingConventions::kArg4Reg, THR);
139 __ jmp(Address(THR, target::Thread::jump_to_frame_entry_point_offset()));
140
141 // We leave the created [jump_buf] structure on the stack as well as the
142 // pushed old [Thread::tsan_utils_->setjmp_buffer_].
143 __ Bind(&do_native_call);
144 __ MoveRegister(kSavedRspReg, RSP);
145#endif // defined(TARGET_USES_THREAD_SANITIZER) && !defined(USING_SIMULATOR)
146
147 fun();
148
149#if defined(TARGET_USES_THREAD_SANITIZER) && !defined(USING_SIMULATOR)
150 __ MoveRegister(RSP, kSavedRspReg);
151 __ AddImmediate(RSP, Immediate(kJumpBufferSize));
152 const Register kTsanUtilsReg2 = kSavedRspReg;
153 __ movq(kTsanUtilsReg2, Address(THR, target::Thread::tsan_utils_offset()));
154 __ popq(Address(kTsanUtilsReg2, target::TsanUtils::setjmp_buffer_offset()));
155#endif // defined(TARGET_USES_THREAD_SANITIZER) && !defined(USING_SIMULATOR)
156}
157
158// Input parameters:
159// RSP : points to return address.
160// RSP + 8 : address of last argument in argument array.
161// RSP + 8*R10 : address of first argument in argument array.
162// RSP + 8*R10 + 8 : address of return value.
163// RBX : address of the runtime function to call.
164// R10 : number of arguments to the call.
165// Must preserve callee saved registers R12 and R13.
166void StubCodeCompiler::GenerateCallToRuntimeStub() {
167 const intptr_t thread_offset = target::NativeArguments::thread_offset();
168 const intptr_t argc_tag_offset = target::NativeArguments::argc_tag_offset();
169 const intptr_t argv_offset = target::NativeArguments::argv_offset();
170 const intptr_t retval_offset = target::NativeArguments::retval_offset();
171
172 __ movq(CODE_REG,
173 Address(THR, target::Thread::call_to_runtime_stub_offset()));
174 __ EnterStubFrame();
175
176 // Save exit frame information to enable stack walking as we are about
177 // to transition to Dart VM C++ code.
178 __ movq(Address(THR, target::Thread::top_exit_frame_info_offset()), RBP);
179
180 // Mark that the thread exited generated code through a runtime call.
181 __ movq(Address(THR, target::Thread::exit_through_ffi_offset()),
182 Immediate(target::Thread::exit_through_runtime_call()));
183
184#if defined(DEBUG)
185 {
186 Label ok;
187 // Check that we are always entering from Dart code.
188 __ movq(RAX, Immediate(VMTag::kDartTagId));
191 __ Stop("Not coming from Dart code.");
192 __ Bind(&ok);
193 }
194#endif
195
196 // Mark that the thread is executing VM code.
198
199 WithExceptionCatchingTrampoline(assembler, [&]() {
200 // Reserve space for arguments and align frame before entering C++ world.
201 __ subq(RSP, Immediate(target::NativeArguments::StructSize()));
203 __ andq(RSP, Immediate(~(OS::ActivationFrameAlignment() - 1)));
204 }
205
206 // Pass target::NativeArguments structure by value and call runtime.
207 __ movq(Address(RSP, thread_offset), THR); // Set thread in NativeArgs.
208 __ movq(Address(RSP, argc_tag_offset),
209 R10); // Set argc in target::NativeArguments.
210 // Compute argv.
211 __ leaq(RAX, Address(RBP, R10, TIMES_8,
212 target::frame_layout.param_end_from_fp *
213 target::kWordSize));
214 __ movq(Address(RSP, argv_offset),
215 RAX); // Set argv in target::NativeArguments.
216 __ addq(
217 RAX,
218 Immediate(1 * target::kWordSize)); // Retval is next to 1st argument.
219 __ movq(Address(RSP, retval_offset),
220 RAX); // Set retval in target::NativeArguments.
221#if defined(DART_TARGET_OS_WINDOWS)
222 ASSERT(target::NativeArguments::StructSize() >
223 CallingConventions::kRegisterTransferLimit);
225#endif
226 __ CallCFunction(RBX);
227
228 // Mark that the thread is executing Dart code.
229 __ movq(Assembler::VMTagAddress(), Immediate(VMTag::kDartTagId));
230
231 // Mark that the thread has not exited generated Dart code.
232 __ movq(Address(THR, target::Thread::exit_through_ffi_offset()),
233 Immediate(0));
234
235 // Reset exit frame information in Isolate's mutator thread structure.
236 __ movq(Address(THR, target::Thread::top_exit_frame_info_offset()),
237 Immediate(0));
238
239 // Restore the global object pool after returning from runtime (old space is
240 // moving, so the GOP could have been relocated).
241 if (FLAG_precompiled_mode) {
242 __ movq(PP, Address(THR, target::Thread::global_object_pool_offset()));
243 }
244 });
245
246 __ LeaveStubFrame();
247
248 // The following return can jump to a lazy-deopt stub, which assumes RAX
249 // contains a return value and will save it in a GC-visible way. We therefore
250 // have to ensure RAX does not contain any garbage value left from the C
251 // function we called (which has return type "void").
252 // (See GenerateDeoptimizationSequence::saved_result_slot_from_fp.)
253 __ xorq(RAX, RAX);
254 __ ret();
255}
256
257void StubCodeCompiler::GenerateSharedStubGeneric(
258 bool save_fpu_registers,
259 intptr_t self_code_stub_offset_from_thread,
260 bool allow_return,
261 std::function<void()> perform_runtime_call) {
262 // We want the saved registers to appear like part of the caller's frame, so
263 // we push them before calling EnterStubFrame.
264 const RegisterSet saved_registers(
265 kDartAvailableCpuRegs, save_fpu_registers ? kAllFpuRegistersList : 0);
266 __ PushRegisters(saved_registers);
267
268 const intptr_t kSavedCpuRegisterSlots =
270 const intptr_t kSavedFpuRegisterSlots =
271 save_fpu_registers
272 ? kNumberOfFpuRegisters * kFpuRegisterSize / target::kWordSize
273 : 0;
274 const intptr_t kAllSavedRegistersSlots =
275 kSavedCpuRegisterSlots + kSavedFpuRegisterSlots;
276
277 // Copy down the return address so the stack layout is correct.
278 __ pushq(Address(RSP, kAllSavedRegistersSlots * target::kWordSize));
279 __ movq(CODE_REG, Address(THR, self_code_stub_offset_from_thread));
280 __ EnterStubFrame();
281 perform_runtime_call();
282 if (!allow_return) {
283 __ Breakpoint();
284 return;
285 }
286 __ LeaveStubFrame();
287 // Copy up the return address (in case it was changed).
288 __ popq(TMP);
289 __ movq(Address(RSP, kAllSavedRegistersSlots * target::kWordSize), TMP);
290 __ PopRegisters(saved_registers);
291 __ ret();
292}
293
294void StubCodeCompiler::GenerateSharedStub(
295 bool save_fpu_registers,
296 const RuntimeEntry* target,
297 intptr_t self_code_stub_offset_from_thread,
298 bool allow_return,
299 bool store_runtime_result_in_result_register) {
300 auto perform_runtime_call = [&]() {
301 if (store_runtime_result_in_result_register) {
302 __ PushImmediate(Immediate(0));
303 }
304 __ CallRuntime(*target, /*argument_count=*/0);
305 if (store_runtime_result_in_result_register) {
306 __ PopRegister(RAX);
307 __ movq(Address(RBP, target::kWordSize *
310 RAX);
311 }
312 };
313 GenerateSharedStubGeneric(save_fpu_registers,
314 self_code_stub_offset_from_thread, allow_return,
315 perform_runtime_call);
316}
317
318void StubCodeCompiler::GenerateEnterSafepointStub() {
319 RegisterSet all_registers;
320 all_registers.AddAllGeneralRegisters();
321 __ PushRegisters(all_registers);
322
323 __ EnterFrame(0);
324 __ ReserveAlignedFrameSpace(0);
325 __ movq(RAX, Address(THR, kEnterSafepointRuntimeEntry.OffsetFromThread()));
326 __ CallCFunction(RAX);
327 __ LeaveFrame();
328
329 __ PopRegisters(all_registers);
330 __ ret();
331}
332
333static void GenerateExitSafepointStubCommon(Assembler* assembler,
334 uword runtime_entry_offset) {
335 RegisterSet all_registers;
336 all_registers.AddAllGeneralRegisters();
337 __ PushRegisters(all_registers);
338
339 __ EnterFrame(0);
340 __ ReserveAlignedFrameSpace(0);
341
342 // Set the execution state to VM while waiting for the safepoint to end.
343 // This isn't strictly necessary but enables tests to check that we're not
344 // in native code anymore. See tests/ffi/function_gc_test.dart for example.
345 __ movq(Address(THR, target::Thread::execution_state_offset()),
346 Immediate(target::Thread::vm_execution_state()));
347
348 __ movq(RAX, Address(THR, runtime_entry_offset));
349 __ CallCFunction(RAX);
350 __ LeaveFrame();
351
352 __ PopRegisters(all_registers);
353 __ ret();
354}
355
356void StubCodeCompiler::GenerateExitSafepointStub() {
357 GenerateExitSafepointStubCommon(
358 assembler, kExitSafepointRuntimeEntry.OffsetFromThread());
359}
360
361void StubCodeCompiler::GenerateExitSafepointIgnoreUnwindInProgressStub() {
362 GenerateExitSafepointStubCommon(
363 assembler,
364 kExitSafepointIgnoreUnwindInProgressRuntimeEntry.OffsetFromThread());
365}
366
367// Calls native code within a safepoint.
368//
369// On entry:
370// Stack: arguments set up and aligned for native call, excl. shadow space
371// RBX = target address to call
372//
373// On exit:
374// Stack pointer lowered by shadow space
375// RBX, R12 clobbered
376void StubCodeCompiler::GenerateCallNativeThroughSafepointStub() {
377 __ movq(R12, compiler::Immediate(target::Thread::exit_through_ffi()));
378 __ TransitionGeneratedToNative(RBX, FPREG, R12,
379 /*enter_safepoint=*/true);
380
381 __ popq(R12);
382 __ CallCFunction(RBX, /*restore_rsp=*/true);
383
384 __ TransitionNativeToGenerated(/*leave_safepoint=*/true);
385
386 // Faster than jmp because it doesn't confuse the branch predictor.
387 __ pushq(R12);
388 __ ret();
389}
390
391void StubCodeCompiler::GenerateLoadBSSEntry(BSS::Relocation relocation,
392 Register dst,
393 Register tmp) {
394 compiler::Label skip_reloc;
395 __ jmp(&skip_reloc);
396 InsertBSSRelocation(relocation);
397 const intptr_t reloc_end = __ CodeSize();
398 __ Bind(&skip_reloc);
399
400 const intptr_t kLeaqLength = 7;
402 -kLeaqLength - compiler::target::kWordSize));
403 ASSERT((__ CodeSize() - reloc_end) == kLeaqLength);
404
405 // dst holds the address of the relocation.
406 __ movq(tmp, compiler::Address(dst, 0));
407
408 // tmp holds the relocation itself: dst - bss_start.
409 // dst = dst + (bss_start - dst) = bss_start
410 __ addq(dst, tmp);
411
412 // dst holds the start of the BSS section.
413 // Load the routine.
414 __ movq(dst, compiler::Address(dst, 0));
415}
416
417void StubCodeCompiler::GenerateLoadFfiCallbackMetadataRuntimeFunction(
418 uword function_index,
419 Register dst) {
420 // Keep in sync with FfiCallbackMetadata::EnsureFirstTrampolinePageLocked.
421 // Note: If the stub was aligned, this could be a single PC relative load.
422
423 // Load a pointer to the beginning of the stub into dst.
424 const intptr_t kLeaqLength = 7;
425 const intptr_t code_size = __ CodeSize();
426 __ leaq(dst, Address::AddressRIPRelative(-kLeaqLength - code_size));
427
428 // Round dst down to the page size.
429 __ andq(dst, Immediate(FfiCallbackMetadata::kPageMask));
430
431 // Load the function from the function table.
432 __ LoadFromOffset(dst, dst,
434}
435
436static const RegisterSet kArgumentRegisterSet(
439
440void StubCodeCompiler::GenerateFfiCallbackTrampolineStub() {
441 // RAX is volatile and not used for passing any arguments.
443
444 Label body;
446 ++i) {
447 // The FfiCallbackMetadata table is keyed by the trampoline entry point. So
448 // look up the current PC, then jump to the shared section. RIP gives us the
449 // address of the next instruction, so to get the true entry point, we have
450 // to subtract the size of the leaq instruction.
451 const intptr_t kLeaqLength = 7;
452 const intptr_t size_before = __ CodeSize();
453 __ leaq(RAX, Address::AddressRIPRelative(-kLeaqLength));
454 const intptr_t size_after = __ CodeSize();
455 ASSERT_EQUAL(size_after - size_before, kLeaqLength);
456 __ jmp(&body);
457 }
458
459 ASSERT_EQUAL(__ CodeSize(),
460 FfiCallbackMetadata::kNativeCallbackTrampolineSize *
462
463 __ Bind(&body);
464
465 const intptr_t shared_stub_start = __ CodeSize();
466
467 // Save THR which is callee-saved.
468 __ pushq(THR);
469
470 // 2 = THR & return address
471 COMPILE_ASSERT(2 == FfiCallbackMetadata::kNativeCallbackTrampolineStackDelta);
472
473 // Save all registers which might hold arguments.
474 __ PushRegisters(kArgumentRegisterSet);
475
476 // Load the thread, verify the callback ID and exit the safepoint.
477 //
478 // We exit the safepoint inside DLRT_GetFfiCallbackMetadata in order to safe
479 // code size on this shared stub.
480 {
483
484 // We also need to look up the entry point for the trampoline. This is
485 // returned using a pointer passed to the second arg of the C function
486 // below. We aim that pointer at a reserved stack slot.
488 __ pushq(Immediate(0)); // Reserve a stack slot for the entry point.
490
491 // We also need to know if this is a sync or async callback. This is also
492 // returned by pointer.
494 __ pushq(Immediate(0)); // Reserve a stack slot for the trampoline type.
496
497#if defined(DART_TARGET_OS_FUCHSIA)
498 // TODO(https://dartbug.com/52579): Remove.
499 if (FLAG_precompiled_mode) {
501 TMP);
502 } else {
503 __ movq(RAX, Immediate(
504 reinterpret_cast<int64_t>(DLRT_GetFfiCallbackMetadata)));
505 }
506#else
507 GenerateLoadFfiCallbackMetadataRuntimeFunction(
509#endif // defined(DART_TARGET_OS_FUCHSIA)
510
511 __ EnterFrame(0);
512 __ ReserveAlignedFrameSpace(0);
513
514 __ CallCFunction(RAX);
515 __ movq(THR, RAX);
516
517 __ LeaveFrame();
518
519 // The trampoline type is at the top of the stack. Pop it into RAX.
520 __ popq(RAX);
521
522 // Entry point is now at the top of the stack. Pop it into TMP.
523 __ popq(TMP);
524 }
525
526 // Restore the arguments.
527 __ PopRegisters(kArgumentRegisterSet);
528
529 // Current state:
530 //
531 // Stack:
532 // <old stack (arguments)>
533 // <return address>
534 // <saved THR>
535 //
536 // Registers: Like entry, except TMP == target, RAX == abi, and THR == thread
537 // All argument registers are untouched.
538
539 Label async_callback;
540 Label done;
541
542 // If GetFfiCallbackMetadata returned a null thread, it means that the
543 // callback was invoked after it was deleted. In this case, do nothing.
544 __ cmpq(THR, Immediate(0));
546
547 // Check the trampoline type to see how the callback should be invoked.
548 __ cmpq(RAX, Immediate(static_cast<uword>(
551
552 // Sync callback. The entry point contains the target function, so just call
553 // it. DLRT_GetThreadForNativeCallbackTrampoline exited the safepoint, so
554 // re-enter it afterwards.
555
556 // On entry to the function, there will be two extra slots on the stack:
557 // the saved THR and the return address. The target will know to skip them.
558 __ call(TMP);
559
560 // Takes care to not clobber *any* registers (besides TMP).
561 __ EnterFullSafepoint();
562
564 __ Bind(&async_callback);
565
566 // Async callback. The entrypoint marshals the arguments into a message and
567 // sends it over the send port. DLRT_GetThreadForNativeCallbackTrampoline
568 // entered a temporary isolate, so exit it afterwards.
569
570 // On entry to the function, there will be two extra slots on the stack:
571 // the saved THR and the return address. The target will know to skip them.
572 __ call(TMP);
573
574 // Exit the temporary isolate.
575 {
576#if defined(DART_TARGET_OS_FUCHSIA)
577 // TODO(https://dartbug.com/52579): Remove.
578 if (FLAG_precompiled_mode) {
579 GenerateLoadBSSEntry(BSS::Relocation::DRT_ExitTemporaryIsolate, RAX, TMP);
580 } else {
581 __ movq(RAX,
582 Immediate(reinterpret_cast<int64_t>(DLRT_ExitTemporaryIsolate)));
583 }
584#else
585 GenerateLoadFfiCallbackMetadataRuntimeFunction(
587#endif // defined(DART_TARGET_OS_FUCHSIA)
588
589 __ EnterFrame(0);
590 __ ReserveAlignedFrameSpace(0);
591
592 __ CallCFunction(RAX);
593
594 __ LeaveFrame();
595 }
596
597 __ Bind(&done);
598
599 // Restore THR (callee-saved).
600 __ popq(THR);
601
602 __ ret();
603
604 // 'kNativeCallbackSharedStubSize' is an upper bound because the exact
605 // instruction size can vary slightly based on OS calling conventions.
606 ASSERT_LESS_OR_EQUAL(__ CodeSize() - shared_stub_start,
607 FfiCallbackMetadata::kNativeCallbackSharedStubSize);
609
610#if defined(DEBUG)
611 while (__ CodeSize() < FfiCallbackMetadata::kPageSize) {
612 __ Breakpoint();
613 }
614#endif
615}
616
617void StubCodeCompiler::GenerateDispatchTableNullErrorStub() {
618 __ EnterStubFrame();
621 __ CallRuntime(kDispatchTableNullErrorRuntimeEntry, /*argument_count=*/1);
622 // The NullError runtime entry does not return.
623 __ Breakpoint();
624}
625
626void StubCodeCompiler::GenerateRangeError(bool with_fpu_regs) {
627 auto perform_runtime_call = [&]() {
628 // If the generated code has unboxed index/length we need to box them before
629 // calling the runtime entry.
631 Label length, smi_case;
632
633 // The user-controlled index might not fit into a Smi.
634#if !defined(DART_COMPRESSED_POINTERS)
636 __ BranchIf(NO_OVERFLOW, &length);
637#else
640 __ sarq(TMP, Immediate(30));
641 __ addq(TMP, Immediate(1));
642 __ cmpq(TMP, Immediate(2));
643 __ j(BELOW, &length);
644#endif
645 {
646 // Allocate a mint, reload the two registers and populate the mint.
647 __ PushImmediate(Immediate(0));
648 __ CallRuntime(kAllocateMintRuntimeEntry, /*argument_count=*/0);
649 __ PopRegister(RangeErrorABI::kIndexReg);
650 __ movq(
651 TMP,
652 Address(RBP, target::kWordSize *
655 __ movq(FieldAddress(RangeErrorABI::kIndexReg,
656 target::Mint::value_offset()),
657 TMP);
658 __ movq(
660 Address(RBP, target::kWordSize *
663 }
664
665 // Length is guaranteed to be in positive Smi range (it comes from a load
666 // of a vm recognized array).
667 __ Bind(&length);
669 }
670 __ PushRegistersInOrder(
672 __ CallRuntime(kRangeErrorRuntimeEntry, /*argument_count=*/2);
673 __ Breakpoint();
674 };
675
676 GenerateSharedStubGeneric(
677 /*save_fpu_registers=*/with_fpu_regs,
678 with_fpu_regs
679 ? target::Thread::range_error_shared_with_fpu_regs_stub_offset()
680 : target::Thread::range_error_shared_without_fpu_regs_stub_offset(),
681 /*allow_return=*/false, perform_runtime_call);
682}
683
684void StubCodeCompiler::GenerateWriteError(bool with_fpu_regs) {
685 auto perform_runtime_call = [&]() {
686 __ CallRuntime(kWriteErrorRuntimeEntry, /*argument_count=*/2);
687 __ Breakpoint();
688 };
689
690 GenerateSharedStubGeneric(
691 /*save_fpu_registers=*/with_fpu_regs,
692 with_fpu_regs
693 ? target::Thread::write_error_shared_with_fpu_regs_stub_offset()
694 : target::Thread::write_error_shared_without_fpu_regs_stub_offset(),
695 /*allow_return=*/false, perform_runtime_call);
696}
697
698// Input parameters:
699// RSP : points to return address.
700// RSP + 8 : address of return value.
701// R13 : address of first argument in argument array.
702// RBX : address of the native function to call.
703// R10 : argc_tag including number of arguments and function kind.
704static void GenerateCallNativeWithWrapperStub(Assembler* assembler,
705 Address wrapper_address) {
706 const intptr_t native_args_struct_offset = 0;
707 const intptr_t thread_offset =
708 target::NativeArguments::thread_offset() + native_args_struct_offset;
709 const intptr_t argc_tag_offset =
710 target::NativeArguments::argc_tag_offset() + native_args_struct_offset;
711 const intptr_t argv_offset =
712 target::NativeArguments::argv_offset() + native_args_struct_offset;
713 const intptr_t retval_offset =
714 target::NativeArguments::retval_offset() + native_args_struct_offset;
715
716 __ EnterStubFrame();
717
718 // Save exit frame information to enable stack walking as we are about
719 // to transition to native code.
720 __ movq(Address(THR, target::Thread::top_exit_frame_info_offset()), RBP);
721
722 // Mark that the thread exited generated code through a runtime call.
723 __ movq(Address(THR, target::Thread::exit_through_ffi_offset()),
724 Immediate(target::Thread::exit_through_runtime_call()));
725
726#if defined(DEBUG)
727 {
728 Label ok;
729 // Check that we are always entering from Dart code.
730 __ movq(R8, Immediate(VMTag::kDartTagId));
733 __ Stop("Not coming from Dart code.");
734 __ Bind(&ok);
735 }
736#endif
737
738 // Mark that the thread is executing native code.
740
741 WithExceptionCatchingTrampoline(assembler, [&]() {
742 // Reserve space for the native arguments structure passed on the stack (the
743 // outgoing pointer parameter to the native arguments structure is passed in
744 // RDI) and align frame before entering the C++ world.
745 __ subq(RSP, Immediate(target::NativeArguments::StructSize()));
747 __ andq(RSP, Immediate(~(OS::ActivationFrameAlignment() - 1)));
748 }
749
750 // Pass target::NativeArguments structure by value and call native function.
751 // Set thread in NativeArgs.
752 __ movq(Address(RSP, thread_offset), THR);
753 // Set argc in target::NativeArguments.
754 __ movq(Address(RSP, argc_tag_offset), R10);
755 // Set argv in target::NativeArguments.
756 __ movq(Address(RSP, argv_offset), R13);
757 // Compute return value addr.
758 __ leaq(RAX, Address(RBP, (target::frame_layout.param_end_from_fp + 1) *
759 target::kWordSize));
760 // Set retval in target::NativeArguments.
761 __ movq(Address(RSP, retval_offset), RAX);
762
763 // Pass the pointer to the target::NativeArguments.
765 // Pass pointer to function entrypoint.
767
768 __ movq(RAX, wrapper_address);
769 __ CallCFunction(RAX);
770
771 // Mark that the thread is executing Dart code.
772 __ movq(Assembler::VMTagAddress(), Immediate(VMTag::kDartTagId));
773
774 // Mark that the thread has not exited generated Dart code.
775 __ movq(Address(THR, target::Thread::exit_through_ffi_offset()),
776 Immediate(0));
777
778 // Reset exit frame information in Isolate's mutator thread structure.
779 __ movq(Address(THR, target::Thread::top_exit_frame_info_offset()),
780 Immediate(0));
781
782 // Restore the global object pool after returning from runtime (old space is
783 // moving, so the GOP could have been relocated).
784 if (FLAG_precompiled_mode) {
785 __ movq(PP, Address(THR, target::Thread::global_object_pool_offset()));
786 }
787 });
788
789 __ LeaveStubFrame();
790 __ ret();
791}
792
793void StubCodeCompiler::GenerateCallNoScopeNativeStub() {
794 GenerateCallNativeWithWrapperStub(
795 assembler,
796 Address(THR,
797 target::Thread::no_scope_native_wrapper_entry_point_offset()));
798}
799
800void StubCodeCompiler::GenerateCallAutoScopeNativeStub() {
801 GenerateCallNativeWithWrapperStub(
802 assembler,
803 Address(THR,
804 target::Thread::auto_scope_native_wrapper_entry_point_offset()));
805}
806
807// Input parameters:
808// RSP : points to return address.
809// RSP + 8 : address of return value.
810// RAX : address of first argument in argument array.
811// RBX : address of the native function to call.
812// R10 : argc_tag including number of arguments and function kind.
813void StubCodeCompiler::GenerateCallBootstrapNativeStub() {
814 GenerateCallNativeWithWrapperStub(
815 assembler,
816 Address(THR,
817 target::Thread::bootstrap_native_wrapper_entry_point_offset()));
818}
819
820// Input parameters:
821// ARGS_DESC_REG: arguments descriptor array.
822void StubCodeCompiler::GenerateCallStaticFunctionStub() {
823 __ EnterStubFrame();
824 __ pushq(ARGS_DESC_REG); // Preserve arguments descriptor array.
825 // Setup space on stack for return value.
826 __ pushq(Immediate(0));
827 __ CallRuntime(kPatchStaticCallRuntimeEntry, 0);
828 __ popq(CODE_REG); // Get Code object result.
829 __ popq(ARGS_DESC_REG); // Restore arguments descriptor array.
830 // Remove the stub frame as we are about to jump to the dart function.
831 __ LeaveStubFrame();
832
833 __ movq(RBX, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
834 __ jmp(RBX);
835}
836
837// Called from a static call only when an invalid code has been entered
838// (invalid because its function was optimized or deoptimized).
839// ARGS_DESC_REG: arguments descriptor array.
840void StubCodeCompiler::GenerateFixCallersTargetStub() {
841 Label monomorphic;
842 __ BranchOnMonomorphicCheckedEntryJIT(&monomorphic);
843
844 // This was a static call.
845 // Load code pointer to this stub from the thread:
846 // The one that is passed in, is not correct - it points to the code object
847 // that needs to be replaced.
848 __ movq(CODE_REG,
849 Address(THR, target::Thread::fix_callers_target_code_offset()));
850 __ EnterStubFrame();
851 __ pushq(ARGS_DESC_REG); // Preserve arguments descriptor array.
852 // Setup space on stack for return value.
853 __ pushq(Immediate(0));
854 __ CallRuntime(kFixCallersTargetRuntimeEntry, 0);
855 __ popq(CODE_REG); // Get Code object.
856 __ popq(ARGS_DESC_REG); // Restore arguments descriptor array.
857 __ movq(RAX, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
858 __ LeaveStubFrame();
859 __ jmp(RAX);
860 __ int3();
861
862 __ Bind(&monomorphic);
863 // This was a switchable call.
864 // Load code pointer to this stub from the thread:
865 // The one that is passed in, is not correct - it points to the code object
866 // that needs to be replaced.
867 __ movq(CODE_REG,
868 Address(THR, target::Thread::fix_callers_target_code_offset()));
869 __ EnterStubFrame();
870 __ pushq(Immediate(0)); // Result slot.
871 __ pushq(RDX); // Preserve receiver.
872 __ pushq(RBX); // Old cache value (also 2nd return value).
873 __ CallRuntime(kFixCallersTargetMonomorphicRuntimeEntry, 2);
874 __ popq(RBX); // Get target cache object.
875 __ popq(RDX); // Restore receiver.
876 __ popq(CODE_REG); // Get target Code object.
877 __ movq(RAX, FieldAddress(CODE_REG, target::Code::entry_point_offset(
879 __ LeaveStubFrame();
880 __ jmp(RAX);
881 __ int3();
882}
883
884// Called from object allocate instruction when the allocation stub has been
885// disabled.
886void StubCodeCompiler::GenerateFixAllocationStubTargetStub() {
887 // Load code pointer to this stub from the thread:
888 // The one that is passed in, is not correct - it points to the code object
889 // that needs to be replaced.
890 __ movq(CODE_REG,
891 Address(THR, target::Thread::fix_allocation_stub_code_offset()));
892 __ EnterStubFrame();
893 // Setup space on stack for return value.
894 __ pushq(Immediate(0));
895 __ CallRuntime(kFixAllocationStubTargetRuntimeEntry, 0);
896 __ popq(CODE_REG); // Get Code object.
897 __ movq(RAX, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
898 __ LeaveStubFrame();
899 __ jmp(RAX);
900 __ int3();
901}
902
903// Called from object allocate instruction when the allocation stub for a
904// generic class has been disabled.
905void StubCodeCompiler::GenerateFixParameterizedAllocationStubTargetStub() {
906 // Load code pointer to this stub from the thread:
907 // The one that is passed in, is not correct - it points to the code object
908 // that needs to be replaced.
909 __ movq(CODE_REG,
910 Address(THR, target::Thread::fix_allocation_stub_code_offset()));
911 __ EnterStubFrame();
912 // Setup space on stack for return value.
914 __ pushq(Immediate(0));
915 __ CallRuntime(kFixAllocationStubTargetRuntimeEntry, 0);
916 __ popq(CODE_REG); // Get Code object.
918 __ movq(RAX, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
919 __ LeaveStubFrame();
920 __ jmp(RAX);
921 __ int3();
922}
923
924// Input parameters:
925// R10: smi-tagged argument count, may be zero.
926// RBP[target::frame_layout.param_end_from_fp + 1]: last argument.
927static void PushArrayOfArguments(Assembler* assembler) {
928 __ LoadObject(R12, NullObject());
929 // Allocate array to store arguments of caller.
930 __ movq(RBX, R12); // Null element type for raw Array.
932 __ SmiUntag(R10);
933 // RAX: newly allocated array.
934 // R10: length of the array (was preserved by the stub).
935 __ pushq(RAX); // Array is in RAX and on top of stack.
936 __ leaq(R12,
937 Address(RBP, R10, TIMES_8,
938 target::frame_layout.param_end_from_fp * target::kWordSize));
939 __ leaq(RBX, FieldAddress(RAX, target::Array::data_offset()));
940 // R12: address of first argument on stack.
941 // RBX: address of first argument in array.
942 Label loop, loop_condition;
943#if defined(DEBUG)
944 static auto const kJumpLength = Assembler::kFarJump;
945#else
946 static auto const kJumpLength = Assembler::kNearJump;
947#endif // DEBUG
948 __ jmp(&loop_condition, kJumpLength);
949 __ Bind(&loop);
950 __ movq(RDI, Address(R12, 0));
951 // Generational barrier is needed, array is not necessarily in new space.
952 __ StoreCompressedIntoObject(RAX, Address(RBX, 0), RDI);
953 __ addq(RBX, Immediate(target::kCompressedWordSize));
954 __ subq(R12, Immediate(target::kWordSize));
955 __ Bind(&loop_condition);
956 __ decq(R10);
958}
959
960// Used by eager and lazy deoptimization. Preserve result in RAX if necessary.
961// This stub translates optimized frame into unoptimized frame. The optimized
962// frame can contain values in registers and on stack, the unoptimized
963// frame contains all values on stack.
964// Deoptimization occurs in following steps:
965// - Push all registers that can contain values.
966// - Call C routine to copy the stack and saved registers into temporary buffer.
967// - Adjust caller's frame to correct unoptimized frame size.
968// - Fill the unoptimized frame.
969// - Materialize objects that require allocation (e.g. Double instances).
970// GC can occur only after frame is fully rewritten.
971// Stack after EnterDartFrame(0, PP, kNoRegister) below:
972// +------------------+
973// | Saved PP | <- PP
974// +------------------+
975// | PC marker | <- TOS
976// +------------------+
977// | Saved FP | <- FP of stub
978// +------------------+
979// | return-address | (deoptimization point)
980// +------------------+
981// | Saved CODE_REG |
982// +------------------+
983// | ... | <- SP of optimized frame
984//
985// Parts of the code cannot GC, part of the code can GC.
986static void GenerateDeoptimizationSequence(Assembler* assembler,
987 DeoptStubKind kind) {
988 // DeoptimizeCopyFrame expects a Dart frame, i.e. EnterDartFrame(0), but there
989 // is no need to set the correct PC marker or load PP, since they get patched.
990 __ EnterStubFrame();
991
992 // The code in this frame may not cause GC. kDeoptimizeCopyFrameRuntimeEntry
993 // and kDeoptimizeFillFrameRuntimeEntry are leaf runtime calls.
994 const intptr_t saved_result_slot_from_fp =
995 target::frame_layout.first_local_from_fp + 1 -
997 const intptr_t saved_exception_slot_from_fp =
998 target::frame_layout.first_local_from_fp + 1 -
1000 const intptr_t saved_stacktrace_slot_from_fp =
1001 target::frame_layout.first_local_from_fp + 1 -
1003 // Result in RAX is preserved as part of pushing all registers below.
1004
1005 // Push registers in their enumeration order: lowest register number at
1006 // lowest address.
1007 for (intptr_t i = kNumberOfCpuRegisters - 1; i >= 0; i--) {
1008 if (i == CODE_REG) {
1009 // Save the original value of CODE_REG pushed before invoking this stub
1010 // instead of the value used to call this stub.
1011 __ pushq(Address(RBP, 2 * target::kWordSize));
1012 } else {
1013 __ pushq(static_cast<Register>(i));
1014 }
1015 }
1016 __ subq(RSP, Immediate(kNumberOfXmmRegisters * kFpuRegisterSize));
1017 intptr_t offset = 0;
1018 for (intptr_t reg_idx = 0; reg_idx < kNumberOfXmmRegisters; ++reg_idx) {
1019 XmmRegister xmm_reg = static_cast<XmmRegister>(reg_idx);
1020 __ movups(Address(RSP, offset), xmm_reg);
1022 }
1023
1024 {
1025 // Pass address of saved registers block.
1027 LeafRuntimeScope rt(assembler,
1028 /*frame_size=*/0,
1029 /*preserve_registers=*/false);
1030 bool is_lazy =
1031 (kind == kLazyDeoptFromReturn) || (kind == kLazyDeoptFromThrow);
1032 __ movq(CallingConventions::kArg2Reg, Immediate(is_lazy ? 1 : 0));
1033 rt.Call(kDeoptimizeCopyFrameRuntimeEntry, 2);
1034 // Result (RAX) is stack-size (FP - SP) in bytes.
1035 }
1036
1037 if (kind == kLazyDeoptFromReturn) {
1038 // Restore result into RBX temporarily.
1039 __ movq(RBX, Address(RBP, saved_result_slot_from_fp * target::kWordSize));
1040 } else if (kind == kLazyDeoptFromThrow) {
1041 // Restore result into RBX temporarily.
1042 __ movq(RBX,
1043 Address(RBP, saved_exception_slot_from_fp * target::kWordSize));
1044 __ movq(RDX,
1045 Address(RBP, saved_stacktrace_slot_from_fp * target::kWordSize));
1046 }
1047
1048 // There is a Dart Frame on the stack. We must restore PP and leave frame.
1049 __ RestoreCodePointer();
1050 __ LeaveStubFrame();
1051
1052 __ popq(RCX); // Preserve return address.
1053 __ movq(RSP, RBP); // Discard optimized frame.
1054 __ subq(RSP, RAX); // Reserve space for deoptimized frame.
1055 __ pushq(RCX); // Restore return address.
1056
1057 // DeoptimizeFillFrame expects a Dart frame, i.e. EnterDartFrame(0), but there
1058 // is no need to set the correct PC marker or load PP, since they get patched.
1059 __ EnterStubFrame();
1060
1061 if (kind == kLazyDeoptFromReturn) {
1062 __ pushq(RBX); // Preserve result as first local.
1063 } else if (kind == kLazyDeoptFromThrow) {
1064 __ pushq(RBX); // Preserve exception as first local.
1065 __ pushq(RDX); // Preserve stacktrace as second local.
1066 }
1067 {
1068 __ movq(CallingConventions::kArg1Reg, RBP); // Pass last FP as a parameter.
1069 LeafRuntimeScope rt(assembler,
1070 /*frame_size=*/0,
1071 /*preserve_registers=*/false);
1072 rt.Call(kDeoptimizeFillFrameRuntimeEntry, 1);
1073 }
1074 if (kind == kLazyDeoptFromReturn) {
1075 // Restore result into RBX.
1076 __ movq(RBX, Address(RBP, target::frame_layout.first_local_from_fp *
1077 target::kWordSize));
1078 } else if (kind == kLazyDeoptFromThrow) {
1079 // Restore exception into RBX.
1080 __ movq(RBX, Address(RBP, target::frame_layout.first_local_from_fp *
1081 target::kWordSize));
1082 // Restore stacktrace into RDX.
1083 __ movq(RDX, Address(RBP, (target::frame_layout.first_local_from_fp - 1) *
1084 target::kWordSize));
1085 }
1086 // Code above cannot cause GC.
1087 // There is a Dart Frame on the stack. We must restore PP and leave frame.
1088 __ RestoreCodePointer();
1089 __ LeaveStubFrame();
1090
1091 // Frame is fully rewritten at this point and it is safe to perform a GC.
1092 // Materialize any objects that were deferred by FillFrame because they
1093 // require allocation.
1094 // Enter stub frame with loading PP. The caller's PP is not materialized yet.
1095 __ EnterStubFrame();
1096 if (kind == kLazyDeoptFromReturn) {
1097 __ pushq(RBX); // Preserve result, it will be GC-d here.
1098 } else if (kind == kLazyDeoptFromThrow) {
1099 // Preserve CODE_REG for one more runtime call.
1100 __ pushq(CODE_REG);
1101 __ pushq(RBX); // Preserve exception.
1102 __ pushq(RDX); // Preserve stacktrace.
1103 }
1104 __ pushq(Immediate(target::ToRawSmi(0))); // Space for the result.
1105 __ CallRuntime(kDeoptimizeMaterializeRuntimeEntry, 0);
1106 // Result tells stub how many bytes to remove from the expression stack
1107 // of the bottom-most frame. They were used as materialization arguments.
1108 __ popq(RBX);
1109 __ SmiUntag(RBX);
1110 if (kind == kLazyDeoptFromReturn) {
1111 __ popq(RAX); // Restore result.
1112 } else if (kind == kLazyDeoptFromThrow) {
1113 __ popq(RDX); // Restore stacktrace.
1114 __ popq(RAX); // Restore exception.
1115 __ popq(CODE_REG);
1116 }
1117 __ LeaveStubFrame();
1118
1119 __ popq(RCX); // Pop return address.
1120 __ addq(RSP, RBX); // Remove materialization arguments.
1121 __ pushq(RCX); // Push return address.
1122 // The caller is responsible for emitting the return instruction.
1123
1124 if (kind == kLazyDeoptFromThrow) {
1125 // Unoptimized frame is now ready to accept the exception. Rethrow it to
1126 // find the right handler.
1127 __ EnterStubFrame();
1128 __ pushq(Immediate(target::ToRawSmi(0))); // Space for the result.
1129 __ pushq(RAX); // Exception
1130 __ pushq(RDX); // Stacktrace
1131 __ pushq(Immediate(target::ToRawSmi(1))); // Bypass debugger.
1132 __ CallRuntime(kReThrowRuntimeEntry, 3);
1133 __ LeaveStubFrame();
1134 }
1135}
1136
1137// RAX: result, must be preserved
1138void StubCodeCompiler::GenerateDeoptimizeLazyFromReturnStub() {
1139 // Push zap value instead of CODE_REG for lazy deopt.
1140 __ pushq(Immediate(kZapCodeReg));
1141 // Return address for "call" to deopt stub.
1142 __ pushq(Immediate(kZapReturnAddress));
1143 __ movq(CODE_REG,
1144 Address(THR, target::Thread::lazy_deopt_from_return_stub_offset()));
1145 GenerateDeoptimizationSequence(assembler, kLazyDeoptFromReturn);
1146 __ ret();
1147}
1148
1149// RAX: exception, must be preserved
1150// RDX: stacktrace, must be preserved
1151void StubCodeCompiler::GenerateDeoptimizeLazyFromThrowStub() {
1152 // Push zap value instead of CODE_REG for lazy deopt.
1153 __ pushq(Immediate(kZapCodeReg));
1154 // Return address for "call" to deopt stub.
1155 __ pushq(Immediate(kZapReturnAddress));
1156 __ movq(CODE_REG,
1157 Address(THR, target::Thread::lazy_deopt_from_throw_stub_offset()));
1158 GenerateDeoptimizationSequence(assembler, kLazyDeoptFromThrow);
1159 __ ret();
1160}
1161
1162void StubCodeCompiler::GenerateDeoptimizeStub() {
1163 __ popq(TMP);
1164 __ pushq(CODE_REG);
1165 __ pushq(TMP);
1166 __ movq(CODE_REG, Address(THR, target::Thread::deoptimize_stub_offset()));
1167 GenerateDeoptimizationSequence(assembler, kEagerDeopt);
1168 __ ret();
1169}
1170
1171// Input:
1172// IC_DATA_REG - icdata/megamorphic_cache
1173// RDI - arguments descriptor size
1174static void GenerateNoSuchMethodDispatcherBody(Assembler* assembler,
1175 Register receiver_reg) {
1176 __ pushq(Immediate(0)); // Setup space on stack for result.
1177 __ pushq(receiver_reg); // Receiver.
1178 __ pushq(IC_DATA_REG); // ICData/MegamorphicCache.
1179 __ pushq(ARGS_DESC_REG); // Arguments descriptor array.
1180
1181 // Adjust arguments count.
1182 __ OBJ(cmp)(FieldAddress(ARGS_DESC_REG,
1183 target::ArgumentsDescriptor::type_args_len_offset()),
1184 Immediate(0));
1185 __ OBJ(mov)(R10, RDI);
1186 Label args_count_ok;
1187 __ j(EQUAL, &args_count_ok, Assembler::kNearJump);
1188 // Include the type arguments.
1189 __ OBJ(add)(R10, Immediate(target::ToRawSmi(1)));
1190 __ Bind(&args_count_ok);
1191
1192 // R10: Smi-tagged arguments array length.
1193 PushArrayOfArguments(assembler);
1194 const intptr_t kNumArgs = 4;
1195 __ CallRuntime(kNoSuchMethodFromCallStubRuntimeEntry, kNumArgs);
1196 __ Drop(4);
1197 __ popq(RAX); // Return value.
1198 __ LeaveStubFrame();
1199 __ ret();
1200}
1201
1202// Input:
1203// IC_DATA_REG - icdata/megamorphic_cache
1204// ARGS_DESC_REG - argument descriptor
1205static void GenerateDispatcherCode(Assembler* assembler,
1206 Label* call_target_function) {
1207 __ Comment("NoSuchMethodDispatch");
1208 // When lazily generated invocation dispatchers are disabled, the
1209 // miss-handler may return null.
1210 __ CompareObject(RAX, NullObject());
1211 __ j(NOT_EQUAL, call_target_function);
1212
1213 __ EnterStubFrame();
1214 // Load the receiver.
1215 __ OBJ(mov)(RDI, FieldAddress(ARGS_DESC_REG,
1216 target::ArgumentsDescriptor::size_offset()));
1217 __ movq(RAX,
1218 Address(RBP, RDI, TIMES_HALF_WORD_SIZE,
1219 target::frame_layout.param_end_from_fp * target::kWordSize));
1220
1221 GenerateNoSuchMethodDispatcherBody(assembler, /*receiver_reg=*/RAX);
1222}
1223
1224// Input:
1225// IC_DATA_REG - icdata/megamorphic_cache
1226// RDX - receiver
1227void StubCodeCompiler::GenerateNoSuchMethodDispatcherStub() {
1228 __ EnterStubFrame();
1229
1230 __ movq(ARGS_DESC_REG,
1231 FieldAddress(IC_DATA_REG,
1232 target::CallSiteData::arguments_descriptor_offset()));
1233 __ OBJ(mov)(RDI, FieldAddress(ARGS_DESC_REG,
1234 target::ArgumentsDescriptor::size_offset()));
1235
1236 GenerateNoSuchMethodDispatcherBody(assembler, /*receiver_reg=*/RDX);
1237}
1238
1239// Called for inline allocation of arrays.
1240// Input registers (preserved):
1241// AllocateArrayABI::kLengthReg: array length as Smi.
1242// AllocateArrayABI::kTypeArgumentsReg: type arguments of array.
1243// Output registers:
1244// AllocateArrayABI::kResultReg: newly allocated array.
1245// Clobbered:
1246// RCX, RDI, R12
1247void StubCodeCompiler::GenerateAllocateArrayStub() {
1248 if (!FLAG_use_slow_path && FLAG_inline_alloc) {
1249 Label slow_case;
1250 // Compute the size to be allocated, it is based on the array length
1251 // and is computed as:
1252 // RoundedAllocationSize(
1253 // (array_length * target::kCompressedWordSize) +
1254 // target::Array::header_size()).
1255 __ movq(RDI, AllocateArrayABI::kLengthReg); // Array Length.
1256 // Check that length is Smi.
1257 __ testq(RDI, Immediate(kSmiTagMask));
1258 __ j(NOT_ZERO, &slow_case);
1259
1260 // Check length >= 0 && length <= kMaxNewSpaceElements
1261 const Immediate& max_len =
1262 Immediate(target::ToRawSmi(target::Array::kMaxNewSpaceElements));
1263 __ OBJ(cmp)(RDI, max_len);
1264 __ j(ABOVE, &slow_case);
1265
1266 // Check for allocation tracing.
1267 NOT_IN_PRODUCT(__ MaybeTraceAllocation(kArrayCid, &slow_case));
1268
1269 const intptr_t fixed_size_plus_alignment_padding =
1270 target::Array::header_size() +
1272 // RDI is a Smi.
1274 fixed_size_plus_alignment_padding));
1275 ASSERT(kSmiTagShift == 1);
1277
1278 const intptr_t cid = kArrayCid;
1280 Address(THR, target::Thread::top_offset()));
1281
1282 // RDI: allocation size.
1284 __ addq(RCX, RDI);
1285 __ j(CARRY, &slow_case);
1286
1287 // Check if the allocation fits into the remaining space.
1288 // AllocateArrayABI::kResultReg: potential new object start.
1289 // RCX: potential next object start.
1290 // RDI: allocation size.
1291 __ cmpq(RCX, Address(THR, target::Thread::end_offset()));
1292 __ j(ABOVE_EQUAL, &slow_case);
1293 __ CheckAllocationCanary(AllocateArrayABI::kResultReg);
1294
1295 // Successfully allocated the object(s), now update top to point to
1296 // next object start and initialize the object.
1297 __ movq(Address(THR, target::Thread::top_offset()), RCX);
1299
1300 // Initialize the tags.
1301 // AllocateArrayABI::kResultReg: new object start as a tagged pointer.
1302 // RDI: allocation size.
1303 {
1304 Label size_tag_overflow, done;
1305 __ cmpq(RDI, Immediate(target::UntaggedObject::kSizeTagMaxSizeTag));
1306 __ j(ABOVE, &size_tag_overflow, Assembler::kNearJump);
1307 __ shlq(RDI, Immediate(target::UntaggedObject::kTagBitsSizeTagPos -
1310
1311 __ Bind(&size_tag_overflow);
1312 __ LoadImmediate(RDI, Immediate(0));
1313 __ Bind(&done);
1314
1315 // Get the class index and insert it into the tags.
1317 __ orq(RDI, Immediate(tags));
1318 __ movq(FieldAddress(RAX, target::Array::tags_offset()), RDI); // Tags.
1319 }
1320
1321 // AllocateArrayABI::kResultReg: new object start as a tagged pointer.
1322 // Store the type argument field.
1323 // No generational barrier needed, since we store into a new object.
1324 __ StoreCompressedIntoObjectNoBarrier(
1326 FieldAddress(AllocateArrayABI::kResultReg,
1327 target::Array::type_arguments_offset()),
1329
1330 // Set the length field.
1331 __ StoreCompressedIntoObjectNoBarrier(
1333 FieldAddress(AllocateArrayABI::kResultReg,
1334 target::Array::length_offset()),
1336
1337 // Initialize all array elements to raw_null.
1338 // AllocateArrayABI::kResultReg: new object start as a tagged pointer.
1339 // RCX: new object end address.
1340 // RDI: iterator which initially points to the start of the variable
1341 // data area to be initialized.
1342 __ LoadObject(R12, NullObject());
1343 __ leaq(RDI, FieldAddress(AllocateArrayABI::kResultReg,
1344 target::Array::header_size()));
1345 Label loop;
1346 __ Bind(&loop);
1347 for (intptr_t offset = 0; offset < target::kObjectAlignment;
1348 offset += target::kCompressedWordSize) {
1349 // No generational barrier needed, since we are storing null.
1350 __ StoreCompressedIntoObjectNoBarrier(AllocateArrayABI::kResultReg,
1351 Address(RDI, offset), R12);
1352 }
1353 // Safe to only check every kObjectAlignment bytes instead of each word.
1354 ASSERT(kAllocationRedZoneSize >= target::kObjectAlignment);
1355 __ addq(RDI, Immediate(target::kObjectAlignment));
1356 __ cmpq(RDI, RCX);
1357 __ j(UNSIGNED_LESS, &loop);
1358 __ WriteAllocationCanary(RCX);
1359 __ ret();
1360
1361 // Unable to allocate the array using the fast inline code, just call
1362 // into the runtime.
1363 __ Bind(&slow_case);
1364 }
1365 // Create a stub frame as we are pushing some objects on the stack before
1366 // calling into the runtime.
1367 __ EnterStubFrame();
1368 __ pushq(Immediate(0)); // Space for return value.
1369 __ pushq(AllocateArrayABI::kLengthReg); // Array length as Smi.
1370 __ pushq(AllocateArrayABI::kTypeArgumentsReg); // Element type.
1371 __ CallRuntime(kAllocateArrayRuntimeEntry, 2);
1372
1373 // Write-barrier elimination might be enabled for this array (depending on the
1374 // array length). To be sure we will check if the allocated object is in old
1375 // space and if so call a leaf runtime to add it to the remembered set.
1376 __ movq(AllocateArrayABI::kResultReg, Address(RSP, 2 * target::kWordSize));
1378
1379 __ popq(AllocateArrayABI::kTypeArgumentsReg); // Pop element type argument.
1380 __ popq(AllocateArrayABI::kLengthReg); // Pop array length argument.
1381 __ popq(AllocateArrayABI::kResultReg); // Pop allocated object.
1382 __ LeaveStubFrame();
1383 __ ret();
1384}
1385
1386void StubCodeCompiler::GenerateAllocateMintSharedWithFPURegsStub() {
1387 // For test purpose call allocation stub without inline allocation attempt.
1388 if (!FLAG_use_slow_path && FLAG_inline_alloc) {
1389 Label slow_case;
1390 __ TryAllocate(compiler::MintClass(), &slow_case, Assembler::kNearJump,
1392 __ Ret();
1393
1394 __ Bind(&slow_case);
1395 }
1398 GenerateSharedStub(/*save_fpu_registers=*/true, &kAllocateMintRuntimeEntry,
1399 target::Thread::allocate_mint_with_fpu_regs_stub_offset(),
1400 /*allow_return=*/true,
1401 /*store_runtime_result_in_result_register=*/true);
1402}
1403
1404void StubCodeCompiler::GenerateAllocateMintSharedWithoutFPURegsStub() {
1405 // For test purpose call allocation stub without inline allocation attempt.
1406 if (!FLAG_use_slow_path && FLAG_inline_alloc) {
1407 Label slow_case;
1408 __ TryAllocate(compiler::MintClass(), &slow_case, Assembler::kNearJump,
1410 __ Ret();
1411
1412 __ Bind(&slow_case);
1413 }
1416 GenerateSharedStub(
1417 /*save_fpu_registers=*/false, &kAllocateMintRuntimeEntry,
1418 target::Thread::allocate_mint_without_fpu_regs_stub_offset(),
1419 /*allow_return=*/true,
1420 /*store_runtime_result_in_result_register=*/true);
1421}
1422
1423static const RegisterSet kCalleeSavedRegisterSet(
1426
1427// Called when invoking Dart code from C++ (VM code).
1428// Input parameters:
1429// RSP : points to return address.
1430// RDI : target code or entry point (in bare instructions mode).
1431// RSI : arguments descriptor array.
1432// RDX : arguments array.
1433// RCX : current thread.
1434void StubCodeCompiler::GenerateInvokeDartCodeStub() {
1435 __ EnterFrame(0);
1436
1437 const Register kTargetReg = CallingConventions::kArg1Reg;
1438 const Register kArgDescReg = CallingConventions::kArg2Reg;
1439 const Register kArgsReg = CallingConventions::kArg3Reg;
1440 const Register kThreadReg = CallingConventions::kArg4Reg;
1441
1442 // Push code object to PC marker slot.
1443 __ pushq(Address(kThreadReg, target::Thread::invoke_dart_code_stub_offset()));
1444
1445 // At this point, the stack looks like:
1446 // | stub code object
1447 // | saved RBP | <-- RBP
1448 // | saved PC (return to DartEntry::InvokeFunction) |
1449
1450 const intptr_t kInitialOffset = 2;
1451 // Save arguments descriptor array, later replaced by Smi argument count.
1452 const intptr_t kArgumentsDescOffset = -(kInitialOffset)*target::kWordSize;
1453 __ pushq(kArgDescReg);
1454
1455 // Save C++ ABI callee-saved registers.
1456 __ PushRegisters(kCalleeSavedRegisterSet);
1457
1458 // If any additional (or fewer) values are pushed, the offsets in
1459 // target::frame_layout.exit_link_slot_from_entry_fp will need to be changed.
1460
1461 // Set up THR, which caches the current thread in Dart code.
1462 if (THR != kThreadReg) {
1463 __ movq(THR, kThreadReg);
1464 }
1465
1466#if defined(USING_SHADOW_CALL_STACK)
1467#error Unimplemented
1468#endif
1469
1470 // Save the current VMTag on the stack.
1472 __ pushq(RAX);
1473
1474 // Save top resource and top exit frame info. Use RAX as a temporary register.
1475 // StackFrameIterator reads the top exit frame info saved in this frame.
1476 __ movq(RAX, Address(THR, target::Thread::top_resource_offset()));
1477 __ pushq(RAX);
1478 __ movq(Address(THR, target::Thread::top_resource_offset()), Immediate(0));
1479
1480 __ movq(RAX, Address(THR, target::Thread::exit_through_ffi_offset()));
1481 __ pushq(RAX);
1482 __ movq(Address(THR, target::Thread::exit_through_ffi_offset()),
1483 Immediate(0));
1484
1485 __ movq(RAX, Address(THR, target::Thread::top_exit_frame_info_offset()));
1486 __ pushq(RAX);
1487
1488 // The constant target::frame_layout.exit_link_slot_from_entry_fp must be kept
1489 // in sync with the code above.
1490 __ EmitEntryFrameVerification();
1491
1492 __ movq(Address(THR, target::Thread::top_exit_frame_info_offset()),
1493 Immediate(0));
1494
1495 // Mark that the thread is executing Dart code. Do this after initializing the
1496 // exit link for the profiler.
1497 __ movq(Assembler::VMTagAddress(), Immediate(VMTag::kDartTagId));
1498
1499 // Load arguments descriptor array into R10, which is passed to Dart code.
1500 __ movq(R10, kArgDescReg);
1501
1502 // Push arguments. At this point we only need to preserve kTargetReg.
1503 ASSERT(kTargetReg != RDX);
1504
1505 // Load number of arguments into RBX and adjust count for type arguments.
1506 __ OBJ(mov)(RBX,
1507 FieldAddress(R10, target::ArgumentsDescriptor::count_offset()));
1508 __ OBJ(cmp)(
1509 FieldAddress(R10, target::ArgumentsDescriptor::type_args_len_offset()),
1510 Immediate(0));
1511 Label args_count_ok;
1512 __ j(EQUAL, &args_count_ok, Assembler::kNearJump);
1513 __ addq(RBX, Immediate(target::ToRawSmi(1))); // Include the type arguments.
1514 __ Bind(&args_count_ok);
1515 // Save number of arguments as Smi on stack, replacing saved ArgumentsDesc.
1516 __ movq(Address(RBP, kArgumentsDescOffset), RBX);
1517 __ SmiUntag(RBX);
1518
1519 // Compute address of 'arguments array' data area into RDX.
1520 __ leaq(RDX, FieldAddress(kArgsReg, target::Array::data_offset()));
1521
1522 // Set up arguments for the Dart call.
1523 Label push_arguments;
1524 Label done_push_arguments;
1525 __ j(ZERO, &done_push_arguments, Assembler::kNearJump);
1526 __ LoadImmediate(RAX, Immediate(0));
1527 __ Bind(&push_arguments);
1528#if defined(DART_COMPRESSED_POINTERS)
1529 __ LoadCompressed(TMP, Address(RDX, RAX, TIMES_COMPRESSED_WORD_SIZE, 0));
1530 __ pushq(TMP);
1531#else
1532 __ pushq(Address(RDX, RAX, TIMES_8, 0));
1533#endif
1534 __ incq(RAX);
1535 __ cmpq(RAX, RBX);
1536 __ j(LESS, &push_arguments, Assembler::kNearJump);
1537 __ Bind(&done_push_arguments);
1538
1539 // Call the Dart code entrypoint.
1540 if (FLAG_precompiled_mode) {
1541 __ movq(PP, Address(THR, target::Thread::global_object_pool_offset()));
1542 __ xorq(CODE_REG, CODE_REG); // GC-safe value into CODE_REG.
1543 } else {
1544 __ xorq(PP, PP); // GC-safe value into PP.
1545 __ movq(CODE_REG, kTargetReg);
1546 __ movq(kTargetReg,
1547 FieldAddress(CODE_REG, target::Code::entry_point_offset()));
1548 }
1549 __ call(kTargetReg); // R10 is the arguments descriptor array.
1550
1551 // Read the saved number of passed arguments as Smi.
1552 __ movq(RDX, Address(RBP, kArgumentsDescOffset));
1553
1554 // Get rid of arguments pushed on the stack.
1555 __ leaq(RSP, Address(RSP, RDX, TIMES_4, 0)); // RDX is a Smi.
1556
1557 // Restore the saved top exit frame info and top resource back into the
1558 // Isolate structure.
1559 __ popq(Address(THR, target::Thread::top_exit_frame_info_offset()));
1560 __ popq(Address(THR, target::Thread::exit_through_ffi_offset()));
1561 __ popq(Address(THR, target::Thread::top_resource_offset()));
1562
1563 // Restore the current VMTag from the stack.
1565
1566#if defined(USING_SHADOW_CALL_STACK)
1567#error Unimplemented
1568#endif
1569
1570 // Restore C++ ABI callee-saved registers.
1571 __ PopRegisters(kCalleeSavedRegisterSet);
1572 __ set_constant_pool_allowed(false);
1573
1574 // Restore the frame pointer.
1575 __ LeaveFrame();
1576
1577 __ ret();
1578}
1579
1580// Helper to generate space allocation of context stub.
1581// This does not initialize the fields of the context.
1582// Input:
1583// R10: number of context variables.
1584// Output:
1585// RAX: new, uninitialized allocated Context object.
1586// Clobbered:
1587// R13
1588static void GenerateAllocateContextSpaceStub(Assembler* assembler,
1589 Label* slow_case) {
1590 // First compute the rounded instance size.
1591 // R10: number of context variables.
1592 intptr_t fixed_size_plus_alignment_padding =
1593 (target::Context::header_size() +
1595 __ leaq(R13, Address(R10, TIMES_COMPRESSED_WORD_SIZE,
1596 fixed_size_plus_alignment_padding));
1598
1599 // Check for allocation tracing.
1600 NOT_IN_PRODUCT(__ MaybeTraceAllocation(kContextCid, slow_case));
1601
1602 // Now allocate the object.
1603 // R10: number of context variables.
1604 __ movq(RAX, Address(THR, target::Thread::top_offset()));
1605 __ addq(R13, RAX);
1606 // Check if the allocation fits into the remaining space.
1607 // RAX: potential new object.
1608 // R13: potential next object start.
1609 // R10: number of context variables.
1610 __ cmpq(R13, Address(THR, target::Thread::end_offset()));
1611 __ j(ABOVE_EQUAL, slow_case);
1612 __ CheckAllocationCanary(RAX);
1613
1614 // Successfully allocated the object, now update top to point to
1615 // next object start and initialize the object.
1616 // RAX: new object.
1617 // R13: next object start.
1618 // R10: number of context variables.
1619 __ movq(Address(THR, target::Thread::top_offset()), R13);
1620 // R13: Size of allocation in bytes.
1621 __ subq(R13, RAX);
1622 __ addq(RAX, Immediate(kHeapObjectTag));
1623 // Generate isolate-independent code to allow sharing between isolates.
1624
1625 // Calculate the size tag.
1626 // RAX: new object.
1627 // R10: number of context variables.
1628 {
1629 Label size_tag_overflow, done;
1630 __ leaq(R13, Address(R10, TIMES_COMPRESSED_WORD_SIZE,
1631 fixed_size_plus_alignment_padding));
1633 __ cmpq(R13, Immediate(target::UntaggedObject::kSizeTagMaxSizeTag));
1634 __ j(ABOVE, &size_tag_overflow, Assembler::kNearJump);
1635 __ shlq(R13, Immediate(target::UntaggedObject::kTagBitsSizeTagPos -
1637 __ jmp(&done);
1638
1639 __ Bind(&size_tag_overflow);
1640 // Set overflow size tag value.
1641 __ LoadImmediate(R13, Immediate(0));
1642
1643 __ Bind(&done);
1644 // RAX: new object.
1645 // R10: number of context variables.
1646 // R13: size and bit tags.
1647 uword tags = target::MakeTagWordForNewSpaceObject(kContextCid, 0);
1648 __ orq(R13, Immediate(tags));
1649 __ movq(FieldAddress(RAX, target::Object::tags_offset()), R13); // Tags.
1650 }
1651
1652 // Setup up number of context variables field.
1653 // RAX: new object.
1654 // R10: number of context variables as integer value (not object).
1655 __ movl(FieldAddress(RAX, target::Context::num_variables_offset()), R10);
1656}
1657
1658// Called for inline allocation of contexts.
1659// Input:
1660// R10: number of context variables.
1661// Output:
1662// RAX: new allocated Context object.
1663// Clobbered:
1664// R9, R13
1665void StubCodeCompiler::GenerateAllocateContextStub() {
1666 __ LoadObject(R9, NullObject());
1667 if (!FLAG_use_slow_path && FLAG_inline_alloc) {
1668 Label slow_case;
1669
1670 GenerateAllocateContextSpaceStub(assembler, &slow_case);
1671
1672 // Setup the parent field.
1673 // RAX: new object.
1674 // R9: Parent object, initialized to null.
1675 // No generational barrier needed, since we are storing null.
1676 __ StoreCompressedIntoObjectNoBarrier(
1677 RAX, FieldAddress(RAX, target::Context::parent_offset()), R9);
1678
1679 // Initialize the context variables.
1680 // RAX: new object.
1681 // R10: number of context variables.
1682 {
1683 Label loop, entry;
1684 __ leaq(R13, FieldAddress(RAX, target::Context::variable_offset(0)));
1685#if defined(DEBUG)
1686 static auto const kJumpLength = Assembler::kFarJump;
1687#else
1688 static auto const kJumpLength = Assembler::kNearJump;
1689#endif // DEBUG
1690 __ jmp(&entry, kJumpLength);
1691 __ Bind(&loop);
1692 __ decq(R10);
1693 // No generational barrier needed, since we are storing null.
1694 __ StoreCompressedIntoObjectNoBarrier(
1695 RAX, Address(R13, R10, TIMES_COMPRESSED_WORD_SIZE, 0), R9);
1696 __ Bind(&entry);
1697 __ cmpq(R10, Immediate(0));
1699 }
1700
1701 // Done allocating and initializing the context.
1702 // RAX: new object.
1703 __ ret();
1704
1705 __ Bind(&slow_case);
1706 }
1707 // Create a stub frame.
1708 __ EnterStubFrame();
1709 __ pushq(R9); // Setup space on stack for the return value.
1710 __ SmiTag(R10);
1711 __ pushq(R10); // Push number of context variables.
1712 __ CallRuntime(kAllocateContextRuntimeEntry, 1); // Allocate context.
1713 __ popq(RAX); // Pop number of context variables argument.
1714 __ popq(RAX); // Pop the new context object.
1715 // Write-barrier elimination might be enabled for this context (depending on
1716 // the size). To be sure we will check if the allocated object is in old
1717 // space and if so call a leaf runtime to add it to the remembered set.
1719
1720 // RAX: new object
1721 // Restore the frame pointer.
1722 __ LeaveStubFrame();
1723
1724 __ ret();
1725}
1726
1727// Called for inline clone of contexts.
1728// Input:
1729// R9: context to clone.
1730// Output:
1731// RAX: new allocated Context object.
1732// Clobbered:
1733// R10, R13
1734void StubCodeCompiler::GenerateCloneContextStub() {
1735 if (!FLAG_use_slow_path && FLAG_inline_alloc) {
1736 Label slow_case;
1737
1738 // Load num. variable (int32_t) in the existing context.
1739 __ movsxd(R10, FieldAddress(R9, target::Context::num_variables_offset()));
1740
1741 // Allocate new context of same size.
1742 GenerateAllocateContextSpaceStub(assembler, &slow_case);
1743
1744 // Load parent in the existing context.
1745 __ LoadCompressed(R13, FieldAddress(R9, target::Context::parent_offset()));
1746 // Setup the parent field.
1747 // RAX: new object.
1748 // R9: Old parent object.
1749 __ StoreCompressedIntoObjectNoBarrier(
1750 RAX, FieldAddress(RAX, target::Context::parent_offset()), R13);
1751
1752 // Clone the context variables.
1753 // RAX: new context clone.
1754 // R10: number of context variables.
1755 {
1756 Label loop, entry;
1757 __ jmp(&entry, Assembler::kNearJump);
1758 __ Bind(&loop);
1759 __ decq(R10);
1760 __ LoadCompressed(R13, FieldAddress(R9, R10, TIMES_COMPRESSED_WORD_SIZE,
1761 target::Context::variable_offset(0)));
1762 __ StoreCompressedIntoObjectNoBarrier(
1763 RAX,
1764 FieldAddress(RAX, R10, TIMES_COMPRESSED_WORD_SIZE,
1765 target::Context::variable_offset(0)),
1766 R13);
1767 __ Bind(&entry);
1768 __ cmpq(R10, Immediate(0));
1770 }
1771
1772 // Done allocating and initializing the context.
1773 // RAX: new object.
1774 __ ret();
1775
1776 __ Bind(&slow_case);
1777 }
1778
1779 // Create a stub frame.
1780 __ EnterStubFrame();
1781
1782 __ PushObject(NullObject()); // Make space on stack for the return value.
1783 __ pushq(R9); // Push context.
1784 __ CallRuntime(kCloneContextRuntimeEntry, 1); // Clone context.
1785 __ popq(RAX); // Pop context argument.
1786 __ popq(RAX); // Pop the new context object.
1787
1788 // Write-barrier elimination might be enabled for this context (depending on
1789 // the size). To be sure we will check if the allocated object is in old
1790 // space and if so call a leaf runtime to add it to the remembered set.
1792
1793 // RAX: new object
1794 // Restore the frame pointer.
1795 __ LeaveStubFrame();
1796
1797 __ ret();
1798}
1799
1800void StubCodeCompiler::GenerateWriteBarrierWrappersStub() {
1801 for (intptr_t i = 0; i < kNumberOfCpuRegisters; ++i) {
1802 if ((kDartAvailableCpuRegs & (1 << i)) == 0) continue;
1803
1804 Register reg = static_cast<Register>(i);
1805 intptr_t start = __ CodeSize();
1807 __ movq(kWriteBarrierObjectReg, reg);
1808 __ call(Address(THR, target::Thread::write_barrier_entry_point_offset()));
1810 __ ret();
1811 intptr_t end = __ CodeSize();
1812
1814 }
1815}
1816
1817// Helper stub to implement Assembler::StoreIntoObject/Array.
1818// Input parameters:
1819// RDX: Object (old)
1820// RAX: Value (old or new)
1821// R13: Slot
1822// If RAX is new, add RDX to the store buffer. Otherwise RAX is old, mark RAX
1823// and add it to the mark list.
1827static void GenerateWriteBarrierStubHelper(Assembler* assembler, bool cards) {
1828 Label skip_marking;
1829 __ movq(TMP, FieldAddress(RAX, target::Object::tags_offset()));
1830 __ andq(TMP, Address(THR, target::Thread::write_barrier_mask_offset()));
1831 __ testq(TMP, Immediate(target::UntaggedObject::kIncrementalBarrierMask));
1832 __ j(ZERO, &skip_marking);
1833
1834 {
1835 // Atomically clear kNotMarkedBit.
1836 Label retry, done;
1837 __ pushq(RAX); // Spill.
1838 __ pushq(RCX); // Spill.
1839 __ movq(TMP, RAX); // RAX is fixed implicit operand of CAS.
1840 __ movq(RAX, FieldAddress(TMP, target::Object::tags_offset()));
1841
1842 __ Bind(&retry);
1843 __ movq(RCX, RAX);
1844 __ testq(RCX, Immediate(1 << target::UntaggedObject::kNotMarkedBit));
1845 __ j(ZERO, &done); // Marked by another thread.
1846
1847 __ andq(RCX, Immediate(~(1 << target::UntaggedObject::kNotMarkedBit)));
1848 // Cmpxchgq: compare value = implicit operand RAX, new value = RCX.
1849 // On failure, RAX is updated with the current value.
1850 __ LockCmpxchgq(FieldAddress(TMP, target::Object::tags_offset()), RCX);
1851 __ j(NOT_EQUAL, &retry, Assembler::kNearJump);
1852
1853 __ movq(RAX, Address(THR, target::Thread::marking_stack_block_offset()));
1855 __ movq(Address(RAX, RCX, TIMES_8,
1857 TMP);
1858 __ incq(RCX);
1860 __ cmpl(RCX, Immediate(target::MarkingStackBlock::kSize));
1861 __ j(NOT_EQUAL, &done);
1862
1863 {
1864 LeafRuntimeScope rt(assembler,
1865 /*frame_size=*/0,
1866 /*preserve_registers=*/true);
1868 rt.Call(kMarkingStackBlockProcessRuntimeEntry, 1);
1869 }
1870
1871 __ Bind(&done);
1872 __ popq(RCX); // Unspill.
1873 __ popq(RAX); // Unspill.
1874 }
1875
1876 Label add_to_remembered_set, remember_card;
1877 __ Bind(&skip_marking);
1878 __ movq(TMP, FieldAddress(RDX, target::Object::tags_offset()));
1879 __ shrl(TMP, Immediate(target::UntaggedObject::kBarrierOverlapShift));
1880 __ andq(TMP, FieldAddress(RAX, target::Object::tags_offset()));
1881 __ testq(TMP, Immediate(target::UntaggedObject::kGenerationalBarrierMask));
1882 __ j(NOT_ZERO, &add_to_remembered_set, Assembler::kNearJump);
1883 __ ret();
1884
1885 __ Bind(&add_to_remembered_set);
1886 if (cards) {
1887 __ movl(TMP, FieldAddress(RDX, target::Object::tags_offset()));
1888 __ testl(TMP, Immediate(1 << target::UntaggedObject::kCardRememberedBit));
1889 __ j(NOT_ZERO, &remember_card, Assembler::kFarJump);
1890 } else {
1891#if defined(DEBUG)
1892 Label ok;
1893 __ movl(TMP, FieldAddress(RDX, target::Object::tags_offset()));
1894 __ testl(TMP, Immediate(1 << target::UntaggedObject::kCardRememberedBit));
1896 __ Stop("Wrong barrier");
1897 __ Bind(&ok);
1898#endif
1899 }
1900 {
1901 // Atomically clear kOldAndNotRemembered.
1902 Label retry, done;
1903 __ pushq(RAX); // Spill.
1904 __ pushq(RCX); // Spill.
1905 __ movq(RAX, FieldAddress(RDX, target::Object::tags_offset()));
1906
1907 __ Bind(&retry);
1908 __ movq(RCX, RAX);
1909 __ testq(RCX,
1910 Immediate(1 << target::UntaggedObject::kOldAndNotRememberedBit));
1911 __ j(ZERO, &done); // Remembered by another thread.
1912 __ andq(RCX,
1913 Immediate(~(1 << target::UntaggedObject::kOldAndNotRememberedBit)));
1914 // Cmpxchgq: compare value = implicit operand RAX, new value = RCX.
1915 // On failure, RAX is updated with the current value.
1916 __ LockCmpxchgq(FieldAddress(RDX, target::Object::tags_offset()), RCX);
1917 __ j(NOT_EQUAL, &retry, Assembler::kNearJump);
1918
1919 // Load the StoreBuffer block out of the thread. Then load top_ out of the
1920 // StoreBufferBlock and add the address to the pointers_.
1921 // RDX: Address being stored
1922 __ movq(RAX, Address(THR, target::Thread::store_buffer_block_offset()));
1924 __ movq(
1926 RDX);
1927
1928 // Increment top_ and check for overflow.
1929 // RCX: top_
1930 // RAX: StoreBufferBlock
1931 __ incq(RCX);
1933 __ cmpl(RCX, Immediate(target::StoreBufferBlock::kSize));
1934 __ j(NOT_EQUAL, &done);
1935
1936 {
1937 LeafRuntimeScope rt(assembler,
1938 /*frame_size=*/0,
1939 /*preserve_registers=*/true);
1941 rt.Call(kStoreBufferBlockProcessRuntimeEntry, 1);
1942 }
1943
1944 __ Bind(&done);
1945 __ popq(RCX); // Unspill.
1946 __ popq(RAX); // Unspill.
1947 __ ret();
1948 }
1949
1950 if (cards) {
1951 Label remember_card_slow;
1952
1953 // Get card table.
1954 __ Bind(&remember_card);
1955 __ movq(TMP, RDX); // Object.
1956 __ andq(TMP, Immediate(target::kPageMask)); // Page.
1957 __ cmpq(Address(TMP, target::Page::card_table_offset()), Immediate(0));
1958 __ j(EQUAL, &remember_card_slow, Assembler::kNearJump);
1959
1960 // Dirty the card. Not atomic: we assume mutable arrays are not shared
1961 // between threads.
1962 __ pushq(RAX);
1963 __ pushq(RCX);
1964 __ subq(R13, TMP); // Offset in page.
1965 __ movq(TMP,
1966 Address(TMP, target::Page::card_table_offset())); // Card table.
1967 __ shrq(R13, Immediate(target::Page::kBytesPerCardLog2)); // Card index.
1968 __ movq(RCX, R13);
1969 __ shrq(R13, Immediate(target::kBitsPerWordLog2)); // Word offset.
1970 __ movq(RAX, Immediate(1));
1971 __ shlq(RAX, RCX); // Bit mask. (Shift amount is mod 63.)
1972 __ orq(Address(TMP, R13, TIMES_8, 0), RAX);
1973 __ popq(RCX);
1974 __ popq(RAX);
1975 __ ret();
1976
1977 // Card table not yet allocated.
1978 __ Bind(&remember_card_slow);
1979 {
1980 LeafRuntimeScope rt(assembler,
1981 /*frame_size=*/0,
1982 /*preserve_registers=*/true);
1985 rt.Call(kRememberCardRuntimeEntry, 2);
1986 }
1987 __ ret();
1988 }
1989}
1990
1991void StubCodeCompiler::GenerateWriteBarrierStub() {
1992 GenerateWriteBarrierStubHelper(assembler, false);
1993}
1994
1995void StubCodeCompiler::GenerateArrayWriteBarrierStub() {
1996 GenerateWriteBarrierStubHelper(assembler, true);
1997}
1998
1999static void GenerateAllocateObjectHelper(Assembler* assembler,
2000 bool is_cls_parameterized) {
2001 // Note: Keep in sync with calling function.
2002 const Register kTagsReg = AllocateObjectABI::kTagsReg;
2003
2004 {
2005 Label slow_case;
2006 const Register kNewTopReg = R9;
2007
2008#if !defined(PRODUCT)
2009 {
2010 const Register kCidRegister = RSI;
2011 __ ExtractClassIdFromTags(kCidRegister, AllocateObjectABI::kTagsReg);
2012 __ MaybeTraceAllocation(kCidRegister, &slow_case, TMP);
2013 }
2014#endif
2015 // Allocate the object and update top to point to
2016 // next object start and initialize the allocated object.
2017 {
2018 const Register kInstanceSizeReg = RSI;
2019
2020 __ ExtractInstanceSizeFromTags(kInstanceSizeReg, kTagsReg);
2021
2023 Address(THR, target::Thread::top_offset()));
2024 __ leaq(kNewTopReg, Address(AllocateObjectABI::kResultReg,
2025 kInstanceSizeReg, TIMES_1, 0));
2026 // Check if the allocation fits into the remaining space.
2027 __ cmpq(kNewTopReg, Address(THR, target::Thread::end_offset()));
2028 __ j(ABOVE_EQUAL, &slow_case);
2029 __ CheckAllocationCanary(AllocateObjectABI::kResultReg);
2030
2031 __ movq(Address(THR, target::Thread::top_offset()), kNewTopReg);
2032 } // kInstanceSizeReg = RSI
2033
2034 // Set the tags.
2035 // 64 bit store also zeros the identity hash field.
2036 __ movq(
2037 Address(AllocateObjectABI::kResultReg, target::Object::tags_offset()),
2038 kTagsReg);
2039
2041
2042 // Initialize the remaining words of the object.
2043 {
2044 const Register kNextFieldReg = RDI;
2045 __ leaq(kNextFieldReg,
2046 FieldAddress(AllocateObjectABI::kResultReg,
2047 target::Instance::first_field_offset()));
2048
2049 const Register kNullReg = R10;
2050 __ LoadObject(kNullReg, NullObject());
2051
2052 // Loop until the whole object is initialized.
2053 Label loop;
2054 __ Bind(&loop);
2055 for (intptr_t offset = 0; offset < target::kObjectAlignment;
2056 offset += target::kCompressedWordSize) {
2057 __ StoreCompressedIntoObjectNoBarrier(AllocateObjectABI::kResultReg,
2058 Address(kNextFieldReg, offset),
2059 kNullReg);
2060 }
2061 // Safe to only check every kObjectAlignment bytes instead of each word.
2062 ASSERT(kAllocationRedZoneSize >= target::kObjectAlignment);
2063 __ addq(kNextFieldReg, Immediate(target::kObjectAlignment));
2064 __ cmpq(kNextFieldReg, kNewTopReg);
2065 __ j(UNSIGNED_LESS, &loop);
2066 } // kNextFieldReg = RDI, kNullReg = R10
2067
2068 __ WriteAllocationCanary(kNewTopReg); // Fix overshoot.
2069
2070 if (is_cls_parameterized) {
2071 Label not_parameterized_case;
2072
2073 const Register kClsIdReg = R9;
2074 const Register kTypeOffsetReg = RDI;
2075
2076 __ ExtractClassIdFromTags(kClsIdReg, kTagsReg);
2077
2078 // Load class' type_arguments_field offset in words.
2079 __ LoadClassById(kTypeOffsetReg, kClsIdReg);
2080 __ movl(
2081 kTypeOffsetReg,
2082 FieldAddress(kTypeOffsetReg,
2083 target::Class::
2084 host_type_arguments_field_offset_in_words_offset()));
2085
2086 // Set the type arguments in the new object.
2087 __ StoreCompressedIntoObject(
2089 FieldAddress(AllocateObjectABI::kResultReg, kTypeOffsetReg,
2092
2093 __ Bind(&not_parameterized_case);
2094 } // kTypeOffsetReg = RDI;
2095
2096 __ ret();
2097
2098 __ Bind(&slow_case);
2099 } // kNewTopReg = R9;
2100
2101 // Fall back on slow case:
2102 if (!is_cls_parameterized) {
2104 }
2105 // Tail call to generic allocation stub.
2106 __ jmp(
2107 Address(THR, target::Thread::allocate_object_slow_entry_point_offset()));
2108}
2109
2110// Called for inline allocation of objects (any class).
2111void StubCodeCompiler::GenerateAllocateObjectStub() {
2112 GenerateAllocateObjectHelper(assembler, /*is_cls_parameterized=*/false);
2113}
2114
2115void StubCodeCompiler::GenerateAllocateObjectParameterizedStub() {
2116 GenerateAllocateObjectHelper(assembler, /*is_cls_parameterized=*/true);
2117}
2118
2119void StubCodeCompiler::GenerateAllocateObjectSlowStub() {
2120 if (!FLAG_precompiled_mode) {
2121 __ movq(CODE_REG,
2122 Address(THR, target::Thread::call_to_runtime_stub_offset()));
2123 }
2124
2125 __ ExtractClassIdFromTags(AllocateObjectABI::kTagsReg,
2127
2128 // Create a stub frame.
2129 // Ensure constant pool is allowed so we can e.g. load class object.
2130 __ EnterStubFrame();
2131
2132 // Setup space on stack for return value.
2135
2136 // Push class of object to be allocated.
2139
2140 // Must be Object::null() if non-parameterized class.
2142
2143 __ CallRuntime(kAllocateObjectRuntimeEntry, 2);
2144
2145 __ popq(AllocateObjectABI::kResultReg); // Drop type arguments.
2146 __ popq(AllocateObjectABI::kResultReg); // Drop class.
2147 __ popq(AllocateObjectABI::kResultReg); // Pop newly allocated object.
2148
2149 // Write-barrier elimination is enabled for [cls] and we therefore need to
2150 // ensure that the object is in new-space or has remembered bit set.
2152
2153 // AllocateObjectABI::kResultReg: new object
2154 // Restore the frame pointer.
2155 __ LeaveStubFrame();
2156
2157 __ ret();
2158}
2159
2160// Called for inline allocation of objects.
2162 UnresolvedPcRelativeCalls* unresolved_calls,
2163 const Class& cls,
2164 const Code& allocate_object,
2165 const Code& allocat_object_parametrized) {
2166 classid_t cls_id = target::Class::GetId(cls);
2167 ASSERT(cls_id != kIllegalCid);
2168
2169 const intptr_t cls_type_arg_field_offset =
2170 target::Class::TypeArgumentsFieldOffset(cls);
2171
2172 // The generated code is different if the class is parameterized.
2173 const bool is_cls_parameterized = target::Class::NumTypeArguments(cls) > 0;
2174 ASSERT(!is_cls_parameterized ||
2175 cls_type_arg_field_offset != target::Class::kNoTypeArguments);
2176
2177 const intptr_t instance_size = target::Class::GetInstanceSize(cls);
2178 ASSERT(instance_size > 0);
2179 const uword tags =
2180 target::MakeTagWordForNewSpaceObject(cls_id, instance_size);
2181
2182 const Register kTagsReg = AllocateObjectABI::kTagsReg;
2183
2184 __ movq(kTagsReg, Immediate(tags));
2185
2186 // Load the appropriate generic alloc. stub.
2187 if (!FLAG_use_slow_path && FLAG_inline_alloc &&
2188 !target::Class::TraceAllocation(cls) &&
2189 target::SizeFitsInSizeTag(instance_size)) {
2191 RELEASE_ASSERT(target::Heap::IsAllocatableInNewSpace(instance_size));
2192
2193 if (is_cls_parameterized) {
2194 if (!IsSameObject(NullObject(),
2195 CastHandle<Object>(allocat_object_parametrized))) {
2196 __ GenerateUnRelocatedPcRelativeTailCall();
2197 unresolved_calls->Add(new UnresolvedPcRelativeCall(
2198 __ CodeSize(), allocat_object_parametrized, /*is_tail_call=*/true));
2199 } else {
2200 __ jmp(Address(THR,
2201 target::Thread::
2202 allocate_object_parameterized_entry_point_offset()));
2203 }
2204 } else {
2205 if (!IsSameObject(NullObject(), CastHandle<Object>(allocate_object))) {
2206 __ GenerateUnRelocatedPcRelativeTailCall();
2207 unresolved_calls->Add(new UnresolvedPcRelativeCall(
2208 __ CodeSize(), allocate_object, /*is_tail_call=*/true));
2209 } else {
2210 __ jmp(
2211 Address(THR, target::Thread::allocate_object_entry_point_offset()));
2212 }
2213 }
2214 } else {
2215 if (!is_cls_parameterized) {
2217 }
2218 __ jmp(Address(THR,
2219 target::Thread::allocate_object_slow_entry_point_offset()));
2220 }
2221}
2222
2223// Called for invoking "dynamic noSuchMethod(Invocation invocation)" function
2224// from the entry code of a dart function after an error in passed argument
2225// name or number is detected.
2226// Input parameters:
2227// RSP : points to return address.
2228// RSP + 8 : address of last argument.
2229// R10 : arguments descriptor array.
2230void StubCodeCompiler::GenerateCallClosureNoSuchMethodStub() {
2231 __ EnterStubFrame();
2232
2233 // Load the receiver.
2234 // Note: In compressed pointer mode LoadCompressedSmi zero extends R13,
2235 // rather than sign extending it. This is ok since it's an unsigned value.
2236 __ LoadCompressedSmi(
2237 R13, FieldAddress(R10, target::ArgumentsDescriptor::size_offset()));
2238 __ movq(RAX,
2239 Address(RBP, R13, TIMES_4,
2240 target::frame_layout.param_end_from_fp * target::kWordSize));
2241
2242 // Load the function.
2243 __ LoadCompressed(RBX, FieldAddress(RAX, target::Closure::function_offset()));
2244
2245 __ pushq(Immediate(0)); // Result slot.
2246 __ pushq(RAX); // Receiver.
2247 __ pushq(RBX); // Function.
2248 __ pushq(R10); // Arguments descriptor array.
2249
2250 // Adjust arguments count.
2251 __ OBJ(cmp)(
2252 FieldAddress(R10, target::ArgumentsDescriptor::type_args_len_offset()),
2253 Immediate(0));
2254 __ movq(R10, R13);
2255 Label args_count_ok;
2256 __ j(EQUAL, &args_count_ok, Assembler::kNearJump);
2257 __ addq(R10, Immediate(target::ToRawSmi(1))); // Include the type arguments.
2258 __ Bind(&args_count_ok);
2259
2260 // R10: Smi-tagged arguments array length.
2261 PushArrayOfArguments(assembler);
2262
2263 const intptr_t kNumArgs = 4;
2264 __ CallRuntime(kNoSuchMethodFromPrologueRuntimeEntry, kNumArgs);
2265 // noSuchMethod on closures always throws an error, so it will never return.
2266 __ int3();
2267}
2268
2269// Cannot use function object from ICData as it may be the inlined
2270// function and not the top-scope function.
2272 if (FLAG_precompiled_mode) {
2273 __ Breakpoint();
2274 return;
2275 }
2276 Register ic_reg = RBX;
2277 Register func_reg = RDI;
2278 if (FLAG_trace_optimized_ic_calls) {
2279 __ EnterStubFrame();
2280 __ pushq(func_reg); // Preserve
2281 __ pushq(ic_reg); // Preserve.
2282 __ pushq(ic_reg); // Argument.
2283 __ pushq(func_reg); // Argument.
2284 __ CallRuntime(kTraceICCallRuntimeEntry, 2);
2285 __ popq(RAX); // Discard argument;
2286 __ popq(RAX); // Discard argument;
2287 __ popq(ic_reg); // Restore.
2288 __ popq(func_reg); // Restore.
2289 __ LeaveStubFrame();
2290 }
2291 __ incl(FieldAddress(func_reg, target::Function::usage_counter_offset()));
2292}
2293
2294// Loads function into 'temp_reg', preserves IC_DATA_REG.
2296 if (FLAG_precompiled_mode) {
2297 __ Breakpoint();
2298 return;
2299 }
2300 if (FLAG_optimization_counter_threshold >= 0) {
2301 Register func_reg = temp_reg;
2302 ASSERT(func_reg != IC_DATA_REG);
2303 __ Comment("Increment function counter");
2304 __ movq(func_reg,
2305 FieldAddress(IC_DATA_REG, target::ICData::owner_offset()));
2306 __ incl(FieldAddress(func_reg, target::Function::usage_counter_offset()));
2307 }
2308}
2309
2310// Note: RBX must be preserved.
2311// Attempt a quick Smi operation for known operations ('kind'). The ICData
2312// must have been primed with a Smi/Smi check that will be used for counting
2313// the invocations.
2314static void EmitFastSmiOp(Assembler* assembler,
2315 Token::Kind kind,
2316 intptr_t num_args,
2317 Label* not_smi_or_overflow) {
2318 __ Comment("Fast Smi op");
2319 ASSERT(num_args == 2);
2320 __ movq(RAX, Address(RSP, +2 * target::kWordSize)); // Left.
2321 __ movq(RCX, Address(RSP, +1 * target::kWordSize)); // Right
2322 __ movq(R13, RCX);
2323 __ orq(R13, RAX);
2324 __ testq(R13, Immediate(kSmiTagMask));
2325 __ j(NOT_ZERO, not_smi_or_overflow);
2326 switch (kind) {
2327 case Token::kADD: {
2328 __ OBJ(add)(RAX, RCX);
2329 __ j(OVERFLOW, not_smi_or_overflow);
2330 break;
2331 }
2332 case Token::kLT: {
2333 __ OBJ(cmp)(RAX, RCX);
2335 __ movzxb(RAX, RAX); // RAX := RAX < RCX ? 0 : 1
2336 __ movq(RAX,
2337 Address(THR, RAX, TIMES_8, target::Thread::bool_true_offset()));
2338 ASSERT(target::Thread::bool_true_offset() + 8 ==
2339 target::Thread::bool_false_offset());
2340 break;
2341 }
2342 case Token::kEQ: {
2343 __ OBJ(cmp)(RAX, RCX);
2344 __ setcc(NOT_EQUAL, ByteRegisterOf(RAX));
2345 __ movzxb(RAX, RAX); // RAX := RAX == RCX ? 0 : 1
2346 __ movq(RAX,
2347 Address(THR, RAX, TIMES_8, target::Thread::bool_true_offset()));
2348 ASSERT(target::Thread::bool_true_offset() + 8 ==
2349 target::Thread::bool_false_offset());
2350 break;
2351 }
2352 default:
2353 UNIMPLEMENTED();
2354 }
2355
2356 // RBX: IC data object (preserved).
2357 __ movq(R13, FieldAddress(RBX, target::ICData::entries_offset()));
2358 // R13: ic_data_array with check entries: classes and target functions.
2359 __ leaq(R13, FieldAddress(R13, target::Array::data_offset()));
2360// R13: points directly to the first ic data array element.
2361#if defined(DEBUG)
2362 // Check that first entry is for Smi/Smi.
2363 Label error, ok;
2364 const Immediate& imm_smi_cid = Immediate(target::ToRawSmi(kSmiCid));
2365 __ OBJ(cmp)(Address(R13, 0 * target::kCompressedWordSize), imm_smi_cid);
2367 __ OBJ(cmp)(Address(R13, 1 * target::kCompressedWordSize), imm_smi_cid);
2369 __ Bind(&error);
2370 __ Stop("Incorrect IC data");
2371 __ Bind(&ok);
2372#endif
2373
2374 if (FLAG_optimization_counter_threshold >= 0) {
2375 const intptr_t count_offset =
2376 target::ICData::CountIndexFor(num_args) * target::kCompressedWordSize;
2377 // Update counter, ignore overflow.
2378 __ OBJ(add)(Address(R13, count_offset), Immediate(target::ToRawSmi(1)));
2379 }
2380
2381 __ ret();
2382}
2383
2384// Saves the offset of the target entry-point (from the Function) into R8.
2385//
2386// Must be the first code generated, since any code before will be skipped in
2387// the unchecked entry-point.
2388static void GenerateRecordEntryPoint(Assembler* assembler) {
2389 Label done;
2390 __ movq(R8,
2391 Immediate(target::Function::entry_point_offset() - kHeapObjectTag));
2392 __ jmp(&done);
2393 __ BindUncheckedEntryPoint();
2394 __ movq(R8, Immediate(target::Function::entry_point_offset(
2397 __ Bind(&done);
2398}
2399
2400// Generate inline cache check for 'num_args'.
2401// RDX: receiver (if instance call)
2402// RBX: ICData
2403// RSP[0]: return address
2404// Control flow:
2405// - If receiver is null -> jump to IC miss.
2406// - If receiver is Smi -> load Smi class.
2407// - If receiver is not-Smi -> load receiver's class.
2408// - Check if 'num_args' (including receiver) match any IC data group.
2409// - Match found -> jump to target.
2410// - Match not found -> jump to IC miss.
2412 intptr_t num_args,
2413 const RuntimeEntry& handle_ic_miss,
2414 Token::Kind kind,
2415 Optimized optimized,
2416 CallType type,
2417 Exactness exactness) {
2418 if (FLAG_precompiled_mode) {
2419 __ Breakpoint();
2420 return;
2421 }
2422
2423 const bool save_entry_point = kind == Token::kILLEGAL;
2424 if (save_entry_point) {
2425 GenerateRecordEntryPoint(assembler);
2426 }
2427
2428 if (optimized == kOptimized) {
2430 } else {
2431 GenerateUsageCounterIncrement(/* scratch */ RCX);
2432 }
2433
2434 ASSERT(num_args == 1 || num_args == 2);
2435#if defined(DEBUG)
2436 {
2437 Label ok;
2438 // Check that the IC data array has NumArgsTested() == num_args.
2439 // 'NumArgsTested' is stored in the least significant bits of 'state_bits'.
2440 __ movl(RCX, FieldAddress(RBX, target::ICData::state_bits_offset()));
2441 ASSERT(target::ICData::NumArgsTestedShift() == 0); // No shift needed.
2442 __ andq(RCX, Immediate(target::ICData::NumArgsTestedMask()));
2443 __ cmpq(RCX, Immediate(num_args));
2445 __ Stop("Incorrect stub for IC data");
2446 __ Bind(&ok);
2447 }
2448#endif // DEBUG
2449
2450#if !defined(PRODUCT)
2451 Label stepping, done_stepping;
2452 if (optimized == kUnoptimized) {
2453 __ Comment("Check single stepping");
2454 __ LoadIsolate(RAX);
2455 __ cmpb(Address(RAX, target::Isolate::single_step_offset()), Immediate(0));
2456 __ j(NOT_EQUAL, &stepping);
2457 __ Bind(&done_stepping);
2458 }
2459#endif
2460
2461 Label not_smi_or_overflow;
2462 if (kind != Token::kILLEGAL) {
2463 EmitFastSmiOp(assembler, kind, num_args, &not_smi_or_overflow);
2464 }
2465 __ Bind(&not_smi_or_overflow);
2466
2467 __ Comment("Extract ICData initial values and receiver cid");
2468 // RBX: IC data object (preserved).
2469 __ movq(R13, FieldAddress(RBX, target::ICData::entries_offset()));
2470 // R13: ic_data_array with check entries: classes and target functions.
2471 __ leaq(R13, FieldAddress(R13, target::Array::data_offset()));
2472 // R13: points directly to the first ic data array element.
2473
2474 if (type == kInstanceCall) {
2475 __ LoadTaggedClassIdMayBeSmi(RAX, RDX);
2476 __ movq(
2478 FieldAddress(RBX, target::CallSiteData::arguments_descriptor_offset()));
2479 if (num_args == 2) {
2480 __ OBJ(mov)(RCX,
2481 FieldAddress(ARGS_DESC_REG,
2482 target::ArgumentsDescriptor::count_offset()));
2483 __ movq(R9, Address(RSP, RCX, TIMES_4, -target::kWordSize));
2484 __ LoadTaggedClassIdMayBeSmi(RCX, R9);
2485 }
2486 } else {
2487 __ movq(
2489 FieldAddress(RBX, target::CallSiteData::arguments_descriptor_offset()));
2490 __ OBJ(mov)(RCX, FieldAddress(ARGS_DESC_REG,
2491 target::ArgumentsDescriptor::count_offset()));
2492 __ movq(RDX, Address(RSP, RCX, TIMES_4, 0));
2493 __ LoadTaggedClassIdMayBeSmi(RAX, RDX);
2494 if (num_args == 2) {
2495 __ movq(R9, Address(RSP, RCX, TIMES_4, -target::kWordSize));
2496 __ LoadTaggedClassIdMayBeSmi(RCX, R9);
2497 }
2498 }
2499 // RAX: first argument class ID as Smi.
2500 // RCX: second argument class ID as Smi.
2501 // R10: args descriptor
2502
2503 // Loop that checks if there is an IC data match.
2504 Label loop, found, miss;
2505 __ Comment("ICData loop");
2506
2507 // We unroll the generic one that is generated once more than the others.
2508 const bool optimize = kind == Token::kILLEGAL;
2509 const intptr_t target_offset =
2510 target::ICData::TargetIndexFor(num_args) * target::kCompressedWordSize;
2511 const intptr_t count_offset =
2512 target::ICData::CountIndexFor(num_args) * target::kCompressedWordSize;
2513 const intptr_t exactness_offset =
2514 target::ICData::ExactnessIndexFor(num_args) * target::kCompressedWordSize;
2515
2516 __ Bind(&loop);
2517 for (int unroll = optimize ? 4 : 2; unroll >= 0; unroll--) {
2518 Label update;
2519 __ OBJ(mov)(R9, Address(R13, 0));
2520 __ cmpq(RAX, R9); // Class id match?
2521 if (num_args == 2) {
2522 __ j(NOT_EQUAL, &update); // Continue.
2523 __ OBJ(mov)(R9, Address(R13, target::kCompressedWordSize));
2524 // R9: next class ID to check (smi).
2525 __ cmpq(RCX, R9); // Class id match?
2526 }
2527 __ j(EQUAL, &found); // Break.
2528
2529 __ Bind(&update);
2530
2531 const intptr_t entry_size = target::ICData::TestEntryLengthFor(
2532 num_args, exactness == kCheckExactness) *
2533 target::kCompressedWordSize;
2534 __ addq(R13, Immediate(entry_size)); // Next entry.
2535
2536 __ cmpq(R9, Immediate(target::ToRawSmi(kIllegalCid))); // Done?
2537 if (unroll == 0) {
2538 __ j(NOT_EQUAL, &loop);
2539 } else {
2540 __ j(EQUAL, &miss);
2541 }
2542 }
2543
2544 __ Bind(&miss);
2545 __ Comment("IC miss");
2546 // Compute address of arguments (first read number of arguments from
2547 // arguments descriptor array and then compute address on the stack).
2548 __ OBJ(mov)(RAX, FieldAddress(ARGS_DESC_REG,
2549 target::ArgumentsDescriptor::count_offset()));
2550 __ leaq(RAX, Address(RSP, RAX, TIMES_4, 0)); // RAX is Smi.
2551 __ EnterStubFrame();
2552 if (save_entry_point) {
2553 __ SmiTag(R8); // Entry-point offset is not Smi.
2554 __ pushq(R8); // Preserve entry point.
2555 }
2556 __ pushq(ARGS_DESC_REG); // Preserve arguments descriptor array.
2557 __ pushq(RBX); // Preserve IC data object.
2558 __ pushq(Immediate(0)); // Result slot.
2559 // Push call arguments.
2560 for (intptr_t i = 0; i < num_args; i++) {
2561 __ movq(RCX, Address(RAX, -target::kWordSize * i));
2562 __ pushq(RCX);
2563 }
2564 __ pushq(RBX); // Pass IC data object.
2565 __ CallRuntime(handle_ic_miss, num_args + 1);
2566 // Remove the call arguments pushed earlier, including the IC data object.
2567 for (intptr_t i = 0; i < num_args + 1; i++) {
2568 __ popq(RAX);
2569 }
2570 __ popq(FUNCTION_REG); // Pop returned function object into RAX.
2571 __ popq(RBX); // Restore IC data array.
2572 __ popq(ARGS_DESC_REG); // Restore arguments descriptor array.
2573 if (save_entry_point) {
2574 __ popq(R8); // Restore entry point.
2575 __ SmiUntag(R8); // Entry-point offset is not Smi.
2576 }
2577 __ RestoreCodePointer();
2578 __ LeaveStubFrame();
2579 Label call_target_function;
2580 if (!FLAG_lazy_dispatchers) {
2581 GenerateDispatcherCode(assembler, &call_target_function);
2582 } else {
2583 __ jmp(&call_target_function);
2584 }
2585
2586 __ Bind(&found);
2587 // R13: Pointer to an IC data check group.
2588 Label call_target_function_through_unchecked_entry;
2589 if (exactness == kCheckExactness) {
2590 Label exactness_ok;
2591 ASSERT(num_args == 1);
2592 __ OBJ(mov)(RAX, Address(R13, exactness_offset));
2593 __ OBJ(cmp)(RAX,
2594 Immediate(target::ToRawSmi(
2596 __ j(LESS, &exactness_ok);
2597 __ j(EQUAL, &call_target_function_through_unchecked_entry);
2598
2599 // Check trivial exactness.
2600 // Note: UntaggedICData::receivers_static_type_ is guaranteed to be not null
2601 // because we only emit calls to this stub when it is not null.
2602 __ movq(RCX,
2603 FieldAddress(RBX, target::ICData::receivers_static_type_offset()));
2604 __ LoadCompressed(RCX, FieldAddress(RCX, target::Type::arguments_offset()));
2605 // RAX contains an offset to type arguments in words as a smi,
2606 // hence TIMES_4. RDX is guaranteed to be non-smi because it is expected
2607 // to have type arguments.
2608#if defined(DART_COMPRESSED_POINTERS)
2609 __ movsxd(RAX, RAX);
2610#endif
2611 __ OBJ(cmp)(RCX,
2612 FieldAddress(RDX, RAX, TIMES_COMPRESSED_HALF_WORD_SIZE, 0));
2613 __ j(EQUAL, &call_target_function_through_unchecked_entry);
2614
2615 // Update exactness state (not-exact anymore).
2616 __ OBJ(mov)(Address(R13, exactness_offset),
2617 Immediate(target::ToRawSmi(
2619 __ Bind(&exactness_ok);
2620 }
2621 __ LoadCompressed(FUNCTION_REG, Address(R13, target_offset));
2622
2623 if (FLAG_optimization_counter_threshold >= 0) {
2624 __ Comment("Update ICData counter");
2625 // Ignore overflow.
2626 __ OBJ(add)(Address(R13, count_offset), Immediate(target::ToRawSmi(1)));
2627 }
2628
2629 __ Comment("Call target (via specified entry point)");
2630 __ Bind(&call_target_function);
2631 // RAX: Target function.
2632 __ LoadCompressed(
2633 CODE_REG, FieldAddress(FUNCTION_REG, target::Function::code_offset()));
2634 if (save_entry_point) {
2635 __ addq(R8, RAX);
2636 __ jmp(Address(R8, 0));
2637 } else {
2638 __ jmp(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
2639 }
2640
2641 if (exactness == kCheckExactness) {
2642 __ Bind(&call_target_function_through_unchecked_entry);
2643 if (FLAG_optimization_counter_threshold >= 0) {
2644 __ Comment("Update ICData counter");
2645 // Ignore overflow.
2646 __ addq(Address(R13, count_offset), Immediate(target::ToRawSmi(1)));
2647 }
2648 __ Comment("Call target (via unchecked entry point)");
2649 __ LoadCompressed(FUNCTION_REG, Address(R13, target_offset));
2650 __ LoadCompressed(
2651 CODE_REG, FieldAddress(FUNCTION_REG, target::Function::code_offset()));
2652 __ jmp(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset(
2654 }
2655
2656#if !defined(PRODUCT)
2657 if (optimized == kUnoptimized) {
2658 __ Bind(&stepping);
2659 __ EnterStubFrame();
2660 if (type == kInstanceCall) {
2661 __ pushq(RDX); // Preserve receiver.
2662 }
2663 __ pushq(RBX); // Preserve ICData.
2664 if (save_entry_point) {
2665 __ SmiTag(R8); // Entry-point offset is not Smi.
2666 __ pushq(R8); // Preserve entry point.
2667 }
2668 __ CallRuntime(kSingleStepHandlerRuntimeEntry, 0);
2669 if (save_entry_point) {
2670 __ popq(R8); // Restore entry point.
2671 __ SmiUntag(R8);
2672 }
2673 __ popq(RBX); // Restore ICData.
2674 if (type == kInstanceCall) {
2675 __ popq(RDX); // Restore receiver.
2676 }
2677 __ RestoreCodePointer();
2678 __ LeaveStubFrame();
2679 __ jmp(&done_stepping);
2680 }
2681#endif
2682}
2683
2684// RDX: receiver
2685// RBX: ICData
2686// RSP[0]: return address
2687void StubCodeCompiler::GenerateOneArgCheckInlineCacheStub() {
2689 1, kInlineCacheMissHandlerOneArgRuntimeEntry, Token::kILLEGAL,
2691}
2692
2693// RDX: receiver
2694// RBX: ICData
2695// RSP[0]: return address
2696void StubCodeCompiler::GenerateOneArgCheckInlineCacheWithExactnessCheckStub() {
2698 1, kInlineCacheMissHandlerOneArgRuntimeEntry, Token::kILLEGAL,
2700}
2701
2702// RDX: receiver
2703// RBX: ICData
2704// RSP[0]: return address
2705void StubCodeCompiler::GenerateTwoArgsCheckInlineCacheStub() {
2707 2, kInlineCacheMissHandlerTwoArgsRuntimeEntry, Token::kILLEGAL,
2709}
2710
2711// RDX: receiver
2712// RBX: ICData
2713// RSP[0]: return address
2714void StubCodeCompiler::GenerateSmiAddInlineCacheStub() {
2716 2, kInlineCacheMissHandlerTwoArgsRuntimeEntry, Token::kADD, kUnoptimized,
2718}
2719
2720// RDX: receiver
2721// RBX: ICData
2722// RSP[0]: return address
2723void StubCodeCompiler::GenerateSmiLessInlineCacheStub() {
2725 2, kInlineCacheMissHandlerTwoArgsRuntimeEntry, Token::kLT, kUnoptimized,
2727}
2728
2729// RDX: receiver
2730// RBX: ICData
2731// RSP[0]: return address
2732void StubCodeCompiler::GenerateSmiEqualInlineCacheStub() {
2734 2, kInlineCacheMissHandlerTwoArgsRuntimeEntry, Token::kEQ, kUnoptimized,
2736}
2737
2738// RDX: receiver
2739// RBX: ICData
2740// RDI: Function
2741// RSP[0]: return address
2742void StubCodeCompiler::GenerateOneArgOptimizedCheckInlineCacheStub() {
2744 1, kInlineCacheMissHandlerOneArgRuntimeEntry, Token::kILLEGAL, kOptimized,
2746}
2747
2748// RDX: receiver
2749// RBX: ICData
2750// RDI: Function
2751// RSP[0]: return address
2752void StubCodeCompiler::
2753 GenerateOneArgOptimizedCheckInlineCacheWithExactnessCheckStub() {
2755 1, kInlineCacheMissHandlerOneArgRuntimeEntry, Token::kILLEGAL, kOptimized,
2757}
2758
2759// RDX: receiver
2760// RBX: ICData
2761// RDI: Function
2762// RSP[0]: return address
2763void StubCodeCompiler::GenerateTwoArgsOptimizedCheckInlineCacheStub() {
2765 2, kInlineCacheMissHandlerTwoArgsRuntimeEntry, Token::kILLEGAL,
2767}
2768
2769// RBX: ICData
2770// RSP[0]: return address
2771void StubCodeCompiler::GenerateZeroArgsUnoptimizedStaticCallStub() {
2772 GenerateRecordEntryPoint(assembler);
2773 GenerateUsageCounterIncrement(/* scratch */ RCX);
2774#if defined(DEBUG)
2775 {
2776 Label ok;
2777 // Check that the IC data array has NumArgsTested() == 0.
2778 // 'NumArgsTested' is stored in the least significant bits of 'state_bits'.
2779 __ movl(RCX, FieldAddress(RBX, target::ICData::state_bits_offset()));
2780 ASSERT(target::ICData::NumArgsTestedShift() == 0); // No shift needed.
2781 __ andq(RCX, Immediate(target::ICData::NumArgsTestedMask()));
2782 __ cmpq(RCX, Immediate(0));
2784 __ Stop("Incorrect IC data for unoptimized static call");
2785 __ Bind(&ok);
2786 }
2787#endif // DEBUG
2788
2789#if !defined(PRODUCT)
2790 // Check single stepping.
2791 Label stepping, done_stepping;
2792 __ LoadIsolate(RAX);
2793 __ movzxb(RAX, Address(RAX, target::Isolate::single_step_offset()));
2794 __ cmpq(RAX, Immediate(0));
2795#if defined(DEBUG)
2796 static auto const kJumpLength = Assembler::kFarJump;
2797#else
2798 static auto const kJumpLength = Assembler::kNearJump;
2799#endif // DEBUG
2800 __ j(NOT_EQUAL, &stepping, kJumpLength);
2801 __ Bind(&done_stepping);
2802#endif
2803
2804 // RBX: IC data object (preserved).
2805 __ movq(R12, FieldAddress(RBX, target::ICData::entries_offset()));
2806 // R12: ic_data_array with entries: target functions and count.
2807 __ leaq(R12, FieldAddress(R12, target::Array::data_offset()));
2808 // R12: points directly to the first ic data array element.
2809 const intptr_t target_offset =
2810 target::ICData::TargetIndexFor(0) * target::kCompressedWordSize;
2811 const intptr_t count_offset =
2812 target::ICData::CountIndexFor(0) * target::kCompressedWordSize;
2813
2814 if (FLAG_optimization_counter_threshold >= 0) {
2815 // Increment count for this call, ignore overflow.
2816 __ OBJ(add)(Address(R12, count_offset), Immediate(target::ToRawSmi(1)));
2817 }
2818
2819 // Load arguments descriptor into R10.
2820 __ movq(
2822 FieldAddress(RBX, target::CallSiteData::arguments_descriptor_offset()));
2823
2824 // Get function and call it, if possible.
2825 __ LoadCompressed(FUNCTION_REG, Address(R12, target_offset));
2826 __ LoadCompressed(
2827 CODE_REG, FieldAddress(FUNCTION_REG, target::Function::code_offset()));
2828
2829 __ addq(R8, FUNCTION_REG);
2830 __ jmp(Address(R8, 0));
2831
2832#if !defined(PRODUCT)
2833 __ Bind(&stepping);
2834 __ EnterStubFrame();
2835 __ pushq(RBX); // Preserve IC data object.
2836 __ SmiTag(R8); // Entry-point is not Smi.
2837 __ pushq(R8); // Preserve entry-point.
2838 __ CallRuntime(kSingleStepHandlerRuntimeEntry, 0);
2839 __ popq(R8); // Restore entry-point.
2840 __ SmiUntag(R8);
2841 __ popq(RBX);
2842 __ RestoreCodePointer();
2843 __ LeaveStubFrame();
2844 __ jmp(&done_stepping, Assembler::kNearJump);
2845#endif
2846}
2847
2848// RBX: ICData
2849// RSP[0]: return address
2850void StubCodeCompiler::GenerateOneArgUnoptimizedStaticCallStub() {
2851 GenerateNArgsCheckInlineCacheStub(1, kStaticCallMissHandlerOneArgRuntimeEntry,
2852 Token::kILLEGAL, kUnoptimized, kStaticCall,
2854}
2855
2856// RBX: ICData
2857// RSP[0]: return address
2858void StubCodeCompiler::GenerateTwoArgsUnoptimizedStaticCallStub() {
2860 2, kStaticCallMissHandlerTwoArgsRuntimeEntry, Token::kILLEGAL,
2862}
2863
2864// Stub for compiling a function and jumping to the compiled code.
2865// ARGS_DESC_REG: Arguments descriptor.
2866// FUNCTION_REG: Function.
2867void StubCodeCompiler::GenerateLazyCompileStub() {
2868 __ EnterStubFrame();
2869 __ pushq(ARGS_DESC_REG); // Preserve arguments descriptor array.
2870 __ pushq(FUNCTION_REG); // Pass function.
2871 __ CallRuntime(kCompileFunctionRuntimeEntry, 1);
2872 __ popq(FUNCTION_REG); // Restore function.
2873 __ popq(ARGS_DESC_REG); // Restore arguments descriptor array.
2874 __ LeaveStubFrame();
2875
2876 __ LoadCompressed(
2877 CODE_REG, FieldAddress(FUNCTION_REG, target::Function::code_offset()));
2878 __ movq(RCX,
2879 FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
2880 __ jmp(RCX);
2881}
2882
2883// RBX: Contains an ICData.
2884// TOS(0): return address (Dart code).
2885void StubCodeCompiler::GenerateICCallBreakpointStub() {
2886#if defined(PRODUCT)
2887 __ Stop("No debugging in PRODUCT mode");
2888#else
2889 __ EnterStubFrame();
2890 __ pushq(RDX); // Preserve receiver.
2891 __ pushq(RBX); // Preserve IC data.
2892 __ pushq(Immediate(0)); // Result slot.
2893 __ CallRuntime(kBreakpointRuntimeHandlerRuntimeEntry, 0);
2894 __ popq(CODE_REG); // Original stub.
2895 __ popq(RBX); // Restore IC data.
2896 __ popq(RDX); // Restore receiver.
2897 __ LeaveStubFrame();
2898
2899 __ movq(RAX, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
2900 __ jmp(RAX); // Jump to original stub.
2901#endif // defined(PRODUCT)
2902}
2903
2904void StubCodeCompiler::GenerateUnoptStaticCallBreakpointStub() {
2905#if defined(PRODUCT)
2906 __ Stop("No debugging in PRODUCT mode");
2907#else
2908 __ EnterStubFrame();
2909 __ pushq(RBX); // Preserve IC data.
2910 __ pushq(Immediate(0)); // Result slot.
2911 __ CallRuntime(kBreakpointRuntimeHandlerRuntimeEntry, 0);
2912 __ popq(CODE_REG); // Original stub.
2913 __ popq(RBX); // Restore IC data.
2914 __ LeaveStubFrame();
2915
2916 __ movq(RAX, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
2917 __ jmp(RAX); // Jump to original stub.
2918#endif // defined(PRODUCT)
2919}
2920
2921// TOS(0): return address (Dart code).
2922void StubCodeCompiler::GenerateRuntimeCallBreakpointStub() {
2923#if defined(PRODUCT)
2924 __ Stop("No debugging in PRODUCT mode");
2925#else
2926 __ EnterStubFrame();
2927 __ pushq(Immediate(0)); // Result slot.
2928 __ CallRuntime(kBreakpointRuntimeHandlerRuntimeEntry, 0);
2929 __ popq(CODE_REG); // Original stub.
2930 __ LeaveStubFrame();
2931
2932 __ movq(RAX, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
2933 __ jmp(RAX); // Jump to original stub.
2934#endif // defined(PRODUCT)
2935}
2936
2937// Called only from unoptimized code.
2938void StubCodeCompiler::GenerateDebugStepCheckStub() {
2939#if defined(PRODUCT)
2940 __ Stop("No debugging in PRODUCT mode");
2941#else
2942 // Check single stepping.
2943 Label stepping, done_stepping;
2944 __ LoadIsolate(RAX);
2945 __ movzxb(RAX, Address(RAX, target::Isolate::single_step_offset()));
2946 __ cmpq(RAX, Immediate(0));
2947 __ j(NOT_EQUAL, &stepping, Assembler::kNearJump);
2948 __ Bind(&done_stepping);
2949 __ ret();
2950
2951 __ Bind(&stepping);
2952 __ EnterStubFrame();
2953 __ CallRuntime(kSingleStepHandlerRuntimeEntry, 0);
2954 __ LeaveStubFrame();
2955 __ jmp(&done_stepping, Assembler::kNearJump);
2956#endif // defined(PRODUCT)
2957}
2958
2959// Used to check class and type arguments. Arguments passed in registers:
2960//
2961// Input registers (all preserved, from TypeTestABI struct):
2962// - kSubtypeTestCacheReg: UntaggedSubtypeTestCache
2963// - kInstanceReg: instance to test against (must be preserved).
2964// - kDstTypeReg: destination type (for n>=7).
2965// - kInstantiatorTypeArgumentsReg : instantiator type arguments (for n>=3).
2966// - kFunctionTypeArgumentsReg : function type arguments (for n>=4).
2967// Inputs from stack:
2968// - TOS + 0: return address.
2969//
2970// Outputs (from TypeTestABI struct):
2971// - kSubtypeTestCacheResultReg: the cached result, or null if not found.
2972void StubCodeCompiler::GenerateSubtypeNTestCacheStub(Assembler* assembler,
2973 int n) {
2974 ASSERT(n >= 1);
2976 // If we need the parent function type arguments for a closure, we also need
2977 // the delayed type arguments, so this case will never happen.
2978 ASSERT(n != 5);
2979 RegisterSet saved_registers;
2980
2981 // Until we have the result, we use the result register to store the null
2982 // value for quick access. This has the side benefit of initializing the
2983 // result to null, so it only needs to be changed if found.
2985 __ LoadObject(kNullReg, NullObject());
2986
2987 // Free up additional registers needed for checks in the loop. Initially
2988 // define them as kNoRegister so any unexpected uses are caught.
2989 Register kInstanceParentFunctionTypeArgumentsReg = kNoRegister;
2990 if (n >= 5) {
2991 kInstanceParentFunctionTypeArgumentsReg = PP;
2992 saved_registers.AddRegister(kInstanceParentFunctionTypeArgumentsReg);
2993 }
2994 Register kInstanceDelayedFunctionTypeArgumentsReg = kNoRegister;
2995 if (n >= 6) {
2996 kInstanceDelayedFunctionTypeArgumentsReg = CODE_REG;
2997 saved_registers.AddRegister(kInstanceDelayedFunctionTypeArgumentsReg);
2998 }
2999
3000 // We'll replace these with actual registers if possible, but fall back to
3001 // the stack if register pressure is too great. The last two values are
3002 // used in every loop iteration, and so are more important to put in
3003 // registers if possible, whereas the first is used only when we go off
3004 // the end of the backing array (usually at most once per check).
3005 Register kCacheContentsSizeReg = kNoRegister;
3006 if (n < 5) {
3007 // Use the register we would have used for the parent function type args.
3008 kCacheContentsSizeReg = PP;
3009 saved_registers.AddRegister(kCacheContentsSizeReg);
3010 }
3011 Register kProbeDistanceReg = kNoRegister;
3012 if (n < 6) {
3013 // Use the register we would have used for the delayed type args.
3014 kProbeDistanceReg = CODE_REG;
3015 saved_registers.AddRegister(kProbeDistanceReg);
3016 }
3017 Register kCacheEntryEndReg = kNoRegister;
3018 if (n < 2) {
3019 // This register isn't in use and doesn't require saving/restoring.
3021 } else if (n < 7) {
3022 // Use the destination type, as that is the last input that might be unused.
3023 kCacheEntryEndReg = TypeTestABI::kDstTypeReg;
3024 saved_registers.AddRegister(TypeTestABI::kDstTypeReg);
3025 }
3026
3027 __ PushRegisters(saved_registers);
3028
3029 Label done;
3030 GenerateSubtypeTestCacheSearch(
3034 kInstanceParentFunctionTypeArgumentsReg,
3035 kInstanceDelayedFunctionTypeArgumentsReg, kCacheEntryEndReg,
3036 kCacheContentsSizeReg, kProbeDistanceReg,
3037 [&](Assembler* assembler, int n) {
3040 target::kCompressedWordSize *
3041 target::SubtypeTestCache::kTestResult));
3042 __ PopRegisters(saved_registers);
3043 __ Ret();
3044 },
3045 [&](Assembler* assembler, int n) {
3046 // We initialize kSubtypeTestCacheResultReg to null so it can be used
3047 // for null checks, so the result value is already set.
3048 __ PopRegisters(saved_registers);
3049 __ Ret();
3050 });
3051}
3052
3053// Return the current stack pointer address, used to stack alignment
3054// checks.
3055// TOS + 0: return address
3056// Result in RAX.
3057void StubCodeCompiler::GenerateGetCStackPointerStub() {
3058 __ leaq(RAX, Address(RSP, target::kWordSize));
3059 __ ret();
3060}
3061
3062// Jump to a frame on the call stack.
3063// TOS + 0: return address
3064// Arg1: program counter
3065// Arg2: stack pointer
3066// Arg3: frame_pointer
3067// Arg4: thread
3068// No Result.
3069void StubCodeCompiler::GenerateJumpToFrameStub() {
3073#if defined(USING_SHADOW_CALL_STACK)
3074#error Unimplemented
3075#endif
3076 Label exit_through_non_ffi;
3077 // Check if we exited generated from FFI. If so do transition - this is needed
3078 // because normally runtime calls transition back to generated via destructor
3079 // of TransitionGeneratedToVM/Native that is part of runtime boilerplate
3080 // code (see DEFINE_RUNTIME_ENTRY_IMPL in runtime_entry.h). Ffi calls don't
3081 // have this boilerplate, don't have this stack resource, have to transition
3082 // explicitly.
3083 __ cmpq(compiler::Address(
3084 THR, compiler::target::Thread::exit_through_ffi_offset()),
3085 compiler::Immediate(target::Thread::exit_through_ffi()));
3086 __ j(NOT_EQUAL, &exit_through_non_ffi, compiler::Assembler::kNearJump);
3087 __ TransitionNativeToGenerated(/*leave_safepoint=*/true,
3088 /*ignore_unwind_in_progress=*/true);
3089 __ Bind(&exit_through_non_ffi);
3090
3091 // Set the tag.
3092 __ movq(Assembler::VMTagAddress(), Immediate(VMTag::kDartTagId));
3093 // Clear top exit frame.
3094 __ movq(Address(THR, target::Thread::top_exit_frame_info_offset()),
3095 Immediate(0));
3096 // Restore the pool pointer.
3097 __ RestoreCodePointer();
3098 if (FLAG_precompiled_mode) {
3099 __ movq(PP, Address(THR, target::Thread::global_object_pool_offset()));
3100 } else {
3101 __ LoadPoolPointer(PP);
3102 }
3103 __ jmp(CallingConventions::kArg1Reg); // Jump to program counter.
3104}
3105
3106// Run an exception handler. Execution comes from JumpToFrame stub.
3107//
3108// The arguments are stored in the Thread object.
3109// No result.
3110void StubCodeCompiler::GenerateRunExceptionHandlerStub() {
3114 Address(THR, target::Thread::resume_pc_offset()));
3115
3116 word offset_from_thread = 0;
3117 bool ok = target::CanLoadFromThread(NullObject(), &offset_from_thread);
3118 ASSERT(ok);
3119 __ movq(TMP, Address(THR, offset_from_thread));
3120
3121 // Load the exception from the current thread.
3122 Address exception_addr(THR, target::Thread::active_exception_offset());
3123 __ movq(kExceptionObjectReg, exception_addr);
3124 __ movq(exception_addr, TMP);
3125
3126 // Load the stacktrace from the current thread.
3127 Address stacktrace_addr(THR, target::Thread::active_stacktrace_offset());
3128 __ movq(kStackTraceObjectReg, stacktrace_addr);
3129 __ movq(stacktrace_addr, TMP);
3130
3131 __ jmp(CallingConventions::kArg1Reg); // Jump to continuation point.
3132}
3133
3134// Deoptimize a frame on the call stack before rewinding.
3135// The arguments are stored in the Thread object.
3136// No result.
3137void StubCodeCompiler::GenerateDeoptForRewindStub() {
3138 // Push zap value instead of CODE_REG.
3139 __ pushq(Immediate(kZapCodeReg));
3140
3141 // Push the deopt pc.
3142 __ pushq(Address(THR, target::Thread::resume_pc_offset()));
3143#if defined(USING_SHADOW_CALL_STACK)
3144#error Unimplemented
3145#endif
3146 GenerateDeoptimizationSequence(assembler, kEagerDeopt);
3147
3148 // After we have deoptimized, jump to the correct frame.
3149 __ EnterStubFrame();
3150 __ CallRuntime(kRewindPostDeoptRuntimeEntry, 0);
3151 __ LeaveStubFrame();
3152 __ int3();
3153}
3154
3155// Calls to the runtime to optimize the given function.
3156// RDI: function to be reoptimized.
3157// ARGS_DESC_REG: argument descriptor (preserved).
3158void StubCodeCompiler::GenerateOptimizeFunctionStub() {
3159 __ movq(CODE_REG, Address(THR, target::Thread::optimize_stub_offset()));
3160 __ EnterStubFrame();
3161 __ pushq(ARGS_DESC_REG); // Preserve args descriptor.
3162 __ pushq(Immediate(0)); // Result slot.
3163 __ pushq(RDI); // Arg0: function to optimize
3164 __ CallRuntime(kOptimizeInvokedFunctionRuntimeEntry, 1);
3165 __ popq(RAX); // Discard argument.
3166 __ popq(FUNCTION_REG); // Get Function object.
3167 __ popq(ARGS_DESC_REG); // Restore argument descriptor.
3168 __ LeaveStubFrame();
3169 __ LoadCompressed(
3170 CODE_REG, FieldAddress(FUNCTION_REG, target::Function::code_offset()));
3171 __ movq(RCX,
3172 FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
3173 __ jmp(RCX);
3174 __ int3();
3175}
3176
3177// Does identical check (object references are equal or not equal) with special
3178// checks for boxed numbers.
3179// Left and right are pushed on stack.
3180// Return ZF set.
3181// Note: A Mint cannot contain a value that would fit in Smi.
3182static void GenerateIdenticalWithNumberCheckStub(Assembler* assembler,
3183 const Register left,
3184 const Register right) {
3185 Label reference_compare, done, check_mint;
3186 // If any of the arguments is Smi do reference compare.
3187 __ testq(left, Immediate(kSmiTagMask));
3188 __ j(ZERO, &reference_compare);
3189 __ testq(right, Immediate(kSmiTagMask));
3190 __ j(ZERO, &reference_compare);
3191
3192 // Value compare for two doubles.
3193 __ CompareClassId(left, kDoubleCid);
3194 __ j(NOT_EQUAL, &check_mint, Assembler::kNearJump);
3195 __ CompareClassId(right, kDoubleCid);
3197
3198 // Double values bitwise compare.
3199 __ movq(left, FieldAddress(left, target::Double::value_offset()));
3200 __ cmpq(left, FieldAddress(right, target::Double::value_offset()));
3202
3203 __ Bind(&check_mint);
3204 __ CompareClassId(left, kMintCid);
3205 __ j(NOT_EQUAL, &reference_compare, Assembler::kNearJump);
3206 __ CompareClassId(right, kMintCid);
3208 __ movq(left, FieldAddress(left, target::Mint::value_offset()));
3209 __ cmpq(left, FieldAddress(right, target::Mint::value_offset()));
3211
3212 __ Bind(&reference_compare);
3213 __ CompareObjectRegisters(left, right);
3214 __ Bind(&done);
3215}
3216
3217// Called only from unoptimized code. All relevant registers have been saved.
3218// TOS + 0: return address
3219// TOS + 1: right argument.
3220// TOS + 2: left argument.
3221// Returns ZF set.
3222void StubCodeCompiler::GenerateUnoptimizedIdenticalWithNumberCheckStub() {
3223#if !defined(PRODUCT)
3224 // Check single stepping.
3225 Label stepping, done_stepping;
3226 __ LoadIsolate(RAX);
3227 __ movzxb(RAX, Address(RAX, target::Isolate::single_step_offset()));
3228 __ cmpq(RAX, Immediate(0));
3229 __ j(NOT_EQUAL, &stepping);
3230 __ Bind(&done_stepping);
3231#endif
3232
3233 const Register left = RAX;
3234 const Register right = RDX;
3235
3236 __ movq(left, Address(RSP, 2 * target::kWordSize));
3237 __ movq(right, Address(RSP, 1 * target::kWordSize));
3238 GenerateIdenticalWithNumberCheckStub(assembler, left, right);
3239 __ ret();
3240
3241#if !defined(PRODUCT)
3242 __ Bind(&stepping);
3243 __ EnterStubFrame();
3244 __ CallRuntime(kSingleStepHandlerRuntimeEntry, 0);
3245 __ RestoreCodePointer();
3246 __ LeaveStubFrame();
3247 __ jmp(&done_stepping);
3248#endif
3249}
3250
3251// Called from optimized code only.
3252// TOS + 0: return address
3253// TOS + 1: right argument.
3254// TOS + 2: left argument.
3255// Returns ZF set.
3256void StubCodeCompiler::GenerateOptimizedIdenticalWithNumberCheckStub() {
3257 const Register left = RAX;
3258 const Register right = RDX;
3259
3260 __ movq(left, Address(RSP, 2 * target::kWordSize));
3261 __ movq(right, Address(RSP, 1 * target::kWordSize));
3262 GenerateIdenticalWithNumberCheckStub(assembler, left, right);
3263 __ ret();
3264}
3265
3266// Called from megamorphic calls.
3267// RDX: receiver (passed to target)
3268// IC_DATA_REG: target::MegamorphicCache (preserved)
3269// Passed to target:
3270// FUNCTION_REG: target function
3271// CODE_REG: target Code
3272// ARGS_DESC_REG: arguments descriptor
3273void StubCodeCompiler::GenerateMegamorphicCallStub() {
3274 // Jump if receiver is a smi.
3275 Label smi_case;
3276 __ testq(RDX, Immediate(kSmiTagMask));
3277 // Jump out of line for smi case.
3278 __ j(ZERO, &smi_case, Assembler::kNearJump);
3279
3280 // Loads the cid of the object.
3281 __ LoadClassId(RAX, RDX);
3282
3283 Label cid_loaded;
3284 __ Bind(&cid_loaded);
3285 __ movq(R9,
3286 FieldAddress(IC_DATA_REG, target::MegamorphicCache::mask_offset()));
3287 __ movq(RDI, FieldAddress(IC_DATA_REG,
3288 target::MegamorphicCache::buckets_offset()));
3289 // R9: mask as a smi.
3290 // RDI: cache buckets array.
3291
3292 // Tag cid as a smi.
3293 __ addq(RAX, RAX);
3294
3295 // Compute the table index.
3296 ASSERT(target::MegamorphicCache::kSpreadFactor == 7);
3297 // Use leaq and subq multiply with 7 == 8 - 1.
3298 __ leaq(RCX, Address(RAX, TIMES_8, 0));
3299 __ subq(RCX, RAX);
3300
3301 Label loop;
3302 __ Bind(&loop);
3303 __ andq(RCX, R9);
3304
3305 const intptr_t base = target::Array::data_offset();
3306 // RCX is smi tagged, but table entries are two words, so TIMES_8.
3307 Label probe_failed;
3308 __ OBJ(cmp)(RAX, FieldAddress(RDI, RCX, TIMES_COMPRESSED_WORD_SIZE, base));
3309 __ j(NOT_EQUAL, &probe_failed, Assembler::kNearJump);
3310
3311 Label load_target;
3312 __ Bind(&load_target);
3313 // Call the target found in the cache. For a class id match, this is a
3314 // proper target for the given name and arguments descriptor. If the
3315 // illegal class id was found, the target is a cache miss handler that can
3316 // be invoked as a normal Dart function.
3317 __ LoadCompressed(FUNCTION_REG,
3318 FieldAddress(RDI, RCX, TIMES_COMPRESSED_WORD_SIZE,
3319 base + target::kCompressedWordSize));
3320 __ movq(ARGS_DESC_REG,
3321 FieldAddress(IC_DATA_REG,
3322 target::CallSiteData::arguments_descriptor_offset()));
3323 __ movq(RCX,
3324 FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
3325 if (!FLAG_precompiled_mode) {
3326 __ LoadCompressed(
3327 CODE_REG, FieldAddress(FUNCTION_REG, target::Function::code_offset()));
3328 }
3329 __ jmp(RCX);
3330
3331 // Probe failed, check if it is a miss.
3332 __ Bind(&probe_failed);
3333 __ OBJ(cmp)(FieldAddress(RDI, RCX, TIMES_COMPRESSED_WORD_SIZE, base),
3334 Immediate(target::ToRawSmi(kIllegalCid)));
3335 Label miss;
3336 __ j(ZERO, &miss, Assembler::kNearJump);
3337
3338 // Try next entry in the table.
3339 __ AddImmediate(RCX, Immediate(target::ToRawSmi(1)));
3340 __ jmp(&loop);
3341
3342 // Load cid for the Smi case.
3343 __ Bind(&smi_case);
3344 __ movq(RAX, Immediate(kSmiCid));
3345 __ jmp(&cid_loaded);
3346
3347 __ Bind(&miss);
3348 GenerateSwitchableCallMissStub();
3349}
3350
3351// Input:
3352// IC_DATA_REG - icdata
3353// RDX - receiver object
3354void StubCodeCompiler::GenerateICCallThroughCodeStub() {
3355 Label loop, found, miss;
3356 __ movq(R13, FieldAddress(IC_DATA_REG, target::ICData::entries_offset()));
3357 __ movq(ARGS_DESC_REG,
3358 FieldAddress(IC_DATA_REG,
3359 target::CallSiteData::arguments_descriptor_offset()));
3360 __ leaq(R13, FieldAddress(R13, target::Array::data_offset()));
3361 // R13: first IC entry
3362 __ LoadTaggedClassIdMayBeSmi(RAX, RDX);
3363 // RAX: receiver cid as Smi
3364
3365 __ Bind(&loop);
3366 __ OBJ(mov)(R9, Address(R13, 0));
3367 __ OBJ(cmp)(RAX, R9);
3368 __ j(EQUAL, &found, Assembler::kNearJump);
3369
3371 __ OBJ(test)(R9, R9);
3372 __ j(ZERO, &miss, Assembler::kNearJump);
3373
3374 const intptr_t entry_length =
3375 target::ICData::TestEntryLengthFor(1, /*tracking_exactness=*/false) *
3376 target::kCompressedWordSize;
3377 __ addq(R13, Immediate(entry_length)); // Next entry.
3378 __ jmp(&loop);
3379
3380 __ Bind(&found);
3381 if (FLAG_precompiled_mode) {
3382 const intptr_t entry_offset =
3383 target::ICData::EntryPointIndexFor(1) * target::kCompressedWordSize;
3384 __ LoadCompressed(RCX, Address(R13, entry_offset));
3385 __ jmp(FieldAddress(RCX, target::Function::entry_point_offset()));
3386 } else {
3387 const intptr_t code_offset =
3388 target::ICData::CodeIndexFor(1) * target::kCompressedWordSize;
3389 __ LoadCompressed(CODE_REG, Address(R13, code_offset));
3390 __ jmp(FieldAddress(CODE_REG, target::Code::entry_point_offset()));
3391 }
3392
3393 __ Bind(&miss);
3394 __ jmp(Address(THR, target::Thread::switchable_call_miss_entry_offset()));
3395}
3396
3397void StubCodeCompiler::GenerateMonomorphicSmiableCheckStub() {
3398 Label have_cid, miss;
3399
3400 __ movq(RAX, Immediate(kSmiCid));
3401 __ movzxw(
3402 RCX,
3403 FieldAddress(RBX, target::MonomorphicSmiableCall::expected_cid_offset()));
3404 __ testq(RDX, Immediate(kSmiTagMask));
3405 __ j(ZERO, &have_cid, Assembler::kNearJump);
3406 __ LoadClassId(RAX, RDX);
3407 __ Bind(&have_cid);
3408 __ cmpq(RAX, RCX);
3410 // Note: this stub is only used in AOT mode, hence the direct (bare) call.
3411 __ jmp(
3412 FieldAddress(RBX, target::MonomorphicSmiableCall::entrypoint_offset()));
3413
3414 __ Bind(&miss);
3415 __ jmp(Address(THR, target::Thread::switchable_call_miss_entry_offset()));
3416}
3417
3418// Called from switchable IC calls.
3419// RDX: receiver
3420void StubCodeCompiler::GenerateSwitchableCallMissStub() {
3421 __ movq(CODE_REG,
3422 Address(THR, target::Thread::switchable_call_miss_stub_offset()));
3423 __ EnterStubFrame();
3424 __ pushq(RDX); // Preserve receiver.
3425
3426 __ pushq(Immediate(0)); // Result slot.
3427 __ pushq(Immediate(0)); // Arg0: stub out.
3428 __ pushq(RDX); // Arg1: Receiver
3429 __ CallRuntime(kSwitchableCallMissRuntimeEntry, 2);
3430 __ popq(RBX);
3431 __ popq(CODE_REG); // result = stub
3432 __ popq(RBX); // result = IC
3433
3434 __ popq(RDX); // Restore receiver.
3435 __ LeaveStubFrame();
3436
3437 __ movq(RCX, FieldAddress(CODE_REG, target::Code::entry_point_offset(
3439 __ jmp(RCX);
3440}
3441
3442// Called from switchable IC calls.
3443// RDX: receiver
3444// RBX: SingleTargetCache
3445// Passed to target::
3446// CODE_REG: target Code object
3447void StubCodeCompiler::GenerateSingleTargetCallStub() {
3448 Label miss;
3449 __ LoadClassIdMayBeSmi(RAX, RDX);
3450 __ movzxw(R9,
3451 FieldAddress(RBX, target::SingleTargetCache::lower_limit_offset()));
3452 __ movzxw(R10,
3453 FieldAddress(RBX, target::SingleTargetCache::upper_limit_offset()));
3454 __ cmpq(RAX, R9);
3455 __ j(LESS, &miss, Assembler::kNearJump);
3456 __ cmpq(RAX, R10);
3457 __ j(GREATER, &miss, Assembler::kNearJump);
3458 __ movq(RCX,
3459 FieldAddress(RBX, target::SingleTargetCache::entry_point_offset()));
3460 __ movq(CODE_REG,
3461 FieldAddress(RBX, target::SingleTargetCache::target_offset()));
3462 __ jmp(RCX);
3463
3464 __ Bind(&miss);
3465 __ EnterStubFrame();
3466 __ pushq(RDX); // Preserve receiver.
3467
3468 __ pushq(Immediate(0)); // Result slot.
3469 __ pushq(Immediate(0)); // Arg0: stub out
3470 __ pushq(RDX); // Arg1: Receiver
3471 __ CallRuntime(kSwitchableCallMissRuntimeEntry, 2);
3472 __ popq(RBX);
3473 __ popq(CODE_REG); // result = stub
3474 __ popq(RBX); // result = IC
3475
3476 __ popq(RDX); // Restore receiver.
3477 __ LeaveStubFrame();
3478
3479 __ movq(RCX, FieldAddress(CODE_REG, target::Code::entry_point_offset(
3481 __ jmp(RCX);
3482}
3483
3484static ScaleFactor GetScaleFactor(intptr_t size) {
3485 switch (size) {
3486 case 1:
3487 return TIMES_1;
3488 case 2:
3489 return TIMES_2;
3490 case 4:
3491 return TIMES_4;
3492 case 8:
3493 return TIMES_8;
3494 case 16:
3495 return TIMES_16;
3496 }
3497 UNREACHABLE();
3498 return static_cast<ScaleFactor>(0);
3499}
3500
3501void StubCodeCompiler::GenerateAllocateTypedDataArrayStub(intptr_t cid) {
3503 const intptr_t max_len = TypedDataMaxNewSpaceElements(cid);
3504 ScaleFactor scale_factor = GetScaleFactor(element_size);
3505
3508
3509 if (!FLAG_use_slow_path && FLAG_inline_alloc) {
3510 // Save length argument for possible runtime call, as
3511 // RAX is clobbered.
3512 Label call_runtime;
3514
3515 NOT_IN_PRODUCT(__ MaybeTraceAllocation(cid, &call_runtime));
3517 /* Check that length is a positive Smi. */
3518 /* RDI: requested array length argument. */
3519 __ testq(RDI, Immediate(kSmiTagMask));
3520 __ j(NOT_ZERO, &call_runtime);
3521 __ SmiUntag(RDI);
3522 /* Check for length >= 0 && length <= max_len. */
3523 /* RDI: untagged array length. */
3524 __ cmpq(RDI, Immediate(max_len));
3525 __ j(ABOVE, &call_runtime);
3526 /* Special case for scaling by 16. */
3527 if (scale_factor == TIMES_16) {
3528 /* double length of array. */
3529 __ addq(RDI, RDI);
3530 /* only scale by 8. */
3531 scale_factor = TIMES_8;
3532 }
3533 const intptr_t fixed_size_plus_alignment_padding =
3534 target::TypedData::HeaderSize() +
3536 __ leaq(RDI, Address(RDI, scale_factor, fixed_size_plus_alignment_padding));
3538 __ movq(RAX, Address(THR, target::Thread::top_offset()));
3539 __ movq(RCX, RAX);
3540
3541 /* RDI: allocation size. */
3542 __ addq(RCX, RDI);
3543 __ j(CARRY, &call_runtime);
3544
3545 /* Check if the allocation fits into the remaining space. */
3546 /* RAX: potential new object start. */
3547 /* RCX: potential next object start. */
3548 /* RDI: allocation size. */
3549 __ cmpq(RCX, Address(THR, target::Thread::end_offset()));
3550 __ j(ABOVE_EQUAL, &call_runtime);
3551 __ CheckAllocationCanary(RAX);
3552
3553 /* Successfully allocated the object(s), now update top to point to */
3554 /* next object start and initialize the object. */
3555 __ movq(Address(THR, target::Thread::top_offset()), RCX);
3556 __ addq(RAX, Immediate(kHeapObjectTag));
3557 /* Initialize the tags. */
3558 /* RAX: new object start as a tagged pointer. */
3559 /* RCX: new object end address. */
3560 /* RDI: allocation size. */
3561 /* R13: scratch register. */
3562 {
3563 Label size_tag_overflow, done;
3564 __ cmpq(RDI, Immediate(target::UntaggedObject::kSizeTagMaxSizeTag));
3565 __ j(ABOVE, &size_tag_overflow, Assembler::kNearJump);
3566 __ shlq(RDI, Immediate(target::UntaggedObject::kTagBitsSizeTagPos -
3569
3570 __ Bind(&size_tag_overflow);
3571 __ LoadImmediate(RDI, Immediate(0));
3572 __ Bind(&done);
3573
3574 /* Get the class index and insert it into the tags. */
3575 uword tags =
3576 target::MakeTagWordForNewSpaceObject(cid, /*instance_size=*/0);
3577 __ orq(RDI, Immediate(tags));
3578 __ movq(FieldAddress(RAX, target::Object::tags_offset()),
3579 RDI); /* Tags. */
3580 }
3581 /* Set the length field. */
3582 /* RAX: new object start as a tagged pointer. */
3583 /* RCX: new object end address. */
3584 __ popq(RDI); /* Array length. */
3585 __ StoreCompressedIntoObjectNoBarrier(
3586 RAX, FieldAddress(RAX, target::TypedDataBase::length_offset()), RDI);
3587 /* Initialize all array elements to 0. */
3588 /* RAX: new object start as a tagged pointer. */
3589 /* RCX: new object end address. */
3590 /* RDI: iterator which initially points to the start of the variable */
3591 /* RBX: scratch register. */
3592 /* data area to be initialized. */
3593 __ pxor(XMM0, XMM0); /* Zero. */
3594 __ leaq(RDI, FieldAddress(RAX, target::TypedData::HeaderSize()));
3595 __ StoreInternalPointer(
3596 RAX, FieldAddress(RAX, target::PointerBase::data_offset()), RDI);
3597 Label loop;
3598 __ Bind(&loop);
3599 ASSERT(target::kObjectAlignment == kFpuRegisterSize);
3600 __ movups(Address(RDI, 0), XMM0);
3601 // Safe to only check every kObjectAlignment bytes instead of each word.
3602 ASSERT(kAllocationRedZoneSize >= target::kObjectAlignment);
3603 __ addq(RDI, Immediate(target::kObjectAlignment));
3604 __ cmpq(RDI, RCX);
3606
3607 __ WriteAllocationCanary(RCX); // Fix overshoot.
3608 __ ret();
3609
3610 __ Bind(&call_runtime);
3612 }
3613
3614 __ EnterStubFrame();
3615 __ PushObject(Object::null_object()); // Make room for the result.
3616 __ PushImmediate(Immediate(target::ToRawSmi(cid)));
3618 __ CallRuntime(kAllocateTypedDataRuntimeEntry, 2);
3619 __ Drop(2); // Drop arguments.
3621 __ LeaveStubFrame();
3622 __ ret();
3623}
3624
3625} // namespace compiler
3626
3627} // namespace dart
3628
3629#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 async_callback(void *c, std::unique_ptr< const SkImage::AsyncReadResult > result)
static bool ok(int result)
static SkTileMode optimize(SkTileMode tm, int dimension)
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
static size_t element_size(Layout layout, SkSLType type)
#define __
#define OBJ(op)
#define UNREACHABLE()
Definition assert.h:248
#define ASSERT_LESS_OR_EQUAL(expected, actual)
Definition assert.h:313
#define ASSERT_EQUAL(expected, actual)
Definition assert.h:309
#define RELEASE_ASSERT(cond)
Definition assert.h:327
#define COMPILE_ASSERT(expr)
Definition assert.h:339
virtual bool WillAllocateNewOrRemembered() const
Definition il.h:7412
static constexpr intptr_t kCalleeSaveCpuRegisters
static constexpr intptr_t kVolatileCpuRegisters
static constexpr intptr_t kFpuArgumentRegisters
static constexpr Register kArg3Reg
static constexpr Register kArg1Reg
static constexpr intptr_t kArgumentRegisters
static constexpr Register kArg2Reg
static constexpr Register kArg4Reg
static constexpr intptr_t kCalleeSaveXmmRegisters
static constexpr intptr_t NumCallbackTrampolinesPerPage()
static constexpr intptr_t kPageMask
static constexpr uword RuntimeFunctionOffset(uword function_index)
static constexpr intptr_t kPageSize
static bool UseUnboxedRepresentation()
Definition il.h:10810
static intptr_t ActivationFrameAlignment()
static intptr_t pointers_offset()
static intptr_t top_offset()
static StaticTypeExactnessState HasExactSuperType()
static StaticTypeExactnessState NotExact()
static constexpr intptr_t kMaxInputs
Definition object.h:7676
static constexpr int CountOneBitsWord(uword x)
Definition utils.h:161
static Address AddressRIPRelative(int32_t disp)
static Address VMTagAddress()
void GenerateNArgsCheckInlineCacheStub(intptr_t num_args, const RuntimeEntry &handle_ic_miss, Token::Kind kind, Optimized optimized, CallType type, Exactness exactness)
void GenerateUsageCounterIncrement(Register temp_reg)
void GenerateAllocationStubForClass(UnresolvedPcRelativeCalls *unresolved_calls, const Class &cls, const dart::Code &allocate_object, const dart::Code &allocat_object_parametrized)
static intptr_t WordOffsetFromFpToCpuRegister(Register cpu_register)
#define UNIMPLEMENTED
#define ASSERT(E)
glong glong end
const uint8_t uint32_t uint32_t GError ** error
uint32_t * target
size_t length
uword MakeTagWordForNewSpaceObject(classid_t cid, uword instance_size)
bool CanLoadFromThread(const dart::Object &object, intptr_t *offset)
word ToRawSmi(const dart::Object &a)
bool SizeFitsInSizeTag(uword instance_size)
word TypedDataMaxNewSpaceElements(classid_t cid)
word TypedDataElementSizeInBytes(classid_t cid)
GrowableArray< UnresolvedPcRelativeCall * > UnresolvedPcRelativeCalls
bool IsSameObject(const Object &a, const Object &b)
const Object & NullObject()
const Code & StubCodeAllocateArray()
const Class & MintClass()
const Register kWriteBarrierSlotReg
@ TIMES_COMPRESSED_HALF_WORD_SIZE
@ TIMES_COMPRESSED_WORD_SIZE
const Register THR
const Register kExceptionObjectReg
const Register kWriteBarrierObjectReg
Thread * DLRT_GetFfiCallbackMetadata(FfiCallbackMetadata::Trampoline trampoline, uword *out_entry_point, uword *out_trampoline_type)
const RegList kAllFpuRegistersList
const Register kWriteBarrierValueReg
static constexpr bool IsCalleeSavedRegister(Register reg)
Definition constants.h:85
static constexpr uword kZapReturnAddress
int32_t classid_t
Definition globals.h:524
@ kIllegalCid
Definition class_id.h:214
@ kHeapObjectTag
uintptr_t uword
Definition globals.h:501
void DLRT_ExitTemporaryIsolate()
intptr_t word
Definition globals.h:500
const Register CODE_REG
@ GREATER_EQUAL
@ UNSIGNED_GREATER_EQUAL
@ NO_OVERFLOW
@ UNSIGNED_LESS
@ ABOVE_EQUAL
const Register ARGS_DESC_REG
@ kNumberOfCpuRegisters
@ kNoRegister
const int kNumberOfFpuRegisters
static constexpr bool IsArgumentRegister(Register reg)
Definition constants.h:77
constexpr RegList kDartAvailableCpuRegs
const Register TMP
const Register FPREG
const intptr_t cid
const intptr_t kStoreBufferWrapperSize
const Register FUNCTION_REG
const Register IC_DATA_REG
static constexpr intptr_t kAllocationRedZoneSize
Definition page.h:41
const Register PP
static constexpr uword kZapCodeReg
const Register kStackTraceObjectReg
const int kFpuRegisterSize
ByteRegister ByteRegisterOf(Register reg)
@ kNumberOfXmmRegisters
Point 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 kTagsReg
static constexpr Register kLengthReg
static constexpr Register kResultReg
static constexpr Register kClassIdReg
static constexpr intptr_t kObjectAlignmentLog2
static constexpr intptr_t kObjectAlignment
static constexpr Register kLengthReg
static constexpr Register kIndexReg
static constexpr Register kInstanceInstantiatorTypeArgumentsReg
static constexpr Register kInstanceCidOrSignatureReg
static constexpr Register kCacheEntryReg
static constexpr Register kResultReg
static constexpr Register kDstTypeReg
static constexpr Register kSubtypeTestCacheResultReg
#define NOT_IN_PRODUCT(code)
Definition globals.h:84