V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
code-stubs-arm64.cc
1 // Copyright 2013 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_ARM64
6 
7 #include "src/api-arguments.h"
8 #include "src/arm64/assembler-arm64-inl.h"
9 #include "src/arm64/macro-assembler-arm64-inl.h"
10 #include "src/bootstrapper.h"
11 #include "src/code-stubs.h"
12 #include "src/counters.h"
13 #include "src/frame-constants.h"
14 #include "src/frames.h"
15 #include "src/ic/ic.h"
16 #include "src/ic/stub-cache.h"
17 #include "src/isolate.h"
18 #include "src/macro-assembler.h"
19 #include "src/objects/api-callbacks.h"
20 #include "src/objects/regexp-match-info.h"
21 #include "src/regexp/jsregexp.h"
22 #include "src/regexp/regexp-macro-assembler.h"
23 #include "src/runtime/runtime.h"
24 
25 #include "src/arm64/code-stubs-arm64.h" // Cannot be the first include.
26 
27 namespace v8 {
28 namespace internal {
29 
30 #define __ ACCESS_MASM(masm)
31 
32 // This is the entry point from C++. 5 arguments are provided in x0-x4.
33 // See use of the JSEntryFunction for example in src/execution.cc.
34 // Input:
35 // x0: code entry.
36 // x1: function.
37 // x2: receiver.
38 // x3: argc.
39 // x4: argv.
40 // Output:
41 // x0: result.
42 void JSEntryStub::Generate(MacroAssembler* masm) {
43  Label invoke, handler_entry, exit;
44 
45  Register code_entry = x0;
46 
47  {
48  NoRootArrayScope no_root_array(masm);
49 
50  // Enable instruction instrumentation. This only works on the simulator, and
51  // will have no effect on the model or real hardware.
52  __ EnableInstrumentation();
53 
54  __ PushCalleeSavedRegisters();
55 
56  // Set up the reserved register for 0.0.
57  __ Fmov(fp_zero, 0.0);
58 
59  // Initialize the root array register
60  __ InitializeRootRegister();
61  }
62 
63  // Build an entry frame (see layout below).
64  StackFrame::Type marker = type();
65  int64_t bad_frame_pointer = -1L; // Bad frame pointer to fail if it is used.
66  __ Mov(x13, bad_frame_pointer);
67  __ Mov(x12, StackFrame::TypeToMarker(marker));
68  __ Mov(x11, ExternalReference::Create(IsolateAddressId::kCEntryFPAddress,
69  isolate()));
70  __ Ldr(x10, MemOperand(x11));
71 
72  __ Push(x13, x12, xzr, x10);
73  // Set up fp.
74  __ Sub(fp, sp, EntryFrameConstants::kCallerFPOffset);
75 
76  // Push the JS entry frame marker. Also set js_entry_sp if this is the
77  // outermost JS call.
78  Label non_outermost_js, done;
79  ExternalReference js_entry_sp =
80  ExternalReference::Create(IsolateAddressId::kJSEntrySPAddress, isolate());
81  __ Mov(x10, js_entry_sp);
82  __ Ldr(x11, MemOperand(x10));
83 
84  // Select between the inner and outermost frame marker, based on the JS entry
85  // sp. We assert that the inner marker is zero, so we can use xzr to save a
86  // move instruction.
87  DCHECK_EQ(StackFrame::INNER_JSENTRY_FRAME, 0);
88  __ Cmp(x11, 0); // If x11 is zero, this is the outermost frame.
89  __ Csel(x12, xzr, StackFrame::OUTERMOST_JSENTRY_FRAME, ne);
90  __ B(ne, &done);
91  __ Str(fp, MemOperand(x10));
92 
93  __ Bind(&done);
94  __ Push(x12, padreg);
95 
96  // The frame set up looks like this:
97  // sp[0] : padding.
98  // sp[1] : JS entry frame marker.
99  // sp[2] : C entry FP.
100  // sp[3] : stack frame marker.
101  // sp[4] : stack frame marker.
102  // sp[5] : bad frame pointer 0xFFF...FF <- fp points here.
103 
104  // Jump to a faked try block that does the invoke, with a faked catch
105  // block that sets the pending exception.
106  __ B(&invoke);
107 
108  // Prevent the constant pool from being emitted between the record of the
109  // handler_entry position and the first instruction of the sequence here.
110  // There is no risk because Assembler::Emit() emits the instruction before
111  // checking for constant pool emission, but we do not want to depend on
112  // that.
113  {
114  Assembler::BlockPoolsScope block_pools(masm);
115  __ bind(&handler_entry);
116  handler_offset_ = handler_entry.pos();
117  // Caught exception: Store result (exception) in the pending exception
118  // field in the JSEnv and return a failure sentinel. Coming in here the
119  // fp will be invalid because the PushTryHandler below sets it to 0 to
120  // signal the existence of the JSEntry frame.
121  __ Mov(x10, Operand(ExternalReference::Create(
122  IsolateAddressId::kPendingExceptionAddress, isolate())));
123  }
124  __ Str(code_entry, MemOperand(x10));
125  __ LoadRoot(x0, RootIndex::kException);
126  __ B(&exit);
127 
128  // Invoke: Link this frame into the handler chain.
129  __ Bind(&invoke);
130 
131  // Push new stack handler.
132  static_assert(StackHandlerConstants::kSize == 2 * kPointerSize,
133  "Unexpected offset for StackHandlerConstants::kSize");
134  static_assert(StackHandlerConstants::kNextOffset == 0 * kPointerSize,
135  "Unexpected offset for StackHandlerConstants::kNextOffset");
136 
137  // Link the current handler as the next handler.
138  __ Mov(x11, ExternalReference::Create(IsolateAddressId::kHandlerAddress,
139  isolate()));
140  __ Ldr(x10, MemOperand(x11));
141  __ Push(padreg, x10);
142 
143  // Set this new handler as the current one.
144  {
145  UseScratchRegisterScope temps(masm);
146  Register scratch = temps.AcquireX();
147  __ Mov(scratch, sp);
148  __ Str(scratch, MemOperand(x11));
149  }
150 
151  // If an exception not caught by another handler occurs, this handler
152  // returns control to the code after the B(&invoke) above, which
153  // restores all callee-saved registers (including cp and fp) to their
154  // saved values before returning a failure to C.
155 
156  // Invoke the function by calling through the JS entry trampoline builtin.
157  // Notice that we cannot store a reference to the trampoline code directly in
158  // this stub, because runtime stubs are not traversed when doing GC.
159 
160  // Expected registers by Builtins::JSEntryTrampoline
161  // x0: code entry.
162  // x1: function.
163  // x2: receiver.
164  // x3: argc.
165  // x4: argv.
166  __ Call(EntryTrampoline(), RelocInfo::CODE_TARGET);
167 
168  // Pop the stack handler and unlink this frame from the handler chain.
169  static_assert(StackHandlerConstants::kNextOffset == 0 * kPointerSize,
170  "Unexpected offset for StackHandlerConstants::kNextOffset");
171  __ Pop(x10, padreg);
172  __ Mov(x11, ExternalReference::Create(IsolateAddressId::kHandlerAddress,
173  isolate()));
174  __ Drop(StackHandlerConstants::kSlotCount - 2);
175  __ Str(x10, MemOperand(x11));
176 
177  __ Bind(&exit);
178  // x0 holds the result.
179  // The stack pointer points to the top of the entry frame pushed on entry from
180  // C++ (at the beginning of this stub):
181  // sp[0] : padding.
182  // sp[1] : JS entry frame marker.
183  // sp[2] : C entry FP.
184  // sp[3] : stack frame marker.
185  // sp[4] : stack frame marker.
186  // sp[5] : bad frame pointer 0xFFF...FF <- fp points here.
187 
188  // Check if the current stack frame is marked as the outermost JS frame.
189  Label non_outermost_js_2;
190  {
191  Register c_entry_fp = x11;
192  __ PeekPair(x10, c_entry_fp, 1 * kPointerSize);
193  __ Cmp(x10, StackFrame::OUTERMOST_JSENTRY_FRAME);
194  __ B(ne, &non_outermost_js_2);
195  __ Mov(x12, js_entry_sp);
196  __ Str(xzr, MemOperand(x12));
197  __ Bind(&non_outermost_js_2);
198 
199  // Restore the top frame descriptors from the stack.
200  __ Mov(x12, ExternalReference::Create(IsolateAddressId::kCEntryFPAddress,
201  isolate()));
202  __ Str(c_entry_fp, MemOperand(x12));
203  }
204 
205  // Reset the stack to the callee saved registers.
206  static_assert(EntryFrameConstants::kFixedFrameSize % (2 * kPointerSize) == 0,
207  "Size of entry frame is not a multiple of 16 bytes");
208  __ Drop(EntryFrameConstants::kFixedFrameSize / kPointerSize);
209  // Restore the callee-saved registers and return.
210  __ PopCalleeSavedRegisters();
211  __ Ret();
212 }
213 
214 void DirectCEntryStub::Generate(MacroAssembler* masm) {
215  // Put return address on the stack (accessible to GC through exit frame pc).
216  __ Poke(lr, 0);
217  // Call the C++ function.
218  __ Blr(x10);
219  // Return to calling code.
220  __ Peek(lr, 0);
221  __ AssertFPCRState();
222  __ Ret();
223 }
224 
225 void DirectCEntryStub::GenerateCall(MacroAssembler* masm,
226  Register target) {
227  // Branch to the stub.
228  __ Mov(x10, target);
229  __ Call(GetCode(), RelocInfo::CODE_TARGET);
230 }
231 
232 // The number of register that CallApiFunctionAndReturn will need to save on
233 // the stack. The space for these registers need to be allocated in the
234 // ExitFrame before calling CallApiFunctionAndReturn.
235 static const int kCallApiFunctionSpillSpace = 4;
236 
237 
238 static int AddressOffset(ExternalReference ref0, ExternalReference ref1) {
239  return static_cast<int>(ref0.address() - ref1.address());
240 }
241 
242 // Calls an API function. Allocates HandleScope, extracts returned value
243 // from handle and propagates exceptions.
244 // 'stack_space' is the space to be unwound on exit (includes the call JS
245 // arguments space and the additional space allocated for the fast call).
246 // 'spill_offset' is the offset from the stack pointer where
247 // CallApiFunctionAndReturn can spill registers.
248 static void CallApiFunctionAndReturn(MacroAssembler* masm,
249  Register function_address,
250  ExternalReference thunk_ref,
251  int stack_space, int spill_offset,
252  MemOperand return_value_operand) {
253  ASM_LOCATION("CallApiFunctionAndReturn");
254  Isolate* isolate = masm->isolate();
255  ExternalReference next_address =
256  ExternalReference::handle_scope_next_address(isolate);
257  const int kNextOffset = 0;
258  const int kLimitOffset = AddressOffset(
259  ExternalReference::handle_scope_limit_address(isolate), next_address);
260  const int kLevelOffset = AddressOffset(
261  ExternalReference::handle_scope_level_address(isolate), next_address);
262 
263  DCHECK(function_address.is(x1) || function_address.is(x2));
264 
265  Label profiler_disabled;
266  Label end_profiler_check;
267  __ Mov(x10, ExternalReference::is_profiling_address(isolate));
268  __ Ldrb(w10, MemOperand(x10));
269  __ Cbz(w10, &profiler_disabled);
270  __ Mov(x3, thunk_ref);
271  __ B(&end_profiler_check);
272 
273  __ Bind(&profiler_disabled);
274  __ Mov(x3, function_address);
275  __ Bind(&end_profiler_check);
276 
277  // Save the callee-save registers we are going to use.
278  // TODO(all): Is this necessary? ARM doesn't do it.
279  STATIC_ASSERT(kCallApiFunctionSpillSpace == 4);
280  __ Poke(x19, (spill_offset + 0) * kXRegSize);
281  __ Poke(x20, (spill_offset + 1) * kXRegSize);
282  __ Poke(x21, (spill_offset + 2) * kXRegSize);
283  __ Poke(x22, (spill_offset + 3) * kXRegSize);
284 
285  // Allocate HandleScope in callee-save registers.
286  // We will need to restore the HandleScope after the call to the API function,
287  // by allocating it in callee-save registers they will be preserved by C code.
288  Register handle_scope_base = x22;
289  Register next_address_reg = x19;
290  Register limit_reg = x20;
291  Register level_reg = w21;
292 
293  __ Mov(handle_scope_base, next_address);
294  __ Ldr(next_address_reg, MemOperand(handle_scope_base, kNextOffset));
295  __ Ldr(limit_reg, MemOperand(handle_scope_base, kLimitOffset));
296  __ Ldr(level_reg, MemOperand(handle_scope_base, kLevelOffset));
297  __ Add(level_reg, level_reg, 1);
298  __ Str(level_reg, MemOperand(handle_scope_base, kLevelOffset));
299 
300  if (FLAG_log_timer_events) {
301  FrameScope frame(masm, StackFrame::MANUAL);
302  __ PushSafepointRegisters();
303  __ Mov(x0, ExternalReference::isolate_address(isolate));
304  __ CallCFunction(ExternalReference::log_enter_external_function(), 1);
305  __ PopSafepointRegisters();
306  }
307 
308  // Native call returns to the DirectCEntry stub which redirects to the
309  // return address pushed on stack (could have moved after GC).
310  // DirectCEntry stub itself is generated early and never moves.
311  DirectCEntryStub stub(isolate);
312  stub.GenerateCall(masm, x3);
313 
314  if (FLAG_log_timer_events) {
315  FrameScope frame(masm, StackFrame::MANUAL);
316  __ PushSafepointRegisters();
317  __ Mov(x0, ExternalReference::isolate_address(isolate));
318  __ CallCFunction(ExternalReference::log_leave_external_function(), 1);
319  __ PopSafepointRegisters();
320  }
321 
322  Label promote_scheduled_exception;
323  Label delete_allocated_handles;
324  Label leave_exit_frame;
325  Label return_value_loaded;
326 
327  // Load value from ReturnValue.
328  __ Ldr(x0, return_value_operand);
329  __ Bind(&return_value_loaded);
330  // No more valid handles (the result handle was the last one). Restore
331  // previous handle scope.
332  __ Str(next_address_reg, MemOperand(handle_scope_base, kNextOffset));
333  if (__ emit_debug_code()) {
334  __ Ldr(w1, MemOperand(handle_scope_base, kLevelOffset));
335  __ Cmp(w1, level_reg);
336  __ Check(eq, AbortReason::kUnexpectedLevelAfterReturnFromApiCall);
337  }
338  __ Sub(level_reg, level_reg, 1);
339  __ Str(level_reg, MemOperand(handle_scope_base, kLevelOffset));
340  __ Ldr(x1, MemOperand(handle_scope_base, kLimitOffset));
341  __ Cmp(limit_reg, x1);
342  __ B(ne, &delete_allocated_handles);
343 
344  // Leave the API exit frame.
345  __ Bind(&leave_exit_frame);
346  // Restore callee-saved registers.
347  __ Peek(x19, (spill_offset + 0) * kXRegSize);
348  __ Peek(x20, (spill_offset + 1) * kXRegSize);
349  __ Peek(x21, (spill_offset + 2) * kXRegSize);
350  __ Peek(x22, (spill_offset + 3) * kXRegSize);
351 
352  __ LeaveExitFrame(false, x1, x5);
353 
354  // Check if the function scheduled an exception.
355  __ Mov(x5, ExternalReference::scheduled_exception_address(isolate));
356  __ Ldr(x5, MemOperand(x5));
357  __ JumpIfNotRoot(x5, RootIndex::kTheHoleValue, &promote_scheduled_exception);
358 
359  __ DropSlots(stack_space);
360  __ Ret();
361 
362  // Re-throw by promoting a scheduled exception.
363  __ Bind(&promote_scheduled_exception);
364  __ TailCallRuntime(Runtime::kPromoteScheduledException);
365 
366  // HandleScope limit has changed. Delete allocated extensions.
367  __ Bind(&delete_allocated_handles);
368  __ Str(limit_reg, MemOperand(handle_scope_base, kLimitOffset));
369  // Save the return value in a callee-save register.
370  Register saved_result = x19;
371  __ Mov(saved_result, x0);
372  __ Mov(x0, ExternalReference::isolate_address(isolate));
373  __ CallCFunction(ExternalReference::delete_handle_scope_extensions(), 1);
374  __ Mov(x0, saved_result);
375  __ B(&leave_exit_frame);
376 }
377 
378 void CallApiCallbackStub::Generate(MacroAssembler* masm) {
379  // ----------- S t a t e -------------
380  // -- x4 : call_data
381  // -- x2 : holder
382  // -- x1 : api_function_address
383  // -- cp : context
384  // --
385  // -- sp[0] : last argument
386  // -- ...
387  // -- sp[(argc - 1) * 8] : first argument
388  // -- sp[argc * 8] : receiver
389  // -----------------------------------
390 
391  Register call_data = x4;
392  Register holder = x2;
393  Register api_function_address = x1;
394 
395  typedef FunctionCallbackArguments FCA;
396 
397  STATIC_ASSERT(FCA::kArgsLength == 6);
398  STATIC_ASSERT(FCA::kNewTargetIndex == 5);
399  STATIC_ASSERT(FCA::kDataIndex == 4);
400  STATIC_ASSERT(FCA::kReturnValueOffset == 3);
401  STATIC_ASSERT(FCA::kReturnValueDefaultValueIndex == 2);
402  STATIC_ASSERT(FCA::kIsolateIndex == 1);
403  STATIC_ASSERT(FCA::kHolderIndex == 0);
404 
405  Register undef = x7;
406  __ LoadRoot(undef, RootIndex::kUndefinedValue);
407 
408  // Push new target, call data.
409  __ Push(undef, call_data);
410 
411  Register isolate_reg = x5;
412  __ Mov(isolate_reg, ExternalReference::isolate_address(masm->isolate()));
413 
414  // FunctionCallbackArguments:
415  // return value, return value default, isolate, holder.
416  __ Push(undef, undef, isolate_reg, holder);
417 
418  // Prepare arguments.
419  Register args = x6;
420  __ Mov(args, sp);
421 
422  // Allocate the v8::Arguments structure in the arguments' space, since it's
423  // not controlled by GC.
424  const int kApiStackSpace = 3;
425 
426  // Allocate space so that CallApiFunctionAndReturn can store some scratch
427  // registers on the stack.
428  const int kCallApiFunctionSpillSpace = 4;
429 
430  FrameScope frame_scope(masm, StackFrame::MANUAL);
431  __ EnterExitFrame(false, x10, kApiStackSpace + kCallApiFunctionSpillSpace);
432 
433  DCHECK(!AreAliased(x0, api_function_address));
434  // x0 = FunctionCallbackInfo&
435  // Arguments is after the return address.
436  __ SlotAddress(x0, 1);
437  // FunctionCallbackInfo::implicit_args_ and FunctionCallbackInfo::values_
438  __ Add(x10, args, Operand((FCA::kArgsLength - 1 + argc()) * kPointerSize));
439  __ Stp(args, x10, MemOperand(x0, 0 * kPointerSize));
440  // FunctionCallbackInfo::length_ = argc
441  __ Mov(x10, argc());
442  __ Str(x10, MemOperand(x0, 2 * kPointerSize));
443 
444  ExternalReference thunk_ref = ExternalReference::invoke_function_callback();
445 
446  AllowExternalCallThatCantCauseGC scope(masm);
447  // Stores return the first js argument
448  int return_value_offset = 2 + FCA::kReturnValueOffset;
449  MemOperand return_value_operand(fp, return_value_offset * kPointerSize);
450  // The number of arguments might be odd, but will be padded when calling the
451  // stub. We do not round up stack_space to account for odd argc here, this
452  // will be done in CallApiFunctionAndReturn.
453  const int stack_space = (argc() + 1) + FCA::kArgsLength;
454 
455  // The current frame needs to be aligned.
456  DCHECK_EQ((stack_space - (argc() + 1)) % 2, 0);
457  const int spill_offset = 1 + kApiStackSpace;
458  CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, stack_space,
459  spill_offset, return_value_operand);
460 }
461 
462 
463 void CallApiGetterStub::Generate(MacroAssembler* masm) {
464  STATIC_ASSERT(PropertyCallbackArguments::kShouldThrowOnErrorIndex == 0);
465  STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 1);
466  STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 2);
467  STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 3);
468  STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 4);
469  STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 5);
470  STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 6);
471  STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 7);
472 
473  Register receiver = ApiGetterDescriptor::ReceiverRegister();
474  Register holder = ApiGetterDescriptor::HolderRegister();
475  Register callback = ApiGetterDescriptor::CallbackRegister();
476  Register data = x4;
477  Register undef = x5;
478  Register isolate_address = x6;
479  Register name = x7;
480  DCHECK(!AreAliased(receiver, holder, callback, data, undef, isolate_address,
481  name));
482 
483  __ Ldr(data, FieldMemOperand(callback, AccessorInfo::kDataOffset));
484  __ LoadRoot(undef, RootIndex::kUndefinedValue);
485  __ Mov(isolate_address, ExternalReference::isolate_address(isolate()));
486  __ Ldr(name, FieldMemOperand(callback, AccessorInfo::kNameOffset));
487 
488  // PropertyCallbackArguments:
489  // receiver, data, return value, return value default, isolate, holder,
490  // should_throw_on_error
491  // These are followed by the property name, which is also pushed below the
492  // exit frame to make the GC aware of it.
493  __ Push(receiver, data, undef, undef, isolate_address, holder, xzr, name);
494 
495  // v8::PropertyCallbackInfo::args_ array and name handle.
496  static const int kStackUnwindSpace =
497  PropertyCallbackArguments::kArgsLength + 1;
498  static_assert(kStackUnwindSpace % 2 == 0,
499  "slots must be a multiple of 2 for stack pointer alignment");
500 
501  // Load address of v8::PropertyAccessorInfo::args_ array and name handle.
502  __ Mov(x0, sp); // x0 = Handle<Name>
503  __ Add(x1, x0, 1 * kPointerSize); // x1 = v8::PCI::args_
504 
505  const int kApiStackSpace = 1;
506 
507  // Allocate space so that CallApiFunctionAndReturn can store some scratch
508  // registers on the stack.
509  const int kCallApiFunctionSpillSpace = 4;
510 
511  FrameScope frame_scope(masm, StackFrame::MANUAL);
512  __ EnterExitFrame(false, x10, kApiStackSpace + kCallApiFunctionSpillSpace);
513 
514  // Create v8::PropertyCallbackInfo object on the stack and initialize
515  // it's args_ field.
516  __ Poke(x1, 1 * kPointerSize);
517  __ SlotAddress(x1, 1);
518  // x1 = v8::PropertyCallbackInfo&
519 
520  ExternalReference thunk_ref =
521  ExternalReference::invoke_accessor_getter_callback();
522 
523  Register api_function_address = x2;
524  Register js_getter = x4;
525  __ Ldr(js_getter, FieldMemOperand(callback, AccessorInfo::kJsGetterOffset));
526  __ Ldr(api_function_address,
527  FieldMemOperand(js_getter, Foreign::kForeignAddressOffset));
528 
529  const int spill_offset = 1 + kApiStackSpace;
530  // +3 is to skip prolog, return address and name handle.
531  MemOperand return_value_operand(
532  fp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
533  CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
534  kStackUnwindSpace, spill_offset,
535  return_value_operand);
536 }
537 
538 #undef __
539 
540 } // namespace internal
541 } // namespace v8
542 
543 #endif // V8_TARGET_ARCH_ARM64
Definition: libplatform.h:13