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(&copyArguments);
1151        __ Subq(actualNumArgs, tmp);
1152        PushUndefinedWithArgc(assembler, tmp);
1153    }
1154    __ Bind(&copyArguments);
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