Flutter Engine
The Flutter Engine
stub_code_compiler_riscv.cc
Go to the documentation of this file.
1// Copyright (c) 2021, 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_RISCV32) || defined(TARGET_ARCH_RISCV64)
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 [A0] 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 [A0], [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, A0, target::kPageMask);
44 __ LoadFromOffset(TMP, TMP, target::Page::original_top_offset());
45 __ CompareRegisters(A0, TMP);
46 __ BranchIf(UNSIGNED_GREATER_EQUAL, &done);
47
48 {
49 LeafRuntimeScope rt(assembler, /*frame_size=*/0,
50 /*preserve_registers=*/false);
51 // A0 already loaded.
52 __ mv(A1, THR);
53 rt.Call(kEnsureRememberedAndMarkingDeferredRuntimeEntry,
54 /*argument_count=*/2);
55 }
56
57 __ Bind(&done);
58}
59
60// Input parameters:
61// RA : return address.
62// SP : address of last argument in argument array.
63// SP + 8*T4 - 8 : address of first argument in argument array.
64// SP + 8*T4 : address of return value.
65// T5 : address of the runtime function to call.
66// T4 : number of arguments to the call.
67void StubCodeCompiler::GenerateCallToRuntimeStub() {
68 const intptr_t thread_offset = target::NativeArguments::thread_offset();
69 const intptr_t argc_tag_offset = target::NativeArguments::argc_tag_offset();
70 const intptr_t argv_offset = target::NativeArguments::argv_offset();
71 const intptr_t retval_offset = target::NativeArguments::retval_offset();
72
73 __ Comment("CallToRuntimeStub");
75 __ SetPrologueOffset();
76 __ EnterStubFrame();
77
78 // Save exit frame information to enable stack walking as we are about
79 // to transition to Dart VM C++ code.
81
82 // Mark that the thread exited generated code through a runtime call.
85
86#if defined(DEBUG)
87 {
88 Label ok;
89 // Check that we are always entering from Dart code.
90 __ LoadFromOffset(TMP, THR, target::Thread::vm_tag_offset());
91 __ CompareImmediate(TMP, VMTag::kDartTagId);
92 __ BranchIf(EQ, &ok);
93 __ Stop("Not coming from Dart code.");
94 __ Bind(&ok);
95 }
96#endif
97
98 // Mark that the thread is executing VM code.
99 __ StoreToOffset(T5, THR, target::Thread::vm_tag_offset());
100
101 // Reserve space for arguments and align frame before entering C++ world.
102 // target::NativeArguments are passed in registers.
103 __ Comment("align stack");
104 // Reserve space for arguments.
106 __ ReserveAlignedFrameSpace(target::NativeArguments::StructSize());
107
108 // Pass target::NativeArguments structure by value and call runtime.
109 // Registers R0, R1, R2, and R3 are used.
110
111 ASSERT(thread_offset == 0 * target::kWordSize);
112 ASSERT(argc_tag_offset == 1 * target::kWordSize);
113 ASSERT(argv_offset == 2 * target::kWordSize);
115 __ add(T2, FP, T2); // Compute argv.
116 // Set argv in target::NativeArguments.
117 __ AddImmediate(T2,
118 target::frame_layout.param_end_from_fp * target::kWordSize);
119
120 ASSERT(retval_offset == 3 * target::kWordSize);
121 __ AddImmediate(T3, T2, target::kWordSize);
122
123 __ StoreToOffset(THR, SP, thread_offset);
124 __ StoreToOffset(T4, SP, argc_tag_offset);
125 __ StoreToOffset(T2, SP, argv_offset);
126 __ StoreToOffset(T3, SP, retval_offset);
127 __ mv(A0, SP); // Pass the pointer to the target::NativeArguments.
128
130 __ jalr(T5);
131 __ Comment("CallToRuntimeStub return");
132
133 // Refresh pinned registers values (inc. write barrier mask and null object).
134 __ RestorePinnedRegisters();
135
136 // Retval is next to 1st argument.
137 // Mark that the thread is executing Dart code.
138 __ LoadImmediate(TMP, VMTag::kDartTagId);
139 __ StoreToOffset(TMP, THR, target::Thread::vm_tag_offset());
140
141 // Mark that the thread has not exited generated Dart code.
143
144 // Reset exit frame information in Isolate's mutator thread structure.
146
147 // Restore the global object pool after returning from runtime (old space is
148 // moving, so the GOP could have been relocated).
149 if (FLAG_precompiled_mode) {
150 __ SetupGlobalPoolAndDispatchTable();
151 }
152
153 __ LeaveStubFrame();
154
155 // The following return can jump to a lazy-deopt stub, which assumes A0
156 // contains a return value and will save it in a GC-visible way. We therefore
157 // have to ensure A0 does not contain any garbage value left from the C
158 // function we called (which has return type "void").
159 // (See GenerateDeoptimizationSequence::saved_result_slot_from_fp.)
160 __ LoadImmediate(A0, 0);
161 __ ret();
162}
163
164void StubCodeCompiler::GenerateSharedStubGeneric(
165 bool save_fpu_registers,
166 intptr_t self_code_stub_offset_from_thread,
167 bool allow_return,
168 std::function<void()> perform_runtime_call) {
169 // We want the saved registers to appear like part of the caller's frame, so
170 // we push them before calling EnterStubFrame.
171 RegisterSet all_registers;
172 all_registers.AddAllNonReservedRegisters(save_fpu_registers);
173
174 // To make the stack map calculation architecture independent we do the same
175 // as on intel.
176 __ PushRegister(RA);
177 __ PushRegisters(all_registers);
178 __ lx(CODE_REG, Address(THR, self_code_stub_offset_from_thread));
179 __ EnterStubFrame();
180 perform_runtime_call();
181 if (!allow_return) {
182 __ Breakpoint();
183 return;
184 }
185 __ LeaveStubFrame();
186 __ PopRegisters(all_registers);
187 __ Drop(1); // We use the RA restored via LeaveStubFrame.
188 __ ret();
189}
190
191void StubCodeCompiler::GenerateSharedStub(
192 bool save_fpu_registers,
193 const RuntimeEntry* target,
194 intptr_t self_code_stub_offset_from_thread,
195 bool allow_return,
196 bool store_runtime_result_in_result_register) {
197 ASSERT(!store_runtime_result_in_result_register || allow_return);
198 auto perform_runtime_call = [&]() {
199 if (store_runtime_result_in_result_register) {
200 __ PushRegister(NULL_REG);
201 }
202 __ CallRuntime(*target, /*argument_count=*/0);
203 if (store_runtime_result_in_result_register) {
204 __ PopRegister(A0);
205 __ sx(A0, Address(FP, target::kWordSize *
208 }
209 };
210 GenerateSharedStubGeneric(save_fpu_registers,
211 self_code_stub_offset_from_thread, allow_return,
212 perform_runtime_call);
213}
214
215void StubCodeCompiler::GenerateEnterSafepointStub() {
216 RegisterSet all_registers;
217 all_registers.AddAllGeneralRegisters();
218
219 __ PushRegisters(all_registers);
220 __ EnterFrame(0);
221
222 __ ReserveAlignedFrameSpace(0);
223
224 __ lx(TMP, Address(THR, kEnterSafepointRuntimeEntry.OffsetFromThread()));
225 __ jalr(TMP);
226
227 __ LeaveFrame();
228 __ PopRegisters(all_registers);
229 __ ret();
230}
231
232static void GenerateExitSafepointStubCommon(Assembler* assembler,
233 uword runtime_entry_offset) {
234 RegisterSet all_registers;
235 all_registers.AddAllGeneralRegisters();
236
237 __ PushRegisters(all_registers);
238 __ EnterFrame(0);
239
240 __ ReserveAlignedFrameSpace(0);
241
242 // Set the execution state to VM while waiting for the safepoint to end.
243 // This isn't strictly necessary but enables tests to check that we're not
244 // in native code anymore. See tests/ffi/function_gc_test.dart for example.
245 __ LoadImmediate(TMP, target::Thread::vm_execution_state());
247
248 __ lx(TMP, Address(THR, runtime_entry_offset));
249 __ jalr(TMP);
250
251 __ LeaveFrame();
252 __ PopRegisters(all_registers);
253 __ ret();
254}
255
256void StubCodeCompiler::GenerateExitSafepointStub() {
257 GenerateExitSafepointStubCommon(
258 assembler, kExitSafepointRuntimeEntry.OffsetFromThread());
259}
260
261void StubCodeCompiler::GenerateExitSafepointIgnoreUnwindInProgressStub() {
262 GenerateExitSafepointStubCommon(
263 assembler,
264 kExitSafepointIgnoreUnwindInProgressRuntimeEntry.OffsetFromThread());
265}
266
267// Calls native code within a safepoint.
268//
269// On entry:
270// T0: target to call
271// Stack: set up for native call (SP), aligned, CSP < SP
272//
273// On exit:
274// S3: clobbered, although normally callee-saved
275// Stack: preserved, CSP == SP
276void StubCodeCompiler::GenerateCallNativeThroughSafepointStub() {
278 __ mv(S3, RA);
279 __ LoadImmediate(T1, target::Thread::exit_through_ffi());
280 __ TransitionGeneratedToNative(T0, FPREG, T1 /*volatile*/,
281 /*enter_safepoint=*/true);
282
283#if defined(DEBUG)
284 // Check SP alignment.
285 __ andi(T2 /*volatile*/, SP, ~(OS::ActivationFrameAlignment() - 1));
286 Label done;
287 __ beq(T2, SP, &done);
288 __ Breakpoint();
289 __ Bind(&done);
290#endif
291
292 __ jalr(T0);
293
294 __ TransitionNativeToGenerated(T1, /*leave_safepoint=*/true);
295 __ jr(S3);
296}
297
298void StubCodeCompiler::GenerateLoadBSSEntry(BSS::Relocation relocation,
300 Register tmp) {
301 compiler::Label skip_reloc;
302 __ j(&skip_reloc, compiler::Assembler::kNearJump);
303 InsertBSSRelocation(relocation);
304 __ Bind(&skip_reloc);
305
306 __ auipc(tmp, 0);
307 __ addi(tmp, tmp, -compiler::target::kWordSize);
308
309 // tmp holds the address of the relocation.
310 __ lx(dst, compiler::Address(tmp));
311
312 // dst holds the relocation itself: tmp - bss_start.
313 // tmp = tmp + (bss_start - tmp) = bss_start
314 __ add(tmp, tmp, dst);
315
316 // tmp holds the start of the BSS section.
317 // Load the "get-thread" routine: *bss_start.
318 __ lx(dst, compiler::Address(tmp));
319}
320
321void StubCodeCompiler::GenerateLoadFfiCallbackMetadataRuntimeFunction(
322 uword function_index,
323 Register dst) {
324 // Keep in sync with FfiCallbackMetadata::EnsureFirstTrampolinePageLocked.
325 // Note: If the stub was aligned, this could be a single PC relative load.
326
327 // Load a pointer to the beginning of the stub into dst.
328 const intptr_t code_size = __ CodeSize();
329 __ auipc(dst, 0);
330 __ AddImmediate(dst, -code_size);
331
332 // Round dst down to the page size.
334
335 // Load the function from the function table.
336 __ LoadFromOffset(dst, dst,
338}
339
340void StubCodeCompiler::GenerateFfiCallbackTrampolineStub() {
341#if defined(USING_SIMULATOR) && !defined(DART_PRECOMPILER)
342 // TODO(37299): FFI is not supported in SIMRISCV32/64.
343 __ ebreak();
344#else
345 Label body;
346
347 // T1 is volatile and not used for passing any arguments.
350 ++i) {
351 // The FfiCallbackMetadata table is keyed by the trampoline entry point. So
352 // look up the current PC, then jump to the shared section.
353 __ auipc(T1, 0);
354 __ j(&body);
355 }
356
357 ASSERT_EQUAL(__ CodeSize(),
358 FfiCallbackMetadata::kNativeCallbackTrampolineSize *
360
361 const intptr_t shared_stub_start = __ CodeSize();
362
363 __ Bind(&body);
364
365 // Save THR (callee-saved) and RA. Keeps stack aligned.
366 COMPILE_ASSERT(FfiCallbackMetadata::kNativeCallbackTrampolineStackDelta == 2);
367 __ PushRegisterPair(RA, THR);
369
370 // Load the thread, verify the callback ID and exit the safepoint.
371 //
372 // We exit the safepoint inside DLRT_GetFfiCallbackMetadata in order to save
373 // code size on this shared stub.
374 {
375 // Push arguments and callback id.
376 __ subi(SP, SP, 9 * target::kWordSize);
377 __ sx(T1, Address(SP, 8 * target::kWordSize));
378 __ sx(A7, Address(SP, 7 * target::kWordSize));
379 __ sx(A6, Address(SP, 6 * target::kWordSize));
380 __ sx(A5, Address(SP, 5 * target::kWordSize));
381 __ sx(A4, Address(SP, 4 * target::kWordSize));
382 __ sx(A3, Address(SP, 3 * target::kWordSize));
383 __ sx(A2, Address(SP, 2 * target::kWordSize));
384 __ sx(A1, Address(SP, 1 * target::kWordSize));
385 __ sx(A0, Address(SP, 0 * target::kWordSize));
386
387 __ EnterFrame(0);
388 // Reserve one slot for the entry point and one for the tramp abi.
389 __ ReserveAlignedFrameSpace(2 * target::kWordSize);
390
391 // Since DLRT_GetFfiCallbackMetadata can theoretically be loaded anywhere,
392 // we use the same trick as before to ensure a predictable instruction
393 // sequence.
394 Label call;
395 __ mv(A0, T1); // trampoline
396 __ mv(A1, SPREG); // out_entry_point
397 __ addi(A2, SPREG, target::kWordSize); // out_trampoline_type
398
399#if defined(DART_TARGET_OS_FUCHSIA)
400 // TODO(https://dartbug.com/52579): Remove.
401 if (FLAG_precompiled_mode) {
402 GenerateLoadBSSEntry(BSS::Relocation::DRT_GetFfiCallbackMetadata, T1, T2);
403 } else {
404 const intptr_t kPCRelativeLoadOffset = 12;
405 intptr_t start = __ CodeSize();
406 __ auipc(T1, 0);
407 __ lx(T1, Address(T1, kPCRelativeLoadOffset));
408 __ j(&call);
409
410 ASSERT_EQUAL(__ CodeSize() - start, kPCRelativeLoadOffset);
411#if XLEN == 32
412 __ Emit32(reinterpret_cast<int32_t>(&DLRT_GetFfiCallbackMetadata));
413#else
414 __ Emit64(reinterpret_cast<int64_t>(&DLRT_GetFfiCallbackMetadata));
415#endif
416 }
417#else
418 GenerateLoadFfiCallbackMetadataRuntimeFunction(
420#endif // defined(DART_TARGET_OS_FUCHSIA)
421
422 __ Bind(&call);
423 __ jalr(T1);
424 __ mv(THR, A0);
425 __ lx(T2, Address(SPREG, 0)); // entry_point
426 __ lx(T3, Address(SPREG, target::kWordSize)); // trampoline_type
427
428 __ LeaveFrame();
429
430 // Restore arguments and callback id.
431 __ lx(A0, Address(SP, 0 * target::kWordSize));
432 __ lx(A1, Address(SP, 1 * target::kWordSize));
433 __ lx(A2, Address(SP, 2 * target::kWordSize));
434 __ lx(A3, Address(SP, 3 * target::kWordSize));
435 __ lx(A4, Address(SP, 4 * target::kWordSize));
436 __ lx(A5, Address(SP, 5 * target::kWordSize));
437 __ lx(A6, Address(SP, 6 * target::kWordSize));
438 __ lx(A7, Address(SP, 7 * target::kWordSize));
439 __ lx(T1, Address(SP, 8 * target::kWordSize));
440 __ addi(SP, SP, 9 * target::kWordSize);
441 }
442
445
446 Label async_callback;
447 Label done;
448
449 // If GetFfiCallbackMetadata returned a null thread, it means that the
450 // callback was invoked after it was deleted. In this case, do nothing.
452
453 // Check the trampoline type to see how the callback should be invoked.
457
458 // Sync callback. The entry point contains the target function, so just call
459 // it. DLRT_GetThreadForNativeCallbackTrampoline exited the safepoint, so
460 // re-enter it afterwards.
461
462 // Clobbers all volatile registers, including the callback ID in T1.
463 __ jalr(T2);
464
465 // Clobbers TMP, TMP2 and T1 -- all volatile and not holding return values.
466 __ EnterFullSafepoint(/*scratch=*/T1);
467
470
471 // Async callback. The entrypoint marshals the arguments into a message and
472 // sends it over the send port. DLRT_GetThreadForNativeCallbackTrampoline
473 // entered a temporary isolate, so exit it afterwards.
474
475 // Clobbers all volatile registers, including the callback ID in T1.
476 __ jalr(T2);
477
478 // Exit the temporary isolate.
479 {
480 __ EnterFrame(0);
481 __ ReserveAlignedFrameSpace(0);
482
483 Label call;
484
485#if defined(DART_TARGET_OS_FUCHSIA)
486 // TODO(https://dartbug.com/52579): Remove.
487 if (FLAG_precompiled_mode) {
488 GenerateLoadBSSEntry(BSS::Relocation::DRT_ExitTemporaryIsolate, T1, T2);
489 } else {
490 const intptr_t kPCRelativeLoadOffset = 12;
491 intptr_t start = __ CodeSize();
492 __ auipc(T1, 0);
493 __ lx(T1, Address(T1, kPCRelativeLoadOffset));
494 __ j(&call);
495
496 ASSERT_EQUAL(__ CodeSize() - start, kPCRelativeLoadOffset);
497#if XLEN == 32
498 __ Emit32(reinterpret_cast<int32_t>(&DLRT_ExitTemporaryIsolate));
499#else
500 __ Emit64(reinterpret_cast<int64_t>(&DLRT_ExitTemporaryIsolate));
501#endif
502 }
503#else
504 GenerateLoadFfiCallbackMetadataRuntimeFunction(
506#endif // defined(DART_TARGET_OS_FUCHSIA)
507
508 __ Bind(&call);
509 __ jalr(T1);
510
511 __ LeaveFrame();
512 }
513
514 __ Bind(&done);
515 __ PopRegisterPair(RA, THR);
516 __ ret();
517
518 ASSERT_LESS_OR_EQUAL(__ CodeSize() - shared_stub_start,
519 FfiCallbackMetadata::kNativeCallbackSharedStubSize);
521
522#if defined(DEBUG)
523 while (__ CodeSize() < FfiCallbackMetadata::kPageSize) {
524 __ ebreak();
525 }
526#endif
527#endif
528}
529
530void StubCodeCompiler::GenerateDispatchTableNullErrorStub() {
531 __ EnterStubFrame();
534 __ CallRuntime(kDispatchTableNullErrorRuntimeEntry, /*argument_count=*/1);
535 // The NullError runtime entry does not return.
536 __ Breakpoint();
537}
538
539void StubCodeCompiler::GenerateRangeError(bool with_fpu_regs) {
540 auto perform_runtime_call = [&]() {
541 // If the generated code has unboxed index/length we need to box them before
542 // calling the runtime entry.
543#if XLEN == 32
545#else
547 Label length, smi_case;
548
549 // The user-controlled index might not fit into a Smi.
553 __ beq(TMP, TMP2, &length); // No overflow.
554 {
555 // Allocate a mint, reload the two registers and populate the mint.
556 __ PushRegister(NULL_REG);
557 __ CallRuntime(kAllocateMintRuntimeEntry, /*argument_count=*/0);
558 __ PopRegister(RangeErrorABI::kIndexReg);
559 __ lx(TMP,
560 Address(FP, target::kWordSize *
563 __ sx(TMP, FieldAddress(RangeErrorABI::kIndexReg,
566 Address(FP, target::kWordSize *
569 }
570
571 // Length is guaranteed to be in positive Smi range (it comes from a load
572 // of a vm recognized array).
573 __ Bind(&length);
575 }
576#endif // XLEN != 32
577 __ PushRegistersInOrder(
579 __ CallRuntime(kRangeErrorRuntimeEntry, /*argument_count=*/2);
580 __ Breakpoint();
581 };
582
583 GenerateSharedStubGeneric(
584 /*save_fpu_registers=*/with_fpu_regs,
585 with_fpu_regs
588 /*allow_return=*/false, perform_runtime_call);
589}
590
591void StubCodeCompiler::GenerateWriteError(bool with_fpu_regs) {
592 auto perform_runtime_call = [&]() {
593 __ CallRuntime(kWriteErrorRuntimeEntry, /*argument_count=*/2);
594 __ Breakpoint();
595 };
596
597 GenerateSharedStubGeneric(
598 /*save_fpu_registers=*/with_fpu_regs,
599 with_fpu_regs
602 /*allow_return=*/false, perform_runtime_call);
603}
604
605// Input parameters:
606// RA : return address.
607// SP : address of return value.
608// T5 : address of the native function to call.
609// T2 : address of first argument in argument array.
610// T1 : argc_tag including number of arguments and function kind.
611static void GenerateCallNativeWithWrapperStub(Assembler* assembler,
612 Address wrapper) {
613 const intptr_t thread_offset = target::NativeArguments::thread_offset();
614 const intptr_t argc_tag_offset = target::NativeArguments::argc_tag_offset();
615 const intptr_t argv_offset = target::NativeArguments::argv_offset();
616 const intptr_t retval_offset = target::NativeArguments::retval_offset();
617
618 __ EnterStubFrame();
619
620 // Save exit frame information to enable stack walking as we are about
621 // to transition to native code.
623
624 // Mark that the thread exited generated code through a runtime call.
627
628#if defined(DEBUG)
629 {
630 Label ok;
631 // Check that we are always entering from Dart code.
632 __ LoadFromOffset(TMP, THR, target::Thread::vm_tag_offset());
633 __ CompareImmediate(TMP, VMTag::kDartTagId);
634 __ BranchIf(EQ, &ok);
635 __ Stop("Not coming from Dart code.");
636 __ Bind(&ok);
637 }
638#endif
639
640 // Mark that the thread is executing native code.
641 __ StoreToOffset(T5, THR, target::Thread::vm_tag_offset());
642
643 // Reserve space for the native arguments structure passed on the stack (the
644 // outgoing pointer parameter to the native arguments structure is passed in
645 // R0) and align frame before entering the C++ world.
646 __ ReserveAlignedFrameSpace(target::NativeArguments::StructSize());
647
648 // Initialize target::NativeArguments structure and call native function.
649 ASSERT(thread_offset == 0 * target::kWordSize);
650 ASSERT(argc_tag_offset == 1 * target::kWordSize);
651 // Set argc in target::NativeArguments: R1 already contains argc.
652 ASSERT(argv_offset == 2 * target::kWordSize);
653 // Set argv in target::NativeArguments: R2 already contains argv.
654 // Set retval in NativeArgs.
655 ASSERT(retval_offset == 3 * target::kWordSize);
656 __ AddImmediate(
657 T3, FP, (target::frame_layout.param_end_from_fp + 1) * target::kWordSize);
658
659 // Passing the structure by value as in runtime calls would require changing
660 // Dart API for native functions.
661 // For now, space is reserved on the stack and we pass a pointer to it.
662 __ StoreToOffset(THR, SP, thread_offset);
663 __ StoreToOffset(T1, SP, argc_tag_offset);
664 __ StoreToOffset(T2, SP, argv_offset);
665 __ StoreToOffset(T3, SP, retval_offset);
666 __ mv(A0, SP); // Pass the pointer to the target::NativeArguments.
667 __ mv(A1, T5); // Pass the function entrypoint to call.
668
669 // Call native function invocation wrapper or redirection via simulator.
671 __ Call(wrapper);
672
673 // Refresh pinned registers values (inc. write barrier mask and null object).
674 __ RestorePinnedRegisters();
675
676 // Mark that the thread is executing Dart code.
677 __ LoadImmediate(TMP, VMTag::kDartTagId);
678 __ StoreToOffset(TMP, THR, target::Thread::vm_tag_offset());
679
680 // Mark that the thread has not exited generated Dart code.
682
683 // Reset exit frame information in Isolate's mutator thread structure.
685
686 // Restore the global object pool after returning from runtime (old space is
687 // moving, so the GOP could have been relocated).
688 if (FLAG_precompiled_mode) {
689 __ SetupGlobalPoolAndDispatchTable();
690 }
691
692 __ LeaveStubFrame();
693 __ ret();
694}
695
696void StubCodeCompiler::GenerateCallNoScopeNativeStub() {
697 GenerateCallNativeWithWrapperStub(
698 assembler,
699 Address(THR,
701}
702
703void StubCodeCompiler::GenerateCallAutoScopeNativeStub() {
704 GenerateCallNativeWithWrapperStub(
705 assembler,
706 Address(THR,
708}
709
710// Input parameters:
711// RA : return address.
712// SP : address of return value.
713// R5 : address of the native function to call.
714// R2 : address of first argument in argument array.
715// R1 : argc_tag including number of arguments and function kind.
716void StubCodeCompiler::GenerateCallBootstrapNativeStub() {
717 GenerateCallNativeWithWrapperStub(
718 assembler,
719 Address(THR,
721}
722
723// Input parameters:
724// ARGS_DESC_REG: arguments descriptor array.
725void StubCodeCompiler::GenerateCallStaticFunctionStub() {
726 // Create a stub frame as we are pushing some objects on the stack before
727 // calling into the runtime.
728 __ EnterStubFrame();
729 __ subi(SP, SP, 2 * target::kWordSize);
730 __ sx(ARGS_DESC_REG,
731 Address(SP, 1 * target::kWordSize)); // Preserve args descriptor.
732 __ sx(ZR, Address(SP, 0 * target::kWordSize)); // Result slot.
733 __ CallRuntime(kPatchStaticCallRuntimeEntry, 0);
734 __ lx(CODE_REG, Address(SP, 0 * target::kWordSize)); // Result.
735 __ lx(ARGS_DESC_REG,
736 Address(SP, 1 * target::kWordSize)); // Restore args descriptor.
737 __ addi(SP, SP, 2 * target::kWordSize);
738 __ LeaveStubFrame();
739 // Jump to the dart function.
740 __ LoadFieldFromOffset(TMP, CODE_REG, target::Code::entry_point_offset());
741 __ jr(TMP);
742}
743
744// Called from a static call only when an invalid code has been entered
745// (invalid because its function was optimized or deoptimized).
746// ARGS_DESC_REG: arguments descriptor array.
747void StubCodeCompiler::GenerateFixCallersTargetStub() {
748 Label monomorphic;
749 __ BranchOnMonomorphicCheckedEntryJIT(&monomorphic);
750
751 // Load code pointer to this stub from the thread:
752 // The one that is passed in, is not correct - it points to the code object
753 // that needs to be replaced.
754 __ lx(CODE_REG,
756 // Create a stub frame as we are pushing some objects on the stack before
757 // calling into the runtime.
758 __ EnterStubFrame();
759 // Setup space on stack for return value and preserve arguments descriptor.
760 __ PushRegistersInOrder({ARGS_DESC_REG, ZR});
761 __ CallRuntime(kFixCallersTargetRuntimeEntry, 0);
762 // Get Code object result and restore arguments descriptor array.
763 __ PopRegister(CODE_REG);
764 __ PopRegister(ARGS_DESC_REG);
765 // Remove the stub frame.
766 __ LeaveStubFrame();
767 // Jump to the dart function.
768 __ LoadFieldFromOffset(TMP, CODE_REG, target::Code::entry_point_offset());
769 __ jr(TMP);
770
771 __ Bind(&monomorphic);
772 // Load code pointer to this stub from the thread:
773 // The one that is passed in, is not correct - it points to the code object
774 // that needs to be replaced.
775 __ lx(CODE_REG,
777 // Create a stub frame as we are pushing some objects on the stack before
778 // calling into the runtime.
779 __ EnterStubFrame();
780 // Setup result slot, preserve receiver and
781 // push old cache value (also 2nd return value).
782 __ PushRegistersInOrder({ZR, A0, S5});
783 __ CallRuntime(kFixCallersTargetMonomorphicRuntimeEntry, 2);
784 __ PopRegister(S5); // Get target cache object.
785 __ PopRegister(A0); // Restore receiver.
786 __ PopRegister(CODE_REG); // Get target Code object.
787 // Remove the stub frame.
788 __ LeaveStubFrame();
789 // Jump to the dart function.
790 __ LoadFieldFromOffset(
791 TMP, CODE_REG,
793 __ jr(TMP);
794}
795
796// Called from object allocate instruction when the allocation stub has been
797// disabled.
798void StubCodeCompiler::GenerateFixAllocationStubTargetStub() {
799 // Load code pointer to this stub from the thread:
800 // The one that is passed in, is not correct - it points to the code object
801 // that needs to be replaced.
802 __ lx(CODE_REG,
804 __ EnterStubFrame();
805 // Setup space on stack for return value.
806 __ PushRegister(ZR);
807 __ CallRuntime(kFixAllocationStubTargetRuntimeEntry, 0);
808 // Get Code object result.
809 __ PopRegister(CODE_REG);
810 // Remove the stub frame.
811 __ LeaveStubFrame();
812 // Jump to the dart function.
813 __ LoadFieldFromOffset(TMP, CODE_REG, target::Code::entry_point_offset());
814 __ jr(TMP);
815}
816
817// Called from object allocate instruction when the allocation stub for a
818// generic class has been disabled.
819void StubCodeCompiler::GenerateFixParameterizedAllocationStubTargetStub() {
820 // Load code pointer to this stub from the thread:
821 // The one that is passed in, is not correct - it points to the code object
822 // that needs to be replaced.
823 __ lx(CODE_REG,
825 __ EnterStubFrame();
826 // Preserve type arguments register.
828 // Setup space on stack for return value.
829 __ PushRegister(ZR);
830 __ CallRuntime(kFixAllocationStubTargetRuntimeEntry, 0);
831 // Get Code object result.
832 __ PopRegister(CODE_REG);
833 // Restore type arguments register.
835 // Remove the stub frame.
836 __ LeaveStubFrame();
837 // Jump to the dart function.
838 __ LoadFieldFromOffset(TMP, CODE_REG, target::Code::entry_point_offset());
839 __ jr(TMP);
840}
841
842// Input parameters:
843// T2: smi-tagged argument count, may be zero.
844// FP[target::frame_layout.param_end_from_fp + 1]: last argument.
845static void PushArrayOfArguments(Assembler* assembler) {
848
849 // Allocate array to store arguments of caller.
850 __ LoadObject(T1, NullObject());
851 // T1: null element type for raw Array.
852 // T2: smi-tagged argument count, may be zero.
853 __ JumpAndLink(StubCodeAllocateArray());
854 // A0: newly allocated array.
855 // T2: smi-tagged argument count, may be zero (was preserved by the stub).
856 __ PushRegister(A0); // Array is in A0 and on top of stack.
857 __ SmiUntag(T2);
859 __ add(T1, T1, FP);
860 __ AddImmediate(T1,
861 target::frame_layout.param_end_from_fp * target::kWordSize);
863 // T1: address of first argument on stack.
864 // T3: address of first argument in array.
865
866 Label loop, loop_exit;
867 __ Bind(&loop);
868 __ beqz(T2, &loop_exit);
869 __ lx(T6, Address(T1, 0));
870 __ addi(T1, T1, -target::kWordSize);
871 __ StoreCompressedIntoObject(A0, Address(T3, 0), T6);
873 __ addi(T2, T2, -1);
874 __ j(&loop);
875 __ Bind(&loop_exit);
876}
877
878// Used by eager and lazy deoptimization. Preserve result in RAX if necessary.
879// This stub translates optimized frame into unoptimized frame. The optimized
880// frame can contain values in registers and on stack, the unoptimized
881// frame contains all values on stack.
882// Deoptimization occurs in following steps:
883// - Push all registers that can contain values.
884// - Call C routine to copy the stack and saved registers into temporary buffer.
885// - Adjust caller's frame to correct unoptimized frame size.
886// - Fill the unoptimized frame.
887// - Materialize objects that require allocation (e.g. Double instances).
888// GC can occur only after frame is fully rewritten.
889// Stack after TagAndPushPP() below:
890// +------------------+
891// | Saved PP | <- PP
892// +------------------+
893// | PC marker | <- TOS
894// +------------------+
895// | Saved FP |
896// +------------------+
897// | return-address | (deoptimization point)
898// +------------------+
899// | Saved CODE_REG | <- FP of stub
900// +------------------+
901// | ... | <- SP of optimized frame
902//
903// Parts of the code cannot GC, part of the code can GC.
904static void GenerateDeoptimizationSequence(Assembler* assembler,
905 DeoptStubKind kind) {
906 // DeoptimizeCopyFrame expects a Dart frame, i.e. EnterDartFrame(0), but there
907 // is no need to set the correct PC marker or load PP, since they get patched.
908 __ EnterStubFrame();
909
910 // The code in this frame may not cause GC. kDeoptimizeCopyFrameRuntimeEntry
911 // and kDeoptimizeFillFrameRuntimeEntry are leaf runtime calls.
912 const intptr_t saved_result_slot_from_fp =
915 const intptr_t saved_exception_slot_from_fp =
918 const intptr_t saved_stacktrace_slot_from_fp =
921 // Result in A0 is preserved as part of pushing all registers below.
922
923 // Push registers in their enumeration order: lowest register number at
924 // lowest address.
926 for (intptr_t i = kNumberOfCpuRegisters - 1; i >= 0; i--) {
927 const Register r = static_cast<Register>(i);
928 if (r == CODE_REG) {
929 // Save the original value of CODE_REG pushed before invoking this stub
930 // instead of the value used to call this stub.
931 COMPILE_ASSERT(TMP > CODE_REG); // TMP saved first
932 __ lx(TMP, Address(FP, 0 * target::kWordSize));
933 __ sx(TMP, Address(SP, i * target::kWordSize));
934 } else {
935 __ sx(r, Address(SP, i * target::kWordSize));
936 }
937 }
938
940 for (intptr_t i = kNumberOfFpuRegisters - 1; i >= 0; i--) {
941 FRegister freg = static_cast<FRegister>(i);
942 __ fsd(freg, Address(SP, i * kFpuRegisterSize));
943 }
944
945 {
946 __ mv(A0, SP); // Pass address of saved registers block.
947 LeafRuntimeScope rt(assembler,
948 /*frame_size=*/0,
949 /*preserve_registers=*/false);
950 bool is_lazy =
951 (kind == kLazyDeoptFromReturn) || (kind == kLazyDeoptFromThrow);
952 __ li(A1, is_lazy ? 1 : 0);
953 rt.Call(kDeoptimizeCopyFrameRuntimeEntry, 2);
954 // Result (A0) is stack-size (FP - SP) in bytes.
955 }
956
957 if (kind == kLazyDeoptFromReturn) {
958 // Restore result into T1 temporarily.
959 __ LoadFromOffset(T1, FP, saved_result_slot_from_fp * target::kWordSize);
960 } else if (kind == kLazyDeoptFromThrow) {
961 // Restore result into T1 temporarily.
962 __ LoadFromOffset(T1, FP, saved_exception_slot_from_fp * target::kWordSize);
963 __ LoadFromOffset(T2, FP,
964 saved_stacktrace_slot_from_fp * target::kWordSize);
965 }
966
967 // There is a Dart Frame on the stack. We must restore PP and leave frame.
968 __ RestoreCodePointer();
969 __ LeaveStubFrame();
970 __ sub(SP, FP, A0);
971
972 // DeoptimizeFillFrame expects a Dart frame, i.e. EnterDartFrame(0), but there
973 // is no need to set the correct PC marker or load PP, since they get patched.
974 __ EnterStubFrame();
975
976 if (kind == kLazyDeoptFromReturn) {
977 __ PushRegister(T1); // Preserve result as first local.
978 } else if (kind == kLazyDeoptFromThrow) {
979 // Preserve exception as first local.
980 // Preserve stacktrace as second local.
981 __ PushRegistersInOrder({T1, T2});
982 }
983 {
984 __ mv(A0, FP); // Pass last FP as parameter in R0.
985 LeafRuntimeScope rt(assembler,
986 /*frame_size=*/0,
987 /*preserve_registers=*/false);
988 rt.Call(kDeoptimizeFillFrameRuntimeEntry, 1);
989 }
990 if (kind == kLazyDeoptFromReturn) {
991 // Restore result into T1.
992 __ LoadFromOffset(
993 T1, FP, target::frame_layout.first_local_from_fp * target::kWordSize);
994 } else if (kind == kLazyDeoptFromThrow) {
995 // Restore result into T1.
996 __ LoadFromOffset(
997 T1, FP, target::frame_layout.first_local_from_fp * target::kWordSize);
998 __ LoadFromOffset(
999 T2, FP,
1000 (target::frame_layout.first_local_from_fp - 1) * target::kWordSize);
1001 }
1002 // Code above cannot cause GC.
1003 // There is a Dart Frame on the stack. We must restore PP and leave frame.
1004 __ RestoreCodePointer();
1005 __ LeaveStubFrame();
1006
1007 // Frame is fully rewritten at this point and it is safe to perform a GC.
1008 // Materialize any objects that were deferred by FillFrame because they
1009 // require allocation.
1010 // Enter stub frame with loading PP. The caller's PP is not materialized yet.
1011 __ EnterStubFrame();
1012 if (kind == kLazyDeoptFromReturn) {
1013 __ PushRegister(T1); // Preserve result, it will be GC-d here.
1014 } else if (kind == kLazyDeoptFromThrow) {
1015 // Preserve CODE_REG for one more runtime call.
1016 __ PushRegister(CODE_REG);
1017 // Preserve exception, it will be GC-d here.
1018 // Preserve stacktrace, it will be GC-d here.
1019 __ PushRegistersInOrder({T1, T2});
1020 }
1021
1022 __ PushRegister(ZR); // Space for the result.
1023 __ CallRuntime(kDeoptimizeMaterializeRuntimeEntry, 0);
1024 // Result tells stub how many bytes to remove from the expression stack
1025 // of the bottom-most frame. They were used as materialization arguments.
1026 __ PopRegister(T2);
1027 __ SmiUntag(T2);
1028 if (kind == kLazyDeoptFromReturn) {
1029 __ PopRegister(A0); // Restore result.
1030 } else if (kind == kLazyDeoptFromThrow) {
1031 __ PopRegister(A1); // Restore stacktrace.
1032 __ PopRegister(A0); // Restore exception.
1033 __ PopRegister(CODE_REG);
1034 }
1035 __ LeaveStubFrame();
1036 // Remove materialization arguments.
1037 __ add(SP, SP, T2);
1038 // The caller is responsible for emitting the return instruction.
1039
1040 if (kind == kLazyDeoptFromThrow) {
1041 // Unoptimized frame is now ready to accept the exception. Rethrow it to
1042 // find the right handler. Ask rethrow machinery to bypass debugger it
1043 // was already notified about this exception.
1044 __ EnterStubFrame();
1045 __ PushRegister(ZR); // Space for the result value (unused)
1046 __ PushRegister(A0); // Exception
1047 __ PushRegister(A1); // Stacktrace
1048 __ PushImmediate(target::ToRawSmi(1)); // Bypass debugger.
1049 __ CallRuntime(kReThrowRuntimeEntry, 3);
1050 __ LeaveStubFrame();
1051 }
1052}
1053
1054// A0: result, must be preserved
1055void StubCodeCompiler::GenerateDeoptimizeLazyFromReturnStub() {
1056 // Push zap value instead of CODE_REG for lazy deopt.
1057 __ LoadImmediate(TMP, kZapCodeReg);
1058 __ PushRegister(TMP);
1059 // Return address for "call" to deopt stub.
1060 __ LoadImmediate(RA, kZapReturnAddress);
1061 __ lx(CODE_REG,
1063 GenerateDeoptimizationSequence(assembler, kLazyDeoptFromReturn);
1064 __ ret();
1065}
1066
1067// A0: exception, must be preserved
1068// A1: stacktrace, must be preserved
1069void StubCodeCompiler::GenerateDeoptimizeLazyFromThrowStub() {
1070 // Push zap value instead of CODE_REG for lazy deopt.
1071 __ LoadImmediate(TMP, kZapCodeReg);
1072 __ PushRegister(TMP);
1073 // Return address for "call" to deopt stub.
1074 __ LoadImmediate(RA, kZapReturnAddress);
1075 __ lx(CODE_REG,
1077 GenerateDeoptimizationSequence(assembler, kLazyDeoptFromThrow);
1078 __ ret();
1079}
1080
1081void StubCodeCompiler::GenerateDeoptimizeStub() {
1082 __ PushRegister(CODE_REG);
1084 GenerateDeoptimizationSequence(assembler, kEagerDeopt);
1085 __ ret();
1086}
1087
1088// IC_DATA_REG: ICData/MegamorphicCache
1089static void GenerateNoSuchMethodDispatcherBody(Assembler* assembler) {
1090 __ EnterStubFrame();
1091
1092 __ lx(ARGS_DESC_REG,
1093 FieldAddress(IC_DATA_REG,
1095
1096 // Load the receiver.
1097 __ LoadCompressedSmiFieldFromOffset(
1099 __ AddShifted(TMP, FP, T2, target::kWordSizeLog2 - 1); // T2 is Smi.
1100 __ LoadFromOffset(A0, TMP,
1101 target::frame_layout.param_end_from_fp * target::kWordSize);
1102 // Push: result slot, receiver, ICData/MegamorphicCache,
1103 // arguments descriptor.
1104 __ PushRegistersInOrder({ZR, A0, IC_DATA_REG, ARGS_DESC_REG});
1105
1106 // Adjust arguments count.
1107 __ LoadCompressedSmiFieldFromOffset(
1109 Label args_count_ok;
1110 __ beqz(T3, &args_count_ok, Assembler::kNearJump);
1111 // Include the type arguments.
1112 __ addi(T2, T2, target::ToRawSmi(1));
1113 __ Bind(&args_count_ok);
1114
1115 // T2: Smi-tagged arguments array length.
1116 PushArrayOfArguments(assembler);
1117 const intptr_t kNumArgs = 4;
1118 __ CallRuntime(kNoSuchMethodFromCallStubRuntimeEntry, kNumArgs);
1119 __ Drop(4);
1120 __ PopRegister(A0); // Return value.
1121 __ LeaveStubFrame();
1122 __ ret();
1123}
1124
1125static void GenerateDispatcherCode(Assembler* assembler,
1126 Label* call_target_function) {
1127 __ Comment("NoSuchMethodDispatch");
1128 // When lazily generated invocation dispatchers are disabled, the
1129 // miss-handler may return null.
1130 __ bne(T0, NULL_REG, call_target_function);
1131
1132 GenerateNoSuchMethodDispatcherBody(assembler);
1133}
1134
1135// Input:
1136// ARGS_DESC_REG - arguments descriptor
1137// IC_DATA_REG - icdata/megamorphic_cache
1138void StubCodeCompiler::GenerateNoSuchMethodDispatcherStub() {
1139 GenerateNoSuchMethodDispatcherBody(assembler);
1140}
1141
1142// Called for inline allocation of arrays.
1143// Input registers (preserved):
1144// RA: return address.
1145// AllocateArrayABI::kLengthReg: array length as Smi.
1146// AllocateArrayABI::kTypeArgumentsReg: type arguments of array.
1147// Output registers:
1148// AllocateArrayABI::kResultReg: newly allocated array.
1149// Clobbered:
1150// T3, T4, T5
1151void StubCodeCompiler::GenerateAllocateArrayStub() {
1152 if (!FLAG_use_slow_path && FLAG_inline_alloc) {
1153 Label slow_case;
1154 // Compute the size to be allocated, it is based on the array length
1155 // and is computed as:
1156 // RoundedAllocationSize(
1157 // (array_length * kCompressedWordSize) + target::Array::header_size()).
1158 // Check that length is a Smi.
1159 __ BranchIfNotSmi(AllocateArrayABI::kLengthReg, &slow_case);
1160
1161 // Check length >= 0 && length <= kMaxNewSpaceElements
1162 const intptr_t max_len =
1164 __ CompareImmediate(AllocateArrayABI::kLengthReg, max_len, kObjectBytes);
1165 __ BranchIf(HI, &slow_case);
1166
1167 const intptr_t cid = kArrayCid;
1168 NOT_IN_PRODUCT(__ MaybeTraceAllocation(kArrayCid, &slow_case, T4));
1169
1170 // Calculate and align allocation size.
1171 // Load new object start and calculate next object start.
1172 // AllocateArrayABI::kTypeArgumentsReg: type arguments of array.
1173 // AllocateArrayABI::kLengthReg: array length as Smi.
1175 Address(THR, target::Thread::top_offset()));
1176 intptr_t fixed_size_plus_alignment_padding =
1179 // AllocateArrayABI::kLengthReg is Smi.
1182 __ AddImmediate(T3, fixed_size_plus_alignment_padding);
1184 // AllocateArrayABI::kResultReg: potential new object start.
1185 // T3: object size in bytes.
1187 // Branch if unsigned overflow.
1188 __ bltu(T4, AllocateArrayABI::kResultReg, &slow_case);
1189
1190 // Check if the allocation fits into the remaining space.
1191 // AllocateArrayABI::kResultReg: potential new object start.
1192 // AllocateArrayABI::kTypeArgumentsReg: type arguments of array.
1193 // AllocateArrayABI::kLengthReg: array length as Smi.
1194 // T3: array size.
1195 // T4: potential next object start.
1196 __ LoadFromOffset(TMP, THR, target::Thread::end_offset());
1197 __ bgeu(T4, TMP, &slow_case); // Branch if unsigned higher or equal.
1198 __ CheckAllocationCanary(AllocateArrayABI::kResultReg);
1199
1200 // Successfully allocated the object(s), now update top to point to
1201 // next object start and initialize the object.
1202 // AllocateArrayABI::kResultReg: potential new object start.
1203 // T3: array size.
1204 // T4: potential next object start.
1205 __ sx(T4, Address(THR, target::Thread::top_offset()));
1208
1209 // AllocateArrayABI::kResultReg: new object start as a tagged pointer.
1210 // AllocateArrayABI::kTypeArgumentsReg: type arguments of array.
1211 // AllocateArrayABI::kLengthReg: array length as Smi.
1212 // R3: array size.
1213 // R7: new object end address.
1214
1215 // Calculate the size tag.
1216 // AllocateArrayABI::kResultReg: new object start as a tagged pointer.
1217 // AllocateArrayABI::kLengthReg: array length as Smi.
1218 // T3: array size.
1219 // T4: new object end address.
1220 const intptr_t shift = target::UntaggedObject::kTagBitsSizeTagPos -
1222 __ li(T5, 0);
1224 compiler::Label zero_tag;
1225 __ BranchIf(UNSIGNED_GREATER, &zero_tag);
1226 __ slli(T5, T3, shift);
1227 __ Bind(&zero_tag);
1228
1229 // Get the class index and insert it into the tags.
1230 const uword tags =
1231 target::MakeTagWordForNewSpaceObject(cid, /*instance_size=*/0);
1232
1233 __ OrImmediate(T5, T5, tags);
1234 __ StoreFieldToOffset(T5, AllocateArrayABI::kResultReg,
1236
1237 // Store the type argument field.
1238 __ StoreCompressedIntoObjectOffsetNoBarrier(
1241
1242 // Set the length field.
1243 __ StoreCompressedIntoObjectOffsetNoBarrier(AllocateArrayABI::kResultReg,
1246
1247 // Initialize all array elements to raw_null.
1248 // AllocateArrayABI::kResultReg: new object start as a tagged pointer.
1249 // R7: new object end address.
1250 // AllocateArrayABI::kLengthReg: array length as Smi.
1251 __ AddImmediate(T3, AllocateArrayABI::kResultReg,
1253 // R3: iterator which initially points to the start of the variable
1254 // data area to be initialized.
1255 Label loop;
1256 __ Bind(&loop);
1257 for (intptr_t offset = 0; offset < target::kObjectAlignment;
1259 __ StoreCompressedIntoObjectNoBarrier(AllocateArrayABI::kResultReg,
1260 Address(T3, offset), NULL_REG);
1261 }
1262 // Safe to only check every kObjectAlignment bytes instead of each word.
1265 __ bltu(T3, T4, &loop);
1266 __ WriteAllocationCanary(T4); // Fix overshoot.
1267
1268 // Done allocating and initializing the array.
1269 // AllocateArrayABI::kResultReg: new object.
1270 // AllocateArrayABI::kLengthReg: array length as Smi (preserved).
1271 __ ret();
1272
1273 // Unable to allocate the array using the fast inline code, just call
1274 // into the runtime.
1275 __ Bind(&slow_case);
1276 }
1277
1278 // Create a stub frame as we are pushing some objects on the stack before
1279 // calling into the runtime.
1280 __ EnterStubFrame();
1281 __ subi(SP, SP, 3 * target::kWordSize);
1282 __ sx(ZR, Address(SP, 2 * target::kWordSize)); // Result slot.
1285 Address(SP, 0 * target::kWordSize));
1286 __ CallRuntime(kAllocateArrayRuntimeEntry, 2);
1287
1288 // Write-barrier elimination might be enabled for this array (depending on the
1289 // array length). To be sure we will check if the allocated object is in old
1290 // space and if so call a leaf runtime to add it to the remembered set.
1294
1296 Address(SP, 0 * target::kWordSize));
1299 __ addi(SP, SP, 3 * target::kWordSize);
1300 __ LeaveStubFrame();
1301 __ ret();
1302}
1303
1304void StubCodeCompiler::GenerateAllocateMintSharedWithFPURegsStub() {
1305 // For test purpose call allocation stub without inline allocation attempt.
1306 if (!FLAG_use_slow_path && FLAG_inline_alloc) {
1307 Label slow_case;
1308 __ TryAllocate(compiler::MintClass(), &slow_case, Assembler::kNearJump,
1310 __ ret();
1311
1312 __ Bind(&slow_case);
1313 }
1316 GenerateSharedStub(/*save_fpu_registers=*/true, &kAllocateMintRuntimeEntry,
1318 /*allow_return=*/true,
1319 /*store_runtime_result_in_result_register=*/true);
1320}
1321
1322void StubCodeCompiler::GenerateAllocateMintSharedWithoutFPURegsStub() {
1323 // For test purpose call allocation stub without inline allocation attempt.
1324 if (!FLAG_use_slow_path && FLAG_inline_alloc) {
1325 Label slow_case;
1326 __ TryAllocate(compiler::MintClass(), &slow_case, Assembler::kNearJump,
1328 __ ret();
1329
1330 __ Bind(&slow_case);
1331 }
1334 GenerateSharedStub(
1335 /*save_fpu_registers=*/false, &kAllocateMintRuntimeEntry,
1337 /*allow_return=*/true,
1338 /*store_runtime_result_in_result_register=*/true);
1339}
1340
1341// Called when invoking Dart code from C++ (VM code).
1342// Input parameters:
1343// RA : points to return address.
1344// A0 : target code or entry point (in bare instructions mode).
1345// A1 : arguments descriptor array.
1346// A2 : arguments array.
1347// A3 : current thread.
1348// Beware! TMP == A3
1349void StubCodeCompiler::GenerateInvokeDartCodeStub() {
1350 __ Comment("InvokeDartCodeStub");
1351
1352 __ EnterFrame(1 * target::kWordSize);
1353
1354 // Push code object to PC marker slot.
1356 __ sx(TMP2, Address(SP, 0 * target::kWordSize));
1357
1358#if defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_ANDROID)
1360#elif defined(USING_SHADOW_CALL_STACK)
1361#error Unimplemented
1362#endif
1363
1364 // TODO(riscv): Consider using only volatile FPU registers in Dart code so we
1365 // don't need to save the preserved FPU registers here.
1366 __ PushNativeCalleeSavedRegisters();
1367
1368 // Set up THR, which caches the current thread in Dart code.
1369 if (THR != A3) {
1370 __ mv(THR, A3);
1371 }
1372
1373 // Refresh pinned registers values (inc. write barrier mask and null object).
1374 __ RestorePinnedRegisters();
1375
1376 // Save the current VMTag, top resource and top exit frame info on the stack.
1377 // StackFrameIterator reads the top exit frame info saved in this frame.
1378 __ subi(SP, SP, 4 * target::kWordSize);
1379 __ lx(TMP, Address(THR, target::Thread::vm_tag_offset()));
1380 __ sx(TMP, Address(SP, 3 * target::kWordSize));
1383 __ sx(TMP, Address(SP, 2 * target::kWordSize));
1386 __ sx(TMP, Address(SP, 1 * target::kWordSize));
1389 __ sx(TMP, Address(SP, 0 * target::kWordSize));
1390 // target::frame_layout.exit_link_slot_from_entry_fp must be kept in sync
1391 // with the code below.
1392#if XLEN == 32
1393 ASSERT_EQUAL(target::frame_layout.exit_link_slot_from_entry_fp, -42);
1394#elif XLEN == 64
1395 ASSERT_EQUAL(target::frame_layout.exit_link_slot_from_entry_fp, -30);
1396#endif
1397 // In debug mode, verify that we've pushed the top exit frame info at the
1398 // correct offset from FP.
1399 __ EmitEntryFrameVerification();
1400
1401 // Mark that the thread is executing Dart code. Do this after initializing the
1402 // exit link for the profiler.
1403 __ LoadImmediate(TMP, VMTag::kDartTagId);
1404 __ StoreToOffset(TMP, THR, target::Thread::vm_tag_offset());
1405
1406 // Load arguments descriptor array, which is passed to Dart code.
1407 __ mv(ARGS_DESC_REG, A1);
1408
1409 // Load number of arguments into T5 and adjust count for type arguments.
1410 __ LoadFieldFromOffset(T5, ARGS_DESC_REG,
1412 __ LoadFieldFromOffset(T3, ARGS_DESC_REG,
1414 __ SmiUntag(T5);
1415 // Include the type arguments.
1416 __ snez(T3, T3); // T3 <- T3 == 0 ? 0 : 1
1417 __ add(T5, T5, T3);
1418
1419 // Compute address of 'arguments array' data area into A2.
1420 __ AddImmediate(A2, A2, target::Array::data_offset() - kHeapObjectTag);
1421
1422 // Set up arguments for the Dart call.
1423 Label push_arguments;
1424 Label done_push_arguments;
1425 __ beqz(T5, &done_push_arguments); // check if there are arguments.
1426 __ LoadImmediate(T2, 0);
1427 __ Bind(&push_arguments);
1428 __ lx(T3, Address(A2, 0));
1429 __ PushRegister(T3);
1430 __ addi(T2, T2, 1);
1431 __ addi(A2, A2, target::kWordSize);
1432 __ blt(T2, T5, &push_arguments, compiler::Assembler::kNearJump);
1433 __ Bind(&done_push_arguments);
1434
1435 if (FLAG_precompiled_mode) {
1436 __ SetupGlobalPoolAndDispatchTable();
1437 __ mv(CODE_REG, ZR); // GC-safe value into CODE_REG.
1438 } else {
1439 // We now load the pool pointer(PP) with a GC safe value as we are about to
1440 // invoke dart code. We don't need a real object pool here.
1441 __ li(PP, 1); // PP is untagged, callee will tag and spill PP.
1442 __ mv(CODE_REG, A0);
1443 __ lx(A0, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
1444 }
1445
1446 // Call the Dart code entrypoint.
1447 __ jalr(A0); // ARGS_DESC_REG is the arguments descriptor array.
1448 __ Comment("InvokeDartCodeStub return");
1449
1450 // Get rid of arguments pushed on the stack.
1451 __ addi(
1452 SP, FP,
1453 target::frame_layout.exit_link_slot_from_entry_fp * target::kWordSize);
1454
1455 // Restore the current VMTag, the saved top exit frame info and top resource
1456 // back into the Thread structure.
1457 __ lx(TMP, Address(SP, 0 * target::kWordSize));
1459 __ lx(TMP, Address(SP, 1 * target::kWordSize));
1461 __ lx(TMP, Address(SP, 2 * target::kWordSize));
1463 __ lx(TMP, Address(SP, 3 * target::kWordSize));
1464 __ sx(TMP, Address(THR, target::Thread::vm_tag_offset()));
1465 __ addi(SP, SP, 4 * target::kWordSize);
1466
1467 __ PopNativeCalleeSavedRegisters();
1468
1469 // Restore the frame pointer and C stack pointer and return.
1470 __ LeaveFrame();
1471 __ ret();
1472}
1473
1474// Helper to generate space allocation of context stub.
1475// This does not initialise the fields of the context.
1476// Input:
1477// T1: number of context variables.
1478// Output:
1479// A0: new allocated Context object.
1480// Clobbered:
1481// T2, T3, T4, TMP
1482static void GenerateAllocateContextSpaceStub(Assembler* assembler,
1483 Label* slow_case) {
1484 // First compute the rounded instance size.
1485 // R1: number of context variables.
1486 intptr_t fixed_size_plus_alignment_padding =
1490 __ AddImmediate(T2, fixed_size_plus_alignment_padding);
1492
1493 NOT_IN_PRODUCT(__ MaybeTraceAllocation(kContextCid, slow_case, T4));
1494 // Now allocate the object.
1495 // T1: number of context variables.
1496 // T2: object size.
1497 __ lx(A0, Address(THR, target::Thread::top_offset()));
1498 __ add(T3, T2, A0);
1499 // Check if the allocation fits into the remaining space.
1500 // A0: potential new object.
1501 // T1: number of context variables.
1502 // T2: object size.
1503 // T3: potential next object start.
1504 __ lx(TMP, Address(THR, target::Thread::end_offset()));
1505 __ CompareRegisters(T3, TMP);
1506 __ BranchIf(CS, slow_case); // Branch if unsigned higher or equal.
1507 __ CheckAllocationCanary(A0);
1508
1509 // Successfully allocated the object, now update top to point to
1510 // next object start and initialize the object.
1511 // A0: new object.
1512 // T1: number of context variables.
1513 // T2: object size.
1514 // T3: next object start.
1515 __ sx(T3, Address(THR, target::Thread::top_offset()));
1516 __ addi(A0, A0, kHeapObjectTag);
1517
1518 // Calculate the size tag.
1519 // A0: new object.
1520 // T1: number of context variables.
1521 // T2: object size.
1522 const intptr_t shift = target::UntaggedObject::kTagBitsSizeTagPos -
1524 __ li(T3, 0);
1526 // If no size tag overflow, shift R2 left, else set R2 to zero.
1527 compiler::Label zero_tag;
1528 __ BranchIf(HI, &zero_tag);
1529 __ slli(T3, T2, shift);
1530 __ Bind(&zero_tag);
1531
1532 // Get the class index and insert it into the tags.
1533 // T3: size and bit tags.
1534 const uword tags =
1535 target::MakeTagWordForNewSpaceObject(kContextCid, /*instance_size=*/0);
1536
1537 __ OrImmediate(T3, T3, tags);
1538 __ StoreFieldToOffset(T3, A0, target::Object::tags_offset());
1539
1540 // Setup up number of context variables field.
1541 // A0: new object.
1542 // T1: number of context variables as integer value (not object).
1543 __ StoreFieldToOffset(T1, A0, target::Context::num_variables_offset(),
1544 kFourBytes);
1545}
1546
1547// Called for inline allocation of contexts.
1548// Input:
1549// T1: number of context variables.
1550// Output:
1551// A0: new allocated Context object.
1552void StubCodeCompiler::GenerateAllocateContextStub() {
1553 if (!FLAG_use_slow_path && FLAG_inline_alloc) {
1554 Label slow_case;
1555
1556 GenerateAllocateContextSpaceStub(assembler, &slow_case);
1557
1558 // Setup the parent field.
1559 // A0: new object.
1560 // T1: number of context variables.
1561 __ StoreCompressedIntoObjectOffset(A0, target::Context::parent_offset(),
1562 NULL_REG);
1563
1564 // Initialize the context variables.
1565 // A0: new object.
1566 // T1: number of context variables.
1567 {
1568 Label loop, done;
1569 __ AddImmediate(T3, A0,
1571 __ Bind(&loop);
1572 __ subi(T1, T1, 1);
1573 __ bltz(T1, &done);
1574 __ sx(NULL_REG, Address(T3, 0));
1576 __ j(&loop);
1577 __ Bind(&done);
1578 }
1579
1580 // Done allocating and initializing the context.
1581 // A0: new object.
1582 __ ret();
1583
1584 __ Bind(&slow_case);
1585 }
1586
1587 // Create a stub frame as we are pushing some objects on the stack before
1588 // calling into the runtime.
1589 __ EnterStubFrame();
1590 // Setup space on stack for return value.
1591 __ SmiTag(T1);
1592 __ PushObject(NullObject());
1593 __ PushRegister(T1);
1594 __ CallRuntime(kAllocateContextRuntimeEntry, 1); // Allocate context.
1595 __ Drop(1); // Pop number of context variables argument.
1596 __ PopRegister(A0); // Pop the new context object.
1597
1598 // Write-barrier elimination might be enabled for this context (depending on
1599 // the size). To be sure we will check if the allocated object is in old
1600 // space and if so call a leaf runtime to add it to the remembered set.
1602
1603 // A0: new object
1604 // Restore the frame pointer.
1605 __ LeaveStubFrame();
1606 __ ret();
1607}
1608
1609// Called for clone of contexts.
1610// Input:
1611// T5: context variable to clone.
1612// Output:
1613// A0: new allocated Context object.
1614void StubCodeCompiler::GenerateCloneContextStub() {
1615 if (!FLAG_use_slow_path && FLAG_inline_alloc) {
1616 Label slow_case;
1617
1618 // Load num. variable (int32) in the existing context.
1619 __ lw(T1, FieldAddress(T5, target::Context::num_variables_offset()));
1620
1621 GenerateAllocateContextSpaceStub(assembler, &slow_case);
1622
1623 // Load parent in the existing context.
1624 __ LoadCompressed(T3, FieldAddress(T5, target::Context::parent_offset()));
1625 // Setup the parent field.
1626 // A0: new context.
1627 __ StoreCompressedIntoObjectNoBarrier(
1628 A0, FieldAddress(A0, target::Context::parent_offset()), T3);
1629
1630 // Clone the context variables.
1631 // A0: new context.
1632 // T1: number of context variables.
1633 {
1634 Label loop, done;
1635 // T3: Variable array address, new context.
1636 __ AddImmediate(T3, A0,
1638 // T4: Variable array address, old context.
1639 __ AddImmediate(T4, T5,
1641
1642 __ Bind(&loop);
1643 __ subi(T1, T1, 1);
1644 __ bltz(T1, &done);
1645 __ lx(T5, Address(T4, 0));
1647 __ sx(T5, Address(T3, 0));
1649 __ j(&loop);
1650
1651 __ Bind(&done);
1652 }
1653
1654 // Done allocating and initializing the context.
1655 // A0: new object.
1656 __ ret();
1657
1658 __ Bind(&slow_case);
1659 }
1660
1661 __ EnterStubFrame();
1662
1663 __ subi(SP, SP, 2 * target::kWordSize);
1664 __ sx(NULL_REG, Address(SP, 1 * target::kWordSize)); // Result slot.
1665 __ sx(T5, Address(SP, 0 * target::kWordSize)); // Context argument.
1666 __ CallRuntime(kCloneContextRuntimeEntry, 1);
1667 __ lx(A0, Address(SP, 1 * target::kWordSize)); // Context result.
1668 __ subi(SP, SP, 2 * target::kWordSize);
1669
1670 // Write-barrier elimination might be enabled for this context (depending on
1671 // the size). To be sure we will check if the allocated object is in old
1672 // space and if so call a leaf runtime to add it to the remembered set.
1674
1675 // A0: new object
1676 __ LeaveStubFrame();
1677 __ ret();
1678}
1679
1680void StubCodeCompiler::GenerateWriteBarrierWrappersStub() {
1681 for (intptr_t i = 0; i < kNumberOfCpuRegisters; ++i) {
1682 if ((kDartAvailableCpuRegs & (1 << i)) == 0) continue;
1683
1684 Register reg = static_cast<Register>(i);
1685 intptr_t start = __ CodeSize();
1686 __ addi(SP, SP, -3 * target::kWordSize);
1687 __ sx(RA, Address(SP, 2 * target::kWordSize));
1688 __ sx(TMP, Address(SP, 1 * target::kWordSize));
1689 __ sx(kWriteBarrierObjectReg, Address(SP, 0 * target::kWordSize));
1690 __ mv(kWriteBarrierObjectReg, reg);
1692 __ lx(kWriteBarrierObjectReg, Address(SP, 0 * target::kWordSize));
1693 __ lx(TMP, Address(SP, 1 * target::kWordSize));
1694 __ lx(RA, Address(SP, 2 * target::kWordSize));
1695 __ addi(SP, SP, 3 * target::kWordSize);
1696 __ jr(TMP); // Return.
1697 intptr_t end = __ CodeSize();
1699 }
1700}
1701
1702// Helper stub to implement Assembler::StoreIntoObject/Array.
1703// Input parameters:
1704// A0: Object (old)
1705// A1: Value (old or new)
1706// A6: Slot
1707// If A1 is new, add A0 to the store buffer. Otherwise A1 is old, mark A1
1708// and add it to the mark list.
1712static void GenerateWriteBarrierStubHelper(Assembler* assembler, bool cards) {
1713 RegisterSet spill_set((1 << T2) | (1 << T3) | (1 << T4), 0);
1714
1715 Label skip_marking;
1716 __ lbu(TMP, FieldAddress(A1, target::Object::tags_offset()));
1718 __ and_(TMP, TMP, TMP2);
1720 __ beqz(TMP, &skip_marking);
1721
1722 {
1723 // Atomically clear kNotMarkedBit.
1724 Label is_new, done;
1725 __ PushRegisters(spill_set);
1727 // T3: Untagged address of header word (amo's do not support offsets).
1729#if XLEN == 32
1730 __ amoandw(TMP2, TMP2, Address(T3, 0));
1731#else
1732 __ amoandd(TMP2, TMP2, Address(T3, 0));
1733#endif
1735 __ beqz(TMP2, &done); // Was already clear -> lost race.
1736
1738 __ bnez(TMP2, &is_new);
1739
1740 auto mark_stack_push = [&](intptr_t offset, const RuntimeEntry& entry) {
1741 __ lx(T4, Address(THR, offset));
1743 __ slli(T3, T2, target::kWordSizeLog2);
1744 __ add(T3, T4, T3);
1746 __ addi(T2, T2, 1);
1748 __ CompareImmediate(T2, target::MarkingStackBlock::kSize);
1749 __ BranchIf(NE, &done);
1750
1751 {
1752 LeafRuntimeScope rt(assembler, /*frame_size=*/0,
1753 /*preserve_registers=*/true);
1754 __ mv(A0, THR);
1755 rt.Call(entry, /*argument_count=*/1);
1756 }
1757 };
1758
1760 kOldMarkingStackBlockProcessRuntimeEntry);
1761 __ j(&done);
1762
1763 __ Bind(&is_new);
1765 kNewMarkingStackBlockProcessRuntimeEntry);
1766
1767 __ Bind(&done);
1768 __ PopRegisters(spill_set);
1769 }
1770
1771 Label add_to_remembered_set, remember_card;
1772 __ Bind(&skip_marking);
1773 __ lbu(TMP, FieldAddress(A0, target::Object::tags_offset()));
1774 __ lbu(TMP2, FieldAddress(A1, target::Object::tags_offset()));
1776 __ and_(TMP, TMP2, TMP);
1778 __ bnez(TMP, &add_to_remembered_set);
1779 __ ret();
1780
1781 __ Bind(&add_to_remembered_set);
1782 if (cards) {
1783 __ lbu(TMP2, FieldAddress(A0, target::Object::tags_offset()));
1785 __ bnez(TMP2, &remember_card);
1786 } else {
1787#if defined(DEBUG)
1788 Label ok;
1789 __ lbu(TMP2, FieldAddress(A0, target::Object::tags_offset()));
1791 __ beqz(TMP2, &ok, Assembler::kNearJump);
1792 __ Stop("Wrong barrier!");
1793 __ Bind(&ok);
1794#endif
1795 }
1796 {
1797 // Atomically clear kOldAndNotRememberedBit.
1798 Label done;
1799 __ PushRegisters(spill_set);
1801 // T3: Untagged address of header word (amo's do not support offsets).
1803#if XLEN == 32
1804 __ amoandw(TMP2, TMP2, Address(T3, 0));
1805#else
1806 __ amoandd(TMP2, TMP2, Address(T3, 0));
1807#endif
1809 __ beqz(TMP2, &done); // Was already clear -> lost race.
1810
1813 __ slli(T3, T2, target::kWordSizeLog2);
1814 __ add(T3, T4, T3);
1816 __ addi(T2, T2, 1);
1818 __ CompareImmediate(T2, target::StoreBufferBlock::kSize);
1819 __ BranchIf(NE, &done);
1820
1821 {
1822 LeafRuntimeScope rt(assembler, /*frame_size=*/0,
1823 /*preserve_registers=*/true);
1824 __ mv(A0, THR);
1825 rt.Call(kStoreBufferBlockProcessRuntimeEntry, /*argument_count=*/1);
1826 }
1827
1828 __ Bind(&done);
1829 __ PopRegisters(spill_set);
1830 __ ret();
1831 }
1832 if (cards) {
1833 Label remember_card_slow;
1834
1835 // Get card table.
1836 __ Bind(&remember_card);
1837 __ AndImmediate(TMP, A0, target::kPageMask); // Page.
1838 __ lx(TMP2,
1839 Address(TMP, target::Page::card_table_offset())); // Card table.
1840 __ beqz(TMP2, &remember_card_slow);
1841
1842 // Atomically dirty the card.
1843 __ sub(A6, A6, TMP); // Offset in page.
1844 __ srli(A6, A6, target::Page::kBytesPerCardLog2); // Card index.
1845 __ li(TMP, 1);
1846 __ sll(TMP, TMP, A6); // Bit mask. (Shift amount is mod XLEN.)
1848 __ slli(A6, A6, target::kWordSizeLog2);
1849 __ add(TMP2, TMP2, A6); // Card word address.
1850#if XLEN == 32
1851 __ amoorw(ZR, TMP, Address(TMP2, 0));
1852#else
1853 __ amoord(ZR, TMP, Address(TMP2, 0));
1854#endif
1855 __ ret();
1856
1857 // Card table not yet allocated.
1858 __ Bind(&remember_card_slow);
1859 {
1860 LeafRuntimeScope rt(assembler, /*frame_size=*/0,
1861 /*preserve_registers=*/true);
1862 __ mv(A0, A0); // Arg0 = Object
1863 __ mv(A1, A6); // Arg1 = Slot
1864 rt.Call(kRememberCardRuntimeEntry, /*argument_count=*/2);
1865 }
1866 __ ret();
1867 }
1868}
1869
1870void StubCodeCompiler::GenerateWriteBarrierStub() {
1871 GenerateWriteBarrierStubHelper(assembler, false);
1872}
1873
1874void StubCodeCompiler::GenerateArrayWriteBarrierStub() {
1875 GenerateWriteBarrierStubHelper(assembler, true);
1876}
1877
1878static void GenerateAllocateObjectHelper(Assembler* assembler,
1879 bool is_cls_parameterized) {
1880 const Register kTagsReg = AllocateObjectABI::kTagsReg;
1881
1882 {
1883 Label slow_case;
1884
1885#if !defined(PRODUCT)
1886 {
1887 const Register kCidRegister = TMP2;
1888 __ ExtractClassIdFromTags(kCidRegister, AllocateObjectABI::kTagsReg);
1889 __ MaybeTraceAllocation(kCidRegister, &slow_case, TMP);
1890 }
1891#endif
1892
1893 const Register kNewTopReg = T3;
1894
1895 // Bump allocation.
1896 {
1897 const Register kInstanceSizeReg = T4;
1898 const Register kEndReg = T5;
1899
1900 __ ExtractInstanceSizeFromTags(kInstanceSizeReg, kTagsReg);
1901
1902 // Load two words from Thread::top: top and end.
1903 // AllocateObjectABI::kResultReg: potential next object start.
1905 Address(THR, target::Thread::top_offset()));
1906 __ lx(kEndReg, Address(THR, target::Thread::end_offset()));
1907
1908 __ add(kNewTopReg, AllocateObjectABI::kResultReg, kInstanceSizeReg);
1909
1910 __ CompareRegisters(kEndReg, kNewTopReg);
1911 __ BranchIf(UNSIGNED_LESS_EQUAL, &slow_case);
1912 __ CheckAllocationCanary(AllocateObjectABI::kResultReg);
1913
1914 // Successfully allocated the object, now update top to point to
1915 // next object start and store the class in the class field of object.
1916 __ sx(kNewTopReg, Address(THR, target::Thread::top_offset()));
1917 } // kInstanceSizeReg = R4, kEndReg = R5
1918
1919 // Tags.
1920 __ sx(kTagsReg, Address(AllocateObjectABI::kResultReg,
1922
1923 // Initialize the remaining words of the object.
1924 {
1925 const Register kFieldReg = T4;
1926
1927 __ AddImmediate(kFieldReg, AllocateObjectABI::kResultReg,
1929 Label loop;
1930 __ Bind(&loop);
1931 for (intptr_t offset = 0; offset < target::kObjectAlignment;
1933 __ StoreCompressedIntoObjectNoBarrier(AllocateObjectABI::kResultReg,
1934 Address(kFieldReg, offset),
1935 NULL_REG);
1936 }
1937 // Safe to only check every kObjectAlignment bytes instead of each word.
1939 __ addi(kFieldReg, kFieldReg, target::kObjectAlignment);
1940 __ bltu(kFieldReg, kNewTopReg, &loop);
1941 __ WriteAllocationCanary(kNewTopReg); // Fix overshoot.
1942 } // kFieldReg = T4
1943
1944 __ AddImmediate(AllocateObjectABI::kResultReg,
1946
1947 if (is_cls_parameterized) {
1948 Label not_parameterized_case;
1949
1950 const Register kClsIdReg = T4;
1951 const Register kTypeOffsetReg = T5;
1952
1953 __ ExtractClassIdFromTags(kClsIdReg, kTagsReg);
1954
1955 // Load class' type_arguments_field offset in words.
1956 __ LoadClassById(kTypeOffsetReg, kClsIdReg);
1957 __ lw(
1958 kTypeOffsetReg,
1959 FieldAddress(kTypeOffsetReg,
1960 target::Class::
1961 host_type_arguments_field_offset_in_words_offset()));
1962
1963 // Set the type arguments in the new object.
1964 __ AddShifted(kTypeOffsetReg, AllocateObjectABI::kResultReg,
1965 kTypeOffsetReg, target::kWordSizeLog2);
1966 __ StoreCompressedIntoObjectNoBarrier(
1967 AllocateObjectABI::kResultReg, FieldAddress(kTypeOffsetReg, 0),
1969
1970 __ Bind(&not_parameterized_case);
1971 } // kClsIdReg = R4, kTypeOffsetReg = R5
1972
1973 __ ret();
1974
1975 __ Bind(&slow_case);
1976 } // kNewTopReg = R3
1977
1978 // Fall back on slow case:
1979 if (!is_cls_parameterized) {
1981 }
1982 // Tail call to generic allocation stub.
1983 __ lx(
1984 TMP,
1986 __ jr(TMP);
1987}
1988
1989// Called for inline allocation of objects (any class).
1990void StubCodeCompiler::GenerateAllocateObjectStub() {
1991 GenerateAllocateObjectHelper(assembler, /*is_cls_parameterized=*/false);
1992}
1993
1994void StubCodeCompiler::GenerateAllocateObjectParameterizedStub() {
1995 GenerateAllocateObjectHelper(assembler, /*is_cls_parameterized=*/true);
1996}
1997
1998void StubCodeCompiler::GenerateAllocateObjectSlowStub() {
1999 if (!FLAG_precompiled_mode) {
2000 __ lx(CODE_REG,
2002 }
2003
2004 // Create a stub frame as we are pushing some objects on the stack before
2005 // calling into the runtime.
2006 __ EnterStubFrame();
2007
2008 __ ExtractClassIdFromTags(AllocateObjectABI::kTagsReg,
2010 __ LoadClassById(A0, AllocateObjectABI::kTagsReg);
2011
2012 __ subi(SP, SP, 3 * target::kWordSize);
2013 __ sx(ZR, Address(SP, 2 * target::kWordSize)); // Result slot.
2014 __ sx(A0, Address(SP, 1 * target::kWordSize)); // Arg0: Class object.
2016 Address(SP, 0 * target::kWordSize)); // Arg1: Type args or null.
2017 __ CallRuntime(kAllocateObjectRuntimeEntry, 2);
2019 __ addi(SP, SP, 3 * target::kWordSize);
2020
2021 // Write-barrier elimination is enabled for [cls] and we therefore need to
2022 // ensure that the object is in new-space or has remembered bit set.
2024
2025 __ LeaveStubFrame();
2026
2027 __ ret();
2028}
2029
2030// Called for inline allocation of objects.
2032 UnresolvedPcRelativeCalls* unresolved_calls,
2033 const Class& cls,
2034 const Code& allocate_object,
2035 const Code& allocat_object_parametrized) {
2036 classid_t cls_id = target::Class::GetId(cls);
2037 ASSERT(cls_id != kIllegalCid);
2038
2039 // The generated code is different if the class is parameterized.
2040 const bool is_cls_parameterized = target::Class::NumTypeArguments(cls) > 0;
2041 ASSERT(!is_cls_parameterized || target::Class::TypeArgumentsFieldOffset(
2043
2044 const intptr_t instance_size = target::Class::GetInstanceSize(cls);
2045 ASSERT(instance_size > 0);
2046
2047 const uword tags =
2048 target::MakeTagWordForNewSpaceObject(cls_id, instance_size);
2049
2050 // Note: Keep in sync with helper function.
2051 const Register kTagsReg = AllocateObjectABI::kTagsReg;
2053
2054 __ LoadImmediate(kTagsReg, tags);
2055
2056 if (!FLAG_use_slow_path && FLAG_inline_alloc &&
2058 target::SizeFitsInSizeTag(instance_size)) {
2061 if (is_cls_parameterized) {
2062 if (!IsSameObject(NullObject(),
2063 CastHandle<Object>(allocat_object_parametrized))) {
2064 __ GenerateUnRelocatedPcRelativeTailCall();
2065 unresolved_calls->Add(new UnresolvedPcRelativeCall(
2066 __ CodeSize(), allocat_object_parametrized, /*is_tail_call=*/true));
2067 } else {
2068 __ lx(TMP,
2069 Address(THR,
2070 target::Thread::
2071 allocate_object_parameterized_entry_point_offset()));
2072 __ jr(TMP);
2073 }
2074 } else {
2075 if (!IsSameObject(NullObject(), CastHandle<Object>(allocate_object))) {
2076 __ GenerateUnRelocatedPcRelativeTailCall();
2077 unresolved_calls->Add(new UnresolvedPcRelativeCall(
2078 __ CodeSize(), allocate_object, /*is_tail_call=*/true));
2079 } else {
2080 __ lx(
2081 TMP,
2083 __ jr(TMP);
2084 }
2085 }
2086 } else {
2087 if (!is_cls_parameterized) {
2089 }
2090 __ lx(TMP,
2091 Address(THR,
2093 __ jr(TMP);
2094 }
2095}
2096
2097// Called for invoking "dynamic noSuchMethod(Invocation invocation)" function
2098// from the entry code of a dart function after an error in passed argument
2099// name or number is detected.
2100// Input parameters:
2101// RA : return address.
2102// SP : address of last argument.
2103// S4: arguments descriptor array.
2104void StubCodeCompiler::GenerateCallClosureNoSuchMethodStub() {
2105 __ EnterStubFrame();
2106
2107 // Load the receiver.
2108 __ LoadCompressedSmiFieldFromOffset(
2110 __ AddShifted(TMP, FP, T2, target::kWordSizeLog2 - 1); // T2 is Smi
2111 __ LoadFromOffset(A0, TMP,
2112 target::frame_layout.param_end_from_fp * target::kWordSize);
2113
2114 // Load the function.
2115 __ LoadCompressedFieldFromOffset(TMP, A0, target::Closure::function_offset());
2116
2117 // Push result slot, receiver, function, arguments descriptor.
2118 __ PushRegistersInOrder({ZR, A0, TMP, S4});
2119
2120 // Adjust arguments count.
2121 __ LoadCompressedSmiFieldFromOffset(
2123 Label args_count_ok;
2124 __ beqz(T3, &args_count_ok, Assembler::kNearJump);
2125 // Include the type arguments.
2126 __ addi(T2, T2, target::ToRawSmi(1));
2127 __ Bind(&args_count_ok);
2128
2129 // T2: Smi-tagged arguments array length.
2130 PushArrayOfArguments(assembler);
2131
2132 const intptr_t kNumArgs = 4;
2133 __ CallRuntime(kNoSuchMethodFromPrologueRuntimeEntry, kNumArgs);
2134 // noSuchMethod on closures always throws an error, so it will never return.
2135 __ ebreak();
2136}
2137
2138// A6: function object.
2139// S5: inline cache data object.
2140// Cannot use function object from ICData as it may be the inlined
2141// function and not the top-scope function.
2143 if (FLAG_precompiled_mode) {
2144 __ Breakpoint();
2145 return;
2146 }
2147 if (FLAG_trace_optimized_ic_calls) {
2148 __ Stop("Unimplemented");
2149 }
2150 __ LoadFieldFromOffset(TMP, A6, target::Function::usage_counter_offset(),
2151 kFourBytes);
2152 __ addi(TMP, TMP, 1);
2153 __ StoreFieldToOffset(TMP, A6, target::Function::usage_counter_offset(),
2154 kFourBytes);
2155}
2156
2157// Loads function into 'func_reg'.
2159 if (FLAG_precompiled_mode) {
2160 __ trap();
2161 return;
2162 }
2163 if (FLAG_optimization_counter_threshold >= 0) {
2164 __ Comment("Increment function counter");
2165 __ LoadFieldFromOffset(func_reg, IC_DATA_REG,
2167 __ LoadFieldFromOffset(
2169 __ AddImmediate(A1, 1);
2170 __ StoreFieldToOffset(A1, func_reg,
2172 }
2173}
2174
2175// Note: S5 must be preserved.
2176// Attempt a quick Smi operation for known operations ('kind'). The ICData
2177// must have been primed with a Smi/Smi check that will be used for counting
2178// the invocations.
2179static void EmitFastSmiOp(Assembler* assembler,
2180 Token::Kind kind,
2181 intptr_t num_args,
2182 Label* not_smi_or_overflow) {
2183 __ Comment("Fast Smi op");
2184 __ lx(A0, Address(SP, +1 * target::kWordSize)); // Left.
2185 __ lx(A1, Address(SP, +0 * target::kWordSize)); // Right.
2186 __ or_(TMP2, A0, A1);
2187 __ andi(TMP2, TMP2, kSmiTagMask);
2188 __ bnez(TMP2, not_smi_or_overflow);
2189 switch (kind) {
2190 case Token::kADD: {
2191 __ AddBranchOverflow(A0, A0, A1, not_smi_or_overflow);
2192 break;
2193 }
2194 case Token::kLT: {
2195 // TODO(riscv): Bit tricks with stl and NULL_REG.
2196 Label load_true, done;
2197 __ blt(A0, A1, &load_true, compiler::Assembler::kNearJump);
2198 __ LoadObject(A0, CastHandle<Object>(FalseObject()));
2200 __ Bind(&load_true);
2201 __ LoadObject(A0, CastHandle<Object>(TrueObject()));
2202 __ Bind(&done);
2203 break;
2204 }
2205 case Token::kEQ: {
2206 // TODO(riscv): Bit tricks with stl and NULL_REG.
2207 Label load_true, done;
2208 __ beq(A0, A1, &load_true, Assembler::kNearJump);
2209 __ LoadObject(A0, CastHandle<Object>(FalseObject()));
2211 __ Bind(&load_true);
2212 __ LoadObject(A0, CastHandle<Object>(TrueObject()));
2213 __ Bind(&done);
2214 break;
2215 }
2216 default:
2217 UNIMPLEMENTED();
2218 }
2219
2220 // S5: IC data object (preserved).
2221 __ LoadFieldFromOffset(A6, IC_DATA_REG, target::ICData::entries_offset());
2222 // R6: ic_data_array with check entries: classes and target functions.
2224// R6: points directly to the first ic data array element.
2225#if defined(DEBUG)
2226 // Check that first entry is for Smi/Smi.
2227 Label error, ok;
2228 const intptr_t imm_smi_cid = target::ToRawSmi(kSmiCid);
2229 __ LoadCompressedSmiFromOffset(TMP, A6, 0);
2230 __ CompareImmediate(TMP, imm_smi_cid);
2231 __ BranchIf(NE, &error);
2232 __ LoadCompressedSmiFromOffset(TMP, A6, target::kCompressedWordSize);
2233 __ CompareImmediate(TMP, imm_smi_cid);
2234 __ BranchIf(EQ, &ok);
2235 __ Bind(&error);
2236 __ Stop("Incorrect IC data");
2237 __ Bind(&ok);
2238#endif
2239 if (FLAG_optimization_counter_threshold >= 0) {
2240 const intptr_t count_offset =
2242 // Update counter, ignore overflow.
2243 __ LoadCompressedSmiFromOffset(A1, A6, count_offset);
2244 __ addi(A1, A1, target::ToRawSmi(1));
2245 __ StoreToOffset(A1, A6, count_offset);
2246 }
2247
2248 __ ret();
2249}
2250
2251// Saves the offset of the target entry-point (from the Function) into T6.
2252//
2253// Must be the first code generated, since any code before will be skipped in
2254// the unchecked entry-point.
2255static void GenerateRecordEntryPoint(Assembler* assembler) {
2256 Label done;
2259 __ BindUncheckedEntryPoint();
2260 __ LoadImmediate(
2263 __ Bind(&done);
2264}
2265
2266// Generate inline cache check for 'num_args'.
2267// A0: receiver (if instance call)
2268// S5: ICData
2269// RA: return address
2270// Control flow:
2271// - If receiver is null -> jump to IC miss.
2272// - If receiver is Smi -> load Smi class.
2273// - If receiver is not-Smi -> load receiver's class.
2274// - Check if 'num_args' (including receiver) match any IC data group.
2275// - Match found -> jump to target.
2276// - Match not found -> jump to IC miss.
2278 intptr_t num_args,
2279 const RuntimeEntry& handle_ic_miss,
2280 Token::Kind kind,
2281 Optimized optimized,
2282 CallType type,
2283 Exactness exactness) {
2284 const bool save_entry_point = kind == Token::kILLEGAL;
2285 if (FLAG_precompiled_mode) {
2286 __ Breakpoint();
2287 return;
2288 }
2289
2290 if (save_entry_point) {
2291 GenerateRecordEntryPoint(assembler);
2292 // T6: untagged entry point offset
2293 }
2294
2295 if (optimized == kOptimized) {
2297 } else {
2298 GenerateUsageCounterIncrement(/*scratch=*/T0);
2299 }
2300
2301 ASSERT(num_args == 1 || num_args == 2);
2302#if defined(DEBUG)
2303 {
2304 Label ok;
2305 // Check that the IC data array has NumArgsTested() == num_args.
2306 // 'NumArgsTested' is stored in the least significant bits of 'state_bits'.
2307 __ LoadFromOffset(TMP, IC_DATA_REG,
2310 ASSERT(target::ICData::NumArgsTestedShift() == 0); // No shift needed.
2312 __ CompareImmediate(TMP2, num_args);
2313 __ BranchIf(EQ, &ok, Assembler::kNearJump);
2314 __ Stop("Incorrect stub for IC data");
2315 __ Bind(&ok);
2316 }
2317#endif // DEBUG
2318
2319#if !defined(PRODUCT)
2320 Label stepping, done_stepping;
2321 if (optimized == kUnoptimized) {
2322 __ Comment("Check single stepping");
2323 __ LoadIsolate(TMP);
2324 __ LoadFromOffset(TMP, TMP, target::Isolate::single_step_offset(),
2326 __ bnez(TMP, &stepping);
2327 __ Bind(&done_stepping);
2328 }
2329#endif
2330
2331 Label not_smi_or_overflow;
2332 if (kind != Token::kILLEGAL) {
2333 EmitFastSmiOp(assembler, kind, num_args, &not_smi_or_overflow);
2334 }
2335 __ Bind(&not_smi_or_overflow);
2336
2337 __ Comment("Extract ICData initial values and receiver cid");
2338 // S5: IC data object (preserved).
2339 __ LoadFieldFromOffset(A1, IC_DATA_REG, target::ICData::entries_offset());
2340 // A1: ic_data_array with check entries: classes and target functions.
2342 // A1: points directly to the first ic data array element.
2343
2344 if (type == kInstanceCall) {
2345 __ LoadTaggedClassIdMayBeSmi(T1, A0);
2346 __ LoadFieldFromOffset(ARGS_DESC_REG, IC_DATA_REG,
2348 if (num_args == 2) {
2349 __ LoadCompressedSmiFieldFromOffset(
2352 __ add(A7, SP, A7);
2353 __ lx(A6, Address(A7, -2 * target::kWordSize));
2354 __ LoadTaggedClassIdMayBeSmi(T2, A6);
2355 }
2356 } else {
2357 __ LoadFieldFromOffset(ARGS_DESC_REG, IC_DATA_REG,
2359 __ LoadCompressedSmiFieldFromOffset(
2362 __ add(A7, A7, SP);
2363 __ lx(A6, Address(A7, -1 * target::kWordSize));
2364 __ LoadTaggedClassIdMayBeSmi(T1, A6);
2365 if (num_args == 2) {
2366 __ lx(A6, Address(A7, -2 * target::kWordSize));
2367 __ LoadTaggedClassIdMayBeSmi(T2, A6);
2368 }
2369 }
2370 // T1: first argument class ID as Smi.
2371 // T2: second argument class ID as Smi.
2372 // S4: args descriptor
2373
2374 // We unroll the generic one that is generated once more than the others.
2375 const bool optimize = kind == Token::kILLEGAL;
2376
2377 // Loop that checks if there is an IC data match.
2378 Label loop, found, miss;
2379 __ Comment("ICData loop");
2380
2381 __ Bind(&loop);
2382 for (int unroll = optimize ? 4 : 2; unroll >= 0; unroll--) {
2383 Label update;
2384
2385 __ LoadCompressedSmiFromOffset(A7, A1, 0);
2386 if (num_args == 1) {
2387 __ beq(A7, T1, &found); // Class id match?
2388 } else {
2389 __ bne(A7, T1, &update); // Continue.
2390 __ LoadCompressedSmiFromOffset(A7, A1, target::kCompressedWordSize);
2391 __ beq(A7, T2, &found); // Class id match?
2392 }
2393 __ Bind(&update);
2394
2395 const intptr_t entry_size = target::ICData::TestEntryLengthFor(
2396 num_args, exactness == kCheckExactness) *
2398 __ AddImmediate(A1, entry_size); // Next entry.
2399
2400 __ CompareImmediate(A7, target::ToRawSmi(kIllegalCid)); // Done?
2401 if (unroll == 0) {
2402 __ BranchIf(NE, &loop);
2403 } else {
2404 __ BranchIf(EQ, &miss);
2405 }
2406 }
2407
2408 __ Bind(&miss);
2409 __ Comment("IC miss");
2410
2411 // Compute address of arguments.
2412 __ LoadCompressedSmiFieldFromOffset(
2415 __ add(A7, A7, SP);
2416 __ subi(A7, A7, 1 * target::kWordSize);
2417
2418 // A7: address of receiver
2419 // Create a stub frame as we are pushing some objects on the stack before
2420 // calling into the runtime.
2421 __ EnterStubFrame();
2422 // Preserve IC data object and arguments descriptor array and
2423 // setup space on stack for result (target code object).
2424 __ PushRegistersInOrder({ARGS_DESC_REG, IC_DATA_REG});
2425 if (save_entry_point) {
2426 __ SmiTag(T6);
2427 __ PushRegister(T6);
2428 }
2429 // Setup space on stack for the result (target code object).
2430 __ PushRegister(ZR);
2431 // Push call arguments.
2432 for (intptr_t i = 0; i < num_args; i++) {
2433 __ LoadFromOffset(TMP, A7, -target::kWordSize * i);
2434 __ PushRegister(TMP);
2435 }
2436 // Pass IC data object.
2437 __ PushRegister(IC_DATA_REG);
2438 __ CallRuntime(handle_ic_miss, num_args + 1);
2439 // Remove the call arguments pushed earlier, including the IC data object.
2440 __ Drop(num_args + 1);
2441 // Pop returned function object into R0.
2442 // Restore arguments descriptor array and IC data array.
2443 __ PopRegister(FUNCTION_REG); // Pop returned function object into T0.
2444 if (save_entry_point) {
2445 __ PopRegister(T6);
2446 __ SmiUntag(T6);
2447 }
2448 __ PopRegister(IC_DATA_REG); // Restore IC Data.
2449 __ PopRegister(ARGS_DESC_REG); // Restore arguments descriptor array.
2450 __ RestoreCodePointer();
2451 __ LeaveStubFrame();
2452 Label call_target_function;
2453 if (FLAG_precompiled_mode) {
2454 GenerateDispatcherCode(assembler, &call_target_function);
2455 } else {
2456 __ j(&call_target_function);
2457 }
2458
2459 __ Bind(&found);
2460 // A1: pointer to an IC data check group.
2461 const intptr_t target_offset =
2463 const intptr_t count_offset =
2465 const intptr_t exactness_offset =
2467
2468 Label call_target_function_through_unchecked_entry;
2469 if (exactness == kCheckExactness) {
2470 Label exactness_ok;
2471 ASSERT(num_args == 1);
2472 __ LoadCompressedSmi(T1, Address(A1, exactness_offset));
2473 __ LoadImmediate(
2476 __ blt(T1, TMP, &exactness_ok);
2477 __ beq(T1, TMP, &call_target_function_through_unchecked_entry);
2478
2479 // Check trivial exactness.
2480 // Note: UntaggedICData::receivers_static_type_ is guaranteed to be not null
2481 // because we only emit calls to this stub when it is not null.
2482 __ LoadCompressed(
2484 __ LoadCompressed(T2, FieldAddress(T2, target::Type::arguments_offset()));
2485 // T1 contains an offset to type arguments in words as a smi,
2486 // hence TIMES_4. A0 is guaranteed to be non-smi because it is expected
2487 // to have type arguments.
2488 __ LoadIndexedPayload(TMP, A0, 0, T1, TIMES_COMPRESSED_HALF_WORD_SIZE,
2489 kObjectBytes);
2490 __ beq(T2, TMP, &call_target_function_through_unchecked_entry);
2491
2492 // Update exactness state (not-exact anymore).
2493 __ LoadImmediate(
2495 __ StoreToOffset(TMP, A1, exactness_offset, kObjectBytes);
2496 __ Bind(&exactness_ok);
2497 }
2498 __ LoadCompressedFromOffset(FUNCTION_REG, A1, target_offset);
2499
2500 if (FLAG_optimization_counter_threshold >= 0) {
2501 __ Comment("Update caller's counter");
2502 __ LoadCompressedSmiFromOffset(TMP, A1, count_offset);
2503 __ addi(TMP, TMP, target::ToRawSmi(1)); // Ignore overflow.
2504 __ StoreToOffset(TMP, A1, count_offset, kObjectBytes);
2505 }
2506
2507 __ Comment("Call target");
2508 __ Bind(&call_target_function);
2509 // T0: target function.
2510 __ LoadCompressedFieldFromOffset(CODE_REG, FUNCTION_REG,
2512 if (save_entry_point) {
2513 __ add(A7, FUNCTION_REG, T6);
2514 __ lx(A7, Address(A7, 0));
2515 } else {
2516 __ LoadFieldFromOffset(A7, FUNCTION_REG,
2518 }
2519 __ jr(A7); // FUNCTION_REG: Function, argument to lazy compile stub.
2520
2521 if (exactness == kCheckExactness) {
2522 __ Bind(&call_target_function_through_unchecked_entry);
2523 if (FLAG_optimization_counter_threshold >= 0) {
2524 __ Comment("Update ICData counter");
2525 __ LoadCompressedSmiFromOffset(TMP, A1, count_offset);
2526 __ addi(TMP, TMP, target::ToRawSmi(1)); // Ignore overflow.
2527 __ StoreToOffset(TMP, A1, count_offset, kObjectBytes);
2528 }
2529 __ Comment("Call target (via unchecked entry point)");
2530 __ LoadCompressedFromOffset(FUNCTION_REG, A1, target_offset);
2531 __ LoadCompressedFieldFromOffset(CODE_REG, FUNCTION_REG,
2533 __ LoadFieldFromOffset(
2536 __ jr(A7);
2537 }
2538
2539#if !defined(PRODUCT)
2540 if (optimized == kUnoptimized) {
2541 __ Bind(&stepping);
2542 __ EnterStubFrame();
2543 if (type == kInstanceCall) {
2544 __ PushRegister(A0); // Preserve receiver.
2545 }
2546 if (save_entry_point) {
2547 __ SmiTag(T6);
2548 __ PushRegister(T6);
2549 }
2550 __ PushRegister(IC_DATA_REG); // Preserve IC data.
2551 __ CallRuntime(kSingleStepHandlerRuntimeEntry, 0);
2552 __ PopRegister(IC_DATA_REG);
2553 if (save_entry_point) {
2554 __ PopRegister(T6);
2555 __ SmiUntag(T6);
2556 }
2557 if (type == kInstanceCall) {
2558 __ PopRegister(A0);
2559 }
2560 __ RestoreCodePointer();
2561 __ LeaveStubFrame();
2562 __ j(&done_stepping);
2563 }
2564#endif
2565}
2566
2567// A0: receiver
2568// S5: ICData
2569// RA: return address
2570void StubCodeCompiler::GenerateOneArgCheckInlineCacheStub() {
2572 1, kInlineCacheMissHandlerOneArgRuntimeEntry, Token::kILLEGAL,
2574}
2575
2576// A0: receiver
2577// S5: ICData
2578// RA: return address
2579void StubCodeCompiler::GenerateOneArgCheckInlineCacheWithExactnessCheckStub() {
2581 1, kInlineCacheMissHandlerOneArgRuntimeEntry, Token::kILLEGAL,
2583}
2584
2585// A0: receiver
2586// S5: ICData
2587// RA: return address
2588void StubCodeCompiler::GenerateTwoArgsCheckInlineCacheStub() {
2590 2, kInlineCacheMissHandlerTwoArgsRuntimeEntry, Token::kILLEGAL,
2592}
2593
2594// A0: receiver
2595// S5: ICData
2596// RA: return address
2597void StubCodeCompiler::GenerateSmiAddInlineCacheStub() {
2599 2, kInlineCacheMissHandlerTwoArgsRuntimeEntry, Token::kADD, kUnoptimized,
2601}
2602
2603// A0: receiver
2604// S5: ICData
2605// RA: return address
2606void StubCodeCompiler::GenerateSmiLessInlineCacheStub() {
2608 2, kInlineCacheMissHandlerTwoArgsRuntimeEntry, Token::kLT, kUnoptimized,
2610}
2611
2612// A0: receiver
2613// S5: ICData
2614// RA: return address
2615void StubCodeCompiler::GenerateSmiEqualInlineCacheStub() {
2617 2, kInlineCacheMissHandlerTwoArgsRuntimeEntry, Token::kEQ, kUnoptimized,
2619}
2620
2621// A0: receiver
2622// S5: ICData
2623// A6: Function
2624// RA: return address
2625void StubCodeCompiler::GenerateOneArgOptimizedCheckInlineCacheStub() {
2627 1, kInlineCacheMissHandlerOneArgRuntimeEntry, Token::kILLEGAL, kOptimized,
2629}
2630
2631// A0: receiver
2632// S5: ICData
2633// A6: Function
2634// RA: return address
2635void StubCodeCompiler::
2636 GenerateOneArgOptimizedCheckInlineCacheWithExactnessCheckStub() {
2638 1, kInlineCacheMissHandlerOneArgRuntimeEntry, Token::kILLEGAL, kOptimized,
2640}
2641
2642// A0: receiver
2643// S5: ICData
2644// A6: Function
2645// RA: return address
2646void StubCodeCompiler::GenerateTwoArgsOptimizedCheckInlineCacheStub() {
2648 2, kInlineCacheMissHandlerTwoArgsRuntimeEntry, Token::kILLEGAL,
2650}
2651
2652// S5: ICData
2653// RA: return address
2654void StubCodeCompiler::GenerateZeroArgsUnoptimizedStaticCallStub() {
2655 GenerateRecordEntryPoint(assembler);
2656 GenerateUsageCounterIncrement(/* scratch */ T0);
2657
2658#if defined(DEBUG)
2659 {
2660 Label ok;
2661 // Check that the IC data array has NumArgsTested() == 0.
2662 // 'NumArgsTested' is stored in the least significant bits of 'state_bits'.
2663 __ LoadFromOffset(TMP, IC_DATA_REG,
2666 ASSERT(target::ICData::NumArgsTestedShift() == 0); // No shift needed.
2668 __ CompareImmediate(TMP, 0);
2669 __ BranchIf(EQ, &ok);
2670 __ Stop("Incorrect IC data for unoptimized static call");
2671 __ Bind(&ok);
2672 }
2673#endif // DEBUG
2674
2675 // Check single stepping.
2676#if !defined(PRODUCT)
2677 Label stepping, done_stepping;
2678 __ LoadIsolate(TMP);
2679 __ LoadFromOffset(TMP, TMP, target::Isolate::single_step_offset(),
2681 __ bnez(TMP, &stepping, Assembler::kNearJump);
2682 __ Bind(&done_stepping);
2683#endif
2684
2685 // T5: IC data object (preserved).
2686 __ LoadFieldFromOffset(A0, IC_DATA_REG, target::ICData::entries_offset());
2687 // A0: ic_data_array with entries: target functions and count.
2689 // A0: points directly to the first ic data array element.
2690 const intptr_t target_offset =
2692 const intptr_t count_offset =
2694
2695 if (FLAG_optimization_counter_threshold >= 0) {
2696 // Increment count for this call, ignore overflow.
2697 __ LoadCompressedSmiFromOffset(TMP, A0, count_offset);
2698 __ addi(TMP, TMP, target::ToRawSmi(1));
2699 __ StoreToOffset(TMP, A0, count_offset);
2700 }
2701
2702 // Load arguments descriptor into T4.
2703 __ LoadFieldFromOffset(ARGS_DESC_REG, IC_DATA_REG,
2705
2706 // Get function and call it, if possible.
2707 __ LoadCompressedFromOffset(FUNCTION_REG, A0, target_offset);
2708 __ LoadCompressedFieldFromOffset(CODE_REG, FUNCTION_REG,
2710 __ add(A0, FUNCTION_REG, T6);
2711 __ lx(TMP, Address(A0, 0));
2712 __ jr(TMP); // FUNCTION_REG: Function, argument to lazy compile stub.
2713
2714#if !defined(PRODUCT)
2715 __ Bind(&stepping);
2716 __ EnterStubFrame();
2717 __ PushRegister(IC_DATA_REG); // Preserve IC data.
2718 __ SmiTag(T6);
2719 __ PushRegister(T6);
2720 __ CallRuntime(kSingleStepHandlerRuntimeEntry, 0);
2721 __ PopRegister(T6);
2722 __ SmiUntag(T6);
2723 __ PopRegister(IC_DATA_REG);
2724 __ RestoreCodePointer();
2725 __ LeaveStubFrame();
2726 __ j(&done_stepping);
2727#endif
2728}
2729
2730// S5: ICData
2731// RA: return address
2732void StubCodeCompiler::GenerateOneArgUnoptimizedStaticCallStub() {
2733 GenerateUsageCounterIncrement(/* scratch */ T0);
2734 GenerateNArgsCheckInlineCacheStub(1, kStaticCallMissHandlerOneArgRuntimeEntry,
2735 Token::kILLEGAL, kUnoptimized, kStaticCall,
2737}
2738
2739// S5: ICData
2740// RA: return address
2741void StubCodeCompiler::GenerateTwoArgsUnoptimizedStaticCallStub() {
2742 GenerateUsageCounterIncrement(/* scratch */ T0);
2744 2, kStaticCallMissHandlerTwoArgsRuntimeEntry, Token::kILLEGAL,
2746}
2747
2748// Stub for compiling a function and jumping to the compiled code.
2749// ARGS_DESC_REG: Arguments descriptor.
2750// FUNCTION_REG: Function.
2751void StubCodeCompiler::GenerateLazyCompileStub() {
2752 // Preserve arg desc.
2753 __ EnterStubFrame();
2754 // Save arguments descriptor and pass function.
2755 __ PushRegistersInOrder({ARGS_DESC_REG, FUNCTION_REG});
2756 __ CallRuntime(kCompileFunctionRuntimeEntry, 1);
2757 __ PopRegister(FUNCTION_REG); // Restore function.
2758 __ PopRegister(ARGS_DESC_REG); // Restore arg desc.
2759 __ LeaveStubFrame();
2760
2761 __ LoadCompressedFieldFromOffset(CODE_REG, FUNCTION_REG,
2763 __ LoadFieldFromOffset(TMP, FUNCTION_REG,
2765 __ jr(TMP);
2766}
2767
2768// A0: Receiver
2769// S5: ICData
2770void StubCodeCompiler::GenerateICCallBreakpointStub() {
2771#if defined(PRODUCT)
2772 __ Stop("No debugging in PRODUCT mode");
2773#else
2774 __ EnterStubFrame();
2775 __ subi(SP, SP, 3 * target::kWordSize);
2776 __ sx(A0, Address(SP, 2 * target::kWordSize)); // Preserve receiver.
2777 __ sx(S5, Address(SP, 1 * target::kWordSize)); // Preserve IC data.
2778 __ sx(ZR, Address(SP, 0 * target::kWordSize)); // Space for result.
2779 __ CallRuntime(kBreakpointRuntimeHandlerRuntimeEntry, 0);
2780 __ lx(CODE_REG, Address(SP, 0 * target::kWordSize)); // Original stub.
2781 __ lx(S5, Address(SP, 1 * target::kWordSize)); // Restore IC data.
2782 __ lx(A0, Address(SP, 2 * target::kWordSize)); // Restore receiver.
2783 __ LeaveStubFrame();
2784 __ LoadFieldFromOffset(TMP, CODE_REG, target::Code::entry_point_offset());
2785 __ jr(TMP);
2786#endif
2787}
2788
2789// S5: ICData
2790void StubCodeCompiler::GenerateUnoptStaticCallBreakpointStub() {
2791#if defined(PRODUCT)
2792 __ Stop("No debugging in PRODUCT mode");
2793#else
2794 __ EnterStubFrame();
2795 __ subi(SP, SP, 2 * target::kWordSize);
2796 __ sx(S5, Address(SP, 1 * target::kWordSize)); // Preserve IC data.
2797 __ sx(ZR, Address(SP, 0 * target::kWordSize)); // Space for result.
2798 __ CallRuntime(kBreakpointRuntimeHandlerRuntimeEntry, 0);
2799 __ lx(CODE_REG, Address(SP, 0 * target::kWordSize)); // Original stub.
2800 __ lx(S5, Address(SP, 1 * target::kWordSize)); // Restore IC data.
2801 __ LeaveStubFrame();
2802 __ LoadFieldFromOffset(TMP, CODE_REG, target::Code::entry_point_offset());
2803 __ jr(TMP);
2804#endif // defined(PRODUCT)
2805}
2806
2807void StubCodeCompiler::GenerateRuntimeCallBreakpointStub() {
2808#if defined(PRODUCT)
2809 __ Stop("No debugging in PRODUCT mode");
2810#else
2811 __ EnterStubFrame();
2812 __ subi(SP, SP, 1 * target::kWordSize);
2813 __ sx(ZR, Address(SP, 0 * target::kWordSize)); // Space for result.
2814 __ CallRuntime(kBreakpointRuntimeHandlerRuntimeEntry, 0);
2815 __ lx(CODE_REG, Address(SP, 0 * target::kWordSize));
2816 __ LeaveStubFrame();
2817 __ LoadFieldFromOffset(TMP, CODE_REG, target::Code::entry_point_offset());
2818 __ jr(TMP);
2819#endif // defined(PRODUCT)
2820}
2821
2822// Called only from unoptimized code. All relevant registers have been saved.
2823void StubCodeCompiler::GenerateDebugStepCheckStub() {
2824#if defined(PRODUCT)
2825 __ Stop("No debugging in PRODUCT mode");
2826#else
2827 // Check single stepping.
2828 Label stepping, done_stepping;
2829 __ LoadIsolate(A1);
2830 __ LoadFromOffset(A1, A1, target::Isolate::single_step_offset(),
2832 __ bnez(A1, &stepping, compiler::Assembler::kNearJump);
2833 __ Bind(&done_stepping);
2834 __ ret();
2835
2836 __ Bind(&stepping);
2837 __ EnterStubFrame();
2838 __ CallRuntime(kSingleStepHandlerRuntimeEntry, 0);
2839 __ LeaveStubFrame();
2840 __ j(&done_stepping);
2841#endif // defined(PRODUCT)
2842}
2843
2844// Used to check class and type arguments. Arguments passed in registers:
2845//
2846// Inputs (all preserved, mostly from TypeTestABI struct):
2847// - kSubtypeTestCacheReg: UntaggedSubtypeTestCache
2848// - kInstanceReg: instance to test against.
2849// - kDstTypeReg: destination type (for n>=7).
2850// - kInstantiatorTypeArgumentsReg: instantiator type arguments (for n>=3).
2851// - kFunctionTypeArgumentsReg: function type arguments (for n>=4).
2852// - RA: return address.
2853//
2854// Outputs (from TypeTestABI struct):
2855// - kSubtypeTestCacheResultReg: the cached result, or null if not found.
2856void StubCodeCompiler::GenerateSubtypeNTestCacheStub(Assembler* assembler,
2857 int n) {
2858 ASSERT(n >= 1);
2860 // If we need the parent function type arguments for a closure, we also need
2861 // the delayed type arguments, so this case will never happen.
2862 ASSERT(n != 5);
2863
2864 // We could initialize kSubtypeTestCacheResultReg with null and use that as
2865 // the null register up until exit, which means we'd just need to return
2866 // without setting it in the not_found case.
2867 //
2868 // However, that would mean the expense of keeping another register live
2869 // across the loop to hold the cache entry address, and the not_found case
2870 // means we're going to runtime, so optimize for the found case instead.
2871 //
2872 // Thus, we use it to store the current cache entry, since it's distinct from
2873 // all the preserved input registers and the scratch register, and the last
2874 // use of the current cache entry is to set kSubtypeTestCacheResultReg.
2876
2877 Label not_found;
2878 GenerateSubtypeTestCacheSearch(
2879 assembler, n, NULL_REG, kCacheArrayReg,
2887 [&](Assembler* assembler, int n) {
2888 __ LoadCompressed(
2890 Address(kCacheArrayReg, target::kCompressedWordSize *
2892 __ Ret();
2893 },
2894 [&](Assembler* assembler, int n) {
2896 __ Ret();
2897 });
2898}
2899
2900void StubCodeCompiler::GenerateGetCStackPointerStub() {
2901 __ mv(A0, SP);
2902 __ ret();
2903}
2904
2905// Jump to a frame on the call stack.
2906// RA: return address.
2907// A0: program_counter.
2908// A1: stack_pointer.
2909// A2: frame_pointer.
2910// A3: thread.
2911// Does not return.
2912//
2913// Notice: We need to keep this in sync with `Simulator::JumpToFrame()`.
2914void StubCodeCompiler::GenerateJumpToFrameStub() {
2917 __ mv(CALLEE_SAVED_TEMP, A0); // Program counter.
2918 __ mv(SP, A1); // Stack pointer.
2919 __ mv(FP, A2); // Frame_pointer.
2920 __ mv(THR, A3);
2921#if defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_ANDROID)
2922 // We need to restore the shadow call stack pointer like longjmp would,
2923 // effectively popping all the return addresses between the Dart exit frame
2924 // and Exceptions::JumpToFrame, otherwise the shadow call stack might
2925 // eventually overflow.
2927#elif defined(USING_SHADOW_CALL_STACK)
2928#error Unimplemented
2929#endif
2930 Label exit_through_non_ffi;
2931 // Check if we exited generated from FFI. If so do transition - this is needed
2932 // because normally runtime calls transition back to generated via destructor
2933 // of TransitionGeneratedToVM/Native that is part of runtime boilerplate
2934 // code (see DEFINE_RUNTIME_ENTRY_IMPL in runtime_entry.h). Ffi calls don't
2935 // have this boilerplate, don't have this stack resource, have to transition
2936 // explicitly.
2937 __ LoadFromOffset(TMP, THR,
2939 __ LoadImmediate(TMP2, target::Thread::exit_through_ffi());
2940 __ bne(TMP, TMP2, &exit_through_non_ffi);
2941 __ TransitionNativeToGenerated(TMP, /*leave_safepoint=*/true,
2942 /*ignore_unwind_in_progress=*/true);
2943 __ Bind(&exit_through_non_ffi);
2944
2945 // Refresh pinned registers values (inc. write barrier mask and null object).
2946 __ RestorePinnedRegisters();
2947 // Set the tag.
2948 __ LoadImmediate(TMP, VMTag::kDartTagId);
2949 __ StoreToOffset(TMP, THR, target::Thread::vm_tag_offset());
2950 // Clear top exit frame.
2952 // Restore the pool pointer.
2953 __ RestoreCodePointer();
2954 if (FLAG_precompiled_mode) {
2955 __ SetupGlobalPoolAndDispatchTable();
2956 } else {
2957 __ LoadPoolPointer();
2958 }
2959 __ jr(CALLEE_SAVED_TEMP); // Jump to continuation point.
2960}
2961
2962// Run an exception handler. Execution comes from JumpToFrame
2963// stub or from the simulator.
2964//
2965// The arguments are stored in the Thread object.
2966// Does not return.
2967void StubCodeCompiler::GenerateRunExceptionHandlerStub() {
2968 // Exception object.
2972
2973 // StackTrace object.
2977
2978 __ LoadFromOffset(RA, THR, target::Thread::resume_pc_offset());
2979 __ ret(); // Jump to the exception handler code.
2980}
2981
2982// Deoptimize a frame on the call stack before rewinding.
2983// The arguments are stored in the Thread object.
2984// No result.
2985void StubCodeCompiler::GenerateDeoptForRewindStub() {
2986 // Push zap value instead of CODE_REG.
2987 __ LoadImmediate(TMP, kZapCodeReg);
2988 __ PushRegister(TMP);
2989
2990 // Load the deopt pc into RA.
2991 __ LoadFromOffset(RA, THR, target::Thread::resume_pc_offset());
2992 GenerateDeoptimizationSequence(assembler, kEagerDeopt);
2993
2994 // After we have deoptimized, jump to the correct frame.
2995 __ EnterStubFrame();
2996 __ CallRuntime(kRewindPostDeoptRuntimeEntry, 0);
2997 __ LeaveStubFrame();
2998 __ ebreak();
2999}
3000
3001// Calls to the runtime to optimize the given function.
3002// A0: function to be re-optimized.
3003// ARGS_DESC_REG: argument descriptor (preserved).
3004void StubCodeCompiler::GenerateOptimizeFunctionStub() {
3006 __ EnterStubFrame();
3007
3008 __ subi(SP, SP, 3 * target::kWordSize);
3009 __ sx(ARGS_DESC_REG,
3010 Address(SP, 2 * target::kWordSize)); // Preserves args descriptor.
3011 __ sx(ZR, Address(SP, 1 * target::kWordSize)); // Result slot.
3012 __ sx(A0, Address(SP, 0 * target::kWordSize)); // Function argument.
3013 __ CallRuntime(kOptimizeInvokedFunctionRuntimeEntry, 1);
3014 __ lx(FUNCTION_REG, Address(SP, 1 * target::kWordSize)); // Function result.
3015 __ lx(ARGS_DESC_REG,
3016 Address(SP, 2 * target::kWordSize)); // Restore args descriptor.
3017 __ addi(SP, SP, 3 * target::kWordSize);
3018
3019 __ LoadCompressedFieldFromOffset(CODE_REG, FUNCTION_REG,
3021 __ LoadFieldFromOffset(A1, FUNCTION_REG,
3023 __ LeaveStubFrame();
3024 __ jr(A1);
3025 __ ebreak();
3026}
3027
3028// Does identical check (object references are equal or not equal) with special
3029// checks for boxed numbers and returns with TMP = 0 iff left and right are
3030// identical.
3031static void GenerateIdenticalWithNumberCheckStub(Assembler* assembler,
3032 const Register left,
3033 const Register right) {
3034 Label reference_compare, check_mint, done;
3035 // If any of the arguments is Smi do reference compare.
3036 // Note: A Mint cannot contain a value that would fit in Smi.
3037 __ BranchIfSmi(left, &reference_compare, Assembler::kNearJump);
3038 __ BranchIfSmi(right, &reference_compare, Assembler::kNearJump);
3039
3040 // Value compare for two doubles.
3041 __ CompareClassId(left, kDoubleCid, /*scratch*/ TMP);
3042 __ BranchIf(NOT_EQUAL, &check_mint, Assembler::kNearJump);
3043 __ CompareClassId(right, kDoubleCid, /*scratch*/ TMP);
3044 __ BranchIf(NOT_EQUAL, &reference_compare, Assembler::kNearJump);
3045
3046 // Double values bitwise compare.
3047#if XLEN == 32
3048 __ lw(T0, FieldAddress(left, target::Double::value_offset()));
3049 __ lw(T1, FieldAddress(right, target::Double::value_offset()));
3050 __ xor_(TMP, T0, T1);
3051 __ lw(T0, FieldAddress(left, target::Double::value_offset() + 4));
3052 __ lw(T1, FieldAddress(right, target::Double::value_offset() + 4));
3053 __ xor_(TMP2, T0, T1);
3054 __ or_(TMP, TMP, TMP2);
3055#else
3056 __ ld(T0, FieldAddress(left, target::Double::value_offset()));
3057 __ ld(T1, FieldAddress(right, target::Double::value_offset()));
3058 __ xor_(TMP, T0, T1);
3059#endif
3061
3062 __ Bind(&check_mint);
3063 __ CompareClassId(left, kMintCid, /*scratch*/ TMP);
3064 __ BranchIf(NOT_EQUAL, &reference_compare, Assembler::kNearJump);
3065 __ CompareClassId(right, kMintCid, /*scratch*/ TMP);
3066 __ BranchIf(NOT_EQUAL, &reference_compare, Assembler::kNearJump);
3067#if XLEN == 32
3068 __ lw(T0, FieldAddress(left, target::Mint::value_offset()));
3069 __ lw(T1, FieldAddress(right, target::Mint::value_offset()));
3070 __ xor_(TMP, T0, T1);
3071 __ lw(T0, FieldAddress(left, target::Mint::value_offset() + 4));
3072 __ lw(T1, FieldAddress(right, target::Mint::value_offset() + 4));
3073 __ xor_(TMP2, T0, T1);
3074 __ or_(TMP, TMP, TMP2);
3075#else
3076 __ ld(T0, FieldAddress(left, target::Mint::value_offset()));
3077 __ ld(T1, FieldAddress(right, target::Mint::value_offset()));
3078 __ xor_(TMP, T0, T1);
3079#endif
3081
3082 __ Bind(&reference_compare);
3083 __ xor_(TMP, left, right);
3084 __ Bind(&done);
3085}
3086
3087// Called only from unoptimized code. All relevant registers have been saved.
3088// RA: return address.
3089// SP + 4: left operand.
3090// SP + 0: right operand.
3091// Return TMP set to 0 if equal.
3092void StubCodeCompiler::GenerateUnoptimizedIdenticalWithNumberCheckStub() {
3093#if !defined(PRODUCT)
3094 // Check single stepping.
3095 Label stepping, done_stepping;
3096 __ LoadIsolate(TMP);
3097 __ LoadFromOffset(TMP, TMP, target::Isolate::single_step_offset(),
3099 __ bnez(TMP, &stepping);
3100 __ Bind(&done_stepping);
3101#endif
3102
3103 const Register left = A0;
3104 const Register right = A1;
3105 __ LoadFromOffset(left, SP, 1 * target::kWordSize);
3106 __ LoadFromOffset(right, SP, 0 * target::kWordSize);
3107 GenerateIdenticalWithNumberCheckStub(assembler, left, right);
3108 __ ret();
3109
3110#if !defined(PRODUCT)
3111 __ Bind(&stepping);
3112 __ EnterStubFrame();
3113 __ CallRuntime(kSingleStepHandlerRuntimeEntry, 0);
3114 __ RestoreCodePointer();
3115 __ LeaveStubFrame();
3116 __ j(&done_stepping);
3117#endif
3118}
3119
3120// Called from optimized code only.
3121// RA: return address.
3122// SP + 4: left operand.
3123// SP + 0: right operand.
3124// Return TMP set to 0 if equal.
3125void StubCodeCompiler::GenerateOptimizedIdenticalWithNumberCheckStub() {
3126 const Register left = A0;
3127 const Register right = A1;
3128 __ LoadFromOffset(left, SP, 1 * target::kWordSize);
3129 __ LoadFromOffset(right, SP, 0 * target::kWordSize);
3130 GenerateIdenticalWithNumberCheckStub(assembler, left, right);
3131 __ ret();
3132}
3133
3134// Called from megamorphic call sites.
3135// A0: receiver (passed to target)
3136// IC_DATA_REG: MegamorphicCache (preserved)
3137// Passed to target:
3138// FUNCTION_REG: target function
3139// CODE_REG: target Code
3140// ARGS_DESC_REG: arguments descriptor
3141void StubCodeCompiler::GenerateMegamorphicCallStub() {
3142 // Jump if receiver is a smi.
3143 Label smi_case;
3144 __ BranchIfSmi(A0, &smi_case);
3145
3146 // Loads the cid of the object.
3147 __ LoadClassId(T5, A0);
3148
3149 Label cid_loaded;
3150 __ Bind(&cid_loaded);
3151 __ lx(T2,
3154 // T2: cache buckets array.
3155 // T1: mask as a smi.
3156
3157 // Make the cid into a smi.
3158 __ SmiTag(T5);
3159 // T5: class ID of the receiver (smi).
3160
3161 // Compute the table index.
3163 // Use lsl and sub to multiply with 7 == 8 - 1.
3164 __ slli(T3, T5, 3);
3165 __ sub(T3, T3, T5);
3166 // T3: probe.
3167 Label loop;
3168 __ Bind(&loop);
3169 __ and_(T3, T3, T1);
3170
3171 const intptr_t base = target::Array::data_offset();
3172 // T3 is smi tagged, but table entries are 16 bytes, so LSL 3.
3173 __ AddShifted(TMP, T2, T3, kCompressedWordSizeLog2);
3174 __ LoadCompressedSmiFieldFromOffset(T4, TMP, base);
3175 Label probe_failed;
3176 __ CompareObjectRegisters(T4, T5);
3177 __ BranchIf(NE, &probe_failed);
3178
3179 Label load_target;
3180 __ Bind(&load_target);
3181 // Call the target found in the cache. For a class id match, this is a
3182 // proper target for the given name and arguments descriptor. If the
3183 // illegal class id was found, the target is a cache miss handler that can
3184 // be invoked as a normal Dart function.
3185 __ LoadCompressed(FUNCTION_REG,
3186 FieldAddress(TMP, base + target::kCompressedWordSize));
3188 __ lx(ARGS_DESC_REG,
3189 FieldAddress(IC_DATA_REG,
3191 if (!FLAG_precompiled_mode) {
3192 __ LoadCompressed(
3194 }
3195 __ jr(A1); // T0: Function, argument to lazy compile stub.
3196
3197 // Probe failed, check if it is a miss.
3198 __ Bind(&probe_failed);
3199 ASSERT(kIllegalCid == 0);
3200 Label miss;
3201 __ beqz(T4, &miss); // branch if miss.
3202
3203 // Try next extry in the table.
3204 __ AddImmediate(T3, target::ToRawSmi(1));
3205 __ j(&loop);
3206
3207 // Load cid for the Smi case.
3208 __ Bind(&smi_case);
3209 __ LoadImmediate(T5, kSmiCid);
3210 __ j(&cid_loaded);
3211
3212 __ Bind(&miss);
3213 GenerateSwitchableCallMissStub();
3214}
3215
3216// Input:
3217// A0 - receiver
3218// IC_DATA_REG - icdata
3219void StubCodeCompiler::GenerateICCallThroughCodeStub() {
3220 Label loop, found, miss;
3221 __ lx(T1, FieldAddress(IC_DATA_REG, target::ICData::entries_offset()));
3222 __ lx(ARGS_DESC_REG,
3223 FieldAddress(IC_DATA_REG,
3226 // T1: first IC entry
3227 __ LoadTaggedClassIdMayBeSmi(A1, A0);
3228 // A1: receiver cid as Smi
3229
3230 __ Bind(&loop);
3231 __ LoadCompressedSmi(T2, Address(T1, 0));
3232 __ beq(A1, T2, &found);
3233 __ CompareImmediate(T2, target::ToRawSmi(kIllegalCid));
3234 __ BranchIf(EQ, &miss);
3235
3236 const intptr_t entry_length =
3237 target::ICData::TestEntryLengthFor(1, /*tracking_exactness=*/false) *
3239 __ AddImmediate(T1, entry_length); // Next entry.
3240 __ j(&loop);
3241
3242 __ Bind(&found);
3243 if (FLAG_precompiled_mode) {
3244 const intptr_t entry_offset =
3246 __ LoadCompressed(A1, Address(T1, entry_offset));
3247 __ lx(A1, FieldAddress(A1, target::Function::entry_point_offset()));
3248 } else {
3249 const intptr_t code_offset =
3251 __ LoadCompressed(CODE_REG, Address(T1, code_offset));
3252 __ lx(A1, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
3253 }
3254 __ jr(A1);
3255
3256 __ Bind(&miss);
3258 __ jr(A1);
3259}
3260
3261// Implement the monomorphic entry check for call-sites where the receiver
3262// might be a Smi.
3263//
3264// A0: receiver
3265// S5: MonomorphicSmiableCall object
3266//
3267// T1,T2: clobbered
3268void StubCodeCompiler::GenerateMonomorphicSmiableCheckStub() {
3269 Label miss;
3270 __ LoadClassIdMayBeSmi(T1, A0);
3271
3272 // Note: this stub is only used in AOT mode, hence the direct (bare) call.
3273 __ LoadField(
3274 T2,
3276 __ LoadField(
3277 TMP,
3279 __ bne(T1, T2, &miss);
3280 __ jr(TMP);
3281
3282 __ Bind(&miss);
3284 __ jr(TMP);
3285}
3286
3287// Called from switchable IC calls.
3288// A0: receiver
3289void StubCodeCompiler::GenerateSwitchableCallMissStub() {
3290 __ lx(CODE_REG,
3292 __ EnterStubFrame();
3293 // Preserve receiver, setup result slot,
3294 // pass Arg0: stub out and Arg1: Receiver.
3295 __ PushRegistersInOrder({A0, ZR, ZR, A0});
3296 __ CallRuntime(kSwitchableCallMissRuntimeEntry, 2);
3297 __ Drop(1);
3298 __ PopRegister(CODE_REG); // result = stub
3299 __ PopRegister(IC_DATA_REG); // result = IC
3300
3301 __ PopRegister(A0); // Restore receiver.
3302 __ LeaveStubFrame();
3303
3306 __ jr(TMP);
3307}
3308
3309// Called from switchable IC calls.
3310// A0: receiver
3311// S5: SingleTargetCache
3312// Passed to target:
3313// CODE_REG: target Code object
3314void StubCodeCompiler::GenerateSingleTargetCallStub() {
3315 Label miss;
3316 __ LoadClassIdMayBeSmi(A1, A0);
3319
3320 __ blt(A1, T2, &miss);
3321 __ bgt(A1, T3, &miss);
3322
3325 __ jr(TMP);
3326
3327 __ Bind(&miss);
3328 __ EnterStubFrame();
3329 // Preserve receiver, setup result slot,
3330 // pass Arg0: Stub out and Arg1: Receiver.
3331 __ PushRegistersInOrder({A0, ZR, ZR, A0});
3332 __ CallRuntime(kSwitchableCallMissRuntimeEntry, 2);
3333 __ Drop(1);
3334 __ PopRegister(CODE_REG); // result = stub
3335 __ PopRegister(S5); // result = IC
3336
3337 __ PopRegister(A0); // Restore receiver.
3338 __ LeaveStubFrame();
3339
3342 __ jr(TMP);
3343}
3344
3345static int GetScaleFactor(intptr_t size) {
3346 switch (size) {
3347 case 1:
3348 return 0;
3349 case 2:
3350 return 1;
3351 case 4:
3352 return 2;
3353 case 8:
3354 return 3;
3355 case 16:
3356 return 4;
3357 }
3358 UNREACHABLE();
3359 return -1;
3360}
3361
3362void StubCodeCompiler::GenerateAllocateTypedDataArrayStub(intptr_t cid) {
3364 const intptr_t max_len = TypedDataMaxNewSpaceElements(cid);
3365 const intptr_t scale_shift = GetScaleFactor(element_size);
3366
3369
3370 if (!FLAG_use_slow_path && FLAG_inline_alloc) {
3371 Label call_runtime;
3372 NOT_IN_PRODUCT(__ MaybeTraceAllocation(cid, &call_runtime, T3));
3374 /* Check that length is a positive Smi. */
3375 /* T3: requested array length argument. */
3376 __ BranchIfNotSmi(T3, &call_runtime);
3377 __ SmiUntag(T3);
3378 /* Check for length >= 0 && length <= max_len. */
3379 /* T3: untagged array length. */
3380 __ CompareImmediate(T3, max_len, kObjectBytes);
3381 __ BranchIf(UNSIGNED_GREATER, &call_runtime);
3382 if (scale_shift != 0) {
3383 __ slli(T3, T3, scale_shift);
3384 }
3385 const intptr_t fixed_size_plus_alignment_padding =
3388 __ AddImmediate(T3, fixed_size_plus_alignment_padding);
3390 __ lx(A0, Address(THR, target::Thread::top_offset()));
3391
3392 /* T3: allocation size. */
3393 __ add(T4, A0, T3);
3394 __ bltu(T4, A0, &call_runtime); /* Fail on unsigned overflow. */
3395
3396 /* Check if the allocation fits into the remaining space. */
3397 /* A0: potential new object start. */
3398 /* T4: potential next object start. */
3399 /* T3: allocation size. */
3400 __ lx(TMP, Address(THR, target::Thread::end_offset()));
3401 __ bgeu(T4, TMP, &call_runtime);
3402 __ CheckAllocationCanary(A0);
3403
3404 /* Successfully allocated the object(s), now update top to point to */
3405 /* next object start and initialize the object. */
3406 __ sx(T4, Address(THR, target::Thread::top_offset()));
3407 __ AddImmediate(A0, kHeapObjectTag);
3408 /* Initialize the tags. */
3409 /* A0: new object start as a tagged pointer. */
3410 /* T4: new object end address. */
3411 /* T3: allocation size. */
3412 {
3413 __ li(T5, 0);
3415 compiler::Label zero_tags;
3416 __ BranchIf(HI, &zero_tags);
3417 __ slli(T5, T3,
3420 __ Bind(&zero_tags);
3421
3422 /* Get the class index and insert it into the tags. */
3423 uword tags =
3424 target::MakeTagWordForNewSpaceObject(cid, /*instance_size=*/0);
3425 __ OrImmediate(T5, T5, tags);
3426 __ sx(T5, FieldAddress(A0, target::Object::tags_offset())); /* Tags. */
3427 }
3428 /* Set the length field. */
3429 /* A0: new object start as a tagged pointer. */
3430 /* T4: new object end address. */
3431 __ mv(T3, AllocateTypedDataArrayABI::kLengthReg); /* Array length. */
3432 __ StoreCompressedIntoObjectNoBarrier(
3433 A0, FieldAddress(A0, target::TypedDataBase::length_offset()), T3);
3434 /* Initialize all array elements to 0. */
3435 /* A0: new object start as a tagged pointer. */
3436 /* T4: new object end address. */
3437 /* T3: iterator which initially points to the start of the variable */
3438 /* R3: scratch register. */
3439 /* data area to be initialized. */
3440 __ AddImmediate(T3, A0, target::TypedData::HeaderSize() - 1);
3441 __ StoreInternalPointer(
3442 A0, FieldAddress(A0, target::PointerBase::data_offset()), T3);
3443 Label loop;
3444 __ Bind(&loop);
3445 for (intptr_t offset = 0; offset < target::kObjectAlignment;
3447 __ sx(ZR, Address(T3, offset));
3448 }
3449 // Safe to only check every kObjectAlignment bytes instead of each word.
3452 __ bltu(T3, T4, &loop);
3453 __ WriteAllocationCanary(T4); // Fix overshoot.
3454
3455 __ Ret();
3456
3457 __ Bind(&call_runtime);
3458 }
3459
3460 __ EnterStubFrame();
3461 __ PushRegister(ZR); // Result slot.
3462 __ PushImmediate(target::ToRawSmi(cid)); // Cid
3463 __ PushRegister(AllocateTypedDataArrayABI::kLengthReg); // Array length
3464 __ CallRuntime(kAllocateTypedDataRuntimeEntry, 2);
3465 __ Drop(2); // Drop arguments.
3467 __ LeaveStubFrame();
3468 __ Ret();
3469}
3470
3471} // namespace compiler
3472
3473} // namespace dart
3474
3475#endif // defined(TARGET_ARCH_RISCV)
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 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
GLenum type
virtual bool WillAllocateNewOrRemembered() const
Definition: il.h:7451
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:10864
static intptr_t ActivationFrameAlignment()
static StaticTypeExactnessState HasExactSuperType()
static StaticTypeExactnessState NotExact()
static constexpr intptr_t kMaxInputs
Definition: object.h:7705
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)
static word type_arguments_offset()
static const word kMaxNewSpaceElements
Definition: runtime_api.h:595
static bool TraceAllocation(const dart::Class &klass)
Definition: runtime_api.cc:543
static intptr_t NumTypeArguments(const dart::Class &klass)
Definition: runtime_api.cc:530
static uword GetInstanceSize(const dart::Class &handle)
Definition: runtime_api.cc:515
static const word kNoTypeArguments
Definition: runtime_api.h:486
static classid_t GetId(const dart::Class &handle)
Definition: runtime_api.cc:441
static intptr_t TypeArgumentsFieldOffset(const dart::Class &klass)
Definition: runtime_api.cc:539
static word entry_point_offset(CodeEntryKind kind=CodeEntryKind::kNormal)
static word variable_offset(intptr_t index)
static word entry_point_offset(CodeEntryKind kind=CodeEntryKind::kNormal)
static bool IsAllocatableInNewSpace(intptr_t instance_size)
static word ExactnessIndexFor(word num_args)
Definition: runtime_api.cc:615
static word TestEntryLengthFor(word num_args, bool exactness_check)
Definition: runtime_api.cc:619
static word receivers_static_type_offset()
static word CodeIndexFor(word num_args)
Definition: runtime_api.cc:603
static word TargetIndexFor(word num_args)
Definition: runtime_api.cc:611
static word CountIndexFor(word num_args)
Definition: runtime_api.cc:607
static word EntryPointIndexFor(word num_args)
Definition: runtime_api.cc:623
static word original_top_offset()
static const word kBytesPerCardLog2
Definition: runtime_api.h:1487
static word allocate_mint_without_fpu_regs_stub_offset()
static word allocate_object_slow_entry_point_offset()
static word auto_scope_native_wrapper_entry_point_offset()
static word lazy_deopt_from_throw_stub_offset()
static word active_exception_offset()
static word exit_through_ffi_offset()
static uword exit_through_runtime_call()
Definition: runtime_api.cc:919
static word new_marking_stack_block_offset()
static word invoke_dart_code_stub_offset()
static word saved_shadow_call_stack_offset()
static word write_error_shared_without_fpu_regs_stub_offset()
static word no_scope_native_wrapper_entry_point_offset()
static word top_exit_frame_info_offset()
static word range_error_shared_without_fpu_regs_stub_offset()
static word range_error_shared_with_fpu_regs_stub_offset()
static word fix_allocation_stub_code_offset()
static word switchable_call_miss_stub_offset()
static word fix_callers_target_code_offset()
static word store_buffer_block_offset()
static word deoptimize_stub_offset()
static word write_barrier_entry_point_offset()
static word lazy_deopt_from_return_stub_offset()
static word allocate_object_entry_point_offset()
static word switchable_call_miss_entry_offset()
static word active_stacktrace_offset()
static word allocate_mint_with_fpu_regs_stub_offset()
static word bootstrap_native_wrapper_entry_point_offset()
static word write_error_shared_with_fpu_regs_stub_offset()
static word write_barrier_mask_offset()
static word call_to_runtime_stub_offset()
static word execution_state_offset()
static word old_marking_stack_block_offset()
static const word kGenerationalBarrierMask
Definition: runtime_api.h:434
#define UNIMPLEMENTED
#define ASSERT(E)
glong glong end
const uint8_t uint32_t uint32_t GError ** error
uint32_t * target
Dart_NativeFunction function
Definition: fuchsia.cc:51
size_t length
SK_API bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
uword MakeTagWordForNewSpaceObject(classid_t cid, uword instance_size)
Definition: runtime_api.cc:360
static constexpr word kBitsPerWordLog2
Definition: runtime_api.h:290
word ToRawSmi(const dart::Object &a)
Definition: runtime_api.cc:960
static constexpr intptr_t kWordSize
Definition: runtime_api.h:274
static constexpr intptr_t kCompressedWordSize
Definition: runtime_api.h:286
bool SizeFitsInSizeTag(uword instance_size)
Definition: runtime_api.cc:355
static constexpr intptr_t kObjectAlignment
Definition: runtime_api.h:313
FrameLayout frame_layout
Definition: stack_frame.cc:76
word TypedDataMaxNewSpaceElements(classid_t cid)
Definition: runtime_api.cc:255
word TypedDataElementSizeInBytes(classid_t cid)
Definition: runtime_api.cc:251
const Bool & TrueObject()
Definition: runtime_api.cc:157
GrowableArray< UnresolvedPcRelativeCall * > UnresolvedPcRelativeCalls
bool IsSameObject(const Object &a, const Object &b)
Definition: runtime_api.cc:60
const Bool & FalseObject()
Definition: runtime_api.cc:161
const Object & NullObject()
Definition: runtime_api.cc:149
const Code & StubCodeAllocateArray()
Definition: runtime_api.cc:294
const Class & MintClass()
Definition: runtime_api.cc:190
Definition: dart_vm.cc:33
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 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
constexpr intptr_t kWordSizeLog2
Definition: globals.h:507
uintptr_t uword
Definition: globals.h:501
void DLRT_ExitTemporaryIsolate()
const Register CODE_REG
@ UNSIGNED_GREATER
@ UNSIGNED_GREATER_EQUAL
@ NOT_EQUAL
@ UNSIGNED_LESS_EQUAL
const Register TMP2
const Register ARGS_DESC_REG
@ kNumberOfCpuRegisters
Definition: constants_arm.h:98
const int kNumberOfFpuRegisters
static constexpr bool IsArgumentRegister(Register reg)
Definition: constants.h:77
constexpr RegList kDartAvailableCpuRegs
const Register TMP
const Register FPREG
const intptr_t cid
const intptr_t kStoreBufferWrapperSize
const Register FUNCTION_REG
const Register IC_DATA_REG
static constexpr intptr_t kAllocationRedZoneSize
Definition: page.h:41
const Register PP
static constexpr uword kZapCodeReg
const Register kStackTraceObjectReg
NOT_IN_PRODUCT(LibraryPtr ReloadTestScript(const char *script))
@ kSmiTagSize
@ kHeapObjectTag
@ kSmiTagMask
const Register SPREG
COMPILE_ASSERT(kUnreachableReference==WeakTable::kNoValue)
const int kFpuRegisterSize
@ kLazyDeoptFromThrow
@ kLazyDeoptFromReturn
def call(args)
Definition: dom.py:159
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dst
Definition: cp.py:12
static bool Bind(PassBindingsCacheMTL &pass, ShaderStage stage, size_t bind_index, const BufferView &view)
Definition: update.py:1
SeparatedVector2 offset
static constexpr Register kResultReg
static constexpr Register kLengthReg
static constexpr Register kTypeArgumentsReg
static constexpr Register kResultReg
static constexpr Register kTempReg
static constexpr Register kTypeArgumentsReg
static constexpr Register kResultReg
static constexpr Register kTagsReg
static constexpr Register kLengthReg
static constexpr Register kResultReg
static constexpr Register kClassIdReg
intptr_t first_local_from_fp
Definition: frame_layout.h:37
static constexpr intptr_t kNewObjectBitPosition
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