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
30namespace panda::ecmascript::aarch64 {
31using 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
41void 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//          +--------------------------+ -----------------
95void 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
260void 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
339void 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