Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(750)

Side by Side Diff: runtime/vm/stub_code_mips.cc

Issue 2858623002: Remove MIPS support (Closed)
Patch Set: Merge and cleanup Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « runtime/vm/stack_frame_mips.h ('k') | runtime/vm/stub_code_mips_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 #include "vm/globals.h"
6 #if defined(TARGET_ARCH_MIPS)
7
8 #include "vm/assembler.h"
9 #include "vm/compiler.h"
10 #include "vm/dart_entry.h"
11 #include "vm/flow_graph_compiler.h"
12 #include "vm/heap.h"
13 #include "vm/instructions.h"
14 #include "vm/object_store.h"
15 #include "vm/runtime_entry.h"
16 #include "vm/stack_frame.h"
17 #include "vm/stub_code.h"
18 #include "vm/tags.h"
19
20 #define __ assembler->
21
22 namespace dart {
23
24 DEFINE_FLAG(bool, inline_alloc, true, "Inline allocation of objects.");
25 DEFINE_FLAG(bool,
26 use_slow_path,
27 false,
28 "Set to true for debugging & verifying the slow paths.");
29 DECLARE_FLAG(bool, trace_optimized_ic_calls);
30
31 // Input parameters:
32 // RA : return address.
33 // SP : address of last argument in argument array.
34 // SP + 4*S4 - 4 : address of first argument in argument array.
35 // SP + 4*S4 : address of return value.
36 // S5 : address of the runtime function to call.
37 // S4 : number of arguments to the call.
38 void StubCode::GenerateCallToRuntimeStub(Assembler* assembler) {
39 const intptr_t thread_offset = NativeArguments::thread_offset();
40 const intptr_t argc_tag_offset = NativeArguments::argc_tag_offset();
41 const intptr_t argv_offset = NativeArguments::argv_offset();
42 const intptr_t retval_offset = NativeArguments::retval_offset();
43
44 __ SetPrologueOffset();
45 __ Comment("CallToRuntimeStub");
46 __ EnterStubFrame();
47
48 // Save exit frame information to enable stack walking as we are about
49 // to transition to Dart VM C++ code.
50 __ sw(FP, Address(THR, Thread::top_exit_frame_info_offset()));
51
52 #if defined(DEBUG)
53 {
54 Label ok;
55 // Check that we are always entering from Dart code.
56 __ lw(T0, Assembler::VMTagAddress());
57 __ BranchEqual(T0, Immediate(VMTag::kDartTagId), &ok);
58 __ Stop("Not coming from Dart code.");
59 __ Bind(&ok);
60 }
61 #endif
62
63 // Mark that the thread is executing VM code.
64 __ sw(S5, Assembler::VMTagAddress());
65
66 // Reserve space for arguments and align frame before entering C++ world.
67 // NativeArguments are passed in registers.
68 ASSERT(sizeof(NativeArguments) == 4 * kWordSize);
69 __ ReserveAlignedFrameSpace(4 * kWordSize); // Reserve space for arguments.
70
71 // Pass NativeArguments structure by value and call runtime.
72 // Registers A0, A1, A2, and A3 are used.
73
74 ASSERT(thread_offset == 0 * kWordSize);
75 // Set thread in NativeArgs.
76 __ mov(A0, THR);
77
78 // There are no runtime calls to closures, so we do not need to set the tag
79 // bits kClosureFunctionBit and kInstanceFunctionBit in argc_tag_.
80 ASSERT(argc_tag_offset == 1 * kWordSize);
81 __ mov(A1, S4); // Set argc in NativeArguments.
82
83 ASSERT(argv_offset == 2 * kWordSize);
84 __ sll(A2, S4, 2);
85 __ addu(A2, FP, A2); // Compute argv.
86 // Set argv in NativeArguments.
87 __ addiu(A2, A2, Immediate(kParamEndSlotFromFp * kWordSize));
88
89
90 // Call runtime or redirection via simulator.
91 // We defensively always jalr through T9 because it is sometimes required by
92 // the MIPS ABI.
93 __ mov(T9, S5);
94 __ jalr(T9);
95
96 ASSERT(retval_offset == 3 * kWordSize);
97 // Retval is next to 1st argument.
98 __ delay_slot()->addiu(A3, A2, Immediate(kWordSize));
99 __ Comment("CallToRuntimeStub return");
100
101 // Mark that the thread is executing Dart code.
102 __ LoadImmediate(A2, VMTag::kDartTagId);
103 __ sw(A2, Assembler::VMTagAddress());
104
105 // Reset exit frame information in Isolate structure.
106 __ sw(ZR, Address(THR, Thread::top_exit_frame_info_offset()));
107
108 __ LeaveStubFrameAndReturn();
109 }
110
111
112 // Print the stop message.
113 DEFINE_LEAF_RUNTIME_ENTRY(void, PrintStopMessage, 1, const char* message) {
114 OS::Print("Stop message: %s\n", message);
115 }
116 END_LEAF_RUNTIME_ENTRY
117
118
119 // Input parameters:
120 // A0 : stop message (const char*).
121 // Must preserve all registers.
122 void StubCode::GeneratePrintStopMessageStub(Assembler* assembler) {
123 __ EnterCallRuntimeFrame(0);
124 // Call the runtime leaf function. A0 already contains the parameter.
125 __ CallRuntime(kPrintStopMessageRuntimeEntry, 1);
126 __ LeaveCallRuntimeFrame();
127 __ Ret();
128 }
129
130
131 // Input parameters:
132 // RA : return address.
133 // SP : address of return value.
134 // T5 : address of the native function to call.
135 // A2 : address of first argument in argument array.
136 // A1 : argc_tag including number of arguments and function kind.
137 static void GenerateCallNativeWithWrapperStub(Assembler* assembler,
138 Address wrapper) {
139 const intptr_t thread_offset = NativeArguments::thread_offset();
140 const intptr_t argc_tag_offset = NativeArguments::argc_tag_offset();
141 const intptr_t argv_offset = NativeArguments::argv_offset();
142 const intptr_t retval_offset = NativeArguments::retval_offset();
143
144 __ SetPrologueOffset();
145 __ Comment("CallNativeCFunctionStub");
146 __ EnterStubFrame();
147
148 // Save exit frame information to enable stack walking as we are about
149 // to transition to native code.
150 __ sw(FP, Address(THR, Thread::top_exit_frame_info_offset()));
151
152 #if defined(DEBUG)
153 {
154 Label ok;
155 // Check that we are always entering from Dart code.
156 __ lw(T0, Assembler::VMTagAddress());
157 __ BranchEqual(T0, Immediate(VMTag::kDartTagId), &ok);
158 __ Stop("Not coming from Dart code.");
159 __ Bind(&ok);
160 }
161 #endif
162
163 // Mark that the thread is executing native code.
164 __ sw(T5, Assembler::VMTagAddress());
165
166 // Initialize NativeArguments structure and call native function.
167 // Registers A0, A1, A2, and A3 are used.
168
169 ASSERT(thread_offset == 0 * kWordSize);
170 // Set thread in NativeArgs.
171 __ mov(A0, THR);
172
173 // There are no native calls to closures, so we do not need to set the tag
174 // bits kClosureFunctionBit and kInstanceFunctionBit in argc_tag_.
175 ASSERT(argc_tag_offset == 1 * kWordSize);
176 // Set argc in NativeArguments: A1 already contains argc.
177
178 ASSERT(argv_offset == 2 * kWordSize);
179 // Set argv in NativeArguments: A2 already contains argv.
180
181 ASSERT(retval_offset == 3 * kWordSize);
182 // Set retval in NativeArgs.
183 __ addiu(A3, FP, Immediate(kCallerSpSlotFromFp * kWordSize));
184
185 // Passing the structure by value as in runtime calls would require changing
186 // Dart API for native functions.
187 // For now, space is reserved on the stack and we pass a pointer to it.
188 __ addiu(SP, SP, Immediate(-4 * kWordSize));
189 __ sw(A3, Address(SP, 3 * kWordSize));
190 __ sw(A2, Address(SP, 2 * kWordSize));
191 __ sw(A1, Address(SP, 1 * kWordSize));
192 __ sw(A0, Address(SP, 0 * kWordSize));
193 __ mov(A0, SP); // Pass the pointer to the NativeArguments.
194
195
196 __ mov(A1, T5); // Pass the function entrypoint.
197 __ ReserveAlignedFrameSpace(2 * kWordSize); // Just passing A0, A1.
198
199 // Call native wrapper function or redirection via simulator.
200 __ lw(T9, wrapper);
201 __ jalr(T9);
202 __ Comment("CallNativeCFunctionStub return");
203
204 // Mark that the thread is executing Dart code.
205 __ LoadImmediate(A2, VMTag::kDartTagId);
206 __ sw(A2, Assembler::VMTagAddress());
207
208 // Reset exit frame information in Isolate structure.
209 __ sw(ZR, Address(THR, Thread::top_exit_frame_info_offset()));
210
211 __ LeaveStubFrameAndReturn();
212 }
213
214
215 void StubCode::GenerateCallNoScopeNativeStub(Assembler* assembler) {
216 GenerateCallNativeWithWrapperStub(
217 assembler,
218 Address(THR, Thread::no_scope_native_wrapper_entry_point_offset()));
219 }
220
221
222 void StubCode::GenerateCallAutoScopeNativeStub(Assembler* assembler) {
223 GenerateCallNativeWithWrapperStub(
224 assembler,
225 Address(THR, Thread::auto_scope_native_wrapper_entry_point_offset()));
226 }
227
228
229 // Input parameters:
230 // RA : return address.
231 // SP : address of return value.
232 // T5 : address of the native function to call.
233 // A2 : address of first argument in argument array.
234 // A1 : argc_tag including number of arguments and function kind.
235 void StubCode::GenerateCallBootstrapNativeStub(Assembler* assembler) {
236 const intptr_t thread_offset = NativeArguments::thread_offset();
237 const intptr_t argc_tag_offset = NativeArguments::argc_tag_offset();
238 const intptr_t argv_offset = NativeArguments::argv_offset();
239 const intptr_t retval_offset = NativeArguments::retval_offset();
240
241 __ SetPrologueOffset();
242 __ Comment("CallNativeCFunctionStub");
243 __ EnterStubFrame();
244
245 // Save exit frame information to enable stack walking as we are about
246 // to transition to native code.
247 __ sw(FP, Address(THR, Thread::top_exit_frame_info_offset()));
248
249 #if defined(DEBUG)
250 {
251 Label ok;
252 // Check that we are always entering from Dart code.
253 __ lw(T0, Assembler::VMTagAddress());
254 __ BranchEqual(T0, Immediate(VMTag::kDartTagId), &ok);
255 __ Stop("Not coming from Dart code.");
256 __ Bind(&ok);
257 }
258 #endif
259
260 // Mark that the thread is executing native code.
261 __ sw(T5, Assembler::VMTagAddress());
262
263 // Initialize NativeArguments structure and call native function.
264 // Registers A0, A1, A2, and A3 are used.
265
266 ASSERT(thread_offset == 0 * kWordSize);
267 // Set thread in NativeArgs.
268 __ mov(A0, THR);
269
270 // There are no native calls to closures, so we do not need to set the tag
271 // bits kClosureFunctionBit and kInstanceFunctionBit in argc_tag_.
272 ASSERT(argc_tag_offset == 1 * kWordSize);
273 // Set argc in NativeArguments: A1 already contains argc.
274
275 ASSERT(argv_offset == 2 * kWordSize);
276 // Set argv in NativeArguments: A2 already contains argv.
277
278 ASSERT(retval_offset == 3 * kWordSize);
279 // Set retval in NativeArgs.
280 __ addiu(A3, FP, Immediate(kCallerSpSlotFromFp * kWordSize));
281
282 // Passing the structure by value as in runtime calls would require changing
283 // Dart API for native functions.
284 // For now, space is reserved on the stack and we pass a pointer to it.
285 __ addiu(SP, SP, Immediate(-4 * kWordSize));
286 __ sw(A3, Address(SP, 3 * kWordSize));
287 __ sw(A2, Address(SP, 2 * kWordSize));
288 __ sw(A1, Address(SP, 1 * kWordSize));
289 __ sw(A0, Address(SP, 0 * kWordSize));
290 __ mov(A0, SP); // Pass the pointer to the NativeArguments.
291
292 __ ReserveAlignedFrameSpace(kWordSize); // Just passing A0.
293
294 // Call native function or redirection via simulator.
295
296 // We defensively always jalr through T9 because it is sometimes required by
297 // the MIPS ABI.
298 __ mov(T9, T5);
299 __ jalr(T9);
300 __ Comment("CallNativeCFunctionStub return");
301
302 // Mark that the thread is executing Dart code.
303 __ LoadImmediate(A2, VMTag::kDartTagId);
304 __ sw(A2, Assembler::VMTagAddress());
305
306 // Reset exit frame information in Isolate structure.
307 __ sw(ZR, Address(THR, Thread::top_exit_frame_info_offset()));
308
309 __ LeaveStubFrameAndReturn();
310 }
311
312
313 // Input parameters:
314 // S4: arguments descriptor array.
315 void StubCode::GenerateCallStaticFunctionStub(Assembler* assembler) {
316 __ Comment("CallStaticFunctionStub");
317 __ EnterStubFrame();
318 // Setup space on stack for return value and preserve arguments descriptor.
319
320 __ addiu(SP, SP, Immediate(-2 * kWordSize));
321 __ sw(S4, Address(SP, 1 * kWordSize));
322 __ sw(ZR, Address(SP, 0 * kWordSize));
323
324 __ CallRuntime(kPatchStaticCallRuntimeEntry, 0);
325 __ Comment("CallStaticFunctionStub return");
326
327 // Get Code object result and restore arguments descriptor array.
328 __ lw(CODE_REG, Address(SP, 0 * kWordSize));
329 __ lw(S4, Address(SP, 1 * kWordSize));
330 __ addiu(SP, SP, Immediate(2 * kWordSize));
331
332 __ lw(T0, FieldAddress(CODE_REG, Code::entry_point_offset()));
333
334 // Remove the stub frame as we are about to jump to the dart function.
335 __ LeaveStubFrameAndReturn(T0);
336 }
337
338
339 // Called from a static call only when an invalid code has been entered
340 // (invalid because its function was optimized or deoptimized).
341 // S4: arguments descriptor array.
342 void StubCode::GenerateFixCallersTargetStub(Assembler* assembler) {
343 // Load code pointer to this stub from the thread:
344 // The one that is passed in, is not correct - it points to the code object
345 // that needs to be replaced.
346 __ lw(CODE_REG, Address(THR, Thread::fix_callers_target_code_offset()));
347 // Create a stub frame as we are pushing some objects on the stack before
348 // calling into the runtime.
349 __ EnterStubFrame();
350 // Setup space on stack for return value and preserve arguments descriptor.
351 __ addiu(SP, SP, Immediate(-2 * kWordSize));
352 __ sw(S4, Address(SP, 1 * kWordSize));
353 __ sw(ZR, Address(SP, 0 * kWordSize));
354 __ CallRuntime(kFixCallersTargetRuntimeEntry, 0);
355 // Get Code object result and restore arguments descriptor array.
356 __ lw(CODE_REG, Address(SP, 0 * kWordSize));
357 __ lw(S4, Address(SP, 1 * kWordSize));
358 __ addiu(SP, SP, Immediate(2 * kWordSize));
359
360 // Jump to the dart function.
361 __ lw(T0, FieldAddress(CODE_REG, Code::entry_point_offset()));
362
363 // Remove the stub frame.
364 __ LeaveStubFrameAndReturn(T0);
365 }
366
367
368 // Called from object allocate instruction when the allocation stub has been
369 // disabled.
370 void StubCode::GenerateFixAllocationStubTargetStub(Assembler* assembler) {
371 // Load code pointer to this stub from the thread:
372 // The one that is passed in, is not correct - it points to the code object
373 // that needs to be replaced.
374 __ lw(CODE_REG, Address(THR, Thread::fix_allocation_stub_code_offset()));
375 __ EnterStubFrame();
376 // Setup space on stack for return value.
377 __ addiu(SP, SP, Immediate(-1 * kWordSize));
378 __ sw(ZR, Address(SP, 0 * kWordSize));
379 __ CallRuntime(kFixAllocationStubTargetRuntimeEntry, 0);
380 // Get Code object result.
381 __ lw(CODE_REG, Address(SP, 0 * kWordSize));
382 __ addiu(SP, SP, Immediate(1 * kWordSize));
383
384 // Jump to the dart function.
385 __ lw(T0, FieldAddress(CODE_REG, Code::entry_point_offset()));
386
387 // Remove the stub frame.
388 __ LeaveStubFrameAndReturn(T0);
389 }
390
391
392 // Input parameters:
393 // A1: Smi-tagged argument count, may be zero.
394 // FP[kParamEndSlotFromFp + 1]: Last argument.
395 static void PushArgumentsArray(Assembler* assembler) {
396 __ Comment("PushArgumentsArray");
397 // Allocate array to store arguments of caller.
398 __ LoadObject(A0, Object::null_object());
399 // A0: Null element type for raw Array.
400 // A1: Smi-tagged argument count, may be zero.
401 __ BranchLink(*StubCode::AllocateArray_entry());
402 __ Comment("PushArgumentsArray return");
403 // V0: newly allocated array.
404 // A1: Smi-tagged argument count, may be zero (was preserved by the stub).
405 __ Push(V0); // Array is in V0 and on top of stack.
406 __ sll(T1, A1, 1);
407 __ addu(T1, FP, T1);
408 __ AddImmediate(T1, kParamEndSlotFromFp * kWordSize);
409 // T1: address of first argument on stack.
410 // T2: address of first argument in array.
411
412 Label loop, loop_exit;
413 __ blez(A1, &loop_exit);
414 __ delay_slot()->addiu(T2, V0,
415 Immediate(Array::data_offset() - kHeapObjectTag));
416 __ Bind(&loop);
417 __ lw(T3, Address(T1));
418 __ addiu(A1, A1, Immediate(-Smi::RawValue(1)));
419 __ addiu(T1, T1, Immediate(-kWordSize));
420 __ addiu(T2, T2, Immediate(kWordSize));
421 __ bgez(A1, &loop);
422 __ delay_slot()->sw(T3, Address(T2, -kWordSize));
423 __ Bind(&loop_exit);
424 }
425
426
427 // Used by eager and lazy deoptimization. Preserve result in V0 if necessary.
428 // This stub translates optimized frame into unoptimized frame. The optimized
429 // frame can contain values in registers and on stack, the unoptimized
430 // frame contains all values on stack.
431 // Deoptimization occurs in following steps:
432 // - Push all registers that can contain values.
433 // - Call C routine to copy the stack and saved registers into temporary buffer.
434 // - Adjust caller's frame to correct unoptimized frame size.
435 // - Fill the unoptimized frame.
436 // - Materialize objects that require allocation (e.g. Double instances).
437 // GC can occur only after frame is fully rewritten.
438 // Stack after EnterFrame(...) below:
439 // +------------------+
440 // | Saved PP | <- TOS
441 // +------------------+
442 // | Saved CODE_REG |
443 // +------------------+
444 // | Saved FP | <- FP of stub
445 // +------------------+
446 // | Saved LR | (deoptimization point)
447 // +------------------+
448 // | Saved CODE_REG |
449 // +------------------+
450 // | ... | <- SP of optimized frame
451 //
452 // Parts of the code cannot GC, part of the code can GC.
453 static void GenerateDeoptimizationSequence(Assembler* assembler,
454 DeoptStubKind kind) {
455 const intptr_t kPushedRegistersSize =
456 kNumberOfCpuRegisters * kWordSize + kNumberOfFRegisters * kWordSize;
457
458 __ SetPrologueOffset();
459 __ Comment("GenerateDeoptimizationSequence");
460 // DeoptimizeCopyFrame expects a Dart frame.
461 __ EnterStubFrame(kPushedRegistersSize);
462
463 // The code in this frame may not cause GC. kDeoptimizeCopyFrameRuntimeEntry
464 // and kDeoptimizeFillFrameRuntimeEntry are leaf runtime calls.
465 const intptr_t saved_result_slot_from_fp =
466 kFirstLocalSlotFromFp + 1 - (kNumberOfCpuRegisters - V0);
467 const intptr_t saved_exception_slot_from_fp =
468 kFirstLocalSlotFromFp + 1 - (kNumberOfCpuRegisters - V0);
469 const intptr_t saved_stacktrace_slot_from_fp =
470 kFirstLocalSlotFromFp + 1 - (kNumberOfCpuRegisters - V1);
471 // Result in V0 is preserved as part of pushing all registers below.
472
473 // Push registers in their enumeration order: lowest register number at
474 // lowest address.
475 for (int i = 0; i < kNumberOfCpuRegisters; i++) {
476 const int slot = kNumberOfCpuRegisters - i;
477 Register reg = static_cast<Register>(i);
478 if (reg == CODE_REG) {
479 // Save the original value of CODE_REG pushed before invoking this stub
480 // instead of the value used to call this stub.
481 COMPILE_ASSERT(TMP < CODE_REG); // Assert TMP is pushed first.
482 __ lw(TMP, Address(FP, kCallerSpSlotFromFp * kWordSize));
483 __ sw(TMP, Address(SP, kPushedRegistersSize - slot * kWordSize));
484 } else {
485 __ sw(reg, Address(SP, kPushedRegistersSize - slot * kWordSize));
486 }
487 }
488 for (int i = 0; i < kNumberOfFRegisters; i++) {
489 // These go below the CPU registers.
490 const int slot = kNumberOfCpuRegisters + kNumberOfFRegisters - i;
491 FRegister reg = static_cast<FRegister>(i);
492 __ swc1(reg, Address(SP, kPushedRegistersSize - slot * kWordSize));
493 }
494
495 __ mov(A0, SP); // Pass address of saved registers block.
496 bool is_lazy =
497 (kind == kLazyDeoptFromReturn) || (kind == kLazyDeoptFromThrow);
498 __ LoadImmediate(A1, is_lazy ? 1 : 0);
499 __ ReserveAlignedFrameSpace(1 * kWordSize);
500 __ CallRuntime(kDeoptimizeCopyFrameRuntimeEntry, 2);
501 // Result (V0) is stack-size (FP - SP) in bytes, incl. the return address.
502
503 if (kind == kLazyDeoptFromReturn) {
504 // Restore result into T1 temporarily.
505 __ lw(T1, Address(FP, saved_result_slot_from_fp * kWordSize));
506 } else if (kind == kLazyDeoptFromThrow) {
507 // Restore result into T1 temporarily.
508 __ lw(T1, Address(FP, saved_exception_slot_from_fp * kWordSize));
509 __ lw(T2, Address(FP, saved_stacktrace_slot_from_fp * kWordSize));
510 }
511
512 __ RestoreCodePointer();
513 __ LeaveDartFrame();
514 __ subu(SP, FP, V0);
515
516 // DeoptimizeFillFrame expects a Dart frame, i.e. EnterDartFrame(0), but there
517 // is no need to set the correct PC marker or load PP, since they get patched.
518 __ EnterStubFrame();
519
520 __ mov(A0, FP); // Get last FP address.
521 if (kind == kLazyDeoptFromReturn) {
522 __ Push(T1); // Preserve result as first local.
523 } else if (kind == kLazyDeoptFromThrow) {
524 __ Push(T1); // Preserve exception as first local.
525 __ Push(T2); // Preserve stacktrace as second local.
526 }
527 __ ReserveAlignedFrameSpace(1 * kWordSize);
528 __ CallRuntime(kDeoptimizeFillFrameRuntimeEntry, 1); // Pass last FP in A0.
529 if (kind == kLazyDeoptFromReturn) {
530 // Restore result into T1.
531 __ lw(T1, Address(FP, kFirstLocalSlotFromFp * kWordSize));
532 } else if (kind == kLazyDeoptFromThrow) {
533 // Restore result into T1.
534 __ lw(T1, Address(FP, kFirstLocalSlotFromFp * kWordSize));
535 __ lw(T2, Address(FP, (kFirstLocalSlotFromFp - 1) * kWordSize));
536 }
537 // Code above cannot cause GC.
538 __ RestoreCodePointer();
539 __ LeaveStubFrame();
540
541 // Frame is fully rewritten at this point and it is safe to perform a GC.
542 // Materialize any objects that were deferred by FillFrame because they
543 // require allocation.
544 // Enter stub frame with loading PP. The caller's PP is not materialized yet.
545 __ EnterStubFrame();
546 if (kind == kLazyDeoptFromReturn) {
547 __ Push(T1); // Preserve result, it will be GC-d here.
548 } else if (kind == kLazyDeoptFromThrow) {
549 __ Push(T1); // Preserve exception, it will be GC-d here.
550 __ Push(T2); // Preserve stacktrace, it will be GC-d here.
551 }
552 __ PushObject(Smi::ZoneHandle()); // Space for the result.
553 __ CallRuntime(kDeoptimizeMaterializeRuntimeEntry, 0);
554 // Result tells stub how many bytes to remove from the expression stack
555 // of the bottom-most frame. They were used as materialization arguments.
556 __ Pop(T1);
557 if (kind == kLazyDeoptFromReturn) {
558 __ Pop(V0); // Restore result.
559 } else if (kind == kLazyDeoptFromThrow) {
560 __ Pop(V1); // Restore stacktrace.
561 __ Pop(V0); // Restore exception.
562 }
563 __ LeaveStubFrame();
564 // Remove materialization arguments.
565 __ SmiUntag(T1);
566 __ addu(SP, SP, T1);
567 // The caller is responsible for emitting the return instruction.
568 }
569
570 // V0: result, must be preserved
571 void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
572 // Push zap value instead of CODE_REG for lazy deopt.
573 __ LoadImmediate(TMP, kZapCodeReg);
574 __ Push(TMP);
575 // Return address for "call" to deopt stub.
576 __ LoadImmediate(RA, kZapReturnAddress);
577 __ lw(CODE_REG, Address(THR, Thread::lazy_deopt_from_return_stub_offset()));
578 GenerateDeoptimizationSequence(assembler, kLazyDeoptFromReturn);
579 __ Ret();
580 }
581
582
583 // V0: exception, must be preserved
584 // V1: stacktrace, must be preserved
585 void StubCode::GenerateDeoptimizeLazyFromThrowStub(Assembler* assembler) {
586 // Push zap value instead of CODE_REG for lazy deopt.
587 __ LoadImmediate(TMP, kZapCodeReg);
588 __ Push(TMP);
589 // Return address for "call" to deopt stub.
590 __ LoadImmediate(RA, kZapReturnAddress);
591 __ lw(CODE_REG, Address(THR, Thread::lazy_deopt_from_throw_stub_offset()));
592 GenerateDeoptimizationSequence(assembler, kLazyDeoptFromThrow);
593 __ Ret();
594 }
595
596
597 void StubCode::GenerateDeoptimizeStub(Assembler* assembler) {
598 GenerateDeoptimizationSequence(assembler, kEagerDeopt);
599 __ Ret();
600 }
601
602
603 static void GenerateDispatcherCode(Assembler* assembler,
604 Label* call_target_function) {
605 __ Comment("NoSuchMethodDispatch");
606 // When lazily generated invocation dispatchers are disabled, the
607 // miss-handler may return null.
608 __ BranchNotEqual(T0, Object::null_object(), call_target_function);
609 __ EnterStubFrame();
610 // Load the receiver.
611 __ lw(A1, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
612 __ sll(TMP, A1, 1); // A1 is a Smi.
613 __ addu(TMP, FP, TMP);
614 __ lw(T6, Address(TMP, kParamEndSlotFromFp * kWordSize));
615
616 // Push space for the return value.
617 // Push the receiver.
618 // Push ICData/MegamorphicCache object.
619 // Push arguments descriptor array.
620 // Push original arguments array.
621 __ addiu(SP, SP, Immediate(-4 * kWordSize));
622 __ sw(ZR, Address(SP, 3 * kWordSize));
623 __ sw(T6, Address(SP, 2 * kWordSize));
624 __ sw(S5, Address(SP, 1 * kWordSize));
625 __ sw(S4, Address(SP, 0 * kWordSize));
626
627 // Adjust arguments count.
628 __ lw(TMP, FieldAddress(S4, ArgumentsDescriptor::type_args_len_offset()));
629 Label args_count_ok;
630 __ BranchEqual(TMP, Immediate(0), &args_count_ok);
631 __ AddImmediate(A1, A1, Smi::RawValue(1)); // Include the type arguments.
632 __ Bind(&args_count_ok);
633
634 // A1: Smi-tagged arguments array length.
635 PushArgumentsArray(assembler);
636 const intptr_t kNumArgs = 4;
637 __ CallRuntime(kInvokeNoSuchMethodDispatcherRuntimeEntry, kNumArgs);
638 __ lw(V0, Address(SP, 4 * kWordSize)); // Return value.
639 __ addiu(SP, SP, Immediate(5 * kWordSize));
640 __ LeaveStubFrame();
641 __ Ret();
642 }
643
644
645 void StubCode::GenerateMegamorphicMissStub(Assembler* assembler) {
646 __ EnterStubFrame();
647
648 // Load the receiver.
649 __ lw(T2, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
650 __ sll(T2, T2, 1); // T2 is a Smi.
651 __ addu(TMP, FP, T2);
652 __ lw(T6, Address(TMP, kParamEndSlotFromFp * kWordSize));
653
654 // Preserve IC data and arguments descriptor.
655 __ addiu(SP, SP, Immediate(-6 * kWordSize));
656 __ sw(S5, Address(SP, 5 * kWordSize));
657 __ sw(S4, Address(SP, 4 * kWordSize));
658
659 // Push space for the return value.
660 // Push the receiver.
661 // Push IC data object.
662 // Push arguments descriptor array.
663 __ sw(ZR, Address(SP, 3 * kWordSize));
664 __ sw(T6, Address(SP, 2 * kWordSize));
665 __ sw(S5, Address(SP, 1 * kWordSize));
666 __ sw(S4, Address(SP, 0 * kWordSize));
667
668 __ CallRuntime(kMegamorphicCacheMissHandlerRuntimeEntry, 3);
669
670 __ lw(T0, Address(SP, 3 * kWordSize)); // Get result function.
671 __ lw(S4, Address(SP, 4 * kWordSize)); // Restore argument descriptor.
672 __ lw(S5, Address(SP, 5 * kWordSize)); // Restore IC data.
673 __ addiu(SP, SP, Immediate(6 * kWordSize));
674
675 __ RestoreCodePointer();
676 __ LeaveStubFrame();
677
678 if (!FLAG_lazy_dispatchers) {
679 Label call_target_function;
680 GenerateDispatcherCode(assembler, &call_target_function);
681 __ Bind(&call_target_function);
682 }
683
684 __ lw(CODE_REG, FieldAddress(T0, Function::code_offset()));
685 __ lw(T2, FieldAddress(T0, Function::entry_point_offset()));
686 __ jr(T2);
687 }
688
689
690 // Called for inline allocation of arrays.
691 // Input parameters:
692 // RA: return address.
693 // A1: Array length as Smi (must be preserved).
694 // A0: array element type (either NULL or an instantiated type).
695 // NOTE: A1 cannot be clobbered here as the caller relies on it being saved.
696 // The newly allocated object is returned in V0.
697 void StubCode::GenerateAllocateArrayStub(Assembler* assembler) {
698 __ Comment("AllocateArrayStub");
699 Label slow_case;
700 // Compute the size to be allocated, it is based on the array length
701 // and is computed as:
702 // RoundedAllocationSize((array_length * kwordSize) + sizeof(RawArray)).
703 __ mov(T3, A1); // Array length.
704
705 // Check that length is a positive Smi.
706 __ andi(CMPRES1, T3, Immediate(kSmiTagMask));
707 if (FLAG_use_slow_path) {
708 __ b(&slow_case);
709 } else {
710 __ bne(CMPRES1, ZR, &slow_case);
711 }
712 __ bltz(T3, &slow_case);
713
714 // Check for maximum allowed length.
715 const intptr_t max_len =
716 reinterpret_cast<int32_t>(Smi::New(Array::kMaxElements));
717 __ BranchUnsignedGreater(T3, Immediate(max_len), &slow_case);
718
719 const intptr_t cid = kArrayCid;
720 NOT_IN_PRODUCT(__ MaybeTraceAllocation(kArrayCid, T4, &slow_case));
721
722 const intptr_t fixed_size_plus_alignment_padding =
723 sizeof(RawArray) + kObjectAlignment - 1;
724 __ LoadImmediate(T2, fixed_size_plus_alignment_padding);
725 __ sll(T3, T3, 1); // T3 is a Smi.
726 __ addu(T2, T2, T3);
727 ASSERT(kSmiTagShift == 1);
728 __ LoadImmediate(T3, ~(kObjectAlignment - 1));
729 __ and_(T2, T2, T3);
730
731 // T2: Allocation size.
732
733 Heap::Space space = Heap::kNew;
734 __ lw(T3, Address(THR, Thread::heap_offset()));
735 // Potential new object start.
736 __ lw(T0, Address(T3, Heap::TopOffset(space)));
737
738 __ addu(T1, T0, T2); // Potential next object start.
739 __ BranchUnsignedLess(T1, T0, &slow_case); // Branch on unsigned overflow.
740
741 // Check if the allocation fits into the remaining space.
742 // T0: potential new object start.
743 // T1: potential next object start.
744 // T2: allocation size.
745 // T3: heap.
746 __ lw(T4, Address(T3, Heap::EndOffset(space)));
747 __ BranchUnsignedGreaterEqual(T1, T4, &slow_case);
748
749 // Successfully allocated the object(s), now update top to point to
750 // next object start and initialize the object.
751 // T3: heap.
752 __ sw(T1, Address(T3, Heap::TopOffset(space)));
753 __ addiu(T0, T0, Immediate(kHeapObjectTag));
754 NOT_IN_PRODUCT(__ UpdateAllocationStatsWithSize(cid, T2, T4, space));
755
756 // Initialize the tags.
757 // T0: new object start as a tagged pointer.
758 // T1: new object end address.
759 // T2: allocation size.
760 {
761 Label overflow, done;
762 const intptr_t shift = RawObject::kSizeTagPos - kObjectAlignmentLog2;
763
764 __ BranchUnsignedGreater(T2, Immediate(RawObject::SizeTag::kMaxSizeTag),
765 &overflow);
766 __ b(&done);
767 __ delay_slot()->sll(T2, T2, shift);
768 __ Bind(&overflow);
769 __ mov(T2, ZR);
770 __ Bind(&done);
771
772 // Get the class index and insert it into the tags.
773 // T2: size and bit tags.
774 __ LoadImmediate(TMP, RawObject::ClassIdTag::encode(cid));
775 __ or_(T2, T2, TMP);
776 __ sw(T2, FieldAddress(T0, Array::tags_offset())); // Store tags.
777 }
778
779 // T0: new object start as a tagged pointer.
780 // T1: new object end address.
781 // Store the type argument field.
782 __ StoreIntoObjectNoBarrier(
783 T0, FieldAddress(T0, Array::type_arguments_offset()), A0);
784
785 // Set the length field.
786 __ StoreIntoObjectNoBarrier(T0, FieldAddress(T0, Array::length_offset()), A1);
787
788 __ LoadObject(T7, Object::null_object());
789 // Initialize all array elements to raw_null.
790 // T0: new object start as a tagged pointer.
791 // T1: new object end address.
792 // T2: iterator which initially points to the start of the variable
793 // data area to be initialized.
794 // T7: null.
795 __ AddImmediate(T2, T0, sizeof(RawArray) - kHeapObjectTag);
796
797 Label done;
798 Label init_loop;
799 __ Bind(&init_loop);
800 __ BranchUnsignedGreaterEqual(T2, T1, &done);
801 __ sw(T7, Address(T2, 0));
802 __ b(&init_loop);
803 __ delay_slot()->addiu(T2, T2, Immediate(kWordSize));
804 __ Bind(&done);
805
806 __ Ret(); // Returns the newly allocated object in V0.
807 __ delay_slot()->mov(V0, T0);
808
809 // Unable to allocate the array using the fast inline code, just call
810 // into the runtime.
811 __ Bind(&slow_case);
812 // Create a stub frame as we are pushing some objects on the stack before
813 // calling into the runtime.
814 __ EnterStubFrame();
815 // Setup space on stack for return value.
816 // Push array length as Smi and element type.
817 __ addiu(SP, SP, Immediate(-3 * kWordSize));
818 __ sw(ZR, Address(SP, 2 * kWordSize));
819 __ sw(A1, Address(SP, 1 * kWordSize));
820 __ sw(A0, Address(SP, 0 * kWordSize));
821 __ CallRuntime(kAllocateArrayRuntimeEntry, 2);
822 __ Comment("AllocateArrayStub return");
823 // Pop arguments; result is popped in IP.
824 __ lw(V0, Address(SP, 2 * kWordSize));
825 __ lw(A1, Address(SP, 1 * kWordSize));
826 __ lw(A0, Address(SP, 0 * kWordSize));
827 __ addiu(SP, SP, Immediate(3 * kWordSize));
828
829 __ LeaveStubFrameAndReturn();
830 }
831
832
833 // Called when invoking Dart code from C++ (VM code).
834 // Input parameters:
835 // RA : points to return address.
836 // A0 : code object of the Dart function to call.
837 // A1 : arguments descriptor array.
838 // A2 : arguments array.
839 // A3 : current thread.
840 void StubCode::GenerateInvokeDartCodeStub(Assembler* assembler) {
841 // Save frame pointer coming in.
842 __ Comment("InvokeDartCodeStub");
843 __ EnterFrame();
844
845 // Push code object to PC marker slot.
846 __ lw(TMP, Address(A3, Thread::invoke_dart_code_stub_offset()));
847 __ Push(TMP);
848
849 // Save new context and C++ ABI callee-saved registers.
850
851 // The saved vm tag, top resource, and top exit frame info.
852 const intptr_t kPreservedSlots = 3;
853 const intptr_t kPreservedRegSpace =
854 kWordSize *
855 (kAbiPreservedCpuRegCount + kAbiPreservedFpuRegCount + kPreservedSlots);
856
857 __ addiu(SP, SP, Immediate(-kPreservedRegSpace));
858 for (int i = S0; i <= S7; i++) {
859 Register r = static_cast<Register>(i);
860 const intptr_t slot = i - S0 + kPreservedSlots;
861 __ sw(r, Address(SP, slot * kWordSize));
862 }
863
864 for (intptr_t i = kAbiFirstPreservedFpuReg; i <= kAbiLastPreservedFpuReg;
865 i++) {
866 FRegister r = static_cast<FRegister>(i);
867 const intptr_t slot = kAbiPreservedCpuRegCount + kPreservedSlots + i -
868 kAbiFirstPreservedFpuReg;
869 __ swc1(r, Address(SP, slot * kWordSize));
870 }
871
872 // We now load the pool pointer(PP) with a GC safe value as we are about
873 // to invoke dart code.
874 __ LoadImmediate(PP, 0);
875
876 // Set up THR, which caches the current thread in Dart code.
877 if (THR != A3) {
878 __ mov(THR, A3);
879 }
880
881 // Save the current VMTag on the stack.
882 __ lw(T1, Assembler::VMTagAddress());
883 __ sw(T1, Address(SP, 2 * kWordSize));
884
885 // Mark that the thread is executing Dart code.
886 __ LoadImmediate(T0, VMTag::kDartTagId);
887 __ sw(T0, Assembler::VMTagAddress());
888
889 // Save top resource and top exit frame info. Use T0 as a temporary register.
890 // StackFrameIterator reads the top exit frame info saved in this frame.
891 __ lw(T0, Address(THR, Thread::top_resource_offset()));
892 __ sw(ZR, Address(THR, Thread::top_resource_offset()));
893 __ sw(T0, Address(SP, 1 * kWordSize));
894 __ lw(T0, Address(THR, Thread::top_exit_frame_info_offset()));
895 __ sw(ZR, Address(THR, Thread::top_exit_frame_info_offset()));
896 // kExitLinkSlotFromEntryFp must be kept in sync with the code below.
897 ASSERT(kExitLinkSlotFromEntryFp == -24);
898 __ sw(T0, Address(SP, 0 * kWordSize));
899
900 // After the call, The stack pointer is restored to this location.
901 // Pushed S0-7, F20-31, T0, T0, T1 = 23.
902
903 // Load arguments descriptor array into S4, which is passed to Dart code.
904 __ lw(S4, Address(A1, VMHandles::kOffsetOfRawPtrInHandle));
905
906 // No need to check for type args, disallowed by DartEntry::InvokeFunction.
907 // Load number of arguments into S5.
908 __ lw(T1, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
909 __ SmiUntag(T1);
910
911 // Compute address of 'arguments array' data area into A2.
912 __ lw(A2, Address(A2, VMHandles::kOffsetOfRawPtrInHandle));
913
914 // Set up arguments for the Dart call.
915 Label push_arguments;
916 Label done_push_arguments;
917 __ beq(T1, ZR, &done_push_arguments); // check if there are arguments.
918 __ delay_slot()->addiu(A2, A2,
919 Immediate(Array::data_offset() - kHeapObjectTag));
920 __ mov(A1, ZR);
921 __ Bind(&push_arguments);
922 __ lw(A3, Address(A2));
923 __ Push(A3);
924 __ addiu(A1, A1, Immediate(1));
925 __ BranchSignedLess(A1, T1, &push_arguments);
926 __ delay_slot()->addiu(A2, A2, Immediate(kWordSize));
927
928 __ Bind(&done_push_arguments);
929
930 // Call the Dart code entrypoint.
931 // We are calling into Dart code, here, so there is no need to call through
932 // T9 to match the ABI.
933 __ lw(CODE_REG, Address(A0, VMHandles::kOffsetOfRawPtrInHandle));
934 __ lw(A0, FieldAddress(CODE_REG, Code::entry_point_offset()));
935 __ jalr(A0); // S4 is the arguments descriptor array.
936 __ Comment("InvokeDartCodeStub return");
937
938 // Get rid of arguments pushed on the stack.
939 __ AddImmediate(SP, FP, kExitLinkSlotFromEntryFp * kWordSize);
940
941
942 // Restore the current VMTag from the stack.
943 __ lw(T1, Address(SP, 2 * kWordSize));
944 __ sw(T1, Assembler::VMTagAddress());
945
946 // Restore the saved top resource and top exit frame info back into the
947 // Isolate structure. Uses T0 as a temporary register for this.
948 __ lw(T0, Address(SP, 1 * kWordSize));
949 __ sw(T0, Address(THR, Thread::top_resource_offset()));
950 __ lw(T0, Address(SP, 0 * kWordSize));
951 __ sw(T0, Address(THR, Thread::top_exit_frame_info_offset()));
952
953 // Restore C++ ABI callee-saved registers.
954 for (int i = S0; i <= S7; i++) {
955 Register r = static_cast<Register>(i);
956 const intptr_t slot = i - S0 + kPreservedSlots;
957 __ lw(r, Address(SP, slot * kWordSize));
958 }
959
960 for (intptr_t i = kAbiFirstPreservedFpuReg; i <= kAbiLastPreservedFpuReg;
961 i++) {
962 FRegister r = static_cast<FRegister>(i);
963 const intptr_t slot = kAbiPreservedCpuRegCount + kPreservedSlots + i -
964 kAbiFirstPreservedFpuReg;
965 __ lwc1(r, Address(SP, slot * kWordSize));
966 }
967
968 __ addiu(SP, SP, Immediate(kPreservedRegSpace));
969
970 // Restore the frame pointer and return.
971 __ LeaveFrameAndReturn();
972 }
973
974
975 // Called for inline allocation of contexts.
976 // Input:
977 // T1: number of context variables.
978 // Output:
979 // V0: new allocated RawContext object.
980 void StubCode::GenerateAllocateContextStub(Assembler* assembler) {
981 __ Comment("AllocateContext");
982 if (FLAG_inline_alloc) {
983 Label slow_case;
984 // First compute the rounded instance size.
985 // T1: number of context variables.
986 intptr_t fixed_size_plus_alignment_padding =
987 sizeof(RawContext) + kObjectAlignment - 1;
988 __ LoadImmediate(T2, fixed_size_plus_alignment_padding);
989 __ sll(T0, T1, 2);
990 __ addu(T2, T2, T0);
991 ASSERT(kSmiTagShift == 1);
992 __ LoadImmediate(T0, ~((kObjectAlignment)-1));
993 __ and_(T2, T2, T0);
994
995 NOT_IN_PRODUCT(__ MaybeTraceAllocation(kContextCid, T4, &slow_case));
996 // Now allocate the object.
997 // T1: number of context variables.
998 // T2: object size.
999 const intptr_t cid = kContextCid;
1000 Heap::Space space = Heap::kNew;
1001 __ lw(T5, Address(THR, Thread::heap_offset()));
1002 __ lw(V0, Address(T5, Heap::TopOffset(space)));
1003 __ addu(T3, T2, V0);
1004
1005 // Check if the allocation fits into the remaining space.
1006 // V0: potential new object.
1007 // T1: number of context variables.
1008 // T2: object size.
1009 // T3: potential next object start.
1010 // T5: heap.
1011 __ lw(CMPRES1, Address(T5, Heap::EndOffset(space)));
1012 if (FLAG_use_slow_path) {
1013 __ b(&slow_case);
1014 } else {
1015 __ BranchUnsignedGreaterEqual(T3, CMPRES1, &slow_case);
1016 }
1017
1018 // Successfully allocated the object, now update top to point to
1019 // next object start and initialize the object.
1020 // V0: new object.
1021 // T1: number of context variables.
1022 // T2: object size.
1023 // T3: next object start.
1024 // T5: heap.
1025 __ sw(T3, Address(T5, Heap::TopOffset(space)));
1026 __ addiu(V0, V0, Immediate(kHeapObjectTag));
1027 NOT_IN_PRODUCT(__ UpdateAllocationStatsWithSize(cid, T2, T5, space));
1028
1029 // Calculate the size tag.
1030 // V0: new object.
1031 // T1: number of context variables.
1032 // T2: object size.
1033 const intptr_t shift = RawObject::kSizeTagPos - kObjectAlignmentLog2;
1034 __ LoadImmediate(TMP, RawObject::SizeTag::kMaxSizeTag);
1035 __ sltu(CMPRES1, TMP, T2); // CMPRES1 = T2 > TMP ? 1 : 0.
1036 __ movn(T2, ZR, CMPRES1); // T2 = CMPRES1 != 0 ? 0 : T2.
1037 __ sll(TMP, T2, shift); // TMP = T2 << shift.
1038 __ movz(T2, TMP, CMPRES1); // T2 = CMPRES1 == 0 ? TMP : T2.
1039
1040 // Get the class index and insert it into the tags.
1041 // T2: size and bit tags.
1042 __ LoadImmediate(TMP, RawObject::ClassIdTag::encode(cid));
1043 __ or_(T2, T2, TMP);
1044 __ sw(T2, FieldAddress(V0, Context::tags_offset()));
1045
1046 // Setup up number of context variables field.
1047 // V0: new object.
1048 // T1: number of context variables as integer value (not object).
1049 __ sw(T1, FieldAddress(V0, Context::num_variables_offset()));
1050
1051 __ LoadObject(T7, Object::null_object());
1052
1053 // Initialize the context variables.
1054 // V0: new object.
1055 // T1: number of context variables.
1056 Label loop, loop_exit;
1057 __ blez(T1, &loop_exit);
1058 // Setup the parent field.
1059 __ delay_slot()->sw(T7, FieldAddress(V0, Context::parent_offset()));
1060 __ AddImmediate(T3, V0, Context::variable_offset(0) - kHeapObjectTag);
1061 __ sll(T1, T1, 2);
1062 __ Bind(&loop);
1063 __ addiu(T1, T1, Immediate(-kWordSize));
1064 __ addu(T4, T3, T1);
1065 __ bgtz(T1, &loop);
1066 __ delay_slot()->sw(T7, Address(T4));
1067 __ Bind(&loop_exit);
1068
1069 // Done allocating and initializing the context.
1070 // V0: new object.
1071 __ Ret();
1072
1073 __ Bind(&slow_case);
1074 }
1075 // Create a stub frame as we are pushing some objects on the stack before
1076 // calling into the runtime.
1077 __ EnterStubFrame();
1078 // Setup space on stack for return value.
1079 __ SmiTag(T1);
1080 __ addiu(SP, SP, Immediate(-2 * kWordSize));
1081 __ LoadObject(TMP, Object::null_object());
1082 __ sw(TMP, Address(SP, 1 * kWordSize)); // Store null.
1083 __ sw(T1, Address(SP, 0 * kWordSize));
1084 __ CallRuntime(kAllocateContextRuntimeEntry, 1); // Allocate context.
1085 __ lw(V0, Address(SP, 1 * kWordSize)); // Get the new context.
1086 __ addiu(SP, SP, Immediate(2 * kWordSize)); // Pop argument and return.
1087
1088 // V0: new object
1089 // Restore the frame pointer.
1090 __ LeaveStubFrameAndReturn();
1091 }
1092
1093
1094 // Helper stub to implement Assembler::StoreIntoObject.
1095 // Input parameters:
1096 // T0: Address (i.e. object) being stored into.
1097 void StubCode::GenerateUpdateStoreBufferStub(Assembler* assembler) {
1098 // Save values being destroyed.
1099 __ Comment("UpdateStoreBufferStub");
1100 __ addiu(SP, SP, Immediate(-3 * kWordSize));
1101 __ sw(T3, Address(SP, 2 * kWordSize));
1102 __ sw(T2, Address(SP, 1 * kWordSize));
1103 __ sw(T1, Address(SP, 0 * kWordSize));
1104
1105 Label add_to_buffer;
1106 // Check whether this object has already been remembered. Skip adding to the
1107 // store buffer if the object is in the store buffer already.
1108 // Spilled: T1, T2, T3.
1109 // T0: Address being stored.
1110 __ lw(T2, FieldAddress(T0, Object::tags_offset()));
1111 __ andi(CMPRES1, T2, Immediate(1 << RawObject::kRememberedBit));
1112 __ beq(CMPRES1, ZR, &add_to_buffer);
1113 __ lw(T1, Address(SP, 0 * kWordSize));
1114 __ lw(T2, Address(SP, 1 * kWordSize));
1115 __ lw(T3, Address(SP, 2 * kWordSize));
1116 __ addiu(SP, SP, Immediate(3 * kWordSize));
1117 __ Ret();
1118
1119 __ Bind(&add_to_buffer);
1120 // Atomically set the remembered bit of the object header.
1121 Label retry;
1122 __ Bind(&retry);
1123 __ ll(T2, FieldAddress(T0, Object::tags_offset()));
1124 __ ori(T2, T2, Immediate(1 << RawObject::kRememberedBit));
1125 __ sc(T2, FieldAddress(T0, Object::tags_offset()));
1126 // T2 = 1 on success, 0 on failure.
1127 __ beq(T2, ZR, &retry);
1128
1129 // Load the StoreBuffer block out of the thread. Then load top_ out of the
1130 // StoreBufferBlock and add the address to the pointers_.
1131 __ lw(T1, Address(THR, Thread::store_buffer_block_offset()));
1132 __ lw(T2, Address(T1, StoreBufferBlock::top_offset()));
1133 __ sll(T3, T2, 2);
1134 __ addu(T3, T1, T3);
1135 __ sw(T0, Address(T3, StoreBufferBlock::pointers_offset()));
1136
1137 // Increment top_ and check for overflow.
1138 // T2: top_
1139 // T1: StoreBufferBlock
1140 Label L;
1141 __ addiu(T2, T2, Immediate(1));
1142 __ sw(T2, Address(T1, StoreBufferBlock::top_offset()));
1143 __ addiu(CMPRES1, T2, Immediate(-StoreBufferBlock::kSize));
1144 // Restore values.
1145 __ lw(T1, Address(SP, 0 * kWordSize));
1146 __ lw(T2, Address(SP, 1 * kWordSize));
1147 __ lw(T3, Address(SP, 2 * kWordSize));
1148 __ beq(CMPRES1, ZR, &L);
1149 __ delay_slot()->addiu(SP, SP, Immediate(3 * kWordSize));
1150 __ Ret();
1151
1152 // Handle overflow: Call the runtime leaf function.
1153 __ Bind(&L);
1154 // Setup frame, push callee-saved registers.
1155
1156 __ EnterCallRuntimeFrame(1 * kWordSize);
1157 __ mov(A0, THR);
1158 __ CallRuntime(kStoreBufferBlockProcessRuntimeEntry, 1);
1159 __ Comment("UpdateStoreBufferStub return");
1160 // Restore callee-saved registers, tear down frame.
1161 __ LeaveCallRuntimeFrame();
1162 __ Ret();
1163 }
1164
1165
1166 // Called for inline allocation of objects.
1167 // Input parameters:
1168 // RA : return address.
1169 // SP + 0 : type arguments object (only if class is parameterized).
1170 void StubCode::GenerateAllocationStubForClass(Assembler* assembler,
1171 const Class& cls) {
1172 __ Comment("AllocationStubForClass");
1173 // The generated code is different if the class is parameterized.
1174 const bool is_cls_parameterized = cls.NumTypeArguments() > 0;
1175 ASSERT(!is_cls_parameterized ||
1176 (cls.type_arguments_field_offset() != Class::kNoTypeArguments));
1177 // kInlineInstanceSize is a constant used as a threshold for determining
1178 // when the object initialization should be done as a loop or as
1179 // straight line code.
1180 const int kInlineInstanceSize = 12;
1181 const intptr_t instance_size = cls.instance_size();
1182 ASSERT(instance_size > 0);
1183 if (is_cls_parameterized) {
1184 __ lw(T1, Address(SP, 0 * kWordSize));
1185 // T1: type arguments.
1186 }
1187 Isolate* isolate = Isolate::Current();
1188 if (FLAG_inline_alloc && Heap::IsAllocatableInNewSpace(instance_size) &&
1189 !cls.TraceAllocation(isolate)) {
1190 Label slow_case;
1191 // Allocate the object and update top to point to
1192 // next object start and initialize the allocated object.
1193 // T1: instantiated type arguments (if is_cls_parameterized).
1194 Heap::Space space = Heap::kNew;
1195 __ lw(T5, Address(THR, Thread::heap_offset()));
1196 __ lw(T2, Address(T5, Heap::TopOffset(space)));
1197 __ LoadImmediate(T4, instance_size);
1198 __ addu(T3, T2, T4);
1199 // Check if the allocation fits into the remaining space.
1200 // T2: potential new object start.
1201 // T3: potential next object start.
1202 // T5: heap.
1203 __ lw(CMPRES1, Address(T5, Heap::EndOffset(space)));
1204 if (FLAG_use_slow_path) {
1205 __ b(&slow_case);
1206 } else {
1207 __ BranchUnsignedGreaterEqual(T3, CMPRES1, &slow_case);
1208 }
1209 // Successfully allocated the object(s), now update top to point to
1210 // next object start and initialize the object.
1211 __ sw(T3, Address(T5, Heap::TopOffset(space)));
1212 NOT_IN_PRODUCT(__ UpdateAllocationStats(cls.id(), T5, space));
1213
1214 // T2: new object start.
1215 // T3: next object start.
1216 // T1: new object type arguments (if is_cls_parameterized).
1217 // Set the tags.
1218 uint32_t tags = 0;
1219 tags = RawObject::SizeTag::update(instance_size, tags);
1220 ASSERT(cls.id() != kIllegalCid);
1221 tags = RawObject::ClassIdTag::update(cls.id(), tags);
1222 __ LoadImmediate(T0, tags);
1223 __ sw(T0, Address(T2, Instance::tags_offset()));
1224
1225 __ LoadObject(T7, Object::null_object());
1226
1227 // Initialize the remaining words of the object.
1228 // T2: new object start.
1229 // T3: next object start.
1230 // T1: new object type arguments (if is_cls_parameterized).
1231 // First try inlining the initialization without a loop.
1232 if (instance_size < (kInlineInstanceSize * kWordSize)) {
1233 // Check if the object contains any non-header fields.
1234 // Small objects are initialized using a consecutive set of writes.
1235 for (intptr_t current_offset = Instance::NextFieldOffset();
1236 current_offset < instance_size; current_offset += kWordSize) {
1237 __ sw(T7, Address(T2, current_offset));
1238 }
1239 } else {
1240 __ addiu(T4, T2, Immediate(Instance::NextFieldOffset()));
1241 // Loop until the whole object is initialized.
1242 // T2: new object.
1243 // T3: next object start.
1244 // T4: next word to be initialized.
1245 // T1: new object type arguments (if is_cls_parameterized).
1246 Label loop, loop_exit;
1247 __ BranchUnsignedGreaterEqual(T4, T3, &loop_exit);
1248 __ Bind(&loop);
1249 __ addiu(T4, T4, Immediate(kWordSize));
1250 __ bne(T4, T3, &loop);
1251 __ delay_slot()->sw(T7, Address(T4, -kWordSize));
1252 __ Bind(&loop_exit);
1253 }
1254 if (is_cls_parameterized) {
1255 // T1: new object type arguments.
1256 // Set the type arguments in the new object.
1257 __ sw(T1, Address(T2, cls.type_arguments_field_offset()));
1258 }
1259 // Done allocating and initializing the instance.
1260 // T2: new object still missing its heap tag.
1261 __ Ret();
1262 __ delay_slot()->addiu(V0, T2, Immediate(kHeapObjectTag));
1263
1264 __ Bind(&slow_case);
1265 }
1266 // If is_cls_parameterized:
1267 // T1: new object type arguments (instantiated or not).
1268 // Create a stub frame as we are pushing some objects on the stack before
1269 // calling into the runtime.
1270 __ EnterStubFrame(); // Uses pool pointer to pass cls to runtime.
1271 __ LoadObject(TMP, cls);
1272
1273 __ addiu(SP, SP, Immediate(-3 * kWordSize));
1274 // Space on stack for return value.
1275 __ LoadObject(T7, Object::null_object());
1276 __ sw(T7, Address(SP, 2 * kWordSize));
1277 __ sw(TMP, Address(SP, 1 * kWordSize)); // Class of object to be allocated.
1278
1279 if (is_cls_parameterized) {
1280 // Push type arguments of object to be allocated and of instantiator.
1281 __ sw(T1, Address(SP, 0 * kWordSize));
1282 } else {
1283 // Push null type arguments.
1284 __ sw(T7, Address(SP, 0 * kWordSize));
1285 }
1286 __ CallRuntime(kAllocateObjectRuntimeEntry, 2); // Allocate object.
1287 __ Comment("AllocationStubForClass return");
1288 // Pop result (newly allocated object).
1289 __ lw(V0, Address(SP, 2 * kWordSize));
1290 __ addiu(SP, SP, Immediate(3 * kWordSize)); // Pop arguments.
1291 // V0: new object
1292 // Restore the frame pointer and return.
1293 __ LeaveStubFrameAndReturn(RA);
1294 }
1295
1296
1297 // Called for invoking "dynamic noSuchMethod(Invocation invocation)" function
1298 // from the entry code of a dart function after an error in passed argument
1299 // name or number is detected.
1300 // Input parameters:
1301 // RA : return address.
1302 // SP : address of last argument.
1303 // S4: arguments descriptor array.
1304 void StubCode::GenerateCallClosureNoSuchMethodStub(Assembler* assembler) {
1305 __ EnterStubFrame();
1306
1307 // Load the receiver.
1308 __ lw(A1, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
1309 __ sll(TMP, A1, 1); // A1 is a Smi.
1310 __ addu(TMP, FP, TMP);
1311 __ lw(T6, Address(TMP, kParamEndSlotFromFp * kWordSize));
1312
1313 // Push space for the return value.
1314 // Push the receiver.
1315 // Push arguments descriptor array.
1316 const intptr_t kNumArgs = 3;
1317 __ addiu(SP, SP, Immediate(-kNumArgs * kWordSize));
1318 __ sw(ZR, Address(SP, 2 * kWordSize));
1319 __ sw(T6, Address(SP, 1 * kWordSize));
1320 __ sw(S4, Address(SP, 0 * kWordSize));
1321
1322 // Adjust arguments count.
1323 __ lw(TMP, FieldAddress(S4, ArgumentsDescriptor::type_args_len_offset()));
1324 Label args_count_ok;
1325 __ BranchEqual(TMP, Immediate(0), &args_count_ok);
1326 __ AddImmediate(A1, A1, Smi::RawValue(1)); // Include the type arguments.
1327 __ Bind(&args_count_ok);
1328
1329 // A1: Smi-tagged arguments array length.
1330 PushArgumentsArray(assembler);
1331
1332 __ CallRuntime(kInvokeClosureNoSuchMethodRuntimeEntry, kNumArgs);
1333 // noSuchMethod on closures always throws an error, so it will never return.
1334 __ break_(0);
1335 }
1336
1337
1338 // T0: function object.
1339 // S5: inline cache data object.
1340 // Cannot use function object from ICData as it may be the inlined
1341 // function and not the top-scope function.
1342 void StubCode::GenerateOptimizedUsageCounterIncrement(Assembler* assembler) {
1343 __ Comment("OptimizedUsageCounterIncrement");
1344 Register ic_reg = S5;
1345 Register func_reg = T0;
1346 if (FLAG_trace_optimized_ic_calls) {
1347 __ EnterStubFrame();
1348 __ addiu(SP, SP, Immediate(-4 * kWordSize));
1349 __ sw(T0, Address(SP, 3 * kWordSize));
1350 __ sw(S5, Address(SP, 2 * kWordSize));
1351 __ sw(ic_reg, Address(SP, 1 * kWordSize)); // Argument.
1352 __ sw(func_reg, Address(SP, 0 * kWordSize)); // Argument.
1353 __ CallRuntime(kTraceICCallRuntimeEntry, 2);
1354 __ lw(S5, Address(SP, 2 * kWordSize));
1355 __ lw(T0, Address(SP, 3 * kWordSize));
1356 __ addiu(SP, SP, Immediate(4 * kWordSize)); // Discard argument;
1357 __ LeaveStubFrame();
1358 }
1359 __ lw(T7, FieldAddress(func_reg, Function::usage_counter_offset()));
1360 __ addiu(T7, T7, Immediate(1));
1361 __ sw(T7, FieldAddress(func_reg, Function::usage_counter_offset()));
1362 }
1363
1364
1365 // Loads function into 'temp_reg'.
1366 void StubCode::GenerateUsageCounterIncrement(Assembler* assembler,
1367 Register temp_reg) {
1368 if (FLAG_optimization_counter_threshold >= 0) {
1369 __ Comment("UsageCounterIncrement");
1370 Register ic_reg = S5;
1371 Register func_reg = temp_reg;
1372 ASSERT(temp_reg == T0);
1373 __ Comment("Increment function counter");
1374 __ lw(func_reg, FieldAddress(ic_reg, ICData::owner_offset()));
1375 __ lw(T1, FieldAddress(func_reg, Function::usage_counter_offset()));
1376 __ addiu(T1, T1, Immediate(1));
1377 __ sw(T1, FieldAddress(func_reg, Function::usage_counter_offset()));
1378 }
1379 }
1380
1381
1382 // Note: S5 must be preserved.
1383 // Attempt a quick Smi operation for known operations ('kind'). The ICData
1384 // must have been primed with a Smi/Smi check that will be used for counting
1385 // the invocations.
1386 static void EmitFastSmiOp(Assembler* assembler,
1387 Token::Kind kind,
1388 intptr_t num_args,
1389 Label* not_smi_or_overflow) {
1390 __ Comment("Fast Smi op");
1391 ASSERT(num_args == 2);
1392 __ lw(T0, Address(SP, 0 * kWordSize)); // Left.
1393 __ lw(T1, Address(SP, 1 * kWordSize)); // Right.
1394 __ or_(CMPRES1, T0, T1);
1395 __ andi(CMPRES1, CMPRES1, Immediate(kSmiTagMask));
1396 __ bne(CMPRES1, ZR, not_smi_or_overflow);
1397 switch (kind) {
1398 case Token::kADD: {
1399 __ AdduDetectOverflow(V0, T1, T0, CMPRES1); // Add.
1400 __ bltz(CMPRES1, not_smi_or_overflow); // Fall through on overflow.
1401 break;
1402 }
1403 case Token::kSUB: {
1404 __ SubuDetectOverflow(V0, T1, T0, CMPRES1); // Subtract.
1405 __ bltz(CMPRES1, not_smi_or_overflow); // Fall through on overflow.
1406 break;
1407 }
1408 case Token::kEQ: {
1409 Label true_label, done;
1410 __ beq(T1, T0, &true_label);
1411 __ LoadObject(V0, Bool::False());
1412 __ b(&done);
1413 __ Bind(&true_label);
1414 __ LoadObject(V0, Bool::True());
1415 __ Bind(&done);
1416 break;
1417 }
1418 default:
1419 UNIMPLEMENTED();
1420 }
1421 // S5: IC data object (preserved).
1422 __ lw(T0, FieldAddress(S5, ICData::ic_data_offset()));
1423 // T0: ic_data_array with check entries: classes and target functions.
1424 __ AddImmediate(T0, Array::data_offset() - kHeapObjectTag);
1425 // T0: points directly to the first ic data array element.
1426 #if defined(DEBUG)
1427 // Check that first entry is for Smi/Smi.
1428 Label error, ok;
1429 const int32_t imm_smi_cid = reinterpret_cast<int32_t>(Smi::New(kSmiCid));
1430 __ lw(T4, Address(T0));
1431 __ BranchNotEqual(T4, Immediate(imm_smi_cid), &error);
1432 __ lw(T4, Address(T0, kWordSize));
1433 __ BranchEqual(T4, Immediate(imm_smi_cid), &ok);
1434 __ Bind(&error);
1435 __ Stop("Incorrect IC data");
1436 __ Bind(&ok);
1437 #endif
1438 if (FLAG_optimization_counter_threshold >= 0) {
1439 // Update counter, ignore overflow.
1440 const intptr_t count_offset = ICData::CountIndexFor(num_args) * kWordSize;
1441 __ lw(T4, Address(T0, count_offset));
1442 __ AddImmediate(T4, T4, Smi::RawValue(1));
1443 __ sw(T4, Address(T0, count_offset));
1444 }
1445
1446 __ Ret();
1447 }
1448
1449
1450 // Generate inline cache check for 'num_args'.
1451 // RA: return address
1452 // S5: Inline cache data object.
1453 // Control flow:
1454 // - If receiver is null -> jump to IC miss.
1455 // - If receiver is Smi -> load Smi class.
1456 // - If receiver is not-Smi -> load receiver's class.
1457 // - Check if 'num_args' (including receiver) match any IC data group.
1458 // - Match found -> jump to target.
1459 // - Match not found -> jump to IC miss.
1460 void StubCode::GenerateNArgsCheckInlineCacheStub(
1461 Assembler* assembler,
1462 intptr_t num_args,
1463 const RuntimeEntry& handle_ic_miss,
1464 Token::Kind kind,
1465 bool optimized) {
1466 __ Comment("NArgsCheckInlineCacheStub");
1467 ASSERT(num_args == 1 || num_args == 2);
1468 #if defined(DEBUG)
1469 {
1470 Label ok;
1471 // Check that the IC data array has NumArgsTested() == num_args.
1472 // 'NumArgsTested' is stored in the least significant bits of 'state_bits'.
1473 __ lw(T0, FieldAddress(S5, ICData::state_bits_offset()));
1474 ASSERT(ICData::NumArgsTestedShift() == 0); // No shift needed.
1475 __ andi(T0, T0, Immediate(ICData::NumArgsTestedMask()));
1476 __ BranchEqual(T0, Immediate(num_args), &ok);
1477 __ Stop("Incorrect stub for IC data");
1478 __ Bind(&ok);
1479 }
1480 #endif // DEBUG
1481
1482
1483 Label stepping, done_stepping;
1484 if (FLAG_support_debugger && !optimized) {
1485 __ Comment("Check single stepping");
1486 __ LoadIsolate(T0);
1487 __ lbu(T0, Address(T0, Isolate::single_step_offset()));
1488 __ BranchNotEqual(T0, Immediate(0), &stepping);
1489 __ Bind(&done_stepping);
1490 }
1491
1492 Label not_smi_or_overflow;
1493 if (kind != Token::kILLEGAL) {
1494 EmitFastSmiOp(assembler, kind, num_args, &not_smi_or_overflow);
1495 }
1496 __ Bind(&not_smi_or_overflow);
1497
1498 __ Comment("Extract ICData initial values and receiver cid");
1499 // Load argument descriptor into S4.
1500 __ lw(S4, FieldAddress(S5, ICData::arguments_descriptor_offset()));
1501 // Preserve return address, since RA is needed for subroutine call.
1502 __ mov(T2, RA);
1503 // Loop that checks if there is an IC data match.
1504 Label loop, found, miss;
1505 // S5: IC data object (preserved).
1506 __ lw(T0, FieldAddress(S5, ICData::ic_data_offset()));
1507 // T0: ic_data_array with check entries: classes and target functions.
1508 __ AddImmediate(T0, Array::data_offset() - kHeapObjectTag);
1509 // T0: points directly to the first ic data array element.
1510
1511 // Get the receiver's class ID (first read number of arguments from
1512 // arguments descriptor array and then access the receiver from the stack).
1513 __ lw(T1, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
1514 __ sll(T5, T1, 1); // T1 (argument_count - 1) is smi.
1515 __ addu(T5, T5, SP);
1516 __ lw(T3, Address(T5, -kWordSize));
1517 __ LoadTaggedClassIdMayBeSmi(T3, T3);
1518
1519 if (num_args == 2) {
1520 __ lw(T5, Address(T5, -2 * kWordSize));
1521 __ LoadTaggedClassIdMayBeSmi(T5, T5);
1522 }
1523
1524 const intptr_t entry_size = ICData::TestEntryLengthFor(num_args) * kWordSize;
1525 // T1: argument_count (smi).
1526 // T3: receiver's class ID (smi).
1527 // T5: first argument's class ID (smi).
1528
1529 // We unroll the generic one that is generated once more than the others.
1530 const bool optimize = kind == Token::kILLEGAL;
1531
1532 __ Comment("ICData loop");
1533 __ Bind(&loop);
1534 for (int unroll = optimize ? 4 : 2; unroll >= 0; unroll--) {
1535 __ lw(T4, Address(T0, 0));
1536 if (num_args == 1) {
1537 __ beq(T3, T4, &found); // IC hit.
1538 } else {
1539 ASSERT(num_args == 2);
1540 Label update;
1541 __ bne(T3, T4, &update); // Continue.
1542 __ lw(T4, Address(T0, kWordSize));
1543 __ beq(T5, T4, &found); // IC hit.
1544 __ Bind(&update);
1545 }
1546
1547 __ AddImmediate(T0, entry_size); // Next entry.
1548 if (unroll == 0) {
1549 __ BranchNotEqual(T4, Immediate(Smi::RawValue(kIllegalCid)),
1550 &loop); // Done?
1551 } else {
1552 __ BranchEqual(T4, Immediate(Smi::RawValue(kIllegalCid)),
1553 &miss); // Done?
1554 }
1555 }
1556
1557 __ Bind(&miss);
1558 __ Comment("IC miss");
1559 // Restore return address.
1560 __ mov(RA, T2);
1561
1562 // Compute address of arguments (first read number of arguments from
1563 // arguments descriptor array and then compute address on the stack).
1564 // T1: argument_count (smi).
1565 __ addiu(T1, T1, Immediate(Smi::RawValue(-1)));
1566 __ sll(T1, T1, 1); // T1 is Smi.
1567 __ addu(T1, SP, T1);
1568 // T1: address of receiver.
1569 // Create a stub frame as we are pushing some objects on the stack before
1570 // calling into the runtime.
1571 __ EnterStubFrame();
1572 // Preserve IC data object and arguments descriptor array and
1573 // setup space on stack for result (target code object).
1574 int num_slots = num_args + 4;
1575 __ addiu(SP, SP, Immediate(-num_slots * kWordSize));
1576 __ sw(S5, Address(SP, (num_slots - 1) * kWordSize));
1577 __ sw(S4, Address(SP, (num_slots - 2) * kWordSize));
1578 __ sw(ZR, Address(SP, (num_slots - 3) * kWordSize));
1579 // Push call arguments.
1580 for (intptr_t i = 0; i < num_args; i++) {
1581 __ lw(TMP, Address(T1, -i * kWordSize));
1582 __ sw(TMP, Address(SP, (num_slots - i - 4) * kWordSize));
1583 }
1584 // Pass IC data object.
1585 __ sw(S5, Address(SP, (num_slots - num_args - 4) * kWordSize));
1586 __ CallRuntime(handle_ic_miss, num_args + 1);
1587 __ Comment("NArgsCheckInlineCacheStub return");
1588 // Pop returned function object into T3.
1589 // Restore arguments descriptor array and IC data array.
1590 __ lw(T3, Address(SP, (num_slots - 3) * kWordSize));
1591 __ lw(S4, Address(SP, (num_slots - 2) * kWordSize));
1592 __ lw(S5, Address(SP, (num_slots - 1) * kWordSize));
1593 // Remove the call arguments pushed earlier, including the IC data object
1594 // and the arguments descriptor array.
1595 __ addiu(SP, SP, Immediate(num_slots * kWordSize));
1596 __ RestoreCodePointer();
1597 __ LeaveStubFrame();
1598
1599 Label call_target_function;
1600 if (!FLAG_lazy_dispatchers) {
1601 __ mov(T0, T3);
1602 GenerateDispatcherCode(assembler, &call_target_function);
1603 } else {
1604 __ b(&call_target_function);
1605 }
1606
1607 __ Bind(&found);
1608 __ mov(RA, T2); // Restore return address if found.
1609 __ Comment("Update caller's counter");
1610 // T0: Pointer to an IC data check group.
1611 const intptr_t target_offset = ICData::TargetIndexFor(num_args) * kWordSize;
1612 const intptr_t count_offset = ICData::CountIndexFor(num_args) * kWordSize;
1613 __ lw(T3, Address(T0, target_offset));
1614
1615 if (FLAG_optimization_counter_threshold >= 0) {
1616 // Update counter, ignore overflow.
1617 __ lw(T4, Address(T0, count_offset));
1618 __ AddImmediate(T4, T4, Smi::RawValue(1));
1619 __ sw(T4, Address(T0, count_offset));
1620 }
1621
1622 __ Comment("Call target");
1623 __ Bind(&call_target_function);
1624 // T0 <- T3: Target function.
1625 __ mov(T0, T3);
1626 Label is_compiled;
1627 __ lw(T4, FieldAddress(T0, Function::entry_point_offset()));
1628 __ lw(CODE_REG, FieldAddress(T0, Function::code_offset()));
1629 __ jr(T4);
1630
1631 // Call single step callback in debugger.
1632 if (FLAG_support_debugger && !optimized) {
1633 __ Bind(&stepping);
1634 __ EnterStubFrame();
1635 __ addiu(SP, SP, Immediate(-2 * kWordSize));
1636 __ sw(S5, Address(SP, 1 * kWordSize)); // Preserve IC data.
1637 __ sw(RA, Address(SP, 0 * kWordSize)); // Return address.
1638 __ CallRuntime(kSingleStepHandlerRuntimeEntry, 0);
1639 __ lw(RA, Address(SP, 0 * kWordSize));
1640 __ lw(S5, Address(SP, 1 * kWordSize));
1641 __ addiu(SP, SP, Immediate(2 * kWordSize));
1642 __ RestoreCodePointer();
1643 __ LeaveStubFrame();
1644 __ b(&done_stepping);
1645 }
1646 }
1647
1648
1649 // Use inline cache data array to invoke the target or continue in inline
1650 // cache miss handler. Stub for 1-argument check (receiver class).
1651 // RA: Return address.
1652 // S5: Inline cache data object.
1653 // Inline cache data object structure:
1654 // 0: function-name
1655 // 1: N, number of arguments checked.
1656 // 2 .. (length - 1): group of checks, each check containing:
1657 // - N classes.
1658 // - 1 target function.
1659 void StubCode::GenerateOneArgCheckInlineCacheStub(Assembler* assembler) {
1660 GenerateUsageCounterIncrement(assembler, T0);
1661 GenerateNArgsCheckInlineCacheStub(
1662 assembler, 1, kInlineCacheMissHandlerOneArgRuntimeEntry, Token::kILLEGAL);
1663 }
1664
1665
1666 void StubCode::GenerateTwoArgsCheckInlineCacheStub(Assembler* assembler) {
1667 GenerateUsageCounterIncrement(assembler, T0);
1668 GenerateNArgsCheckInlineCacheStub(assembler, 2,
1669 kInlineCacheMissHandlerTwoArgsRuntimeEntry,
1670 Token::kILLEGAL);
1671 }
1672
1673
1674 void StubCode::GenerateSmiAddInlineCacheStub(Assembler* assembler) {
1675 GenerateUsageCounterIncrement(assembler, T0);
1676 GenerateNArgsCheckInlineCacheStub(
1677 assembler, 2, kInlineCacheMissHandlerTwoArgsRuntimeEntry, Token::kADD);
1678 }
1679
1680
1681 void StubCode::GenerateSmiSubInlineCacheStub(Assembler* assembler) {
1682 GenerateUsageCounterIncrement(assembler, T0);
1683 GenerateNArgsCheckInlineCacheStub(
1684 assembler, 2, kInlineCacheMissHandlerTwoArgsRuntimeEntry, Token::kSUB);
1685 }
1686
1687
1688 void StubCode::GenerateSmiEqualInlineCacheStub(Assembler* assembler) {
1689 GenerateUsageCounterIncrement(assembler, T0);
1690 GenerateNArgsCheckInlineCacheStub(
1691 assembler, 2, kInlineCacheMissHandlerTwoArgsRuntimeEntry, Token::kEQ);
1692 }
1693
1694
1695 void StubCode::GenerateOneArgOptimizedCheckInlineCacheStub(
1696 Assembler* assembler) {
1697 GenerateOptimizedUsageCounterIncrement(assembler);
1698 GenerateNArgsCheckInlineCacheStub(assembler, 1,
1699 kInlineCacheMissHandlerOneArgRuntimeEntry,
1700 Token::kILLEGAL, true /* optimized */);
1701 }
1702
1703
1704 void StubCode::GenerateTwoArgsOptimizedCheckInlineCacheStub(
1705 Assembler* assembler) {
1706 GenerateOptimizedUsageCounterIncrement(assembler);
1707 GenerateNArgsCheckInlineCacheStub(assembler, 2,
1708 kInlineCacheMissHandlerTwoArgsRuntimeEntry,
1709 Token::kILLEGAL, true /* optimized */);
1710 }
1711
1712
1713 // Intermediary stub between a static call and its target. ICData contains
1714 // the target function and the call count.
1715 // S5: ICData
1716 void StubCode::GenerateZeroArgsUnoptimizedStaticCallStub(Assembler* assembler) {
1717 GenerateUsageCounterIncrement(assembler, T0);
1718 __ Comment("UnoptimizedStaticCallStub");
1719 #if defined(DEBUG)
1720 {
1721 Label ok;
1722 // Check that the IC data array has NumArgsTested() == 0.
1723 // 'NumArgsTested' is stored in the least significant bits of 'state_bits'.
1724 __ lw(T0, FieldAddress(S5, ICData::state_bits_offset()));
1725 ASSERT(ICData::NumArgsTestedShift() == 0); // No shift needed.
1726 __ andi(T0, T0, Immediate(ICData::NumArgsTestedMask()));
1727 __ beq(T0, ZR, &ok);
1728 __ Stop("Incorrect IC data for unoptimized static call");
1729 __ Bind(&ok);
1730 }
1731 #endif // DEBUG
1732
1733 // Check single stepping.
1734 Label stepping, done_stepping;
1735 if (FLAG_support_debugger) {
1736 __ LoadIsolate(T0);
1737 __ lbu(T0, Address(T0, Isolate::single_step_offset()));
1738 __ BranchNotEqual(T0, Immediate(0), &stepping);
1739 __ Bind(&done_stepping);
1740 }
1741
1742 // S5: IC data object (preserved).
1743 __ lw(T0, FieldAddress(S5, ICData::ic_data_offset()));
1744 // T0: ic_data_array with entries: target functions and count.
1745 __ AddImmediate(T0, Array::data_offset() - kHeapObjectTag);
1746 // T0: points directly to the first ic data array element.
1747 const intptr_t target_offset = ICData::TargetIndexFor(0) * kWordSize;
1748 const intptr_t count_offset = ICData::CountIndexFor(0) * kWordSize;
1749
1750 if (FLAG_optimization_counter_threshold >= 0) {
1751 // Increment count for this call, ignore overflow.
1752 __ lw(T4, Address(T0, count_offset));
1753 __ AddImmediate(T4, T4, Smi::RawValue(1));
1754 __ sw(T4, Address(T0, count_offset));
1755 }
1756
1757 // Load arguments descriptor into S4.
1758 __ lw(S4, FieldAddress(S5, ICData::arguments_descriptor_offset()));
1759
1760 // Get function and call it, if possible.
1761 __ lw(T0, Address(T0, target_offset));
1762 __ lw(CODE_REG, FieldAddress(T0, Function::code_offset()));
1763 __ lw(T4, FieldAddress(T0, Function::entry_point_offset()));
1764 __ jr(T4);
1765
1766 // Call single step callback in debugger.
1767 if (FLAG_support_debugger) {
1768 __ Bind(&stepping);
1769 __ EnterStubFrame();
1770 __ addiu(SP, SP, Immediate(-2 * kWordSize));
1771 __ sw(S5, Address(SP, 1 * kWordSize)); // Preserve IC data.
1772 __ sw(RA, Address(SP, 0 * kWordSize)); // Return address.
1773 __ CallRuntime(kSingleStepHandlerRuntimeEntry, 0);
1774 __ lw(RA, Address(SP, 0 * kWordSize));
1775 __ lw(S5, Address(SP, 1 * kWordSize));
1776 __ addiu(SP, SP, Immediate(2 * kWordSize));
1777 __ RestoreCodePointer();
1778 __ LeaveStubFrame();
1779 __ b(&done_stepping);
1780 }
1781 }
1782
1783
1784 void StubCode::GenerateOneArgUnoptimizedStaticCallStub(Assembler* assembler) {
1785 GenerateUsageCounterIncrement(assembler, T0);
1786 GenerateNArgsCheckInlineCacheStub(
1787 assembler, 1, kStaticCallMissHandlerOneArgRuntimeEntry, Token::kILLEGAL);
1788 }
1789
1790
1791 void StubCode::GenerateTwoArgsUnoptimizedStaticCallStub(Assembler* assembler) {
1792 GenerateUsageCounterIncrement(assembler, T0);
1793 GenerateNArgsCheckInlineCacheStub(
1794 assembler, 2, kStaticCallMissHandlerTwoArgsRuntimeEntry, Token::kILLEGAL);
1795 }
1796
1797
1798 // Stub for compiling a function and jumping to the compiled code.
1799 // S5: IC-Data (for methods).
1800 // S4: Arguments descriptor.
1801 // T0: Function.
1802 void StubCode::GenerateLazyCompileStub(Assembler* assembler) {
1803 __ EnterStubFrame();
1804 __ addiu(SP, SP, Immediate(-3 * kWordSize));
1805 __ sw(S5, Address(SP, 2 * kWordSize)); // Preserve IC data object.
1806 __ sw(S4, Address(SP, 1 * kWordSize)); // Preserve args descriptor array.
1807 __ sw(T0, Address(SP, 0 * kWordSize)); // Pass function.
1808 __ CallRuntime(kCompileFunctionRuntimeEntry, 1);
1809 __ lw(T0, Address(SP, 0 * kWordSize)); // Restore function.
1810 __ lw(S4, Address(SP, 1 * kWordSize)); // Restore args descriptor array.
1811 __ lw(S5, Address(SP, 2 * kWordSize)); // Restore IC data array.
1812 __ addiu(SP, SP, Immediate(3 * kWordSize));
1813 __ LeaveStubFrame();
1814
1815 __ lw(CODE_REG, FieldAddress(T0, Function::code_offset()));
1816 __ lw(T2, FieldAddress(T0, Function::entry_point_offset()));
1817 __ jr(T2);
1818 }
1819
1820
1821 // S5: Contains an ICData.
1822 void StubCode::GenerateICCallBreakpointStub(Assembler* assembler) {
1823 __ Comment("ICCallBreakpoint stub");
1824 __ EnterStubFrame();
1825 __ addiu(SP, SP, Immediate(-2 * kWordSize));
1826 __ sw(S5, Address(SP, 1 * kWordSize));
1827 __ sw(ZR, Address(SP, 0 * kWordSize));
1828
1829 __ CallRuntime(kBreakpointRuntimeHandlerRuntimeEntry, 0);
1830
1831 __ lw(S5, Address(SP, 1 * kWordSize));
1832 __ lw(CODE_REG, Address(SP, 0 * kWordSize));
1833 __ addiu(SP, SP, Immediate(2 * kWordSize));
1834 __ LeaveStubFrame();
1835 __ lw(T0, FieldAddress(CODE_REG, Code::entry_point_offset()));
1836 __ jr(T0);
1837 }
1838
1839
1840 void StubCode::GenerateRuntimeCallBreakpointStub(Assembler* assembler) {
1841 __ Comment("RuntimeCallBreakpoint stub");
1842 __ EnterStubFrame();
1843 __ addiu(SP, SP, Immediate(-1 * kWordSize));
1844 __ sw(ZR, Address(SP, 0 * kWordSize));
1845
1846 __ CallRuntime(kBreakpointRuntimeHandlerRuntimeEntry, 0);
1847
1848 __ lw(CODE_REG, Address(SP, 0 * kWordSize));
1849 __ addiu(SP, SP, Immediate(3 * kWordSize));
1850 __ LeaveStubFrame();
1851 __ lw(T0, FieldAddress(CODE_REG, Code::entry_point_offset()));
1852 __ jr(T0);
1853 }
1854
1855
1856 // Called only from unoptimized code. All relevant registers have been saved.
1857 // RA: return address.
1858 void StubCode::GenerateDebugStepCheckStub(Assembler* assembler) {
1859 // Check single stepping.
1860 Label stepping, done_stepping;
1861 __ LoadIsolate(T0);
1862 __ lbu(T0, Address(T0, Isolate::single_step_offset()));
1863 __ BranchNotEqual(T0, Immediate(0), &stepping);
1864 __ Bind(&done_stepping);
1865
1866 __ Ret();
1867
1868 // Call single step callback in debugger.
1869 __ Bind(&stepping);
1870 __ EnterStubFrame();
1871 __ addiu(SP, SP, Immediate(-1 * kWordSize));
1872 __ sw(RA, Address(SP, 0 * kWordSize)); // Return address.
1873 __ CallRuntime(kSingleStepHandlerRuntimeEntry, 0);
1874 __ lw(RA, Address(SP, 0 * kWordSize));
1875 __ addiu(SP, SP, Immediate(1 * kWordSize));
1876 __ LeaveStubFrame();
1877 __ b(&done_stepping);
1878 }
1879
1880
1881 // Used to check class and type arguments. Arguments passed in registers:
1882 // RA: return address.
1883 // A0: instance (must be preserved).
1884 // A1: instantiator type arguments (only if n == 4, can be raw_null).
1885 // A2: function type arguments (only if n == 4, can be raw_null).
1886 // A3: SubtypeTestCache.
1887 // Result in V0: null -> not found, otherwise result (true or false).
1888 static void GenerateSubtypeNTestCacheStub(Assembler* assembler, int n) {
1889 __ Comment("SubtypeNTestCacheStub");
1890 ASSERT((n == 1) || (n == 2) || (n == 4));
1891 if (n > 1) {
1892 __ LoadClass(T0, A0);
1893 // Compute instance type arguments into T1.
1894 Label has_no_type_arguments;
1895 __ LoadObject(T1, Object::null_object());
1896 __ lw(T2, FieldAddress(
1897 T0, Class::type_arguments_field_offset_in_words_offset()));
1898 __ BranchEqual(T2, Immediate(Class::kNoTypeArguments),
1899 &has_no_type_arguments);
1900 __ sll(T2, T2, 2);
1901 __ addu(T2, A0, T2); // T2 <- A0 + T2 * 4
1902 __ lw(T1, FieldAddress(T2, 0));
1903 __ Bind(&has_no_type_arguments);
1904 }
1905 __ LoadClassId(T0, A0);
1906 // A0: instance.
1907 // A1: instantiator type arguments (only if n == 4, can be raw_null).
1908 // A2: function type arguments (only if n == 4, can be raw_null).
1909 // A3: SubtypeTestCache.
1910 // T0: instance class id.
1911 // T1: instance type arguments (null if none), used only if n > 1.
1912 __ lw(T2, FieldAddress(A3, SubtypeTestCache::cache_offset()));
1913 __ AddImmediate(T2, Array::data_offset() - kHeapObjectTag);
1914
1915 __ LoadObject(T7, Object::null_object());
1916 Label loop, found, not_found, next_iteration;
1917 // T0: instance class id.
1918 // T1: instance type arguments (still null if closure).
1919 // T2: Entry start.
1920 // T7: null.
1921 __ SmiTag(T0);
1922 __ BranchNotEqual(T0, Immediate(Smi::RawValue(kClosureCid)), &loop);
1923 __ lw(T1, FieldAddress(A0, Closure::function_type_arguments_offset()));
1924 __ bne(T1, T7, &not_found); // Cache cannot be used for generic closures.
1925 __ lw(T1, FieldAddress(A0, Closure::instantiator_type_arguments_offset()));
1926 __ lw(T0, FieldAddress(A0, Closure::function_offset()));
1927 // T0: instance class id as Smi or function.
1928 __ Bind(&loop);
1929 __ lw(T3,
1930 Address(T2, kWordSize * SubtypeTestCache::kInstanceClassIdOrFunction));
1931 __ beq(T3, T7, &not_found);
1932 if (n == 1) {
1933 __ beq(T3, T0, &found);
1934 } else {
1935 __ bne(T3, T0, &next_iteration);
1936 __ lw(T3,
1937 Address(T2, kWordSize * SubtypeTestCache::kInstanceTypeArguments));
1938 if (n == 2) {
1939 __ beq(T3, T1, &found);
1940 } else {
1941 __ bne(T3, T1, &next_iteration);
1942 __ lw(T3, Address(T2, kWordSize *
1943 SubtypeTestCache::kInstantiatorTypeArguments));
1944 __ bne(T3, A1, &next_iteration);
1945 __ lw(T3,
1946 Address(T2, kWordSize * SubtypeTestCache::kFunctionTypeArguments));
1947 __ beq(T3, A2, &found);
1948 }
1949 }
1950 __ Bind(&next_iteration);
1951 __ b(&loop);
1952 __ delay_slot()->addiu(
1953 T2, T2, Immediate(kWordSize * SubtypeTestCache::kTestEntryLength));
1954 // Fall through to not found.
1955 __ Bind(&not_found);
1956 __ Ret();
1957 __ delay_slot()->mov(V0, T7);
1958
1959 __ Bind(&found);
1960 __ Ret();
1961 __ delay_slot()->lw(V0,
1962 Address(T2, kWordSize * SubtypeTestCache::kTestResult));
1963 }
1964
1965
1966 // Used to check class and type arguments. Arguments passed in registers:
1967 // RA: return address.
1968 // A0: instance (must be preserved).
1969 // A1: unused.
1970 // A2: unused.
1971 // A3: SubtypeTestCache.
1972 // Result in V0: null -> not found, otherwise result (true or false).
1973 void StubCode::GenerateSubtype1TestCacheStub(Assembler* assembler) {
1974 GenerateSubtypeNTestCacheStub(assembler, 1);
1975 }
1976
1977
1978 // Used to check class and type arguments. Arguments passed in registers:
1979 // RA: return address.
1980 // A0: instance (must be preserved).
1981 // A1: unused.
1982 // A2: unused.
1983 // A3: SubtypeTestCache.
1984 // Result in V0: null -> not found, otherwise result (true or false).
1985 void StubCode::GenerateSubtype2TestCacheStub(Assembler* assembler) {
1986 GenerateSubtypeNTestCacheStub(assembler, 2);
1987 }
1988
1989
1990 // Used to check class and type arguments. Arguments passed in registers:
1991 // RA: return address.
1992 // A0: instance (must be preserved).
1993 // A1: instantiator type arguments (can be raw_null).
1994 // A2: function type arguments (can be raw_null).
1995 // A3: SubtypeTestCache.
1996 // Result in V0: null -> not found, otherwise result (true or false).
1997 void StubCode::GenerateSubtype4TestCacheStub(Assembler* assembler) {
1998 GenerateSubtypeNTestCacheStub(assembler, 4);
1999 }
2000
2001
2002 // Return the current stack pointer address, used to stack alignment
2003 // checks.
2004 void StubCode::GenerateGetCStackPointerStub(Assembler* assembler) {
2005 __ Ret();
2006 __ delay_slot()->mov(V0, SP);
2007 }
2008
2009
2010 // Jump to the exception or error handler.
2011 // RA: return address.
2012 // A0: program_counter.
2013 // A1: stack_pointer.
2014 // A2: frame_pointer.
2015 // A3: thread.
2016 // Does not return.
2017 void StubCode::GenerateJumpToFrameStub(Assembler* assembler) {
2018 ASSERT(kExceptionObjectReg == V0);
2019 ASSERT(kStackTraceObjectReg == V1);
2020 __ mov(FP, A2); // Frame_pointer.
2021 __ mov(THR, A3); // Thread.
2022 // Set tag.
2023 __ LoadImmediate(A2, VMTag::kDartTagId);
2024 __ sw(A2, Assembler::VMTagAddress());
2025 // Clear top exit frame.
2026 __ sw(ZR, Address(THR, Thread::top_exit_frame_info_offset()));
2027 // Restore pool pointer.
2028 __ RestoreCodePointer();
2029 __ LoadPoolPointer();
2030 __ jr(A0); // Jump to the program counter.
2031 __ delay_slot()->mov(SP, A1); // Stack pointer.
2032 }
2033
2034
2035 // Run an exception handler. Execution comes from JumpToFrame
2036 // stub or from the simulator.
2037 //
2038 // The arguments are stored in the Thread object.
2039 // Does not return.
2040 void StubCode::GenerateRunExceptionHandlerStub(Assembler* assembler) {
2041 __ lw(A0, Address(THR, Thread::resume_pc_offset()));
2042 __ LoadImmediate(A2, 0);
2043
2044 // Load the exception from the current thread.
2045 Address exception_addr(THR, Thread::active_exception_offset());
2046 __ lw(V0, exception_addr);
2047 __ sw(A2, exception_addr);
2048
2049 // Load the stacktrace from the current thread.
2050 Address stacktrace_addr(THR, Thread::active_stacktrace_offset());
2051 __ lw(V1, stacktrace_addr);
2052
2053 __ jr(A0); // Jump to continuation point.
2054 __ delay_slot()->sw(A2, stacktrace_addr);
2055 }
2056
2057
2058 // Deoptimize a frame on the call stack before rewinding.
2059 // The arguments are stored in the Thread object.
2060 // No result.
2061 void StubCode::GenerateDeoptForRewindStub(Assembler* assembler) {
2062 // Push zap value instead of CODE_REG.
2063 __ LoadImmediate(TMP, kZapCodeReg);
2064 __ Push(TMP);
2065
2066 // Load the deopt pc into RA.
2067 __ lw(RA, Address(THR, Thread::resume_pc_offset()));
2068 GenerateDeoptimizationSequence(assembler, kEagerDeopt);
2069
2070 // After we have deoptimized, jump to the correct frame.
2071 __ EnterStubFrame();
2072 __ CallRuntime(kRewindPostDeoptRuntimeEntry, 0);
2073 __ LeaveStubFrame();
2074 __ break_(0);
2075 }
2076
2077
2078 // Calls to the runtime to optimize the given function.
2079 // T0: function to be reoptimized.
2080 // S4: argument descriptor (preserved).
2081 void StubCode::GenerateOptimizeFunctionStub(Assembler* assembler) {
2082 __ Comment("OptimizeFunctionStub");
2083 __ EnterStubFrame();
2084 __ addiu(SP, SP, Immediate(-3 * kWordSize));
2085 __ sw(S4, Address(SP, 2 * kWordSize));
2086 // Setup space on stack for return value.
2087 __ sw(ZR, Address(SP, 1 * kWordSize));
2088 __ sw(T0, Address(SP, 0 * kWordSize));
2089 __ CallRuntime(kOptimizeInvokedFunctionRuntimeEntry, 1);
2090 __ Comment("OptimizeFunctionStub return");
2091 __ lw(T0, Address(SP, 1 * kWordSize)); // Get Function object
2092 __ lw(S4, Address(SP, 2 * kWordSize)); // Restore argument descriptor.
2093 __ addiu(SP, SP, Immediate(3 * kWordSize)); // Discard argument.
2094
2095 __ lw(CODE_REG, FieldAddress(T0, Function::code_offset()));
2096 __ lw(T1, FieldAddress(T0, Function::entry_point_offset()));
2097 __ LeaveStubFrameAndReturn(T1);
2098 __ break_(0);
2099 }
2100
2101
2102 // Does identical check (object references are equal or not equal) with special
2103 // checks for boxed numbers.
2104 // Returns: CMPRES1 is zero if equal, non-zero otherwise.
2105 // Note: A Mint cannot contain a value that would fit in Smi, a Bigint
2106 // cannot contain a value that fits in Mint or Smi.
2107 static void GenerateIdenticalWithNumberCheckStub(Assembler* assembler,
2108 const Register left,
2109 const Register right,
2110 const Register temp1,
2111 const Register temp2) {
2112 __ Comment("IdenticalWithNumberCheckStub");
2113 Label reference_compare, done, check_mint, check_bigint;
2114 // If any of the arguments is Smi do reference compare.
2115 __ andi(temp1, left, Immediate(kSmiTagMask));
2116 __ beq(temp1, ZR, &reference_compare);
2117 __ andi(temp1, right, Immediate(kSmiTagMask));
2118 __ beq(temp1, ZR, &reference_compare);
2119
2120 // Value compare for two doubles.
2121 __ LoadImmediate(temp1, kDoubleCid);
2122 __ LoadClassId(temp2, left);
2123 __ bne(temp1, temp2, &check_mint);
2124 __ LoadClassId(temp2, right);
2125 __ subu(CMPRES1, temp1, temp2);
2126 __ bne(CMPRES1, ZR, &done);
2127
2128 // Double values bitwise compare.
2129 __ lw(temp1, FieldAddress(left, Double::value_offset() + 0 * kWordSize));
2130 __ lw(temp2, FieldAddress(right, Double::value_offset() + 0 * kWordSize));
2131 __ subu(CMPRES1, temp1, temp2);
2132 __ bne(CMPRES1, ZR, &done);
2133 __ lw(temp1, FieldAddress(left, Double::value_offset() + 1 * kWordSize));
2134 __ lw(temp2, FieldAddress(right, Double::value_offset() + 1 * kWordSize));
2135 __ b(&done);
2136 __ delay_slot()->subu(CMPRES1, temp1, temp2);
2137
2138 __ Bind(&check_mint);
2139 __ LoadImmediate(temp1, kMintCid);
2140 __ LoadClassId(temp2, left);
2141 __ bne(temp1, temp2, &check_bigint);
2142 __ LoadClassId(temp2, right);
2143 __ subu(CMPRES1, temp1, temp2);
2144 __ bne(CMPRES1, ZR, &done);
2145
2146 __ lw(temp1, FieldAddress(left, Mint::value_offset() + 0 * kWordSize));
2147 __ lw(temp2, FieldAddress(right, Mint::value_offset() + 0 * kWordSize));
2148 __ subu(CMPRES1, temp1, temp2);
2149 __ bne(CMPRES1, ZR, &done);
2150 __ lw(temp1, FieldAddress(left, Mint::value_offset() + 1 * kWordSize));
2151 __ lw(temp2, FieldAddress(right, Mint::value_offset() + 1 * kWordSize));
2152 __ b(&done);
2153 __ delay_slot()->subu(CMPRES1, temp1, temp2);
2154
2155 __ Bind(&check_bigint);
2156 __ LoadImmediate(temp1, kBigintCid);
2157 __ LoadClassId(temp2, left);
2158 __ bne(temp1, temp2, &reference_compare);
2159 __ LoadClassId(temp2, right);
2160 __ subu(CMPRES1, temp1, temp2);
2161 __ bne(CMPRES1, ZR, &done);
2162
2163 __ EnterStubFrame();
2164 __ ReserveAlignedFrameSpace(2 * kWordSize);
2165 __ sw(left, Address(SP, 1 * kWordSize));
2166 __ sw(right, Address(SP, 0 * kWordSize));
2167 __ mov(A0, left);
2168 __ mov(A1, right);
2169 __ CallRuntime(kBigintCompareRuntimeEntry, 2);
2170 __ Comment("IdenticalWithNumberCheckStub return");
2171 // Result in V0, 0 means equal.
2172 __ LeaveStubFrame();
2173 __ b(&done);
2174 __ delay_slot()->mov(CMPRES1, V0);
2175
2176 __ Bind(&reference_compare);
2177 __ subu(CMPRES1, left, right);
2178 __ Bind(&done);
2179 // A branch or test after this comparison will check CMPRES1 == ZR.
2180 }
2181
2182
2183 // Called only from unoptimized code. All relevant registers have been saved.
2184 // RA: return address.
2185 // SP + 4: left operand.
2186 // SP + 0: right operand.
2187 // Returns: CMPRES1 is zero if equal, non-zero otherwise.
2188 void StubCode::GenerateUnoptimizedIdenticalWithNumberCheckStub(
2189 Assembler* assembler) {
2190 // Check single stepping.
2191 Label stepping, done_stepping;
2192 if (FLAG_support_debugger) {
2193 __ LoadIsolate(T0);
2194 __ lbu(T0, Address(T0, Isolate::single_step_offset()));
2195 __ BranchNotEqual(T0, Immediate(0), &stepping);
2196 __ Bind(&done_stepping);
2197 }
2198
2199 const Register temp1 = T2;
2200 const Register temp2 = T3;
2201 const Register left = T1;
2202 const Register right = T0;
2203 __ lw(left, Address(SP, 1 * kWordSize));
2204 __ lw(right, Address(SP, 0 * kWordSize));
2205 GenerateIdenticalWithNumberCheckStub(assembler, left, right, temp1, temp2);
2206 __ Ret();
2207
2208 // Call single step callback in debugger.
2209 if (FLAG_support_debugger) {
2210 __ Bind(&stepping);
2211 __ EnterStubFrame();
2212 __ addiu(SP, SP, Immediate(-1 * kWordSize));
2213 __ sw(RA, Address(SP, 0 * kWordSize)); // Return address.
2214 __ CallRuntime(kSingleStepHandlerRuntimeEntry, 0);
2215 __ lw(RA, Address(SP, 0 * kWordSize));
2216 __ addiu(SP, SP, Immediate(1 * kWordSize));
2217 __ RestoreCodePointer();
2218 __ LeaveStubFrame();
2219 __ b(&done_stepping);
2220 }
2221 }
2222
2223
2224 // Called from optimized code only.
2225 // SP + 4: left operand.
2226 // SP + 0: right operand.
2227 // Returns: CMPRES1 is zero if equal, non-zero otherwise.
2228 void StubCode::GenerateOptimizedIdenticalWithNumberCheckStub(
2229 Assembler* assembler) {
2230 const Register temp1 = T2;
2231 const Register temp2 = T3;
2232 const Register left = T1;
2233 const Register right = T0;
2234 __ lw(left, Address(SP, 1 * kWordSize));
2235 __ lw(right, Address(SP, 0 * kWordSize));
2236 GenerateIdenticalWithNumberCheckStub(assembler, left, right, temp1, temp2);
2237 __ Ret();
2238 }
2239
2240
2241 // Called from megamorphic calls.
2242 // T0: receiver
2243 // S5: MegamorphicCache (preserved)
2244 // Passed to target:
2245 // CODE_REG: target Code object
2246 // S4: arguments descriptor
2247 void StubCode::GenerateMegamorphicCallStub(Assembler* assembler) {
2248 __ LoadTaggedClassIdMayBeSmi(T0, T0);
2249 // T0: class ID of the receiver (smi).
2250 __ lw(S4, FieldAddress(S5, MegamorphicCache::arguments_descriptor_offset()));
2251 __ lw(T2, FieldAddress(S5, MegamorphicCache::buckets_offset()));
2252 __ lw(T1, FieldAddress(S5, MegamorphicCache::mask_offset()));
2253 // T2: cache buckets array.
2254 // T1: mask.
2255 __ LoadImmediate(TMP, MegamorphicCache::kSpreadFactor);
2256 __ mult(TMP, T0);
2257 __ mflo(T3);
2258 // T3: probe.
2259
2260 Label loop, update, call_target_function;
2261 __ b(&loop);
2262
2263 __ Bind(&update);
2264 __ addiu(T3, T3, Immediate(Smi::RawValue(1)));
2265 __ Bind(&loop);
2266 __ and_(T3, T3, T1);
2267 const intptr_t base = Array::data_offset();
2268 // T3 is smi tagged, but table entries are two words, so LSL 2.
2269 __ sll(TMP, T3, 2);
2270 __ addu(TMP, T2, TMP);
2271 __ lw(T4, FieldAddress(TMP, base));
2272
2273 ASSERT(kIllegalCid == 0);
2274 __ beq(T4, ZR, &call_target_function);
2275 __ bne(T4, T0, &update);
2276
2277 __ Bind(&call_target_function);
2278 // Call the target found in the cache. For a class id match, this is a
2279 // proper target for the given name and arguments descriptor. If the
2280 // illegal class id was found, the target is a cache miss handler that can
2281 // be invoked as a normal Dart function.
2282 __ sll(T1, T3, 2);
2283 __ addu(T1, T2, T1);
2284 __ lw(T0, FieldAddress(T1, base + kWordSize));
2285
2286 __ lw(T1, FieldAddress(T0, Function::entry_point_offset()));
2287 __ lw(CODE_REG, FieldAddress(T0, Function::code_offset()));
2288 __ jr(T1);
2289 }
2290
2291
2292 // Called from switchable IC calls.
2293 // T0: receiver
2294 // S5: ICData (preserved)
2295 // Passed to target:
2296 // CODE_REG: target Code object
2297 // S4: arguments descriptor
2298 void StubCode::GenerateICCallThroughFunctionStub(Assembler* assembler) {
2299 Label loop, found, miss;
2300 __ lw(T6, FieldAddress(S5, ICData::ic_data_offset()));
2301 __ lw(S4, FieldAddress(S5, ICData::arguments_descriptor_offset()));
2302 __ AddImmediate(T6, T6, Array::data_offset() - kHeapObjectTag);
2303 // T6: first IC entry.
2304 __ LoadTaggedClassIdMayBeSmi(T1, T0);
2305 // T1: receiver cid as Smi
2306
2307 __ Bind(&loop);
2308 __ lw(T2, Address(T6, 0));
2309 __ beq(T1, T2, &found);
2310 ASSERT(Smi::RawValue(kIllegalCid) == 0);
2311 __ beq(T2, ZR, &miss);
2312
2313 const intptr_t entry_length = ICData::TestEntryLengthFor(1) * kWordSize;
2314 __ AddImmediate(T6, entry_length); // Next entry.
2315 __ b(&loop);
2316
2317 __ Bind(&found);
2318 const intptr_t target_offset = ICData::TargetIndexFor(1) * kWordSize;
2319 __ lw(T0, Address(T6, target_offset));
2320 __ lw(T1, FieldAddress(T0, Function::entry_point_offset()));
2321 __ lw(CODE_REG, FieldAddress(T0, Function::code_offset()));
2322 __ jr(T1);
2323
2324 __ Bind(&miss);
2325 __ LoadIsolate(T2);
2326 __ lw(CODE_REG, Address(T2, Isolate::ic_miss_code_offset()));
2327 __ lw(T1, FieldAddress(CODE_REG, Code::entry_point_offset()));
2328 __ jr(T1);
2329 }
2330
2331
2332 void StubCode::GenerateICCallThroughCodeStub(Assembler* assembler) {
2333 Label loop, found, miss;
2334 __ lw(T6, FieldAddress(S5, ICData::ic_data_offset()));
2335 __ lw(S4, FieldAddress(S5, ICData::arguments_descriptor_offset()));
2336 __ AddImmediate(T6, T6, Array::data_offset() - kHeapObjectTag);
2337 // T6: first IC entry.
2338 __ LoadTaggedClassIdMayBeSmi(T1, T0);
2339 // T1: receiver cid as Smi
2340
2341 __ Bind(&loop);
2342 __ lw(T2, Address(T6, 0));
2343 __ beq(T1, T2, &found);
2344 ASSERT(Smi::RawValue(kIllegalCid) == 0);
2345 __ beq(T2, ZR, &miss);
2346
2347 const intptr_t entry_length = ICData::TestEntryLengthFor(1) * kWordSize;
2348 __ AddImmediate(T6, entry_length); // Next entry.
2349 __ b(&loop);
2350
2351 __ Bind(&found);
2352 const intptr_t code_offset = ICData::CodeIndexFor(1) * kWordSize;
2353 const intptr_t entry_offset = ICData::EntryPointIndexFor(1) * kWordSize;
2354 __ lw(T1, Address(T6, entry_offset));
2355 __ lw(CODE_REG, Address(T6, code_offset));
2356 __ jr(T1);
2357
2358 __ Bind(&miss);
2359 __ LoadIsolate(T2);
2360 __ lw(CODE_REG, Address(T2, Isolate::ic_miss_code_offset()));
2361 __ lw(T1, FieldAddress(CODE_REG, Code::entry_point_offset()));
2362 __ jr(T1);
2363 }
2364
2365
2366 // Called from switchable IC calls.
2367 // T0: receiver
2368 // S5: SingleTargetCache
2369 void StubCode::GenerateUnlinkedCallStub(Assembler* assembler) {
2370 __ EnterStubFrame();
2371 __ Push(T0); // Preserve receiver.
2372
2373 __ Push(ZR); // Result slot.
2374 __ Push(T0); // Arg0: Receiver
2375 __ Push(S5); // Arg1: UnlinkedCall
2376 __ CallRuntime(kUnlinkedCallRuntimeEntry, 2);
2377 __ Drop(2);
2378 __ Pop(S5); // result = IC
2379
2380 __ Pop(T0); // Restore receiver.
2381 __ LeaveStubFrame();
2382
2383 __ lw(CODE_REG, Address(THR, Thread::ic_lookup_through_code_stub_offset()));
2384 __ lw(T1, FieldAddress(CODE_REG, Code::checked_entry_point_offset()));
2385 __ jr(T1);
2386 }
2387
2388
2389 // Called from switchable IC calls.
2390 // T0: receiver
2391 // S5: SingleTargetCache
2392 // Passed to target:
2393 // CODE_REG: target Code object
2394 void StubCode::GenerateSingleTargetCallStub(Assembler* assembler) {
2395 Label miss;
2396 __ LoadClassIdMayBeSmi(T1, T0);
2397 __ lhu(T2, FieldAddress(S5, SingleTargetCache::lower_limit_offset()));
2398 __ lhu(T3, FieldAddress(S5, SingleTargetCache::upper_limit_offset()));
2399
2400 __ BranchUnsignedLess(T1, T2, &miss);
2401 __ BranchUnsignedGreater(T1, T3, &miss);
2402
2403 __ lw(T1, FieldAddress(S5, SingleTargetCache::entry_point_offset()));
2404 __ lw(CODE_REG, FieldAddress(S5, SingleTargetCache::target_offset()));
2405 __ jr(T1);
2406
2407 __ Bind(&miss);
2408 __ EnterStubFrame();
2409 __ Push(T0); // Preserve receiver.
2410
2411 __ Push(ZR); // Result slot.
2412 __ Push(T0); // Arg0: Receiver
2413 __ CallRuntime(kSingleTargetMissRuntimeEntry, 1);
2414 __ Drop(1);
2415 __ Pop(S5); // result = IC
2416
2417 __ Pop(T0); // Restore receiver.
2418 __ LeaveStubFrame();
2419
2420 __ lw(CODE_REG, Address(THR, Thread::ic_lookup_through_code_stub_offset()));
2421 __ lw(T1, FieldAddress(CODE_REG, Code::checked_entry_point_offset()));
2422 __ jr(T1);
2423 }
2424
2425
2426 // Called from the monomorphic checked entry.
2427 // T0: receiver
2428 void StubCode::GenerateMonomorphicMissStub(Assembler* assembler) {
2429 __ lw(CODE_REG, Address(THR, Thread::monomorphic_miss_stub_offset()));
2430 __ EnterStubFrame();
2431 __ Push(T0); // Preserve receiver.
2432
2433 __ Push(ZR); // Result slot.
2434 __ Push(T0); // Arg0: Receiver
2435 __ CallRuntime(kMonomorphicMissRuntimeEntry, 1);
2436 __ Drop(1);
2437 __ Pop(S5); // result = IC
2438
2439 __ Pop(T0); // Restore receiver.
2440 __ LeaveStubFrame();
2441
2442 __ lw(CODE_REG, Address(THR, Thread::ic_lookup_through_code_stub_offset()));
2443 __ lw(T1, FieldAddress(CODE_REG, Code::checked_entry_point_offset()));
2444 __ jr(T1);
2445 }
2446
2447
2448 void StubCode::GenerateFrameAwaitingMaterializationStub(Assembler* assembler) {
2449 __ break_(0);
2450 }
2451
2452
2453 void StubCode::GenerateAsynchronousGapMarkerStub(Assembler* assembler) {
2454 __ break_(0);
2455 }
2456
2457 } // namespace dart
2458
2459 #endif // defined TARGET_ARCH_MIPS
OLDNEW
« no previous file with comments | « runtime/vm/stack_frame_mips.h ('k') | runtime/vm/stub_code_mips_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698