1/*
2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/compiler/trampoline/x64/common_call.h"
17
18#include "ecmascript/compiler/assembler/assembler.h"
19#include "ecmascript/compiler/rt_call_signature.h"
20#include "ecmascript/compiler/argument_accessor.h"
21#include "ecmascript/deoptimizer/deoptimizer.h"
22#include "ecmascript/ecma_runtime_call_info.h"
23#include "ecmascript/frames.h"
24#include "ecmascript/js_function.h"
25#include "ecmascript/js_thread.h"
26#include "ecmascript/message_string.h"
27#include "ecmascript/method.h"
28#include "ecmascript/runtime_call_id.h"
29
30namespace panda::ecmascript::x64 {
31#define __ assembler->
32
33// * uint64_t OptimizedFastCallEntry(uintptr_t glue, uint32_t actualNumArgs, const JSTaggedType argV[],
34//                                   uintptr_t prevFp)
35// * Arguments:
36//        %rdi - glue
37//        %rsi - actualNumArgs
38//        %rdx - argV
39//        %rcx - prevFp
40
41void OptimizedFastCall::OptimizedFastCallEntry(ExtendedAssembler *assembler)
42{
43    __ BindAssemblerStub(RTSTUB_ID(OptimizedFastCallEntry));
44    Register glueReg = rdi;
45    Register argv = rdx;
46    Register prevFpReg = rcx;
47
48    OptimizedCall::PushJSFunctionEntryFrame(assembler, prevFpReg);
49    __ Movq(argv, r8);
50    __ Movq(rsi, rcx);
51    __ Movq(Operand(r8, 0), rsi); // func
52    __ Movq(Operand(r8, FRAME_SLOT_SIZE), rdx); // thisobj
53    __ Addq(DOUBLE_SLOT_SIZE, r8);
54    __ CallAssemblerStub(RTSTUB_ID(JSFastCallWithArgV), false);
55
56    __ Popq(prevFpReg);
57    __ Addq(FRAME_SLOT_SIZE, rsp); // 8: frame type
58    __ Popq(rbp);
59    __ Popq(glueReg); // caller restore
60    __ PopCppCalleeSaveRegisters(); // callee restore
61    __ Movq(prevFpReg, Operand(glueReg, JSThread::GlueData::GetLeaveFrameOffset(false)));
62    __ Ret();
63}
64
65
66// * uint64_t OptimizedFastCallAndPushArgv(uintptr_t glue, uint32_t expectedNumArgs, uint32_t actualNumArgs,
67//                                   uintptr_t codeAddr, uintptr_t argv)
68// * Arguments wil CC calling convention:
69//         %rdi - glue
70//         %rsi - actualNumArgs
71//         %rdx - actualArgv
72//         %rcx - func
73//         %r8  - new target
74//         %r9  - this
75// * The OptimizedJSFunctionArgsConfig Frame's structure is illustrated as the following:
76//          +--------------------------+
77//          |         arg[N-1]         |
78//          +--------------------------+
79//          |         . . . .          |
80//          +--------------------------+
81//          |         arg[0]           |
82//          +--------------------------+
83//          |         argC             |
84//  sp ---> +--------------------------+ -----------------
85//          |                          |                 ^
86//          |        prevFP            |                 |
87//          |--------------------------|    OptimizedJSFunctionArgsConfigFrame
88//          |       frameType          |                 |
89//          |                          |                 V
90//          +--------------------------+ -----------------
91void OptimizedFastCall::OptimizedFastCallAndPushArgv(ExtendedAssembler *assembler)
92{
93    __ BindAssemblerStub(RTSTUB_ID(OptimizedFastCallAndPushArgv));
94    Register actualNumArgsReg = rsi;
95    Register jsFuncReg = rcx;
96    Register thisObj = r9;
97    Label lCopyExtraAument1;
98    Label lCopyExtraUndefineToSp;
99    Label lCopyLoop1;
100    Label lCopyLoop2;
101    Label pushUndefined;
102    Label call;
103    Label arg4;
104    Label argc;
105    Label checkExpectedArgs;
106    JsFunctionArgsConfigFrameScope scope(assembler); // push frametype and callee save
107    __ Movq(actualNumArgsReg, r13);
108    actualNumArgsReg = r13;
109    __ Movq(rcx, rsi); // func move to argc
110    jsFuncReg = rsi;
111    __ Movq(thisObj, rdx); // this move to argv
112    Register method = r14;
113    Register methodCallField = rbx;
114    Register codeAddrReg = rax;
115    Register argvReg = r12;
116    __ Leaq(Operand(rsp, 8 * FRAME_SLOT_SIZE), argvReg); // 8: skip 8 frames to get argv
117    __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method
118    __ Mov(Operand(jsFuncReg, JSFunctionBase::CODE_ENTRY_OFFSET), codeAddrReg); // get codeAddress
119    __ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField); // get call field
120    __ Shr(MethodLiteral::NumArgsBits::START_BIT, methodCallField);
121    __ Andl(((1LU <<  MethodLiteral::NumArgsBits::SIZE) - 1), methodCallField);
122    __ Addl(NUM_MANDATORY_JSFUNC_ARGS, methodCallField); // add mandatory argumentr
123    Register expectedNumArgsReg = rbx;
124
125    Label arg5;
126    Label arg6;
127    __ Cmp(Immediate(3), actualNumArgsReg); // 3: func new this
128    __ Jne(&arg4);
129    __ Movq(JSTaggedValue::VALUE_UNDEFINED, rcx);
130    __ Movq(JSTaggedValue::VALUE_UNDEFINED, r8);
131    __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9);
132    __ Subq(3, expectedNumArgsReg); // 3: skip 3 register
133    __ Jmp(&checkExpectedArgs);
134
135    __ Bind(&arg4);
136    {
137        __ Movq(Operand(argvReg, 0), rcx);
138        __ Addq(FRAME_SLOT_SIZE, argvReg);
139        __ Cmp(Immediate(4), actualNumArgsReg); // 4: func new this arg0
140        __ Jne(&arg5);
141        __ Movq(JSTaggedValue::VALUE_UNDEFINED, r8);
142        __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9);
143        __ Subq(3, expectedNumArgsReg); // 3: skip 3 register
144        __ Jmp(&checkExpectedArgs);
145    }
146
147    __ Bind(&arg5);
148    {
149        __ Movq(Operand(argvReg, 0), r8);
150        __ Addq(FRAME_SLOT_SIZE, argvReg);
151        __ Cmp(Immediate(5), actualNumArgsReg); // 5: 5 args
152        __ Jne(&arg6);
153        __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9);
154        __ Subq(3, expectedNumArgsReg); // 3: skip 3 register
155        __ Jmp(&checkExpectedArgs);
156    }
157
158    __ Bind(&arg6);
159    {
160        __ Movq(Operand(argvReg, 0), r9);
161        __ Addq(FRAME_SLOT_SIZE, argvReg);
162        __ Cmp(Immediate(6), actualNumArgsReg); // 6: 6 args
163        __ Jne(&argc);
164        __ Subq(3, expectedNumArgsReg); // 3: skip above 3 args
165        __ Jmp(&checkExpectedArgs);
166    }
167
168    __ Bind(&argc); // actualNumArgsReg >=7
169    {
170        __ Cmpq(expectedNumArgsReg, actualNumArgsReg);
171        __ Jb(&pushUndefined);
172        // 16 bytes align check
173        __ Subq(6, actualNumArgsReg); // 6: skip above 6 args
174        __ Subq(6, expectedNumArgsReg); // 6: skip above 6 args
175        __ Testb(1, actualNumArgsReg);
176        __ Je(&lCopyLoop2);
177        __ Pushq(0);
178        __ Bind(&lCopyLoop2);
179        __ Movq(Operand(argvReg, actualNumArgsReg, Scale::Times8, -FRAME_SLOT_SIZE), r14); // -8: stack index
180        __ Pushq(r14);
181        __ Subq(1, actualNumArgsReg);
182        __ Jne(&lCopyLoop2);
183        __ Jmp(&call);
184
185        __ Bind(&pushUndefined);
186        // 16 bytes align check
187        __ Subq(6, actualNumArgsReg); // 6: skip above 6 args
188        __ Subq(6, expectedNumArgsReg); // 6: skip above 6 args
189        __ Testb(1, expectedNumArgsReg);
190        __ Je(&lCopyExtraAument1);
191        __ Pushq(0);
192        __ Bind(&lCopyExtraAument1); // copy undefined value to stack
193        __ Pushq(JSTaggedValue::VALUE_UNDEFINED);
194        __ Subq(1, expectedNumArgsReg);
195        __ Cmpq(actualNumArgsReg, expectedNumArgsReg);
196        __ Ja(&lCopyExtraAument1);
197        __ Bind(&lCopyLoop1);
198        __ Movq(Operand(argvReg, expectedNumArgsReg, Scale::Times8, -FRAME_SLOT_SIZE), r14); // -8: stack index
199        __ Pushq(r14);
200        __ Subq(1, expectedNumArgsReg);
201        __ Jne(&lCopyLoop1);
202        __ Jmp(&call);
203    }
204
205    __ Bind(&checkExpectedArgs); // actualNumArgsReg < 7
206    {
207        __ Cmp(Immediate(3), expectedNumArgsReg); // 3: expectedNumArgsReg <= 3 jump
208        __ Jbe(&call);
209        // expectedNumArgsReg > 6, expectedNumArgsReg > actual;NumArgsReg
210        __ Subq(3, expectedNumArgsReg); // 3 : skpi func new this
211        __ Testb(1, expectedNumArgsReg);
212        __ Je(&lCopyExtraUndefineToSp);
213        __ Pushq(0); // expectedNumArgsReg is odd need align
214        __ Bind(&lCopyExtraUndefineToSp); // copy undefined value to stack
215        __ Pushq(JSTaggedValue::VALUE_UNDEFINED);
216        __ Subq(1, expectedNumArgsReg);
217        __ Cmp(0, expectedNumArgsReg);
218        __ Ja(&lCopyExtraUndefineToSp);
219        __ Jmp(&call);
220    }
221    __ Bind(&call);
222    __ Callq(codeAddrReg); // then call jsFunction
223}
224
225// * uint64_t JSFastCallWithArgV(uintptr_t glue, uint32_t actualNumArgs, const JSTaggedType argV[], uintptr_t prevFp,
226//                            size_t callType)
227// cc callconv
228// * Arguments:
229//        %rdi - glue
230//        %rsi - func
231//        %rdx - this
232//        %rcx - actualNumArgs
233//        %r8 -  argv
234
235void OptimizedFastCall::JSFastCallWithArgV(ExtendedAssembler *assembler)
236{
237    __ BindAssemblerStub(RTSTUB_ID(JSFastCallWithArgV));
238    Register sp(rsp);
239    Register callsiteSp = __ AvailableRegister2();
240    Label align16Bytes;
241    Label call;
242
243    __ Movq(sp, callsiteSp);
244    __ Addq(Immediate(FRAME_SLOT_SIZE), callsiteSp);   // 8 : 8 means skip pc to get last callsitesp
245    OptimizedUnfoldArgVFrameFrameScope scope(assembler); // push frametype and callee save
246    __ Movq(rcx, r12);
247    __ Movq(r8, rbx);
248    Register actualNumArgs(r12);
249    Register argV(rbx);
250
251    __ Cmp(0, actualNumArgs);
252    __ Jz(&call);
253    __ Movq(Operand(argV, 0), rcx); // first arg
254    __ Addq(FRAME_SLOT_SIZE, argV);
255    __ Addq(-1, actualNumArgs);
256
257    __ Cmp(0, actualNumArgs);
258    __ Jz(&call);
259    __ Movq(Operand(argV, 0), r8); // second arg
260    __ Addq(FRAME_SLOT_SIZE, argV);
261    __ Addq(-1, actualNumArgs);
262
263    __ Cmp(0, actualNumArgs);
264    __ Jz(&call);
265    __ Movq(Operand(argV, 0), r9); // third arg
266    __ Addq(FRAME_SLOT_SIZE, argV);
267    __ Addq(-1, actualNumArgs);
268
269    __ Cmp(0, actualNumArgs);
270    __ Jz(&call);
271
272    __ Testb(1, actualNumArgs);
273    __ Je(&align16Bytes);
274    __ PushAlignBytes();
275    __ Bind(&align16Bytes);
276    __ Mov(actualNumArgs, rax);
277    CopyArgumentWithArgV(assembler, rax, argV);
278
279    __ Bind(&call);
280    Register method = r12;
281    Register jsFuncReg = rsi;
282    __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method
283    __ Mov(Operand(jsFuncReg, JSFunctionBase::CODE_ENTRY_OFFSET), rbx); // get codeAddress
284    __ Callq(rbx);
285}
286
287// cc callconv
288// * Arguments:
289//        %rdi - glue
290//        %rsi - func
291//        %rdx - this
292//        %rcx - actualNumArgs
293//        %r8 -  argv
294//        %r9 -  expectedNumArgs
295
296void OptimizedFastCall::JSFastCallWithArgVAndPushArgv(ExtendedAssembler *assembler)
297{
298    __ BindAssemblerStub(RTSTUB_ID(JSFastCallWithArgVAndPushArgv));
299    Register sp(rsp);
300    Register callsiteSp = __ AvailableRegister2();
301    Label call;
302    Label lCopyExtraAument1;
303    Label lCopyExtraUndefineToSp;
304    Label lCopyLoop1;
305    Label lCopyLoop2;
306    Label pushUndefined;
307    Label arg1;
308    Label arg2;
309    Label arg3;
310    Label argc;
311    Label checkExpectedArgs;
312
313    __ Movq(sp, callsiteSp);
314    __ Addq(Immediate(FRAME_SLOT_SIZE), callsiteSp);   // 8 : 8 means skip pc to get last callsitesp
315    OptimizedUnfoldArgVFrameFrame1Scope scope(assembler);
316
317    __ Movq(rcx, r12);
318    __ Movq(r8, rbx);
319    __ Movq(r9, r14);
320    Register actualNumArgsReg(r12);
321    Register expectedNumArgsReg(r14);
322    Register argV(rbx);
323
324    __ Cmp(0, actualNumArgsReg);
325    __ Jne(&arg1);
326    __ Movq(JSTaggedValue::VALUE_UNDEFINED, rcx);
327    __ Movq(JSTaggedValue::VALUE_UNDEFINED, r8);
328    __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9);
329    __ Jmp(&checkExpectedArgs);
330
331    __ Bind(&arg1);
332    {
333        __ Movq(Operand(argV, 0), rcx); // first arg
334        __ Addq(FRAME_SLOT_SIZE, argV);
335        __ Cmp(1, actualNumArgsReg);
336        __ Jne(&arg2);
337        __ Movq(JSTaggedValue::VALUE_UNDEFINED, r8);
338        __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9);
339        __ Jmp(&checkExpectedArgs);
340    }
341
342    __ Bind(&arg2);
343    {
344        __ Movq(Operand(argV, 0), r8); // second arg
345        __ Addq(FRAME_SLOT_SIZE, argV);
346        __ Cmp(2, actualNumArgsReg); // 2: 2 args
347        __ Jne(&arg3);
348        __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9);
349        __ Jmp(&checkExpectedArgs);
350    }
351
352    __ Bind(&arg3);
353    {
354        __ Movq(Operand(argV, 0), r9); // third arg
355        __ Addq(FRAME_SLOT_SIZE, argV);
356        __ Cmp(3, actualNumArgsReg); // 3: 3 args
357        __ Jne(&argc);
358        __ Jmp(&checkExpectedArgs);
359    }
360
361    __ Bind(&argc); // actualNumArgsReg >=4
362    {
363        __ Cmpq(expectedNumArgsReg, actualNumArgsReg);
364        __ Jb(&pushUndefined);
365        __ Subq(3, actualNumArgsReg); // 3: skip above 3 args
366        __ Subq(3, expectedNumArgsReg); // 3: skip above 3 args
367        __ Testb(1, actualNumArgsReg);
368        __ Je(&lCopyLoop2);
369        __ Pushq(0);
370        __ Bind(&lCopyLoop2);
371        __ Movq(Operand(argV, actualNumArgsReg, Scale::Times8, -FRAME_SLOT_SIZE), r13); // -8: stack index
372        __ Pushq(r13);
373        __ Subq(1, actualNumArgsReg);
374        __ Jne(&lCopyLoop2);
375        __ Jmp(&call);
376
377        __ Bind(&pushUndefined);
378        __ Subq(3, actualNumArgsReg); // 3: skip above 3 args
379        __ Subq(3, expectedNumArgsReg); // 3: skip above 3 args
380        __ Testb(1, expectedNumArgsReg);
381        __ Je(&lCopyExtraAument1);
382        __ Pushq(0);
383        __ Bind(&lCopyExtraAument1); // copy undefined value to stack
384        __ Pushq(JSTaggedValue::VALUE_UNDEFINED);
385        __ Subq(1, expectedNumArgsReg);
386        __ Cmpq(actualNumArgsReg, expectedNumArgsReg);
387        __ Ja(&lCopyExtraAument1);
388        __ Bind(&lCopyLoop1);
389        __ Movq(Operand(argV, expectedNumArgsReg, Scale::Times8, -FRAME_SLOT_SIZE), r13); // -8: stack index
390        __ Pushq(r13);
391        __ Subq(1, expectedNumArgsReg);
392        __ Jne(&lCopyLoop1);
393        __ Jmp(&call);
394    }
395
396    __ Bind(&checkExpectedArgs);
397    {
398        __ Cmp(Immediate(3), expectedNumArgsReg); // 3:expectedNumArgsReg <= 3 jump
399        __ Jbe(&call);
400        __ Subq(3, expectedNumArgsReg); // 3 : skpi func new this
401        __ Testb(1, expectedNumArgsReg);
402        __ Je(&lCopyExtraUndefineToSp);
403        __ Pushq(0); // expectedNumArgsReg is odd need align
404        __ Bind(&lCopyExtraUndefineToSp); // copy undefined value to stack
405        __ Pushq(JSTaggedValue::VALUE_UNDEFINED);
406        __ Subq(1, expectedNumArgsReg);
407        __ Cmp(0, expectedNumArgsReg);
408        __ Ja(&lCopyExtraUndefineToSp);
409        __ Jmp(&call);
410    }
411    __ Bind(&call);
412    Register method = r12;
413    Register jsFuncReg = rsi;
414    __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method
415    __ Mov(Operand(jsFuncReg, JSFunctionBase::CODE_ENTRY_OFFSET), rbx); // get codeAddress
416    __ Callq(rbx);
417}
418#undef __
419}  // namespace panda::ecmascript::x64