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