• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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/common_stubs.h"
20 #include "ecmascript/compiler/rt_call_signature.h"
21 #include "ecmascript/compiler/argument_accessor.h"
22 #include "ecmascript/deoptimizer/deoptimizer.h"
23 #include "ecmascript/ecma_runtime_call_info.h"
24 #include "ecmascript/frames.h"
25 #include "ecmascript/js_function.h"
26 #include "ecmascript/js_thread.h"
27 #include "ecmascript/message_string.h"
28 #include "ecmascript/method.h"
29 #include "ecmascript/runtime_call_id.h"
30 
31 namespace panda::ecmascript::x64 {
32 #define __ assembler->
33 
34 // * uint64_t JSFunctionEntry(uintptr_t glue, uint32_t actualNumArgs, const JSTaggedType argV[], uintptr_t prevFp)
35 // * Arguments:
36 //        %rdi - glue
37 //        %rsi - actualNumArgs
38 //        %rdx - argV
39 //        %rcx - prevFp
40 //        %r8 - needPushUndefined
41 //
42 // * The JSFunctionEntry Frame's structure is illustrated as the following:
43 //          +--------------------------+
44 //          |      . . . . . .         |
45 //  sp ---> +--------------------------+ -----------------
46 //          |        prevFP            |                 ^
47 //          |--------------------------|                 |
48 //          |       frameType          |      JSFunctionEntryFrame
49 //          |--------------------------|                 |
50 //          |    preLeaveFrameFp       |                 v
51 //          +--------------------------+ -----------------
52 
JSFunctionEntry(ExtendedAssembler * assembler)53 void OptimizedCall::JSFunctionEntry(ExtendedAssembler *assembler)
54 {
55     __ BindAssemblerStub(RTSTUB_ID(JSFunctionEntry));
56     Register glueReg = rdi;
57     Register argv = rdx;
58     Register prevFpReg = rcx;
59     Register needPushUndefined = r8;
60     Label lJSCallWithArgVAndPushUndefined;
61     Label lPopFrame;
62     PushJSFunctionEntryFrame(assembler, prevFpReg);
63     __ Movq(argv, rbx);
64     __ Movq(needPushUndefined, r12);
65     __ Movq(Operand(rbx, 0), rdx);
66     __ Movq(Operand(rbx, FRAME_SLOT_SIZE), rcx);
67     __ Movq(Operand(rbx, DOUBLE_SLOT_SIZE), r8);
68     __ Addq(TRIPLE_SLOT_SIZE, rbx);
69     __ Movq(rbx, r9);
70     __ Cmp(1, r12);
71     __ Je(&lJSCallWithArgVAndPushUndefined);
72     __ CallAssemblerStub(RTSTUB_ID(JSCallWithArgV), false);
73     __ Jmp(&lPopFrame);
74 
75     __ Bind(&lJSCallWithArgVAndPushUndefined);
76     __ CallAssemblerStub(RTSTUB_ID(JSCallWithArgVAndPushUndefined), false);
77 
78     __ Bind(&lPopFrame);
79     __ Popq(prevFpReg);
80     __ Addq(FRAME_SLOT_SIZE, rsp); // 8: frame type
81     __ Popq(rbp);
82     __ Popq(glueReg); // caller restore
83     __ PopCppCalleeSaveRegisters(); // callee restore
84     __ Movq(prevFpReg, Operand(glueReg, JSThread::GlueData::GetLeaveFrameOffset(false)));
85     __ Ret();
86 }
87 
88 // * uint64_t OptimizedCallAndPushUndefined(uintptr_t glue, uint32_t argc, JSTaggedType calltarget, JSTaggedType new,
89 //                   JSTaggedType this, arg[0], arg[1], arg[2], ..., arg[N-1])
90 // * webkit_jscc calling convention call js function()
91 //
92 // * OptimizedJSFunctionFrame layout description as the following:
93 //               +--------------------------+
94 //               |        arg[N-1]          |
95 //               +--------------------------+
96 //               |       ...                |
97 //               +--------------------------+
98 //               |       arg[1]             |
99 //               +--------------------------+
100 //               |       arg[0]             |
101 //               +--------------------------+
102 //               |       this               |
103 //               +--------------------------+
104 //               |       new-target         |
105 //               +--------------------------+
106 //               |       call-target        |
107 //               |--------------------------|
108 //               |       argc               |
109 //               |--------------------------| ---------------
110 //               |       returnAddr         |               ^
111 //      sp ----> |--------------------------|               |
112 //               |       callsiteFp         |               |
113 //               |--------------------------|   OptimizedJSFunctionFrame
114 //               |       frameType          |               |
115 //               |--------------------------|               |
116 //               |       call-target        |               v
117 //               +--------------------------+ ---------------
OptimizedCallAndPushUndefined(ExtendedAssembler * assembler)118 void OptimizedCall::OptimizedCallAndPushUndefined(ExtendedAssembler *assembler)
119 {
120     __ BindAssemblerStub(RTSTUB_ID(OptimizedCallAndPushUndefined));
121     Register jsFuncReg = rdi;
122     Register method = r9;
123     Register codeAddrReg = rsi;
124     __ Movq(Operand(rsp, DOUBLE_SLOT_SIZE), jsFuncReg); // sp + 16 get jsFunc
125     __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method
126     __ Mov(Operand(method, Method::CODE_ENTRY_OFFSET), codeAddrReg);
127 
128     Register methodCallField = rcx;
129     __ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField); // get call field
130     __ Shr(MethodLiteral::NumArgsBits::START_BIT, methodCallField);
131     __ Andl(((1LU <<  MethodLiteral::NumArgsBits::SIZE) - 1), methodCallField);
132     __ Addl(NUM_MANDATORY_JSFUNC_ARGS, methodCallField); // add mandatory argumentr
133 
134     __ Movl(Operand(rsp, FRAME_SLOT_SIZE), rdx); // argc rdx
135     __ Movq(rsp, r8);
136     Register argvReg = r8;
137     auto funcSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1;  // 1: return addr
138     __ Addq(funcSlotOffset * FRAME_SLOT_SIZE, argvReg); // skip return addr and argc
139 
140     Register expectedNumArgsReg = rcx;
141     Register actualNumArgsReg = rdx;
142 
143     Label lCopyExtraAument1;
144     Label lCopyLoop1;
145     Label lPopFrame1;
146     __ Pushq(rbp);
147     __ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME));
148     __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
149     // callee save
150     __ Pushq(r14);
151     __ Pushq(rbx);
152     __ Pushq(rax);
153 
154     // 16 bytes align check
155     __ Movl(expectedNumArgsReg, r14);
156     __ Testb(1, r14);
157     __ Jne(&lCopyExtraAument1);
158     __ Pushq(0);
159 
160     __ Bind(&lCopyExtraAument1); // copy undefined value to stack
161     __ Pushq(JSTaggedValue::VALUE_UNDEFINED);
162     __ Addq(-1, expectedNumArgsReg);
163     __ Cmpq(actualNumArgsReg, expectedNumArgsReg);
164     __ Ja(&lCopyExtraAument1);
165 
166     __ Bind(&lCopyLoop1);
167     __ Movq(Operand(argvReg, expectedNumArgsReg, Scale::Times8, -FRAME_SLOT_SIZE), rbx); // -8: stack index
168     __ Pushq(rbx);
169     __ Addq(-1, expectedNumArgsReg);
170     __ Jne(&lCopyLoop1);
171     __ Pushq(actualNumArgsReg); // actual argc
172 
173     __ Callq(codeAddrReg); // then call jsFunction
174     __ Leaq(Operand(r14, Scale::Times8, 0), codeAddrReg);
175     __ Addq(codeAddrReg, rsp);
176     __ Addq(FRAME_SLOT_SIZE, rsp); // skip actualNumArgsReg
177     __ Testb(1, r14); // stack 16bytes align check
178     __ Jne(&lPopFrame1);
179     __ Addq(8, rsp); // 8: align byte
180 
181     __ Bind(&lPopFrame1);
182     __ Addq(8, rsp); // 8: skip rax
183     __ Popq(rbx);
184     __ Popq(r14);
185     __ Addq(FRAME_SLOT_SIZE, rsp); // skip frame type
186     __ Pop(rbp);
187     __ Ret();
188 }
189 
OptimizedCallAsmInterpreter(ExtendedAssembler * assembler)190 void OptimizedCall::OptimizedCallAsmInterpreter(ExtendedAssembler *assembler)
191 {
192     Label target;
193     PushAsmInterpBridgeFrame(assembler);
194     __ Callq(&target);
195     PopAsmInterpBridgeFrame(assembler);
196     __ Ret();
197     __ Bind(&target);
198     AsmInterpreterCall::JSCallCommonEntry(assembler, JSCallMode::CALL_FROM_AOT);
199 }
200 
201 // * uint64_t CallBuiltinTrampoline(uintptr_t glue, uintptr_t codeAddress, uint32_t argc, ...)
202 // * webkit_jscc calling convention call runtime_id's runtime function(c-abi)
203 //
204 // * Construct Native Leave Frame Layout:
205 //          +--------------------------+
206 //          |       argv[N-1]          |
207 //          +--------------------------+
208 //          |      . . . . . .         |
209 //          +--------------------------+
210 //          |      argv[3]=a0          |
211 //          +--------------------------+
212 //          |      argv[2]=this        |
213 //          +--------------------------+
214 //          |   argv[1]=new-target     |
215 //          +--------------------------+
216 //          |   argv[0]=call-target    |
217 //          +--------------------------+ -----------------
218 //          |       argc               |                 ^
219 //          |--------------------------|                 |
220 //          |       thread             |                 |
221 //          |--------------------------|                 |
222 //          |       returnAddr         |    OptimizedBuiltinLeaveFrame
223 //  sp ---> |--------------------------|                 |
224 //          |       callsiteFp         |                 |
225 //          |--------------------------|                 |
226 //          |       frameType          |                 |
227 //          |--------------------------|                 |
228 //          |       align byte         |                 v
229 //          +--------------------------+ -----------------
230 
CallBuiltinTrampoline(ExtendedAssembler * assembler)231 void OptimizedCall::CallBuiltinTrampoline(ExtendedAssembler *assembler)
232 {
233     Register glueReg = rax;
234     Register nativeCode = rsi;
235 
236     __ Movq(Operand(rsp, 0), rdx);
237     __ Movq(glueReg, Operand(rsp, 0));
238     __ Push(rdx);
239 
240     AsmInterpreterCall::PushBuiltinFrame(assembler, glueReg, FrameType::BUILTIN_CALL_LEAVE_FRAME);
241     __ Leaq(Operand(rbp, 2 * FRAME_SLOT_SIZE), rdi); // 2: skip rbp & return Addr
242     AsmInterpreterCall::CallNativeInternal(assembler, nativeCode);
243     __ Pop(rdx);
244     __ Movq(rdx, Operand(rsp, 0));
245     __ Ret();
246 }
247 
248 // * uint64_t JSProxyCallInternalWithArgV(uintptr_t glue, JSTaggedType calltarget)
249 // * c++ calling convention call js function
250 // * Arguments:
251 //        %rdi - glue
252 //        %rsi - calltarget
253 
JSProxyCallInternalWithArgV(ExtendedAssembler * assembler)254 void OptimizedCall::JSProxyCallInternalWithArgV(ExtendedAssembler *assembler)
255 {
256     __ BindAssemblerStub(RTSTUB_ID(JSProxyCallInternalWithArgV));
257     Register ccGlueReg = rdi;
258     Register jsccGlueReg = rax;
259     Register callTarget = rsi;
260     __ Movq(ccGlueReg, jsccGlueReg);  // c++ calling convention as webkit_jscc calling convention
261     __ Movq(callTarget, Operand(rsp, DOUBLE_SLOT_SIZE));  // update callTarget slot
262     GenJSCall(assembler, false);
263 }
264 
265 // * uint64_t JSCall(uintptr_t glue, uint32_t argc, JSTaggedType calltarget, JSTaggedType new,
266 //                   JSTaggedType this, arg[0], arg[1], arg[2], ..., arg[N-1])
267 // * webkit_jscc calling convention call js function()
268 //
269 // * OptimizedJSFunctionFrame layout description as the following:
270 //               +--------------------------+
271 //               |        arg[N-1]          |
272 //               +--------------------------+
273 //               |       ...                |
274 //               +--------------------------+
275 //               |       arg[1]             |
276 //               +--------------------------+
277 //               |       arg[0]             |
278 //               +--------------------------+
279 //               |       this               |
280 //               +--------------------------+
281 //               |       new-target         |
282 //               +--------------------------+
283 //               |       call-target        |
284 //               |--------------------------|
285 //               |       argc               |
286 //               |--------------------------| ---------------
287 //               |       returnAddr         |               ^
288 //      sp ----> |--------------------------|               |
289 //               |       callsiteFp         |               |
290 //               |--------------------------|   OptimizedJSFunctionFrame
291 //               |       frameType          |               |
292 //               |--------------------------|               |
293 //               |       call-target        |               v
294 //               +--------------------------+ ---------------
JSCallNew(ExtendedAssembler * assembler)295 void OptimizedCall::JSCallNew(ExtendedAssembler *assembler)
296 {
297     __ BindAssemblerStub(RTSTUB_ID(JSCallNew));
298     GenJSCall(assembler, true);
299 }
300 
JSCall(ExtendedAssembler * assembler)301 void OptimizedCall::JSCall(ExtendedAssembler *assembler)
302 {
303     __ BindAssemblerStub(RTSTUB_ID(JSCall));
304     GenJSCall(assembler, false);
305 }
306 
GenJSCall(ExtendedAssembler * assembler,bool isNew)307 void OptimizedCall::GenJSCall(ExtendedAssembler *assembler, bool isNew)
308 {
309     Label jsCall;
310     Label lJSCallStart;
311     Label lNotJSFunction;
312     Label lNonCallable;
313     Label lJSFunctionCall;
314     Label lJSBoundFunction;
315     Label lJSProxy;
316     Label lCallNativeMethod;
317     Label lCallNativeCpp;
318     Label lCallNativeBuiltinStub;
319     Register glueReg = rax;
320     __ Bind(&jsCall);
321     {
322         __ Movq(glueReg, rdi);
323         glueReg = rdi;
324         __ Movq(Operand(rsp, DOUBLE_SLOT_SIZE), rax); // sp + 16 get jsFunc
325     }
326     __ Bind(&lJSCallStart);
327     Register jsFuncReg = rax;
328     {
329         JSCallCheck(assembler, jsFuncReg, &lNonCallable, &lNotJSFunction, &lJSFunctionCall);
330     }
331 
332     __ Bind(&lNotJSFunction);
333     {
334         __ Cmpb(static_cast<uint8_t>(JSType::JS_BOUND_FUNCTION), rax); // IsBoundFunction
335         __ Je(&lJSBoundFunction);
336         __ Cmpb(static_cast<uint8_t>(JSType::JS_PROXY), rax); // IsJsProxy
337         __ Je(&lJSProxy);
338     }
339 
340     __ Bind(&lNonCallable);
341     {
342         ThrowNonCallableInternal(assembler, glueReg);
343     }
344 
345     __ Bind(&lJSFunctionCall);
346     jsFuncReg = rsi;
347     Register argc = r8;
348     Register methodCallField = rcx;
349     Register method = rdx;
350     Register argV = r9;
351     {
352         Label lCallConstructor;
353         __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method
354         __ Movl(Operand(rsp, FRAME_SLOT_SIZE), argc); // skip return addr
355         __ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField); // get call field
356         __ Btq(MethodLiteral::IsNativeBit::START_BIT, methodCallField); // is native
357         __ Jb(&lCallNativeMethod);
358         if (!isNew) {
359             __ Btq(JSHClass::ClassConstructorBit::START_BIT, rax); // is CallConstructor
360             __ Jb(&lCallConstructor);
361         }
362         __ Movq(rsp, argV);
363         auto argvSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1;  // 1: return addr
364         __ Addq(argvSlotOffset * FRAME_SLOT_SIZE, argV); // skip return addr and argc
365         __ Subq(Immediate(kungfu::ArgumentAccessor::GetFixArgsNum()), argc);
366         // argv + 24 get asm interpreter argv
367         __ Addq(kungfu::ArgumentAccessor::GetFixArgsNum() * FRAME_SLOT_SIZE, argV);
368         OptimizedCallAsmInterpreter(assembler);
369         __ Bind(&lCallConstructor);
370         {
371             __ Pushq(rbp);
372             __ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME)); // set frame type
373             __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
374             __ Pushq(0); // PushAlign
375             __ Pushq(0); // argc
376             __ Pushq(RTSTUB_ID(ThrowCallConstructorException)); // runtime id
377             __ Movq(glueReg, rax); // glue
378             __ Movq(kungfu::RuntimeStubCSigns::ID_CallRuntime, r10);
379             __ Movq(Operand(rax, r10, Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r10);
380             __ Callq(r10); // call CallRuntime
381             __ Addq(4 * FRAME_SLOT_SIZE, rsp);
382             __ Pop(rbp);
383             __ Ret();
384         }
385     }
386 
387     __ Bind(&lCallNativeMethod);
388     {
389         Register nativePointer = rsi;
390         method = rax;
391         __ Movq(jsFuncReg, rdx);
392         __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method);  // get method
393         __ Mov(Operand(method, Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET), nativePointer);  // native pointer
394         __ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField);  // get call field
395         __ Btq(MethodLiteral::IsFastBuiltinBit::START_BIT, methodCallField);  // is builtin stub
396 
397         if (!isNew) {
398             __ Jnb(&lCallNativeCpp);
399             __ Cmpl(NUM_MANDATORY_JSFUNC_ARGS + 3, argc);  // 3:call0, call1, call2, call3
400             __ Jbe(&lCallNativeBuiltinStub);
401         } else {
402             __ Jb(&lCallNativeBuiltinStub);
403         }
404     }
405 
406     __ Bind(&lCallNativeCpp);
407     {
408         __ Movq(glueReg, rax);
409         CallBuiltinTrampoline(assembler);
410     }
411 
412     __ Bind(&lCallNativeBuiltinStub);
413     {
414         Register methodExtraLiteralInfo = rax;
415         __ Mov(Operand(method, Method::EXTRA_LITERAL_INFO_OFFSET), methodExtraLiteralInfo);  // get extra literal
416         __ Shr(MethodLiteral::BuiltinIdBits::START_BIT, methodExtraLiteralInfo);
417         __ Andl(((1LU <<  MethodLiteral::BuiltinIdBits::SIZE) - 1), methodExtraLiteralInfo);  // get builtin stub id
418         if (!isNew) {
419             __ Cmpl(kungfu::BuiltinsStubCSigns::BUILTINS_CONSTRUCTOR_STUB_FIRST, methodExtraLiteralInfo);
420             __ Jnb(&lCallNativeCpp);
421         }
422 
423         __ Movq(glueReg, rdi);
424         __ Movq(methodExtraLiteralInfo, r10);
425         __ Movq(Operand(glueReg, r10, Times8, JSThread::GlueData::GetBuiltinsStubEntriesOffset(false)), r10);
426 
427         __ Movq(argc, r9);
428         __ Movq(Operand(rsp, TRIPLE_SLOT_SIZE), rcx);              // newTarget
429         __ Movq(Operand(rsp, QUADRUPLE_SLOT_SIZE), r8);               // this
430         __ Subq(NUM_MANDATORY_JSFUNC_ARGS, r9);                       // argc
431 
432         Label lCall0;
433         Label lCall1;
434         Label lCall2;
435         Label lCall3;
436         Label lexit;
437         argV = rax;
438 
439         __ Movq(rsp, argV);
440         auto argvSlotOffset = kungfu::ArgumentAccessor::GetFixArgsNum() +
441             kungfu::ArgumentAccessor::GetExtraArgsNum() + 1;  // 1: return addr
442         __ Addq(argvSlotOffset *FRAME_SLOT_SIZE, argV);
443         __ Pushq(rbp);
444         __ Pushq(static_cast<int32_t>(FrameType::ASM_BRIDGE_FRAME));
445         __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
446 
447         if (!isNew) {
448             __ Cmpl(0, r9);  // 0: callarg0
449             __ Je(&lCall0);
450             __ Cmpl(1, r9);  // 1: callarg1
451             __ Je(&lCall1);
452             __ Cmpl(2, r9);  // 2: callarg2
453             __ Je(&lCall2);
454             __ Cmpl(3, r9);  // 3: callarg3
455             __ Je(&lCall3);
456 
457             __ Bind(&lCall0);
458             {
459                 __ PushAlignBytes();
460                 __ Callq(r10);
461                 __ Addq(DOUBLE_SLOT_SIZE, rsp);
462                 __ Jmp(&lexit);
463             }
464 
465             __ Bind(&lCall1);
466             {
467                 __ Movq(Operand(argV, 0), r11);                     // arg0
468                 __ Pushq(r11);
469                 __ Callq(r10);
470                 __ Addq(DOUBLE_SLOT_SIZE, rsp);
471                 __ Jmp(&lexit);
472             }
473 
474             __ Bind(&lCall2);
475             {
476                 __ PushAlignBytes();
477                 __ Movq(Operand(argV, FRAME_SLOT_SIZE), r11);        // arg1
478                 __ Pushq(r11);
479                 __ Movq(Operand(argV, 0), r11);                      // arg0
480                 __ Pushq(r11);
481                 __ Callq(r10);
482                 __ Addq(QUADRUPLE_SLOT_SIZE, rsp);
483                 __ Jmp(&lexit);
484             }
485 
486             __ Bind(&lCall3);
487             {
488                 __ Movq(Operand(argV, DOUBLE_SLOT_SIZE), r11);     // arg2
489                 __ Pushq(r11);
490                 __ Movq(Operand(argV, FRAME_SLOT_SIZE), r11);      // arg1
491                 __ Pushq(r11);
492                 __ Movq(Operand(argV, 0), r11);                    // arg0
493                 __ Pushq(r11);
494                 __ Callq(r10);
495                 __ Addq(QUADRUPLE_SLOT_SIZE, rsp);
496             }
497         } else {
498             __ Pushq(argV);                                        // argv
499             __ Callq(r10);
500             __ Addq(DOUBLE_SLOT_SIZE, rsp);
501         }
502 
503         __ Bind(&lexit);
504         {
505             __ Pop(rbp);
506             __ Ret();
507         }
508     }
509 
510     __ Bind(&lJSBoundFunction);
511     {
512         JSBoundFunctionCallInternal(assembler, jsFuncReg, &jsCall);
513     }
514     __ Bind(&lJSProxy);
515     JSProxyCallInternal(assembler, jsFuncReg);
516 }
517 
JSCallCheck(ExtendedAssembler * assembler,Register jsFuncReg,Label * lNonCallable,Label * lNotJSFunction,Label * lJSFunctionCall)518 void OptimizedCall::JSCallCheck(ExtendedAssembler *assembler, Register jsFuncReg,
519                                 Label *lNonCallable, Label *lNotJSFunction, Label *lJSFunctionCall)
520 {
521     __ Movabs(JSTaggedValue::TAG_INT, rdx); // IsTaggedInt
522     __ And(jsFuncReg, rdx);
523     __ Cmp(0x0, rdx);
524     __ Jne(lNonCallable);
525     __ Cmp(0x0, jsFuncReg); // IsHole
526     __ Je(lNonCallable);
527     __ Movabs(JSTaggedValue::TAG_SPECIAL, rdx);
528     __ And(jsFuncReg, rdx);  // IsSpecial
529     __ Cmp(0x0, rdx);
530     __ Jne(lNonCallable);
531 
532     __ Movq(jsFuncReg, rsi); // save jsFunc
533     __ Movq(Operand(jsFuncReg, JSFunction::HCLASS_OFFSET), rax); // get jsHclass
534     Register jsHclassReg = rax;
535     __ Movl(Operand(jsHclassReg, JSHClass::BIT_FIELD_OFFSET), rax);
536     __ Btl(JSHClass::CallableBit::START_BIT, rax); // IsCallable
537     __ Jnb(lNonCallable);
538 
539     __ Cmpb(static_cast<int32_t>(JSType::JS_FUNCTION_FIRST), rax);
540     __ Jb(lNotJSFunction);
541     __ Cmpb(static_cast<int32_t>(JSType::JS_FUNCTION_LAST), rax);
542     __ Jbe(lJSFunctionCall); // objecttype in (0x04 ~ 0x0c)
543 }
544 
ThrowNonCallableInternal(ExtendedAssembler * assembler,Register glueReg)545 void OptimizedCall::ThrowNonCallableInternal(ExtendedAssembler *assembler, Register glueReg)
546 {
547     __ Pushq(rbp);
548     __ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME)); // set frame type
549     __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
550     __ Movq(MessageString::Message_NonCallable, rax);
551     __ Movabs(JSTaggedValue::TAG_INT, r10);
552     __ Orq(r10, rax);
553     __ Pushq(rax); // message id
554     __ Pushq(1); // argc
555     __ Pushq(RTSTUB_ID(ThrowTypeError)); // runtime id
556     __ Movq(glueReg, rax); // glue
557     __ Movq(kungfu::RuntimeStubCSigns::ID_CallRuntime, r10);
558     __ Movq(Operand(rax, r10, Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r10);
559     __ Callq(r10); // call CallRuntime
560     __ Movabs(JSTaggedValue::VALUE_EXCEPTION, rax); // return exception
561     __ Addq(4 * FRAME_SLOT_SIZE, rsp); // 32: sp + 32 argv
562     __ Pop(rbp);
563     __ Ret();
564 }
565 
JSBoundFunctionCallInternal(ExtendedAssembler * assembler,Register jsFuncReg,Label * jsCall)566 void OptimizedCall::JSBoundFunctionCallInternal(ExtendedAssembler *assembler, Register jsFuncReg, Label *jsCall)
567 {
568     Label lAlign16Bytes2;
569     Label lCopyBoundArgument;
570     Label lCopyArgument2;
571     Label lPushCallTarget;
572     Label lCopyBoundArgumentLoop;
573     Label lPopFrame2;
574     Label slowCall;
575     Label aotCall;
576     Label popArgs;
577     Label isJsFunc;
578     __ Pushq(rbp);
579     __ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME));
580     __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
581     __ Pushq(r10); // callee save
582     __ Movq(rsp, rdx);
583     __ Addq(QUADRUPLE_SLOT_SIZE, rdx); // skip return addr, prevFp, frame type and callee save
584     __ Mov(Operand(rdx, 0), rax); // get origin argc
585     __ Movq(rax, r10);
586     // get bound target
587     __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_ARGUMENTS_OFFSET), rcx);
588     // get bound length
589     __ Mov(Operand(rcx, TaggedArray::LENGTH_OFFSET), rcx);
590     __ Addq(rcx, r10);
591 
592     // 16 bytes align check
593     __ Testb(1, r10);
594     __ Jne(&lAlign16Bytes2);
595     __ PushAlignBytes(); // push zero to align 16 bytes stack
596 
597     __ Bind(&lAlign16Bytes2);
598     {
599         __ Subq(NUM_MANDATORY_JSFUNC_ARGS, rax);
600         __ Cmp(0, rax);
601         __ Je(&lCopyBoundArgument);
602     }
603 
604     __ Bind(&lCopyArgument2);
605     {
606         __ Movq(Operand(rdx, rax, Scale::Times8, kungfu::ArgumentAccessor::GetFixArgsNum() * FRAME_SLOT_SIZE), rcx);
607         __ Pushq(rcx);
608         __ Addq(-1, rax);
609         __ Jne(&lCopyArgument2);
610     }
611 
612     __ Bind(&lCopyBoundArgument);
613     {
614         // get bound target
615         __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_ARGUMENTS_OFFSET), rdx);
616         // get bound length
617         __ Mov(Operand(rdx, TaggedArray::LENGTH_OFFSET), rax);
618         __ Addq(TaggedArray::DATA_OFFSET, rdx);
619         __ Cmp(0, rax);
620         __ Je(&lPushCallTarget);
621     }
622     __ Bind(&lCopyBoundArgumentLoop);
623     {
624         __ Addq(-1, rax);
625         __ Movq(Operand(rdx, rax, Scale::Times8, 0), rcx);
626         __ Pushq(rcx);
627         __ Jne(&lCopyBoundArgumentLoop);
628     }
629     __ Bind(&lPushCallTarget);
630     {
631         __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_THIS_OFFSET), r8); // thisObj
632         __ Pushq(r8);
633         __ Pushq(JSTaggedValue::VALUE_UNDEFINED); // newTarget
634         __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_TARGET_OFFSET), rax); // callTarget
635         __ Pushq(rax);
636         __ Pushq(r10); // push actual arguments
637     }
638     JSCallCheck(assembler, rax, &slowCall, &slowCall, &isJsFunc); // jsfunc -> rsi hclassfiled -> rax
639     __ Jmp(&slowCall);
640     Register jsfunc = rsi;
641     __ Bind(&isJsFunc);
642     {
643         __ Btq(JSHClass::ClassConstructorBit::START_BIT, rax); // is CallConstructor
644         __ Jb(&slowCall);
645         __ Btq(JSHClass::IsOptimizedBit::START_BIT, rax); // is aot
646         __ Jnb(&slowCall);
647         __ Bind(&aotCall);
648         {
649             // output: glue:rdi argc:rsi calltarget:rdx argv:rcx this:r8 newtarget:r9
650             __ Movq(jsfunc, rdx);
651             __ Movq(r10, rsi);
652             __ Leaq(Operand(rsp, 4 * FRAME_SLOT_SIZE), rcx); // 4: skip argc and func new this
653             __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9);
654             __ Movq(kungfu::CommonStubCSigns::JsBoundCallInternal, r10);
655             __ Movq(Operand(rdi, r10, Scale::Times8, JSThread::GlueData::GetCOStubEntriesOffset(false)), rax);
656             __ Callq(rax); // call JSCall
657             __ Jmp(&popArgs);
658         }
659     }
660     __ Bind(&slowCall);
661     {
662         __ Movq(rdi, rax);
663         __ Callq(jsCall); // call JSCall
664         __ Jmp(&popArgs);
665     }
666     __ Bind(&popArgs);
667     {
668         __ Pop(r10);
669         __ Leaq(Operand(r10, Scale::Times8, 0), rcx); // 8: disp
670         __ Addq(rcx, rsp);
671         __ Testb(1, r10);  // stack 16bytes align check
672         __ Jne(&lPopFrame2);
673         __ Addq(8, rsp); // 8: align byte
674     }
675     __ Bind(&lPopFrame2);
676     {
677         __ Pop(r10);
678         __ Addq(8, rsp); // 8: sp + 8
679         __ Pop(rbp);
680         __ Ret();
681     }
682 }
683 
JSProxyCallInternal(ExtendedAssembler * assembler,Register jsFuncReg)684 void OptimizedCall::JSProxyCallInternal(ExtendedAssembler *assembler, Register jsFuncReg)
685 {
686     __ Movq(jsFuncReg, rdx); // calltarget
687     __ Movq(rsp, rcx);
688     __ Addq(FRAME_SLOT_SIZE, rcx); // skip returnAddr
689     __ Mov(Operand(rcx, 0), rsi); // get origin argc
690     __ Addq(kungfu::ArgumentAccessor::GetExtraArgsNum() * FRAME_SLOT_SIZE, rcx); // skip extra args: argc
691     __ Movq(kungfu::CommonStubCSigns::JsProxyCallInternal, r9);
692     __ Movq(Operand(rdi, r9, Scale::Times8, JSThread::GlueData::GetCOStubEntriesOffset(false)), r8);
693     __ Jmp(r8);
694     __ Ret();
695 }
696 
697 // * uint64_t CallRuntime(uintptr_t glue, uint64_t runtime_id, uint64_t argc, uintptr_t arg0, ...)
698 // * webkit_jscc calling convention call runtime_id's runtime function(c-abi)
699 // * Arguments:
700 //         %rax - glue
701 //
702 // * Optimized-leaved-frame layout as the following:
703 //         +--------------------------+
704 //         |       argv[N-1]          |
705 //         |--------------------------|
706 //         |       . . . . .          |
707 //         |--------------------------|
708 //         |       argv[0]            |
709 //         +--------------------------+-------------
710 //         |       argc               |            ^
711 //         |--------------------------|            |
712 //         |       RuntimeId          |            |
713 //  sp --> |--------------------------|   OptimizedLeaveFrame
714 //         |       ret-addr           |            |
715 //         |--------------------------|            |
716 //         |       prevFp             |            |
717 //         |--------------------------|            |
718 //         |       frameType          |            v
719 //         +--------------------------+-------------
720 
CallRuntime(ExtendedAssembler * assembler)721 void OptimizedCall::CallRuntime(ExtendedAssembler *assembler)
722 {
723     __ BindAssemblerStub(RTSTUB_ID(CallRuntime));
724     __ Pushq(rbp);
725     __ Movq(rsp, Operand(rax, JSThread::GlueData::GetLeaveFrameOffset(false)));
726     __ Pushq(static_cast<int32_t>(FrameType::LEAVE_FRAME));
727     __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);  // 8: skip frame type
728 
729     __ Pushq(r10);
730     __ Pushq(rdx);
731     __ Pushq(rax);
732 
733     __ Movq(rbp, rdx);
734     // 16: rbp & return address
735     __ Addq(2 * FRAME_SLOT_SIZE, rdx);
736 
737     __ Movq(Operand(rdx, 0), r10);
738     __ Movq(Operand(rax, r10, Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r10);
739     __ Movq(rax, rdi);
740     // 8: argc
741     __ Movq(Operand(rdx, FRAME_SLOT_SIZE), rsi);
742     // 16: argv
743     __ Addq(2 * FRAME_SLOT_SIZE, rdx);
744     __ Callq(r10);
745 
746     // 8: skip rax
747     __ Addq(FRAME_SLOT_SIZE, rsp);
748     __ Popq(rdx);
749     __ Popq(r10);
750 
751     // 8: skip frame type
752     __ Addq(FRAME_SLOT_SIZE, rsp);
753     __ Popq(rbp);
754     __ Ret();
755 }
756 
757 // * uint64_t CallRuntimeWithArgv(uintptr_t glue, uint64_t runtime_id, uint64_t argc, uintptr_t argv)
758 // * cc calling convention call runtime_id's runtion function(c-abi)
759 // * Arguments:
760 //         %rdi - glue
761 //         %rsi - runtime_id
762 //         %edx - argc
763 //         %rcx - argv
764 //
765 // * Optimized-leaved-frame-with-argv layout as the following:
766 //         +--------------------------+
767 //         |       argv[]             |
768 //         +--------------------------+-------------
769 //         |       argc               |            ^
770 //         |--------------------------|            |
771 //         |       RuntimeId          |   OptimizedWithArgvLeaveFrame
772 //  sp --> |--------------------------|            |
773 //         |       returnAddr         |            |
774 //         |--------------------------|            |
775 //         |       callsiteFp         |            |
776 //         |--------------------------|            |
777 //         |       frameType          |            v
778 //         +--------------------------+-------------
779 
CallRuntimeWithArgv(ExtendedAssembler * assembler)780 void OptimizedCall::CallRuntimeWithArgv(ExtendedAssembler *assembler)
781 {
782     __ BindAssemblerStub(RTSTUB_ID(CallRuntimeWithArgv));
783     Register glueReg = rdi;
784     Register runtimeIdReg = rsi;
785     Register argcReg = rdx;
786     Register argvReg = rcx;
787 
788     __ Movq(rsp, r8);
789     Register returnAddrReg = r9;
790     __ Movq(Operand(rsp, 0), returnAddrReg);
791     __ Pushq(argvReg); // argv[]
792     __ Pushq(argcReg); // argc
793     __ Pushq(runtimeIdReg); // runtime_id
794     __ Pushq(returnAddrReg); // returnAddr
795 
796     // construct leave frame
797     __ Pushq(rbp);
798     __ Movq(rsp, Operand(glueReg, JSThread::GlueData::GetLeaveFrameOffset(false))); // save to thread->leaveFrame_
799     __ Pushq(static_cast<int32_t>(FrameType::LEAVE_FRAME_WITH_ARGV));
800     __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
801 
802     __ Movq(Operand(glueReg, runtimeIdReg, Scale::Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r9);
803     __ Movq(argcReg, rsi); // argc
804     __ Movq(argvReg, rdx); // argv
805     __ Pushq(r8);
806     __ Callq(r9);
807     __ Popq(r8);
808     __ Addq(FRAME_SLOT_SIZE, rsp); // 8: skip type
809     __ Popq(rbp);
810     __ Movq(r8, rsp);
811     __ Ret();
812 }
813 
PushMandatoryJSArgs(ExtendedAssembler * assembler,Register jsfunc,Register thisObj,Register newTarget)814 void OptimizedCall::PushMandatoryJSArgs(ExtendedAssembler *assembler, Register jsfunc,
815                                         Register thisObj, Register newTarget)
816 {
817     __ Pushq(thisObj);
818     __ Pushq(newTarget);
819     __ Pushq(jsfunc);
820 }
821 
822 // output expectedNumArgs (r14)
PushArgsWithArgV(ExtendedAssembler * assembler,Register jsfunc,Register actualNumArgs,Register argV,Label * pushCallThis)823 void OptimizedCall::PushArgsWithArgV(ExtendedAssembler *assembler, Register jsfunc,
824                                      Register actualNumArgs, Register argV, Label *pushCallThis)
825 {
826     Register expectedNumArgs(r14); // output
827     Register tmp(rax);
828     Label align16Bytes;
829     Label copyArguments;
830     // get expected num Args
831     __ Movq(Operand(jsfunc, JSFunctionBase::METHOD_OFFSET), tmp);
832     __ Movq(Operand(tmp, Method::CALL_FIELD_OFFSET), tmp);
833     __ Shr(MethodLiteral::NumArgsBits::START_BIT, tmp);
834     __ Andl(((1LU <<  MethodLiteral::NumArgsBits::SIZE) - 1), tmp);
835 
836     __ Mov(tmp, expectedNumArgs);
837     __ Testb(1, expectedNumArgs);
838     __ Jne(&align16Bytes);
839     __ PushAlignBytes();
840 
841     __ Bind(&align16Bytes);
842     {
843         __ Cmpq(actualNumArgs, expectedNumArgs);
844         __ Jbe(&copyArguments);
845         __ Subq(actualNumArgs, tmp);
846         PushUndefinedWithArgc(assembler, tmp);
847     }
848     __ Bind(&copyArguments);
849     {
850         __ Cmpq(actualNumArgs, expectedNumArgs);
851         __ Movq(actualNumArgs, tmp);     // rax -> actualNumArgsReg
852         __ CMovbe(expectedNumArgs, tmp);
853         __ Cmpq(0, tmp);
854         __ Je(pushCallThis);
855         CopyArgumentWithArgV(assembler, tmp, argV);
856     }
857 }
858 
PopJSFunctionArgs(ExtendedAssembler * assembler,Register expectedNumArgs)859 void OptimizedCall::PopJSFunctionArgs(ExtendedAssembler *assembler, Register expectedNumArgs)
860 {
861     Label align16Bytes;
862     __ Testb(1, expectedNumArgs);
863     __ Jne(&align16Bytes);
864     __ Addq(FRAME_SLOT_SIZE, rsp);
865     __ Bind(&align16Bytes);
866     __ Leaq(Operand(expectedNumArgs, Scale::Times8, 0), expectedNumArgs);
867     __ Addq(expectedNumArgs, rsp);
868     __ Addq(FRAME_SLOT_SIZE, rsp); // 8: skip expectedNumArgs
869 }
870 
PushJSFunctionEntryFrame(ExtendedAssembler * assembler,Register prevFp)871 void OptimizedCall::PushJSFunctionEntryFrame(ExtendedAssembler *assembler, Register prevFp)
872 {
873     __ PushCppCalleeSaveRegisters();
874     __ Pushq(rdi);
875 
876     // construct optimized entry frame
877     __ Pushq(rbp);
878     __ Pushq(static_cast<int64_t>(FrameType::OPTIMIZED_ENTRY_FRAME));
879     __ Pushq(prevFp);
880     // 2: skip prevFp and frameType
881     __ Leaq(Operand(rsp, 2 * FRAME_SLOT_SIZE), rbp);
882 }
883 
PopJSFunctionEntryFrame(ExtendedAssembler * assembler,Register glue)884 void OptimizedCall::PopJSFunctionEntryFrame(ExtendedAssembler *assembler, Register glue)
885 {
886     Register prevFp(rsi);
887     __ Popq(prevFp);
888     __ Addq(FRAME_SLOT_SIZE, rsp); // 8: frame type
889     __ Popq(rbp);
890     __ Popq(glue); // caller restore
891     __ PopCppCalleeSaveRegisters(); // callee restore
892     __ Movq(prevFp, Operand(glue, JSThread::GlueData::GetLeaveFrameOffset(false)));
893 }
894 
895 // * uint64_t PushOptimizedUnfoldArgVFrame(uintptr_t glue, uint32_t argc, JSTaggedType calltarget,
896 //                                         JSTaggedType new, JSTaggedType this, JSTaggedType, argV[])
897 // * cc calling convention call js function()
898 // * arguments:
899 //              %rdi - glue
900 //              %rsi - argc
901 //              %rdx - call-target
902 //              %rcx - new-target
903 //              %r8  - this
904 //              %r9  - argv
905 //
906 // * OptimizedUnfoldArgVFrame layout description as the following:
907 //      sp ----> |--------------------------| ---------------
908 //               |       returnAddr         |               ^
909 //  currentFp--> |--------------------------|               |
910 //               |       prevFp             |               |
911 //               |--------------------------|   OptimizedUnfoldArgVFrame
912 //               |       frameType          |               |
913 //               |--------------------------|               |
914 //               |       currentFp          |               v
915 //               +--------------------------+ ---------------
916 
PushOptimizedUnfoldArgVFrame(ExtendedAssembler * assembler,Register callSiteSp)917 void OptimizedCall::PushOptimizedUnfoldArgVFrame(ExtendedAssembler *assembler, Register callSiteSp)
918 {
919     __ Pushq(rbp);
920     // construct frame
921     __ Pushq(static_cast<int64_t>(FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME));
922     __ Pushq(callSiteSp);
923     // 2: skip callSiteSp and frameType
924     __ Leaq(Operand(rsp, 2 * FRAME_SLOT_SIZE), rbp);
925 }
926 
PopOptimizedUnfoldArgVFrame(ExtendedAssembler * assembler)927 void OptimizedCall::PopOptimizedUnfoldArgVFrame(ExtendedAssembler *assembler)
928 {
929     Register sp(rsp);
930     // 16 : 16 means pop call site sp and type
931     __ Addq(Immediate(2 * FRAME_SLOT_SIZE), sp);
932     __ Popq(rbp);
933 }
934 
935 // * uint64_t JSCallWithArgV(uintptr_t glue, uint32_t argc, JSTaggedType calltarget,
936 //                          JSTaggedType new, JSTaggedType this, argV)
937 // * cc calling convention call js function()
938 // * arguments:
939 //              %rdi - glue
940 //              %rsi - argc
941 //              %rdx - call-target
942 //              %rcx - new-target
943 //              %r8  - this
944 //              %r9  - argv
945 //
946 // * OptimizedJSFunctionFrame layout description as the following:
947 //               +--------------------------+
948 //               |       argn               |
949 //               |--------------------------|
950 //               |       argn - 1           |
951 //               |--------------------------|
952 //               |       .....              |
953 //               |--------------------------|
954 //               |       arg2               |
955 //               |--------------------------|
956 //               |       arg1               |
957 //      sp ----> |--------------------------| ---------------
958 //               |       returnAddr         |               ^
959 //               |--------------------------|               |
960 //               |       callsiteFp         |               |
961 //               |--------------------------|   OptimizedJSFunctionFrame
962 //               |       frameType          |               |
963 //               |--------------------------|               |
964 //               |       call-target        |               v
965 //               +--------------------------+ ---------------
966 
GenJSCallWithArgV(ExtendedAssembler * assembler,bool needAddExpectedArgs)967 void OptimizedCall::GenJSCallWithArgV(ExtendedAssembler *assembler, bool needAddExpectedArgs)
968 {
969     Register sp(rsp);
970     Register glue(rdi);
971     Register actualNumArgs(rsi);
972     Register jsfunc(rdx);
973     Register newTarget(rcx);
974     Register thisObj(r8);
975     Register argV(r9);
976     Register callsiteSp = __ AvailableRegister2();
977     Label align16Bytes;
978     Label pushCallThis;
979 
980     __ Movq(sp, callsiteSp);
981     __ Addq(Immediate(FRAME_SLOT_SIZE), callsiteSp);   // 8 : 8 means skip pc to get last callsitesp
982     PushOptimizedUnfoldArgVFrame(assembler, callsiteSp);
983     __ Testb(1, actualNumArgs);
984     __ Je(&align16Bytes);
985     __ PushAlignBytes();
986     __ Bind(&align16Bytes);
987     __ Cmp(Immediate(0), actualNumArgs);
988     __ Jz(&pushCallThis);
989     __ Mov(actualNumArgs, rax);
990     CopyArgumentWithArgV(assembler, rax, argV);
991     __ Bind(&pushCallThis);
992     PushMandatoryJSArgs(assembler, jsfunc, thisObj, newTarget);
993     __ Addq(Immediate(NUM_MANDATORY_JSFUNC_ARGS), actualNumArgs);
994     __ Pushq(actualNumArgs);
995     __ Movq(glue, rax);
996     if (needAddExpectedArgs) {
997         __ CallAssemblerStub(RTSTUB_ID(OptimizedCallAndPushUndefined), false);
998     } else {
999         __ CallAssemblerStub(RTSTUB_ID(CallOptimized), false);
1000     }
1001 
1002     __ Mov(Operand(sp, 0), actualNumArgs);
1003     PopJSFunctionArgs(assembler, actualNumArgs);
1004     PopOptimizedUnfoldArgVFrame(assembler);
1005     __ Ret();
1006 }
1007 
1008 // * uint64_t JSCallWithArgVAndPushUndefined(uintptr_t glue, uint32_t argc, JSTaggedType calltarget,
1009 //                          JSTaggedType new, JSTaggedType this, argV)
1010 // * cc calling convention call js function()
1011 // * arguments:
1012 //              %rdi - glue
1013 //              %rsi - argc
1014 //              %rdx - call-target
1015 //              %rcx - new-target
1016 //              %r8  - this
1017 //              %r9  - argv
JSCallWithArgVAndPushUndefined(ExtendedAssembler * assembler)1018 void OptimizedCall::JSCallWithArgVAndPushUndefined(ExtendedAssembler *assembler)
1019 {
1020     __ BindAssemblerStub(RTSTUB_ID(JSCallWithArgVAndPushUndefined));
1021     GenJSCallWithArgV(assembler, true);
1022 }
1023 
JSCallWithArgV(ExtendedAssembler * assembler)1024 void OptimizedCall::JSCallWithArgV(ExtendedAssembler *assembler)
1025 {
1026     __ BindAssemblerStub(RTSTUB_ID(JSCallWithArgV));
1027     GenJSCallWithArgV(assembler, false);
1028 }
1029 
CallOptimized(ExtendedAssembler * assembler)1030 void OptimizedCall::CallOptimized(ExtendedAssembler *assembler)
1031 {
1032     __ BindAssemblerStub(RTSTUB_ID(CallOptimized));
1033     Register jsFuncReg = rdi;
1034     Register method = r9;
1035     Register codeAddrReg = rsi;
1036     __ Movq(Operand(rsp, DOUBLE_SLOT_SIZE), jsFuncReg); // sp + 16 get jsFunc
1037     __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method
1038     __ Mov(Operand(method, Method::CODE_ENTRY_OFFSET), codeAddrReg);
1039     __ Jmp(codeAddrReg);
1040 }
1041 
1042 // Input: %rdi - glue
1043 //        %rsi - context
DeoptEnterAsmInterp(ExtendedAssembler * assembler)1044 void OptimizedCall::DeoptEnterAsmInterp(ExtendedAssembler *assembler)
1045 {
1046     // rdi
1047     Register glueRegister = __ GlueRegister();
1048     Register context = rsi;
1049     // rax, rdx, rcx, r8, r9, r10, r11 is free
1050     Register tempRegister = rax;
1051     Register opRegister = r10;
1052     Register outputCount = rdx;
1053     Register frameStateBase = rcx;
1054     Register depth = r11;
1055     Label loopBegin;
1056     Label stackOverflow;
1057     Label pushArgv;
1058     __ Movq(Operand(context, AsmStackContext::GetInlineDepthOffset(false)), depth);
1059     __ Leaq(Operand(context, AsmStackContext::GetSize(false)), context);
1060     __ Movq(Immediate(0), r12);
1061     __ Bind(&loopBegin);
1062     __ Movq(Operand(context, 0), outputCount);
1063     __ Leaq(Operand(context, FRAME_SLOT_SIZE), frameStateBase);
1064     __ Cmpq(0, r12);
1065     __ Je(&pushArgv);
1066     __ Movq(rsp, r8);
1067     __ Addq(AsmInterpretedFrame::GetSize(false), r8);
1068     __ Leaq(Operand(frameStateBase, AsmInterpretedFrame::GetBaseOffset(false)), r10);
1069     __ Movq(r8, Operand(r10, InterpretedFrameBase::GetPrevOffset(false)));
1070     __ Testq(15, rsp);  // 15: low 4 bits must be 0b0000
1071     __ Jnz(&pushArgv);
1072     __ PushAlignBytes();
1073     __ Bind(&pushArgv);
1074     // update fp
1075     __ Movq(rsp, Operand(frameStateBase, AsmInterpretedFrame::GetFpOffset(false)));
1076     PushArgsWithArgvAndCheckStack(assembler, glueRegister, outputCount,
1077         frameStateBase, tempRegister, opRegister, &stackOverflow);
1078     __ Leaq(Operand(context, outputCount, Scale::Times8, FRAME_SLOT_SIZE), context);
1079     __ Addq(1, r12);
1080     __ Cmpq(r12, depth);
1081     __ Jae(&loopBegin);
1082     Register callTargetRegister = r8;
1083     Register methodRegister = r9;
1084     {
1085         // r13, rbp, r12, rbx,      r14,     rsi,  rdi
1086         // glue sp   pc  constpool  profile  acc   hotness
1087         __ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetFunctionOffset(false)), callTargetRegister);
1088         __ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetPcOffset(false)), r12);
1089         __ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetAccOffset(false)), rsi);
1090         __ Movq(Operand(callTargetRegister, JSFunctionBase::METHOD_OFFSET), methodRegister);
1091 
1092         __ Leaq(Operand(rsp, AsmInterpretedFrame::GetSize(false)), opRegister);
1093         AsmInterpreterCall::DispatchCall(assembler, r12, opRegister, methodRegister, rsi);
1094     }
1095 
1096     __ Bind(&stackOverflow);
1097     {
1098         [[maybe_unused]] TempRegisterScope scope(assembler);
1099         Register temp = __ TempRegister();
1100         AsmInterpreterCall::ThrowStackOverflowExceptionAndReturn(assembler,
1101             glueRegister, rsp, temp);
1102     }
1103 }
1104 
1105 // Input: %rdi - glue
DeoptHandlerAsm(ExtendedAssembler * assembler)1106 void OptimizedCall::DeoptHandlerAsm(ExtendedAssembler *assembler)
1107 {
1108     __ BindAssemblerStub(RTSTUB_ID(DeoptHandlerAsm));
1109 
1110     Register glueReg = rdi;
1111     __ Pushq(rbp);
1112     __ Pushq(static_cast<int32_t>(FrameType::ASM_BRIDGE_FRAME));
1113     __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
1114     __ Push(glueReg);
1115     __ PushCppCalleeSaveRegisters();
1116 
1117     __ Movq(rdi, rax); // glue
1118     Register deoptType = rsi;
1119     Register depth = rdx;
1120     __ Subq(FRAME_SLOT_SIZE, rsp);
1121     __ Pushq(depth);
1122     __ Pushq(deoptType);  // argv[0]
1123     __ Pushq(2);  // 2: argc
1124     __ Pushq(kungfu::RuntimeStubCSigns::ID_DeoptHandler);
1125     __ CallAssemblerStub(RTSTUB_ID(CallRuntime), false);
1126 
1127     __ Addq(5 * FRAME_SLOT_SIZE, rsp); // 5: skip runtimeId argc deoptType depth align
1128 
1129     Register context = rsi;
1130     __ Movq(rax, context);
1131 
1132     Label target;
1133     __ PopCppCalleeSaveRegisters();
1134     __ Pop(glueReg);
1135 
1136     Label stackOverflow;
1137     __ Cmpq(JSTaggedValue::VALUE_EXCEPTION, rax);
1138     __ Je(&stackOverflow);
1139 
1140     __ Movq(Operand(context, AsmStackContext::GetCallerFpOffset(false)), rbp);
1141     __ Movq(Operand(context, AsmStackContext::GetCallFrameTopOffset(false)), rsp);
1142     __ Subq(FRAME_SLOT_SIZE, rsp); // skip lr
1143 
1144     PushAsmInterpBridgeFrame(assembler);
1145     __ Callq(&target);
1146     PopAsmInterpBridgeFrame(assembler);
1147     __ Ret();
1148     __ Bind(&target);
1149     DeoptEnterAsmInterp(assembler);
1150     __ Int3();
1151 
1152     __ Bind(&stackOverflow);
1153     {
1154         __ Movq(rdi, rax);
1155         __ Pushq(0); // argc
1156         __ Pushq(kungfu::RuntimeStubCSigns::ID_ThrowStackOverflowException);
1157         __ CallAssemblerStub(RTSTUB_ID(CallRuntime), false);
1158         __ Addq(FRAME_SLOT_SIZE * 3, rsp); // 3 : skip runtimeId argc & type
1159         __ Popq(rbp);
1160         __ Ret();
1161     }
1162 }
1163 #undef __
1164 }  // namespace panda::ecmascript::x64