• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
30 namespace 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 
OptimizedFastCallEntry(ExtendedAssembler * assembler)41 void 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 //          +--------------------------+ -----------------
OptimizedFastCallAndPushArgv(ExtendedAssembler * assembler)91 void 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 
JSFastCallWithArgV(ExtendedAssembler * assembler)235 void 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 
JSFastCallWithArgVAndPushArgv(ExtendedAssembler * assembler)296 void 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