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