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