V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
code-stubs-mips64.cc
1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #if V8_TARGET_ARCH_MIPS64
6 
7 #include "src/api-arguments.h"
8 #include "src/bootstrapper.h"
9 #include "src/code-stubs.h"
10 #include "src/frame-constants.h"
11 #include "src/frames.h"
12 #include "src/ic/ic.h"
13 #include "src/ic/stub-cache.h"
14 #include "src/isolate.h"
15 #include "src/macro-assembler.h"
16 #include "src/objects/api-callbacks.h"
17 #include "src/regexp/jsregexp.h"
18 #include "src/regexp/regexp-macro-assembler.h"
19 #include "src/runtime/runtime.h"
20 
21 #include "src/mips64/code-stubs-mips64.h" // Cannot be the first include.
22 
23 namespace v8 {
24 namespace internal {
25 
26 #define __ ACCESS_MASM(masm)
27 
28 void JSEntryStub::Generate(MacroAssembler* masm) {
29  Label invoke, handler_entry, exit;
30  Isolate* isolate = masm->isolate();
31 
32  {
33  NoRootArrayScope no_root_array(masm);
34 
35  // TODO(plind): unify the ABI description here.
36  // Registers:
37  // a0: entry address
38  // a1: function
39  // a2: receiver
40  // a3: argc
41  // a4 (a4): on mips64
42 
43  // Stack:
44  // 0 arg slots on mips64 (4 args slots on mips)
45  // args -- in a4/a4 on mips64, on stack on mips
46 
47  // Save callee saved registers on the stack.
48  __ MultiPush(kCalleeSaved | ra.bit());
49 
50  // Save callee-saved FPU registers.
51  __ MultiPushFPU(kCalleeSavedFPU);
52  // Set up the reserved register for 0.0.
53  __ Move(kDoubleRegZero, 0.0);
54 
55  // Load argv in s0 register.
56  __ mov(s0, a4); // 5th parameter in mips64 a4 (a4) register.
57 
58  __ InitializeRootRegister();
59  }
60 
61  // We build an EntryFrame.
62  __ li(a7, Operand(-1)); // Push a bad frame pointer to fail if it is used.
63  StackFrame::Type marker = type();
64  __ li(a6, Operand(StackFrame::TypeToMarker(marker)));
65  __ li(a5, Operand(StackFrame::TypeToMarker(marker)));
66  ExternalReference c_entry_fp =
67  ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate);
68  __ li(a4, Operand(c_entry_fp));
69  __ Ld(a4, MemOperand(a4));
70  __ Push(a7, a6, a5, a4);
71  // Set up frame pointer for the frame to be pushed.
72  __ daddiu(fp, sp, -EntryFrameConstants::kCallerFPOffset);
73 
74  // Registers:
75  // a0: entry_address
76  // a1: function
77  // a2: receiver_pointer
78  // a3: argc
79  // s0: argv
80  //
81  // Stack:
82  // caller fp |
83  // function slot | entry frame
84  // context slot |
85  // bad fp (0xFF...F) |
86  // callee saved registers + ra
87  // [ O32: 4 args slots]
88  // args
89 
90  // If this is the outermost JS call, set js_entry_sp value.
91  Label non_outermost_js;
92  ExternalReference js_entry_sp =
93  ExternalReference::Create(IsolateAddressId::kJSEntrySPAddress, isolate);
94  __ li(a5, js_entry_sp);
95  __ Ld(a6, MemOperand(a5));
96  __ Branch(&non_outermost_js, ne, a6, Operand(zero_reg));
97  __ Sd(fp, MemOperand(a5));
98  __ li(a4, Operand(StackFrame::OUTERMOST_JSENTRY_FRAME));
99  Label cont;
100  __ b(&cont);
101  __ nop(); // Branch delay slot nop.
102  __ bind(&non_outermost_js);
103  __ li(a4, Operand(StackFrame::INNER_JSENTRY_FRAME));
104  __ bind(&cont);
105  __ push(a4);
106 
107  // Jump to a faked try block that does the invoke, with a faked catch
108  // block that sets the pending exception.
109  __ jmp(&invoke);
110  __ bind(&handler_entry);
111  handler_offset_ = handler_entry.pos();
112  // Caught exception: Store result (exception) in the pending exception
113  // field in the JSEnv and return a failure sentinel. Coming in here the
114  // fp will be invalid because the PushStackHandler below sets it to 0 to
115  // signal the existence of the JSEntry frame.
116  __ li(a4, ExternalReference::Create(
117  IsolateAddressId::kPendingExceptionAddress, isolate));
118  __ Sd(v0, MemOperand(a4)); // We come back from 'invoke'. result is in v0.
119  __ LoadRoot(v0, RootIndex::kException);
120  __ b(&exit); // b exposes branch delay slot.
121  __ nop(); // Branch delay slot nop.
122 
123  // Invoke: Link this frame into the handler chain.
124  __ bind(&invoke);
125  __ PushStackHandler();
126  // If an exception not caught by another handler occurs, this handler
127  // returns control to the code after the bal(&invoke) above, which
128  // restores all kCalleeSaved registers (including cp and fp) to their
129  // saved values before returning a failure to C.
130 
131  // Invoke the function by calling through JS entry trampoline builtin.
132  // Notice that we cannot store a reference to the trampoline code directly in
133  // this stub, because runtime stubs are not traversed when doing GC.
134 
135  // Registers:
136  // a0: entry_address
137  // a1: function
138  // a2: receiver_pointer
139  // a3: argc
140  // s0: argv
141  //
142  // Stack:
143  // handler frame
144  // entry frame
145  // callee saved registers + ra
146  // [ O32: 4 args slots]
147  // args
148  __ Call(EntryTrampoline(), RelocInfo::CODE_TARGET);
149 
150  // Unlink this frame from the handler chain.
151  __ PopStackHandler();
152 
153  __ bind(&exit); // v0 holds result
154  // Check if the current stack frame is marked as the outermost JS frame.
155  Label non_outermost_js_2;
156  __ pop(a5);
157  __ Branch(&non_outermost_js_2, ne, a5,
158  Operand(StackFrame::OUTERMOST_JSENTRY_FRAME));
159  __ li(a5, ExternalReference(js_entry_sp));
160  __ Sd(zero_reg, MemOperand(a5));
161  __ bind(&non_outermost_js_2);
162 
163  // Restore the top frame descriptors from the stack.
164  __ pop(a5);
165  __ li(a4,
166  ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate));
167  __ Sd(a5, MemOperand(a4));
168 
169  // Reset the stack to the callee saved registers.
170  __ daddiu(sp, sp, -EntryFrameConstants::kCallerFPOffset);
171 
172  // Restore callee-saved fpu registers.
173  __ MultiPopFPU(kCalleeSavedFPU);
174 
175  // Restore callee saved registers from the stack.
176  __ MultiPop(kCalleeSaved | ra.bit());
177  // Return.
178  __ Jump(ra);
179 }
180 
181 void DirectCEntryStub::Generate(MacroAssembler* masm) {
182  // Make place for arguments to fit C calling convention. Most of the callers
183  // of DirectCEntryStub::GenerateCall are using EnterExitFrame/LeaveExitFrame
184  // so they handle stack restoring and we don't have to do that here.
185  // Any caller of DirectCEntryStub::GenerateCall must take care of dropping
186  // kCArgsSlotsSize stack space after the call.
187  __ daddiu(sp, sp, -kCArgsSlotsSize);
188  // Place the return address on the stack, making the call
189  // GC safe. The RegExp backend also relies on this.
190  __ Sd(ra, MemOperand(sp, kCArgsSlotsSize));
191  __ Call(t9); // Call the C++ function.
192  __ Ld(t9, MemOperand(sp, kCArgsSlotsSize));
193 
194  if (FLAG_debug_code && FLAG_enable_slow_asserts) {
195  // In case of an error the return address may point to a memory area
196  // filled with kZapValue by the GC.
197  // Dereference the address and check for this.
198  __ Uld(a4, MemOperand(t9));
199  __ Assert(ne, AbortReason::kReceivedInvalidReturnAddress, a4,
200  Operand(reinterpret_cast<uint64_t>(kZapValue)));
201  }
202  __ Jump(t9);
203 }
204 
205 
206 void DirectCEntryStub::GenerateCall(MacroAssembler* masm,
207  Register target) {
208  if (FLAG_embedded_builtins) {
209  if (masm->root_array_available() &&
210  isolate()->ShouldLoadConstantsFromRootList()) {
211  // This is basically an inlined version of Call(Handle<Code>) that loads
212  // the code object into kScratchReg instead of t9.
213  __ Move(t9, target);
214  __ IndirectLoadConstant(kScratchReg, GetCode());
215  __ Daddu(kScratchReg, kScratchReg,
216  Operand(Code::kHeaderSize - kHeapObjectTag));
217  __ Call(kScratchReg);
218  return;
219  }
220  }
221  intptr_t loc =
222  reinterpret_cast<intptr_t>(GetCode().location());
223  __ Move(t9, target);
224  __ li(kScratchReg, Operand(loc, RelocInfo::CODE_TARGET), CONSTANT_SIZE);
225  __ Call(kScratchReg);
226 }
227 
228 static int AddressOffset(ExternalReference ref0, ExternalReference ref1) {
229  int64_t offset = (ref0.address() - ref1.address());
230  DCHECK(static_cast<int>(offset) == offset);
231  return static_cast<int>(offset);
232 }
233 
234 
235 // Calls an API function. Allocates HandleScope, extracts returned value
236 // from handle and propagates exceptions. Restores context. stack_space
237 // - space to be unwound on exit (includes the call JS arguments space and
238 // the additional space allocated for the fast call).
239 static void CallApiFunctionAndReturn(MacroAssembler* masm,
240  Register function_address,
241  ExternalReference thunk_ref,
242  int stack_space,
243  int32_t stack_space_offset,
244  MemOperand return_value_operand) {
245  Isolate* isolate = masm->isolate();
246  ExternalReference next_address =
247  ExternalReference::handle_scope_next_address(isolate);
248  const int kNextOffset = 0;
249  const int kLimitOffset = AddressOffset(
250  ExternalReference::handle_scope_limit_address(isolate), next_address);
251  const int kLevelOffset = AddressOffset(
252  ExternalReference::handle_scope_level_address(isolate), next_address);
253 
254  DCHECK(function_address == a1 || function_address == a2);
255 
256  Label profiler_disabled;
257  Label end_profiler_check;
258  __ li(t9, ExternalReference::is_profiling_address(isolate));
259  __ Lb(t9, MemOperand(t9, 0));
260  __ Branch(&profiler_disabled, eq, t9, Operand(zero_reg));
261 
262  // Additional parameter is the address of the actual callback.
263  __ li(t9, thunk_ref);
264  __ jmp(&end_profiler_check);
265 
266  __ bind(&profiler_disabled);
267  __ mov(t9, function_address);
268  __ bind(&end_profiler_check);
269 
270  // Allocate HandleScope in callee-save registers.
271  __ li(s5, next_address);
272  __ Ld(s0, MemOperand(s5, kNextOffset));
273  __ Ld(s1, MemOperand(s5, kLimitOffset));
274  __ Lw(s2, MemOperand(s5, kLevelOffset));
275  __ Addu(s2, s2, Operand(1));
276  __ Sw(s2, MemOperand(s5, kLevelOffset));
277 
278  if (FLAG_log_timer_events) {
279  FrameScope frame(masm, StackFrame::MANUAL);
280  __ PushSafepointRegisters();
281  __ PrepareCallCFunction(1, a0);
282  __ li(a0, ExternalReference::isolate_address(isolate));
283  __ CallCFunction(ExternalReference::log_enter_external_function(), 1);
284  __ PopSafepointRegisters();
285  }
286 
287  // Native call returns to the DirectCEntry stub which redirects to the
288  // return address pushed on stack (could have moved after GC).
289  // DirectCEntry stub itself is generated early and never moves.
290  DirectCEntryStub stub(isolate);
291  stub.GenerateCall(masm, t9);
292 
293  if (FLAG_log_timer_events) {
294  FrameScope frame(masm, StackFrame::MANUAL);
295  __ PushSafepointRegisters();
296  __ PrepareCallCFunction(1, a0);
297  __ li(a0, ExternalReference::isolate_address(isolate));
298  __ CallCFunction(ExternalReference::log_leave_external_function(), 1);
299  __ PopSafepointRegisters();
300  }
301 
302  Label promote_scheduled_exception;
303  Label delete_allocated_handles;
304  Label leave_exit_frame;
305  Label return_value_loaded;
306 
307  // Load value from ReturnValue.
308  __ Ld(v0, return_value_operand);
309  __ bind(&return_value_loaded);
310 
311  // No more valid handles (the result handle was the last one). Restore
312  // previous handle scope.
313  __ Sd(s0, MemOperand(s5, kNextOffset));
314  if (__ emit_debug_code()) {
315  __ Lw(a1, MemOperand(s5, kLevelOffset));
316  __ Check(eq, AbortReason::kUnexpectedLevelAfterReturnFromApiCall, a1,
317  Operand(s2));
318  }
319  __ Subu(s2, s2, Operand(1));
320  __ Sw(s2, MemOperand(s5, kLevelOffset));
321  __ Ld(kScratchReg, MemOperand(s5, kLimitOffset));
322  __ Branch(&delete_allocated_handles, ne, s1, Operand(kScratchReg));
323 
324  // Leave the API exit frame.
325  __ bind(&leave_exit_frame);
326 
327  if (stack_space_offset != kInvalidStackOffset) {
328  DCHECK_EQ(kCArgsSlotsSize, 0);
329  __ Ld(s0, MemOperand(sp, stack_space_offset));
330  } else {
331  __ li(s0, Operand(stack_space));
332  }
333  __ LeaveExitFrame(false, s0, NO_EMIT_RETURN,
334  stack_space_offset != kInvalidStackOffset);
335 
336  // Check if the function scheduled an exception.
337  __ LoadRoot(a4, RootIndex::kTheHoleValue);
338  __ li(kScratchReg, ExternalReference::scheduled_exception_address(isolate));
339  __ Ld(a5, MemOperand(kScratchReg));
340  __ Branch(&promote_scheduled_exception, ne, a4, Operand(a5));
341 
342  __ Ret();
343 
344  // Re-throw by promoting a scheduled exception.
345  __ bind(&promote_scheduled_exception);
346  __ TailCallRuntime(Runtime::kPromoteScheduledException);
347 
348  // HandleScope limit has changed. Delete allocated extensions.
349  __ bind(&delete_allocated_handles);
350  __ Sd(s1, MemOperand(s5, kLimitOffset));
351  __ mov(s0, v0);
352  __ mov(a0, v0);
353  __ PrepareCallCFunction(1, s1);
354  __ li(a0, ExternalReference::isolate_address(isolate));
355  __ CallCFunction(ExternalReference::delete_handle_scope_extensions(), 1);
356  __ mov(v0, s0);
357  __ jmp(&leave_exit_frame);
358 }
359 
360 void CallApiCallbackStub::Generate(MacroAssembler* masm) {
361  // ----------- S t a t e -------------
362  // -- a4 : call_data
363  // -- a2 : holder
364  // -- a1 : api_function_address
365  // -- cp : context
366  // --
367  // -- sp[0] : last argument
368  // -- ...
369  // -- sp[(argc - 1) * 8] : first argument
370  // -- sp[argc * 8] : receiver
371  // -----------------------------------
372 
373  Register call_data = a4;
374  Register holder = a2;
375  Register api_function_address = a1;
376 
377  typedef FunctionCallbackArguments FCA;
378 
379  STATIC_ASSERT(FCA::kArgsLength == 6);
380  STATIC_ASSERT(FCA::kNewTargetIndex == 5);
381  STATIC_ASSERT(FCA::kDataIndex == 4);
382  STATIC_ASSERT(FCA::kReturnValueOffset == 3);
383  STATIC_ASSERT(FCA::kReturnValueDefaultValueIndex == 2);
384  STATIC_ASSERT(FCA::kIsolateIndex == 1);
385  STATIC_ASSERT(FCA::kHolderIndex == 0);
386 
387  // new target
388  __ PushRoot(RootIndex::kUndefinedValue);
389 
390  // call data.
391  __ Push(call_data);
392 
393  Register scratch = call_data;
394  __ LoadRoot(scratch, RootIndex::kUndefinedValue);
395  // Push return value and default return value.
396  __ Push(scratch, scratch);
397  __ li(scratch, ExternalReference::isolate_address(masm->isolate()));
398  // Push isolate and holder.
399  __ Push(scratch, holder);
400 
401  // Prepare arguments.
402  __ mov(scratch, sp);
403 
404  // Allocate the v8::Arguments structure in the arguments' space since
405  // it's not controlled by GC.
406  const int kApiStackSpace = 3;
407 
408  FrameScope frame_scope(masm, StackFrame::MANUAL);
409  __ EnterExitFrame(false, kApiStackSpace);
410 
411  DCHECK(api_function_address != a0 && scratch != a0);
412  // a0 = FunctionCallbackInfo&
413  // Arguments is after the return address.
414  __ Daddu(a0, sp, Operand(1 * kPointerSize));
415  // FunctionCallbackInfo::implicit_args_
416  __ Sd(scratch, MemOperand(a0, 0 * kPointerSize));
417  // FunctionCallbackInfo::values_
418  __ Daddu(kScratchReg, scratch,
419  Operand((FCA::kArgsLength - 1 + argc()) * kPointerSize));
420  __ Sd(kScratchReg, MemOperand(a0, 1 * kPointerSize));
421  // FunctionCallbackInfo::length_ = argc
422  // Stored as int field, 32-bit integers within struct on stack always left
423  // justified by n64 ABI.
424  __ li(kScratchReg, Operand(argc()));
425  __ Sw(kScratchReg, MemOperand(a0, 2 * kPointerSize));
426 
427  ExternalReference thunk_ref = ExternalReference::invoke_function_callback();
428 
429  AllowExternalCallThatCantCauseGC scope(masm);
430  // Stores return the first js argument.
431  int return_value_offset = 2 + FCA::kReturnValueOffset;
432  MemOperand return_value_operand(fp, return_value_offset * kPointerSize);
433  const int stack_space = argc() + FCA::kArgsLength + 1;
434  // TODO(adamk): Why are we clobbering this immediately?
435  const int32_t stack_space_offset = kInvalidStackOffset;
436  CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, stack_space,
437  stack_space_offset, return_value_operand);
438 }
439 
440 
441 void CallApiGetterStub::Generate(MacroAssembler* masm) {
442  // Build v8::PropertyCallbackInfo::args_ array on the stack and push property
443  // name below the exit frame to make GC aware of them.
444  STATIC_ASSERT(PropertyCallbackArguments::kShouldThrowOnErrorIndex == 0);
445  STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 1);
446  STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 2);
447  STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 3);
448  STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 4);
449  STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 5);
450  STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 6);
451  STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 7);
452 
453  Register receiver = ApiGetterDescriptor::ReceiverRegister();
454  Register holder = ApiGetterDescriptor::HolderRegister();
455  Register callback = ApiGetterDescriptor::CallbackRegister();
456  Register scratch = a4;
457  DCHECK(!AreAliased(receiver, holder, callback, scratch));
458 
459  Register api_function_address = a2;
460 
461  // Here and below +1 is for name() pushed after the args_ array.
462  typedef PropertyCallbackArguments PCA;
463  __ Dsubu(sp, sp, (PCA::kArgsLength + 1) * kPointerSize);
464  __ Sd(receiver, MemOperand(sp, (PCA::kThisIndex + 1) * kPointerSize));
465  __ Ld(scratch, FieldMemOperand(callback, AccessorInfo::kDataOffset));
466  __ Sd(scratch, MemOperand(sp, (PCA::kDataIndex + 1) * kPointerSize));
467  __ LoadRoot(scratch, RootIndex::kUndefinedValue);
468  __ Sd(scratch, MemOperand(sp, (PCA::kReturnValueOffset + 1) * kPointerSize));
469  __ Sd(scratch, MemOperand(sp, (PCA::kReturnValueDefaultValueIndex + 1) *
470  kPointerSize));
471  __ li(scratch, ExternalReference::isolate_address(isolate()));
472  __ Sd(scratch, MemOperand(sp, (PCA::kIsolateIndex + 1) * kPointerSize));
473  __ Sd(holder, MemOperand(sp, (PCA::kHolderIndex + 1) * kPointerSize));
474  // should_throw_on_error -> false
475  DCHECK_NULL(Smi::kZero);
476  __ Sd(zero_reg,
477  MemOperand(sp, (PCA::kShouldThrowOnErrorIndex + 1) * kPointerSize));
478  __ Ld(scratch, FieldMemOperand(callback, AccessorInfo::kNameOffset));
479  __ Sd(scratch, MemOperand(sp, 0 * kPointerSize));
480 
481  // v8::PropertyCallbackInfo::args_ array and name handle.
482  const int kStackUnwindSpace = PropertyCallbackArguments::kArgsLength + 1;
483 
484  // Load address of v8::PropertyAccessorInfo::args_ array and name handle.
485  __ mov(a0, sp); // a0 = Handle<Name>
486  __ Daddu(a1, a0, Operand(1 * kPointerSize)); // a1 = v8::PCI::args_
487 
488  const int kApiStackSpace = 1;
489  FrameScope frame_scope(masm, StackFrame::MANUAL);
490  __ EnterExitFrame(false, kApiStackSpace);
491 
492  // Create v8::PropertyCallbackInfo object on the stack and initialize
493  // it's args_ field.
494  __ Sd(a1, MemOperand(sp, 1 * kPointerSize));
495  __ Daddu(a1, sp, Operand(1 * kPointerSize));
496  // a1 = v8::PropertyCallbackInfo&
497 
498  ExternalReference thunk_ref =
499  ExternalReference::invoke_accessor_getter_callback();
500 
501  __ Ld(scratch, FieldMemOperand(callback, AccessorInfo::kJsGetterOffset));
502  __ Ld(api_function_address,
503  FieldMemOperand(scratch, Foreign::kForeignAddressOffset));
504 
505  // +3 is to skip prolog, return address and name handle.
506  MemOperand return_value_operand(
507  fp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
508  CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
509  kStackUnwindSpace, kInvalidStackOffset,
510  return_value_operand);
511 }
512 
513 #undef __
514 
515 } // namespace internal
516 } // namespace v8
517 
518 #endif // V8_TARGET_ARCH_MIPS64
Definition: libplatform.h:13