1/* 2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "ecmascript/compiler/trampoline/x64/common_call.h" 17 18#include "ecmascript/compiler/assembler/assembler.h" 19#include "ecmascript/compiler/rt_call_signature.h" 20#include "ecmascript/compiler/argument_accessor.h" 21#include "ecmascript/deoptimizer/deoptimizer.h" 22#include "ecmascript/ecma_runtime_call_info.h" 23#include "ecmascript/frames.h" 24#include "ecmascript/js_function.h" 25#include "ecmascript/js_thread.h" 26#include "ecmascript/message_string.h" 27#include "ecmascript/method.h" 28#include "ecmascript/runtime_call_id.h" 29 30namespace panda::ecmascript::x64 { 31#define __ assembler-> 32 33// * uint64_t JSFunctionEntry(uintptr_t glue, uint32_t actualNumArgs, const JSTaggedType argV[], uintptr_t prevFp) 34// * Arguments: 35// %rdi - glue 36// %rsi - actualNumArgs 37// %rdx - argV 38// %rcx - prevFp 39// %r8 - needPushArgv 40// 41// * The JSFunctionEntry Frame's structure is illustrated as the following: 42// +--------------------------+ 43// | . . . . . . | 44// sp ---> +--------------------------+ ----------------- 45// | prevFP | ^ 46// |--------------------------| | 47// | frameType | JSFunctionEntryFrame 48// |--------------------------| | 49// | preLeaveFrameFp | v 50// +--------------------------+ ----------------- 51 52void OptimizedCall::JSFunctionEntry(ExtendedAssembler *assembler) 53{ 54 __ BindAssemblerStub(RTSTUB_ID(JSFunctionEntry)); 55 Register glueReg = rdi; 56 Register argv = rdx; 57 Register prevFpReg = rcx; 58 Register needPushArgv = r8; 59 Label lJSCallWithArgVAndPushArgv; 60 Label lPopFrame; 61 PushJSFunctionEntryFrame(assembler, prevFpReg); 62 __ Movq(argv, rbx); 63 __ Movq(needPushArgv, r12); 64 __ Movq(Operand(rbx, 0), rdx); 65 __ Movq(Operand(rbx, FRAME_SLOT_SIZE), rcx); 66 __ Movq(Operand(rbx, DOUBLE_SLOT_SIZE), r8); 67 __ Addq(TRIPLE_SLOT_SIZE, rbx); 68 __ Movq(rbx, r9); 69 __ Cmp(1, r12); 70 __ Je(&lJSCallWithArgVAndPushArgv); 71 __ CallAssemblerStub(RTSTUB_ID(JSCallWithArgV), false); 72 __ Jmp(&lPopFrame); 73 74 __ Bind(&lJSCallWithArgVAndPushArgv); 75 __ CallAssemblerStub(RTSTUB_ID(JSCallWithArgVAndPushArgv), false); 76 77 __ Bind(&lPopFrame); 78 __ Popq(prevFpReg); 79 __ Addq(FRAME_SLOT_SIZE, rsp); // 8: frame type 80 __ Popq(rbp); 81 __ Popq(glueReg); // caller restore 82 __ PopCppCalleeSaveRegisters(); // callee restore 83 __ Movq(prevFpReg, Operand(glueReg, JSThread::GlueData::GetLeaveFrameOffset(false))); 84 __ Ret(); 85} 86 87// * uint64_t OptimizedCallAndPushArgv(uintptr_t glue, uint32_t argc, JSTaggedType calltarget, JSTaggedType new, 88// JSTaggedType this, arg[0], arg[1], arg[2], ..., arg[N-1]) 89// * webkit_jscc calling convention call js function() 90// 91// * OptimizedJSFunctionFrame layout description as the following: 92// +--------------------------+ 93// | arg[N-1] | 94// +--------------------------+ 95// | ... | 96// +--------------------------+ 97// | arg[1] | 98// +--------------------------+ 99// | arg[0] | 100// +--------------------------+ 101// | this | 102// +--------------------------+ 103// | new-target | 104// +--------------------------+ 105// | call-target | 106// +--------------------------+ 107// | argv | 108// |--------------------------| 109// | argc | 110// |--------------------------| --------------- 111// | returnAddr | ^ 112// sp ----> |--------------------------| | 113// | callsiteFp | | 114// |--------------------------| OptimizedJSFunctionFrame 115// | frameType | | 116// |--------------------------| | 117// | call-target | v 118// +--------------------------+ --------------- 119 120void OptimizedCall::OptimizedCallAndPushArgv(ExtendedAssembler *assembler) 121{ 122 __ BindAssemblerStub(RTSTUB_ID(OptimizedCallAndPushArgv)); 123 Register jsFuncReg = rdi; 124 Register method = r9; 125 Register codeAddrReg = rsi; 126 127 auto funcSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1; // 1: return addr 128 __ Movq(Operand(rsp, funcSlotOffset * FRAME_SLOT_SIZE), jsFuncReg); // sp + 24 get jsFunc 129 __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method 130 __ Mov(Operand(jsFuncReg, JSFunctionBase::CODE_ENTRY_OFFSET), codeAddrReg); 131 132 Register methodCallField = rcx; 133 __ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField); // get call field 134 __ Shr(MethodLiteral::NumArgsBits::START_BIT, methodCallField); 135 __ Andl(((1LU << MethodLiteral::NumArgsBits::SIZE) - 1), methodCallField); 136 __ Addl(NUM_MANDATORY_JSFUNC_ARGS, methodCallField); // add mandatory argumentr 137 138 __ Movl(Operand(rsp, FRAME_SLOT_SIZE), rdx); // argc rdx 139 __ Movq(rsp, r8); 140 Register argvReg = r8; 141 142 __ Addq(funcSlotOffset * FRAME_SLOT_SIZE, argvReg); // skip return addr, argc and agv 143 144 Register expectedNumArgsReg = rcx; 145 Register actualNumArgsReg = rdx; 146 147 __ Pushq(rbp); 148 __ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME)); 149 __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp); 150 // callee save 151 __ Pushq(r14); 152 __ Pushq(rbx); 153 __ Pushq(rax); 154 155 Label lCopyExtraAument1; 156 Label lCopyLoop1; 157 Label lCopyLoop2; 158 Label lPopFrame1; 159 Label pushUndefined; 160 Label commonCall; 161 162 __ Cmpq(expectedNumArgsReg, actualNumArgsReg); 163 __ Jb(&pushUndefined); 164 // 16 bytes align check 165 __ Movl(actualNumArgsReg, r14); 166 __ Testb(1, r14); 167 __ Je(&lCopyLoop2); 168 __ Pushq(0); 169 170 __ Bind(&lCopyLoop2); 171 __ Movq(Operand(argvReg, r14, Scale::Times8, -FRAME_SLOT_SIZE), rbx); // -8: stack index 172 __ Pushq(rbx); 173 __ Addq(-1, r14); 174 __ Jne(&lCopyLoop2); 175 __ Movl(actualNumArgsReg, r14); 176 __ Jmp(&commonCall); 177 178 __ Bind(&pushUndefined); 179 // 16 bytes align check 180 __ Movl(expectedNumArgsReg, r14); 181 __ Testb(1, r14); 182 __ Je(&lCopyExtraAument1); 183 __ Pushq(0); 184 185 __ Bind(&lCopyExtraAument1); // copy undefined value to stack 186 __ Pushq(JSTaggedValue::VALUE_UNDEFINED); 187 __ Addq(-1, expectedNumArgsReg); 188 __ Cmpq(actualNumArgsReg, expectedNumArgsReg); 189 __ Ja(&lCopyExtraAument1); 190 191 __ Bind(&lCopyLoop1); 192 __ Movq(Operand(argvReg, expectedNumArgsReg, Scale::Times8, -FRAME_SLOT_SIZE), rbx); // -8: stack index 193 __ Pushq(rbx); 194 __ Addq(-1, expectedNumArgsReg); 195 __ Jne(&lCopyLoop1); 196 __ Jmp(&commonCall); 197 198 __ Bind(&commonCall); 199 __ Pushq(rsp); // actual argv 200 __ Pushq(actualNumArgsReg); // actual argc 201 202 __ Callq(codeAddrReg); // then call jsFunction 203 __ Leaq(Operand(r14, Scale::Times8, 0), codeAddrReg); 204 __ Addq(codeAddrReg, rsp); 205 __ Addq(FRAME_SLOT_SIZE, rsp); // skip actualNumArgsReg 206 __ Addq(FRAME_SLOT_SIZE, rsp); // skip argvReg 207 __ Testb(1, r14); // stack 16bytes align check 208 __ Je(&lPopFrame1); 209 __ Addq(8, rsp); // 8: align byte 210 211 __ Bind(&lPopFrame1); 212 __ Addq(8, rsp); // 8: skip rax 213 __ Popq(rbx); 214 __ Popq(r14); 215 __ Addq(FRAME_SLOT_SIZE, rsp); // skip frame type 216 __ Pop(rbp); 217 __ Ret(); 218} 219 220void OptimizedCall::OptimizedCallAsmInterpreter(ExtendedAssembler *assembler) 221{ 222 Label target; 223 PushAsmInterpBridgeFrame(assembler); 224 __ Callq(&target); 225 PopAsmInterpBridgeFrame(assembler); 226 __ Ret(); 227 __ Bind(&target); 228 AsmInterpreterCall::JSCallCommonEntry( 229 assembler, JSCallMode::CALL_FROM_AOT, FrameTransitionType::OTHER_TO_OTHER); 230} 231 232// * uint64_t CallBuiltinTrampoline(uintptr_t glue, uintptr_t codeAddress, uint32_t argc, ...) 233// * webkit_jscc calling convention call runtime_id's runtime function(c-abi) 234// 235// * Construct Native Leave Frame Layout: 236// +--------------------------+ 237// | argv[N-1] | 238// +--------------------------+ 239// | . . . . . . | 240// +--------------------------+ 241// | argv[3]=a0 | 242// +--------------------------+ 243// | argv[2]=this | 244// +--------------------------+ 245// | argv[1]=new-target | 246// +--------------------------+ 247// | argv[0]=call-target | 248// +--------------------------+ ----------------- 249// | argc | ^ 250// |--------------------------| | 251// | thread | | 252// |--------------------------| | 253// | returnAddr | OptimizedBuiltinLeaveFrame 254// sp ---> |--------------------------| | 255// | callsiteFp | | 256// |--------------------------| | 257// | frameType | | 258// |--------------------------| | 259// | align byte | v 260// +--------------------------+ ----------------- 261 262void OptimizedCall::RemoveArgv(ExtendedAssembler *assembler, Register temp) 263{ 264 // remove argv 265 __ Movq(Operand(rsp, FRAME_SLOT_SIZE), temp); 266 __ Movq(temp, Operand(rsp, DOUBLE_SLOT_SIZE)); // argc -> argv 267 __ Movq(Operand(rsp, 0), temp); 268 __ Movq(temp, Operand(rsp, FRAME_SLOT_SIZE)); // returnAddr -> argc 269 __ Addq(FRAME_SLOT_SIZE, rsp); // skip argv 270} 271 272void OptimizedCall::CallBuiltinTrampoline(ExtendedAssembler *assembler, Register temp) 273{ 274 Register glueReg = rax; 275 Register nativeCode = rsi; 276 277 // remove argv 278 RemoveArgv(assembler, temp); 279 280 __ Movq(Operand(rsp, 0), rdx); 281 __ Movq(glueReg, Operand(rsp, 0)); 282 __ Push(rdx); 283 284 AsmInterpreterCall::PushBuiltinFrame(assembler, glueReg, FrameType::BUILTIN_CALL_LEAVE_FRAME); 285 __ Leaq(Operand(rbp, DOUBLE_SLOT_SIZE), rdi); // 2: skip rbp & return Addr 286 __ PushAlignBytes(); 287 AsmInterpreterCall::CallNativeInternal(assembler, nativeCode); 288 __ Movq(Operand(rsp, DOUBLE_SLOT_SIZE), temp); // argc 289 __ Movq(Immediate(0), Operand(rsp, DOUBLE_SLOT_SIZE)); // argv -> argc 290 __ Movq(temp, Operand(rsp, FRAME_SLOT_SIZE)); // argc -> thread 291 292 __ Ret(); 293} 294 295// * uint64_t CallBuiltinConstructorStub(uintptr_t glue, uintptr_t codeAddress, uint32_t argc, ...) 296// * webkit_jscc calling convention call runtime_id's runtime function(c-abi) 297// 298// * Construct Native Leave Frame Layout: 299// +--------------------------+ 300// | argv[N-1] | 301// +--------------------------+ 302// | . . . . . . | 303// +--------------------------+ 304// | argv[3]=a0 | 305// +--------------------------+ 306// | argv[2]=this | 307// +--------------------------+ 308// | argv[1]=new-target | 309// +--------------------------+ 310// | argv[0]=call-target | 311// +--------------------------+ ----------------- 312// | argc | ^ 313// |--------------------------| | 314// | thread | | 315// |--------------------------| | 316// | returnAddr | OptimizedBuiltinLeaveFrame 317// sp ---> |--------------------------| | 318// | callsiteFp | | 319// |--------------------------| | 320// | frameType | v 321// +--------------------------+ ----------------- 322 323void OptimizedCall::CallBuiltinConstructorStub(ExtendedAssembler *assembler, Register builtinStub, Register argv, 324 Register glue, Register temp) 325{ 326 // remove argv 327 RemoveArgv(assembler, temp); 328 329 __ Movq(Operand(rsp, 0), temp); 330 __ Movq(glue, Operand(rsp, 0)); 331 __ Push(temp); 332 333 // push rbp & frameType 334 AsmInterpreterCall::PushBuiltinFrame(assembler, glue, FrameType::BUILTIN_CALL_LEAVE_FRAME); 335 __ Push(argv); 336 __ Callq(builtinStub); 337 // resume rsp 338 __ Movq(rbp, rsp); 339 __ Pop(rbp); 340 __ Movq(Operand(rsp, DOUBLE_SLOT_SIZE), temp); // argc 341 __ Movq(Immediate(0), Operand(rsp, DOUBLE_SLOT_SIZE)); // 0 -> argc 342 __ Movq(temp, Operand(rsp, FRAME_SLOT_SIZE)); // argc -> thread 343 __ Ret(); 344} 345 346// * uint64_t JSProxyCallInternalWithArgV(uintptr_t glue, JSTaggedType calltarget) 347// * c++ calling convention call js function 348// * Arguments: 349// %rdi - glue 350// %rsi - calltarget 351 352void OptimizedCall::JSProxyCallInternalWithArgV(ExtendedAssembler *assembler) 353{ 354 __ BindAssemblerStub(RTSTUB_ID(JSProxyCallInternalWithArgV)); 355 Register ccGlueReg = rdi; 356 Register jsccGlueReg = rax; 357 Register callTarget = rsi; 358 __ Movq(ccGlueReg, jsccGlueReg); // c++ calling convention as webkit_jscc calling convention 359 auto funcSlotOffSet = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1; 360 __ Movq(callTarget, Operand(rsp, funcSlotOffSet * FRAME_SLOT_SIZE)); // update callTarget slot 361 GenJSCall(assembler, false); 362} 363 364// * uint64_t JSCall(uintptr_t glue, uint32_t argc, JSTaggedType calltarget, JSTaggedType new, 365// JSTaggedType this, arg[0], arg[1], arg[2], ..., arg[N-1]) 366// * webkit_jscc calling convention call js function() 367// 368// * OptimizedJSFunctionFrame layout description as the following: 369// +--------------------------+ 370// | arg[N-1] | 371// +--------------------------+ 372// | ... | 373// +--------------------------+ 374// | arg[1] | 375// +--------------------------+ 376// | arg[0] | 377// +--------------------------+ 378// | this | 379// +--------------------------+ 380// | new-target | 381// +--------------------------+ 382// | call-target | 383// +--------------------------+ 384// | argv | 385// |--------------------------| 386// | argc | 387// |--------------------------| --------------- 388// | returnAddr | ^ 389// sp ----> |--------------------------| | 390// | callsiteFp | | 391// |--------------------------| OptimizedJSFunctionFrame 392// | frameType | | 393// |--------------------------| | 394// | call-target | v 395// +--------------------------+ --------------- 396void OptimizedCall::JSCallNew(ExtendedAssembler *assembler) 397{ 398 __ BindAssemblerStub(RTSTUB_ID(JSCallNew)); 399 GenJSCall(assembler, true); 400} 401 402void OptimizedCall::JSCall(ExtendedAssembler *assembler) 403{ 404 __ BindAssemblerStub(RTSTUB_ID(JSCall)); 405 GenJSCall(assembler, false); 406} 407 408void OptimizedCall::GenJSCall(ExtendedAssembler *assembler, bool isNew) 409{ 410 Label jsCall; 411 Label lJSCallStart; 412 Label lNotJSFunction; 413 Label lNonCallable; 414 Label lJSFunctionCall; 415 Label lJSBoundFunction; 416 Label lJSProxy; 417 Label lCallNativeMethod; 418 Label lCallNativeCpp; 419 Label lCallNativeBuiltinStub; 420 Register glueReg = rax; 421 __ Bind(&jsCall); 422 { 423 __ Movq(glueReg, rdi); 424 glueReg = rdi; 425 auto funcSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1; 426 __ Movq(Operand(rsp, funcSlotOffset * FRAME_SLOT_SIZE), rax); // sp + 24 get jsFunc 427 } 428 __ Bind(&lJSCallStart); 429 Register jsFuncReg = rax; 430 { 431 JSCallCheck(assembler, jsFuncReg, &lNonCallable, &lNotJSFunction, &lJSFunctionCall); 432 } 433 434 __ Bind(&lNotJSFunction); 435 { 436 __ Cmpb(static_cast<uint8_t>(JSType::JS_BOUND_FUNCTION), rax); // IsBoundFunction 437 __ Je(&lJSBoundFunction); 438 __ Cmpb(static_cast<uint8_t>(JSType::JS_PROXY), rax); // IsJsProxy 439 __ Je(&lJSProxy); 440 } 441 442 __ Bind(&lNonCallable); 443 { 444 ThrowNonCallableInternal(assembler, glueReg); 445 } 446 447 __ Bind(&lJSFunctionCall); 448 jsFuncReg = rsi; 449 Register argc = r8; 450 Register methodCallField = rcx; 451 Register method = rdx; 452 Register argV = r9; 453 { 454 Label lCallConstructor; 455 Label lNotClass; 456 __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method 457 __ Movl(Operand(rsp, FRAME_SLOT_SIZE), argc); // skip return addr 458 __ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField); // get call field 459 __ Btq(MethodLiteral::IsNativeBit::START_BIT, methodCallField); // is native 460 __ Jb(&lCallNativeMethod); 461 if (!isNew) { 462 __ Btq(JSHClass::IsClassConstructorOrPrototypeBit::START_BIT, rax); // is CallConstructor 463 __ Jnb(&lNotClass); 464 __ Btq(JSHClass::ConstructorBit::START_BIT, rax); // is CallConstructor 465 __ Jb(&lCallConstructor); 466 } 467 __ Bind(&lNotClass); 468 __ Movq(rsp, argV); 469 auto argvSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1; // 1: return addr 470 __ Addq(argvSlotOffset * FRAME_SLOT_SIZE, argV); // skip return addr, argc and argv 471 __ Subq(Immediate(kungfu::ArgumentAccessor::GetFixArgsNum()), argc); 472 // argv + 24 get asm interpreter argv 473 __ Addq(kungfu::ArgumentAccessor::GetFixArgsNum() * FRAME_SLOT_SIZE, argV); 474 OptimizedCallAsmInterpreter(assembler); 475 __ Bind(&lCallConstructor); 476 { 477 __ Pushq(rbp); 478 __ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME)); // set frame type 479 __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp); 480 __ Pushq(0); // PushAlign 481 __ Pushq(0); // argc 482 __ Pushq(RTSTUB_ID(ThrowCallConstructorException)); // runtime id 483 __ Movq(glueReg, rax); // glue 484 __ Movq(kungfu::RuntimeStubCSigns::ID_CallRuntime, r10); 485 __ Movq(Operand(rax, r10, Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r10); 486 __ Callq(r10); // call CallRuntime 487 __ Addq(4 * FRAME_SLOT_SIZE, rsp); // 4: sp + 32 argv 488 __ Pop(rbp); 489 __ Ret(); 490 } 491 } 492 493 __ Bind(&lCallNativeMethod); 494 { 495 Register nativePointer = rsi; 496 method = rax; 497 __ Movq(jsFuncReg, rdx); 498 __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method 499 __ Mov(Operand(method, Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET), nativePointer); // native pointer 500 __ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField); // get call field 501 __ Btq(MethodLiteral::IsFastBuiltinBit::START_BIT, methodCallField); // is builtin stub 502 503 if (!isNew) { 504 __ Jnb(&lCallNativeCpp); 505 __ Cmpl(NUM_MANDATORY_JSFUNC_ARGS + 3, argc); // 3:call0, call1, call2, call3 506 __ Jbe(&lCallNativeBuiltinStub); 507 } else { 508 __ Jb(&lCallNativeBuiltinStub); 509 } 510 } 511 512 __ Bind(&lCallNativeCpp); 513 { 514 __ Movq(glueReg, rax); 515 CallBuiltinTrampoline(assembler, r11); 516 } 517 518 __ Bind(&lCallNativeBuiltinStub); 519 { 520 Register methodExtraLiteralInfo = rax; 521 __ Mov(Operand(method, Method::EXTRA_LITERAL_INFO_OFFSET), methodExtraLiteralInfo); // get extra literal 522 __ Shr(MethodLiteral::BuiltinIdBits::START_BIT, methodExtraLiteralInfo); 523 __ Andl(((1LU << MethodLiteral::BuiltinIdBits::SIZE) - 1), methodExtraLiteralInfo); // get builtin stub id 524 if (!isNew) { 525 __ Cmpl(kungfu::BuiltinsStubCSigns::BUILTINS_CONSTRUCTOR_STUB_FIRST, methodExtraLiteralInfo); 526 __ Jnb(&lCallNativeCpp); 527 } 528 529 __ Movq(glueReg, rdi); 530 __ Movq(methodExtraLiteralInfo, r10); 531 __ Movq(Operand(glueReg, r10, Times8, JSThread::GlueData::GetBuiltinsStubEntriesOffset(false)), r10); 532 533 __ Movq(argc, r9); 534 __ Movq(Operand(rsp, QUADRUPLE_SLOT_SIZE), rcx); // newTarget 535 __ Movq(Operand(rsp, QUINTUPLE_SLOT_SIZE), r8); // this 536 __ Subq(NUM_MANDATORY_JSFUNC_ARGS, r9); // argc 537 538 Label lCall0; 539 Label lCall1; 540 Label lCall2; 541 Label lCall3; 542 argV = rax; 543 544 __ Movq(rsp, argV); 545 auto argvSlotOffset = kungfu::ArgumentAccessor::GetFixArgsNum() + 546 kungfu::ArgumentAccessor::GetExtraArgsNum() + 1; // 1: return addr 547 __ Addq(argvSlotOffset *FRAME_SLOT_SIZE, argV); 548 if (!isNew) { 549 PushAsmBridgeFrame(assembler); 550 551 __ Cmpl(0, r9); // 0: callarg0 552 __ Je(&lCall0); 553 __ Cmpl(1, r9); // 1: callarg1 554 __ Je(&lCall1); 555 __ Cmpl(2, r9); // 2: callarg2 556 __ Je(&lCall2); 557 __ Cmpl(3, r9); // 3: callarg3 558 __ Je(&lCall3); 559 560 __ Bind(&lCall0); 561 { 562 __ Pushq(JSTaggedValue::VALUE_UNDEFINED); 563 __ Pushq(JSTaggedValue::VALUE_UNDEFINED); 564 __ Pushq(JSTaggedValue::VALUE_UNDEFINED); 565 __ Callq(r10); 566 __ Addq(QUADRUPLE_SLOT_SIZE, rsp); 567 __ Pop(rbp); 568 __ Ret(); 569 } 570 571 __ Bind(&lCall1); 572 { 573 __ Pushq(JSTaggedValue::VALUE_UNDEFINED); 574 __ Pushq(JSTaggedValue::VALUE_UNDEFINED); 575 __ Movq(Operand(argV, 0), r11); // arg0 576 __ Pushq(r11); 577 __ Callq(r10); 578 __ Addq(QUADRUPLE_SLOT_SIZE, rsp); 579 __ Pop(rbp); 580 __ Ret(); 581 } 582 583 __ Bind(&lCall2); 584 { 585 __ Pushq(JSTaggedValue::VALUE_UNDEFINED); 586 __ Movq(Operand(argV, FRAME_SLOT_SIZE), r11); // arg1 587 __ Pushq(r11); 588 __ Movq(Operand(argV, 0), r11); // arg0 589 __ Pushq(r11); 590 __ Callq(r10); 591 __ Addq(QUADRUPLE_SLOT_SIZE, rsp); 592 __ Pop(rbp); 593 __ Ret(); 594 } 595 596 __ Bind(&lCall3); 597 { 598 __ Movq(Operand(argV, DOUBLE_SLOT_SIZE), r11); // arg2 599 __ Pushq(r11); 600 __ Movq(Operand(argV, FRAME_SLOT_SIZE), r11); // arg1 601 __ Pushq(r11); 602 __ Movq(Operand(argV, 0), r11); // arg0 603 __ Pushq(r11); 604 __ Callq(r10); 605 __ Addq(QUADRUPLE_SLOT_SIZE, rsp); 606 __ Pop(rbp); 607 __ Ret(); 608 } 609 } else { 610 CallBuiltinConstructorStub(assembler, r10, argV, glueReg, r11); 611 __ Int3(); 612 } 613 } 614 615 __ Bind(&lJSBoundFunction); 616 { 617 JSBoundFunctionCallInternal(assembler, jsFuncReg, &jsCall); 618 } 619 __ Bind(&lJSProxy); 620 { 621 __ Mov(Operand(jsFuncReg, JSProxy::METHOD_OFFSET), method); 622 __ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField); 623 __ Mov(Operand(rsp, FRAME_SLOT_SIZE), argc); 624 __ Jmp(&lCallNativeMethod); 625 } 626} 627 628// After the callee function of common aot call deopt, use this bridge to deal with this aot call. 629// calling convention: webkit_jsc 630// Input structure: 631// %rax - glue 632// stack: 633// +--------------------------+ 634// | arg[N-1] | 635// +--------------------------+ 636// | ... | 637// +--------------------------+ 638// | arg[1] | 639// +--------------------------+ 640// | arg[0] | 641// +--------------------------+ 642// | this | 643// +--------------------------+ 644// | new-target | 645// +--------------------------+ 646// | call-target | 647// |--------------------------| 648// | argv | 649// |--------------------------| 650// | argc | 651// |--------------------------| 652// | returnAddr | 653// +--------------------------+ <---- sp 654void OptimizedCall::AOTCallToAsmInterBridge(ExtendedAssembler *assembler) 655{ 656 __ BindAssemblerStub(RTSTUB_ID(AOTCallToAsmInterBridge)); 657 // params of c++ calling convention 658 Register glueReg = rdi; 659 Register jsFuncReg = rsi; 660 Register method = rdx; 661 Register methodCallField = rcx; 662 Register argc = r8; 663 Register argV = r9; 664 665 __ Movq(rax, glueReg); 666 __ Movq(Operand(rsp, TRIPLE_SLOT_SIZE), jsFuncReg); 667 __ Movq(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method 668 __ Movq(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField); // get call field 669 __ Movl(Operand(rsp, FRAME_SLOT_SIZE), argc); // skip return addr 670 __ Subq(Immediate(kungfu::ArgumentAccessor::GetFixArgsNum()), argc); 671 __ Movq(rsp, argV); 672 auto argvSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1; // 1: return addr 673 __ Addq(argvSlotOffset * FRAME_SLOT_SIZE, argV); // skip return addr and argc 674 __ Addq(kungfu::ArgumentAccessor::GetFixArgsNum() * FRAME_SLOT_SIZE, argV); 675 OptimizedCallAsmInterpreter(assembler); 676} 677 678// After the callee function of fast aot call deopt, use this bridge to deal with this fast aot call. 679// Notice: no argc and new-target params compared with not-fast aot call because these params are not needed 680// according to bytecode-analysis. 681// Intruduction: use expected argc as actual argc below for these reasons: 682// 1) when expected argc == actual argc, pass. 683// 2) when expected argc > actual argc, undefineds have been pushed in OptimizedFastCallAndPushArgv. 684// 3) when expected argc < actual argc, redundant params are useless according to bytecode-analysis, just abandon them. 685// calling convention: c++ calling convention 686// Input structure: 687// %rdi - glue 688// %rsi - call-target 689// %rdx - this 690// %rcx - arg0 691// %r8 - arg1 692// %r9 - arg2 693// stack: 694// +--------------------------+ 695// | arg[N-1] | 696// +--------------------------+ 697// | ... | 698// +--------------------------+ 699// | arg[3] | 700// +--------------------------+ 701// | returnAddr | 702// |--------------------------| <---- sp 703void OptimizedCall::FastCallToAsmInterBridge(ExtendedAssembler *assembler) 704{ 705 __ BindAssemblerStub(RTSTUB_ID(FastCallToAsmInterBridge)); 706 // Input 707 Register glueReg = rdi; 708 Register jsFuncReg = rsi; 709 Register thisReg = rdx; 710 Register maybeArg0 = rcx; 711 Register maybeArg1 = r8; 712 Register maybeArg2 = r9; 713 714 // Add a bridge frame to protect the stack map 715 PushAsmBridgeFrame(assembler); 716 717 Register tempMethod = __ AvailableRegister1(); 718 Register tempCallField = __ AvailableRegister2(); 719 720 __ Movq(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), tempMethod); 721 __ Movq(Operand(tempMethod, Method::CALL_FIELD_OFFSET), tempCallField); 722 // get expected num Args 723 Register tempArgc = tempCallField; 724 __ Shr(MethodLiteral::NumArgsBits::START_BIT, tempArgc); 725 __ Andl(((1LU << MethodLiteral::NumArgsBits::SIZE) - 1), tempArgc); 726 727 { 728 [[maybe_unused]] TempRegisterScope scope(assembler); 729 Register startSp = __ TempRegister(); 730 __ Movq(rsp, startSp); 731 732 Label lCall0; 733 Label lCall1; 734 Label lCall2; 735 Label lCall3; 736 Label lPushCommonRegs; 737 738 __ Cmpl(0, tempArgc); // 0: callarg0 739 __ Je(&lCall0); 740 __ Cmpl(1, tempArgc); // 1: callarg1 741 __ Je(&lCall1); 742 __ Cmpl(2, tempArgc); // 2: callarg2 743 __ Je(&lCall2); 744 __ Cmpl(3, tempArgc); // 3: callarg3 745 __ Je(&lCall3); 746 // default: more than 3 args 747 { 748 __ Subq(Immediate(3), tempArgc); // 3: the first 3 args are not on stack 749 __ Addq(Immediate(TRIPLE_SLOT_SIZE), startSp); // skip bridge frame and return addr 750 CopyArgumentWithArgV(assembler, tempArgc, startSp); 751 __ Subq(Immediate(TRIPLE_SLOT_SIZE), startSp); 752 __ Jmp(&lCall3); 753 } 754 755 __ Bind(&lCall0); 756 { 757 __ Jmp(&lPushCommonRegs); 758 } 759 760 __ Bind(&lCall1); 761 { 762 __ Pushq(maybeArg0); 763 __ Jmp(&lPushCommonRegs); 764 } 765 766 __ Bind(&lCall2); 767 { 768 __ Pushq(maybeArg1); 769 __ Pushq(maybeArg0); 770 __ Jmp(&lPushCommonRegs); 771 } 772 773 __ Bind(&lCall3); 774 { 775 __ Pushq(maybeArg2); 776 __ Pushq(maybeArg1); 777 __ Pushq(maybeArg0); 778 __ Jmp(&lPushCommonRegs); 779 } 780 781 __ Bind(&lPushCommonRegs); 782 { 783 __ Pushq(thisReg); 784 __ Pushq(JSTaggedValue::VALUE_UNDEFINED); // newTarget 785 __ Pushq(jsFuncReg); 786 // fall through 787 } 788 789 // params of c++ calling convention 790 glueReg = rdi; 791 jsFuncReg = rsi; 792 Register method = rdx; 793 Register methodCallField = rcx; 794 Register argc = r8; 795 Register argV = r9; 796 797 // reload and prepare args for JSCallCommonEntry 798 __ Movq(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); 799 __ Movq(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField); 800 __ Movq(methodCallField, argc); 801 __ Shr(MethodLiteral::NumArgsBits::START_BIT, argc); 802 __ Andl(((1LU << MethodLiteral::NumArgsBits::SIZE) - 1), argc); 803 __ Movq(rsp, argV); 804 __ Addq(Immediate(TRIPLE_SLOT_SIZE), argV); // skip func, newtarget and this 805 806 __ Pushq(startSp); // used for resume rsp 807 } 808 809 Label target; 810 PushAsmInterpBridgeFrame(assembler); 811 __ Callq(&target); 812 { 813 PopAsmInterpBridgeFrame(assembler); 814 Register startSp = __ AvailableRegister1(); 815 __ Popq(startSp); 816 __ Movq(startSp, rsp); 817 PopAsmBridgeFrame(assembler); 818 __ Ret(); 819 } 820 __ Bind(&target); 821 AsmInterpreterCall::JSCallCommonEntry( 822 assembler, JSCallMode::CALL_FROM_AOT, FrameTransitionType::OTHER_TO_OTHER); 823} 824 825void OptimizedCall::JSCallCheck(ExtendedAssembler *assembler, Register jsFuncReg, 826 Label *lNonCallable, Label *lNotJSFunction, Label *lJSFunctionCall) 827{ 828 __ Movabs(JSTaggedValue::TAG_INT, rdx); // IsTaggedInt 829 __ And(jsFuncReg, rdx); 830 __ Cmp(0x0, rdx); 831 __ Jne(lNonCallable); 832 __ Cmp(0x0, jsFuncReg); // IsHole 833 __ Je(lNonCallable); 834 __ Movabs(JSTaggedValue::TAG_SPECIAL, rdx); 835 __ And(jsFuncReg, rdx); // IsSpecial 836 __ Cmp(0x0, rdx); 837 __ Jne(lNonCallable); 838 839 __ Movq(jsFuncReg, rsi); // save jsFunc 840 __ Movq(Operand(jsFuncReg, JSFunction::HCLASS_OFFSET), rax); // get jsHclass 841 Register jsHclassReg = rax; 842 __ Movl(Operand(jsHclassReg, JSHClass::BIT_FIELD_OFFSET), rax); 843 __ Btl(JSHClass::CallableBit::START_BIT, rax); // IsCallable 844 __ Jnb(lNonCallable); 845 846 __ Cmpb(static_cast<int32_t>(JSType::JS_FUNCTION_FIRST), rax); 847 __ Jb(lNotJSFunction); 848 __ Cmpb(static_cast<int32_t>(JSType::JS_FUNCTION_LAST), rax); 849 __ Jbe(lJSFunctionCall); // objecttype in (0x04 ~ 0x0c) 850} 851 852void OptimizedCall::ThrowNonCallableInternal(ExtendedAssembler *assembler, Register glueReg) 853{ 854 __ Pushq(rbp); 855 __ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME)); // set frame type 856 __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp); 857 __ Movq(MessageString::Message_NonCallable, rax); 858 __ Movabs(JSTaggedValue::TAG_INT, r10); 859 __ Orq(r10, rax); 860 __ Pushq(rax); // message id 861 __ Pushq(1); // argc 862 __ Pushq(RTSTUB_ID(ThrowTypeError)); // runtime id 863 __ Movq(glueReg, rax); // glue 864 __ Movq(kungfu::RuntimeStubCSigns::ID_CallRuntime, r10); 865 __ Movq(Operand(rax, r10, Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r10); 866 __ Callq(r10); // call CallRuntime 867 __ Movabs(JSTaggedValue::VALUE_EXCEPTION, rax); // return exception 868 __ Addq(4 * FRAME_SLOT_SIZE, rsp); // 4: sp + 32 argv 869 __ Pop(rbp); 870 __ Ret(); 871} 872 873void OptimizedCall::JSBoundFunctionCallInternal(ExtendedAssembler *assembler, Register jsFuncReg, Label *jsCall) 874{ 875 Label lAlign16Bytes2; 876 Label lCopyBoundArgument; 877 Label lCopyArgument2; 878 Label lPushCallTarget; 879 Label lCopyBoundArgumentLoop; 880 Label lPopFrame2; 881 Label slowCall; 882 Label aotCall; 883 Label popArgs; 884 Label isJsFunc; 885 Label isNotClass; 886 __ Pushq(rbp); 887 __ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME)); 888 __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp); 889 __ Pushq(r10); // callee save 890 __ Movq(rsp, rdx); 891 __ Addq(QUADRUPLE_SLOT_SIZE, rdx); // skip return addr, prevFp, frame type and callee save 892 __ Mov(Operand(rdx, 0), rax); // get origin argc 893 __ Mov(Operand(rdx, FRAME_SLOT_SIZE), r9); // get origin argv 894 __ Movq(rax, r10); 895 // get bound target 896 __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_ARGUMENTS_OFFSET), rcx); 897 // get bound length 898 __ Mov(Operand(rcx, TaggedArray::LENGTH_OFFSET), rcx); 899 __ Addq(rcx, r10); 900 901 // 16 bytes align check 902 __ Testb(1, r10); 903 __ Je(&lAlign16Bytes2); 904 __ PushAlignBytes(); // push zero to align 16 bytes stack 905 906 __ Bind(&lAlign16Bytes2); 907 { 908 __ Subq(NUM_MANDATORY_JSFUNC_ARGS, rax); 909 __ Cmp(0, rax); 910 __ Je(&lCopyBoundArgument); 911 } 912 913 __ Bind(&lCopyArgument2); 914 { 915 __ Movq(Operand(rdx, rax, Scale::Times8, 916 (kungfu::ArgumentAccessor::GetFixArgsNum() + 1) * FRAME_SLOT_SIZE), rcx); // argv 917 __ Pushq(rcx); 918 __ Addq(-1, rax); 919 __ Jne(&lCopyArgument2); 920 } 921 922 __ Bind(&lCopyBoundArgument); 923 { 924 // get bound target 925 __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_ARGUMENTS_OFFSET), rdx); 926 // get bound length 927 __ Mov(Operand(rdx, TaggedArray::LENGTH_OFFSET), rax); 928 __ Addq(TaggedArray::DATA_OFFSET, rdx); 929 __ Cmp(0, rax); 930 __ Je(&lPushCallTarget); 931 } 932 __ Bind(&lCopyBoundArgumentLoop); 933 { 934 __ Addq(-1, rax); 935 __ Movq(Operand(rdx, rax, Scale::Times8, 0), rcx); 936 __ Pushq(rcx); 937 __ Jne(&lCopyBoundArgumentLoop); 938 } 939 __ Bind(&lPushCallTarget); 940 { 941 __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_THIS_OFFSET), r8); // thisObj 942 __ Pushq(r8); 943 __ Pushq(JSTaggedValue::VALUE_UNDEFINED); // newTarget 944 __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_TARGET_OFFSET), rax); // callTarget 945 __ Pushq(rax); 946 __ Pushq(r9); 947 __ Pushq(r10); // push actual arguments 948 } 949 JSCallCheck(assembler, rax, &slowCall, &slowCall, &isJsFunc); // jsfunc -> rsi hclassfiled -> rax 950 __ Jmp(&slowCall); 951 Register jsfunc = rsi; 952 Register compiledCodeFlag = rcx; 953 __ Bind(&isJsFunc); 954 { 955 __ Btq(JSHClass::IsClassConstructorOrPrototypeBit::START_BIT, rax); // is CallConstructor 956 __ Jnb(&isNotClass); 957 __ Btq(JSHClass::ConstructorBit::START_BIT, rax); 958 __ Jb(&slowCall); 959 __ Bind(&isNotClass); 960 __ Movzwq(Operand(rsi, JSFunctionBase::BIT_FIELD_OFFSET), compiledCodeFlag); // compiled code flag 961 __ Btq(JSFunctionBase::IsCompiledCodeBit::START_BIT, compiledCodeFlag); // is compiled code 962 __ Jnb(&slowCall); 963 __ Bind(&aotCall); 964 { 965 // output: glue:rdi argc:rsi calltarget:rdx argv:rcx this:r8 newtarget:r9 966 __ Movq(jsfunc, rdx); 967 __ Movq(r10, rsi); 968 auto funcSlotOffSet = kungfu::ArgumentAccessor::GetFixArgsNum() + 969 kungfu::ArgumentAccessor::GetExtraArgsNum(); 970 __ Leaq(Operand(rsp, funcSlotOffSet * FRAME_SLOT_SIZE), rcx); // 5: skip argc and argv func new this 971 __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9); 972 __ Movq(kungfu::CommonStubCSigns::JsBoundCallInternal, r10); 973 __ Movq(Operand(rdi, r10, Scale::Times8, JSThread::GlueData::GetCOStubEntriesOffset(false)), rax); 974 __ Callq(rax); // call JSCall 975 __ Jmp(&popArgs); 976 } 977 } 978 __ Bind(&slowCall); 979 { 980 __ Movq(rdi, rax); 981 __ Callq(jsCall); // call JSCall 982 __ Jmp(&popArgs); 983 } 984 __ Bind(&popArgs); 985 { 986 __ Pop(r10); 987 __ Pop(r9); 988 __ Leaq(Operand(r10, Scale::Times8, 0), rcx); // 8: disp 989 __ Addq(rcx, rsp); 990 __ Testb(1, r10); // stack 16bytes align check 991 __ Je(&lPopFrame2); 992 __ Addq(8, rsp); // 8: align byte 993 } 994 __ Bind(&lPopFrame2); 995 { 996 __ Pop(r10); 997 __ Addq(8, rsp); // 8: sp + 8 998 __ Pop(rbp); 999 __ Ret(); 1000 } 1001} 1002 1003// * uint64_t CallRuntime(uintptr_t glue, uint64_t runtime_id, uint64_t argc, uintptr_t arg0, ...) 1004// * webkit_jscc calling convention call runtime_id's runtime function(c-abi) 1005// * Arguments: 1006// %rax - glue 1007// 1008// * Optimized-leaved-frame layout as the following: 1009// +--------------------------+ 1010// | argv[N-1] | 1011// |--------------------------| 1012// | . . . . . | 1013// |--------------------------| 1014// | argv[0] | 1015// +--------------------------+------------- 1016// | argc | ^ 1017// |--------------------------| | 1018// | RuntimeId | | 1019// sp --> |--------------------------| OptimizedLeaveFrame 1020// | ret-addr | | 1021// |--------------------------| | 1022// | prevFp | | 1023// |--------------------------| | 1024// | frameType | v 1025// +--------------------------+------------- 1026 1027void OptimizedCall::CallRuntime(ExtendedAssembler *assembler) 1028{ 1029 __ BindAssemblerStub(RTSTUB_ID(CallRuntime)); 1030 __ Pushq(rbp); 1031 __ Movq(rsp, Operand(rax, JSThread::GlueData::GetLeaveFrameOffset(false))); 1032 __ Pushq(static_cast<int32_t>(FrameType::LEAVE_FRAME)); 1033 __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp); // 8: skip frame type 1034 1035 __ Pushq(r10); 1036 __ Pushq(rdx); 1037 __ Pushq(rax); 1038 1039 __ Movq(rbp, rdx); 1040 // 2: rbp & return address 1041 __ Addq(2 * FRAME_SLOT_SIZE, rdx); 1042 1043 __ Movq(Operand(rdx, 0), r10); 1044 __ Movq(Operand(rax, r10, Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r10); 1045 __ Movq(rax, rdi); 1046 // 8: argc 1047 __ Movq(Operand(rdx, FRAME_SLOT_SIZE), rsi); 1048 // 2: argv 1049 __ Addq(2 * FRAME_SLOT_SIZE, rdx); 1050 __ Callq(r10); 1051 1052 // 8: skip rax 1053 __ Addq(FRAME_SLOT_SIZE, rsp); 1054 __ Popq(rdx); 1055 __ Popq(r10); 1056 1057 // 8: skip frame type 1058 __ Addq(FRAME_SLOT_SIZE, rsp); 1059 __ Popq(rbp); 1060 __ Ret(); 1061} 1062 1063// * uint64_t CallRuntimeWithArgv(uintptr_t glue, uint64_t runtime_id, uint64_t argc, uintptr_t argv) 1064// * cc calling convention call runtime_id's runtion function(c-abi) 1065// * Arguments: 1066// %rdi - glue 1067// %rsi - runtime_id 1068// %edx - argc 1069// %rcx - argv 1070// 1071// * Optimized-leaved-frame-with-argv layout as the following: 1072// +--------------------------+ 1073// | argv[] | 1074// +--------------------------+------------- 1075// | argc | ^ 1076// |--------------------------| | 1077// | RuntimeId | OptimizedWithArgvLeaveFrame 1078// sp --> |--------------------------| | 1079// | returnAddr | | 1080// |--------------------------| | 1081// | callsiteFp | | 1082// |--------------------------| | 1083// | frameType | v 1084// +--------------------------+------------- 1085 1086void OptimizedCall::CallRuntimeWithArgv(ExtendedAssembler *assembler) 1087{ 1088 __ BindAssemblerStub(RTSTUB_ID(CallRuntimeWithArgv)); 1089 Register glueReg = rdi; 1090 Register runtimeIdReg = rsi; 1091 Register argcReg = rdx; 1092 Register argvReg = rcx; 1093 1094 __ Movq(rsp, r8); 1095 Register returnAddrReg = r9; 1096 __ Movq(Operand(rsp, 0), returnAddrReg); 1097 __ Pushq(argvReg); // argv[] 1098 __ Pushq(argcReg); // argc 1099 __ Pushq(runtimeIdReg); // runtime_id 1100 __ Pushq(returnAddrReg); // returnAddr 1101 1102 // construct leave frame 1103 __ Pushq(rbp); 1104 __ Movq(rsp, Operand(glueReg, JSThread::GlueData::GetLeaveFrameOffset(false))); // save to thread->leaveFrame_ 1105 __ Pushq(static_cast<int32_t>(FrameType::LEAVE_FRAME_WITH_ARGV)); 1106 __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp); 1107 1108 __ Movq(Operand(glueReg, runtimeIdReg, Scale::Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r9); 1109 __ Movq(argcReg, rsi); // argc 1110 __ Movq(argvReg, rdx); // argv 1111 __ Pushq(r8); 1112 __ Callq(r9); 1113 __ Popq(r8); 1114 __ Addq(FRAME_SLOT_SIZE, rsp); // 8: skip type 1115 __ Popq(rbp); 1116 __ Movq(r8, rsp); 1117 __ Ret(); 1118} 1119 1120void OptimizedCall::PushMandatoryJSArgs(ExtendedAssembler *assembler, Register jsfunc, 1121 Register thisObj, Register newTarget) 1122{ 1123 __ Pushq(thisObj); 1124 __ Pushq(newTarget); 1125 __ Pushq(jsfunc); 1126} 1127 1128// output expectedNumArgs (r14) 1129void OptimizedCall::PushArgsWithArgV(ExtendedAssembler *assembler, Register jsfunc, 1130 Register actualNumArgs, Register argV, Label *pushCallThis) 1131{ 1132 Register expectedNumArgs(r14); // output 1133 Register tmp(rax); 1134 Label align16Bytes; 1135 Label copyArguments; 1136 // get expected num Args 1137 __ Movq(Operand(jsfunc, JSFunctionBase::METHOD_OFFSET), tmp); 1138 __ Movq(Operand(tmp, Method::CALL_FIELD_OFFSET), tmp); 1139 __ Shr(MethodLiteral::NumArgsBits::START_BIT, tmp); 1140 __ Andl(((1LU << MethodLiteral::NumArgsBits::SIZE) - 1), tmp); 1141 1142 __ Mov(tmp, expectedNumArgs); 1143 __ Testb(1, expectedNumArgs); 1144 __ Jne(&align16Bytes); 1145 __ PushAlignBytes(); 1146 1147 __ Bind(&align16Bytes); 1148 { 1149 __ Cmpq(actualNumArgs, expectedNumArgs); 1150 __ Jbe(©Arguments); 1151 __ Subq(actualNumArgs, tmp); 1152 PushUndefinedWithArgc(assembler, tmp); 1153 } 1154 __ Bind(©Arguments); 1155 { 1156 __ Cmpq(actualNumArgs, expectedNumArgs); 1157 __ Movq(actualNumArgs, tmp); // rax -> actualNumArgsReg 1158 __ CMovbe(expectedNumArgs, tmp); 1159 __ Cmpq(0, tmp); 1160 __ Je(pushCallThis); 1161 CopyArgumentWithArgV(assembler, tmp, argV); 1162 } 1163} 1164 1165void OptimizedCall::PopJSFunctionArgs(ExtendedAssembler *assembler, Register expectedNumArgs) 1166{ 1167 Label align16Bytes; 1168 __ Testb(1, expectedNumArgs); 1169 __ Je(&align16Bytes); 1170 __ Addq(FRAME_SLOT_SIZE, rsp); 1171 __ Bind(&align16Bytes); 1172 __ Leaq(Operand(expectedNumArgs, Scale::Times8, 0), expectedNumArgs); 1173 __ Addq(DOUBLE_SLOT_SIZE, rsp); // 16: skip actual argc and actual argv 1174 __ Addq(expectedNumArgs, rsp); 1175} 1176 1177void OptimizedCall::PushJSFunctionEntryFrame(ExtendedAssembler *assembler, Register prevFp) 1178{ 1179 __ PushCppCalleeSaveRegisters(); 1180 __ Pushq(rdi); 1181 1182 // construct optimized entry frame 1183 __ Pushq(rbp); 1184 __ Pushq(static_cast<int64_t>(FrameType::OPTIMIZED_ENTRY_FRAME)); 1185 __ Pushq(prevFp); 1186 // 2: skip prevFp and frameType 1187 __ Leaq(Operand(rsp, 2 * FRAME_SLOT_SIZE), rbp); 1188} 1189 1190void OptimizedCall::PopJSFunctionEntryFrame(ExtendedAssembler *assembler, Register glue) 1191{ 1192 Register prevFp(rsi); 1193 __ Popq(prevFp); 1194 __ Addq(FRAME_SLOT_SIZE, rsp); // 8: frame type 1195 __ Popq(rbp); 1196 __ Popq(glue); // caller restore 1197 __ PopCppCalleeSaveRegisters(); // callee restore 1198 __ Movq(prevFp, Operand(glue, JSThread::GlueData::GetLeaveFrameOffset(false))); 1199} 1200 1201// * uint64_t PushOptimizedUnfoldArgVFrame(uintptr_t glue, uint32_t argc, JSTaggedType calltarget, 1202// JSTaggedType new, JSTaggedType this, JSTaggedType, argV[]) 1203// * cc calling convention call js function() 1204// * arguments: 1205// %rdi - glue 1206// %rsi - argc 1207// %rdx - call-target 1208// %rcx - new-target 1209// %r8 - this 1210// %r9 - argv 1211// 1212// * OptimizedUnfoldArgVFrame layout description as the following: 1213// sp ----> |--------------------------| --------------- 1214// | returnAddr | ^ 1215// currentFp--> |--------------------------| | 1216// | prevFp | | 1217// |--------------------------| OptimizedUnfoldArgVFrame 1218// | frameType | | 1219// |--------------------------| | 1220// | currentFp | v 1221// +--------------------------+ --------------- 1222 1223void OptimizedCall::PushOptimizedUnfoldArgVFrame(ExtendedAssembler *assembler, Register callSiteSp) 1224{ 1225 __ Pushq(rbp); 1226 // construct frame 1227 __ Pushq(static_cast<int64_t>(FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME)); 1228 __ Pushq(callSiteSp); 1229 // 2: skip callSiteSp and frameType 1230 __ Leaq(Operand(rsp, 2 * FRAME_SLOT_SIZE), rbp); 1231} 1232 1233void OptimizedCall::PopOptimizedUnfoldArgVFrame(ExtendedAssembler *assembler) 1234{ 1235 Register sp(rsp); 1236 // 2 : 2 means pop call site sp and type 1237 __ Addq(Immediate(2 * FRAME_SLOT_SIZE), sp); 1238 __ Popq(rbp); 1239} 1240 1241// * uint64_t JSCallWithArgV(uintptr_t glue, uint32_t argc, JSTaggedType calltarget, 1242// JSTaggedType new, JSTaggedType this, argV) 1243// * cc calling convention call js function() 1244// * arguments: 1245// %rdi - glue 1246// %rsi - argc 1247// %rdx - call-target 1248// %rcx - new-target 1249// %r8 - this 1250// %r9 - argv 1251// 1252// * OptimizedJSFunctionFrame layout description as the following: 1253// +--------------------------+ 1254// | argn | 1255// |--------------------------| 1256// | argn - 1 | 1257// |--------------------------| 1258// | ..... | 1259// |--------------------------| 1260// | arg2 | 1261// |--------------------------| 1262// | arg1 | 1263// sp ----> |--------------------------| --------------- 1264// | returnAddr | ^ 1265// |--------------------------| | 1266// | callsiteFp | | 1267// |--------------------------| OptimizedJSFunctionFrame 1268// | frameType | | 1269// |--------------------------| | 1270// | call-target | v 1271// +--------------------------+ --------------- 1272 1273void OptimizedCall::GenJSCallWithArgV(ExtendedAssembler *assembler, int id) 1274{ 1275 Register sp(rsp); 1276 Register glue(rdi); 1277 Register actualNumArgs(rsi); 1278 Register jsfunc(rdx); 1279 Register newTarget(rcx); 1280 Register thisObj(r8); 1281 Register argV(r9); 1282 Register callsiteSp = __ AvailableRegister2(); 1283 Label align16Bytes; 1284 Label pushCallThis; 1285 1286 __ Movq(sp, callsiteSp); 1287 __ Addq(Immediate(FRAME_SLOT_SIZE), callsiteSp); // 8 : 8 means skip pc to get last callsitesp 1288 PushOptimizedUnfoldArgVFrame(assembler, callsiteSp); 1289 __ Testb(1, actualNumArgs); 1290 __ Jne(&align16Bytes); 1291 __ PushAlignBytes(); 1292 __ Bind(&align16Bytes); 1293 __ Cmp(Immediate(0), actualNumArgs); 1294 __ Jz(&pushCallThis); 1295 __ Mov(actualNumArgs, rax); 1296 CopyArgumentWithArgV(assembler, rax, argV); 1297 __ Bind(&pushCallThis); 1298 PushMandatoryJSArgs(assembler, jsfunc, thisObj, newTarget); 1299 __ Addq(Immediate(NUM_MANDATORY_JSFUNC_ARGS), actualNumArgs); 1300 __ Pushq(sp); 1301 __ Pushq(actualNumArgs); 1302 __ Movq(glue, rax); 1303 __ CallAssemblerStub(id, false); 1304 __ Mov(Operand(sp, 0), actualNumArgs); 1305 PopJSFunctionArgs(assembler, actualNumArgs); 1306 PopOptimizedUnfoldArgVFrame(assembler); 1307 __ Ret(); 1308} 1309 1310// * uint64_t JSCallWithArgVAndPushArgv(uintptr_t glue, uint32_t argc, JSTaggedType calltarget, 1311// JSTaggedType new, JSTaggedType this, argV) 1312// * cc calling convention call js function() 1313// * arguments: 1314// %rdi - glue 1315// %rsi - argc 1316// %rdx - call-target 1317// %rcx - new-target 1318// %r8 - this 1319// %r9 - argv 1320void OptimizedCall::JSCallWithArgVAndPushArgv(ExtendedAssembler *assembler) 1321{ 1322 __ BindAssemblerStub(RTSTUB_ID(JSCallWithArgVAndPushArgv)); 1323 GenJSCallWithArgV(assembler, RTSTUB_ID(OptimizedCallAndPushArgv)); 1324} 1325 1326void OptimizedCall::JSCallWithArgV(ExtendedAssembler *assembler) 1327{ 1328 __ BindAssemblerStub(RTSTUB_ID(JSCallWithArgV)); 1329 GenJSCallWithArgV(assembler, RTSTUB_ID(CallOptimized)); 1330} 1331 1332void OptimizedCall::SuperCallWithArgV(ExtendedAssembler *assembler) 1333{ 1334 __ BindAssemblerStub(RTSTUB_ID(SuperCallWithArgV)); 1335 GenJSCallWithArgV(assembler, RTSTUB_ID(JSCallNew)); 1336} 1337 1338void OptimizedCall::CallOptimized(ExtendedAssembler *assembler) 1339{ 1340 __ BindAssemblerStub(RTSTUB_ID(CallOptimized)); 1341 Register jsFuncReg = rdi; 1342 Register method = r9; 1343 Register codeAddrReg = rsi; 1344 auto funcSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1; // 1: return addr 1345 __ Movq(Operand(rsp, funcSlotOffset * FRAME_SLOT_SIZE), jsFuncReg); // sp + 24 get jsFunc 1346 __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method 1347 __ Mov(Operand(jsFuncReg, JSFunctionBase::CODE_ENTRY_OFFSET), codeAddrReg); 1348 __ Jmp(codeAddrReg); 1349} 1350 1351// Input: %rdi - glue 1352// %rsi - context 1353void OptimizedCall::DeoptEnterAsmInterp(ExtendedAssembler *assembler) 1354{ 1355 // rdi 1356 Register glueRegister = __ GlueRegister(); 1357 Register context = rsi; 1358 // rax, rdx, rcx, r8, r9, r10, r11 is free 1359 Register tempRegister = rax; 1360 Register opRegister = r10; 1361 Register outputCount = rdx; 1362 Register frameStateBase = rcx; 1363 Register depth = r11; 1364 Label loopBegin; 1365 Label stackOverflow; 1366 Label pushArgv; 1367 __ Movq(Operand(context, AsmStackContext::GetInlineDepthOffset(false)), depth); 1368 __ Leaq(Operand(context, AsmStackContext::GetSize(false)), context); 1369 __ Movq(Immediate(0), r12); 1370 __ Bind(&loopBegin); 1371 __ Movq(Operand(context, 0), outputCount); 1372 __ Leaq(Operand(context, FRAME_SLOT_SIZE), frameStateBase); 1373 __ Cmpq(0, r12); 1374 __ Je(&pushArgv); 1375 __ Movq(rsp, r8); 1376 __ Addq(AsmInterpretedFrame::GetSize(false), r8); 1377 __ Leaq(Operand(frameStateBase, AsmInterpretedFrame::GetBaseOffset(false)), r10); 1378 __ Movq(r8, Operand(r10, InterpretedFrameBase::GetPrevOffset(false))); 1379 __ Testq(15, rsp); // 15: low 4 bits must be 0b0000 1380 __ Jnz(&pushArgv); 1381 __ PushAlignBytes(); 1382 __ Bind(&pushArgv); 1383 // update fp 1384 __ Movq(rsp, Operand(frameStateBase, AsmInterpretedFrame::GetFpOffset(false))); 1385 PushArgsWithArgvAndCheckStack(assembler, glueRegister, outputCount, 1386 frameStateBase, tempRegister, opRegister, &stackOverflow); 1387 __ Leaq(Operand(context, outputCount, Scale::Times8, FRAME_SLOT_SIZE), context); 1388 __ Addq(1, r12); 1389 __ Cmpq(r12, depth); 1390 __ Jae(&loopBegin); 1391 Register callTargetRegister = r8; 1392 Register methodRegister = r9; 1393 { 1394 // r13, rbp, r12, rbx, r14, rsi, rdi 1395 // glue sp pc constpool profile acc hotness 1396 __ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetFunctionOffset(false)), callTargetRegister); 1397 __ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetPcOffset(false)), r12); 1398 __ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetAccOffset(false)), rsi); 1399 __ Movq(Operand(callTargetRegister, JSFunctionBase::METHOD_OFFSET), methodRegister); 1400 1401 __ Leaq(Operand(rsp, AsmInterpretedFrame::GetSize(false)), opRegister); 1402 AsmInterpreterCall::DispatchCall(assembler, r12, opRegister, callTargetRegister, methodRegister, rsi); 1403 } 1404 1405 __ Bind(&stackOverflow); 1406 { 1407 [[maybe_unused]] TempRegisterScope scope(assembler); 1408 Register temp = __ TempRegister(); 1409 AsmInterpreterCall::ThrowStackOverflowExceptionAndReturnToAotFrame(assembler, 1410 glueRegister, rsp, temp); 1411 } 1412} 1413 1414// Input: %rdi - glue 1415void OptimizedCall::DeoptHandlerAsm(ExtendedAssembler *assembler) 1416{ 1417 __ BindAssemblerStub(RTSTUB_ID(DeoptHandlerAsm)); 1418 1419 Register glueReg = rdi; 1420 PushAsmBridgeFrame(assembler); 1421 __ Push(glueReg); 1422 __ PushCppCalleeSaveRegisters(); 1423 1424 __ Movq(rdi, rax); // glue 1425 Register deoptType = rsi; 1426 Register depth = rdx; 1427 __ Subq(FRAME_SLOT_SIZE, rsp); 1428 __ Pushq(depth); 1429 __ Pushq(deoptType); // argv[0] 1430 __ Pushq(2); // 2: argc 1431 __ Pushq(kungfu::RuntimeStubCSigns::ID_DeoptHandler); 1432 __ CallAssemblerStub(RTSTUB_ID(CallRuntime), false); 1433 1434 __ Addq(5 * FRAME_SLOT_SIZE, rsp); // 5: skip runtimeId argc deoptType depth align 1435 1436 Register context = rsi; 1437 __ Movq(rax, context); 1438 1439 Label target; 1440 __ PopCppCalleeSaveRegisters(); 1441 __ Pop(glueReg); 1442 1443 Label stackOverflow; 1444 __ Cmpq(JSTaggedValue::VALUE_EXCEPTION, rax); 1445 __ Je(&stackOverflow); 1446 1447 __ Movq(Operand(context, AsmStackContext::GetCallerFpOffset(false)), rbp); 1448 __ Movq(Operand(context, AsmStackContext::GetCallFrameTopOffset(false)), rsp); 1449 __ Subq(FRAME_SLOT_SIZE, rsp); // skip lr 1450 1451 PushAsmInterpBridgeFrame(assembler); 1452 __ Callq(&target); 1453 PopAsmInterpBridgeFrame(assembler); 1454 __ Ret(); 1455 __ Bind(&target); 1456 DeoptEnterAsmInterp(assembler); 1457 __ Int3(); 1458 1459 __ Bind(&stackOverflow); 1460 { 1461 __ Movq(rdi, rax); 1462 __ Pushq(0); // argc 1463 __ Pushq(kungfu::RuntimeStubCSigns::ID_ThrowStackOverflowException); 1464 __ CallAssemblerStub(RTSTUB_ID(CallRuntime), false); 1465 __ Addq(FRAME_SLOT_SIZE * 3, rsp); // 3 : skip runtimeId argc & type 1466 __ Popq(rbp); 1467 __ Ret(); 1468 } 1469} 1470#undef __ 1471} // namespace panda::ecmascript::x64 1472