1// Copyright 2012 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#if V8_TARGET_ARCH_X64 6 7#include "src/api/api-arguments.h" 8#include "src/base/bits-iterator.h" 9#include "src/base/iterator.h" 10#include "src/codegen/code-factory.h" 11#include "src/codegen/interface-descriptors-inl.h" 12// For interpreter_entry_return_pc_offset. TODO(jkummerow): Drop. 13#include "src/codegen/macro-assembler-inl.h" 14#include "src/codegen/register-configuration.h" 15#include "src/codegen/x64/assembler-x64.h" 16#include "src/common/globals.h" 17#include "src/deoptimizer/deoptimizer.h" 18#include "src/execution/frame-constants.h" 19#include "src/execution/frames.h" 20#include "src/heap/heap-inl.h" 21#include "src/logging/counters.h" 22#include "src/objects/cell.h" 23#include "src/objects/code.h" 24#include "src/objects/debug-objects.h" 25#include "src/objects/foreign.h" 26#include "src/objects/heap-number.h" 27#include "src/objects/js-generator.h" 28#include "src/objects/objects-inl.h" 29#include "src/objects/smi.h" 30 31#if V8_ENABLE_WEBASSEMBLY 32#include "src/wasm/baseline/liftoff-assembler-defs.h" 33#include "src/wasm/object-access.h" 34#include "src/wasm/wasm-constants.h" 35#include "src/wasm/wasm-linkage.h" 36#include "src/wasm/wasm-objects.h" 37#endif // V8_ENABLE_WEBASSEMBLY 38 39namespace v8 { 40namespace internal { 41 42#define __ ACCESS_MASM(masm) 43 44void Builtins::Generate_Adaptor(MacroAssembler* masm, Address address) { 45 __ LoadAddress(kJavaScriptCallExtraArg1Register, 46 ExternalReference::Create(address)); 47 __ Jump(BUILTIN_CODE(masm->isolate(), AdaptorWithBuiltinExitFrame), 48 RelocInfo::CODE_TARGET); 49} 50 51static void GenerateTailCallToReturnedCode( 52 MacroAssembler* masm, Runtime::FunctionId function_id, 53 JumpMode jump_mode = JumpMode::kJump) { 54 // ----------- S t a t e ------------- 55 // -- rax : actual argument count 56 // -- rdx : new target (preserved for callee) 57 // -- rdi : target function (preserved for callee) 58 // ----------------------------------- 59 ASM_CODE_COMMENT(masm); 60 { 61 FrameScope scope(masm, StackFrame::INTERNAL); 62 // Push a copy of the target function, the new target and the actual 63 // argument count. 64 __ Push(kJavaScriptCallTargetRegister); 65 __ Push(kJavaScriptCallNewTargetRegister); 66 __ SmiTag(kJavaScriptCallArgCountRegister); 67 __ Push(kJavaScriptCallArgCountRegister); 68 // Function is also the parameter to the runtime call. 69 __ Push(kJavaScriptCallTargetRegister); 70 71 __ CallRuntime(function_id, 1); 72 __ movq(rcx, rax); 73 74 // Restore target function, new target and actual argument count. 75 __ Pop(kJavaScriptCallArgCountRegister); 76 __ SmiUntag(kJavaScriptCallArgCountRegister); 77 __ Pop(kJavaScriptCallNewTargetRegister); 78 __ Pop(kJavaScriptCallTargetRegister); 79 } 80 static_assert(kJavaScriptCallCodeStartRegister == rcx, "ABI mismatch"); 81 __ JumpCodeTObject(rcx, jump_mode); 82} 83 84namespace { 85 86enum class ArgumentsElementType { 87 kRaw, // Push arguments as they are. 88 kHandle // Dereference arguments before pushing. 89}; 90 91void Generate_PushArguments(MacroAssembler* masm, Register array, Register argc, 92 Register scratch, 93 ArgumentsElementType element_type) { 94 DCHECK(!AreAliased(array, argc, scratch, kScratchRegister)); 95 Register counter = scratch; 96 Label loop, entry; 97 __ leaq(counter, Operand(argc, -kJSArgcReceiverSlots)); 98 __ jmp(&entry); 99 __ bind(&loop); 100 Operand value(array, counter, times_system_pointer_size, 0); 101 if (element_type == ArgumentsElementType::kHandle) { 102 __ movq(kScratchRegister, value); 103 value = Operand(kScratchRegister, 0); 104 } 105 __ Push(value); 106 __ bind(&entry); 107 __ decq(counter); 108 __ j(greater_equal, &loop, Label::kNear); 109} 110 111void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { 112 // ----------- S t a t e ------------- 113 // -- rax: number of arguments 114 // -- rdi: constructor function 115 // -- rdx: new target 116 // -- rsi: context 117 // ----------------------------------- 118 119 Label stack_overflow; 120 __ StackOverflowCheck(rax, &stack_overflow, Label::kFar); 121 122 // Enter a construct frame. 123 { 124 FrameScope scope(masm, StackFrame::CONSTRUCT); 125 126 // Preserve the incoming parameters on the stack. 127 __ SmiTag(rcx, rax); 128 __ Push(rsi); 129 __ Push(rcx); 130 131 // TODO(victorgomes): When the arguments adaptor is completely removed, we 132 // should get the formal parameter count and copy the arguments in its 133 // correct position (including any undefined), instead of delaying this to 134 // InvokeFunction. 135 136 // Set up pointer to first argument (skip receiver). 137 __ leaq(rbx, Operand(rbp, StandardFrameConstants::kCallerSPOffset + 138 kSystemPointerSize)); 139 // Copy arguments to the expression stack. 140 // rbx: Pointer to start of arguments. 141 // rax: Number of arguments. 142 Generate_PushArguments(masm, rbx, rax, rcx, ArgumentsElementType::kRaw); 143 // The receiver for the builtin/api call. 144 __ PushRoot(RootIndex::kTheHoleValue); 145 146 // Call the function. 147 // rax: number of arguments (untagged) 148 // rdi: constructor function 149 // rdx: new target 150 __ InvokeFunction(rdi, rdx, rax, InvokeType::kCall); 151 152 // Restore smi-tagged arguments count from the frame. 153 __ movq(rbx, Operand(rbp, ConstructFrameConstants::kLengthOffset)); 154 155 // Leave construct frame. 156 } 157 158 // Remove caller arguments from the stack and return. 159 __ DropArguments(rbx, rcx, MacroAssembler::kCountIsSmi, 160 TurboAssembler::kCountIncludesReceiver); 161 162 __ ret(0); 163 164 __ bind(&stack_overflow); 165 { 166 FrameScope scope(masm, StackFrame::INTERNAL); 167 __ CallRuntime(Runtime::kThrowStackOverflow); 168 __ int3(); // This should be unreachable. 169 } 170} 171 172} // namespace 173 174// The construct stub for ES5 constructor functions and ES6 class constructors. 175void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { 176 // ----------- S t a t e ------------- 177 // -- rax: number of arguments (untagged) 178 // -- rdi: constructor function 179 // -- rdx: new target 180 // -- rsi: context 181 // -- sp[...]: constructor arguments 182 // ----------------------------------- 183 184 FrameScope scope(masm, StackFrame::MANUAL); 185 // Enter a construct frame. 186 __ EnterFrame(StackFrame::CONSTRUCT); 187 Label post_instantiation_deopt_entry, not_create_implicit_receiver; 188 189 // Preserve the incoming parameters on the stack. 190 __ SmiTag(rcx, rax); 191 __ Push(rsi); 192 __ Push(rcx); 193 __ Push(rdi); 194 __ PushRoot(RootIndex::kTheHoleValue); 195 __ Push(rdx); 196 197 // ----------- S t a t e ------------- 198 // -- sp[0*kSystemPointerSize]: new target 199 // -- sp[1*kSystemPointerSize]: padding 200 // -- rdi and sp[2*kSystemPointerSize]: constructor function 201 // -- sp[3*kSystemPointerSize]: argument count 202 // -- sp[4*kSystemPointerSize]: context 203 // ----------------------------------- 204 205 __ LoadTaggedPointerField( 206 rbx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); 207 __ movl(rbx, FieldOperand(rbx, SharedFunctionInfo::kFlagsOffset)); 208 __ DecodeField<SharedFunctionInfo::FunctionKindBits>(rbx); 209 __ JumpIfIsInRange( 210 rbx, static_cast<uint32_t>(FunctionKind::kDefaultDerivedConstructor), 211 static_cast<uint32_t>(FunctionKind::kDerivedConstructor), 212 ¬_create_implicit_receiver, Label::kNear); 213 214 // If not derived class constructor: Allocate the new receiver object. 215 __ IncrementCounter(masm->isolate()->counters()->constructed_objects(), 1); 216 __ Call(BUILTIN_CODE(masm->isolate(), FastNewObject), RelocInfo::CODE_TARGET); 217 __ jmp(&post_instantiation_deopt_entry, Label::kNear); 218 219 // Else: use TheHoleValue as receiver for constructor call 220 __ bind(¬_create_implicit_receiver); 221 __ LoadRoot(rax, RootIndex::kTheHoleValue); 222 223 // ----------- S t a t e ------------- 224 // -- rax implicit receiver 225 // -- Slot 4 / sp[0*kSystemPointerSize] new target 226 // -- Slot 3 / sp[1*kSystemPointerSize] padding 227 // -- Slot 2 / sp[2*kSystemPointerSize] constructor function 228 // -- Slot 1 / sp[3*kSystemPointerSize] number of arguments (tagged) 229 // -- Slot 0 / sp[4*kSystemPointerSize] context 230 // ----------------------------------- 231 // Deoptimizer enters here. 232 masm->isolate()->heap()->SetConstructStubCreateDeoptPCOffset( 233 masm->pc_offset()); 234 __ bind(&post_instantiation_deopt_entry); 235 236 // Restore new target. 237 __ Pop(rdx); 238 239 // Push the allocated receiver to the stack. 240 __ Push(rax); 241 242 // We need two copies because we may have to return the original one 243 // and the calling conventions dictate that the called function pops the 244 // receiver. The second copy is pushed after the arguments, we saved in r8 245 // since rax needs to store the number of arguments before 246 // InvokingFunction. 247 __ movq(r8, rax); 248 249 // Set up pointer to first argument (skip receiver). 250 __ leaq(rbx, Operand(rbp, StandardFrameConstants::kCallerSPOffset + 251 kSystemPointerSize)); 252 253 // Restore constructor function and argument count. 254 __ movq(rdi, Operand(rbp, ConstructFrameConstants::kConstructorOffset)); 255 __ SmiUntag(rax, Operand(rbp, ConstructFrameConstants::kLengthOffset)); 256 257 // Check if we have enough stack space to push all arguments. 258 // Argument count in rax. 259 Label stack_overflow; 260 __ StackOverflowCheck(rax, &stack_overflow); 261 262 // TODO(victorgomes): When the arguments adaptor is completely removed, we 263 // should get the formal parameter count and copy the arguments in its 264 // correct position (including any undefined), instead of delaying this to 265 // InvokeFunction. 266 267 // Copy arguments to the expression stack. 268 // rbx: Pointer to start of arguments. 269 // rax: Number of arguments. 270 Generate_PushArguments(masm, rbx, rax, rcx, ArgumentsElementType::kRaw); 271 272 // Push implicit receiver. 273 __ Push(r8); 274 275 // Call the function. 276 __ InvokeFunction(rdi, rdx, rax, InvokeType::kCall); 277 278 // ----------- S t a t e ------------- 279 // -- rax constructor result 280 // -- sp[0*kSystemPointerSize] implicit receiver 281 // -- sp[1*kSystemPointerSize] padding 282 // -- sp[2*kSystemPointerSize] constructor function 283 // -- sp[3*kSystemPointerSize] number of arguments 284 // -- sp[4*kSystemPointerSize] context 285 // ----------------------------------- 286 287 // Store offset of return address for deoptimizer. 288 masm->isolate()->heap()->SetConstructStubInvokeDeoptPCOffset( 289 masm->pc_offset()); 290 291 // If the result is an object (in the ECMA sense), we should get rid 292 // of the receiver and use the result; see ECMA-262 section 13.2.2-7 293 // on page 74. 294 Label use_receiver, do_throw, leave_and_return, check_result; 295 296 // If the result is undefined, we'll use the implicit receiver. Otherwise we 297 // do a smi check and fall through to check if the return value is a valid 298 // receiver. 299 __ JumpIfNotRoot(rax, RootIndex::kUndefinedValue, &check_result, 300 Label::kNear); 301 302 // Throw away the result of the constructor invocation and use the 303 // on-stack receiver as the result. 304 __ bind(&use_receiver); 305 __ movq(rax, Operand(rsp, 0 * kSystemPointerSize)); 306 __ JumpIfRoot(rax, RootIndex::kTheHoleValue, &do_throw, Label::kNear); 307 308 __ bind(&leave_and_return); 309 // Restore the arguments count. 310 __ movq(rbx, Operand(rbp, ConstructFrameConstants::kLengthOffset)); 311 __ LeaveFrame(StackFrame::CONSTRUCT); 312 // Remove caller arguments from the stack and return. 313 __ DropArguments(rbx, rcx, MacroAssembler::kCountIsSmi, 314 TurboAssembler::kCountIncludesReceiver); 315 __ ret(0); 316 317 // If the result is a smi, it is *not* an object in the ECMA sense. 318 __ bind(&check_result); 319 __ JumpIfSmi(rax, &use_receiver, Label::kNear); 320 321 // If the type of the result (stored in its map) is less than 322 // FIRST_JS_RECEIVER_TYPE, it is not an object in the ECMA sense. 323 STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); 324 __ CmpObjectType(rax, FIRST_JS_RECEIVER_TYPE, rcx); 325 __ j(above_equal, &leave_and_return, Label::kNear); 326 __ jmp(&use_receiver); 327 328 __ bind(&do_throw); 329 // Restore context from the frame. 330 __ movq(rsi, Operand(rbp, ConstructFrameConstants::kContextOffset)); 331 __ CallRuntime(Runtime::kThrowConstructorReturnedNonObject); 332 // We don't return here. 333 __ int3(); 334 335 __ bind(&stack_overflow); 336 // Restore the context from the frame. 337 __ movq(rsi, Operand(rbp, ConstructFrameConstants::kContextOffset)); 338 __ CallRuntime(Runtime::kThrowStackOverflow); 339 // This should be unreachable. 340 __ int3(); 341} 342 343void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) { 344 Generate_JSBuiltinsConstructStubHelper(masm); 345} 346 347void Builtins::Generate_ConstructedNonConstructable(MacroAssembler* masm) { 348 FrameScope scope(masm, StackFrame::INTERNAL); 349 __ Push(rdi); 350 __ CallRuntime(Runtime::kThrowConstructedNonConstructable); 351} 352 353namespace { 354 355// Called with the native C calling convention. The corresponding function 356// signature is either: 357// using JSEntryFunction = GeneratedCode<Address( 358// Address root_register_value, Address new_target, Address target, 359// Address receiver, intptr_t argc, Address** argv)>; 360// or 361// using JSEntryFunction = GeneratedCode<Address( 362// Address root_register_value, MicrotaskQueue* microtask_queue)>; 363void Generate_JSEntryVariant(MacroAssembler* masm, StackFrame::Type type, 364 Builtin entry_trampoline) { 365 Label invoke, handler_entry, exit; 366 Label not_outermost_js, not_outermost_js_2; 367 368 { 369 NoRootArrayScope uninitialized_root_register(masm); 370 // Set up frame. 371 __ pushq(rbp); 372 __ movq(rbp, rsp); 373 374 // Push the stack frame type. 375 __ Push(Immediate(StackFrame::TypeToMarker(type))); 376 // Reserve a slot for the context. It is filled after the root register has 377 // been set up. 378 __ AllocateStackSpace(kSystemPointerSize); 379 // Save callee-saved registers (X64/X32/Win64 calling conventions). 380 __ pushq(r12); 381 __ pushq(r13); 382 __ pushq(r14); 383 __ pushq(r15); 384#ifdef V8_TARGET_OS_WIN 385 __ pushq(rdi); // Only callee save in Win64 ABI, argument in AMD64 ABI. 386 __ pushq(rsi); // Only callee save in Win64 ABI, argument in AMD64 ABI. 387#endif 388 __ pushq(rbx); 389 390#ifdef V8_TARGET_OS_WIN 391 // On Win64 XMM6-XMM15 are callee-save. 392 __ AllocateStackSpace(EntryFrameConstants::kXMMRegistersBlockSize); 393 __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 0), xmm6); 394 __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 1), xmm7); 395 __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 2), xmm8); 396 __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 3), xmm9); 397 __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 4), xmm10); 398 __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 5), xmm11); 399 __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 6), xmm12); 400 __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 7), xmm13); 401 __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 8), xmm14); 402 __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 9), xmm15); 403 STATIC_ASSERT(EntryFrameConstants::kCalleeSaveXMMRegisters == 10); 404 STATIC_ASSERT(EntryFrameConstants::kXMMRegistersBlockSize == 405 EntryFrameConstants::kXMMRegisterSize * 406 EntryFrameConstants::kCalleeSaveXMMRegisters); 407#endif 408 409 // Initialize the root register. 410 // C calling convention. The first argument is passed in arg_reg_1. 411 __ movq(kRootRegister, arg_reg_1); 412 413#ifdef V8_COMPRESS_POINTERS_IN_SHARED_CAGE 414 // Initialize the pointer cage base register. 415 __ LoadRootRelative(kPtrComprCageBaseRegister, 416 IsolateData::cage_base_offset()); 417#endif 418 } 419 420 // Save copies of the top frame descriptor on the stack. 421 ExternalReference c_entry_fp = ExternalReference::Create( 422 IsolateAddressId::kCEntryFPAddress, masm->isolate()); 423 { 424 Operand c_entry_fp_operand = masm->ExternalReferenceAsOperand(c_entry_fp); 425 __ Push(c_entry_fp_operand); 426 427 // Clear c_entry_fp, now we've pushed its previous value to the stack. 428 // If the c_entry_fp is not already zero and we don't clear it, the 429 // SafeStackFrameIterator will assume we are executing C++ and miss the JS 430 // frames on top. 431 __ Move(c_entry_fp_operand, 0); 432 } 433 434 // Store the context address in the previously-reserved slot. 435 ExternalReference context_address = ExternalReference::Create( 436 IsolateAddressId::kContextAddress, masm->isolate()); 437 __ Load(kScratchRegister, context_address); 438 static constexpr int kOffsetToContextSlot = -2 * kSystemPointerSize; 439 __ movq(Operand(rbp, kOffsetToContextSlot), kScratchRegister); 440 441 // If this is the outermost JS call, set js_entry_sp value. 442 ExternalReference js_entry_sp = ExternalReference::Create( 443 IsolateAddressId::kJSEntrySPAddress, masm->isolate()); 444 __ Load(rax, js_entry_sp); 445 __ testq(rax, rax); 446 __ j(not_zero, ¬_outermost_js); 447 __ Push(Immediate(StackFrame::OUTERMOST_JSENTRY_FRAME)); 448 __ movq(rax, rbp); 449 __ Store(js_entry_sp, rax); 450 Label cont; 451 __ jmp(&cont); 452 __ bind(¬_outermost_js); 453 __ Push(Immediate(StackFrame::INNER_JSENTRY_FRAME)); 454 __ bind(&cont); 455 456 // Jump to a faked try block that does the invoke, with a faked catch 457 // block that sets the pending exception. 458 __ jmp(&invoke); 459 __ bind(&handler_entry); 460 461 // Store the current pc as the handler offset. It's used later to create the 462 // handler table. 463 masm->isolate()->builtins()->SetJSEntryHandlerOffset(handler_entry.pos()); 464 465 // Caught exception: Store result (exception) in the pending exception 466 // field in the JSEnv and return a failure sentinel. 467 ExternalReference pending_exception = ExternalReference::Create( 468 IsolateAddressId::kPendingExceptionAddress, masm->isolate()); 469 __ Store(pending_exception, rax); 470 __ LoadRoot(rax, RootIndex::kException); 471 __ jmp(&exit); 472 473 // Invoke: Link this frame into the handler chain. 474 __ bind(&invoke); 475 __ PushStackHandler(); 476 477 // Invoke the function by calling through JS entry trampoline builtin and 478 // pop the faked function when we return. 479 Handle<CodeT> trampoline_code = 480 masm->isolate()->builtins()->code_handle(entry_trampoline); 481 __ Call(trampoline_code, RelocInfo::CODE_TARGET); 482 483 // Unlink this frame from the handler chain. 484 __ PopStackHandler(); 485 486 __ bind(&exit); 487 // Check if the current stack frame is marked as the outermost JS frame. 488 __ Pop(rbx); 489 __ cmpq(rbx, Immediate(StackFrame::OUTERMOST_JSENTRY_FRAME)); 490 __ j(not_equal, ¬_outermost_js_2); 491 __ Move(kScratchRegister, js_entry_sp); 492 __ movq(Operand(kScratchRegister, 0), Immediate(0)); 493 __ bind(¬_outermost_js_2); 494 495 // Restore the top frame descriptor from the stack. 496 { 497 Operand c_entry_fp_operand = masm->ExternalReferenceAsOperand(c_entry_fp); 498 __ Pop(c_entry_fp_operand); 499 } 500 501 // Restore callee-saved registers (X64 conventions). 502#ifdef V8_TARGET_OS_WIN 503 // On Win64 XMM6-XMM15 are callee-save 504 __ movdqu(xmm6, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 0)); 505 __ movdqu(xmm7, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 1)); 506 __ movdqu(xmm8, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 2)); 507 __ movdqu(xmm9, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 3)); 508 __ movdqu(xmm10, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 4)); 509 __ movdqu(xmm11, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 5)); 510 __ movdqu(xmm12, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 6)); 511 __ movdqu(xmm13, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 7)); 512 __ movdqu(xmm14, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 8)); 513 __ movdqu(xmm15, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 9)); 514 __ addq(rsp, Immediate(EntryFrameConstants::kXMMRegistersBlockSize)); 515#endif 516 517 __ popq(rbx); 518#ifdef V8_TARGET_OS_WIN 519 // Callee save on in Win64 ABI, arguments/volatile in AMD64 ABI. 520 __ popq(rsi); 521 __ popq(rdi); 522#endif 523 __ popq(r15); 524 __ popq(r14); 525 __ popq(r13); 526 __ popq(r12); 527 __ addq(rsp, Immediate(2 * kSystemPointerSize)); // remove markers 528 529 // Restore frame pointer and return. 530 __ popq(rbp); 531 __ ret(0); 532} 533 534} // namespace 535 536void Builtins::Generate_JSEntry(MacroAssembler* masm) { 537 Generate_JSEntryVariant(masm, StackFrame::ENTRY, Builtin::kJSEntryTrampoline); 538} 539 540void Builtins::Generate_JSConstructEntry(MacroAssembler* masm) { 541 Generate_JSEntryVariant(masm, StackFrame::CONSTRUCT_ENTRY, 542 Builtin::kJSConstructEntryTrampoline); 543} 544 545void Builtins::Generate_JSRunMicrotasksEntry(MacroAssembler* masm) { 546 Generate_JSEntryVariant(masm, StackFrame::ENTRY, 547 Builtin::kRunMicrotasksTrampoline); 548} 549 550static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, 551 bool is_construct) { 552 // Expects six C++ function parameters. 553 // - Address root_register_value 554 // - Address new_target (tagged Object pointer) 555 // - Address function (tagged JSFunction pointer) 556 // - Address receiver (tagged Object pointer) 557 // - intptr_t argc 558 // - Address** argv (pointer to array of tagged Object pointers) 559 // (see Handle::Invoke in execution.cc). 560 561 // Open a C++ scope for the FrameScope. 562 { 563 // Platform specific argument handling. After this, the stack contains 564 // an internal frame and the pushed function and receiver, and 565 // register rax and rbx holds the argument count and argument array, 566 // while rdi holds the function pointer, rsi the context, and rdx the 567 // new.target. 568 569 // MSVC parameters in: 570 // rcx : root_register_value 571 // rdx : new_target 572 // r8 : function 573 // r9 : receiver 574 // [rsp+0x20] : argc 575 // [rsp+0x28] : argv 576 // 577 // GCC parameters in: 578 // rdi : root_register_value 579 // rsi : new_target 580 // rdx : function 581 // rcx : receiver 582 // r8 : argc 583 // r9 : argv 584 585 __ movq(rdi, arg_reg_3); 586 __ Move(rdx, arg_reg_2); 587 // rdi : function 588 // rdx : new_target 589 590 // Clear the context before we push it when entering the internal frame. 591 __ Move(rsi, 0); 592 593 // Enter an internal frame. 594 FrameScope scope(masm, StackFrame::INTERNAL); 595 596 // Setup the context (we need to use the caller context from the isolate). 597 ExternalReference context_address = ExternalReference::Create( 598 IsolateAddressId::kContextAddress, masm->isolate()); 599 __ movq(rsi, masm->ExternalReferenceAsOperand(context_address)); 600 601 // Push the function onto the stack. 602 __ Push(rdi); 603 604#ifdef V8_TARGET_OS_WIN 605 // Load the previous frame pointer to access C arguments on stack 606 __ movq(kScratchRegister, Operand(rbp, 0)); 607 // Load the number of arguments and setup pointer to the arguments. 608 __ movq(rax, Operand(kScratchRegister, EntryFrameConstants::kArgcOffset)); 609 __ movq(rbx, Operand(kScratchRegister, EntryFrameConstants::kArgvOffset)); 610#else // V8_TARGET_OS_WIN 611 // Load the number of arguments and setup pointer to the arguments. 612 __ movq(rax, r8); 613 __ movq(rbx, r9); 614 __ movq(r9, arg_reg_4); // Temporarily saving the receiver. 615#endif // V8_TARGET_OS_WIN 616 617 // Current stack contents: 618 // [rsp + kSystemPointerSize] : Internal frame 619 // [rsp] : function 620 // Current register contents: 621 // rax : argc 622 // rbx : argv 623 // rsi : context 624 // rdi : function 625 // rdx : new.target 626 // r9 : receiver 627 628 // Check if we have enough stack space to push all arguments. 629 // Argument count in rax. 630 Label enough_stack_space, stack_overflow; 631 __ StackOverflowCheck(rax, &stack_overflow, Label::kNear); 632 __ jmp(&enough_stack_space, Label::kNear); 633 634 __ bind(&stack_overflow); 635 __ CallRuntime(Runtime::kThrowStackOverflow); 636 // This should be unreachable. 637 __ int3(); 638 639 __ bind(&enough_stack_space); 640 641 // Copy arguments to the stack. 642 // Register rbx points to array of pointers to handle locations. 643 // Push the values of these handles. 644 // rbx: Pointer to start of arguments. 645 // rax: Number of arguments. 646 Generate_PushArguments(masm, rbx, rax, rcx, ArgumentsElementType::kHandle); 647 648 // Push the receiver. 649 __ Push(r9); 650 651 // Invoke the builtin code. 652 Handle<CodeT> builtin = is_construct 653 ? BUILTIN_CODE(masm->isolate(), Construct) 654 : masm->isolate()->builtins()->Call(); 655 __ Call(builtin, RelocInfo::CODE_TARGET); 656 657 // Exit the internal frame. Notice that this also removes the empty 658 // context and the function left on the stack by the code 659 // invocation. 660 } 661 662 __ ret(0); 663} 664 665void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) { 666 Generate_JSEntryTrampolineHelper(masm, false); 667} 668 669void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { 670 Generate_JSEntryTrampolineHelper(masm, true); 671} 672 673void Builtins::Generate_RunMicrotasksTrampoline(MacroAssembler* masm) { 674 // arg_reg_2: microtask_queue 675 __ movq(RunMicrotasksDescriptor::MicrotaskQueueRegister(), arg_reg_2); 676 __ Jump(BUILTIN_CODE(masm->isolate(), RunMicrotasks), RelocInfo::CODE_TARGET); 677} 678 679static void AssertCodeTIsBaselineAllowClobber(MacroAssembler* masm, 680 Register code, Register scratch) { 681 // Verify that the code kind is baseline code via the CodeKind. 682 __ movl(scratch, FieldOperand(code, CodeT::kFlagsOffset)); 683 __ DecodeField<CodeT::KindField>(scratch); 684 __ cmpl(scratch, Immediate(static_cast<int>(CodeKind::BASELINE))); 685 __ Assert(equal, AbortReason::kExpectedBaselineData); 686} 687 688static void AssertCodeTIsBaseline(MacroAssembler* masm, Register code, 689 Register scratch) { 690 DCHECK(!AreAliased(code, scratch)); 691 return AssertCodeTIsBaselineAllowClobber(masm, code, scratch); 692} 693 694static void GetSharedFunctionInfoBytecodeOrBaseline(MacroAssembler* masm, 695 Register sfi_data, 696 Register scratch1, 697 Label* is_baseline) { 698 ASM_CODE_COMMENT(masm); 699 Label done; 700 __ LoadMap(scratch1, sfi_data); 701 702 __ CmpInstanceType(scratch1, CODET_TYPE); 703 if (FLAG_debug_code) { 704 Label not_baseline; 705 __ j(not_equal, ¬_baseline); 706 AssertCodeTIsBaseline(masm, sfi_data, scratch1); 707 __ j(equal, is_baseline); 708 __ bind(¬_baseline); 709 } else { 710 __ j(equal, is_baseline); 711 } 712 713 __ CmpInstanceType(scratch1, INTERPRETER_DATA_TYPE); 714 __ j(not_equal, &done, Label::kNear); 715 716 __ LoadTaggedPointerField( 717 sfi_data, FieldOperand(sfi_data, InterpreterData::kBytecodeArrayOffset)); 718 719 __ bind(&done); 720} 721 722// static 723void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { 724 // ----------- S t a t e ------------- 725 // -- rax : the value to pass to the generator 726 // -- rdx : the JSGeneratorObject to resume 727 // -- rsp[0] : return address 728 // ----------------------------------- 729 730 // Store input value into generator object. 731 __ StoreTaggedField( 732 FieldOperand(rdx, JSGeneratorObject::kInputOrDebugPosOffset), rax); 733 Register object = WriteBarrierDescriptor::ObjectRegister(); 734 __ Move(object, rdx); 735 __ RecordWriteField(object, JSGeneratorObject::kInputOrDebugPosOffset, rax, 736 WriteBarrierDescriptor::SlotAddressRegister(), 737 SaveFPRegsMode::kIgnore); 738 // Check that rdx is still valid, RecordWrite might have clobbered it. 739 __ AssertGeneratorObject(rdx); 740 741 Register decompr_scratch1 = COMPRESS_POINTERS_BOOL ? r8 : no_reg; 742 743 // Load suspended function and context. 744 __ LoadTaggedPointerField( 745 rdi, FieldOperand(rdx, JSGeneratorObject::kFunctionOffset)); 746 __ LoadTaggedPointerField(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); 747 748 // Flood function if we are stepping. 749 Label prepare_step_in_if_stepping, prepare_step_in_suspended_generator; 750 Label stepping_prepared; 751 ExternalReference debug_hook = 752 ExternalReference::debug_hook_on_function_call_address(masm->isolate()); 753 Operand debug_hook_operand = masm->ExternalReferenceAsOperand(debug_hook); 754 __ cmpb(debug_hook_operand, Immediate(0)); 755 __ j(not_equal, &prepare_step_in_if_stepping); 756 757 // Flood function if we need to continue stepping in the suspended generator. 758 ExternalReference debug_suspended_generator = 759 ExternalReference::debug_suspended_generator_address(masm->isolate()); 760 Operand debug_suspended_generator_operand = 761 masm->ExternalReferenceAsOperand(debug_suspended_generator); 762 __ cmpq(rdx, debug_suspended_generator_operand); 763 __ j(equal, &prepare_step_in_suspended_generator); 764 __ bind(&stepping_prepared); 765 766 // Check the stack for overflow. We are not trying to catch interruptions 767 // (i.e. debug break and preemption) here, so check the "real stack limit". 768 Label stack_overflow; 769 __ cmpq(rsp, __ StackLimitAsOperand(StackLimitKind::kRealStackLimit)); 770 __ j(below, &stack_overflow); 771 772 // Pop return address. 773 __ PopReturnAddressTo(rax); 774 775 // ----------- S t a t e ------------- 776 // -- rax : return address 777 // -- rdx : the JSGeneratorObject to resume 778 // -- rdi : generator function 779 // -- rsi : generator context 780 // ----------------------------------- 781 782 // Copy the function arguments from the generator object's register file. 783 __ LoadTaggedPointerField( 784 rcx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); 785 __ movzxwq( 786 rcx, FieldOperand(rcx, SharedFunctionInfo::kFormalParameterCountOffset)); 787 __ decq(rcx); // Exclude receiver. 788 __ LoadTaggedPointerField( 789 rbx, FieldOperand(rdx, JSGeneratorObject::kParametersAndRegistersOffset)); 790 791 { 792 Label done_loop, loop; 793 __ bind(&loop); 794 __ decq(rcx); 795 __ j(less, &done_loop, Label::kNear); 796 __ PushTaggedAnyField( 797 FieldOperand(rbx, rcx, times_tagged_size, FixedArray::kHeaderSize), 798 decompr_scratch1); 799 __ jmp(&loop); 800 __ bind(&done_loop); 801 802 // Push the receiver. 803 __ PushTaggedPointerField( 804 FieldOperand(rdx, JSGeneratorObject::kReceiverOffset), 805 decompr_scratch1); 806 } 807 808 // Underlying function needs to have bytecode available. 809 if (FLAG_debug_code) { 810 Label is_baseline, ok; 811 __ LoadTaggedPointerField( 812 rcx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); 813 __ LoadTaggedPointerField( 814 rcx, FieldOperand(rcx, SharedFunctionInfo::kFunctionDataOffset)); 815 GetSharedFunctionInfoBytecodeOrBaseline(masm, rcx, kScratchRegister, 816 &is_baseline); 817 __ CmpObjectType(rcx, BYTECODE_ARRAY_TYPE, rcx); 818 __ Assert(equal, AbortReason::kMissingBytecodeArray); 819 __ jmp(&ok); 820 821 __ bind(&is_baseline); 822 __ CmpObjectType(rcx, CODET_TYPE, rcx); 823 __ Assert(equal, AbortReason::kMissingBytecodeArray); 824 825 __ bind(&ok); 826 } 827 828 // Resume (Ignition/TurboFan) generator object. 829 { 830 __ PushReturnAddressFrom(rax); 831 __ LoadTaggedPointerField( 832 rax, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); 833 __ movzxwq(rax, FieldOperand( 834 rax, SharedFunctionInfo::kFormalParameterCountOffset)); 835 // We abuse new.target both to indicate that this is a resume call and to 836 // pass in the generator object. In ordinary calls, new.target is always 837 // undefined because generator functions are non-constructable. 838 static_assert(kJavaScriptCallCodeStartRegister == rcx, "ABI mismatch"); 839 __ LoadTaggedPointerField(rcx, FieldOperand(rdi, JSFunction::kCodeOffset)); 840 __ JumpCodeTObject(rcx); 841 } 842 843 __ bind(&prepare_step_in_if_stepping); 844 { 845 FrameScope scope(masm, StackFrame::INTERNAL); 846 __ Push(rdx); 847 __ Push(rdi); 848 // Push hole as receiver since we do not use it for stepping. 849 __ PushRoot(RootIndex::kTheHoleValue); 850 __ CallRuntime(Runtime::kDebugOnFunctionCall); 851 __ Pop(rdx); 852 __ LoadTaggedPointerField( 853 rdi, FieldOperand(rdx, JSGeneratorObject::kFunctionOffset)); 854 } 855 __ jmp(&stepping_prepared); 856 857 __ bind(&prepare_step_in_suspended_generator); 858 { 859 FrameScope scope(masm, StackFrame::INTERNAL); 860 __ Push(rdx); 861 __ CallRuntime(Runtime::kDebugPrepareStepInSuspendedGenerator); 862 __ Pop(rdx); 863 __ LoadTaggedPointerField( 864 rdi, FieldOperand(rdx, JSGeneratorObject::kFunctionOffset)); 865 } 866 __ jmp(&stepping_prepared); 867 868 __ bind(&stack_overflow); 869 { 870 FrameScope scope(masm, StackFrame::INTERNAL); 871 __ CallRuntime(Runtime::kThrowStackOverflow); 872 __ int3(); // This should be unreachable. 873 } 874} 875 876static void ReplaceClosureCodeWithOptimizedCode(MacroAssembler* masm, 877 Register optimized_code, 878 Register closure, 879 Register scratch1, 880 Register slot_address) { 881 ASM_CODE_COMMENT(masm); 882 DCHECK(!AreAliased(optimized_code, closure, scratch1, slot_address)); 883 DCHECK_EQ(closure, kJSFunctionRegister); 884 // Store the optimized code in the closure. 885 __ AssertCodeT(optimized_code); 886 __ StoreTaggedField(FieldOperand(closure, JSFunction::kCodeOffset), 887 optimized_code); 888 // Write barrier clobbers scratch1 below. 889 Register value = scratch1; 890 __ movq(value, optimized_code); 891 892 __ RecordWriteField(closure, JSFunction::kCodeOffset, value, slot_address, 893 SaveFPRegsMode::kIgnore, RememberedSetAction::kOmit, 894 SmiCheck::kOmit); 895} 896 897static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch1, 898 Register scratch2) { 899 ASM_CODE_COMMENT(masm); 900 Register params_size = scratch1; 901 // Get the size of the formal parameters (in bytes). 902 __ movq(params_size, 903 Operand(rbp, InterpreterFrameConstants::kBytecodeArrayFromFp)); 904 __ movl(params_size, 905 FieldOperand(params_size, BytecodeArray::kParameterSizeOffset)); 906 907 Register actual_params_size = scratch2; 908 // Compute the size of the actual parameters (in bytes). 909 __ movq(actual_params_size, 910 Operand(rbp, StandardFrameConstants::kArgCOffset)); 911 __ leaq(actual_params_size, 912 Operand(actual_params_size, times_system_pointer_size, 0)); 913 914 // If actual is bigger than formal, then we should use it to free up the stack 915 // arguments. 916 Label corrected_args_count; 917 __ cmpq(params_size, actual_params_size); 918 __ j(greater_equal, &corrected_args_count, Label::kNear); 919 __ movq(params_size, actual_params_size); 920 __ bind(&corrected_args_count); 921 922 // Leave the frame (also dropping the register file). 923 __ leave(); 924 925 // Drop receiver + arguments. 926 __ DropArguments(params_size, scratch2, TurboAssembler::kCountIsBytes, 927 TurboAssembler::kCountIncludesReceiver); 928} 929 930// Tail-call |function_id| if |actual_state| == |expected_state| 931static void TailCallRuntimeIfStateEquals(MacroAssembler* masm, 932 Register actual_state, 933 TieringState expected_state, 934 Runtime::FunctionId function_id) { 935 ASM_CODE_COMMENT(masm); 936 Label no_match; 937 __ Cmp(actual_state, static_cast<int>(expected_state)); 938 __ j(not_equal, &no_match); 939 GenerateTailCallToReturnedCode(masm, function_id); 940 __ bind(&no_match); 941} 942 943static void MaybeOptimizeCode(MacroAssembler* masm, Register feedback_vector, 944 Register tiering_state) { 945 // ----------- S t a t e ------------- 946 // -- rax : actual argument count 947 // -- rdx : new target (preserved for callee if needed, and caller) 948 // -- rdi : target function (preserved for callee if needed, and caller) 949 // -- feedback vector (preserved for caller if needed) 950 // -- tiering_state : a Smi containing a non-zero tiering state. 951 // ----------------------------------- 952 ASM_CODE_COMMENT(masm); 953 DCHECK(!AreAliased(feedback_vector, rdx, rdi, tiering_state)); 954 955 TailCallRuntimeIfStateEquals(masm, tiering_state, 956 TieringState::kRequestMaglev_Synchronous, 957 Runtime::kCompileMaglev_Synchronous); 958 TailCallRuntimeIfStateEquals(masm, tiering_state, 959 TieringState::kRequestMaglev_Concurrent, 960 Runtime::kCompileMaglev_Concurrent); 961 TailCallRuntimeIfStateEquals(masm, tiering_state, 962 TieringState::kRequestTurbofan_Synchronous, 963 Runtime::kCompileTurbofan_Synchronous); 964 TailCallRuntimeIfStateEquals(masm, tiering_state, 965 TieringState::kRequestTurbofan_Concurrent, 966 Runtime::kCompileTurbofan_Concurrent); 967 968 __ int3(); 969} 970 971static void TailCallOptimizedCodeSlot(MacroAssembler* masm, 972 Register optimized_code_entry, 973 Register closure, Register scratch1, 974 Register scratch2, JumpMode jump_mode) { 975 // ----------- S t a t e ------------- 976 // rax : actual argument count 977 // rdx : new target (preserved for callee if needed, and caller) 978 // rsi : current context, used for the runtime call 979 // rdi : target function (preserved for callee if needed, and caller) 980 // ----------------------------------- 981 ASM_CODE_COMMENT(masm); 982 DCHECK_EQ(closure, kJSFunctionRegister); 983 DCHECK(!AreAliased(rax, rdx, closure, rsi, optimized_code_entry, scratch1, 984 scratch2)); 985 986 Label heal_optimized_code_slot; 987 988 // If the optimized code is cleared, go to runtime to update the optimization 989 // marker field. 990 __ LoadWeakValue(optimized_code_entry, &heal_optimized_code_slot); 991 992 // Check if the optimized code is marked for deopt. If it is, call the 993 // runtime to clear it. 994 __ AssertCodeT(optimized_code_entry); 995 if (V8_EXTERNAL_CODE_SPACE_BOOL) { 996 __ testl(FieldOperand(optimized_code_entry, 997 CodeDataContainer::kKindSpecificFlagsOffset), 998 Immediate(1 << Code::kMarkedForDeoptimizationBit)); 999 } else { 1000 __ LoadTaggedPointerField( 1001 scratch1, 1002 FieldOperand(optimized_code_entry, Code::kCodeDataContainerOffset)); 1003 __ testl( 1004 FieldOperand(scratch1, CodeDataContainer::kKindSpecificFlagsOffset), 1005 Immediate(1 << Code::kMarkedForDeoptimizationBit)); 1006 } 1007 __ j(not_zero, &heal_optimized_code_slot); 1008 1009 // Optimized code is good, get it into the closure and link the closure into 1010 // the optimized functions list, then tail call the optimized code. 1011 ReplaceClosureCodeWithOptimizedCode(masm, optimized_code_entry, closure, 1012 scratch1, scratch2); 1013 static_assert(kJavaScriptCallCodeStartRegister == rcx, "ABI mismatch"); 1014 __ Move(rcx, optimized_code_entry); 1015 __ JumpCodeTObject(rcx, jump_mode); 1016 1017 // Optimized code slot contains deoptimized code or code is cleared and 1018 // optimized code marker isn't updated. Evict the code, update the marker 1019 // and re-enter the closure's code. 1020 __ bind(&heal_optimized_code_slot); 1021 GenerateTailCallToReturnedCode(masm, Runtime::kHealOptimizedCodeSlot, 1022 jump_mode); 1023} 1024 1025// Advance the current bytecode offset. This simulates what all bytecode 1026// handlers do upon completion of the underlying operation. Will bail out to a 1027// label if the bytecode (without prefix) is a return bytecode. Will not advance 1028// the bytecode offset if the current bytecode is a JumpLoop, instead just 1029// re-executing the JumpLoop to jump to the correct bytecode. 1030static void AdvanceBytecodeOffsetOrReturn(MacroAssembler* masm, 1031 Register bytecode_array, 1032 Register bytecode_offset, 1033 Register bytecode, Register scratch1, 1034 Register scratch2, Label* if_return) { 1035 ASM_CODE_COMMENT(masm); 1036 Register bytecode_size_table = scratch1; 1037 1038 // The bytecode offset value will be increased by one in wide and extra wide 1039 // cases. In the case of having a wide or extra wide JumpLoop bytecode, we 1040 // will restore the original bytecode. In order to simplify the code, we have 1041 // a backup of it. 1042 Register original_bytecode_offset = scratch2; 1043 DCHECK(!AreAliased(bytecode_array, bytecode_offset, bytecode, 1044 bytecode_size_table, original_bytecode_offset)); 1045 1046 __ movq(original_bytecode_offset, bytecode_offset); 1047 1048 __ Move(bytecode_size_table, 1049 ExternalReference::bytecode_size_table_address()); 1050 1051 // Check if the bytecode is a Wide or ExtraWide prefix bytecode. 1052 Label process_bytecode, extra_wide; 1053 STATIC_ASSERT(0 == static_cast<int>(interpreter::Bytecode::kWide)); 1054 STATIC_ASSERT(1 == static_cast<int>(interpreter::Bytecode::kExtraWide)); 1055 STATIC_ASSERT(2 == static_cast<int>(interpreter::Bytecode::kDebugBreakWide)); 1056 STATIC_ASSERT(3 == 1057 static_cast<int>(interpreter::Bytecode::kDebugBreakExtraWide)); 1058 __ cmpb(bytecode, Immediate(0x3)); 1059 __ j(above, &process_bytecode, Label::kNear); 1060 // The code to load the next bytecode is common to both wide and extra wide. 1061 // We can hoist them up here. incl has to happen before testb since it 1062 // modifies the ZF flag. 1063 __ incl(bytecode_offset); 1064 __ testb(bytecode, Immediate(0x1)); 1065 __ movzxbq(bytecode, Operand(bytecode_array, bytecode_offset, times_1, 0)); 1066 __ j(not_equal, &extra_wide, Label::kNear); 1067 1068 // Update table to the wide scaled table. 1069 __ addq(bytecode_size_table, 1070 Immediate(kByteSize * interpreter::Bytecodes::kBytecodeCount)); 1071 __ jmp(&process_bytecode, Label::kNear); 1072 1073 __ bind(&extra_wide); 1074 // Update table to the extra wide scaled table. 1075 __ addq(bytecode_size_table, 1076 Immediate(2 * kByteSize * interpreter::Bytecodes::kBytecodeCount)); 1077 1078 __ bind(&process_bytecode); 1079 1080// Bailout to the return label if this is a return bytecode. 1081#define JUMP_IF_EQUAL(NAME) \ 1082 __ cmpb(bytecode, \ 1083 Immediate(static_cast<int>(interpreter::Bytecode::k##NAME))); \ 1084 __ j(equal, if_return, Label::kFar); 1085 RETURN_BYTECODE_LIST(JUMP_IF_EQUAL) 1086#undef JUMP_IF_EQUAL 1087 1088 // If this is a JumpLoop, re-execute it to perform the jump to the beginning 1089 // of the loop. 1090 Label end, not_jump_loop; 1091 __ cmpb(bytecode, 1092 Immediate(static_cast<int>(interpreter::Bytecode::kJumpLoop))); 1093 __ j(not_equal, ¬_jump_loop, Label::kNear); 1094 // We need to restore the original bytecode_offset since we might have 1095 // increased it to skip the wide / extra-wide prefix bytecode. 1096 __ movq(bytecode_offset, original_bytecode_offset); 1097 __ jmp(&end, Label::kNear); 1098 1099 __ bind(¬_jump_loop); 1100 // Otherwise, load the size of the current bytecode and advance the offset. 1101 __ movzxbl(kScratchRegister, 1102 Operand(bytecode_size_table, bytecode, times_1, 0)); 1103 __ addl(bytecode_offset, kScratchRegister); 1104 1105 __ bind(&end); 1106} 1107 1108// Read off the optimization state in the feedback vector and check if there 1109// is optimized code or a tiering state that needs to be processed. 1110static void LoadTieringStateAndJumpIfNeedsProcessing( 1111 MacroAssembler* masm, Register optimization_state, Register feedback_vector, 1112 Label* has_optimized_code_or_state) { 1113 ASM_CODE_COMMENT(masm); 1114 __ movl(optimization_state, 1115 FieldOperand(feedback_vector, FeedbackVector::kFlagsOffset)); 1116 __ testl( 1117 optimization_state, 1118 Immediate( 1119 FeedbackVector::kHasOptimizedCodeOrTieringStateIsAnyRequestMask)); 1120 __ j(not_zero, has_optimized_code_or_state); 1121} 1122 1123static void MaybeOptimizeCodeOrTailCallOptimizedCodeSlot( 1124 MacroAssembler* masm, Register optimization_state, Register feedback_vector, 1125 Register closure, JumpMode jump_mode = JumpMode::kJump) { 1126 ASM_CODE_COMMENT(masm); 1127 DCHECK(!AreAliased(optimization_state, feedback_vector, closure)); 1128 Label maybe_has_optimized_code; 1129 __ testl(optimization_state, 1130 Immediate(FeedbackVector::kTieringStateIsAnyRequestMask)); 1131 __ j(zero, &maybe_has_optimized_code); 1132 1133 Register tiering_state = optimization_state; 1134 __ DecodeField<FeedbackVector::TieringStateBits>(tiering_state); 1135 MaybeOptimizeCode(masm, feedback_vector, tiering_state); 1136 1137 __ bind(&maybe_has_optimized_code); 1138 Register optimized_code_entry = optimization_state; 1139 __ LoadAnyTaggedField( 1140 optimized_code_entry, 1141 FieldOperand(feedback_vector, FeedbackVector::kMaybeOptimizedCodeOffset)); 1142 TailCallOptimizedCodeSlot(masm, optimized_code_entry, closure, r9, 1143 WriteBarrierDescriptor::SlotAddressRegister(), 1144 jump_mode); 1145} 1146 1147namespace { 1148 1149void ResetBytecodeAgeAndOsrState(MacroAssembler* masm, 1150 Register bytecode_array) { 1151 // Reset the bytecode age and OSR state (optimized to a single write). 1152 static_assert(BytecodeArray::kOsrStateAndBytecodeAgeAreContiguous32Bits); 1153 STATIC_ASSERT(BytecodeArray::kNoAgeBytecodeAge == 0); 1154 __ movl(FieldOperand(bytecode_array, 1155 BytecodeArray::kOsrUrgencyAndInstallTargetOffset), 1156 Immediate(0)); 1157} 1158 1159} // namespace 1160 1161// Generate code for entering a JS function with the interpreter. 1162// On entry to the function the receiver and arguments have been pushed on the 1163// stack left to right. 1164// 1165// The live registers are: 1166// o rax: actual argument count 1167// o rdi: the JS function object being called 1168// o rdx: the incoming new target or generator object 1169// o rsi: our context 1170// o rbp: the caller's frame pointer 1171// o rsp: stack pointer (pointing to return address) 1172// 1173// The function builds an interpreter frame. See InterpreterFrameConstants in 1174// frame-constants.h for its layout. 1175void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { 1176 Register closure = rdi; 1177 Register feedback_vector = rbx; 1178 1179 // Get the bytecode array from the function object and load it into 1180 // kInterpreterBytecodeArrayRegister. 1181 __ LoadTaggedPointerField( 1182 kScratchRegister, 1183 FieldOperand(closure, JSFunction::kSharedFunctionInfoOffset)); 1184 __ LoadTaggedPointerField( 1185 kInterpreterBytecodeArrayRegister, 1186 FieldOperand(kScratchRegister, SharedFunctionInfo::kFunctionDataOffset)); 1187 1188 Label is_baseline; 1189 GetSharedFunctionInfoBytecodeOrBaseline( 1190 masm, kInterpreterBytecodeArrayRegister, kScratchRegister, &is_baseline); 1191 1192 // The bytecode array could have been flushed from the shared function info, 1193 // if so, call into CompileLazy. 1194 Label compile_lazy; 1195 __ CmpObjectType(kInterpreterBytecodeArrayRegister, BYTECODE_ARRAY_TYPE, 1196 kScratchRegister); 1197 __ j(not_equal, &compile_lazy); 1198 1199 // Load the feedback vector from the closure. 1200 __ LoadTaggedPointerField( 1201 feedback_vector, FieldOperand(closure, JSFunction::kFeedbackCellOffset)); 1202 __ LoadTaggedPointerField(feedback_vector, 1203 FieldOperand(feedback_vector, Cell::kValueOffset)); 1204 1205 Label push_stack_frame; 1206 // Check if feedback vector is valid. If valid, check for optimized code 1207 // and update invocation count. Otherwise, setup the stack frame. 1208 __ LoadMap(rcx, feedback_vector); 1209 __ CmpInstanceType(rcx, FEEDBACK_VECTOR_TYPE); 1210 __ j(not_equal, &push_stack_frame); 1211 1212 // Check the tiering state. 1213 Label has_optimized_code_or_state; 1214 Register optimization_state = rcx; 1215 LoadTieringStateAndJumpIfNeedsProcessing( 1216 masm, optimization_state, feedback_vector, &has_optimized_code_or_state); 1217 1218 Label not_optimized; 1219 __ bind(¬_optimized); 1220 1221 // Increment invocation count for the function. 1222 __ incl( 1223 FieldOperand(feedback_vector, FeedbackVector::kInvocationCountOffset)); 1224 1225 // Open a frame scope to indicate that there is a frame on the stack. The 1226 // MANUAL indicates that the scope shouldn't actually generate code to set up 1227 // the frame (that is done below). 1228 __ bind(&push_stack_frame); 1229 FrameScope frame_scope(masm, StackFrame::MANUAL); 1230 __ pushq(rbp); // Caller's frame pointer. 1231 __ movq(rbp, rsp); 1232 __ Push(kContextRegister); // Callee's context. 1233 __ Push(kJavaScriptCallTargetRegister); // Callee's JS function. 1234 __ Push(kJavaScriptCallArgCountRegister); // Actual argument count. 1235 1236 ResetBytecodeAgeAndOsrState(masm, kInterpreterBytecodeArrayRegister); 1237 1238 // Load initial bytecode offset. 1239 __ Move(kInterpreterBytecodeOffsetRegister, 1240 BytecodeArray::kHeaderSize - kHeapObjectTag); 1241 1242 // Push bytecode array and Smi tagged bytecode offset. 1243 __ Push(kInterpreterBytecodeArrayRegister); 1244 __ SmiTag(rcx, kInterpreterBytecodeOffsetRegister); 1245 __ Push(rcx); 1246 1247 // Allocate the local and temporary register file on the stack. 1248 Label stack_overflow; 1249 { 1250 // Load frame size from the BytecodeArray object. 1251 __ movl(rcx, FieldOperand(kInterpreterBytecodeArrayRegister, 1252 BytecodeArray::kFrameSizeOffset)); 1253 1254 // Do a stack check to ensure we don't go over the limit. 1255 __ movq(rax, rsp); 1256 __ subq(rax, rcx); 1257 __ cmpq(rax, __ StackLimitAsOperand(StackLimitKind::kRealStackLimit)); 1258 __ j(below, &stack_overflow); 1259 1260 // If ok, push undefined as the initial value for all register file entries. 1261 Label loop_header; 1262 Label loop_check; 1263 __ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kUndefinedValue); 1264 __ j(always, &loop_check, Label::kNear); 1265 __ bind(&loop_header); 1266 // TODO(rmcilroy): Consider doing more than one push per loop iteration. 1267 __ Push(kInterpreterAccumulatorRegister); 1268 // Continue loop if not done. 1269 __ bind(&loop_check); 1270 __ subq(rcx, Immediate(kSystemPointerSize)); 1271 __ j(greater_equal, &loop_header, Label::kNear); 1272 } 1273 1274 // If the bytecode array has a valid incoming new target or generator object 1275 // register, initialize it with incoming value which was passed in rdx. 1276 Label no_incoming_new_target_or_generator_register; 1277 __ movsxlq( 1278 rcx, 1279 FieldOperand(kInterpreterBytecodeArrayRegister, 1280 BytecodeArray::kIncomingNewTargetOrGeneratorRegisterOffset)); 1281 __ testl(rcx, rcx); 1282 __ j(zero, &no_incoming_new_target_or_generator_register, Label::kNear); 1283 __ movq(Operand(rbp, rcx, times_system_pointer_size, 0), rdx); 1284 __ bind(&no_incoming_new_target_or_generator_register); 1285 1286 // Perform interrupt stack check. 1287 // TODO(solanes): Merge with the real stack limit check above. 1288 Label stack_check_interrupt, after_stack_check_interrupt; 1289 __ cmpq(rsp, __ StackLimitAsOperand(StackLimitKind::kInterruptStackLimit)); 1290 __ j(below, &stack_check_interrupt); 1291 __ bind(&after_stack_check_interrupt); 1292 1293 // The accumulator is already loaded with undefined. 1294 1295 // Load the dispatch table into a register and dispatch to the bytecode 1296 // handler at the current bytecode offset. 1297 Label do_dispatch; 1298 __ bind(&do_dispatch); 1299 __ Move( 1300 kInterpreterDispatchTableRegister, 1301 ExternalReference::interpreter_dispatch_table_address(masm->isolate())); 1302 __ movzxbq(kScratchRegister, 1303 Operand(kInterpreterBytecodeArrayRegister, 1304 kInterpreterBytecodeOffsetRegister, times_1, 0)); 1305 __ movq(kJavaScriptCallCodeStartRegister, 1306 Operand(kInterpreterDispatchTableRegister, kScratchRegister, 1307 times_system_pointer_size, 0)); 1308 __ call(kJavaScriptCallCodeStartRegister); 1309 masm->isolate()->heap()->SetInterpreterEntryReturnPCOffset(masm->pc_offset()); 1310 1311 // Any returns to the entry trampoline are either due to the return bytecode 1312 // or the interpreter tail calling a builtin and then a dispatch. 1313 1314 // Get bytecode array and bytecode offset from the stack frame. 1315 __ movq(kInterpreterBytecodeArrayRegister, 1316 Operand(rbp, InterpreterFrameConstants::kBytecodeArrayFromFp)); 1317 __ SmiUntag(kInterpreterBytecodeOffsetRegister, 1318 Operand(rbp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); 1319 1320 // Either return, or advance to the next bytecode and dispatch. 1321 Label do_return; 1322 __ movzxbq(rbx, Operand(kInterpreterBytecodeArrayRegister, 1323 kInterpreterBytecodeOffsetRegister, times_1, 0)); 1324 AdvanceBytecodeOffsetOrReturn(masm, kInterpreterBytecodeArrayRegister, 1325 kInterpreterBytecodeOffsetRegister, rbx, rcx, 1326 r8, &do_return); 1327 __ jmp(&do_dispatch); 1328 1329 __ bind(&do_return); 1330 // The return value is in rax. 1331 LeaveInterpreterFrame(masm, rbx, rcx); 1332 __ ret(0); 1333 1334 __ bind(&stack_check_interrupt); 1335 // Modify the bytecode offset in the stack to be kFunctionEntryBytecodeOffset 1336 // for the call to the StackGuard. 1337 __ Move(Operand(rbp, InterpreterFrameConstants::kBytecodeOffsetFromFp), 1338 Smi::FromInt(BytecodeArray::kHeaderSize - kHeapObjectTag + 1339 kFunctionEntryBytecodeOffset)); 1340 __ CallRuntime(Runtime::kStackGuard); 1341 1342 // After the call, restore the bytecode array, bytecode offset and accumulator 1343 // registers again. Also, restore the bytecode offset in the stack to its 1344 // previous value. 1345 __ movq(kInterpreterBytecodeArrayRegister, 1346 Operand(rbp, InterpreterFrameConstants::kBytecodeArrayFromFp)); 1347 __ Move(kInterpreterBytecodeOffsetRegister, 1348 BytecodeArray::kHeaderSize - kHeapObjectTag); 1349 __ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kUndefinedValue); 1350 1351 __ SmiTag(rcx, kInterpreterBytecodeArrayRegister); 1352 __ movq(Operand(rbp, InterpreterFrameConstants::kBytecodeOffsetFromFp), rcx); 1353 1354 __ jmp(&after_stack_check_interrupt); 1355 1356 __ bind(&compile_lazy); 1357 GenerateTailCallToReturnedCode(masm, Runtime::kCompileLazy); 1358 __ int3(); // Should not return. 1359 1360 __ bind(&has_optimized_code_or_state); 1361 MaybeOptimizeCodeOrTailCallOptimizedCodeSlot(masm, optimization_state, 1362 feedback_vector, closure); 1363 1364 __ bind(&is_baseline); 1365 { 1366 // Load the feedback vector from the closure. 1367 __ LoadTaggedPointerField( 1368 feedback_vector, 1369 FieldOperand(closure, JSFunction::kFeedbackCellOffset)); 1370 __ LoadTaggedPointerField( 1371 feedback_vector, FieldOperand(feedback_vector, Cell::kValueOffset)); 1372 1373 Label install_baseline_code; 1374 // Check if feedback vector is valid. If not, call prepare for baseline to 1375 // allocate it. 1376 __ LoadMap(rcx, feedback_vector); 1377 __ CmpInstanceType(rcx, FEEDBACK_VECTOR_TYPE); 1378 __ j(not_equal, &install_baseline_code); 1379 1380 // Check the tiering state. 1381 LoadTieringStateAndJumpIfNeedsProcessing(masm, optimization_state, 1382 feedback_vector, 1383 &has_optimized_code_or_state); 1384 1385 // Load the baseline code into the closure. 1386 __ Move(rcx, kInterpreterBytecodeArrayRegister); 1387 static_assert(kJavaScriptCallCodeStartRegister == rcx, "ABI mismatch"); 1388 ReplaceClosureCodeWithOptimizedCode( 1389 masm, rcx, closure, kInterpreterBytecodeArrayRegister, 1390 WriteBarrierDescriptor::SlotAddressRegister()); 1391 __ JumpCodeTObject(rcx); 1392 1393 __ bind(&install_baseline_code); 1394 GenerateTailCallToReturnedCode(masm, Runtime::kInstallBaselineCode); 1395 } 1396 1397 __ bind(&stack_overflow); 1398 __ CallRuntime(Runtime::kThrowStackOverflow); 1399 __ int3(); // Should not return. 1400} 1401 1402static void GenerateInterpreterPushArgs(MacroAssembler* masm, Register num_args, 1403 Register start_address, 1404 Register scratch) { 1405 ASM_CODE_COMMENT(masm); 1406 // Find the argument with lowest address. 1407 __ movq(scratch, num_args); 1408 __ negq(scratch); 1409 __ leaq(start_address, 1410 Operand(start_address, scratch, times_system_pointer_size, 1411 kSystemPointerSize)); 1412 // Push the arguments. 1413 __ PushArray(start_address, num_args, scratch, 1414 TurboAssembler::PushArrayOrder::kReverse); 1415} 1416 1417// static 1418void Builtins::Generate_InterpreterPushArgsThenCallImpl( 1419 MacroAssembler* masm, ConvertReceiverMode receiver_mode, 1420 InterpreterPushArgsMode mode) { 1421 DCHECK(mode != InterpreterPushArgsMode::kArrayFunction); 1422 // ----------- S t a t e ------------- 1423 // -- rax : the number of arguments 1424 // -- rbx : the address of the first argument to be pushed. Subsequent 1425 // arguments should be consecutive above this, in the same order as 1426 // they are to be pushed onto the stack. 1427 // -- rdi : the target to call (can be any Object). 1428 // ----------------------------------- 1429 Label stack_overflow; 1430 1431 if (mode == InterpreterPushArgsMode::kWithFinalSpread) { 1432 // The spread argument should not be pushed. 1433 __ decl(rax); 1434 } 1435 1436 __ movl(rcx, rax); 1437 if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { 1438 __ decl(rcx); // Exclude receiver. 1439 } 1440 1441 // Add a stack check before pushing arguments. 1442 __ StackOverflowCheck(rcx, &stack_overflow); 1443 1444 // Pop return address to allow tail-call after pushing arguments. 1445 __ PopReturnAddressTo(kScratchRegister); 1446 1447 // rbx and rdx will be modified. 1448 GenerateInterpreterPushArgs(masm, rcx, rbx, rdx); 1449 1450 // Push "undefined" as the receiver arg if we need to. 1451 if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { 1452 __ PushRoot(RootIndex::kUndefinedValue); 1453 } 1454 1455 if (mode == InterpreterPushArgsMode::kWithFinalSpread) { 1456 // Pass the spread in the register rbx. 1457 // rbx already points to the penultime argument, the spread 1458 // is below that. 1459 __ movq(rbx, Operand(rbx, -kSystemPointerSize)); 1460 } 1461 1462 // Call the target. 1463 __ PushReturnAddressFrom(kScratchRegister); // Re-push return address. 1464 1465 if (mode == InterpreterPushArgsMode::kWithFinalSpread) { 1466 __ Jump(BUILTIN_CODE(masm->isolate(), CallWithSpread), 1467 RelocInfo::CODE_TARGET); 1468 } else { 1469 __ Jump(masm->isolate()->builtins()->Call(receiver_mode), 1470 RelocInfo::CODE_TARGET); 1471 } 1472 1473 // Throw stack overflow exception. 1474 __ bind(&stack_overflow); 1475 { 1476 __ TailCallRuntime(Runtime::kThrowStackOverflow); 1477 // This should be unreachable. 1478 __ int3(); 1479 } 1480} 1481 1482// static 1483void Builtins::Generate_InterpreterPushArgsThenConstructImpl( 1484 MacroAssembler* masm, InterpreterPushArgsMode mode) { 1485 // ----------- S t a t e ------------- 1486 // -- rax : the number of arguments 1487 // -- rdx : the new target (either the same as the constructor or 1488 // the JSFunction on which new was invoked initially) 1489 // -- rdi : the constructor to call (can be any Object) 1490 // -- rbx : the allocation site feedback if available, undefined otherwise 1491 // -- rcx : the address of the first argument to be pushed. Subsequent 1492 // arguments should be consecutive above this, in the same order as 1493 // they are to be pushed onto the stack. 1494 // ----------------------------------- 1495 Label stack_overflow; 1496 1497 // Add a stack check before pushing arguments. 1498 __ StackOverflowCheck(rax, &stack_overflow); 1499 1500 // Pop return address to allow tail-call after pushing arguments. 1501 __ PopReturnAddressTo(kScratchRegister); 1502 1503 if (mode == InterpreterPushArgsMode::kWithFinalSpread) { 1504 // The spread argument should not be pushed. 1505 __ decl(rax); 1506 } 1507 1508 // rcx and r8 will be modified. 1509 Register argc_without_receiver = r11; 1510 __ leaq(argc_without_receiver, Operand(rax, -kJSArgcReceiverSlots)); 1511 GenerateInterpreterPushArgs(masm, argc_without_receiver, rcx, r8); 1512 1513 // Push slot for the receiver to be constructed. 1514 __ Push(Immediate(0)); 1515 1516 if (mode == InterpreterPushArgsMode::kWithFinalSpread) { 1517 // Pass the spread in the register rbx. 1518 __ movq(rbx, Operand(rcx, -kSystemPointerSize)); 1519 // Push return address in preparation for the tail-call. 1520 __ PushReturnAddressFrom(kScratchRegister); 1521 } else { 1522 __ PushReturnAddressFrom(kScratchRegister); 1523 __ AssertUndefinedOrAllocationSite(rbx); 1524 } 1525 1526 if (mode == InterpreterPushArgsMode::kArrayFunction) { 1527 // Tail call to the array construct stub (still in the caller 1528 // context at this point). 1529 __ AssertFunction(rdi); 1530 // Jump to the constructor function (rax, rbx, rdx passed on). 1531 __ Jump(BUILTIN_CODE(masm->isolate(), ArrayConstructorImpl), 1532 RelocInfo::CODE_TARGET); 1533 } else if (mode == InterpreterPushArgsMode::kWithFinalSpread) { 1534 // Call the constructor (rax, rdx, rdi passed on). 1535 __ Jump(BUILTIN_CODE(masm->isolate(), ConstructWithSpread), 1536 RelocInfo::CODE_TARGET); 1537 } else { 1538 DCHECK_EQ(InterpreterPushArgsMode::kOther, mode); 1539 // Call the constructor (rax, rdx, rdi passed on). 1540 __ Jump(BUILTIN_CODE(masm->isolate(), Construct), RelocInfo::CODE_TARGET); 1541 } 1542 1543 // Throw stack overflow exception. 1544 __ bind(&stack_overflow); 1545 { 1546 __ TailCallRuntime(Runtime::kThrowStackOverflow); 1547 // This should be unreachable. 1548 __ int3(); 1549 } 1550} 1551 1552static void Generate_InterpreterEnterBytecode(MacroAssembler* masm) { 1553 // Set the return address to the correct point in the interpreter entry 1554 // trampoline. 1555 Label builtin_trampoline, trampoline_loaded; 1556 Smi interpreter_entry_return_pc_offset( 1557 masm->isolate()->heap()->interpreter_entry_return_pc_offset()); 1558 DCHECK_NE(interpreter_entry_return_pc_offset, Smi::zero()); 1559 1560 // If the SFI function_data is an InterpreterData, the function will have a 1561 // custom copy of the interpreter entry trampoline for profiling. If so, 1562 // get the custom trampoline, otherwise grab the entry address of the global 1563 // trampoline. 1564 __ movq(rbx, Operand(rbp, StandardFrameConstants::kFunctionOffset)); 1565 __ LoadTaggedPointerField( 1566 rbx, FieldOperand(rbx, JSFunction::kSharedFunctionInfoOffset)); 1567 __ LoadTaggedPointerField( 1568 rbx, FieldOperand(rbx, SharedFunctionInfo::kFunctionDataOffset)); 1569 __ CmpObjectType(rbx, INTERPRETER_DATA_TYPE, kScratchRegister); 1570 __ j(not_equal, &builtin_trampoline, Label::kNear); 1571 1572 __ LoadTaggedPointerField( 1573 rbx, FieldOperand(rbx, InterpreterData::kInterpreterTrampolineOffset)); 1574 __ LoadCodeTEntry(rbx, rbx); 1575 __ jmp(&trampoline_loaded, Label::kNear); 1576 1577 __ bind(&builtin_trampoline); 1578 // TODO(jgruber): Replace this by a lookup in the builtin entry table. 1579 __ movq(rbx, 1580 __ ExternalReferenceAsOperand( 1581 ExternalReference:: 1582 address_of_interpreter_entry_trampoline_instruction_start( 1583 masm->isolate()), 1584 kScratchRegister)); 1585 1586 __ bind(&trampoline_loaded); 1587 __ addq(rbx, Immediate(interpreter_entry_return_pc_offset.value())); 1588 __ Push(rbx); 1589 1590 // Initialize dispatch table register. 1591 __ Move( 1592 kInterpreterDispatchTableRegister, 1593 ExternalReference::interpreter_dispatch_table_address(masm->isolate())); 1594 1595 // Get the bytecode array pointer from the frame. 1596 __ movq(kInterpreterBytecodeArrayRegister, 1597 Operand(rbp, InterpreterFrameConstants::kBytecodeArrayFromFp)); 1598 1599 if (FLAG_debug_code) { 1600 // Check function data field is actually a BytecodeArray object. 1601 __ AssertNotSmi(kInterpreterBytecodeArrayRegister); 1602 __ CmpObjectType(kInterpreterBytecodeArrayRegister, BYTECODE_ARRAY_TYPE, 1603 rbx); 1604 __ Assert( 1605 equal, 1606 AbortReason::kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry); 1607 } 1608 1609 // Get the target bytecode offset from the frame. 1610 __ SmiUntag(kInterpreterBytecodeOffsetRegister, 1611 Operand(rbp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); 1612 1613 if (FLAG_debug_code) { 1614 Label okay; 1615 __ cmpq(kInterpreterBytecodeOffsetRegister, 1616 Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag)); 1617 __ j(greater_equal, &okay, Label::kNear); 1618 __ int3(); 1619 __ bind(&okay); 1620 } 1621 1622 // Dispatch to the target bytecode. 1623 __ movzxbq(kScratchRegister, 1624 Operand(kInterpreterBytecodeArrayRegister, 1625 kInterpreterBytecodeOffsetRegister, times_1, 0)); 1626 __ movq(kJavaScriptCallCodeStartRegister, 1627 Operand(kInterpreterDispatchTableRegister, kScratchRegister, 1628 times_system_pointer_size, 0)); 1629 __ jmp(kJavaScriptCallCodeStartRegister); 1630} 1631 1632void Builtins::Generate_InterpreterEnterAtNextBytecode(MacroAssembler* masm) { 1633 // Get bytecode array and bytecode offset from the stack frame. 1634 __ movq(kInterpreterBytecodeArrayRegister, 1635 Operand(rbp, InterpreterFrameConstants::kBytecodeArrayFromFp)); 1636 __ SmiUntag(kInterpreterBytecodeOffsetRegister, 1637 Operand(rbp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); 1638 1639 Label enter_bytecode, function_entry_bytecode; 1640 __ cmpq(kInterpreterBytecodeOffsetRegister, 1641 Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag + 1642 kFunctionEntryBytecodeOffset)); 1643 __ j(equal, &function_entry_bytecode); 1644 1645 // Load the current bytecode. 1646 __ movzxbq(rbx, Operand(kInterpreterBytecodeArrayRegister, 1647 kInterpreterBytecodeOffsetRegister, times_1, 0)); 1648 1649 // Advance to the next bytecode. 1650 Label if_return; 1651 AdvanceBytecodeOffsetOrReturn(masm, kInterpreterBytecodeArrayRegister, 1652 kInterpreterBytecodeOffsetRegister, rbx, rcx, 1653 r8, &if_return); 1654 1655 __ bind(&enter_bytecode); 1656 // Convert new bytecode offset to a Smi and save in the stackframe. 1657 __ SmiTag(kInterpreterBytecodeOffsetRegister); 1658 __ movq(Operand(rbp, InterpreterFrameConstants::kBytecodeOffsetFromFp), 1659 kInterpreterBytecodeOffsetRegister); 1660 1661 Generate_InterpreterEnterBytecode(masm); 1662 1663 __ bind(&function_entry_bytecode); 1664 // If the code deoptimizes during the implicit function entry stack interrupt 1665 // check, it will have a bailout ID of kFunctionEntryBytecodeOffset, which is 1666 // not a valid bytecode offset. Detect this case and advance to the first 1667 // actual bytecode. 1668 __ Move(kInterpreterBytecodeOffsetRegister, 1669 BytecodeArray::kHeaderSize - kHeapObjectTag); 1670 __ jmp(&enter_bytecode); 1671 1672 // We should never take the if_return path. 1673 __ bind(&if_return); 1674 __ Abort(AbortReason::kInvalidBytecodeAdvance); 1675} 1676 1677void Builtins::Generate_InterpreterEnterAtBytecode(MacroAssembler* masm) { 1678 Generate_InterpreterEnterBytecode(masm); 1679} 1680 1681// static 1682void Builtins::Generate_BaselineOutOfLinePrologue(MacroAssembler* masm) { 1683 Register feedback_vector = r8; 1684 Register optimization_state = rcx; 1685 Register return_address = r15; 1686 1687#ifdef DEBUG 1688 for (auto reg : BaselineOutOfLinePrologueDescriptor::registers()) { 1689 DCHECK( 1690 !AreAliased(feedback_vector, optimization_state, return_address, reg)); 1691 } 1692#endif 1693 1694 auto descriptor = 1695 Builtins::CallInterfaceDescriptorFor(Builtin::kBaselineOutOfLinePrologue); 1696 Register closure = descriptor.GetRegisterParameter( 1697 BaselineOutOfLinePrologueDescriptor::kClosure); 1698 // Load the feedback vector from the closure. 1699 __ LoadTaggedPointerField( 1700 feedback_vector, FieldOperand(closure, JSFunction::kFeedbackCellOffset)); 1701 __ LoadTaggedPointerField(feedback_vector, 1702 FieldOperand(feedback_vector, Cell::kValueOffset)); 1703 if (FLAG_debug_code) { 1704 __ CmpObjectType(feedback_vector, FEEDBACK_VECTOR_TYPE, kScratchRegister); 1705 __ Assert(equal, AbortReason::kExpectedFeedbackVector); 1706 } 1707 1708 // Check the tiering state. 1709 Label has_optimized_code_or_state; 1710 LoadTieringStateAndJumpIfNeedsProcessing( 1711 masm, optimization_state, feedback_vector, &has_optimized_code_or_state); 1712 1713 // Increment invocation count for the function. 1714 __ incl( 1715 FieldOperand(feedback_vector, FeedbackVector::kInvocationCountOffset)); 1716 1717 // Save the return address, so that we can push it to the end of the newly 1718 // set-up frame once we're done setting it up. 1719 __ PopReturnAddressTo(return_address); 1720 FrameScope frame_scope(masm, StackFrame::MANUAL); 1721 { 1722 ASM_CODE_COMMENT_STRING(masm, "Frame Setup"); 1723 __ EnterFrame(StackFrame::BASELINE); 1724 1725 __ Push(descriptor.GetRegisterParameter( 1726 BaselineOutOfLinePrologueDescriptor::kCalleeContext)); // Callee's 1727 // context. 1728 Register callee_js_function = descriptor.GetRegisterParameter( 1729 BaselineOutOfLinePrologueDescriptor::kClosure); 1730 DCHECK_EQ(callee_js_function, kJavaScriptCallTargetRegister); 1731 DCHECK_EQ(callee_js_function, kJSFunctionRegister); 1732 __ Push(callee_js_function); // Callee's JS function. 1733 __ Push(descriptor.GetRegisterParameter( 1734 BaselineOutOfLinePrologueDescriptor:: 1735 kJavaScriptCallArgCount)); // Actual argument 1736 // count. 1737 1738 // We'll use the bytecode for both code age/OSR resetting, and pushing 1739 // onto the frame, so load it into a register. 1740 Register bytecode_array = descriptor.GetRegisterParameter( 1741 BaselineOutOfLinePrologueDescriptor::kInterpreterBytecodeArray); 1742 ResetBytecodeAgeAndOsrState(masm, bytecode_array); 1743 __ Push(bytecode_array); 1744 1745 // Baseline code frames store the feedback vector where interpreter would 1746 // store the bytecode offset. 1747 __ Push(feedback_vector); 1748 } 1749 1750 Register new_target = descriptor.GetRegisterParameter( 1751 BaselineOutOfLinePrologueDescriptor::kJavaScriptCallNewTarget); 1752 1753 Label call_stack_guard; 1754 Register frame_size = descriptor.GetRegisterParameter( 1755 BaselineOutOfLinePrologueDescriptor::kStackFrameSize); 1756 { 1757 ASM_CODE_COMMENT_STRING(masm, " Stack/interrupt check"); 1758 // Stack check. This folds the checks for both the interrupt stack limit 1759 // check and the real stack limit into one by just checking for the 1760 // interrupt limit. The interrupt limit is either equal to the real stack 1761 // limit or tighter. By ensuring we have space until that limit after 1762 // building the frame we can quickly precheck both at once. 1763 // 1764 // TODO(v8:11429): Backport this folded check to the 1765 // InterpreterEntryTrampoline. 1766 __ Move(kScratchRegister, rsp); 1767 DCHECK_NE(frame_size, new_target); 1768 __ subq(kScratchRegister, frame_size); 1769 __ cmpq(kScratchRegister, 1770 __ StackLimitAsOperand(StackLimitKind::kInterruptStackLimit)); 1771 __ j(below, &call_stack_guard); 1772 } 1773 1774 // Push the return address back onto the stack for return. 1775 __ PushReturnAddressFrom(return_address); 1776 // Return to caller pushed pc, without any frame teardown. 1777 __ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kUndefinedValue); 1778 __ Ret(); 1779 1780 __ bind(&has_optimized_code_or_state); 1781 { 1782 ASM_CODE_COMMENT_STRING(masm, "Optimized marker check"); 1783 // Drop the return address, rebalancing the return stack buffer by using 1784 // JumpMode::kPushAndReturn. We can't leave the slot and overwrite it on 1785 // return since we may do a runtime call along the way that requires the 1786 // stack to only contain valid frames. 1787 __ Drop(1); 1788 MaybeOptimizeCodeOrTailCallOptimizedCodeSlot(masm, optimization_state, 1789 feedback_vector, closure, 1790 JumpMode::kPushAndReturn); 1791 __ Trap(); 1792 } 1793 1794 __ bind(&call_stack_guard); 1795 { 1796 ASM_CODE_COMMENT_STRING(masm, "Stack/interrupt call"); 1797 { 1798 // Push the baseline code return address now, as if it had been pushed by 1799 // the call to this builtin. 1800 __ PushReturnAddressFrom(return_address); 1801 FrameScope inner_frame_scope(masm, StackFrame::INTERNAL); 1802 // Save incoming new target or generator 1803 __ Push(new_target); 1804 __ SmiTag(frame_size); 1805 __ Push(frame_size); 1806 __ CallRuntime(Runtime::kStackGuardWithGap, 1); 1807 __ Pop(new_target); 1808 } 1809 1810 // Return to caller pushed pc, without any frame teardown. 1811 __ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kUndefinedValue); 1812 __ Ret(); 1813 } 1814} 1815 1816namespace { 1817void Generate_ContinueToBuiltinHelper(MacroAssembler* masm, 1818 bool java_script_builtin, 1819 bool with_result) { 1820 ASM_CODE_COMMENT(masm); 1821 const RegisterConfiguration* config(RegisterConfiguration::Default()); 1822 int allocatable_register_count = config->num_allocatable_general_registers(); 1823 if (with_result) { 1824 if (java_script_builtin) { 1825 // kScratchRegister is not included in the allocateable registers. 1826 __ movq(kScratchRegister, rax); 1827 } else { 1828 // Overwrite the hole inserted by the deoptimizer with the return value 1829 // from the LAZY deopt point. 1830 __ movq( 1831 Operand(rsp, config->num_allocatable_general_registers() * 1832 kSystemPointerSize + 1833 BuiltinContinuationFrameConstants::kFixedFrameSize), 1834 rax); 1835 } 1836 } 1837 for (int i = allocatable_register_count - 1; i >= 0; --i) { 1838 int code = config->GetAllocatableGeneralCode(i); 1839 __ popq(Register::from_code(code)); 1840 if (java_script_builtin && code == kJavaScriptCallArgCountRegister.code()) { 1841 __ SmiUntag(Register::from_code(code)); 1842 } 1843 } 1844 if (with_result && java_script_builtin) { 1845 // Overwrite the hole inserted by the deoptimizer with the return value from 1846 // the LAZY deopt point. rax contains the arguments count, the return value 1847 // from LAZY is always the last argument. 1848 __ movq(Operand(rsp, rax, times_system_pointer_size, 1849 BuiltinContinuationFrameConstants::kFixedFrameSize - 1850 kJSArgcReceiverSlots * kSystemPointerSize), 1851 kScratchRegister); 1852 } 1853 __ movq( 1854 rbp, 1855 Operand(rsp, BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp)); 1856 const int offsetToPC = 1857 BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp - 1858 kSystemPointerSize; 1859 __ popq(Operand(rsp, offsetToPC)); 1860 __ Drop(offsetToPC / kSystemPointerSize); 1861 1862 // Replace the builtin index Smi on the stack with the instruction start 1863 // address of the builtin from the builtins table, and then Ret to this 1864 // address 1865 __ movq(kScratchRegister, Operand(rsp, 0)); 1866 __ movq(kScratchRegister, 1867 __ EntryFromBuiltinIndexAsOperand(kScratchRegister)); 1868 __ movq(Operand(rsp, 0), kScratchRegister); 1869 1870 __ Ret(); 1871} 1872} // namespace 1873 1874void Builtins::Generate_ContinueToCodeStubBuiltin(MacroAssembler* masm) { 1875 Generate_ContinueToBuiltinHelper(masm, false, false); 1876} 1877 1878void Builtins::Generate_ContinueToCodeStubBuiltinWithResult( 1879 MacroAssembler* masm) { 1880 Generate_ContinueToBuiltinHelper(masm, false, true); 1881} 1882 1883void Builtins::Generate_ContinueToJavaScriptBuiltin(MacroAssembler* masm) { 1884 Generate_ContinueToBuiltinHelper(masm, true, false); 1885} 1886 1887void Builtins::Generate_ContinueToJavaScriptBuiltinWithResult( 1888 MacroAssembler* masm) { 1889 Generate_ContinueToBuiltinHelper(masm, true, true); 1890} 1891 1892void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) { 1893 // Enter an internal frame. 1894 { 1895 FrameScope scope(masm, StackFrame::INTERNAL); 1896 __ CallRuntime(Runtime::kNotifyDeoptimized); 1897 // Tear down internal frame. 1898 } 1899 1900 DCHECK_EQ(kInterpreterAccumulatorRegister.code(), rax.code()); 1901 __ movq(rax, Operand(rsp, kPCOnStackSize)); 1902 __ ret(1 * kSystemPointerSize); // Remove rax. 1903} 1904 1905// static 1906void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) { 1907 // ----------- S t a t e ------------- 1908 // -- rax : argc 1909 // -- rsp[0] : return address 1910 // -- rsp[1] : receiver 1911 // -- rsp[2] : thisArg 1912 // -- rsp[3] : argArray 1913 // ----------------------------------- 1914 1915 // 1. Load receiver into rdi, argArray into rbx (if present), remove all 1916 // arguments from the stack (including the receiver), and push thisArg (if 1917 // present) instead. 1918 { 1919 Label no_arg_array, no_this_arg; 1920 StackArgumentsAccessor args(rax); 1921 __ LoadRoot(rdx, RootIndex::kUndefinedValue); 1922 __ movq(rbx, rdx); 1923 __ movq(rdi, args[0]); 1924 __ cmpq(rax, Immediate(JSParameterCount(0))); 1925 __ j(equal, &no_this_arg, Label::kNear); 1926 { 1927 __ movq(rdx, args[1]); 1928 __ cmpq(rax, Immediate(JSParameterCount(1))); 1929 __ j(equal, &no_arg_array, Label::kNear); 1930 __ movq(rbx, args[2]); 1931 __ bind(&no_arg_array); 1932 } 1933 __ bind(&no_this_arg); 1934 __ DropArgumentsAndPushNewReceiver(rax, rdx, rcx, 1935 TurboAssembler::kCountIsInteger, 1936 TurboAssembler::kCountIncludesReceiver); 1937 } 1938 1939 // ----------- S t a t e ------------- 1940 // -- rbx : argArray 1941 // -- rdi : receiver 1942 // -- rsp[0] : return address 1943 // -- rsp[8] : thisArg 1944 // ----------------------------------- 1945 1946 // 2. We don't need to check explicitly for callable receiver here, 1947 // since that's the first thing the Call/CallWithArrayLike builtins 1948 // will do. 1949 1950 // 3. Tail call with no arguments if argArray is null or undefined. 1951 Label no_arguments; 1952 __ JumpIfRoot(rbx, RootIndex::kNullValue, &no_arguments, Label::kNear); 1953 __ JumpIfRoot(rbx, RootIndex::kUndefinedValue, &no_arguments, Label::kNear); 1954 1955 // 4a. Apply the receiver to the given argArray. 1956 __ Jump(BUILTIN_CODE(masm->isolate(), CallWithArrayLike), 1957 RelocInfo::CODE_TARGET); 1958 1959 // 4b. The argArray is either null or undefined, so we tail call without any 1960 // arguments to the receiver. Since we did not create a frame for 1961 // Function.prototype.apply() yet, we use a normal Call builtin here. 1962 __ bind(&no_arguments); 1963 { 1964 __ Move(rax, JSParameterCount(0)); 1965 __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); 1966 } 1967} 1968 1969// static 1970void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { 1971 // Stack Layout: 1972 // rsp[0] : Return address 1973 // rsp[8] : Argument 0 (receiver: callable to call) 1974 // rsp[16] : Argument 1 1975 // ... 1976 // rsp[8 * n] : Argument n-1 1977 // rsp[8 * (n + 1)] : Argument n 1978 // rax contains the number of arguments, n. 1979 1980 // 1. Get the callable to call (passed as receiver) from the stack. 1981 { 1982 StackArgumentsAccessor args(rax); 1983 __ movq(rdi, args.GetReceiverOperand()); 1984 } 1985 1986 // 2. Save the return address and drop the callable. 1987 __ PopReturnAddressTo(rbx); 1988 __ Pop(kScratchRegister); 1989 1990 // 3. Make sure we have at least one argument. 1991 { 1992 Label done; 1993 __ cmpq(rax, Immediate(JSParameterCount(0))); 1994 __ j(greater, &done, Label::kNear); 1995 __ PushRoot(RootIndex::kUndefinedValue); 1996 __ incq(rax); 1997 __ bind(&done); 1998 } 1999 2000 // 4. Push back the return address one slot down on the stack (overwriting the 2001 // original callable), making the original first argument the new receiver. 2002 __ PushReturnAddressFrom(rbx); 2003 __ decq(rax); // One fewer argument (first argument is new receiver). 2004 2005 // 5. Call the callable. 2006 // Since we did not create a frame for Function.prototype.call() yet, 2007 // we use a normal Call builtin here. 2008 __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); 2009} 2010 2011void Builtins::Generate_ReflectApply(MacroAssembler* masm) { 2012 // ----------- S t a t e ------------- 2013 // -- rax : argc 2014 // -- rsp[0] : return address 2015 // -- rsp[8] : receiver 2016 // -- rsp[16] : target (if argc >= 1) 2017 // -- rsp[24] : thisArgument (if argc >= 2) 2018 // -- rsp[32] : argumentsList (if argc == 3) 2019 // ----------------------------------- 2020 2021 // 1. Load target into rdi (if present), argumentsList into rbx (if present), 2022 // remove all arguments from the stack (including the receiver), and push 2023 // thisArgument (if present) instead. 2024 { 2025 Label done; 2026 StackArgumentsAccessor args(rax); 2027 __ LoadRoot(rdi, RootIndex::kUndefinedValue); 2028 __ movq(rdx, rdi); 2029 __ movq(rbx, rdi); 2030 __ cmpq(rax, Immediate(JSParameterCount(1))); 2031 __ j(below, &done, Label::kNear); 2032 __ movq(rdi, args[1]); // target 2033 __ j(equal, &done, Label::kNear); 2034 __ movq(rdx, args[2]); // thisArgument 2035 __ cmpq(rax, Immediate(JSParameterCount(3))); 2036 __ j(below, &done, Label::kNear); 2037 __ movq(rbx, args[3]); // argumentsList 2038 __ bind(&done); 2039 __ DropArgumentsAndPushNewReceiver(rax, rdx, rcx, 2040 TurboAssembler::kCountIsInteger, 2041 TurboAssembler::kCountIncludesReceiver); 2042 } 2043 2044 // ----------- S t a t e ------------- 2045 // -- rbx : argumentsList 2046 // -- rdi : target 2047 // -- rsp[0] : return address 2048 // -- rsp[8] : thisArgument 2049 // ----------------------------------- 2050 2051 // 2. We don't need to check explicitly for callable target here, 2052 // since that's the first thing the Call/CallWithArrayLike builtins 2053 // will do. 2054 2055 // 3. Apply the target to the given argumentsList. 2056 __ Jump(BUILTIN_CODE(masm->isolate(), CallWithArrayLike), 2057 RelocInfo::CODE_TARGET); 2058} 2059 2060void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { 2061 // ----------- S t a t e ------------- 2062 // -- rax : argc 2063 // -- rsp[0] : return address 2064 // -- rsp[8] : receiver 2065 // -- rsp[16] : target 2066 // -- rsp[24] : argumentsList 2067 // -- rsp[32] : new.target (optional) 2068 // ----------------------------------- 2069 2070 // 1. Load target into rdi (if present), argumentsList into rbx (if present), 2071 // new.target into rdx (if present, otherwise use target), remove all 2072 // arguments from the stack (including the receiver), and push thisArgument 2073 // (if present) instead. 2074 { 2075 Label done; 2076 StackArgumentsAccessor args(rax); 2077 __ LoadRoot(rdi, RootIndex::kUndefinedValue); 2078 __ movq(rdx, rdi); 2079 __ movq(rbx, rdi); 2080 __ cmpq(rax, Immediate(JSParameterCount(1))); 2081 __ j(below, &done, Label::kNear); 2082 __ movq(rdi, args[1]); // target 2083 __ movq(rdx, rdi); // new.target defaults to target 2084 __ j(equal, &done, Label::kNear); 2085 __ movq(rbx, args[2]); // argumentsList 2086 __ cmpq(rax, Immediate(JSParameterCount(3))); 2087 __ j(below, &done, Label::kNear); 2088 __ movq(rdx, args[3]); // new.target 2089 __ bind(&done); 2090 __ DropArgumentsAndPushNewReceiver( 2091 rax, masm->RootAsOperand(RootIndex::kUndefinedValue), rcx, 2092 TurboAssembler::kCountIsInteger, 2093 TurboAssembler::kCountIncludesReceiver); 2094 } 2095 2096 // ----------- S t a t e ------------- 2097 // -- rbx : argumentsList 2098 // -- rdx : new.target 2099 // -- rdi : target 2100 // -- rsp[0] : return address 2101 // -- rsp[8] : receiver (undefined) 2102 // ----------------------------------- 2103 2104 // 2. We don't need to check explicitly for constructor target here, 2105 // since that's the first thing the Construct/ConstructWithArrayLike 2106 // builtins will do. 2107 2108 // 3. We don't need to check explicitly for constructor new.target here, 2109 // since that's the second thing the Construct/ConstructWithArrayLike 2110 // builtins will do. 2111 2112 // 4. Construct the target with the given new.target and argumentsList. 2113 __ Jump(BUILTIN_CODE(masm->isolate(), ConstructWithArrayLike), 2114 RelocInfo::CODE_TARGET); 2115} 2116 2117namespace { 2118 2119// Allocate new stack space for |count| arguments and shift all existing 2120// arguments already on the stack. |pointer_to_new_space_out| points to the 2121// first free slot on the stack to copy additional arguments to and 2122// |argc_in_out| is updated to include |count|. 2123void Generate_AllocateSpaceAndShiftExistingArguments( 2124 MacroAssembler* masm, Register count, Register argc_in_out, 2125 Register pointer_to_new_space_out, Register scratch1, Register scratch2) { 2126 DCHECK(!AreAliased(count, argc_in_out, pointer_to_new_space_out, scratch1, 2127 scratch2, kScratchRegister)); 2128 // Use pointer_to_new_space_out as scratch until we set it to the correct 2129 // value at the end. 2130 Register old_rsp = pointer_to_new_space_out; 2131 Register new_space = kScratchRegister; 2132 __ movq(old_rsp, rsp); 2133 2134 __ leaq(new_space, Operand(count, times_system_pointer_size, 0)); 2135 __ AllocateStackSpace(new_space); 2136 2137 Register copy_count = argc_in_out; 2138 Register current = scratch2; 2139 Register value = kScratchRegister; 2140 2141 Label loop, entry; 2142 __ Move(current, 0); 2143 __ jmp(&entry); 2144 __ bind(&loop); 2145 __ movq(value, Operand(old_rsp, current, times_system_pointer_size, 0)); 2146 __ movq(Operand(rsp, current, times_system_pointer_size, 0), value); 2147 __ incq(current); 2148 __ bind(&entry); 2149 __ cmpq(current, copy_count); 2150 __ j(less_equal, &loop, Label::kNear); 2151 2152 // Point to the next free slot above the shifted arguments (copy_count + 1 2153 // slot for the return address). 2154 __ leaq( 2155 pointer_to_new_space_out, 2156 Operand(rsp, copy_count, times_system_pointer_size, kSystemPointerSize)); 2157 // We use addl instead of addq here because we can omit REX.W, saving 1 byte. 2158 // We are especially constrained here because we are close to reaching the 2159 // limit for a near jump to the stackoverflow label, so every byte counts. 2160 __ addl(argc_in_out, count); // Update total number of arguments. 2161} 2162 2163} // namespace 2164 2165// static 2166// TODO(v8:11615): Observe Code::kMaxArguments in CallOrConstructVarargs 2167void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, 2168 Handle<CodeT> code) { 2169 // ----------- S t a t e ------------- 2170 // -- rdi : target 2171 // -- rax : number of parameters on the stack 2172 // -- rbx : arguments list (a FixedArray) 2173 // -- rcx : len (number of elements to push from args) 2174 // -- rdx : new.target (for [[Construct]]) 2175 // -- rsp[0] : return address 2176 // ----------------------------------- 2177 2178 if (FLAG_debug_code) { 2179 // Allow rbx to be a FixedArray, or a FixedDoubleArray if rcx == 0. 2180 Label ok, fail; 2181 __ AssertNotSmi(rbx); 2182 Register map = r9; 2183 __ LoadMap(map, rbx); 2184 __ CmpInstanceType(map, FIXED_ARRAY_TYPE); 2185 __ j(equal, &ok); 2186 __ CmpInstanceType(map, FIXED_DOUBLE_ARRAY_TYPE); 2187 __ j(not_equal, &fail); 2188 __ Cmp(rcx, 0); 2189 __ j(equal, &ok); 2190 // Fall through. 2191 __ bind(&fail); 2192 __ Abort(AbortReason::kOperandIsNotAFixedArray); 2193 2194 __ bind(&ok); 2195 } 2196 2197 Label stack_overflow; 2198 __ StackOverflowCheck(rcx, &stack_overflow, Label::kNear); 2199 2200 // Push additional arguments onto the stack. 2201 // Move the arguments already in the stack, 2202 // including the receiver and the return address. 2203 // rcx: Number of arguments to make room for. 2204 // rax: Number of arguments already on the stack. 2205 // r8: Points to first free slot on the stack after arguments were shifted. 2206 Generate_AllocateSpaceAndShiftExistingArguments(masm, rcx, rax, r8, r9, r12); 2207 // Copy the additional arguments onto the stack. 2208 { 2209 Register value = r12; 2210 Register src = rbx, dest = r8, num = rcx, current = r9; 2211 __ Move(current, 0); 2212 Label done, push, loop; 2213 __ bind(&loop); 2214 __ cmpl(current, num); 2215 __ j(equal, &done, Label::kNear); 2216 // Turn the hole into undefined as we go. 2217 __ LoadAnyTaggedField(value, FieldOperand(src, current, times_tagged_size, 2218 FixedArray::kHeaderSize)); 2219 __ CompareRoot(value, RootIndex::kTheHoleValue); 2220 __ j(not_equal, &push, Label::kNear); 2221 __ LoadRoot(value, RootIndex::kUndefinedValue); 2222 __ bind(&push); 2223 __ movq(Operand(dest, current, times_system_pointer_size, 0), value); 2224 __ incl(current); 2225 __ jmp(&loop); 2226 __ bind(&done); 2227 } 2228 2229 // Tail-call to the actual Call or Construct builtin. 2230 __ Jump(code, RelocInfo::CODE_TARGET); 2231 2232 __ bind(&stack_overflow); 2233 __ TailCallRuntime(Runtime::kThrowStackOverflow); 2234} 2235 2236// static 2237void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm, 2238 CallOrConstructMode mode, 2239 Handle<CodeT> code) { 2240 // ----------- S t a t e ------------- 2241 // -- rax : the number of arguments 2242 // -- rdx : the new target (for [[Construct]] calls) 2243 // -- rdi : the target to call (can be any Object) 2244 // -- rcx : start index (to support rest parameters) 2245 // ----------------------------------- 2246 2247 // Check if new.target has a [[Construct]] internal method. 2248 if (mode == CallOrConstructMode::kConstruct) { 2249 Label new_target_constructor, new_target_not_constructor; 2250 __ JumpIfSmi(rdx, &new_target_not_constructor, Label::kNear); 2251 __ LoadMap(rbx, rdx); 2252 __ testb(FieldOperand(rbx, Map::kBitFieldOffset), 2253 Immediate(Map::Bits1::IsConstructorBit::kMask)); 2254 __ j(not_zero, &new_target_constructor, Label::kNear); 2255 __ bind(&new_target_not_constructor); 2256 { 2257 FrameScope scope(masm, StackFrame::MANUAL); 2258 __ EnterFrame(StackFrame::INTERNAL); 2259 __ Push(rdx); 2260 __ CallRuntime(Runtime::kThrowNotConstructor); 2261 } 2262 __ bind(&new_target_constructor); 2263 } 2264 2265 Label stack_done, stack_overflow; 2266 __ movq(r8, Operand(rbp, StandardFrameConstants::kArgCOffset)); 2267 __ decq(r8); // Exclude receiver. 2268 __ subl(r8, rcx); 2269 __ j(less_equal, &stack_done); 2270 { 2271 // ----------- S t a t e ------------- 2272 // -- rax : the number of arguments already in the stack 2273 // -- rbp : point to the caller stack frame 2274 // -- rcx : start index (to support rest parameters) 2275 // -- rdx : the new target (for [[Construct]] calls) 2276 // -- rdi : the target to call (can be any Object) 2277 // -- r8 : number of arguments to copy, i.e. arguments count - start index 2278 // ----------------------------------- 2279 2280 // Check for stack overflow. 2281 __ StackOverflowCheck(r8, &stack_overflow, Label::kNear); 2282 2283 // Forward the arguments from the caller frame. 2284 // Move the arguments already in the stack, 2285 // including the receiver and the return address. 2286 // r8: Number of arguments to make room for. 2287 // rax: Number of arguments already on the stack. 2288 // r9: Points to first free slot on the stack after arguments were shifted. 2289 Generate_AllocateSpaceAndShiftExistingArguments(masm, r8, rax, r9, r12, 2290 r15); 2291 2292 // Point to the first argument to copy (skipping receiver). 2293 __ leaq(rcx, Operand(rcx, times_system_pointer_size, 2294 CommonFrameConstants::kFixedFrameSizeAboveFp + 2295 kSystemPointerSize)); 2296 __ addq(rcx, rbp); 2297 2298 // Copy the additional caller arguments onto the stack. 2299 // TODO(victorgomes): Consider using forward order as potentially more cache 2300 // friendly. 2301 { 2302 Register src = rcx, dest = r9, num = r8; 2303 Label loop; 2304 __ bind(&loop); 2305 __ decq(num); 2306 __ movq(kScratchRegister, 2307 Operand(src, num, times_system_pointer_size, 0)); 2308 __ movq(Operand(dest, num, times_system_pointer_size, 0), 2309 kScratchRegister); 2310 __ j(not_zero, &loop); 2311 } 2312 } 2313 __ jmp(&stack_done, Label::kNear); 2314 __ bind(&stack_overflow); 2315 __ TailCallRuntime(Runtime::kThrowStackOverflow); 2316 __ bind(&stack_done); 2317 2318 // Tail-call to the {code} handler. 2319 __ Jump(code, RelocInfo::CODE_TARGET); 2320} 2321 2322// static 2323void Builtins::Generate_CallFunction(MacroAssembler* masm, 2324 ConvertReceiverMode mode) { 2325 // ----------- S t a t e ------------- 2326 // -- rax : the number of arguments 2327 // -- rdi : the function to call (checked to be a JSFunction) 2328 // ----------------------------------- 2329 2330 StackArgumentsAccessor args(rax); 2331 __ AssertCallableFunction(rdi); 2332 2333 __ LoadTaggedPointerField( 2334 rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); 2335 // ----------- S t a t e ------------- 2336 // -- rax : the number of arguments 2337 // -- rdx : the shared function info. 2338 // -- rdi : the function to call (checked to be a JSFunction) 2339 // ----------------------------------- 2340 2341 // Enter the context of the function; ToObject has to run in the function 2342 // context, and we also need to take the global proxy from the function 2343 // context in case of conversion. 2344 __ LoadTaggedPointerField(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); 2345 // We need to convert the receiver for non-native sloppy mode functions. 2346 Label done_convert; 2347 __ testl(FieldOperand(rdx, SharedFunctionInfo::kFlagsOffset), 2348 Immediate(SharedFunctionInfo::IsNativeBit::kMask | 2349 SharedFunctionInfo::IsStrictBit::kMask)); 2350 __ j(not_zero, &done_convert); 2351 { 2352 // ----------- S t a t e ------------- 2353 // -- rax : the number of arguments 2354 // -- rdx : the shared function info. 2355 // -- rdi : the function to call (checked to be a JSFunction) 2356 // -- rsi : the function context. 2357 // ----------------------------------- 2358 2359 if (mode == ConvertReceiverMode::kNullOrUndefined) { 2360 // Patch receiver to global proxy. 2361 __ LoadGlobalProxy(rcx); 2362 } else { 2363 Label convert_to_object, convert_receiver; 2364 __ movq(rcx, args.GetReceiverOperand()); 2365 __ JumpIfSmi(rcx, &convert_to_object, Label::kNear); 2366 STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); 2367 __ CmpObjectType(rcx, FIRST_JS_RECEIVER_TYPE, rbx); 2368 __ j(above_equal, &done_convert); 2369 if (mode != ConvertReceiverMode::kNotNullOrUndefined) { 2370 Label convert_global_proxy; 2371 __ JumpIfRoot(rcx, RootIndex::kUndefinedValue, &convert_global_proxy, 2372 Label::kNear); 2373 __ JumpIfNotRoot(rcx, RootIndex::kNullValue, &convert_to_object, 2374 Label::kNear); 2375 __ bind(&convert_global_proxy); 2376 { 2377 // Patch receiver to global proxy. 2378 __ LoadGlobalProxy(rcx); 2379 } 2380 __ jmp(&convert_receiver); 2381 } 2382 __ bind(&convert_to_object); 2383 { 2384 // Convert receiver using ToObject. 2385 // TODO(bmeurer): Inline the allocation here to avoid building the frame 2386 // in the fast case? (fall back to AllocateInNewSpace?) 2387 FrameScope scope(masm, StackFrame::INTERNAL); 2388 __ SmiTag(rax); 2389 __ Push(rax); 2390 __ Push(rdi); 2391 __ movq(rax, rcx); 2392 __ Push(rsi); 2393 __ Call(BUILTIN_CODE(masm->isolate(), ToObject), 2394 RelocInfo::CODE_TARGET); 2395 __ Pop(rsi); 2396 __ movq(rcx, rax); 2397 __ Pop(rdi); 2398 __ Pop(rax); 2399 __ SmiUntag(rax); 2400 } 2401 __ LoadTaggedPointerField( 2402 rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); 2403 __ bind(&convert_receiver); 2404 } 2405 __ movq(args.GetReceiverOperand(), rcx); 2406 } 2407 __ bind(&done_convert); 2408 2409 // ----------- S t a t e ------------- 2410 // -- rax : the number of arguments 2411 // -- rdx : the shared function info. 2412 // -- rdi : the function to call (checked to be a JSFunction) 2413 // -- rsi : the function context. 2414 // ----------------------------------- 2415 2416 __ movzxwq( 2417 rbx, FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset)); 2418 __ InvokeFunctionCode(rdi, no_reg, rbx, rax, InvokeType::kJump); 2419} 2420 2421namespace { 2422 2423void Generate_PushBoundArguments(MacroAssembler* masm) { 2424 // ----------- S t a t e ------------- 2425 // -- rax : the number of arguments 2426 // -- rdx : new.target (only in case of [[Construct]]) 2427 // -- rdi : target (checked to be a JSBoundFunction) 2428 // ----------------------------------- 2429 2430 // Load [[BoundArguments]] into rcx and length of that into rbx. 2431 Label no_bound_arguments; 2432 __ LoadTaggedPointerField( 2433 rcx, FieldOperand(rdi, JSBoundFunction::kBoundArgumentsOffset)); 2434 __ SmiUntagField(rbx, FieldOperand(rcx, FixedArray::kLengthOffset)); 2435 __ testl(rbx, rbx); 2436 __ j(zero, &no_bound_arguments); 2437 { 2438 // ----------- S t a t e ------------- 2439 // -- rax : the number of arguments 2440 // -- rdx : new.target (only in case of [[Construct]]) 2441 // -- rdi : target (checked to be a JSBoundFunction) 2442 // -- rcx : the [[BoundArguments]] (implemented as FixedArray) 2443 // -- rbx : the number of [[BoundArguments]] (checked to be non-zero) 2444 // ----------------------------------- 2445 2446 // TODO(victor): Use Generate_StackOverflowCheck here. 2447 // Check the stack for overflow. 2448 { 2449 Label done; 2450 __ shlq(rbx, Immediate(kSystemPointerSizeLog2)); 2451 __ movq(kScratchRegister, rsp); 2452 __ subq(kScratchRegister, rbx); 2453 2454 // We are not trying to catch interruptions (i.e. debug break and 2455 // preemption) here, so check the "real stack limit". 2456 __ cmpq(kScratchRegister, 2457 __ StackLimitAsOperand(StackLimitKind::kRealStackLimit)); 2458 __ j(above_equal, &done, Label::kNear); 2459 { 2460 FrameScope scope(masm, StackFrame::MANUAL); 2461 __ EnterFrame(StackFrame::INTERNAL); 2462 __ CallRuntime(Runtime::kThrowStackOverflow); 2463 } 2464 __ bind(&done); 2465 } 2466 2467 // Save Return Address and Receiver into registers. 2468 __ Pop(r8); 2469 __ Pop(r10); 2470 2471 // Push [[BoundArguments]] to the stack. 2472 { 2473 Label loop; 2474 __ LoadTaggedPointerField( 2475 rcx, FieldOperand(rdi, JSBoundFunction::kBoundArgumentsOffset)); 2476 __ SmiUntagField(rbx, FieldOperand(rcx, FixedArray::kLengthOffset)); 2477 __ addq(rax, rbx); // Adjust effective number of arguments. 2478 __ bind(&loop); 2479 // Instead of doing decl(rbx) here subtract kTaggedSize from the header 2480 // offset in order to be able to move decl(rbx) right before the loop 2481 // condition. This is necessary in order to avoid flags corruption by 2482 // pointer decompression code. 2483 __ LoadAnyTaggedField( 2484 r12, FieldOperand(rcx, rbx, times_tagged_size, 2485 FixedArray::kHeaderSize - kTaggedSize)); 2486 __ Push(r12); 2487 __ decl(rbx); 2488 __ j(greater, &loop); 2489 } 2490 2491 // Recover Receiver and Return Address. 2492 __ Push(r10); 2493 __ Push(r8); 2494 } 2495 __ bind(&no_bound_arguments); 2496} 2497 2498} // namespace 2499 2500// static 2501void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm) { 2502 // ----------- S t a t e ------------- 2503 // -- rax : the number of arguments 2504 // -- rdi : the function to call (checked to be a JSBoundFunction) 2505 // ----------------------------------- 2506 __ AssertBoundFunction(rdi); 2507 2508 // Patch the receiver to [[BoundThis]]. 2509 StackArgumentsAccessor args(rax); 2510 __ LoadAnyTaggedField(rbx, 2511 FieldOperand(rdi, JSBoundFunction::kBoundThisOffset)); 2512 __ movq(args.GetReceiverOperand(), rbx); 2513 2514 // Push the [[BoundArguments]] onto the stack. 2515 Generate_PushBoundArguments(masm); 2516 2517 // Call the [[BoundTargetFunction]] via the Call builtin. 2518 __ LoadTaggedPointerField( 2519 rdi, FieldOperand(rdi, JSBoundFunction::kBoundTargetFunctionOffset)); 2520 __ Jump(BUILTIN_CODE(masm->isolate(), Call_ReceiverIsAny), 2521 RelocInfo::CODE_TARGET); 2522} 2523 2524// static 2525void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) { 2526 // ----------- S t a t e ------------- 2527 // -- rax : the number of arguments 2528 // -- rdi : the target to call (can be any Object) 2529 // ----------------------------------- 2530 Register argc = rax; 2531 Register target = rdi; 2532 Register map = rcx; 2533 Register instance_type = rdx; 2534 DCHECK(!AreAliased(argc, target, map, instance_type)); 2535 2536 StackArgumentsAccessor args(argc); 2537 2538 Label non_callable, class_constructor; 2539 __ JumpIfSmi(target, &non_callable); 2540 __ LoadMap(map, target); 2541 __ CmpInstanceTypeRange(map, instance_type, FIRST_CALLABLE_JS_FUNCTION_TYPE, 2542 LAST_CALLABLE_JS_FUNCTION_TYPE); 2543 __ Jump(masm->isolate()->builtins()->CallFunction(mode), 2544 RelocInfo::CODE_TARGET, below_equal); 2545 2546 __ cmpw(instance_type, Immediate(JS_BOUND_FUNCTION_TYPE)); 2547 __ Jump(BUILTIN_CODE(masm->isolate(), CallBoundFunction), 2548 RelocInfo::CODE_TARGET, equal); 2549 2550 // Check if target has a [[Call]] internal method. 2551 __ testb(FieldOperand(map, Map::kBitFieldOffset), 2552 Immediate(Map::Bits1::IsCallableBit::kMask)); 2553 __ j(zero, &non_callable, Label::kNear); 2554 2555 // Check if target is a proxy and call CallProxy external builtin 2556 __ cmpw(instance_type, Immediate(JS_PROXY_TYPE)); 2557 __ Jump(BUILTIN_CODE(masm->isolate(), CallProxy), RelocInfo::CODE_TARGET, 2558 equal); 2559 2560 // Check if target is a wrapped function and call CallWrappedFunction external 2561 // builtin 2562 __ cmpw(instance_type, Immediate(JS_WRAPPED_FUNCTION_TYPE)); 2563 __ Jump(BUILTIN_CODE(masm->isolate(), CallWrappedFunction), 2564 RelocInfo::CODE_TARGET, equal); 2565 2566 // ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList) 2567 // Check that the function is not a "classConstructor". 2568 __ cmpw(instance_type, Immediate(JS_CLASS_CONSTRUCTOR_TYPE)); 2569 __ j(equal, &class_constructor); 2570 2571 // 2. Call to something else, which might have a [[Call]] internal method (if 2572 // not we raise an exception). 2573 2574 // Overwrite the original receiver with the (original) target. 2575 __ movq(args.GetReceiverOperand(), target); 2576 // Let the "call_as_function_delegate" take care of the rest. 2577 __ LoadNativeContextSlot(target, Context::CALL_AS_FUNCTION_DELEGATE_INDEX); 2578 __ Jump(masm->isolate()->builtins()->CallFunction( 2579 ConvertReceiverMode::kNotNullOrUndefined), 2580 RelocInfo::CODE_TARGET); 2581 2582 // 3. Call to something that is not callable. 2583 __ bind(&non_callable); 2584 { 2585 FrameScope scope(masm, StackFrame::INTERNAL); 2586 __ Push(target); 2587 __ CallRuntime(Runtime::kThrowCalledNonCallable); 2588 __ Trap(); // Unreachable. 2589 } 2590 2591 // 4. The function is a "classConstructor", need to raise an exception. 2592 __ bind(&class_constructor); 2593 { 2594 FrameScope frame(masm, StackFrame::INTERNAL); 2595 __ Push(target); 2596 __ CallRuntime(Runtime::kThrowConstructorNonCallableError); 2597 __ Trap(); // Unreachable. 2598 } 2599} 2600 2601// static 2602void Builtins::Generate_ConstructFunction(MacroAssembler* masm) { 2603 // ----------- S t a t e ------------- 2604 // -- rax : the number of arguments 2605 // -- rdx : the new target (checked to be a constructor) 2606 // -- rdi : the constructor to call (checked to be a JSFunction) 2607 // ----------------------------------- 2608 __ AssertConstructor(rdi); 2609 __ AssertFunction(rdi); 2610 2611 // Calling convention for function specific ConstructStubs require 2612 // rbx to contain either an AllocationSite or undefined. 2613 __ LoadRoot(rbx, RootIndex::kUndefinedValue); 2614 2615 // Jump to JSBuiltinsConstructStub or JSConstructStubGeneric. 2616 __ LoadTaggedPointerField( 2617 rcx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); 2618 __ testl(FieldOperand(rcx, SharedFunctionInfo::kFlagsOffset), 2619 Immediate(SharedFunctionInfo::ConstructAsBuiltinBit::kMask)); 2620 __ Jump(BUILTIN_CODE(masm->isolate(), JSBuiltinsConstructStub), 2621 RelocInfo::CODE_TARGET, not_zero); 2622 2623 __ Jump(BUILTIN_CODE(masm->isolate(), JSConstructStubGeneric), 2624 RelocInfo::CODE_TARGET); 2625} 2626 2627// static 2628void Builtins::Generate_ConstructBoundFunction(MacroAssembler* masm) { 2629 // ----------- S t a t e ------------- 2630 // -- rax : the number of arguments 2631 // -- rdx : the new target (checked to be a constructor) 2632 // -- rdi : the constructor to call (checked to be a JSBoundFunction) 2633 // ----------------------------------- 2634 __ AssertConstructor(rdi); 2635 __ AssertBoundFunction(rdi); 2636 2637 // Push the [[BoundArguments]] onto the stack. 2638 Generate_PushBoundArguments(masm); 2639 2640 // Patch new.target to [[BoundTargetFunction]] if new.target equals target. 2641 { 2642 Label done; 2643 __ cmpq(rdi, rdx); 2644 __ j(not_equal, &done, Label::kNear); 2645 __ LoadTaggedPointerField( 2646 rdx, FieldOperand(rdi, JSBoundFunction::kBoundTargetFunctionOffset)); 2647 __ bind(&done); 2648 } 2649 2650 // Construct the [[BoundTargetFunction]] via the Construct builtin. 2651 __ LoadTaggedPointerField( 2652 rdi, FieldOperand(rdi, JSBoundFunction::kBoundTargetFunctionOffset)); 2653 __ Jump(BUILTIN_CODE(masm->isolate(), Construct), RelocInfo::CODE_TARGET); 2654} 2655 2656// static 2657void Builtins::Generate_Construct(MacroAssembler* masm) { 2658 // ----------- S t a t e ------------- 2659 // -- rax : the number of arguments 2660 // -- rdx : the new target (either the same as the constructor or 2661 // the JSFunction on which new was invoked initially) 2662 // -- rdi : the constructor to call (can be any Object) 2663 // ----------------------------------- 2664 Register argc = rax; 2665 Register target = rdi; 2666 Register map = rcx; 2667 Register instance_type = r8; 2668 DCHECK(!AreAliased(argc, target, map, instance_type)); 2669 2670 StackArgumentsAccessor args(argc); 2671 2672 // Check if target is a Smi. 2673 Label non_constructor; 2674 __ JumpIfSmi(target, &non_constructor); 2675 2676 // Check if target has a [[Construct]] internal method. 2677 __ LoadMap(map, target); 2678 __ testb(FieldOperand(map, Map::kBitFieldOffset), 2679 Immediate(Map::Bits1::IsConstructorBit::kMask)); 2680 __ j(zero, &non_constructor); 2681 2682 // Dispatch based on instance type. 2683 __ CmpInstanceTypeRange(map, instance_type, FIRST_JS_FUNCTION_TYPE, 2684 LAST_JS_FUNCTION_TYPE); 2685 __ Jump(BUILTIN_CODE(masm->isolate(), ConstructFunction), 2686 RelocInfo::CODE_TARGET, below_equal); 2687 2688 // Only dispatch to bound functions after checking whether they are 2689 // constructors. 2690 __ cmpw(instance_type, Immediate(JS_BOUND_FUNCTION_TYPE)); 2691 __ Jump(BUILTIN_CODE(masm->isolate(), ConstructBoundFunction), 2692 RelocInfo::CODE_TARGET, equal); 2693 2694 // Only dispatch to proxies after checking whether they are constructors. 2695 __ cmpw(instance_type, Immediate(JS_PROXY_TYPE)); 2696 __ Jump(BUILTIN_CODE(masm->isolate(), ConstructProxy), RelocInfo::CODE_TARGET, 2697 equal); 2698 2699 // Called Construct on an exotic Object with a [[Construct]] internal method. 2700 { 2701 // Overwrite the original receiver with the (original) target. 2702 __ movq(args.GetReceiverOperand(), target); 2703 // Let the "call_as_constructor_delegate" take care of the rest. 2704 __ LoadNativeContextSlot(target, 2705 Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX); 2706 __ Jump(masm->isolate()->builtins()->CallFunction(), 2707 RelocInfo::CODE_TARGET); 2708 } 2709 2710 // Called Construct on an Object that doesn't have a [[Construct]] internal 2711 // method. 2712 __ bind(&non_constructor); 2713 __ Jump(BUILTIN_CODE(masm->isolate(), ConstructedNonConstructable), 2714 RelocInfo::CODE_TARGET); 2715} 2716 2717namespace { 2718 2719void Generate_OSREntry(MacroAssembler* masm, Register entry_address) { 2720 // Overwrite the return address on the stack. 2721 __ movq(StackOperandForReturnAddress(0), entry_address); 2722 2723 // And "return" to the OSR entry point of the function. 2724 __ ret(0); 2725} 2726 2727enum class OsrSourceTier { 2728 kInterpreter, 2729 kBaseline, 2730}; 2731 2732void OnStackReplacement(MacroAssembler* masm, OsrSourceTier source) { 2733 { 2734 FrameScope scope(masm, StackFrame::INTERNAL); 2735 __ CallRuntime(Runtime::kCompileOptimizedOSR); 2736 } 2737 2738 Label jump_to_returned_code; 2739 // If the code object is null, just return to the caller. 2740 __ testq(rax, rax); 2741 __ j(not_equal, &jump_to_returned_code, Label::kNear); 2742 __ ret(0); 2743 2744 __ bind(&jump_to_returned_code); 2745 2746 if (source == OsrSourceTier::kInterpreter) { 2747 // Drop the handler frame that is be sitting on top of the actual 2748 // JavaScript frame. This is the case then OSR is triggered from bytecode. 2749 __ leave(); 2750 } 2751 2752 if (V8_EXTERNAL_CODE_SPACE_BOOL) { 2753 __ LoadCodeDataContainerCodeNonBuiltin(rax, rax); 2754 } 2755 2756 // Load deoptimization data from the code object. 2757 __ LoadTaggedPointerField( 2758 rbx, FieldOperand(rax, Code::kDeoptimizationDataOrInterpreterDataOffset)); 2759 2760 // Load the OSR entrypoint offset from the deoptimization data. 2761 __ SmiUntagField( 2762 rbx, FieldOperand(rbx, FixedArray::OffsetOfElementAt( 2763 DeoptimizationData::kOsrPcOffsetIndex))); 2764 2765 // Compute the target address = code_obj + header_size + osr_offset 2766 __ leaq(rax, FieldOperand(rax, rbx, times_1, Code::kHeaderSize)); 2767 2768 Generate_OSREntry(masm, rax); 2769} 2770 2771} // namespace 2772 2773void Builtins::Generate_InterpreterOnStackReplacement(MacroAssembler* masm) { 2774 OnStackReplacement(masm, OsrSourceTier::kInterpreter); 2775} 2776 2777void Builtins::Generate_BaselineOnStackReplacement(MacroAssembler* masm) { 2778 __ movq(kContextRegister, 2779 MemOperand(rbp, BaselineFrameConstants::kContextOffset)); 2780 OnStackReplacement(masm, OsrSourceTier::kBaseline); 2781} 2782 2783#if V8_ENABLE_WEBASSEMBLY 2784void Builtins::Generate_WasmCompileLazy(MacroAssembler* masm) { 2785 // The function index was pushed to the stack by the caller as int32. 2786 __ Pop(r15); 2787 // Convert to Smi for the runtime call. 2788 __ SmiTag(r15); 2789 2790 { 2791 HardAbortScope hard_abort(masm); // Avoid calls to Abort. 2792 FrameScope scope(masm, StackFrame::WASM_COMPILE_LAZY); 2793 2794 // Save all parameter registers (see wasm-linkage.h). They might be 2795 // overwritten in the runtime call below. We don't have any callee-saved 2796 // registers in wasm, so no need to store anything else. 2797 static_assert(WasmCompileLazyFrameConstants::kNumberOfSavedGpParamRegs == 2798 arraysize(wasm::kGpParamRegisters), 2799 "frame size mismatch"); 2800 for (Register reg : wasm::kGpParamRegisters) { 2801 __ Push(reg); 2802 } 2803 static_assert(WasmCompileLazyFrameConstants::kNumberOfSavedFpParamRegs == 2804 arraysize(wasm::kFpParamRegisters), 2805 "frame size mismatch"); 2806 __ AllocateStackSpace(kSimd128Size * arraysize(wasm::kFpParamRegisters)); 2807 int offset = 0; 2808 for (DoubleRegister reg : wasm::kFpParamRegisters) { 2809 __ movdqu(Operand(rsp, offset), reg); 2810 offset += kSimd128Size; 2811 } 2812 2813 // Push the Wasm instance for loading the jump table address after the 2814 // runtime call. 2815 __ Push(kWasmInstanceRegister); 2816 2817 // Push the Wasm instance again as an explicit argument to the runtime 2818 // function. 2819 __ Push(kWasmInstanceRegister); 2820 // Push the function index as second argument. 2821 __ Push(r15); 2822 // Initialize the JavaScript context with 0. CEntry will use it to 2823 // set the current context on the isolate. 2824 __ Move(kContextRegister, Smi::zero()); 2825 __ CallRuntime(Runtime::kWasmCompileLazy, 2); 2826 // The runtime function returns the jump table slot offset as a Smi. Use 2827 // that to compute the jump target in r15. 2828 __ Pop(kWasmInstanceRegister); 2829 __ movq(r15, MemOperand(kWasmInstanceRegister, 2830 wasm::ObjectAccess::ToTagged( 2831 WasmInstanceObject::kJumpTableStartOffset))); 2832 __ SmiUntag(kReturnRegister0); 2833 __ addq(r15, kReturnRegister0); 2834 // r15 now holds the jump table slot where we want to jump to in the end. 2835 2836 // Restore registers. 2837 for (DoubleRegister reg : base::Reversed(wasm::kFpParamRegisters)) { 2838 offset -= kSimd128Size; 2839 __ movdqu(reg, Operand(rsp, offset)); 2840 } 2841 DCHECK_EQ(0, offset); 2842 __ addq(rsp, Immediate(kSimd128Size * arraysize(wasm::kFpParamRegisters))); 2843 for (Register reg : base::Reversed(wasm::kGpParamRegisters)) { 2844 __ Pop(reg); 2845 } 2846 } 2847 2848 // Finally, jump to the jump table slot for the function. 2849 __ jmp(r15); 2850} 2851 2852void Builtins::Generate_WasmDebugBreak(MacroAssembler* masm) { 2853 HardAbortScope hard_abort(masm); // Avoid calls to Abort. 2854 { 2855 FrameScope scope(masm, StackFrame::WASM_DEBUG_BREAK); 2856 2857 // Save all parameter registers. They might hold live values, we restore 2858 // them after the runtime call. 2859 for (Register reg : 2860 base::Reversed(WasmDebugBreakFrameConstants::kPushedGpRegs)) { 2861 __ Push(reg); 2862 } 2863 2864 constexpr int kFpStackSize = 2865 kSimd128Size * WasmDebugBreakFrameConstants::kNumPushedFpRegisters; 2866 __ AllocateStackSpace(kFpStackSize); 2867 int offset = kFpStackSize; 2868 for (DoubleRegister reg : 2869 base::Reversed(WasmDebugBreakFrameConstants::kPushedFpRegs)) { 2870 offset -= kSimd128Size; 2871 __ movdqu(Operand(rsp, offset), reg); 2872 } 2873 2874 // Initialize the JavaScript context with 0. CEntry will use it to 2875 // set the current context on the isolate. 2876 __ Move(kContextRegister, Smi::zero()); 2877 __ CallRuntime(Runtime::kWasmDebugBreak, 0); 2878 2879 // Restore registers. 2880 for (DoubleRegister reg : WasmDebugBreakFrameConstants::kPushedFpRegs) { 2881 __ movdqu(reg, Operand(rsp, offset)); 2882 offset += kSimd128Size; 2883 } 2884 __ addq(rsp, Immediate(kFpStackSize)); 2885 for (Register reg : WasmDebugBreakFrameConstants::kPushedGpRegs) { 2886 __ Pop(reg); 2887 } 2888 } 2889 2890 __ ret(0); 2891} 2892 2893namespace { 2894// Helper functions for the GenericJSToWasmWrapper. 2895void PrepareForBuiltinCall(MacroAssembler* masm, MemOperand GCScanSlotPlace, 2896 const int GCScanSlotCount, Register current_param, 2897 Register param_limit, 2898 Register current_int_param_slot, 2899 Register current_float_param_slot, 2900 Register valuetypes_array_ptr, 2901 Register wasm_instance, Register function_data) { 2902 // Pushes and puts the values in order onto the stack before builtin calls for 2903 // the GenericJSToWasmWrapper. 2904 __ Move(GCScanSlotPlace, GCScanSlotCount); 2905 __ pushq(current_param); 2906 __ pushq(param_limit); 2907 __ pushq(current_int_param_slot); 2908 __ pushq(current_float_param_slot); 2909 __ pushq(valuetypes_array_ptr); 2910 __ pushq(wasm_instance); 2911 __ pushq(function_data); 2912 // We had to prepare the parameters for the Call: we have to put the context 2913 // into rsi. 2914 __ LoadAnyTaggedField( 2915 rsi, 2916 MemOperand(wasm_instance, wasm::ObjectAccess::ToTagged( 2917 WasmInstanceObject::kNativeContextOffset))); 2918} 2919 2920void RestoreAfterBuiltinCall(MacroAssembler* masm, Register function_data, 2921 Register wasm_instance, 2922 Register valuetypes_array_ptr, 2923 Register current_float_param_slot, 2924 Register current_int_param_slot, 2925 Register param_limit, Register current_param) { 2926 // Pop and load values from the stack in order into the registers after 2927 // builtin calls for the GenericJSToWasmWrapper. 2928 __ popq(function_data); 2929 __ popq(wasm_instance); 2930 __ popq(valuetypes_array_ptr); 2931 __ popq(current_float_param_slot); 2932 __ popq(current_int_param_slot); 2933 __ popq(param_limit); 2934 __ popq(current_param); 2935} 2936 2937void FillJumpBuffer(MacroAssembler* masm, Register jmpbuf, Label* pc) { 2938 __ movq(MemOperand(jmpbuf, wasm::kJmpBufSpOffset), rsp); 2939 __ movq(MemOperand(jmpbuf, wasm::kJmpBufFpOffset), rbp); 2940 __ movq(kScratchRegister, 2941 __ StackLimitAsOperand(StackLimitKind::kRealStackLimit)); 2942 __ movq(MemOperand(jmpbuf, wasm::kJmpBufStackLimitOffset), kScratchRegister); 2943 __ leaq(kScratchRegister, MemOperand(pc, 0)); 2944 __ movq(MemOperand(jmpbuf, wasm::kJmpBufPcOffset), kScratchRegister); 2945} 2946 2947void LoadJumpBuffer(MacroAssembler* masm, Register jmpbuf, bool load_pc) { 2948 __ movq(rsp, MemOperand(jmpbuf, wasm::kJmpBufSpOffset)); 2949 __ movq(rbp, MemOperand(jmpbuf, wasm::kJmpBufFpOffset)); 2950 if (load_pc) { 2951 __ jmp(MemOperand(jmpbuf, wasm::kJmpBufPcOffset)); 2952 } 2953 // The stack limit is set separately under the ExecutionAccess lock. 2954} 2955 2956void SaveState(MacroAssembler* masm, Register active_continuation, Register tmp, 2957 Label* suspend) { 2958 Register foreign_jmpbuf = tmp; 2959 __ LoadAnyTaggedField( 2960 foreign_jmpbuf, 2961 FieldOperand(active_continuation, WasmContinuationObject::kJmpbufOffset)); 2962 Register jmpbuf = foreign_jmpbuf; 2963 __ LoadExternalPointerField( 2964 jmpbuf, FieldOperand(foreign_jmpbuf, Foreign::kForeignAddressOffset), 2965 kForeignForeignAddressTag, kScratchRegister); 2966 FillJumpBuffer(masm, jmpbuf, suspend); 2967} 2968 2969// Returns the new continuation in rax. 2970void AllocateContinuation(MacroAssembler* masm, Register function_data, 2971 Register wasm_instance) { 2972 Register suspender = kScratchRegister; 2973 __ LoadAnyTaggedField( 2974 suspender, 2975 FieldOperand(function_data, WasmExportedFunctionData::kSuspenderOffset)); 2976 MemOperand GCScanSlotPlace = 2977 MemOperand(rbp, BuiltinWasmWrapperConstants::kGCScanSlotCountOffset); 2978 __ Move(GCScanSlotPlace, 3); 2979 __ Push(wasm_instance); 2980 __ Push(function_data); 2981 __ Push(suspender); // Argument. 2982 __ Move(kContextRegister, Smi::zero()); 2983 __ CallRuntime(Runtime::kWasmAllocateContinuation); 2984 __ Pop(function_data); 2985 __ Pop(wasm_instance); 2986 STATIC_ASSERT(kReturnRegister0 == rax); 2987 suspender = no_reg; 2988} 2989 2990void LoadTargetJumpBuffer(MacroAssembler* masm, Register target_continuation) { 2991 Register foreign_jmpbuf = target_continuation; 2992 __ LoadAnyTaggedField( 2993 foreign_jmpbuf, 2994 FieldOperand(target_continuation, WasmContinuationObject::kJmpbufOffset)); 2995 Register target_jmpbuf = foreign_jmpbuf; 2996 __ LoadExternalPointerField( 2997 target_jmpbuf, 2998 FieldOperand(foreign_jmpbuf, Foreign::kForeignAddressOffset), 2999 kForeignForeignAddressTag, kScratchRegister); 3000 MemOperand GCScanSlotPlace = 3001 MemOperand(rbp, BuiltinWasmWrapperConstants::kGCScanSlotCountOffset); 3002 __ Move(GCScanSlotPlace, 0); 3003 // Switch stack! 3004 LoadJumpBuffer(masm, target_jmpbuf, false); 3005} 3006 3007void ReloadParentContinuation(MacroAssembler* masm, Register wasm_instance, 3008 Register return_reg, Register tmp1, 3009 Register tmp2) { 3010 Register active_continuation = tmp1; 3011 __ LoadRoot(active_continuation, RootIndex::kActiveContinuation); 3012 3013 // Set a null pointer in the jump buffer's SP slot to indicate to the stack 3014 // frame iterator that this stack is empty. 3015 Register foreign_jmpbuf = kScratchRegister; 3016 __ LoadAnyTaggedField( 3017 foreign_jmpbuf, 3018 FieldOperand(active_continuation, WasmContinuationObject::kJmpbufOffset)); 3019 Register jmpbuf = foreign_jmpbuf; 3020 __ LoadExternalPointerField( 3021 jmpbuf, FieldOperand(foreign_jmpbuf, Foreign::kForeignAddressOffset), 3022 kForeignForeignAddressTag, tmp2); 3023 __ movq(Operand(jmpbuf, wasm::kJmpBufSpOffset), Immediate(kNullAddress)); 3024 3025 Register parent = tmp2; 3026 __ LoadAnyTaggedField( 3027 parent, 3028 FieldOperand(active_continuation, WasmContinuationObject::kParentOffset)); 3029 3030 // Update active continuation root. 3031 __ movq(masm->RootAsOperand(RootIndex::kActiveContinuation), parent); 3032 foreign_jmpbuf = tmp1; 3033 __ LoadAnyTaggedField( 3034 foreign_jmpbuf, 3035 FieldOperand(parent, WasmContinuationObject::kJmpbufOffset)); 3036 jmpbuf = foreign_jmpbuf; 3037 __ LoadExternalPointerField( 3038 jmpbuf, FieldOperand(foreign_jmpbuf, Foreign::kForeignAddressOffset), 3039 kForeignForeignAddressTag, tmp2); 3040 3041 // Switch stack! 3042 LoadJumpBuffer(masm, jmpbuf, false); 3043 MemOperand GCScanSlotPlace = 3044 MemOperand(rbp, BuiltinWasmWrapperConstants::kGCScanSlotCountOffset); 3045 __ Move(GCScanSlotPlace, 1); 3046 __ Push(return_reg); 3047 __ Push(wasm_instance); // Spill. 3048 __ Move(kContextRegister, Smi::zero()); 3049 __ CallRuntime(Runtime::kWasmSyncStackLimit); 3050 __ Pop(wasm_instance); 3051 __ Pop(return_reg); 3052} 3053 3054void RestoreParentSuspender(MacroAssembler* masm) { 3055 Register suspender = kScratchRegister; 3056 __ LoadRoot(suspender, RootIndex::kActiveSuspender); 3057 __ LoadAnyTaggedField( 3058 suspender, FieldOperand(suspender, WasmSuspenderObject::kParentOffset)); 3059 __ CompareRoot(suspender, RootIndex::kUndefinedValue); 3060 Label undefined; 3061 __ j(equal, &undefined, Label::kNear); 3062#ifdef DEBUG 3063 // Check that the parent suspender is inactive. 3064 Label parent_inactive; 3065 Register state = rbx; 3066 __ LoadTaggedSignedField( 3067 state, FieldOperand(suspender, WasmSuspenderObject::kStateOffset)); 3068 __ SmiCompare(state, Smi::FromInt(WasmSuspenderObject::Inactive)); 3069 __ j(equal, &parent_inactive, Label::kNear); 3070 __ Trap(); 3071 __ bind(&parent_inactive); 3072#endif 3073 __ StoreTaggedSignedField( 3074 FieldOperand(suspender, WasmSuspenderObject::kStateOffset), 3075 Smi::FromInt(WasmSuspenderObject::State::Active)); 3076 __ bind(&undefined); 3077 __ movq(masm->RootAsOperand(RootIndex::kActiveSuspender), suspender); 3078} 3079 3080void LoadFunctionDataAndWasmInstance(MacroAssembler* masm, 3081 Register function_data, 3082 Register wasm_instance) { 3083 Register closure = function_data; 3084 Register shared_function_info = closure; 3085 __ LoadAnyTaggedField( 3086 shared_function_info, 3087 MemOperand( 3088 closure, 3089 wasm::ObjectAccess::SharedFunctionInfoOffsetInTaggedJSFunction())); 3090 closure = no_reg; 3091 __ LoadAnyTaggedField( 3092 function_data, 3093 MemOperand(shared_function_info, 3094 SharedFunctionInfo::kFunctionDataOffset - kHeapObjectTag)); 3095 shared_function_info = no_reg; 3096 3097 __ LoadAnyTaggedField( 3098 wasm_instance, 3099 MemOperand(function_data, 3100 WasmExportedFunctionData::kInstanceOffset - kHeapObjectTag)); 3101} 3102 3103void LoadValueTypesArray(MacroAssembler* masm, Register function_data, 3104 Register valuetypes_array_ptr, Register return_count, 3105 Register param_count) { 3106 Register foreign_signature = valuetypes_array_ptr; 3107 __ LoadAnyTaggedField( 3108 foreign_signature, 3109 MemOperand(function_data, 3110 WasmExportedFunctionData::kSignatureOffset - kHeapObjectTag)); 3111 Register signature = foreign_signature; 3112 __ LoadExternalPointerField( 3113 signature, 3114 FieldOperand(foreign_signature, Foreign::kForeignAddressOffset), 3115 kForeignForeignAddressTag, kScratchRegister); 3116 foreign_signature = no_reg; 3117 __ movq(return_count, 3118 MemOperand(signature, wasm::FunctionSig::kReturnCountOffset)); 3119 __ movq(param_count, 3120 MemOperand(signature, wasm::FunctionSig::kParameterCountOffset)); 3121 valuetypes_array_ptr = signature; 3122 __ movq(valuetypes_array_ptr, 3123 MemOperand(signature, wasm::FunctionSig::kRepsOffset)); 3124} 3125 3126void GenericJSToWasmWrapperHelper(MacroAssembler* masm, bool stack_switch) { 3127 // Set up the stackframe. 3128 __ EnterFrame(stack_switch ? StackFrame::STACK_SWITCH 3129 : StackFrame::JS_TO_WASM); 3130 3131 // ------------------------------------------- 3132 // Compute offsets and prepare for GC. 3133 // ------------------------------------------- 3134 constexpr int kGCScanSlotCountOffset = 3135 BuiltinWasmWrapperConstants::kGCScanSlotCountOffset; 3136 // The number of parameters passed to this function. 3137 constexpr int kInParamCountOffset = 3138 BuiltinWasmWrapperConstants::kInParamCountOffset; 3139 // The number of parameters according to the signature. 3140 constexpr int kParamCountOffset = 3141 BuiltinWasmWrapperConstants::kParamCountOffset; 3142 constexpr int kReturnCountOffset = kParamCountOffset - kSystemPointerSize; 3143 constexpr int kValueTypesArrayStartOffset = 3144 kReturnCountOffset - kSystemPointerSize; 3145 // A boolean flag to check if one of the parameters is a reference. If so, we 3146 // iterate over the parameters two times, first for all value types, and then 3147 // for all references. 3148 constexpr int kHasRefTypesOffset = 3149 kValueTypesArrayStartOffset - kSystemPointerSize; 3150 // We set and use this slot only when moving parameters into the parameter 3151 // registers (so no GC scan is needed). 3152 constexpr int kFunctionDataOffset = kHasRefTypesOffset - kSystemPointerSize; 3153 constexpr int kLastSpillOffset = kFunctionDataOffset; 3154 constexpr int kNumSpillSlots = 7; 3155 __ subq(rsp, Immediate(kNumSpillSlots * kSystemPointerSize)); 3156 // Put the in_parameter count on the stack, we only need it at the very end 3157 // when we pop the parameters off the stack. 3158 Register in_param_count = rax; 3159 __ decq(in_param_count); // Exclude receiver. 3160 __ movq(MemOperand(rbp, kInParamCountOffset), in_param_count); 3161 in_param_count = no_reg; 3162 3163 Register function_data = rdi; 3164 Register wasm_instance = rsi; 3165 LoadFunctionDataAndWasmInstance(masm, function_data, wasm_instance); 3166 3167 Label compile_wrapper, compile_wrapper_done; 3168 if (!stack_switch) { 3169 // ------------------------------------------- 3170 // Decrement the budget of the generic wrapper in function data. 3171 // ------------------------------------------- 3172 __ SmiAddConstant( 3173 MemOperand( 3174 function_data, 3175 WasmExportedFunctionData::kWrapperBudgetOffset - kHeapObjectTag), 3176 Smi::FromInt(-1)); 3177 3178 // ------------------------------------------- 3179 // Check if the budget of the generic wrapper reached 0 (zero). 3180 // ------------------------------------------- 3181 // Instead of a specific comparison, we can directly use the flags set 3182 // from the previous addition. 3183 __ j(less_equal, &compile_wrapper); 3184 __ bind(&compile_wrapper_done); 3185 } 3186 3187 Label suspend; 3188 if (stack_switch) { 3189 Register active_continuation = rbx; 3190 __ LoadRoot(active_continuation, RootIndex::kActiveContinuation); 3191 SaveState(masm, active_continuation, rcx, &suspend); 3192 AllocateContinuation(masm, function_data, wasm_instance); 3193 Register target_continuation = rax; /* fixed */ 3194 // Save the old stack's rbp in r9, and use it to access the parameters in 3195 // the parent frame. 3196 // We also distribute the spill slots across the two stacks as needed by 3197 // creating a "shadow frame": 3198 // 3199 // old stack: new stack: 3200 // +-----------------+ 3201 // | <parent frame> | 3202 // r9-> +-----------------+ +-----------------+ 3203 // | <fixed> | | 0 (jmpbuf rbp) | 3204 // +-----------------+ rbp-> +-----------------+ 3205 // |kGCScanSlotCount | |kGCScanSlotCount | 3206 // +-----------------+ +-----------------+ 3207 // | kParamCount | | / | 3208 // +-----------------+ +-----------------+ 3209 // | kInParamCount | | / | 3210 // +-----------------+ +-----------------+ 3211 // | / | | kReturnCount | 3212 // +-----------------+ +-----------------+ 3213 // | / | |kValueTypesArray | 3214 // +-----------------+ +-----------------+ 3215 // | / | | kHasRefTypes | 3216 // +-----------------+ +-----------------+ 3217 // | / | | kFunctionData | 3218 // +-----------------+ rsp-> +-----------------+ 3219 // seal stack | 3220 // V 3221 // 3222 // - When we first enter the prompt, we have access to both frames, so it 3223 // does not matter where the values are spilled. 3224 // - When we suspend for the first time, we longjmp to the original frame 3225 // (left). So the frame needs to contain the necessary information to 3226 // properly deconstruct itself (actual param count and signature param 3227 // count). 3228 // - When we suspend for the second time, we longjmp to the frame that was 3229 // set up by the WasmResume builtin, which has the same layout as the 3230 // original frame (left). 3231 // - When the closure finally resolves, we use the value types pointer 3232 // stored in the shadow frame to get the return type and convert the return 3233 // value accordingly. 3234 __ movq(r9, rbp); 3235 LoadTargetJumpBuffer(masm, target_continuation); 3236 // Push the loaded rbp. We know it is null, because there is no frame yet, 3237 // so we could also push 0 directly. In any case we need to push it, because 3238 // this marks the base of the stack segment for the stack frame iterator. 3239 __ pushq(rbp); 3240 __ movq(rbp, rsp); 3241 __ addq(rsp, Immediate(kLastSpillOffset)); 3242 } 3243 Register original_fp = stack_switch ? r9 : rbp; 3244 3245 // ------------------------------------------- 3246 // Load values from the signature. 3247 // ------------------------------------------- 3248 Register valuetypes_array_ptr = r11; 3249 Register return_count = r8; 3250 Register param_count = rcx; 3251 LoadValueTypesArray(masm, function_data, valuetypes_array_ptr, return_count, 3252 param_count); 3253 3254 // Initialize the {HasRefTypes} slot. 3255 __ movq(MemOperand(rbp, kHasRefTypesOffset), Immediate(0)); 3256 3257 // ------------------------------------------- 3258 // Store signature-related values to the stack. 3259 // ------------------------------------------- 3260 // We store values on the stack to restore them after function calls. 3261 // We cannot push values onto the stack right before the wasm call. The wasm 3262 // function expects the parameters, that didn't fit into the registers, on the 3263 // top of the stack. 3264 __ movq(MemOperand(original_fp, kParamCountOffset), param_count); 3265 __ movq(MemOperand(rbp, kReturnCountOffset), return_count); 3266 __ movq(MemOperand(rbp, kValueTypesArrayStartOffset), valuetypes_array_ptr); 3267 3268 // ------------------------------------------- 3269 // Parameter handling. 3270 // ------------------------------------------- 3271 Label prepare_for_wasm_call; 3272 __ Cmp(param_count, 0); 3273 3274 // IF we have 0 params: jump through parameter handling. 3275 __ j(equal, &prepare_for_wasm_call); 3276 3277 // ------------------------------------------- 3278 // Create 2 sections for integer and float params. 3279 // ------------------------------------------- 3280 // We will create 2 sections on the stack for the evaluated parameters: 3281 // Integer and Float section, both with parameter count size. We will place 3282 // the parameters into these sections depending on their valuetype. This way 3283 // we can easily fill the general purpose and floating point parameter 3284 // registers and place the remaining parameters onto the stack in proper order 3285 // for the Wasm function. These remaining params are the final stack 3286 // parameters for the call to WebAssembly. Example of the stack layout after 3287 // processing 2 int and 1 float parameters when param_count is 4. 3288 // +-----------------+ 3289 // | rbp | 3290 // |-----------------|------------------------------- 3291 // | | Slots we defined 3292 // | Saved values | when setting up 3293 // | | the stack 3294 // | | 3295 // +-Integer section-+--- <--- start_int_section ---- 3296 // | 1st int param | 3297 // |- - - - - - - - -| 3298 // | 2nd int param | 3299 // |- - - - - - - - -| <----- current_int_param_slot 3300 // | | (points to the stackslot 3301 // |- - - - - - - - -| where the next int param should be placed) 3302 // | | 3303 // +--Float section--+--- <--- start_float_section -- 3304 // | 1st float param | 3305 // |- - - - - - - - -| <---- current_float_param_slot 3306 // | | (points to the stackslot 3307 // |- - - - - - - - -| where the next float param should be placed) 3308 // | | 3309 // |- - - - - - - - -| 3310 // | | 3311 // +---Final stack---+------------------------------ 3312 // +-parameters for--+------------------------------ 3313 // +-the Wasm call---+------------------------------ 3314 // | . . . | 3315 3316 constexpr int kIntegerSectionStartOffset = 3317 kLastSpillOffset - kSystemPointerSize; 3318 // For Integer section. 3319 // Set the current_int_param_slot to point to the start of the section. 3320 Register current_int_param_slot = r10; 3321 __ leaq(current_int_param_slot, MemOperand(rsp, -kSystemPointerSize)); 3322 Register params_size = param_count; 3323 param_count = no_reg; 3324 __ shlq(params_size, Immediate(kSystemPointerSizeLog2)); 3325 __ subq(rsp, params_size); 3326 3327 // For Float section. 3328 // Set the current_float_param_slot to point to the start of the section. 3329 Register current_float_param_slot = r15; 3330 __ leaq(current_float_param_slot, MemOperand(rsp, -kSystemPointerSize)); 3331 __ subq(rsp, params_size); 3332 params_size = no_reg; 3333 param_count = rcx; 3334 __ movq(param_count, MemOperand(original_fp, kParamCountOffset)); 3335 3336 // ------------------------------------------- 3337 // Set up for the param evaluation loop. 3338 // ------------------------------------------- 3339 // We will loop through the params starting with the 1st param. 3340 // The order of processing the params is important. We have to evaluate them 3341 // in an increasing order. 3342 // +-----------------+--------------- 3343 // | param n | 3344 // |- - - - - - - - -| 3345 // | param n-1 | Caller 3346 // | ... | frame slots 3347 // | param 1 | 3348 // |- - - - - - - - -| 3349 // | receiver | 3350 // +-----------------+--------------- 3351 // | return addr | 3352 // FP->|- - - - - - - - -| 3353 // | rbp | Spill slots 3354 // |- - - - - - - - -| 3355 // 3356 // [rbp + current_param] gives us the parameter we are processing. 3357 // We iterate through half-open interval <1st param, [rbp + param_limit]). 3358 3359 Register current_param = rbx; 3360 Register param_limit = rdx; 3361 constexpr int kReceiverOnStackSize = kSystemPointerSize; 3362 __ Move(current_param, 3363 kFPOnStackSize + kPCOnStackSize + kReceiverOnStackSize); 3364 __ movq(param_limit, param_count); 3365 __ shlq(param_limit, Immediate(kSystemPointerSizeLog2)); 3366 __ addq(param_limit, 3367 Immediate(kFPOnStackSize + kPCOnStackSize + kReceiverOnStackSize)); 3368 const int increment = kSystemPointerSize; 3369 Register param = rax; 3370 // We have to check the types of the params. The ValueType array contains 3371 // first the return then the param types. 3372 constexpr int kValueTypeSize = sizeof(wasm::ValueType); 3373 STATIC_ASSERT(kValueTypeSize == 4); 3374 const int32_t kValueTypeSizeLog2 = log2(kValueTypeSize); 3375 // Set the ValueType array pointer to point to the first parameter. 3376 Register returns_size = return_count; 3377 return_count = no_reg; 3378 __ shlq(returns_size, Immediate(kValueTypeSizeLog2)); 3379 __ addq(valuetypes_array_ptr, returns_size); 3380 returns_size = no_reg; 3381 Register valuetype = r12; 3382 3383 // ------------------------------------------- 3384 // Param evaluation loop. 3385 // ------------------------------------------- 3386 Label loop_through_params; 3387 __ bind(&loop_through_params); 3388 3389 __ movq(param, MemOperand(original_fp, current_param, times_1, 0)); 3390 __ movl(valuetype, 3391 Operand(valuetypes_array_ptr, wasm::ValueType::bit_field_offset())); 3392 3393 // ------------------------------------------- 3394 // Param conversion. 3395 // ------------------------------------------- 3396 // If param is a Smi we can easily convert it. Otherwise we'll call a builtin 3397 // for conversion. 3398 Label convert_param; 3399 __ cmpq(valuetype, Immediate(wasm::kWasmI32.raw_bit_field())); 3400 __ j(not_equal, &convert_param); 3401 __ JumpIfNotSmi(param, &convert_param); 3402 // Change the paramfrom Smi to int32. 3403 __ SmiUntag(param); 3404 // Zero extend. 3405 __ movl(param, param); 3406 // Place the param into the proper slot in Integer section. 3407 __ movq(MemOperand(current_int_param_slot, 0), param); 3408 __ subq(current_int_param_slot, Immediate(kSystemPointerSize)); 3409 3410 // ------------------------------------------- 3411 // Param conversion done. 3412 // ------------------------------------------- 3413 Label param_conversion_done; 3414 __ bind(¶m_conversion_done); 3415 3416 __ addq(current_param, Immediate(increment)); 3417 __ addq(valuetypes_array_ptr, Immediate(kValueTypeSize)); 3418 3419 __ cmpq(current_param, param_limit); 3420 __ j(not_equal, &loop_through_params); 3421 3422 // ------------------------------------------- 3423 // Second loop to handle references. 3424 // ------------------------------------------- 3425 // In this loop we iterate over all parameters for a second time and copy all 3426 // reference parameters at the end of the integer parameters section. 3427 Label ref_params_done; 3428 // We check if we have seen a reference in the first parameter loop. 3429 Register ref_param_count = param_count; 3430 __ movq(ref_param_count, Immediate(0)); 3431 __ cmpq(MemOperand(rbp, kHasRefTypesOffset), Immediate(0)); 3432 __ j(equal, &ref_params_done); 3433 // We re-calculate the beginning of the value-types array and the beginning of 3434 // the parameters ({valuetypes_array_ptr} and {current_param}). 3435 __ movq(valuetypes_array_ptr, MemOperand(rbp, kValueTypesArrayStartOffset)); 3436 return_count = current_param; 3437 current_param = no_reg; 3438 __ movq(return_count, MemOperand(rbp, kReturnCountOffset)); 3439 returns_size = return_count; 3440 return_count = no_reg; 3441 __ shlq(returns_size, Immediate(kValueTypeSizeLog2)); 3442 __ addq(valuetypes_array_ptr, returns_size); 3443 3444 current_param = returns_size; 3445 returns_size = no_reg; 3446 __ Move(current_param, 3447 kFPOnStackSize + kPCOnStackSize + kReceiverOnStackSize); 3448 3449 Label ref_loop_through_params; 3450 Label ref_loop_end; 3451 // Start of the loop. 3452 __ bind(&ref_loop_through_params); 3453 3454 // Load the current parameter with type. 3455 __ movq(param, MemOperand(original_fp, current_param, times_1, 0)); 3456 __ movl(valuetype, 3457 Operand(valuetypes_array_ptr, wasm::ValueType::bit_field_offset())); 3458 // Extract the ValueKind of the type, to check for kRef and kOptRef. 3459 __ andl(valuetype, Immediate(wasm::kWasmValueKindBitsMask)); 3460 Label move_ref_to_slot; 3461 __ cmpq(valuetype, Immediate(wasm::ValueKind::kOptRef)); 3462 __ j(equal, &move_ref_to_slot); 3463 __ cmpq(valuetype, Immediate(wasm::ValueKind::kRef)); 3464 __ j(equal, &move_ref_to_slot); 3465 __ jmp(&ref_loop_end); 3466 3467 // Place the param into the proper slot in Integer section. 3468 __ bind(&move_ref_to_slot); 3469 __ addq(ref_param_count, Immediate(1)); 3470 __ movq(MemOperand(current_int_param_slot, 0), param); 3471 __ subq(current_int_param_slot, Immediate(kSystemPointerSize)); 3472 3473 // Move to the next parameter. 3474 __ bind(&ref_loop_end); 3475 __ addq(current_param, Immediate(increment)); 3476 __ addq(valuetypes_array_ptr, Immediate(kValueTypeSize)); 3477 3478 // Check if we finished all parameters. 3479 __ cmpq(current_param, param_limit); 3480 __ j(not_equal, &ref_loop_through_params); 3481 3482 __ bind(&ref_params_done); 3483 __ movq(valuetype, ref_param_count); 3484 ref_param_count = valuetype; 3485 valuetype = no_reg; 3486 // ------------------------------------------- 3487 // Move the parameters into the proper param registers. 3488 // ------------------------------------------- 3489 // The Wasm function expects that the params can be popped from the top of the 3490 // stack in an increasing order. 3491 // We can always move the values on the beginning of the sections into the GP 3492 // or FP parameter registers. If the parameter count is less than the number 3493 // of parameter registers, we may move values into the registers that are not 3494 // in the section. 3495 // ----------- S t a t e ------------- 3496 // -- r8 : start_int_section 3497 // -- rdi : start_float_section 3498 // -- r10 : current_int_param_slot 3499 // -- r15 : current_float_param_slot 3500 // -- r11 : valuetypes_array_ptr 3501 // -- r12 : valuetype 3502 // -- rsi : wasm_instance 3503 // -- GpParamRegisters = rax, rdx, rcx, rbx, r9 3504 // ----------------------------------- 3505 3506 Register temp_params_size = rax; 3507 __ movq(temp_params_size, MemOperand(original_fp, kParamCountOffset)); 3508 __ shlq(temp_params_size, Immediate(kSystemPointerSizeLog2)); 3509 // We want to use the register of the function_data = rdi. 3510 __ movq(MemOperand(rbp, kFunctionDataOffset), function_data); 3511 Register start_float_section = function_data; 3512 function_data = no_reg; 3513 __ movq(start_float_section, rbp); 3514 __ addq(start_float_section, Immediate(kIntegerSectionStartOffset)); 3515 __ subq(start_float_section, temp_params_size); 3516 temp_params_size = no_reg; 3517 // Fill the FP param registers. 3518 __ Movsd(xmm1, MemOperand(start_float_section, 0)); 3519 __ Movsd(xmm2, MemOperand(start_float_section, -kSystemPointerSize)); 3520 __ Movsd(xmm3, MemOperand(start_float_section, -2 * kSystemPointerSize)); 3521 __ Movsd(xmm4, MemOperand(start_float_section, -3 * kSystemPointerSize)); 3522 __ Movsd(xmm5, MemOperand(start_float_section, -4 * kSystemPointerSize)); 3523 __ Movsd(xmm6, MemOperand(start_float_section, -5 * kSystemPointerSize)); 3524 // We want the start to point to the last properly placed param. 3525 __ subq(start_float_section, Immediate(5 * kSystemPointerSize)); 3526 3527 Register start_int_section = r8; 3528 __ movq(start_int_section, rbp); 3529 __ addq(start_int_section, Immediate(kIntegerSectionStartOffset)); 3530 // Fill the GP param registers. 3531 __ movq(rax, MemOperand(start_int_section, 0)); 3532 __ movq(rdx, MemOperand(start_int_section, -kSystemPointerSize)); 3533 __ movq(rcx, MemOperand(start_int_section, -2 * kSystemPointerSize)); 3534 __ movq(rbx, MemOperand(start_int_section, -3 * kSystemPointerSize)); 3535 __ movq(r9, MemOperand(start_int_section, -4 * kSystemPointerSize)); 3536 // We want the start to point to the last properly placed param. 3537 __ subq(start_int_section, Immediate(4 * kSystemPointerSize)); 3538 3539 // ------------------------------------------- 3540 // Place the final stack parameters to the proper place. 3541 // ------------------------------------------- 3542 // We want the current_param_slot (insertion) pointers to point at the last 3543 // param of the section instead of the next free slot. 3544 __ addq(current_int_param_slot, Immediate(kSystemPointerSize)); 3545 __ addq(current_float_param_slot, Immediate(kSystemPointerSize)); 3546 3547 // ------------------------------------------- 3548 // Final stack parameters loop. 3549 // ------------------------------------------- 3550 // The parameters that didn't fit into the registers should be placed on the 3551 // top of the stack contiguously. The interval of parameters between the 3552 // start_section and the current_param_slot pointers define the remaining 3553 // parameters of the section. 3554 // We can iterate through the valuetypes array to decide from which section we 3555 // need to push the parameter onto the top of the stack. By iterating in a 3556 // reversed order we can easily pick the last parameter of the proper section. 3557 // The parameter of the section is pushed on the top of the stack only if the 3558 // interval of remaining params is not empty. This way we ensure that only 3559 // params that didn't fit into param registers are pushed again. 3560 3561 Label loop_through_valuetypes; 3562 Label loop_place_ref_params; 3563 __ bind(&loop_place_ref_params); 3564 __ testq(ref_param_count, ref_param_count); 3565 __ j(zero, &loop_through_valuetypes); 3566 3567 __ cmpq(start_int_section, current_int_param_slot); 3568 // if no int or ref param remains, directly iterate valuetypes 3569 __ j(less_equal, &loop_through_valuetypes); 3570 3571 __ pushq(MemOperand(current_int_param_slot, 0)); 3572 __ addq(current_int_param_slot, Immediate(kSystemPointerSize)); 3573 __ subq(ref_param_count, Immediate(1)); 3574 __ jmp(&loop_place_ref_params); 3575 3576 valuetype = ref_param_count; 3577 ref_param_count = no_reg; 3578 __ bind(&loop_through_valuetypes); 3579 3580 // We iterated through the valuetypes array, we are one field over the end in 3581 // the beginning. Also, we have to decrement it in each iteration. 3582 __ subq(valuetypes_array_ptr, Immediate(kValueTypeSize)); 3583 3584 // Check if there are still remaining integer params. 3585 Label continue_loop; 3586 __ cmpq(start_int_section, current_int_param_slot); 3587 // If there are remaining integer params. 3588 __ j(greater, &continue_loop); 3589 3590 // Check if there are still remaining float params. 3591 __ cmpq(start_float_section, current_float_param_slot); 3592 // If there aren't any params remaining. 3593 Label params_done; 3594 __ j(less_equal, ¶ms_done); 3595 3596 __ bind(&continue_loop); 3597 __ movl(valuetype, 3598 Operand(valuetypes_array_ptr, wasm::ValueType::bit_field_offset())); 3599 Label place_integer_param; 3600 Label place_float_param; 3601 __ cmpq(valuetype, Immediate(wasm::kWasmI32.raw_bit_field())); 3602 __ j(equal, &place_integer_param); 3603 3604 __ cmpq(valuetype, Immediate(wasm::kWasmI64.raw_bit_field())); 3605 __ j(equal, &place_integer_param); 3606 3607 __ cmpq(valuetype, Immediate(wasm::kWasmF32.raw_bit_field())); 3608 __ j(equal, &place_float_param); 3609 3610 __ cmpq(valuetype, Immediate(wasm::kWasmF64.raw_bit_field())); 3611 __ j(equal, &place_float_param); 3612 3613 // ref params have already been pushed, so go through directly 3614 __ jmp(&loop_through_valuetypes); 3615 3616 // All other types are reference types. We can just fall through to place them 3617 // in the integer section. 3618 3619 __ bind(&place_integer_param); 3620 __ cmpq(start_int_section, current_int_param_slot); 3621 // If there aren't any integer params remaining, just floats, then go to the 3622 // next valuetype. 3623 __ j(less_equal, &loop_through_valuetypes); 3624 3625 // Copy the param from the integer section to the actual parameter area. 3626 __ pushq(MemOperand(current_int_param_slot, 0)); 3627 __ addq(current_int_param_slot, Immediate(kSystemPointerSize)); 3628 __ jmp(&loop_through_valuetypes); 3629 3630 __ bind(&place_float_param); 3631 __ cmpq(start_float_section, current_float_param_slot); 3632 // If there aren't any float params remaining, just integers, then go to the 3633 // next valuetype. 3634 __ j(less_equal, &loop_through_valuetypes); 3635 3636 // Copy the param from the float section to the actual parameter area. 3637 __ pushq(MemOperand(current_float_param_slot, 0)); 3638 __ addq(current_float_param_slot, Immediate(kSystemPointerSize)); 3639 __ jmp(&loop_through_valuetypes); 3640 3641 __ bind(¶ms_done); 3642 // Restore function_data after we are done with parameter placement. 3643 function_data = rdi; 3644 __ movq(function_data, MemOperand(rbp, kFunctionDataOffset)); 3645 3646 __ bind(&prepare_for_wasm_call); 3647 // ------------------------------------------- 3648 // Prepare for the Wasm call. 3649 // ------------------------------------------- 3650 // Set thread_in_wasm_flag. 3651 Register thread_in_wasm_flag_addr = r12; 3652 __ movq( 3653 thread_in_wasm_flag_addr, 3654 MemOperand(kRootRegister, Isolate::thread_in_wasm_flag_address_offset())); 3655 __ movl(MemOperand(thread_in_wasm_flag_addr, 0), Immediate(1)); 3656 thread_in_wasm_flag_addr = no_reg; 3657 3658 Register function_entry = function_data; 3659 Register scratch = r12; 3660 __ LoadAnyTaggedField( 3661 function_entry, 3662 FieldOperand(function_data, WasmExportedFunctionData::kInternalOffset)); 3663 __ LoadExternalPointerField( 3664 function_entry, 3665 FieldOperand(function_entry, WasmInternalFunction::kForeignAddressOffset), 3666 kForeignForeignAddressTag, scratch); 3667 function_data = no_reg; 3668 scratch = no_reg; 3669 3670 // We set the indicating value for the GC to the proper one for Wasm call. 3671 constexpr int kWasmCallGCScanSlotCount = 0; 3672 __ Move(MemOperand(rbp, kGCScanSlotCountOffset), kWasmCallGCScanSlotCount); 3673 3674 // ------------------------------------------- 3675 // Call the Wasm function. 3676 // ------------------------------------------- 3677 __ call(function_entry); 3678 // Note: we might be returning to a different frame if the stack was suspended 3679 // and resumed during the call. The new frame is set up by WasmResume and has 3680 // a compatible layout. 3681 function_entry = no_reg; 3682 3683 // ------------------------------------------- 3684 // Resetting after the Wasm call. 3685 // ------------------------------------------- 3686 // Restore rsp to free the reserved stack slots for the sections. 3687 __ leaq(rsp, MemOperand(rbp, kLastSpillOffset)); 3688 3689 // Unset thread_in_wasm_flag. 3690 thread_in_wasm_flag_addr = r8; 3691 __ movq( 3692 thread_in_wasm_flag_addr, 3693 MemOperand(kRootRegister, Isolate::thread_in_wasm_flag_address_offset())); 3694 __ movl(MemOperand(thread_in_wasm_flag_addr, 0), Immediate(0)); 3695 thread_in_wasm_flag_addr = no_reg; 3696 3697 // ------------------------------------------- 3698 // Return handling. 3699 // ------------------------------------------- 3700 return_count = r8; 3701 __ movq(return_count, MemOperand(rbp, kReturnCountOffset)); 3702 Register return_reg = rax; 3703 3704 // If we have 1 return value, then jump to conversion. 3705 __ cmpl(return_count, Immediate(1)); 3706 Label convert_return; 3707 __ j(equal, &convert_return); 3708 3709 // Otherwise load undefined. 3710 __ LoadRoot(return_reg, RootIndex::kUndefinedValue); 3711 3712 Label return_done; 3713 __ bind(&return_done); 3714 if (stack_switch) { 3715 ReloadParentContinuation(masm, wasm_instance, return_reg, rbx, rcx); 3716 RestoreParentSuspender(masm); 3717 } 3718 __ bind(&suspend); 3719 // No need to process the return value if the stack is suspended, there is a 3720 // single 'externref' value (the promise) which doesn't require conversion. 3721 3722 __ movq(param_count, MemOperand(rbp, kParamCountOffset)); 3723 3724 // Calculate the number of parameters we have to pop off the stack. This 3725 // number is max(in_param_count, param_count). 3726 in_param_count = rdx; 3727 __ movq(in_param_count, MemOperand(rbp, kInParamCountOffset)); 3728 __ cmpq(param_count, in_param_count); 3729 __ cmovq(less, param_count, in_param_count); 3730 3731 // ------------------------------------------- 3732 // Deconstrunct the stack frame. 3733 // ------------------------------------------- 3734 __ LeaveFrame(stack_switch ? StackFrame::STACK_SWITCH 3735 : StackFrame::JS_TO_WASM); 3736 3737 // We have to remove the caller frame slots: 3738 // - JS arguments 3739 // - the receiver 3740 // and transfer the control to the return address (the return address is 3741 // expected to be on the top of the stack). 3742 // We cannot use just the ret instruction for this, because we cannot pass the 3743 // number of slots to remove in a Register as an argument. 3744 __ DropArguments(param_count, rbx, TurboAssembler::kCountIsInteger, 3745 TurboAssembler::kCountExcludesReceiver); 3746 __ ret(0); 3747 3748 // -------------------------------------------------------------------------- 3749 // Deferred code. 3750 // -------------------------------------------------------------------------- 3751 3752 // ------------------------------------------- 3753 // Param conversion builtins. 3754 // ------------------------------------------- 3755 __ bind(&convert_param); 3756 // Restore function_data register (which was clobbered by the code above, 3757 // but was valid when jumping here earlier). 3758 function_data = rdi; 3759 // The order of pushes is important. We want the heap objects, that should be 3760 // scanned by GC, to be on the top of the stack. 3761 // We have to set the indicating value for the GC to the number of values on 3762 // the top of the stack that have to be scanned before calling the builtin 3763 // function. 3764 // The builtin expects the parameter to be in register param = rax. 3765 3766 constexpr int kBuiltinCallGCScanSlotCount = 2; 3767 PrepareForBuiltinCall(masm, MemOperand(rbp, kGCScanSlotCountOffset), 3768 kBuiltinCallGCScanSlotCount, current_param, param_limit, 3769 current_int_param_slot, current_float_param_slot, 3770 valuetypes_array_ptr, wasm_instance, function_data); 3771 3772 Label param_kWasmI32_not_smi; 3773 Label param_kWasmI64; 3774 Label param_kWasmF32; 3775 Label param_kWasmF64; 3776 3777 __ cmpq(valuetype, Immediate(wasm::kWasmI32.raw_bit_field())); 3778 __ j(equal, ¶m_kWasmI32_not_smi); 3779 3780 __ cmpq(valuetype, Immediate(wasm::kWasmI64.raw_bit_field())); 3781 __ j(equal, ¶m_kWasmI64); 3782 3783 __ cmpq(valuetype, Immediate(wasm::kWasmF32.raw_bit_field())); 3784 __ j(equal, ¶m_kWasmF32); 3785 3786 __ cmpq(valuetype, Immediate(wasm::kWasmF64.raw_bit_field())); 3787 __ j(equal, ¶m_kWasmF64); 3788 3789 // The parameter is a reference. We do not convert the parameter immediately. 3790 // Instead we will later loop over all parameters again to handle reference 3791 // parameters. The reason is that later value type parameters may trigger a 3792 // GC, and we cannot keep reference parameters alive then. Instead we leave 3793 // reference parameters at their initial place on the stack and only copy them 3794 // once no GC can happen anymore. 3795 // As an optimization we set a flag here that indicates that we have seen a 3796 // reference so far. If there was no reference parameter, we would not iterate 3797 // over the parameters for a second time. 3798 __ movq(MemOperand(rbp, kHasRefTypesOffset), Immediate(1)); 3799 RestoreAfterBuiltinCall(masm, function_data, wasm_instance, 3800 valuetypes_array_ptr, current_float_param_slot, 3801 current_int_param_slot, param_limit, current_param); 3802 __ jmp(¶m_conversion_done); 3803 3804 __ int3(); 3805 3806 __ bind(¶m_kWasmI32_not_smi); 3807 __ Call(BUILTIN_CODE(masm->isolate(), WasmTaggedNonSmiToInt32), 3808 RelocInfo::CODE_TARGET); 3809 // Param is the result of the builtin. 3810 __ AssertZeroExtended(param); 3811 RestoreAfterBuiltinCall(masm, function_data, wasm_instance, 3812 valuetypes_array_ptr, current_float_param_slot, 3813 current_int_param_slot, param_limit, current_param); 3814 __ movq(MemOperand(current_int_param_slot, 0), param); 3815 __ subq(current_int_param_slot, Immediate(kSystemPointerSize)); 3816 __ jmp(¶m_conversion_done); 3817 3818 __ bind(¶m_kWasmI64); 3819 __ Call(BUILTIN_CODE(masm->isolate(), BigIntToI64), RelocInfo::CODE_TARGET); 3820 RestoreAfterBuiltinCall(masm, function_data, wasm_instance, 3821 valuetypes_array_ptr, current_float_param_slot, 3822 current_int_param_slot, param_limit, current_param); 3823 __ movq(MemOperand(current_int_param_slot, 0), param); 3824 __ subq(current_int_param_slot, Immediate(kSystemPointerSize)); 3825 __ jmp(¶m_conversion_done); 3826 3827 __ bind(¶m_kWasmF32); 3828 __ Call(BUILTIN_CODE(masm->isolate(), WasmTaggedToFloat64), 3829 RelocInfo::CODE_TARGET); 3830 RestoreAfterBuiltinCall(masm, function_data, wasm_instance, 3831 valuetypes_array_ptr, current_float_param_slot, 3832 current_int_param_slot, param_limit, current_param); 3833 // Clear higher bits. 3834 __ Xorpd(xmm1, xmm1); 3835 // Truncate float64 to float32. 3836 __ Cvtsd2ss(xmm1, xmm0); 3837 __ Movsd(MemOperand(current_float_param_slot, 0), xmm1); 3838 __ subq(current_float_param_slot, Immediate(kSystemPointerSize)); 3839 __ jmp(¶m_conversion_done); 3840 3841 __ bind(¶m_kWasmF64); 3842 __ Call(BUILTIN_CODE(masm->isolate(), WasmTaggedToFloat64), 3843 RelocInfo::CODE_TARGET); 3844 RestoreAfterBuiltinCall(masm, function_data, wasm_instance, 3845 valuetypes_array_ptr, current_float_param_slot, 3846 current_int_param_slot, param_limit, current_param); 3847 __ Movsd(MemOperand(current_float_param_slot, 0), xmm0); 3848 __ subq(current_float_param_slot, Immediate(kSystemPointerSize)); 3849 __ jmp(¶m_conversion_done); 3850 3851 // ------------------------------------------- 3852 // Return conversions. 3853 // ------------------------------------------- 3854 __ bind(&convert_return); 3855 // We have to make sure that the kGCScanSlotCount is set correctly when we 3856 // call the builtins for conversion. For these builtins it's the same as for 3857 // the Wasm call, that is, kGCScanSlotCount = 0, so we don't have to reset it. 3858 // We don't need the JS context for these builtin calls. 3859 3860 __ movq(valuetypes_array_ptr, MemOperand(rbp, kValueTypesArrayStartOffset)); 3861 // The first valuetype of the array is the return's valuetype. 3862 __ movl(valuetype, 3863 Operand(valuetypes_array_ptr, wasm::ValueType::bit_field_offset())); 3864 3865 Label return_kWasmI32; 3866 Label return_kWasmI64; 3867 Label return_kWasmF32; 3868 Label return_kWasmF64; 3869 Label return_kWasmFuncRef; 3870 3871 __ cmpq(valuetype, Immediate(wasm::kWasmI32.raw_bit_field())); 3872 __ j(equal, &return_kWasmI32); 3873 3874 __ cmpq(valuetype, Immediate(wasm::kWasmI64.raw_bit_field())); 3875 __ j(equal, &return_kWasmI64); 3876 3877 __ cmpq(valuetype, Immediate(wasm::kWasmF32.raw_bit_field())); 3878 __ j(equal, &return_kWasmF32); 3879 3880 __ cmpq(valuetype, Immediate(wasm::kWasmF64.raw_bit_field())); 3881 __ j(equal, &return_kWasmF64); 3882 3883 __ cmpq(valuetype, Immediate(wasm::kWasmFuncRef.raw_bit_field())); 3884 __ j(equal, &return_kWasmFuncRef); 3885 3886 // All types that are not SIMD are reference types. 3887 __ cmpq(valuetype, Immediate(wasm::kWasmS128.raw_bit_field())); 3888 // References can be passed to JavaScript as is. 3889 __ j(not_equal, &return_done); 3890 3891 __ int3(); 3892 3893 __ bind(&return_kWasmI32); 3894 Label to_heapnumber; 3895 // If pointer compression is disabled, we can convert the return to a smi. 3896 if (SmiValuesAre32Bits()) { 3897 __ SmiTag(return_reg); 3898 } else { 3899 Register temp = rbx; 3900 __ movq(temp, return_reg); 3901 // Double the return value to test if it can be a Smi. 3902 __ addl(temp, return_reg); 3903 temp = no_reg; 3904 // If there was overflow, convert the return value to a HeapNumber. 3905 __ j(overflow, &to_heapnumber); 3906 // If there was no overflow, we can convert to Smi. 3907 __ SmiTag(return_reg); 3908 } 3909 __ jmp(&return_done); 3910 3911 // Handle the conversion of the I32 return value to HeapNumber when it cannot 3912 // be a smi. 3913 __ bind(&to_heapnumber); 3914 __ Call(BUILTIN_CODE(masm->isolate(), WasmInt32ToHeapNumber), 3915 RelocInfo::CODE_TARGET); 3916 __ jmp(&return_done); 3917 3918 __ bind(&return_kWasmI64); 3919 __ Call(BUILTIN_CODE(masm->isolate(), I64ToBigInt), RelocInfo::CODE_TARGET); 3920 __ jmp(&return_done); 3921 3922 __ bind(&return_kWasmF32); 3923 // The builtin expects the value to be in xmm0. 3924 __ Movss(xmm0, xmm1); 3925 __ Call(BUILTIN_CODE(masm->isolate(), WasmFloat32ToNumber), 3926 RelocInfo::CODE_TARGET); 3927 __ jmp(&return_done); 3928 3929 __ bind(&return_kWasmF64); 3930 // The builtin expects the value to be in xmm0. 3931 __ Movsd(xmm0, xmm1); 3932 __ Call(BUILTIN_CODE(masm->isolate(), WasmFloat64ToNumber), 3933 RelocInfo::CODE_TARGET); 3934 __ jmp(&return_done); 3935 3936 __ bind(&return_kWasmFuncRef); 3937 __ Call(BUILTIN_CODE(masm->isolate(), WasmFuncRefToJS), 3938 RelocInfo::CODE_TARGET); 3939 __ jmp(&return_done); 3940 3941 if (!stack_switch) { 3942 // ------------------------------------------- 3943 // Kick off compilation. 3944 // ------------------------------------------- 3945 __ bind(&compile_wrapper); 3946 // Enable GC. 3947 MemOperand GCScanSlotPlace = MemOperand(rbp, kGCScanSlotCountOffset); 3948 __ Move(GCScanSlotPlace, 4); 3949 // Save registers to the stack. 3950 __ pushq(wasm_instance); 3951 __ pushq(function_data); 3952 // Push the arguments for the runtime call. 3953 __ Push(wasm_instance); // first argument 3954 __ Push(function_data); // second argument 3955 // Set up context. 3956 __ Move(kContextRegister, Smi::zero()); 3957 // Call the runtime function that kicks off compilation. 3958 __ CallRuntime(Runtime::kWasmCompileWrapper, 2); 3959 // Pop the result. 3960 __ movq(r9, kReturnRegister0); 3961 // Restore registers from the stack. 3962 __ popq(function_data); 3963 __ popq(wasm_instance); 3964 __ jmp(&compile_wrapper_done); 3965 } 3966} 3967} // namespace 3968 3969void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) { 3970 GenericJSToWasmWrapperHelper(masm, false); 3971} 3972 3973void Builtins::Generate_WasmReturnPromiseOnSuspend(MacroAssembler* masm) { 3974 GenericJSToWasmWrapperHelper(masm, true); 3975} 3976 3977void Builtins::Generate_WasmSuspend(MacroAssembler* masm) { 3978 // Set up the stackframe. 3979 __ EnterFrame(StackFrame::STACK_SWITCH); 3980 3981 Register promise = rax; 3982 Register suspender = rbx; 3983 3984 __ subq(rsp, Immediate(-(BuiltinWasmWrapperConstants::kGCScanSlotCountOffset - 3985 TypedFrameConstants::kFixedFrameSizeFromFp))); 3986 3987 // TODO(thibaudm): Throw if any of the following holds: 3988 // - caller is null 3989 // - ActiveSuspender is undefined 3990 // - 'suspender' is not the active suspender 3991 3992 // ------------------------------------------- 3993 // Save current state in active jump buffer. 3994 // ------------------------------------------- 3995 Label resume; 3996 Register continuation = rcx; 3997 __ LoadRoot(continuation, RootIndex::kActiveContinuation); 3998 Register jmpbuf = rdx; 3999 __ LoadAnyTaggedField( 4000 jmpbuf, 4001 FieldOperand(continuation, WasmContinuationObject::kJmpbufOffset)); 4002 __ LoadExternalPointerField( 4003 jmpbuf, FieldOperand(jmpbuf, Foreign::kForeignAddressOffset), 4004 kForeignForeignAddressTag, r8); 4005 FillJumpBuffer(masm, jmpbuf, &resume); 4006 __ StoreTaggedSignedField( 4007 FieldOperand(suspender, WasmSuspenderObject::kStateOffset), 4008 Smi::FromInt(WasmSuspenderObject::Suspended)); 4009 jmpbuf = no_reg; 4010 // live: [rax, rbx, rcx] 4011 4012#ifdef DEBUG 4013 // ------------------------------------------- 4014 // Check that the suspender's continuation is the active continuation. 4015 // ------------------------------------------- 4016 // TODO(thibaudm): Once we add core stack-switching instructions, this check 4017 // will not hold anymore: it's possible that the active continuation changed 4018 // (due to an internal switch), so we have to update the suspender. 4019 Register suspender_continuation = rdx; 4020 __ LoadAnyTaggedField( 4021 suspender_continuation, 4022 FieldOperand(suspender, WasmSuspenderObject::kContinuationOffset)); 4023 __ cmpq(suspender_continuation, continuation); 4024 Label ok; 4025 __ j(equal, &ok); 4026 __ Trap(); 4027 __ bind(&ok); 4028#endif 4029 4030 // ------------------------------------------- 4031 // Update roots. 4032 // ------------------------------------------- 4033 Register caller = rcx; 4034 __ LoadAnyTaggedField( 4035 caller, 4036 FieldOperand(suspender, WasmSuspenderObject::kContinuationOffset)); 4037 __ LoadAnyTaggedField( 4038 caller, FieldOperand(caller, WasmContinuationObject::kParentOffset)); 4039 __ movq(masm->RootAsOperand(RootIndex::kActiveContinuation), caller); 4040 Register parent = rdx; 4041 __ LoadAnyTaggedField( 4042 parent, FieldOperand(suspender, WasmSuspenderObject::kParentOffset)); 4043 __ movq(masm->RootAsOperand(RootIndex::kActiveSuspender), parent); 4044 parent = no_reg; 4045 // live: [rax, rcx] 4046 4047 // ------------------------------------------- 4048 // Load jump buffer. 4049 // ------------------------------------------- 4050 MemOperand GCScanSlotPlace = 4051 MemOperand(rbp, BuiltinWasmWrapperConstants::kGCScanSlotCountOffset); 4052 __ Move(GCScanSlotPlace, 2); 4053 __ Push(promise); 4054 __ Push(caller); 4055 __ Move(kContextRegister, Smi::zero()); 4056 __ CallRuntime(Runtime::kWasmSyncStackLimit); 4057 __ Pop(caller); 4058 __ Pop(promise); 4059 jmpbuf = caller; 4060 __ LoadAnyTaggedField( 4061 jmpbuf, FieldOperand(caller, WasmContinuationObject::kJmpbufOffset)); 4062 caller = no_reg; 4063 __ LoadExternalPointerField( 4064 jmpbuf, FieldOperand(jmpbuf, Foreign::kForeignAddressOffset), 4065 kForeignForeignAddressTag, r8); 4066 __ movq(kReturnRegister0, promise); 4067 __ Move(GCScanSlotPlace, 0); 4068 LoadJumpBuffer(masm, jmpbuf, true); 4069 __ Trap(); 4070 __ bind(&resume); 4071 __ LeaveFrame(StackFrame::STACK_SWITCH); 4072 __ ret(0); 4073} 4074 4075// Resume the suspender stored in the closure. 4076void Builtins::Generate_WasmResume(MacroAssembler* masm) { 4077 __ EnterFrame(StackFrame::STACK_SWITCH); 4078 4079 Register param_count = rax; 4080 __ decq(param_count); // Exclude receiver. 4081 Register closure = kJSFunctionRegister; // rdi 4082 4083 // These slots are not used in this builtin. But when we return from the 4084 // resumed continuation, we return to the GenericJSToWasmWrapper code, which 4085 // expects these slots to be set. 4086 constexpr int kInParamCountOffset = 4087 BuiltinWasmWrapperConstants::kInParamCountOffset; 4088 constexpr int kParamCountOffset = 4089 BuiltinWasmWrapperConstants::kParamCountOffset; 4090 __ subq(rsp, Immediate(3 * kSystemPointerSize)); 4091 __ movq(MemOperand(rbp, kParamCountOffset), param_count); 4092 __ movq(MemOperand(rbp, kInParamCountOffset), param_count); 4093 4094 param_count = no_reg; 4095 4096 // ------------------------------------------- 4097 // Load suspender from closure. 4098 // ------------------------------------------- 4099 Register sfi = closure; 4100 __ LoadAnyTaggedField( 4101 sfi, 4102 MemOperand( 4103 closure, 4104 wasm::ObjectAccess::SharedFunctionInfoOffsetInTaggedJSFunction())); 4105 Register function_data = sfi; 4106 __ LoadAnyTaggedField( 4107 function_data, 4108 FieldOperand(sfi, SharedFunctionInfo::kFunctionDataOffset)); 4109 Register suspender = rax; 4110 __ LoadAnyTaggedField( 4111 suspender, 4112 FieldOperand(function_data, WasmOnFulfilledData::kSuspenderOffset)); 4113 // Check the suspender state. 4114 Label suspender_is_suspended; 4115 Register state = rdx; 4116 __ LoadTaggedSignedField( 4117 state, FieldOperand(suspender, WasmSuspenderObject::kStateOffset)); 4118 __ SmiCompare(state, Smi::FromInt(WasmSuspenderObject::Suspended)); 4119 __ j(equal, &suspender_is_suspended); 4120 __ Trap(); // TODO(thibaudm): Throw a wasm trap. 4121 closure = no_reg; 4122 sfi = no_reg; 4123 4124 __ bind(&suspender_is_suspended); 4125 // ------------------------------------------- 4126 // Save current state. 4127 // ------------------------------------------- 4128 Label suspend; 4129 Register active_continuation = r9; 4130 __ LoadRoot(active_continuation, RootIndex::kActiveContinuation); 4131 Register current_jmpbuf = rdi; 4132 __ LoadAnyTaggedField( 4133 current_jmpbuf, 4134 FieldOperand(active_continuation, WasmContinuationObject::kJmpbufOffset)); 4135 __ LoadExternalPointerField( 4136 current_jmpbuf, 4137 FieldOperand(current_jmpbuf, Foreign::kForeignAddressOffset), 4138 kForeignForeignAddressTag, rdx); 4139 FillJumpBuffer(masm, current_jmpbuf, &suspend); 4140 current_jmpbuf = no_reg; 4141 4142 // ------------------------------------------- 4143 // Set suspender's parent to active continuation. 4144 // ------------------------------------------- 4145 __ StoreTaggedSignedField( 4146 FieldOperand(suspender, WasmSuspenderObject::kStateOffset), 4147 Smi::FromInt(WasmSuspenderObject::Active)); 4148 Register target_continuation = rdi; 4149 __ LoadAnyTaggedField( 4150 target_continuation, 4151 FieldOperand(suspender, WasmSuspenderObject::kContinuationOffset)); 4152 Register slot_address = WriteBarrierDescriptor::SlotAddressRegister(); 4153 __ StoreTaggedField( 4154 FieldOperand(target_continuation, WasmContinuationObject::kParentOffset), 4155 active_continuation); 4156 __ RecordWriteField( 4157 target_continuation, WasmContinuationObject::kParentOffset, 4158 active_continuation, slot_address, SaveFPRegsMode::kIgnore); 4159 active_continuation = no_reg; 4160 4161 // ------------------------------------------- 4162 // Update roots. 4163 // ------------------------------------------- 4164 __ movq(masm->RootAsOperand(RootIndex::kActiveContinuation), 4165 target_continuation); 4166 __ movq(masm->RootAsOperand(RootIndex::kActiveSuspender), suspender); 4167 suspender = no_reg; 4168 4169 MemOperand GCScanSlotPlace = 4170 MemOperand(rbp, BuiltinWasmWrapperConstants::kGCScanSlotCountOffset); 4171 __ Move(GCScanSlotPlace, 1); 4172 __ Push(target_continuation); 4173 __ Move(kContextRegister, Smi::zero()); 4174 __ CallRuntime(Runtime::kWasmSyncStackLimit); 4175 __ Pop(target_continuation); 4176 4177 // ------------------------------------------- 4178 // Load state from target jmpbuf (longjmp). 4179 // ------------------------------------------- 4180 Register target_jmpbuf = target_continuation; 4181 __ LoadAnyTaggedField( 4182 target_jmpbuf, 4183 FieldOperand(target_continuation, WasmContinuationObject::kJmpbufOffset)); 4184 __ LoadExternalPointerField( 4185 target_jmpbuf, 4186 FieldOperand(target_jmpbuf, Foreign::kForeignAddressOffset), 4187 kForeignForeignAddressTag, rax); 4188 // Move resolved value to return register. 4189 __ movq(kReturnRegister0, Operand(rbp, 3 * kSystemPointerSize)); 4190 __ Move(GCScanSlotPlace, 0); 4191 LoadJumpBuffer(masm, target_jmpbuf, true); 4192 __ Trap(); 4193 __ bind(&suspend); 4194 __ LeaveFrame(StackFrame::STACK_SWITCH); 4195 __ ret(3); 4196} 4197 4198void Builtins::Generate_WasmOnStackReplace(MacroAssembler* masm) { 4199 MemOperand OSRTargetSlot(rbp, -wasm::kOSRTargetOffset); 4200 __ movq(kScratchRegister, OSRTargetSlot); 4201 __ Move(OSRTargetSlot, 0); 4202 __ jmp(kScratchRegister); 4203} 4204 4205#endif // V8_ENABLE_WEBASSEMBLY 4206 4207void Builtins::Generate_CEntry(MacroAssembler* masm, int result_size, 4208 SaveFPRegsMode save_doubles, ArgvMode argv_mode, 4209 bool builtin_exit_frame) { 4210 // rax: number of arguments including receiver 4211 // rbx: pointer to C function (C callee-saved) 4212 // rbp: frame pointer of calling JS frame (restored after C call) 4213 // rsp: stack pointer (restored after C call) 4214 // rsi: current context (restored) 4215 // 4216 // If argv_mode == ArgvMode::kRegister: 4217 // r15: pointer to the first argument 4218 4219#ifdef V8_TARGET_OS_WIN 4220 // Windows 64-bit ABI passes arguments in rcx, rdx, r8, r9. It requires the 4221 // stack to be aligned to 16 bytes. It only allows a single-word to be 4222 // returned in register rax. Larger return sizes must be written to an address 4223 // passed as a hidden first argument. 4224 const Register kCCallArg0 = rcx; 4225 const Register kCCallArg1 = rdx; 4226 const Register kCCallArg2 = r8; 4227 const Register kCCallArg3 = r9; 4228 const int kArgExtraStackSpace = 2; 4229 const int kMaxRegisterResultSize = 1; 4230#else 4231 // GCC / Clang passes arguments in rdi, rsi, rdx, rcx, r8, r9. Simple results 4232 // are returned in rax, and a struct of two pointers are returned in rax+rdx. 4233 // Larger return sizes must be written to an address passed as a hidden first 4234 // argument. 4235 const Register kCCallArg0 = rdi; 4236 const Register kCCallArg1 = rsi; 4237 const Register kCCallArg2 = rdx; 4238 const Register kCCallArg3 = rcx; 4239 const int kArgExtraStackSpace = 0; 4240 const int kMaxRegisterResultSize = 2; 4241#endif // V8_TARGET_OS_WIN 4242 4243 // Enter the exit frame that transitions from JavaScript to C++. 4244 int arg_stack_space = 4245 kArgExtraStackSpace + 4246 (result_size <= kMaxRegisterResultSize ? 0 : result_size); 4247 if (argv_mode == ArgvMode::kRegister) { 4248 DCHECK(save_doubles == SaveFPRegsMode::kIgnore); 4249 DCHECK(!builtin_exit_frame); 4250 __ EnterApiExitFrame(arg_stack_space); 4251 // Move argc into r12 (argv is already in r15). 4252 __ movq(r12, rax); 4253 } else { 4254 __ EnterExitFrame( 4255 arg_stack_space, save_doubles == SaveFPRegsMode::kSave, 4256 builtin_exit_frame ? StackFrame::BUILTIN_EXIT : StackFrame::EXIT); 4257 } 4258 4259 // rbx: pointer to builtin function (C callee-saved). 4260 // rbp: frame pointer of exit frame (restored after C call). 4261 // rsp: stack pointer (restored after C call). 4262 // r12: number of arguments including receiver (C callee-saved). 4263 // r15: argv pointer (C callee-saved). 4264 4265 // Check stack alignment. 4266 if (FLAG_debug_code) { 4267 __ CheckStackAlignment(); 4268 } 4269 4270 // Call C function. The arguments object will be created by stubs declared by 4271 // DECLARE_RUNTIME_FUNCTION(). 4272 if (result_size <= kMaxRegisterResultSize) { 4273 // Pass a pointer to the Arguments object as the first argument. 4274 // Return result in single register (rax), or a register pair (rax, rdx). 4275 __ movq(kCCallArg0, r12); // argc. 4276 __ movq(kCCallArg1, r15); // argv. 4277 __ Move(kCCallArg2, ExternalReference::isolate_address(masm->isolate())); 4278 } else { 4279 DCHECK_LE(result_size, 2); 4280 // Pass a pointer to the result location as the first argument. 4281 __ leaq(kCCallArg0, StackSpaceOperand(kArgExtraStackSpace)); 4282 // Pass a pointer to the Arguments object as the second argument. 4283 __ movq(kCCallArg1, r12); // argc. 4284 __ movq(kCCallArg2, r15); // argv. 4285 __ Move(kCCallArg3, ExternalReference::isolate_address(masm->isolate())); 4286 } 4287 __ call(rbx); 4288 4289 if (result_size > kMaxRegisterResultSize) { 4290 // Read result values stored on stack. Result is stored 4291 // above the the two Arguments object slots on Win64. 4292 DCHECK_LE(result_size, 2); 4293 __ movq(kReturnRegister0, StackSpaceOperand(kArgExtraStackSpace + 0)); 4294 __ movq(kReturnRegister1, StackSpaceOperand(kArgExtraStackSpace + 1)); 4295 } 4296 // Result is in rax or rdx:rax - do not destroy these registers! 4297 4298 // Check result for exception sentinel. 4299 Label exception_returned; 4300 __ CompareRoot(rax, RootIndex::kException); 4301 __ j(equal, &exception_returned); 4302 4303 // Check that there is no pending exception, otherwise we 4304 // should have returned the exception sentinel. 4305 if (FLAG_debug_code) { 4306 Label okay; 4307 __ LoadRoot(kScratchRegister, RootIndex::kTheHoleValue); 4308 ExternalReference pending_exception_address = ExternalReference::Create( 4309 IsolateAddressId::kPendingExceptionAddress, masm->isolate()); 4310 Operand pending_exception_operand = 4311 masm->ExternalReferenceAsOperand(pending_exception_address); 4312 __ cmp_tagged(kScratchRegister, pending_exception_operand); 4313 __ j(equal, &okay, Label::kNear); 4314 __ int3(); 4315 __ bind(&okay); 4316 } 4317 4318 // Exit the JavaScript to C++ exit frame. 4319 __ LeaveExitFrame(save_doubles == SaveFPRegsMode::kSave, 4320 argv_mode == ArgvMode::kStack); 4321 __ ret(0); 4322 4323 // Handling of exception. 4324 __ bind(&exception_returned); 4325 4326 ExternalReference pending_handler_context_address = ExternalReference::Create( 4327 IsolateAddressId::kPendingHandlerContextAddress, masm->isolate()); 4328 ExternalReference pending_handler_entrypoint_address = 4329 ExternalReference::Create( 4330 IsolateAddressId::kPendingHandlerEntrypointAddress, masm->isolate()); 4331 ExternalReference pending_handler_fp_address = ExternalReference::Create( 4332 IsolateAddressId::kPendingHandlerFPAddress, masm->isolate()); 4333 ExternalReference pending_handler_sp_address = ExternalReference::Create( 4334 IsolateAddressId::kPendingHandlerSPAddress, masm->isolate()); 4335 4336 // Ask the runtime for help to determine the handler. This will set rax to 4337 // contain the current pending exception, don't clobber it. 4338 ExternalReference find_handler = 4339 ExternalReference::Create(Runtime::kUnwindAndFindExceptionHandler); 4340 { 4341 FrameScope scope(masm, StackFrame::MANUAL); 4342 __ Move(arg_reg_1, 0); // argc. 4343 __ Move(arg_reg_2, 0); // argv. 4344 __ Move(arg_reg_3, ExternalReference::isolate_address(masm->isolate())); 4345 __ PrepareCallCFunction(3); 4346 __ CallCFunction(find_handler, 3); 4347 } 4348 4349#ifdef V8_ENABLE_CET_SHADOW_STACK 4350 // Drop frames from the shadow stack. 4351 ExternalReference num_frames_above_pending_handler_address = 4352 ExternalReference::Create( 4353 IsolateAddressId::kNumFramesAbovePendingHandlerAddress, 4354 masm->isolate()); 4355 __ movq(rcx, masm->ExternalReferenceAsOperand( 4356 num_frames_above_pending_handler_address)); 4357 __ IncsspqIfSupported(rcx, kScratchRegister); 4358#endif // V8_ENABLE_CET_SHADOW_STACK 4359 4360 // Retrieve the handler context, SP and FP. 4361 __ movq(rsi, 4362 masm->ExternalReferenceAsOperand(pending_handler_context_address)); 4363 __ movq(rsp, masm->ExternalReferenceAsOperand(pending_handler_sp_address)); 4364 __ movq(rbp, masm->ExternalReferenceAsOperand(pending_handler_fp_address)); 4365 4366 // If the handler is a JS frame, restore the context to the frame. Note that 4367 // the context will be set to (rsi == 0) for non-JS frames. 4368 Label skip; 4369 __ testq(rsi, rsi); 4370 __ j(zero, &skip, Label::kNear); 4371 __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi); 4372 __ bind(&skip); 4373 4374 // Clear c_entry_fp, like we do in `LeaveExitFrame`. 4375 ExternalReference c_entry_fp_address = ExternalReference::Create( 4376 IsolateAddressId::kCEntryFPAddress, masm->isolate()); 4377 Operand c_entry_fp_operand = 4378 masm->ExternalReferenceAsOperand(c_entry_fp_address); 4379 __ movq(c_entry_fp_operand, Immediate(0)); 4380 4381 // Compute the handler entry address and jump to it. 4382 __ movq(rdi, 4383 masm->ExternalReferenceAsOperand(pending_handler_entrypoint_address)); 4384 __ jmp(rdi); 4385} 4386 4387void Builtins::Generate_DoubleToI(MacroAssembler* masm) { 4388 Label check_negative, process_64_bits, done; 4389 4390 // Account for return address and saved regs. 4391 const int kArgumentOffset = 4 * kSystemPointerSize; 4392 4393 MemOperand mantissa_operand(MemOperand(rsp, kArgumentOffset)); 4394 MemOperand exponent_operand( 4395 MemOperand(rsp, kArgumentOffset + kDoubleSize / 2)); 4396 4397 // The result is returned on the stack. 4398 MemOperand return_operand = mantissa_operand; 4399 4400 Register scratch1 = rbx; 4401 4402 // Since we must use rcx for shifts below, use some other register (rax) 4403 // to calculate the result if ecx is the requested return register. 4404 Register result_reg = rax; 4405 // Save ecx if it isn't the return register and therefore volatile, or if it 4406 // is the return register, then save the temp register we use in its stead 4407 // for the result. 4408 Register save_reg = rax; 4409 __ pushq(rcx); 4410 __ pushq(scratch1); 4411 __ pushq(save_reg); 4412 4413 __ movl(scratch1, mantissa_operand); 4414 __ Movsd(kScratchDoubleReg, mantissa_operand); 4415 __ movl(rcx, exponent_operand); 4416 4417 __ andl(rcx, Immediate(HeapNumber::kExponentMask)); 4418 __ shrl(rcx, Immediate(HeapNumber::kExponentShift)); 4419 __ leal(result_reg, MemOperand(rcx, -HeapNumber::kExponentBias)); 4420 __ cmpl(result_reg, Immediate(HeapNumber::kMantissaBits)); 4421 __ j(below, &process_64_bits, Label::kNear); 4422 4423 // Result is entirely in lower 32-bits of mantissa 4424 int delta = 4425 HeapNumber::kExponentBias + base::Double::kPhysicalSignificandSize; 4426 __ subl(rcx, Immediate(delta)); 4427 __ xorl(result_reg, result_reg); 4428 __ cmpl(rcx, Immediate(31)); 4429 __ j(above, &done, Label::kNear); 4430 __ shll_cl(scratch1); 4431 __ jmp(&check_negative, Label::kNear); 4432 4433 __ bind(&process_64_bits); 4434 __ Cvttsd2siq(result_reg, kScratchDoubleReg); 4435 __ jmp(&done, Label::kNear); 4436 4437 // If the double was negative, negate the integer result. 4438 __ bind(&check_negative); 4439 __ movl(result_reg, scratch1); 4440 __ negl(result_reg); 4441 __ cmpl(exponent_operand, Immediate(0)); 4442 __ cmovl(greater, result_reg, scratch1); 4443 4444 // Restore registers 4445 __ bind(&done); 4446 __ movl(return_operand, result_reg); 4447 __ popq(save_reg); 4448 __ popq(scratch1); 4449 __ popq(rcx); 4450 __ ret(0); 4451} 4452 4453namespace { 4454 4455int Offset(ExternalReference ref0, ExternalReference ref1) { 4456 int64_t offset = (ref0.address() - ref1.address()); 4457 // Check that fits into int. 4458 DCHECK(static_cast<int>(offset) == offset); 4459 return static_cast<int>(offset); 4460} 4461 4462// Calls an API function. Allocates HandleScope, extracts returned value 4463// from handle and propagates exceptions. Clobbers r12, r15, rbx and 4464// caller-save registers. Restores context. On return removes 4465// stack_space * kSystemPointerSize (GCed). 4466void CallApiFunctionAndReturn(MacroAssembler* masm, Register function_address, 4467 ExternalReference thunk_ref, 4468 Register thunk_last_arg, int stack_space, 4469 Operand* stack_space_operand, 4470 Operand return_value_operand) { 4471 Label prologue; 4472 Label promote_scheduled_exception; 4473 Label delete_allocated_handles; 4474 Label leave_exit_frame; 4475 4476 Isolate* isolate = masm->isolate(); 4477 Factory* factory = isolate->factory(); 4478 ExternalReference next_address = 4479 ExternalReference::handle_scope_next_address(isolate); 4480 const int kNextOffset = 0; 4481 const int kLimitOffset = Offset( 4482 ExternalReference::handle_scope_limit_address(isolate), next_address); 4483 const int kLevelOffset = Offset( 4484 ExternalReference::handle_scope_level_address(isolate), next_address); 4485 ExternalReference scheduled_exception_address = 4486 ExternalReference::scheduled_exception_address(isolate); 4487 4488 DCHECK(rdx == function_address || r8 == function_address); 4489 // Allocate HandleScope in callee-save registers. 4490 Register prev_next_address_reg = r12; 4491 Register prev_limit_reg = rbx; 4492 Register base_reg = r15; 4493 __ Move(base_reg, next_address); 4494 __ movq(prev_next_address_reg, Operand(base_reg, kNextOffset)); 4495 __ movq(prev_limit_reg, Operand(base_reg, kLimitOffset)); 4496 __ addl(Operand(base_reg, kLevelOffset), Immediate(1)); 4497 4498 Label profiler_enabled, end_profiler_check; 4499 __ Move(rax, ExternalReference::is_profiling_address(isolate)); 4500 __ cmpb(Operand(rax, 0), Immediate(0)); 4501 __ j(not_zero, &profiler_enabled); 4502 __ Move(rax, ExternalReference::address_of_runtime_stats_flag()); 4503 __ cmpl(Operand(rax, 0), Immediate(0)); 4504 __ j(not_zero, &profiler_enabled); 4505 { 4506 // Call the api function directly. 4507 __ Move(rax, function_address); 4508 __ jmp(&end_profiler_check); 4509 } 4510 __ bind(&profiler_enabled); 4511 { 4512 // Third parameter is the address of the actual getter function. 4513 __ Move(thunk_last_arg, function_address); 4514 __ Move(rax, thunk_ref); 4515 } 4516 __ bind(&end_profiler_check); 4517 4518 // Call the api function! 4519 __ call(rax); 4520 4521 // Load the value from ReturnValue 4522 __ movq(rax, return_value_operand); 4523 __ bind(&prologue); 4524 4525 // No more valid handles (the result handle was the last one). Restore 4526 // previous handle scope. 4527 __ subl(Operand(base_reg, kLevelOffset), Immediate(1)); 4528 __ movq(Operand(base_reg, kNextOffset), prev_next_address_reg); 4529 __ cmpq(prev_limit_reg, Operand(base_reg, kLimitOffset)); 4530 __ j(not_equal, &delete_allocated_handles); 4531 4532 // Leave the API exit frame. 4533 __ bind(&leave_exit_frame); 4534 if (stack_space_operand != nullptr) { 4535 DCHECK_EQ(stack_space, 0); 4536 __ movq(rbx, *stack_space_operand); 4537 } 4538 __ LeaveApiExitFrame(); 4539 4540 // Check if the function scheduled an exception. 4541 __ Move(rdi, scheduled_exception_address); 4542 __ Cmp(Operand(rdi, 0), factory->the_hole_value()); 4543 __ j(not_equal, &promote_scheduled_exception); 4544 4545#if DEBUG 4546 // Check if the function returned a valid JavaScript value. 4547 Label ok; 4548 Register return_value = rax; 4549 Register map = rcx; 4550 4551 __ JumpIfSmi(return_value, &ok, Label::kNear); 4552 __ LoadMap(map, return_value); 4553 __ CmpInstanceType(map, LAST_NAME_TYPE); 4554 __ j(below_equal, &ok, Label::kNear); 4555 4556 __ CmpInstanceType(map, FIRST_JS_RECEIVER_TYPE); 4557 __ j(above_equal, &ok, Label::kNear); 4558 4559 __ CompareRoot(map, RootIndex::kHeapNumberMap); 4560 __ j(equal, &ok, Label::kNear); 4561 4562 __ CompareRoot(map, RootIndex::kBigIntMap); 4563 __ j(equal, &ok, Label::kNear); 4564 4565 __ CompareRoot(return_value, RootIndex::kUndefinedValue); 4566 __ j(equal, &ok, Label::kNear); 4567 4568 __ CompareRoot(return_value, RootIndex::kTrueValue); 4569 __ j(equal, &ok, Label::kNear); 4570 4571 __ CompareRoot(return_value, RootIndex::kFalseValue); 4572 __ j(equal, &ok, Label::kNear); 4573 4574 __ CompareRoot(return_value, RootIndex::kNullValue); 4575 __ j(equal, &ok, Label::kNear); 4576 4577 __ Abort(AbortReason::kAPICallReturnedInvalidObject); 4578 4579 __ bind(&ok); 4580#endif 4581 4582 if (stack_space_operand == nullptr) { 4583 DCHECK_NE(stack_space, 0); 4584 __ ret(stack_space * kSystemPointerSize); 4585 } else { 4586 DCHECK_EQ(stack_space, 0); 4587 __ PopReturnAddressTo(rcx); 4588 // {stack_space_operand} was loaded into {rbx} above. 4589 __ addq(rsp, rbx); 4590 // Push and ret (instead of jmp) to keep the RSB and the CET shadow stack 4591 // balanced. 4592 __ PushReturnAddressFrom(rcx); 4593 __ ret(0); 4594 } 4595 4596 // Re-throw by promoting a scheduled exception. 4597 __ bind(&promote_scheduled_exception); 4598 __ TailCallRuntime(Runtime::kPromoteScheduledException); 4599 4600 // HandleScope limit has changed. Delete allocated extensions. 4601 __ bind(&delete_allocated_handles); 4602 __ movq(Operand(base_reg, kLimitOffset), prev_limit_reg); 4603 __ movq(prev_limit_reg, rax); 4604 __ LoadAddress(arg_reg_1, ExternalReference::isolate_address(isolate)); 4605 __ LoadAddress(rax, ExternalReference::delete_handle_scope_extensions()); 4606 __ call(rax); 4607 __ movq(rax, prev_limit_reg); 4608 __ jmp(&leave_exit_frame); 4609} 4610 4611} // namespace 4612 4613// TODO(jgruber): Instead of explicitly setting up implicit_args_ on the stack 4614// in CallApiCallback, we could use the calling convention to set up the stack 4615// correctly in the first place. 4616// 4617// TODO(jgruber): I suspect that most of CallApiCallback could be implemented 4618// as a C++ trampoline, vastly simplifying the assembly implementation. 4619 4620void Builtins::Generate_CallApiCallback(MacroAssembler* masm) { 4621 // ----------- S t a t e ------------- 4622 // -- rsi : context 4623 // -- rdx : api function address 4624 // -- rcx : arguments count (not including the receiver) 4625 // -- rbx : call data 4626 // -- rdi : holder 4627 // -- rsp[0] : return address 4628 // -- rsp[8] : argument 0 (receiver) 4629 // -- rsp[16] : argument 1 4630 // -- ... 4631 // -- rsp[argc * 8] : argument (argc - 1) 4632 // -- rsp[(argc + 1) * 8] : argument argc 4633 // ----------------------------------- 4634 4635 Register api_function_address = rdx; 4636 Register argc = rcx; 4637 Register call_data = rbx; 4638 Register holder = rdi; 4639 4640 DCHECK(!AreAliased(api_function_address, argc, holder, call_data, 4641 kScratchRegister)); 4642 4643 using FCA = FunctionCallbackArguments; 4644 4645 STATIC_ASSERT(FCA::kArgsLength == 6); 4646 STATIC_ASSERT(FCA::kNewTargetIndex == 5); 4647 STATIC_ASSERT(FCA::kDataIndex == 4); 4648 STATIC_ASSERT(FCA::kReturnValueOffset == 3); 4649 STATIC_ASSERT(FCA::kReturnValueDefaultValueIndex == 2); 4650 STATIC_ASSERT(FCA::kIsolateIndex == 1); 4651 STATIC_ASSERT(FCA::kHolderIndex == 0); 4652 4653 // Set up FunctionCallbackInfo's implicit_args on the stack as follows: 4654 // 4655 // Current state: 4656 // rsp[0]: return address 4657 // 4658 // Target state: 4659 // rsp[0 * kSystemPointerSize]: return address 4660 // rsp[1 * kSystemPointerSize]: kHolder 4661 // rsp[2 * kSystemPointerSize]: kIsolate 4662 // rsp[3 * kSystemPointerSize]: undefined (kReturnValueDefaultValue) 4663 // rsp[4 * kSystemPointerSize]: undefined (kReturnValue) 4664 // rsp[5 * kSystemPointerSize]: kData 4665 // rsp[6 * kSystemPointerSize]: undefined (kNewTarget) 4666 4667 __ PopReturnAddressTo(rax); 4668 __ LoadRoot(kScratchRegister, RootIndex::kUndefinedValue); 4669 __ Push(kScratchRegister); 4670 __ Push(call_data); 4671 __ Push(kScratchRegister); 4672 __ Push(kScratchRegister); 4673 __ PushAddress(ExternalReference::isolate_address(masm->isolate())); 4674 __ Push(holder); 4675 __ PushReturnAddressFrom(rax); 4676 4677 // Keep a pointer to kHolder (= implicit_args) in a scratch register. 4678 // We use it below to set up the FunctionCallbackInfo object. 4679 Register scratch = rbx; 4680 __ leaq(scratch, Operand(rsp, 1 * kSystemPointerSize)); 4681 4682 // Allocate the v8::Arguments structure in the arguments' space since 4683 // it's not controlled by GC. 4684 static constexpr int kApiStackSpace = 4; 4685 __ EnterApiExitFrame(kApiStackSpace); 4686 4687 // FunctionCallbackInfo::implicit_args_ (points at kHolder as set up above). 4688 __ movq(StackSpaceOperand(0), scratch); 4689 4690 // FunctionCallbackInfo::values_ (points at the first varargs argument passed 4691 // on the stack). 4692 __ leaq(scratch, 4693 Operand(scratch, (FCA::kArgsLength + 1) * kSystemPointerSize)); 4694 __ movq(StackSpaceOperand(1), scratch); 4695 4696 // FunctionCallbackInfo::length_. 4697 __ movq(StackSpaceOperand(2), argc); 4698 4699 // We also store the number of bytes to drop from the stack after returning 4700 // from the API function here. 4701 __ leaq(kScratchRegister, 4702 Operand(argc, times_system_pointer_size, 4703 (FCA::kArgsLength + 1 /* receiver */) * kSystemPointerSize)); 4704 __ movq(StackSpaceOperand(3), kScratchRegister); 4705 4706 Register arguments_arg = arg_reg_1; 4707 Register callback_arg = arg_reg_2; 4708 4709 // It's okay if api_function_address == callback_arg 4710 // but not arguments_arg 4711 DCHECK(api_function_address != arguments_arg); 4712 4713 // v8::InvocationCallback's argument. 4714 __ leaq(arguments_arg, StackSpaceOperand(0)); 4715 4716 ExternalReference thunk_ref = ExternalReference::invoke_function_callback(); 4717 4718 // There are two stack slots above the arguments we constructed on the stack: 4719 // the stored ebp (pushed by EnterApiExitFrame), and the return address. 4720 static constexpr int kStackSlotsAboveFCA = 2; 4721 Operand return_value_operand( 4722 rbp, 4723 (kStackSlotsAboveFCA + FCA::kReturnValueOffset) * kSystemPointerSize); 4724 4725 static constexpr int kUseStackSpaceOperand = 0; 4726 Operand stack_space_operand = StackSpaceOperand(3); 4727 CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, callback_arg, 4728 kUseStackSpaceOperand, &stack_space_operand, 4729 return_value_operand); 4730} 4731 4732void Builtins::Generate_CallApiGetter(MacroAssembler* masm) { 4733 Register name_arg = arg_reg_1; 4734 Register accessor_info_arg = arg_reg_2; 4735 Register getter_arg = arg_reg_3; 4736 Register api_function_address = r8; 4737 Register receiver = ApiGetterDescriptor::ReceiverRegister(); 4738 Register holder = ApiGetterDescriptor::HolderRegister(); 4739 Register callback = ApiGetterDescriptor::CallbackRegister(); 4740 Register scratch = rax; 4741 Register decompr_scratch1 = COMPRESS_POINTERS_BOOL ? r15 : no_reg; 4742 4743 DCHECK(!AreAliased(receiver, holder, callback, scratch, decompr_scratch1)); 4744 4745 // Build v8::PropertyCallbackInfo::args_ array on the stack and push property 4746 // name below the exit frame to make GC aware of them. 4747 STATIC_ASSERT(PropertyCallbackArguments::kShouldThrowOnErrorIndex == 0); 4748 STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 1); 4749 STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 2); 4750 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 3); 4751 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 4); 4752 STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 5); 4753 STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 6); 4754 STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 7); 4755 4756 // Insert additional parameters into the stack frame above return address. 4757 __ PopReturnAddressTo(scratch); 4758 __ Push(receiver); 4759 __ PushTaggedAnyField(FieldOperand(callback, AccessorInfo::kDataOffset), 4760 decompr_scratch1); 4761 __ LoadRoot(kScratchRegister, RootIndex::kUndefinedValue); 4762 __ Push(kScratchRegister); // return value 4763 __ Push(kScratchRegister); // return value default 4764 __ PushAddress(ExternalReference::isolate_address(masm->isolate())); 4765 __ Push(holder); 4766 __ Push(Smi::zero()); // should_throw_on_error -> false 4767 __ PushTaggedPointerField(FieldOperand(callback, AccessorInfo::kNameOffset), 4768 decompr_scratch1); 4769 __ PushReturnAddressFrom(scratch); 4770 4771 // v8::PropertyCallbackInfo::args_ array and name handle. 4772 const int kStackUnwindSpace = PropertyCallbackArguments::kArgsLength + 1; 4773 4774 // Allocate v8::PropertyCallbackInfo in non-GCed stack space. 4775 const int kArgStackSpace = 1; 4776 4777 // Load address of v8::PropertyAccessorInfo::args_ array. 4778 __ leaq(scratch, Operand(rsp, 2 * kSystemPointerSize)); 4779 4780 __ EnterApiExitFrame(kArgStackSpace); 4781 4782 // Create v8::PropertyCallbackInfo object on the stack and initialize 4783 // it's args_ field. 4784 Operand info_object = StackSpaceOperand(0); 4785 __ movq(info_object, scratch); 4786 4787 __ leaq(name_arg, Operand(scratch, -kSystemPointerSize)); 4788 // The context register (rsi) has been saved in EnterApiExitFrame and 4789 // could be used to pass arguments. 4790 __ leaq(accessor_info_arg, info_object); 4791 4792 ExternalReference thunk_ref = 4793 ExternalReference::invoke_accessor_getter_callback(); 4794 4795 // It's okay if api_function_address == getter_arg 4796 // but not accessor_info_arg or name_arg 4797 DCHECK(api_function_address != accessor_info_arg); 4798 DCHECK(api_function_address != name_arg); 4799 __ LoadTaggedPointerField( 4800 scratch, FieldOperand(callback, AccessorInfo::kJsGetterOffset)); 4801 __ LoadExternalPointerField( 4802 api_function_address, 4803 FieldOperand(scratch, Foreign::kForeignAddressOffset), 4804 kForeignForeignAddressTag, kScratchRegister); 4805 4806 // +3 is to skip prolog, return address and name handle. 4807 Operand return_value_operand( 4808 rbp, 4809 (PropertyCallbackArguments::kReturnValueOffset + 3) * kSystemPointerSize); 4810 Operand* const kUseStackSpaceConstant = nullptr; 4811 CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, getter_arg, 4812 kStackUnwindSpace, kUseStackSpaceConstant, 4813 return_value_operand); 4814} 4815 4816void Builtins::Generate_DirectCEntry(MacroAssembler* masm) { 4817 __ int3(); // Unused on this architecture. 4818} 4819 4820namespace { 4821 4822void Generate_DeoptimizationEntry(MacroAssembler* masm, 4823 DeoptimizeKind deopt_kind) { 4824 Isolate* isolate = masm->isolate(); 4825 4826 // Save all double registers, they will later be copied to the deoptimizer's 4827 // FrameDescription. 4828 static constexpr int kDoubleRegsSize = 4829 kDoubleSize * XMMRegister::kNumRegisters; 4830 __ AllocateStackSpace(kDoubleRegsSize); 4831 4832 const RegisterConfiguration* config = RegisterConfiguration::Default(); 4833 for (int i = 0; i < config->num_allocatable_double_registers(); ++i) { 4834 int code = config->GetAllocatableDoubleCode(i); 4835 XMMRegister xmm_reg = XMMRegister::from_code(code); 4836 int offset = code * kDoubleSize; 4837 __ Movsd(Operand(rsp, offset), xmm_reg); 4838 } 4839 4840 // Save all general purpose registers, they will later be copied to the 4841 // deoptimizer's FrameDescription. 4842 static constexpr int kNumberOfRegisters = Register::kNumRegisters; 4843 for (int i = 0; i < kNumberOfRegisters; i++) { 4844 __ pushq(Register::from_code(i)); 4845 } 4846 4847 static constexpr int kSavedRegistersAreaSize = 4848 kNumberOfRegisters * kSystemPointerSize + kDoubleRegsSize; 4849 static constexpr int kCurrentOffsetToReturnAddress = kSavedRegistersAreaSize; 4850 static constexpr int kCurrentOffsetToParentSP = 4851 kCurrentOffsetToReturnAddress + kPCOnStackSize; 4852 4853 __ Store( 4854 ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate), 4855 rbp); 4856 4857 // Get the address of the location in the code object 4858 // and compute the fp-to-sp delta in register arg5. 4859 __ movq(arg_reg_3, Operand(rsp, kCurrentOffsetToReturnAddress)); 4860 // Load the fp-to-sp-delta. 4861 __ leaq(arg_reg_4, Operand(rsp, kCurrentOffsetToParentSP)); 4862 __ subq(arg_reg_4, rbp); 4863 __ negq(arg_reg_4); 4864 4865 // Allocate a new deoptimizer object. 4866 __ PrepareCallCFunction(5); 4867 __ Move(rax, 0); 4868 Label context_check; 4869 __ movq(rdi, Operand(rbp, CommonFrameConstants::kContextOrFrameTypeOffset)); 4870 __ JumpIfSmi(rdi, &context_check); 4871 __ movq(rax, Operand(rbp, StandardFrameConstants::kFunctionOffset)); 4872 __ bind(&context_check); 4873 __ movq(arg_reg_1, rax); 4874 __ Move(arg_reg_2, static_cast<int>(deopt_kind)); 4875 // Args 3 and 4 are already in the right registers. 4876 4877 // On windows put the arguments on the stack (PrepareCallCFunction 4878 // has created space for this). On linux pass the arguments in r8. 4879#ifdef V8_TARGET_OS_WIN 4880 Register arg5 = r15; 4881 __ LoadAddress(arg5, ExternalReference::isolate_address(isolate)); 4882 __ movq(Operand(rsp, 4 * kSystemPointerSize), arg5); 4883#else 4884 // r8 is arg_reg_5 on Linux 4885 __ LoadAddress(r8, ExternalReference::isolate_address(isolate)); 4886#endif 4887 4888 { 4889 AllowExternalCallThatCantCauseGC scope(masm); 4890 __ CallCFunction(ExternalReference::new_deoptimizer_function(), 5); 4891 } 4892 // Preserve deoptimizer object in register rax and get the input 4893 // frame descriptor pointer. 4894 __ movq(rbx, Operand(rax, Deoptimizer::input_offset())); 4895 4896 // Fill in the input registers. 4897 for (int i = kNumberOfRegisters - 1; i >= 0; i--) { 4898 int offset = 4899 (i * kSystemPointerSize) + FrameDescription::registers_offset(); 4900 __ PopQuad(Operand(rbx, offset)); 4901 } 4902 4903 // Fill in the double input registers. 4904 int double_regs_offset = FrameDescription::double_registers_offset(); 4905 for (int i = 0; i < XMMRegister::kNumRegisters; i++) { 4906 int dst_offset = i * kDoubleSize + double_regs_offset; 4907 __ popq(Operand(rbx, dst_offset)); 4908 } 4909 4910 // Mark the stack as not iterable for the CPU profiler which won't be able to 4911 // walk the stack without the return address. 4912 __ movb(__ ExternalReferenceAsOperand( 4913 ExternalReference::stack_is_iterable_address(isolate)), 4914 Immediate(0)); 4915 4916 // Remove the return address from the stack. 4917 __ addq(rsp, Immediate(kPCOnStackSize)); 4918 4919 // Compute a pointer to the unwinding limit in register rcx; that is 4920 // the first stack slot not part of the input frame. 4921 __ movq(rcx, Operand(rbx, FrameDescription::frame_size_offset())); 4922 __ addq(rcx, rsp); 4923 4924 // Unwind the stack down to - but not including - the unwinding 4925 // limit and copy the contents of the activation frame to the input 4926 // frame description. 4927 __ leaq(rdx, Operand(rbx, FrameDescription::frame_content_offset())); 4928 Label pop_loop_header; 4929 __ jmp(&pop_loop_header); 4930 Label pop_loop; 4931 __ bind(&pop_loop); 4932 __ Pop(Operand(rdx, 0)); 4933 __ addq(rdx, Immediate(sizeof(intptr_t))); 4934 __ bind(&pop_loop_header); 4935 __ cmpq(rcx, rsp); 4936 __ j(not_equal, &pop_loop); 4937 4938 // Compute the output frame in the deoptimizer. 4939 __ pushq(rax); 4940 __ PrepareCallCFunction(2); 4941 __ movq(arg_reg_1, rax); 4942 __ LoadAddress(arg_reg_2, ExternalReference::isolate_address(isolate)); 4943 { 4944 AllowExternalCallThatCantCauseGC scope(masm); 4945 __ CallCFunction(ExternalReference::compute_output_frames_function(), 2); 4946 } 4947 __ popq(rax); 4948 4949 __ movq(rsp, Operand(rax, Deoptimizer::caller_frame_top_offset())); 4950 4951 // Replace the current (input) frame with the output frames. 4952 Label outer_push_loop, inner_push_loop, outer_loop_header, inner_loop_header; 4953 // Outer loop state: rax = current FrameDescription**, rdx = one past the 4954 // last FrameDescription**. 4955 __ movl(rdx, Operand(rax, Deoptimizer::output_count_offset())); 4956 __ movq(rax, Operand(rax, Deoptimizer::output_offset())); 4957 __ leaq(rdx, Operand(rax, rdx, times_system_pointer_size, 0)); 4958 __ jmp(&outer_loop_header); 4959 __ bind(&outer_push_loop); 4960 // Inner loop state: rbx = current FrameDescription*, rcx = loop index. 4961 __ movq(rbx, Operand(rax, 0)); 4962 __ movq(rcx, Operand(rbx, FrameDescription::frame_size_offset())); 4963 __ jmp(&inner_loop_header); 4964 __ bind(&inner_push_loop); 4965 __ subq(rcx, Immediate(sizeof(intptr_t))); 4966 __ Push(Operand(rbx, rcx, times_1, FrameDescription::frame_content_offset())); 4967 __ bind(&inner_loop_header); 4968 __ testq(rcx, rcx); 4969 __ j(not_zero, &inner_push_loop); 4970 __ addq(rax, Immediate(kSystemPointerSize)); 4971 __ bind(&outer_loop_header); 4972 __ cmpq(rax, rdx); 4973 __ j(below, &outer_push_loop); 4974 4975 for (int i = 0; i < config->num_allocatable_double_registers(); ++i) { 4976 int code = config->GetAllocatableDoubleCode(i); 4977 XMMRegister xmm_reg = XMMRegister::from_code(code); 4978 int src_offset = code * kDoubleSize + double_regs_offset; 4979 __ Movsd(xmm_reg, Operand(rbx, src_offset)); 4980 } 4981 4982 // Push pc and continuation from the last output frame. 4983 __ PushQuad(Operand(rbx, FrameDescription::pc_offset())); 4984 __ PushQuad(Operand(rbx, FrameDescription::continuation_offset())); 4985 4986 // Push the registers from the last output frame. 4987 for (int i = 0; i < kNumberOfRegisters; i++) { 4988 int offset = 4989 (i * kSystemPointerSize) + FrameDescription::registers_offset(); 4990 __ PushQuad(Operand(rbx, offset)); 4991 } 4992 4993 // Restore the registers from the stack. 4994 for (int i = kNumberOfRegisters - 1; i >= 0; i--) { 4995 Register r = Register::from_code(i); 4996 // Do not restore rsp, simply pop the value into the next register 4997 // and overwrite this afterwards. 4998 if (r == rsp) { 4999 DCHECK_GT(i, 0); 5000 r = Register::from_code(i - 1); 5001 } 5002 __ popq(r); 5003 } 5004 5005 __ movb(__ ExternalReferenceAsOperand( 5006 ExternalReference::stack_is_iterable_address(isolate)), 5007 Immediate(1)); 5008 5009 // Return to the continuation point. 5010 __ ret(0); 5011} 5012 5013} // namespace 5014 5015void Builtins::Generate_DeoptimizationEntry_Eager(MacroAssembler* masm) { 5016 Generate_DeoptimizationEntry(masm, DeoptimizeKind::kEager); 5017} 5018 5019void Builtins::Generate_DeoptimizationEntry_Lazy(MacroAssembler* masm) { 5020 Generate_DeoptimizationEntry(masm, DeoptimizeKind::kLazy); 5021} 5022 5023void Builtins::Generate_DeoptimizationEntry_Unused(MacroAssembler* masm) { 5024 Generate_DeoptimizationEntry(masm, DeoptimizeKind::kUnused); 5025} 5026 5027namespace { 5028 5029// Restarts execution either at the current or next (in execution order) 5030// bytecode. If there is baseline code on the shared function info, converts an 5031// interpreter frame into a baseline frame and continues execution in baseline 5032// code. Otherwise execution continues with bytecode. 5033void Generate_BaselineOrInterpreterEntry(MacroAssembler* masm, 5034 bool next_bytecode, 5035 bool is_osr = false) { 5036 Label start; 5037 __ bind(&start); 5038 5039 // Get function from the frame. 5040 Register closure = rdi; 5041 __ movq(closure, MemOperand(rbp, StandardFrameConstants::kFunctionOffset)); 5042 5043 // Get the Code object from the shared function info. 5044 Register code_obj = rbx; 5045 __ LoadTaggedPointerField( 5046 code_obj, FieldOperand(closure, JSFunction::kSharedFunctionInfoOffset)); 5047 __ LoadTaggedPointerField( 5048 code_obj, 5049 FieldOperand(code_obj, SharedFunctionInfo::kFunctionDataOffset)); 5050 5051 // Check if we have baseline code. For OSR entry it is safe to assume we 5052 // always have baseline code. 5053 if (!is_osr) { 5054 Label start_with_baseline; 5055 __ CmpObjectType(code_obj, CODET_TYPE, kScratchRegister); 5056 __ j(equal, &start_with_baseline); 5057 5058 // Start with bytecode as there is no baseline code. 5059 Builtin builtin_id = next_bytecode 5060 ? Builtin::kInterpreterEnterAtNextBytecode 5061 : Builtin::kInterpreterEnterAtBytecode; 5062 __ Jump(masm->isolate()->builtins()->code_handle(builtin_id), 5063 RelocInfo::CODE_TARGET); 5064 5065 // Start with baseline code. 5066 __ bind(&start_with_baseline); 5067 } else if (FLAG_debug_code) { 5068 __ CmpObjectType(code_obj, CODET_TYPE, kScratchRegister); 5069 __ Assert(equal, AbortReason::kExpectedBaselineData); 5070 } 5071 5072 if (FLAG_debug_code) { 5073 AssertCodeTIsBaseline(masm, code_obj, r11); 5074 } 5075 if (V8_EXTERNAL_CODE_SPACE_BOOL) { 5076 __ LoadCodeDataContainerCodeNonBuiltin(code_obj, code_obj); 5077 } 5078 5079 // Load the feedback vector. 5080 Register feedback_vector = r11; 5081 __ LoadTaggedPointerField( 5082 feedback_vector, FieldOperand(closure, JSFunction::kFeedbackCellOffset)); 5083 __ LoadTaggedPointerField(feedback_vector, 5084 FieldOperand(feedback_vector, Cell::kValueOffset)); 5085 5086 Label install_baseline_code; 5087 // Check if feedback vector is valid. If not, call prepare for baseline to 5088 // allocate it. 5089 __ CmpObjectType(feedback_vector, FEEDBACK_VECTOR_TYPE, kScratchRegister); 5090 __ j(not_equal, &install_baseline_code); 5091 5092 // Save BytecodeOffset from the stack frame. 5093 __ SmiUntag( 5094 kInterpreterBytecodeOffsetRegister, 5095 MemOperand(rbp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); 5096 // Replace BytecodeOffset with the feedback vector. 5097 __ movq(MemOperand(rbp, InterpreterFrameConstants::kBytecodeOffsetFromFp), 5098 feedback_vector); 5099 feedback_vector = no_reg; 5100 5101 // Compute baseline pc for bytecode offset. 5102 ExternalReference get_baseline_pc_extref; 5103 if (next_bytecode || is_osr) { 5104 get_baseline_pc_extref = 5105 ExternalReference::baseline_pc_for_next_executed_bytecode(); 5106 } else { 5107 get_baseline_pc_extref = 5108 ExternalReference::baseline_pc_for_bytecode_offset(); 5109 } 5110 Register get_baseline_pc = r11; 5111 __ LoadAddress(get_baseline_pc, get_baseline_pc_extref); 5112 5113 // If the code deoptimizes during the implicit function entry stack interrupt 5114 // check, it will have a bailout ID of kFunctionEntryBytecodeOffset, which is 5115 // not a valid bytecode offset. 5116 // TODO(pthier): Investigate if it is feasible to handle this special case 5117 // in TurboFan instead of here. 5118 Label valid_bytecode_offset, function_entry_bytecode; 5119 if (!is_osr) { 5120 __ cmpq(kInterpreterBytecodeOffsetRegister, 5121 Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag + 5122 kFunctionEntryBytecodeOffset)); 5123 __ j(equal, &function_entry_bytecode); 5124 } 5125 5126 __ subq(kInterpreterBytecodeOffsetRegister, 5127 Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag)); 5128 5129 __ bind(&valid_bytecode_offset); 5130 // Get bytecode array from the stack frame. 5131 __ movq(kInterpreterBytecodeArrayRegister, 5132 MemOperand(rbp, InterpreterFrameConstants::kBytecodeArrayFromFp)); 5133 __ pushq(kInterpreterAccumulatorRegister); 5134 { 5135 FrameScope scope(masm, StackFrame::INTERNAL); 5136 __ PrepareCallCFunction(3); 5137 __ movq(arg_reg_1, code_obj); 5138 __ movq(arg_reg_2, kInterpreterBytecodeOffsetRegister); 5139 __ movq(arg_reg_3, kInterpreterBytecodeArrayRegister); 5140 __ CallCFunction(get_baseline_pc, 3); 5141 } 5142 __ leaq(code_obj, 5143 FieldOperand(code_obj, kReturnRegister0, times_1, Code::kHeaderSize)); 5144 __ popq(kInterpreterAccumulatorRegister); 5145 5146 if (is_osr) { 5147 // TODO(pthier): Separate Sparkplug and Turbofan OSR states. 5148 ResetBytecodeAgeAndOsrState(masm, kInterpreterBytecodeArrayRegister); 5149 Generate_OSREntry(masm, code_obj); 5150 } else { 5151 __ jmp(code_obj); 5152 } 5153 __ Trap(); // Unreachable. 5154 5155 if (!is_osr) { 5156 __ bind(&function_entry_bytecode); 5157 // If the bytecode offset is kFunctionEntryOffset, get the start address of 5158 // the first bytecode. 5159 __ Move(kInterpreterBytecodeOffsetRegister, 0); 5160 if (next_bytecode) { 5161 __ LoadAddress(get_baseline_pc, 5162 ExternalReference::baseline_pc_for_bytecode_offset()); 5163 } 5164 __ jmp(&valid_bytecode_offset); 5165 } 5166 5167 __ bind(&install_baseline_code); 5168 { 5169 FrameScope scope(masm, StackFrame::INTERNAL); 5170 __ pushq(kInterpreterAccumulatorRegister); 5171 __ Push(closure); 5172 __ CallRuntime(Runtime::kInstallBaselineCode, 1); 5173 __ popq(kInterpreterAccumulatorRegister); 5174 } 5175 // Retry from the start after installing baseline code. 5176 __ jmp(&start); 5177} 5178 5179} // namespace 5180 5181void Builtins::Generate_BaselineOrInterpreterEnterAtBytecode( 5182 MacroAssembler* masm) { 5183 Generate_BaselineOrInterpreterEntry(masm, false); 5184} 5185 5186void Builtins::Generate_BaselineOrInterpreterEnterAtNextBytecode( 5187 MacroAssembler* masm) { 5188 Generate_BaselineOrInterpreterEntry(masm, true); 5189} 5190 5191void Builtins::Generate_InterpreterOnStackReplacement_ToBaseline( 5192 MacroAssembler* masm) { 5193 Generate_BaselineOrInterpreterEntry(masm, false, true); 5194} 5195 5196#undef __ 5197 5198} // namespace internal 5199} // namespace v8 5200 5201#endif // V8_TARGET_ARCH_X64 5202