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