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