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.
Generate(MacroAssembler * masm)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
MaybeCallEntryHookDelayed(TurboAssembler * tasm,Zone * zone)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
MaybeCallEntryHook(MacroAssembler * masm)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
Generate(MacroAssembler * masm)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
Generate(MacroAssembler * masm)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
GenerateCall(MacroAssembler * masm,Register target)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
AddressOffset(ExternalReference ref0,ExternalReference ref1)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.
CallApiFunctionAndReturn(MacroAssembler * masm,Register function_address,ExternalReference thunk_ref,int stack_space,int spill_offset,MemOperand return_value_operand)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
Generate(MacroAssembler * masm)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
Generate(MacroAssembler * masm)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