• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
30 namespace 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 
JSFunctionEntry(ExtendedAssembler * assembler)52 void 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 
OptimizedCallAndPushArgv(ExtendedAssembler * assembler)120 void 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 
OptimizedCallAsmInterpreter(ExtendedAssembler * assembler)220 void 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 
RemoveArgv(ExtendedAssembler * assembler,Register temp)262 void 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 
CallBuiltinTrampoline(ExtendedAssembler * assembler,Register temp)272 void 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 
CallBuiltinConstructorStub(ExtendedAssembler * assembler,Register builtinStub,Register argv,Register glue,Register temp)323 void 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 
JSProxyCallInternalWithArgV(ExtendedAssembler * assembler)352 void 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 //               +--------------------------+ ---------------
JSCallNew(ExtendedAssembler * assembler)396 void OptimizedCall::JSCallNew(ExtendedAssembler *assembler)
397 {
398     __ BindAssemblerStub(RTSTUB_ID(JSCallNew));
399     GenJSCall(assembler, true);
400 }
401 
JSCall(ExtendedAssembler * assembler)402 void OptimizedCall::JSCall(ExtendedAssembler *assembler)
403 {
404     __ BindAssemblerStub(RTSTUB_ID(JSCall));
405     GenJSCall(assembler, false);
406 }
407 
GenJSCall(ExtendedAssembler * assembler,bool isNew)408 void 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
AOTCallToAsmInterBridge(ExtendedAssembler * assembler)654 void 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
FastCallToAsmInterBridge(ExtendedAssembler * assembler)703 void 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 
PushAsmBridgeFrame(ExtendedAssembler * assembler)825 void OptimizedCall::PushAsmBridgeFrame(ExtendedAssembler *assembler)
826 {
827     __ Pushq(rbp);
828     __ Pushq(static_cast<int32_t>(FrameType::ASM_BRIDGE_FRAME));
829     __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
830 }
831 
PopAsmBridgeFrame(ExtendedAssembler * assembler)832 void OptimizedCall::PopAsmBridgeFrame(ExtendedAssembler *assembler)
833 {
834     __ Addq(FRAME_SLOT_SIZE, rsp);  // skip type
835     __ Popq(rbp);
836 }
837 
JSCallCheck(ExtendedAssembler * assembler,Register jsFuncReg,Label * lNonCallable,Label * lNotJSFunction,Label * lJSFunctionCall)838 void OptimizedCall::JSCallCheck(ExtendedAssembler *assembler, Register jsFuncReg,
839                                 Label *lNonCallable, Label *lNotJSFunction, Label *lJSFunctionCall)
840 {
841     __ Movabs(JSTaggedValue::TAG_INT, rdx); // IsTaggedInt
842     __ And(jsFuncReg, rdx);
843     __ Cmp(0x0, rdx);
844     __ Jne(lNonCallable);
845     __ Cmp(0x0, jsFuncReg); // IsHole
846     __ Je(lNonCallable);
847     __ Movabs(JSTaggedValue::TAG_SPECIAL, rdx);
848     __ And(jsFuncReg, rdx);  // IsSpecial
849     __ Cmp(0x0, rdx);
850     __ Jne(lNonCallable);
851 
852     __ Movq(jsFuncReg, rsi); // save jsFunc
853     __ Movq(Operand(jsFuncReg, JSFunction::HCLASS_OFFSET), rax); // get jsHclass
854     Register jsHclassReg = rax;
855     __ Movl(Operand(jsHclassReg, JSHClass::BIT_FIELD_OFFSET), rax);
856     __ Btl(JSHClass::CallableBit::START_BIT, rax); // IsCallable
857     __ Jnb(lNonCallable);
858 
859     __ Cmpb(static_cast<int32_t>(JSType::JS_FUNCTION_FIRST), rax);
860     __ Jb(lNotJSFunction);
861     __ Cmpb(static_cast<int32_t>(JSType::JS_FUNCTION_LAST), rax);
862     __ Jbe(lJSFunctionCall); // objecttype in (0x04 ~ 0x0c)
863 }
864 
ThrowNonCallableInternal(ExtendedAssembler * assembler,Register glueReg)865 void OptimizedCall::ThrowNonCallableInternal(ExtendedAssembler *assembler, Register glueReg)
866 {
867     __ Pushq(rbp);
868     __ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME)); // set frame type
869     __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
870     __ Movq(MessageString::Message_NonCallable, rax);
871     __ Movabs(JSTaggedValue::TAG_INT, r10);
872     __ Orq(r10, rax);
873     __ Pushq(rax); // message id
874     __ Pushq(1); // argc
875     __ Pushq(RTSTUB_ID(ThrowTypeError)); // runtime id
876     __ Movq(glueReg, rax); // glue
877     __ Movq(kungfu::RuntimeStubCSigns::ID_CallRuntime, r10);
878     __ Movq(Operand(rax, r10, Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r10);
879     __ Callq(r10); // call CallRuntime
880     __ Movabs(JSTaggedValue::VALUE_EXCEPTION, rax); // return exception
881     __ Addq(4 * FRAME_SLOT_SIZE, rsp); // 4: sp + 32 argv
882     __ Pop(rbp);
883     __ Ret();
884 }
885 
JSBoundFunctionCallInternal(ExtendedAssembler * assembler,Register jsFuncReg,Label * jsCall)886 void OptimizedCall::JSBoundFunctionCallInternal(ExtendedAssembler *assembler, Register jsFuncReg, Label *jsCall)
887 {
888     Label lAlign16Bytes2;
889     Label lCopyBoundArgument;
890     Label lCopyArgument2;
891     Label lPushCallTarget;
892     Label lCopyBoundArgumentLoop;
893     Label lPopFrame2;
894     Label slowCall;
895     Label aotCall;
896     Label popArgs;
897     Label isJsFunc;
898     Label isNotClass;
899     __ Pushq(rbp);
900     __ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME));
901     __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
902     __ Pushq(r10); // callee save
903     __ Movq(rsp, rdx);
904     __ Addq(QUADRUPLE_SLOT_SIZE, rdx); // skip return addr, prevFp, frame type and callee save
905     __ Mov(Operand(rdx, 0), rax); // get origin argc
906     __ Mov(Operand(rdx, FRAME_SLOT_SIZE), r9); // get origin argv
907     __ Movq(rax, r10);
908     // get bound target
909     __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_ARGUMENTS_OFFSET), rcx);
910     // get bound length
911     __ Mov(Operand(rcx, TaggedArray::LENGTH_OFFSET), rcx);
912     __ Addq(rcx, r10);
913 
914     // 16 bytes align check
915     __ Testb(1, r10);
916     __ Je(&lAlign16Bytes2);
917     __ PushAlignBytes(); // push zero to align 16 bytes stack
918 
919     __ Bind(&lAlign16Bytes2);
920     {
921         __ Subq(NUM_MANDATORY_JSFUNC_ARGS, rax);
922         __ Cmp(0, rax);
923         __ Je(&lCopyBoundArgument);
924     }
925 
926     __ Bind(&lCopyArgument2);
927     {
928         __ Movq(Operand(rdx, rax, Scale::Times8,
929             (kungfu::ArgumentAccessor::GetFixArgsNum() + 1) * FRAME_SLOT_SIZE), rcx); // argv
930         __ Pushq(rcx);
931         __ Addq(-1, rax);
932         __ Jne(&lCopyArgument2);
933     }
934 
935     __ Bind(&lCopyBoundArgument);
936     {
937         // get bound target
938         __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_ARGUMENTS_OFFSET), rdx);
939         // get bound length
940         __ Mov(Operand(rdx, TaggedArray::LENGTH_OFFSET), rax);
941         __ Addq(TaggedArray::DATA_OFFSET, rdx);
942         __ Cmp(0, rax);
943         __ Je(&lPushCallTarget);
944     }
945     __ Bind(&lCopyBoundArgumentLoop);
946     {
947         __ Addq(-1, rax);
948         __ Movq(Operand(rdx, rax, Scale::Times8, 0), rcx);
949         __ Pushq(rcx);
950         __ Jne(&lCopyBoundArgumentLoop);
951     }
952     __ Bind(&lPushCallTarget);
953     {
954         __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_THIS_OFFSET), r8); // thisObj
955         __ Pushq(r8);
956         __ Pushq(JSTaggedValue::VALUE_UNDEFINED); // newTarget
957         __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_TARGET_OFFSET), rax); // callTarget
958         __ Pushq(rax);
959         __ Pushq(r9);
960         __ Pushq(r10); // push actual arguments
961     }
962     JSCallCheck(assembler, rax, &slowCall, &slowCall, &isJsFunc); // jsfunc -> rsi hclassfiled -> rax
963     __ Jmp(&slowCall);
964     Register jsfunc = rsi;
965     Register compiledCodeFlag = rcx;
966     __ Bind(&isJsFunc);
967     {
968         __ Btq(JSHClass::IsClassConstructorOrPrototypeBit::START_BIT, rax); // is CallConstructor
969         __ Jnb(&isNotClass);
970         __ Btq(JSHClass::ConstructorBit::START_BIT, rax);
971         __ Jb(&slowCall);
972         __ Bind(&isNotClass);
973         __ Movzwq(Operand(rsi, JSFunctionBase::BIT_FIELD_OFFSET), compiledCodeFlag); // compiled code flag
974         __ Btq(JSFunctionBase::IsCompiledCodeBit::START_BIT, compiledCodeFlag); // is compiled code
975         __ Jnb(&slowCall);
976         __ Bind(&aotCall);
977         {
978             // output: glue:rdi argc:rsi calltarget:rdx argv:rcx this:r8 newtarget:r9
979             __ Movq(jsfunc, rdx);
980             __ Movq(r10, rsi);
981             auto funcSlotOffSet = kungfu::ArgumentAccessor::GetFixArgsNum() +
982                                   kungfu::ArgumentAccessor::GetExtraArgsNum();
983             __ Leaq(Operand(rsp, funcSlotOffSet * FRAME_SLOT_SIZE), rcx); // 5: skip argc and argv func new this
984             __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9);
985             __ Movq(kungfu::CommonStubCSigns::JsBoundCallInternal, r10);
986             __ Movq(Operand(rdi, r10, Scale::Times8, JSThread::GlueData::GetCOStubEntriesOffset(false)), rax);
987             __ Callq(rax); // call JSCall
988             __ Jmp(&popArgs);
989         }
990     }
991     __ Bind(&slowCall);
992     {
993         __ Movq(rdi, rax);
994         __ Callq(jsCall); // call JSCall
995         __ Jmp(&popArgs);
996     }
997     __ Bind(&popArgs);
998     {
999         __ Pop(r10);
1000         __ Pop(r9);
1001         __ Leaq(Operand(r10, Scale::Times8, 0), rcx); // 8: disp
1002         __ Addq(rcx, rsp);
1003         __ Testb(1, r10);  // stack 16bytes align check
1004         __ Je(&lPopFrame2);
1005         __ Addq(8, rsp); // 8: align byte
1006     }
1007     __ Bind(&lPopFrame2);
1008     {
1009         __ Pop(r10);
1010         __ Addq(8, rsp); // 8: sp + 8
1011         __ Pop(rbp);
1012         __ Ret();
1013     }
1014 }
1015 
1016 // * uint64_t CallRuntime(uintptr_t glue, uint64_t runtime_id, uint64_t argc, uintptr_t arg0, ...)
1017 // * webkit_jscc calling convention call runtime_id's runtime function(c-abi)
1018 // * Arguments:
1019 //         %rax - glue
1020 //
1021 // * Optimized-leaved-frame layout as the following:
1022 //         +--------------------------+
1023 //         |       argv[N-1]          |
1024 //         |--------------------------|
1025 //         |       . . . . .          |
1026 //         |--------------------------|
1027 //         |       argv[0]            |
1028 //         +--------------------------+-------------
1029 //         |       argc               |            ^
1030 //         |--------------------------|            |
1031 //         |       RuntimeId          |            |
1032 //  sp --> |--------------------------|   OptimizedLeaveFrame
1033 //         |       ret-addr           |            |
1034 //         |--------------------------|            |
1035 //         |       prevFp             |            |
1036 //         |--------------------------|            |
1037 //         |       frameType          |            v
1038 //         +--------------------------+-------------
1039 
CallRuntime(ExtendedAssembler * assembler)1040 void OptimizedCall::CallRuntime(ExtendedAssembler *assembler)
1041 {
1042     __ BindAssemblerStub(RTSTUB_ID(CallRuntime));
1043     __ Pushq(rbp);
1044     __ Movq(rsp, Operand(rax, JSThread::GlueData::GetLeaveFrameOffset(false)));
1045     __ Pushq(static_cast<int32_t>(FrameType::LEAVE_FRAME));
1046     __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);  // 8: skip frame type
1047 
1048     __ Pushq(r10);
1049     __ Pushq(rdx);
1050     __ Pushq(rax);
1051 
1052     __ Movq(rbp, rdx);
1053     // 2: rbp & return address
1054     __ Addq(2 * FRAME_SLOT_SIZE, rdx);
1055 
1056     __ Movq(Operand(rdx, 0), r10);
1057     __ Movq(Operand(rax, r10, Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r10);
1058     __ Movq(rax, rdi);
1059     // 8: argc
1060     __ Movq(Operand(rdx, FRAME_SLOT_SIZE), rsi);
1061     // 2: argv
1062     __ Addq(2 * FRAME_SLOT_SIZE, rdx);
1063     __ Callq(r10);
1064 
1065     // 8: skip rax
1066     __ Addq(FRAME_SLOT_SIZE, rsp);
1067     __ Popq(rdx);
1068     __ Popq(r10);
1069 
1070     // 8: skip frame type
1071     __ Addq(FRAME_SLOT_SIZE, rsp);
1072     __ Popq(rbp);
1073     __ Ret();
1074 }
1075 
1076 // * uint64_t CallRuntimeWithArgv(uintptr_t glue, uint64_t runtime_id, uint64_t argc, uintptr_t argv)
1077 // * cc calling convention call runtime_id's runtion function(c-abi)
1078 // * Arguments:
1079 //         %rdi - glue
1080 //         %rsi - runtime_id
1081 //         %edx - argc
1082 //         %rcx - argv
1083 //
1084 // * Optimized-leaved-frame-with-argv layout as the following:
1085 //         +--------------------------+
1086 //         |       argv[]             |
1087 //         +--------------------------+-------------
1088 //         |       argc               |            ^
1089 //         |--------------------------|            |
1090 //         |       RuntimeId          |   OptimizedWithArgvLeaveFrame
1091 //  sp --> |--------------------------|            |
1092 //         |       returnAddr         |            |
1093 //         |--------------------------|            |
1094 //         |       callsiteFp         |            |
1095 //         |--------------------------|            |
1096 //         |       frameType          |            v
1097 //         +--------------------------+-------------
1098 
CallRuntimeWithArgv(ExtendedAssembler * assembler)1099 void OptimizedCall::CallRuntimeWithArgv(ExtendedAssembler *assembler)
1100 {
1101     __ BindAssemblerStub(RTSTUB_ID(CallRuntimeWithArgv));
1102     Register glueReg = rdi;
1103     Register runtimeIdReg = rsi;
1104     Register argcReg = rdx;
1105     Register argvReg = rcx;
1106 
1107     __ Movq(rsp, r8);
1108     Register returnAddrReg = r9;
1109     __ Movq(Operand(rsp, 0), returnAddrReg);
1110     __ Pushq(argvReg); // argv[]
1111     __ Pushq(argcReg); // argc
1112     __ Pushq(runtimeIdReg); // runtime_id
1113     __ Pushq(returnAddrReg); // returnAddr
1114 
1115     // construct leave frame
1116     __ Pushq(rbp);
1117     __ Movq(rsp, Operand(glueReg, JSThread::GlueData::GetLeaveFrameOffset(false))); // save to thread->leaveFrame_
1118     __ Pushq(static_cast<int32_t>(FrameType::LEAVE_FRAME_WITH_ARGV));
1119     __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
1120 
1121     __ Movq(Operand(glueReg, runtimeIdReg, Scale::Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r9);
1122     __ Movq(argcReg, rsi); // argc
1123     __ Movq(argvReg, rdx); // argv
1124     __ Pushq(r8);
1125     __ Callq(r9);
1126     __ Popq(r8);
1127     __ Addq(FRAME_SLOT_SIZE, rsp); // 8: skip type
1128     __ Popq(rbp);
1129     __ Movq(r8, rsp);
1130     __ Ret();
1131 }
1132 
PushMandatoryJSArgs(ExtendedAssembler * assembler,Register jsfunc,Register thisObj,Register newTarget)1133 void OptimizedCall::PushMandatoryJSArgs(ExtendedAssembler *assembler, Register jsfunc,
1134                                         Register thisObj, Register newTarget)
1135 {
1136     __ Pushq(thisObj);
1137     __ Pushq(newTarget);
1138     __ Pushq(jsfunc);
1139 }
1140 
1141 // output expectedNumArgs (r14)
PushArgsWithArgV(ExtendedAssembler * assembler,Register jsfunc,Register actualNumArgs,Register argV,Label * pushCallThis)1142 void OptimizedCall::PushArgsWithArgV(ExtendedAssembler *assembler, Register jsfunc,
1143                                      Register actualNumArgs, Register argV, Label *pushCallThis)
1144 {
1145     Register expectedNumArgs(r14); // output
1146     Register tmp(rax);
1147     Label align16Bytes;
1148     Label copyArguments;
1149     // get expected num Args
1150     __ Movq(Operand(jsfunc, JSFunctionBase::METHOD_OFFSET), tmp);
1151     __ Movq(Operand(tmp, Method::CALL_FIELD_OFFSET), tmp);
1152     __ Shr(MethodLiteral::NumArgsBits::START_BIT, tmp);
1153     __ Andl(((1LU <<  MethodLiteral::NumArgsBits::SIZE) - 1), tmp);
1154 
1155     __ Mov(tmp, expectedNumArgs);
1156     __ Testb(1, expectedNumArgs);
1157     __ Jne(&align16Bytes);
1158     __ PushAlignBytes();
1159 
1160     __ Bind(&align16Bytes);
1161     {
1162         __ Cmpq(actualNumArgs, expectedNumArgs);
1163         __ Jbe(&copyArguments);
1164         __ Subq(actualNumArgs, tmp);
1165         PushUndefinedWithArgc(assembler, tmp);
1166     }
1167     __ Bind(&copyArguments);
1168     {
1169         __ Cmpq(actualNumArgs, expectedNumArgs);
1170         __ Movq(actualNumArgs, tmp);     // rax -> actualNumArgsReg
1171         __ CMovbe(expectedNumArgs, tmp);
1172         __ Cmpq(0, tmp);
1173         __ Je(pushCallThis);
1174         CopyArgumentWithArgV(assembler, tmp, argV);
1175     }
1176 }
1177 
PopJSFunctionArgs(ExtendedAssembler * assembler,Register expectedNumArgs)1178 void OptimizedCall::PopJSFunctionArgs(ExtendedAssembler *assembler, Register expectedNumArgs)
1179 {
1180     Label align16Bytes;
1181     __ Testb(1, expectedNumArgs);
1182     __ Je(&align16Bytes);
1183     __ Addq(FRAME_SLOT_SIZE, rsp);
1184     __ Bind(&align16Bytes);
1185     __ Leaq(Operand(expectedNumArgs, Scale::Times8, 0), expectedNumArgs);
1186     __ Addq(DOUBLE_SLOT_SIZE, rsp); // 16: skip actual argc and actual argv
1187     __ Addq(expectedNumArgs, rsp);
1188 }
1189 
PushJSFunctionEntryFrame(ExtendedAssembler * assembler,Register prevFp)1190 void OptimizedCall::PushJSFunctionEntryFrame(ExtendedAssembler *assembler, Register prevFp)
1191 {
1192     __ PushCppCalleeSaveRegisters();
1193     __ Pushq(rdi);
1194 
1195     // construct optimized entry frame
1196     __ Pushq(rbp);
1197     __ Pushq(static_cast<int64_t>(FrameType::OPTIMIZED_ENTRY_FRAME));
1198     __ Pushq(prevFp);
1199     // 2: skip prevFp and frameType
1200     __ Leaq(Operand(rsp, 2 * FRAME_SLOT_SIZE), rbp);
1201 }
1202 
PopJSFunctionEntryFrame(ExtendedAssembler * assembler,Register glue)1203 void OptimizedCall::PopJSFunctionEntryFrame(ExtendedAssembler *assembler, Register glue)
1204 {
1205     Register prevFp(rsi);
1206     __ Popq(prevFp);
1207     __ Addq(FRAME_SLOT_SIZE, rsp); // 8: frame type
1208     __ Popq(rbp);
1209     __ Popq(glue); // caller restore
1210     __ PopCppCalleeSaveRegisters(); // callee restore
1211     __ Movq(prevFp, Operand(glue, JSThread::GlueData::GetLeaveFrameOffset(false)));
1212 }
1213 
1214 // * uint64_t PushOptimizedUnfoldArgVFrame(uintptr_t glue, uint32_t argc, JSTaggedType calltarget,
1215 //                                         JSTaggedType new, JSTaggedType this, JSTaggedType, argV[])
1216 // * cc calling convention call js function()
1217 // * arguments:
1218 //              %rdi - glue
1219 //              %rsi - argc
1220 //              %rdx - call-target
1221 //              %rcx - new-target
1222 //              %r8  - this
1223 //              %r9  - argv
1224 //
1225 // * OptimizedUnfoldArgVFrame layout description as the following:
1226 //      sp ----> |--------------------------| ---------------
1227 //               |       returnAddr         |               ^
1228 //  currentFp--> |--------------------------|               |
1229 //               |       prevFp             |               |
1230 //               |--------------------------|   OptimizedUnfoldArgVFrame
1231 //               |       frameType          |               |
1232 //               |--------------------------|               |
1233 //               |       currentFp          |               v
1234 //               +--------------------------+ ---------------
1235 
PushOptimizedUnfoldArgVFrame(ExtendedAssembler * assembler,Register callSiteSp)1236 void OptimizedCall::PushOptimizedUnfoldArgVFrame(ExtendedAssembler *assembler, Register callSiteSp)
1237 {
1238     __ Pushq(rbp);
1239     // construct frame
1240     __ Pushq(static_cast<int64_t>(FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME));
1241     __ Pushq(callSiteSp);
1242     // 2: skip callSiteSp and frameType
1243     __ Leaq(Operand(rsp, 2 * FRAME_SLOT_SIZE), rbp);
1244 }
1245 
PopOptimizedUnfoldArgVFrame(ExtendedAssembler * assembler)1246 void OptimizedCall::PopOptimizedUnfoldArgVFrame(ExtendedAssembler *assembler)
1247 {
1248     Register sp(rsp);
1249     // 2 : 2 means pop call site sp and type
1250     __ Addq(Immediate(2 * FRAME_SLOT_SIZE), sp);
1251     __ Popq(rbp);
1252 }
1253 
1254 // * uint64_t JSCallWithArgV(uintptr_t glue, uint32_t argc, JSTaggedType calltarget,
1255 //                          JSTaggedType new, JSTaggedType this, argV)
1256 // * cc calling convention call js function()
1257 // * arguments:
1258 //              %rdi - glue
1259 //              %rsi - argc
1260 //              %rdx - call-target
1261 //              %rcx - new-target
1262 //              %r8  - this
1263 //              %r9  - argv
1264 //
1265 // * OptimizedJSFunctionFrame layout description as the following:
1266 //               +--------------------------+
1267 //               |       argn               |
1268 //               |--------------------------|
1269 //               |       argn - 1           |
1270 //               |--------------------------|
1271 //               |       .....              |
1272 //               |--------------------------|
1273 //               |       arg2               |
1274 //               |--------------------------|
1275 //               |       arg1               |
1276 //      sp ----> |--------------------------| ---------------
1277 //               |       returnAddr         |               ^
1278 //               |--------------------------|               |
1279 //               |       callsiteFp         |               |
1280 //               |--------------------------|   OptimizedJSFunctionFrame
1281 //               |       frameType          |               |
1282 //               |--------------------------|               |
1283 //               |       call-target        |               v
1284 //               +--------------------------+ ---------------
1285 
GenJSCallWithArgV(ExtendedAssembler * assembler,int id)1286 void OptimizedCall::GenJSCallWithArgV(ExtendedAssembler *assembler, int id)
1287 {
1288     Register sp(rsp);
1289     Register glue(rdi);
1290     Register actualNumArgs(rsi);
1291     Register jsfunc(rdx);
1292     Register newTarget(rcx);
1293     Register thisObj(r8);
1294     Register argV(r9);
1295     Register callsiteSp = __ AvailableRegister2();
1296     Label align16Bytes;
1297     Label pushCallThis;
1298 
1299     __ Movq(sp, callsiteSp);
1300     __ Addq(Immediate(FRAME_SLOT_SIZE), callsiteSp);   // 8 : 8 means skip pc to get last callsitesp
1301     PushOptimizedUnfoldArgVFrame(assembler, callsiteSp);
1302     __ Testb(1, actualNumArgs);
1303     __ Jne(&align16Bytes);
1304     __ PushAlignBytes();
1305     __ Bind(&align16Bytes);
1306     __ Cmp(Immediate(0), actualNumArgs);
1307     __ Jz(&pushCallThis);
1308     __ Mov(actualNumArgs, rax);
1309     CopyArgumentWithArgV(assembler, rax, argV);
1310     __ Bind(&pushCallThis);
1311     PushMandatoryJSArgs(assembler, jsfunc, thisObj, newTarget);
1312     __ Addq(Immediate(NUM_MANDATORY_JSFUNC_ARGS), actualNumArgs);
1313     __ Pushq(sp);
1314     __ Pushq(actualNumArgs);
1315     __ Movq(glue, rax);
1316     __ CallAssemblerStub(id, false);
1317     __ Mov(Operand(sp, 0), actualNumArgs);
1318     PopJSFunctionArgs(assembler, actualNumArgs);
1319     PopOptimizedUnfoldArgVFrame(assembler);
1320     __ Ret();
1321 }
1322 
1323 // * uint64_t JSCallWithArgVAndPushArgv(uintptr_t glue, uint32_t argc, JSTaggedType calltarget,
1324 //                          JSTaggedType new, JSTaggedType this, argV)
1325 // * cc calling convention call js function()
1326 // * arguments:
1327 //              %rdi - glue
1328 //              %rsi - argc
1329 //              %rdx - call-target
1330 //              %rcx - new-target
1331 //              %r8  - this
1332 //              %r9  - argv
JSCallWithArgVAndPushArgv(ExtendedAssembler * assembler)1333 void OptimizedCall::JSCallWithArgVAndPushArgv(ExtendedAssembler *assembler)
1334 {
1335     __ BindAssemblerStub(RTSTUB_ID(JSCallWithArgVAndPushArgv));
1336     GenJSCallWithArgV(assembler, RTSTUB_ID(OptimizedCallAndPushArgv));
1337 }
1338 
JSCallWithArgV(ExtendedAssembler * assembler)1339 void OptimizedCall::JSCallWithArgV(ExtendedAssembler *assembler)
1340 {
1341     __ BindAssemblerStub(RTSTUB_ID(JSCallWithArgV));
1342     GenJSCallWithArgV(assembler, RTSTUB_ID(CallOptimized));
1343 }
1344 
SuperCallWithArgV(ExtendedAssembler * assembler)1345 void OptimizedCall::SuperCallWithArgV(ExtendedAssembler *assembler)
1346 {
1347     __ BindAssemblerStub(RTSTUB_ID(SuperCallWithArgV));
1348     GenJSCallWithArgV(assembler, RTSTUB_ID(JSCallNew));
1349 }
1350 
CallOptimized(ExtendedAssembler * assembler)1351 void OptimizedCall::CallOptimized(ExtendedAssembler *assembler)
1352 {
1353     __ BindAssemblerStub(RTSTUB_ID(CallOptimized));
1354     Register jsFuncReg = rdi;
1355     Register method = r9;
1356     Register codeAddrReg = rsi;
1357     auto funcSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1;  // 1: return addr
1358     __ Movq(Operand(rsp, funcSlotOffset * FRAME_SLOT_SIZE), jsFuncReg); // sp + 24 get jsFunc
1359     __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method
1360     __ Mov(Operand(jsFuncReg, JSFunctionBase::CODE_ENTRY_OFFSET), codeAddrReg);
1361     __ Jmp(codeAddrReg);
1362 }
1363 
1364 // Input: %rdi - glue
1365 //        %rsi - context
DeoptEnterAsmInterp(ExtendedAssembler * assembler)1366 void OptimizedCall::DeoptEnterAsmInterp(ExtendedAssembler *assembler)
1367 {
1368     // rdi
1369     Register glueRegister = __ GlueRegister();
1370     Register context = rsi;
1371     // rax, rdx, rcx, r8, r9, r10, r11 is free
1372     Register tempRegister = rax;
1373     Register opRegister = r10;
1374     Register outputCount = rdx;
1375     Register frameStateBase = rcx;
1376     Register depth = r11;
1377     Label loopBegin;
1378     Label stackOverflow;
1379     Label pushArgv;
1380     __ Movq(Operand(context, AsmStackContext::GetInlineDepthOffset(false)), depth);
1381     __ Leaq(Operand(context, AsmStackContext::GetSize(false)), context);
1382     __ Movq(Immediate(0), r12);
1383     __ Bind(&loopBegin);
1384     __ Movq(Operand(context, 0), outputCount);
1385     __ Leaq(Operand(context, FRAME_SLOT_SIZE), frameStateBase);
1386     __ Cmpq(0, r12);
1387     __ Je(&pushArgv);
1388     __ Movq(rsp, r8);
1389     __ Addq(AsmInterpretedFrame::GetSize(false), r8);
1390     __ Leaq(Operand(frameStateBase, AsmInterpretedFrame::GetBaseOffset(false)), r10);
1391     __ Movq(r8, Operand(r10, InterpretedFrameBase::GetPrevOffset(false)));
1392     __ Testq(15, rsp);  // 15: low 4 bits must be 0b0000
1393     __ Jnz(&pushArgv);
1394     __ PushAlignBytes();
1395     __ Bind(&pushArgv);
1396     // update fp
1397     __ Movq(rsp, Operand(frameStateBase, AsmInterpretedFrame::GetFpOffset(false)));
1398     PushArgsWithArgvAndCheckStack(assembler, glueRegister, outputCount,
1399         frameStateBase, tempRegister, opRegister, &stackOverflow);
1400     __ Leaq(Operand(context, outputCount, Scale::Times8, FRAME_SLOT_SIZE), context);
1401     __ Addq(1, r12);
1402     __ Cmpq(r12, depth);
1403     __ Jae(&loopBegin);
1404     Register callTargetRegister = r8;
1405     Register methodRegister = r9;
1406     {
1407         // r13, rbp, r12, rbx,      r14,     rsi,  rdi
1408         // glue sp   pc  constpool  profile  acc   hotness
1409         __ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetFunctionOffset(false)), callTargetRegister);
1410         __ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetPcOffset(false)), r12);
1411         __ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetAccOffset(false)), rsi);
1412         __ Movq(Operand(callTargetRegister, JSFunctionBase::METHOD_OFFSET), methodRegister);
1413 
1414         __ Leaq(Operand(rsp, AsmInterpretedFrame::GetSize(false)), opRegister);
1415         AsmInterpreterCall::DispatchCall(assembler, r12, opRegister, callTargetRegister, methodRegister, rsi);
1416     }
1417 
1418     __ Bind(&stackOverflow);
1419     {
1420         [[maybe_unused]] TempRegisterScope scope(assembler);
1421         Register temp = __ TempRegister();
1422         AsmInterpreterCall::ThrowStackOverflowExceptionAndReturn(assembler,
1423             glueRegister, rsp, temp);
1424     }
1425 }
1426 
1427 // Input: %rdi - glue
DeoptHandlerAsm(ExtendedAssembler * assembler)1428 void OptimizedCall::DeoptHandlerAsm(ExtendedAssembler *assembler)
1429 {
1430     __ BindAssemblerStub(RTSTUB_ID(DeoptHandlerAsm));
1431 
1432     Register glueReg = rdi;
1433     PushAsmBridgeFrame(assembler);
1434     __ Push(glueReg);
1435     __ PushCppCalleeSaveRegisters();
1436 
1437     __ Movq(rdi, rax); // glue
1438     Register deoptType = rsi;
1439     Register depth = rdx;
1440     __ Subq(FRAME_SLOT_SIZE, rsp);
1441     __ Pushq(depth);
1442     __ Pushq(deoptType);  // argv[0]
1443     __ Pushq(2);  // 2: argc
1444     __ Pushq(kungfu::RuntimeStubCSigns::ID_DeoptHandler);
1445     __ CallAssemblerStub(RTSTUB_ID(CallRuntime), false);
1446 
1447     __ Addq(5 * FRAME_SLOT_SIZE, rsp); // 5: skip runtimeId argc deoptType depth align
1448 
1449     Register context = rsi;
1450     __ Movq(rax, context);
1451 
1452     Label target;
1453     __ PopCppCalleeSaveRegisters();
1454     __ Pop(glueReg);
1455 
1456     Label stackOverflow;
1457     __ Cmpq(JSTaggedValue::VALUE_EXCEPTION, rax);
1458     __ Je(&stackOverflow);
1459 
1460     __ Movq(Operand(context, AsmStackContext::GetCallerFpOffset(false)), rbp);
1461     __ Movq(Operand(context, AsmStackContext::GetCallFrameTopOffset(false)), rsp);
1462     __ Subq(FRAME_SLOT_SIZE, rsp); // skip lr
1463 
1464     PushAsmInterpBridgeFrame(assembler);
1465     __ Callq(&target);
1466     PopAsmInterpBridgeFrame(assembler);
1467     __ Ret();
1468     __ Bind(&target);
1469     DeoptEnterAsmInterp(assembler);
1470     __ Int3();
1471 
1472     __ Bind(&stackOverflow);
1473     {
1474         __ Movq(rdi, rax);
1475         __ Pushq(0); // argc
1476         __ Pushq(kungfu::RuntimeStubCSigns::ID_ThrowStackOverflowException);
1477         __ CallAssemblerStub(RTSTUB_ID(CallRuntime), false);
1478         __ Addq(FRAME_SLOT_SIZE * 3, rsp); // 3 : skip runtimeId argc & type
1479         __ Popq(rbp);
1480         __ Ret();
1481     }
1482 }
1483 #undef __
1484 }  // namespace panda::ecmascript::x64
1485