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/aarch64/common_call.h"
17 
18 #include "ecmascript/compiler/assembler/assembler.h"
19 #include "ecmascript/compiler/argument_accessor.h"
20 #include "ecmascript/compiler/rt_call_signature.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/method.h"
26 #include "ecmascript/js_thread.h"
27 #include "ecmascript/message_string.h"
28 #include "ecmascript/runtime_call_id.h"
29 
30 namespace panda::ecmascript::aarch64 {
31 using Label = panda::ecmascript::Label;
32 #define __ assembler->
33 
34 // * uint64_t OptimizedFastCallEntry(uintptr_t glue, uint32_t actualNumArgs, const JSTaggedType argV[],
35 //                                   uintptr_t prevFp)
36 // * Arguments:
37 //        %x0 - glue
38 //        %x1 - actualNumArgs
39 //        %x2 - argV
40 //        %x3 - prevFp
OptimizedFastCallEntry(ExtendedAssembler *assembler)41 void OptimizedFastCall::OptimizedFastCallEntry(ExtendedAssembler *assembler)
42 {
43     __ BindAssemblerStub(RTSTUB_ID(OptimizedFastCallEntry));
44     Register glueReg(X0);
45     Register argc(X1);
46     Register argV(X2);
47     Register prevFpReg(X3);
48     Register sp(SP);
49 
50     OptimizedCall::PushJSFunctionEntryFrame (assembler, prevFpReg);
51     __ Mov(Register(X3), argc);
52     __ Mov(Register(X4), argV);
53     Register tmpArgc(X3);
54     Register tmpArgV(X4);
55 
56     __ Mov(Register(X20), glueReg);
57     __ Ldr(Register(X1), MemoryOperand(tmpArgV, 0));
58     __ Ldr(Register(X2), MemoryOperand(tmpArgV, FRAME_SLOT_SIZE));
59     __ Add(tmpArgV, tmpArgV, Immediate(DOUBLE_SLOT_SIZE));
60 
61     __ CallAssemblerStub(RTSTUB_ID(JSFastCallWithArgV), false);
62     __ Mov(Register(X2), Register(X20));
63     OptimizedCall::PopJSFunctionEntryFrame(assembler, Register(X2));
64     __ Ret();
65 }
66 
67 // * uint64_t OptimizedFastCallAndPushArgv(uintptr_t glue, uint32_t expectedNumArgs, uint32_t actualNumArgs,
68 //                                   uintptr_t codeAddr, uintptr_t argv)
69 // * Arguments wil CC calling convention:
70 //         %x0 - glue
71 //         %x1 - actualNumArgs
72 //         %x2 - actualArgv
73 //         %x3 - func
74 //         %x4  - new target
75 //         %x5  - this
76 //         %x6  - arg0
77 //         %x7  - arg1
78 //
79 // * The OptimizedJSFunctionArgsConfig Frame's structure is illustrated as the following:
80 //          +--------------------------+
81 //          |         arg[N-1]         |
82 //          +--------------------------+
83 //          |         . . . .          |
84 //          +--------------------------+
85 //          |         arg[0]           |
86 //          +--------------------------+
87 //          |         argC             |
88 //  sp ---> +--------------------------+ -----------------
89 //          |                          |                 ^
90 //          |        prevFP            |                 |
91 //          |--------------------------|    OptimizedJSFunctionArgsConfigFrame
92 //          |       frameType          |                 |
93 //          |                          |                 V
94 //          +--------------------------+ -----------------
OptimizedFastCallAndPushArgv(ExtendedAssembler *assembler)95 void OptimizedFastCall::OptimizedFastCallAndPushArgv(ExtendedAssembler *assembler)
96 {
97     __ BindAssemblerStub(RTSTUB_ID(OptimizedFastCallAndPushArgv));
98     Register glue(X0);
99     Register actualNumArgs(X1);
100     Register actualArgv(X2);
101     Register jsfunc(X3);
102     Register codeAddr(X4);
103     Register sp(SP);
104     Register currentSp = __ AvailableRegister1();
105     Register op = __ AvailableRegister1();
106     Label call;
107     Label arg4;
108     Label arg5;
109     Label arg6;
110     Label argc;
111     Label checkExpectedArgs;
112     Label pushUndefined;
113 
114     // construct frame
115     OptimizedCall::PushOptimizedArgsConfigFrame(assembler);
116 
117     __ Mov(__ AvailableRegister3(), Register(X1));
118     __ Add(__ AvailableRegister4(), sp, Immediate(4 * FRAME_SLOT_SIZE)); // 4 skip fp lr type x19
119     Register actualNumArgsReg = __ AvailableRegister3();
120     Register argV = __ AvailableRegister4();
121 
122     Register method = __ AvailableRegister1();
123     Register expectedNumArgs = __ AvailableRegister2();
124     __ Ldr(method, MemoryOperand(jsfunc, JSFunction::METHOD_OFFSET));
125     __ Ldr(expectedNumArgs, MemoryOperand(method, Method::CALL_FIELD_OFFSET));
126     __ Lsr(expectedNumArgs, expectedNumArgs, MethodLiteral::NumArgsBits::START_BIT);
127     __ And(expectedNumArgs, expectedNumArgs,
128         LogicalImmediate::Create(
129             MethodLiteral::NumArgsBits::Mask() >> MethodLiteral::NumArgsBits::START_BIT, RegXSize));
130     __ Add(expectedNumArgs, expectedNumArgs, Immediate(NUM_MANDATORY_JSFUNC_ARGS));
131 
132     Label arg7;
133     Label arg8;
134     __ Mov(Register(X1), Register(X3)); // func move to argc
135     __ Mov(Register(X2), Register(X5)); // this move to func
136     jsfunc = Register(X1);
137 
138     __ Cmp(actualNumArgsReg, Immediate(3)); // 3: 3 args
139     __ B(Condition::NE, &arg4);
140     __ Mov(Register(X3), Immediate(JSTaggedValue::VALUE_UNDEFINED));
141     __ Mov(Register(X4), Immediate(JSTaggedValue::VALUE_UNDEFINED));
142     __ Mov(Register(X5), Immediate(JSTaggedValue::VALUE_UNDEFINED));
143     __ Mov(Register(X6), Immediate(JSTaggedValue::VALUE_UNDEFINED));
144     __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED));
145     __ B(&checkExpectedArgs);
146 
147     __ Bind(&arg4);
148     {
149         __ Mov(Register(X3), Register(X6));
150         __ Cmp(actualNumArgsReg, Immediate(4)); // 4: 4 args
151         __ B(Condition::NE, &arg5);
152         __ Mov(Register(X4), Immediate(JSTaggedValue::VALUE_UNDEFINED));
153         __ Mov(Register(X5), Immediate(JSTaggedValue::VALUE_UNDEFINED));
154         __ Mov(Register(X6), Immediate(JSTaggedValue::VALUE_UNDEFINED));
155         __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED));
156         __ B(&checkExpectedArgs);
157     }
158 
159     __ Bind(&arg5);
160     {
161         __ Mov(Register(X4), Register(X7));
162         __ Cmp(actualNumArgsReg, Immediate(5)); // 5: 5 args
163         __ B(Condition::NE, &arg6);
164         __ Mov(Register(X5), Immediate(JSTaggedValue::VALUE_UNDEFINED));
165         __ Mov(Register(X6), Immediate(JSTaggedValue::VALUE_UNDEFINED));
166         __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED));
167         __ B(&checkExpectedArgs);
168     }
169 
170     __ Bind(&arg6);
171     {
172         __ Ldr(op, MemoryOperand(argV, 0));
173         __ Mov(Register(X5), op);
174         __ Add(argV, argV, Immediate(FRAME_SLOT_SIZE));
175         __ Cmp(actualNumArgsReg, Immediate(6)); // 6: 6 args
176         __ B(Condition::NE, &arg7);
177         __ Mov(Register(X6), Immediate(JSTaggedValue::VALUE_UNDEFINED));
178         __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED));
179         __ B(&checkExpectedArgs);
180     }
181 
182     __ Bind(&arg7);
183     {
184         __ Ldr(op, MemoryOperand(argV, 0));
185         __ Mov(Register(X6), op);
186         __ Add(argV, argV, Immediate(FRAME_SLOT_SIZE));
187         __ Cmp(actualNumArgsReg, Immediate(7)); // 7: 7 args
188         __ B(Condition::NE, &arg8);
189         __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED));
190         __ B(&checkExpectedArgs);
191     }
192 
193     __ Bind(&arg8);
194     {
195         __ Ldr(op, MemoryOperand(argV, 0));
196         __ Mov(Register(X7), op);
197         __ Add(argV, argV, Immediate(FRAME_SLOT_SIZE));
198         __ Cmp(actualNumArgsReg, Immediate(8)); // 8: 8 args
199         __ B(Condition::NE, &argc);
200         __ B(&checkExpectedArgs);
201     }
202 
203     __ Bind(&argc);
204     {
205         TempRegister1Scope scope1(assembler);
206         TempRegister2Scope scope2(assembler);
207         Register tmp = __ TempRegister1();
208         Register undefinedValue = __ TempRegister2();
209 
210         __ Cmp(expectedNumArgs, actualNumArgsReg);
211         __ B(Condition::GT, &pushUndefined);
212         __ Sub(expectedNumArgs, expectedNumArgs, Immediate(8)); // 8 : register save 8 arg
213         __ Sub(actualNumArgsReg, actualNumArgsReg, Immediate(8)); // 8 : register save 8 arg
214         OptimizedCall::IncreaseStackForArguments(assembler, actualNumArgsReg, currentSp);
215         PushArgsWithArgv(assembler, glue, actualNumArgsReg, argV, undefinedValue, currentSp, nullptr, nullptr);
216         __ B(&call);
217 
218         __ Bind(&pushUndefined);
219         __ Sub(expectedNumArgs, expectedNumArgs, Immediate(8)); // 8 : register save 8 arg
220         __ Sub(actualNumArgsReg, actualNumArgsReg, Immediate(8)); // 8 : register save 8 arg
221         OptimizedCall::IncreaseStackForArguments(assembler, expectedNumArgs, currentSp);
222         __ Sub(tmp, expectedNumArgs, actualNumArgsReg);
223         PushUndefinedWithArgc(assembler, glue, tmp, undefinedValue, currentSp, nullptr, nullptr);
224         PushArgsWithArgv(assembler, glue, actualNumArgsReg, argV, undefinedValue, currentSp, nullptr, nullptr);
225         __ B(&call);
226     }
227 
228     __ Bind(&checkExpectedArgs);
229     {
230         __ Cmp(expectedNumArgs, Immediate(8)); // 8 : register save 8 arg
231         __ B(Condition::LS, &call);
232         __ Sub(expectedNumArgs, expectedNumArgs, Immediate(8)); // 8 : register save 8 arg
233         OptimizedCall::IncreaseStackForArguments(assembler, expectedNumArgs, currentSp);
234         TempRegister2Scope scope2(assembler);
235         Register undefinedValue = __ TempRegister2();
236         PushUndefinedWithArgc(assembler, glue, expectedNumArgs, undefinedValue, currentSp, nullptr, nullptr);
237         __ B(&call);
238     }
239     __ Bind(&call);
240     TempRegister1Scope scope1(assembler);
241     Register method1 = __ TempRegister1();
242     __ Ldr(method1, MemoryOperand(jsfunc, JSFunction::METHOD_OFFSET));
243     __ Ldr(X11, MemoryOperand(jsfunc, JSFunction::CODE_ENTRY_OFFSET));
244     __ Blr(X11);
245 
246     __ Mov(Register(SP), Register(FP));
247     __ RestoreFpAndLr();
248     __ Ret();
249 }
250 
251 // * uint64_t JSFastCallWithArgV(uintptr_t glue, uint32_t argc, JSTaggedType calltarget,
252 //                                JSTaggedType this, argV)
253 // * cc calling convention call js function()
254 // * arguments:
255 //              %x0 - glue
256 //              %x1 - call-target
257 //              %x2 - this
258 //              %x3 - artual argc
259 //              %x4 - argv
JSFastCallWithArgV(ExtendedAssembler *assembler)260 void OptimizedFastCall::JSFastCallWithArgV(ExtendedAssembler *assembler)
261 {
262     __ BindAssemblerStub(RTSTUB_ID(JSFastCallWithArgV));
263     Register sp(SP);
264     Register glue(X0);
265     Register actualNumArgs(X3);
266     Register jsfunc(X1);
267     Register thisObj(X2);
268     Register currentSp = __ AvailableRegister1();
269     Register callsiteSp = __ AvailableRegister2();
270     Label call;
271     __ Mov(callsiteSp, sp);
272     OptimizedCall::PushOptimizedUnfoldArgVFrame(assembler, callsiteSp);
273     TempRegister2Scope scope2(assembler);
274     Register op = __ TempRegister2();
275     Register argC = __ AvailableRegister3();
276     Register argV = __ AvailableRegister4();
277     __ Mov(argC, actualNumArgs);
278     __ Mov(argV, Register(X4));
279 
280     __ Cmp(argC, Immediate(0));
281     __ B(Condition::EQ, &call);
282     __ Ldr(op, MemoryOperand(argV, 0));
283     __ Mov(Register(X3), op); // first arg
284     __ Add(argV, argV, Immediate(FRAME_SLOT_SIZE));
285     __ Sub(argC, argC, Immediate(1));
286 
287     __ Cmp(argC, Immediate(0));
288     __ B(Condition::EQ, &call);
289     __ Ldr(op, MemoryOperand(argV, 0));
290     __ Mov(Register(X4), op); // second arg
291     __ Add(argV, argV, Immediate(FRAME_SLOT_SIZE));
292     __ Sub(argC, argC, Immediate(1));
293 
294     __ Cmp(argC, Immediate(0));
295     __ B(Condition::EQ, &call);
296     __ Ldr(op, MemoryOperand(argV, 0));
297     __ Mov(Register(X5), op); // third arg
298     __ Add(argV, argV, Immediate(FRAME_SLOT_SIZE));
299     __ Sub(argC, argC, Immediate(1));
300 
301     __ Cmp(argC, Immediate(0));
302     __ B(Condition::EQ, &call);
303     __ Ldr(op, MemoryOperand(argV, 0));
304     __ Mov(Register(X6), op);
305     __ Add(argV, argV, Immediate(FRAME_SLOT_SIZE));
306     __ Sub(argC, argC, Immediate(1));
307 
308     __ Cmp(argC, Immediate(0));
309     __ B(Condition::EQ, &call);
310     __ Ldr(op, MemoryOperand(argV, 0));
311     __ Mov(Register(X7), op);
312     __ Add(argV, argV, Immediate(FRAME_SLOT_SIZE));
313     __ Sub(argC, argC, Immediate(1));
314 
315     __ Cmp(argC, Immediate(0));
316     __ B(Condition::EQ, &call);
317     OptimizedCall::IncreaseStackForArguments(assembler, argC, currentSp);
318     PushArgsWithArgv(assembler, glue, argC, argV, op, currentSp, nullptr, nullptr);
319 
320     __ Bind(&call);
321     TempRegister1Scope scope1(assembler);
322     Register method = __ TempRegister1();
323     __ Ldr(method, MemoryOperand(jsfunc, JSFunction::METHOD_OFFSET));
324     __ Ldr(X11, MemoryOperand(jsfunc, JSFunctionBase::CODE_ENTRY_OFFSET));
325     __ Blr(X11);
326 
327     __ Mov(Register(SP), Register(FP));
328     __ RestoreFpAndLr();
329     __ Ret();
330 }
331 
332 // * Arguments:
333 //        %x0 - glue
334 //        %x1 - func
335 //        %x2 - this
336 //        %x3 - actualNumArgs
337 //        %x4 -  argv
338 //        %x5 -  expectedNumArgs
JSFastCallWithArgVAndPushArgv(ExtendedAssembler *assembler)339 void OptimizedFastCall::JSFastCallWithArgVAndPushArgv(ExtendedAssembler *assembler)
340 {
341     __ BindAssemblerStub(RTSTUB_ID(JSFastCallWithArgVAndPushArgv));
342     Register sp(SP);
343     Register glue(X0);
344     Register jsfunc(X1);
345     Register thisObj(X2);
346     Register currentSp = __ AvailableRegister1();
347     Register op = __ AvailableRegister1();
348     Register callsiteSp = __ AvailableRegister2();
349     Label call;
350     Label arg1;
351     Label arg2;
352     Label arg3;
353     Label arg4;
354     Label arg5;
355     Label argc;
356     Label checkExpectedArgs;
357     Label pushUndefined;
358     __ Mov(callsiteSp, sp);
359     OptimizedCall::PushOptimizedUnfoldArgVFrame(assembler, callsiteSp);
360     Register actualNumArgsReg = __ AvailableRegister3();
361     Register argV = __ AvailableRegister4();
362     Register expectedNumArgs = __ AvailableRegister2();
363     __ Mov(actualNumArgsReg, Register(X3));
364     __ Mov(argV, Register(X4));
365     __ Mov(expectedNumArgs, Register(X5));
366 
367     __ Cmp(actualNumArgsReg, Immediate(0));
368     __ B(Condition::NE, &arg1);
369     __ Mov(Register(X3), Immediate(JSTaggedValue::VALUE_UNDEFINED));
370     __ Mov(Register(X4), Immediate(JSTaggedValue::VALUE_UNDEFINED));
371     __ Mov(Register(X5), Immediate(JSTaggedValue::VALUE_UNDEFINED));
372     __ Mov(Register(X6), Immediate(JSTaggedValue::VALUE_UNDEFINED));
373     __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED));
374     __ B(&checkExpectedArgs);
375 
376     __ Bind(&arg1);
377     {
378         __ Ldr(op, MemoryOperand(argV, 0));
379         __ Mov(Register(X3), op);
380         __ Add(argV, argV, Immediate(FRAME_SLOT_SIZE));
381         __ Cmp(actualNumArgsReg, Immediate(1));
382         __ B(Condition::NE, &arg2);
383         __ Mov(Register(X4), Immediate(JSTaggedValue::VALUE_UNDEFINED));
384         __ Mov(Register(X5), Immediate(JSTaggedValue::VALUE_UNDEFINED));
385         __ Mov(Register(X6), Immediate(JSTaggedValue::VALUE_UNDEFINED));
386         __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED));
387         __ B(&checkExpectedArgs);
388     }
389 
390     __ Bind(&arg2);
391     {
392         __ Ldr(op, MemoryOperand(argV, 0));
393         __ Mov(Register(X4), op);
394         __ Add(argV, argV, Immediate(FRAME_SLOT_SIZE));
395         __ Cmp(actualNumArgsReg, Immediate(2)); // 2: 2 args
396         __ B(Condition::NE, &arg3);
397         __ Mov(Register(X5), Immediate(JSTaggedValue::VALUE_UNDEFINED));
398         __ Mov(Register(X6), Immediate(JSTaggedValue::VALUE_UNDEFINED));
399         __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED));
400         __ B(&checkExpectedArgs);
401     }
402 
403     __ Bind(&arg3);
404     {
405         __ Ldr(op, MemoryOperand(argV, 0));
406         __ Mov(Register(X5), op);
407         __ Add(argV, argV, Immediate(FRAME_SLOT_SIZE));
408         __ Cmp(actualNumArgsReg, Immediate(3)); // 3: 3 args
409         __ B(Condition::NE, &arg4);
410         __ Mov(Register(X6), Immediate(JSTaggedValue::VALUE_UNDEFINED));
411         __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED));
412         __ B(&checkExpectedArgs);
413     }
414 
415     __ Bind(&arg4);
416     {
417         __ Ldr(op, MemoryOperand(argV, 0));
418         __ Mov(Register(X6), op);
419         __ Add(argV, argV, Immediate(FRAME_SLOT_SIZE));
420         __ Cmp(actualNumArgsReg, Immediate(4)); // 4: 4 args
421         __ B(Condition::NE, &arg5);
422         __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED));
423         __ B(&checkExpectedArgs);
424     }
425 
426     __ Bind(&arg5);
427     {
428         __ Ldr(op, MemoryOperand(argV, 0));
429         __ Mov(Register(X7), op);
430         __ Add(argV, argV, Immediate(FRAME_SLOT_SIZE));
431         __ Cmp(actualNumArgsReg, Immediate(5)); // 5: 5 args
432         __ B(Condition::NE, &argc);
433         __ B(&checkExpectedArgs);
434     }
435 
436     __ Bind(&argc);
437     {
438         TempRegister1Scope scope1(assembler);
439         TempRegister2Scope scope2(assembler);
440         Register tmp = __ TempRegister1();
441         Register undefinedValue = __ TempRegister2();
442 
443         __ Cmp(expectedNumArgs, actualNumArgsReg);
444         __ B(Condition::GT, &pushUndefined);
445         __ Sub(expectedNumArgs, expectedNumArgs, Immediate(5)); // 5 : register save 5 arg
446         __ Sub(actualNumArgsReg, actualNumArgsReg, Immediate(5)); // 5 : register save 5 arg
447         OptimizedCall::IncreaseStackForArguments(assembler, actualNumArgsReg, currentSp);
448         PushArgsWithArgv(assembler, glue, actualNumArgsReg, argV, undefinedValue, currentSp, nullptr, nullptr);
449         __ B(&call);
450 
451         __ Bind(&pushUndefined);
452         __ Sub(expectedNumArgs, expectedNumArgs, Immediate(5)); // 5 : register save 5 arg
453         __ Sub(actualNumArgsReg, actualNumArgsReg, Immediate(5)); // 5 : register save 5 arg
454         OptimizedCall::IncreaseStackForArguments(assembler, expectedNumArgs, currentSp);
455         __ Sub(tmp, expectedNumArgs, actualNumArgsReg);
456         PushUndefinedWithArgc(assembler, glue, tmp, undefinedValue, currentSp, nullptr, nullptr);
457         PushArgsWithArgv(assembler, glue, actualNumArgsReg, argV, undefinedValue, currentSp, nullptr, nullptr);
458         __ B(&call);
459     }
460 
461     __ Bind(&checkExpectedArgs);
462     {
463         __ Cmp(expectedNumArgs, Immediate(5)); // 5 : register save 5 arg
464         __ B(Condition::LS, &call);
465         __ Sub(expectedNumArgs, expectedNumArgs, Immediate(5)); // 5 : register save 5 arg
466         OptimizedCall::IncreaseStackForArguments(assembler, expectedNumArgs, currentSp);
467         TempRegister2Scope scope2(assembler);
468         Register undefinedValue = __ TempRegister2();
469         PushUndefinedWithArgc(assembler, glue, expectedNumArgs, undefinedValue, currentSp, nullptr, nullptr);
470         __ B(&call);
471     }
472 
473     __ Bind(&call);
474     TempRegister1Scope scope1(assembler);
475     Register method = __ TempRegister1();
476     __ Ldr(method, MemoryOperand(X1, JSFunction::METHOD_OFFSET));
477     __ Ldr(X11, MemoryOperand(X1, JSFunction::CODE_ENTRY_OFFSET));
478     __ Blr(X11);
479 
480     __ Mov(Register(SP), Register(FP));
481     __ RestoreFpAndLr();
482     __ Ret();
483 }
484 #undef __
485 }  // panda::ecmascript::aarch64