1 /*
2 * Copyright (c) 2022-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 JSFunctionEntry(uintptr_t glue, uint32_t actualNumArgs, const JSTaggedType argV[], uintptr_t prevFp)
34 // * Arguments:
35 // %rdi - glue
36 // %rsi - actualNumArgs
37 // %rdx - argV
38 // %rcx - prevFp
39 // %r8 - needPushArgv
40 //
41 // * The JSFunctionEntry Frame's structure is illustrated as the following:
42 // +--------------------------+
43 // | . . . . . . |
44 // sp ---> +--------------------------+ -----------------
45 // | prevFP | ^
46 // |--------------------------| |
47 // | frameType | JSFunctionEntryFrame
48 // |--------------------------| |
49 // | preLeaveFrameFp | v
50 // +--------------------------+ -----------------
51
JSFunctionEntry(ExtendedAssembler * assembler)52 void OptimizedCall::JSFunctionEntry(ExtendedAssembler *assembler)
53 {
54 __ BindAssemblerStub(RTSTUB_ID(JSFunctionEntry));
55 Register glueReg = rdi;
56 Register argv = rdx;
57 Register prevFpReg = rcx;
58 Register needPushArgv = r8;
59 Label lJSCallWithArgVAndPushArgv;
60 Label lPopFrame;
61 PushJSFunctionEntryFrame(assembler, prevFpReg);
62 __ Movq(argv, rbx);
63 __ Movq(needPushArgv, r12);
64 __ Movq(Operand(rbx, 0), rdx);
65 __ Movq(Operand(rbx, FRAME_SLOT_SIZE), rcx);
66 __ Movq(Operand(rbx, DOUBLE_SLOT_SIZE), r8);
67 __ Addq(TRIPLE_SLOT_SIZE, rbx);
68 __ Movq(rbx, r9);
69 __ Cmp(1, r12);
70 __ Je(&lJSCallWithArgVAndPushArgv);
71 __ CallAssemblerStub(RTSTUB_ID(JSCallWithArgV), false);
72 __ Jmp(&lPopFrame);
73
74 __ Bind(&lJSCallWithArgVAndPushArgv);
75 __ CallAssemblerStub(RTSTUB_ID(JSCallWithArgVAndPushArgv), false);
76
77 __ Bind(&lPopFrame);
78 __ Popq(prevFpReg);
79 __ Addq(FRAME_SLOT_SIZE, rsp); // 8: frame type
80 __ Popq(rbp);
81 __ Popq(glueReg); // caller restore
82 __ PopCppCalleeSaveRegisters(); // callee restore
83 __ Movq(prevFpReg, Operand(glueReg, JSThread::GlueData::GetLeaveFrameOffset(false)));
84 __ Ret();
85 }
86
87 // * uint64_t OptimizedCallAndPushArgv(uintptr_t glue, uint32_t argc, JSTaggedType calltarget, JSTaggedType new,
88 // JSTaggedType this, arg[0], arg[1], arg[2], ..., arg[N-1])
89 // * webkit_jscc calling convention call js function()
90 //
91 // * OptimizedJSFunctionFrame layout description as the following:
92 // +--------------------------+
93 // | arg[N-1] |
94 // +--------------------------+
95 // | ... |
96 // +--------------------------+
97 // | arg[1] |
98 // +--------------------------+
99 // | arg[0] |
100 // +--------------------------+
101 // | this |
102 // +--------------------------+
103 // | new-target |
104 // +--------------------------+
105 // | call-target |
106 // +--------------------------+
107 // | argv |
108 // |--------------------------|
109 // | argc |
110 // |--------------------------| ---------------
111 // | returnAddr | ^
112 // sp ----> |--------------------------| |
113 // | callsiteFp | |
114 // |--------------------------| OptimizedJSFunctionFrame
115 // | frameType | |
116 // |--------------------------| |
117 // | call-target | v
118 // +--------------------------+ ---------------
119
OptimizedCallAndPushArgv(ExtendedAssembler * assembler)120 void OptimizedCall::OptimizedCallAndPushArgv(ExtendedAssembler *assembler)
121 {
122 __ BindAssemblerStub(RTSTUB_ID(OptimizedCallAndPushArgv));
123 Register jsFuncReg = rdi;
124 Register method = r9;
125 Register codeAddrReg = rsi;
126
127 auto funcSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1; // 1: return addr
128 __ Movq(Operand(rsp, funcSlotOffset * FRAME_SLOT_SIZE), jsFuncReg); // sp + 24 get jsFunc
129 __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method
130 __ Mov(Operand(jsFuncReg, JSFunctionBase::CODE_ENTRY_OFFSET), codeAddrReg);
131
132 Register methodCallField = rcx;
133 __ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField); // get call field
134 __ Shr(MethodLiteral::NumArgsBits::START_BIT, methodCallField);
135 __ Andl(((1LU << MethodLiteral::NumArgsBits::SIZE) - 1), methodCallField);
136 __ Addl(NUM_MANDATORY_JSFUNC_ARGS, methodCallField); // add mandatory argumentr
137
138 __ Movl(Operand(rsp, FRAME_SLOT_SIZE), rdx); // argc rdx
139 __ Movq(rsp, r8);
140 Register argvReg = r8;
141
142 __ Addq(funcSlotOffset * FRAME_SLOT_SIZE, argvReg); // skip return addr, argc and agv
143
144 Register expectedNumArgsReg = rcx;
145 Register actualNumArgsReg = rdx;
146
147 __ Pushq(rbp);
148 __ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME));
149 __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
150 // callee save
151 __ Pushq(r14);
152 __ Pushq(rbx);
153 __ Pushq(rax);
154
155 Label lCopyExtraAument1;
156 Label lCopyLoop1;
157 Label lCopyLoop2;
158 Label lPopFrame1;
159 Label pushUndefined;
160 Label commonCall;
161
162 __ Cmpq(expectedNumArgsReg, actualNumArgsReg);
163 __ Jb(&pushUndefined);
164 // 16 bytes align check
165 __ Movl(actualNumArgsReg, r14);
166 __ Testb(1, r14);
167 __ Je(&lCopyLoop2);
168 __ Pushq(0);
169
170 __ Bind(&lCopyLoop2);
171 __ Movq(Operand(argvReg, r14, Scale::Times8, -FRAME_SLOT_SIZE), rbx); // -8: stack index
172 __ Pushq(rbx);
173 __ Addq(-1, r14);
174 __ Jne(&lCopyLoop2);
175 __ Movl(actualNumArgsReg, r14);
176 __ Jmp(&commonCall);
177
178 __ Bind(&pushUndefined);
179 // 16 bytes align check
180 __ Movl(expectedNumArgsReg, r14);
181 __ Testb(1, r14);
182 __ Je(&lCopyExtraAument1);
183 __ Pushq(0);
184
185 __ Bind(&lCopyExtraAument1); // copy undefined value to stack
186 __ Pushq(JSTaggedValue::VALUE_UNDEFINED);
187 __ Addq(-1, expectedNumArgsReg);
188 __ Cmpq(actualNumArgsReg, expectedNumArgsReg);
189 __ Ja(&lCopyExtraAument1);
190
191 __ Bind(&lCopyLoop1);
192 __ Movq(Operand(argvReg, expectedNumArgsReg, Scale::Times8, -FRAME_SLOT_SIZE), rbx); // -8: stack index
193 __ Pushq(rbx);
194 __ Addq(-1, expectedNumArgsReg);
195 __ Jne(&lCopyLoop1);
196 __ Jmp(&commonCall);
197
198 __ Bind(&commonCall);
199 __ Pushq(rsp); // actual argv
200 __ Pushq(actualNumArgsReg); // actual argc
201
202 __ Callq(codeAddrReg); // then call jsFunction
203 __ Leaq(Operand(r14, Scale::Times8, 0), codeAddrReg);
204 __ Addq(codeAddrReg, rsp);
205 __ Addq(FRAME_SLOT_SIZE, rsp); // skip actualNumArgsReg
206 __ Addq(FRAME_SLOT_SIZE, rsp); // skip argvReg
207 __ Testb(1, r14); // stack 16bytes align check
208 __ Je(&lPopFrame1);
209 __ Addq(8, rsp); // 8: align byte
210
211 __ Bind(&lPopFrame1);
212 __ Addq(8, rsp); // 8: skip rax
213 __ Popq(rbx);
214 __ Popq(r14);
215 __ Addq(FRAME_SLOT_SIZE, rsp); // skip frame type
216 __ Pop(rbp);
217 __ Ret();
218 }
219
OptimizedCallAsmInterpreter(ExtendedAssembler * assembler)220 void OptimizedCall::OptimizedCallAsmInterpreter(ExtendedAssembler *assembler)
221 {
222 Label target;
223 PushAsmInterpBridgeFrame(assembler);
224 __ Callq(&target);
225 PopAsmInterpBridgeFrame(assembler);
226 __ Ret();
227 __ Bind(&target);
228 AsmInterpreterCall::JSCallCommonEntry(
229 assembler, JSCallMode::CALL_FROM_AOT, FrameTransitionType::OTHER_TO_OTHER);
230 }
231
232 // * uint64_t CallBuiltinTrampoline(uintptr_t glue, uintptr_t codeAddress, uint32_t argc, ...)
233 // * webkit_jscc calling convention call runtime_id's runtime function(c-abi)
234 //
235 // * Construct Native Leave Frame Layout:
236 // +--------------------------+
237 // | argv[N-1] |
238 // +--------------------------+
239 // | . . . . . . |
240 // +--------------------------+
241 // | argv[3]=a0 |
242 // +--------------------------+
243 // | argv[2]=this |
244 // +--------------------------+
245 // | argv[1]=new-target |
246 // +--------------------------+
247 // | argv[0]=call-target |
248 // +--------------------------+ -----------------
249 // | argc | ^
250 // |--------------------------| |
251 // | thread | |
252 // |--------------------------| |
253 // | returnAddr | OptimizedBuiltinLeaveFrame
254 // sp ---> |--------------------------| |
255 // | callsiteFp | |
256 // |--------------------------| |
257 // | frameType | |
258 // |--------------------------| |
259 // | align byte | v
260 // +--------------------------+ -----------------
261
RemoveArgv(ExtendedAssembler * assembler,Register temp)262 void OptimizedCall::RemoveArgv(ExtendedAssembler *assembler, Register temp)
263 {
264 // remove argv
265 __ Movq(Operand(rsp, FRAME_SLOT_SIZE), temp);
266 __ Movq(temp, Operand(rsp, DOUBLE_SLOT_SIZE)); // argc -> argv
267 __ Movq(Operand(rsp, 0), temp);
268 __ Movq(temp, Operand(rsp, FRAME_SLOT_SIZE)); // returnAddr -> argc
269 __ Addq(FRAME_SLOT_SIZE, rsp); // skip argv
270 }
271
CallBuiltinTrampoline(ExtendedAssembler * assembler,Register temp)272 void OptimizedCall::CallBuiltinTrampoline(ExtendedAssembler *assembler, Register temp)
273 {
274 Register glueReg = rax;
275 Register nativeCode = rsi;
276
277 // remove argv
278 RemoveArgv(assembler, temp);
279
280 __ Movq(Operand(rsp, 0), rdx);
281 __ Movq(glueReg, Operand(rsp, 0));
282 __ Push(rdx);
283
284 AsmInterpreterCall::PushBuiltinFrame(assembler, glueReg, FrameType::BUILTIN_CALL_LEAVE_FRAME);
285 __ Leaq(Operand(rbp, DOUBLE_SLOT_SIZE), rdi); // 2: skip rbp & return Addr
286 __ PushAlignBytes();
287 AsmInterpreterCall::CallNativeInternal(assembler, nativeCode);
288 __ Movq(Operand(rsp, DOUBLE_SLOT_SIZE), temp); // argc
289 __ Movq(Immediate(0), Operand(rsp, DOUBLE_SLOT_SIZE)); // argv -> argc
290 __ Movq(temp, Operand(rsp, FRAME_SLOT_SIZE)); // argc -> thread
291
292 __ Ret();
293 }
294
295 // * uint64_t CallBuiltinConstructorStub(uintptr_t glue, uintptr_t codeAddress, uint32_t argc, ...)
296 // * webkit_jscc calling convention call runtime_id's runtime function(c-abi)
297 //
298 // * Construct Native Leave Frame Layout:
299 // +--------------------------+
300 // | argv[N-1] |
301 // +--------------------------+
302 // | . . . . . . |
303 // +--------------------------+
304 // | argv[3]=a0 |
305 // +--------------------------+
306 // | argv[2]=this |
307 // +--------------------------+
308 // | argv[1]=new-target |
309 // +--------------------------+
310 // | argv[0]=call-target |
311 // +--------------------------+ -----------------
312 // | argc | ^
313 // |--------------------------| |
314 // | thread | |
315 // |--------------------------| |
316 // | returnAddr | OptimizedBuiltinLeaveFrame
317 // sp ---> |--------------------------| |
318 // | callsiteFp | |
319 // |--------------------------| |
320 // | frameType | v
321 // +--------------------------+ -----------------
322
CallBuiltinConstructorStub(ExtendedAssembler * assembler,Register builtinStub,Register argv,Register glue,Register temp)323 void OptimizedCall::CallBuiltinConstructorStub(ExtendedAssembler *assembler, Register builtinStub, Register argv,
324 Register glue, Register temp)
325 {
326 // remove argv
327 RemoveArgv(assembler, temp);
328
329 __ Movq(Operand(rsp, 0), temp);
330 __ Movq(glue, Operand(rsp, 0));
331 __ Push(temp);
332
333 // push rbp & frameType
334 AsmInterpreterCall::PushBuiltinFrame(assembler, glue, FrameType::BUILTIN_CALL_LEAVE_FRAME);
335 __ Push(argv);
336 __ Callq(builtinStub);
337 // resume rsp
338 __ Movq(rbp, rsp);
339 __ Pop(rbp);
340 __ Movq(Operand(rsp, DOUBLE_SLOT_SIZE), temp); // argc
341 __ Movq(Immediate(0), Operand(rsp, DOUBLE_SLOT_SIZE)); // 0 -> argc
342 __ Movq(temp, Operand(rsp, FRAME_SLOT_SIZE)); // argc -> thread
343 __ Ret();
344 }
345
346 // * uint64_t JSProxyCallInternalWithArgV(uintptr_t glue, JSTaggedType calltarget)
347 // * c++ calling convention call js function
348 // * Arguments:
349 // %rdi - glue
350 // %rsi - calltarget
351
JSProxyCallInternalWithArgV(ExtendedAssembler * assembler)352 void OptimizedCall::JSProxyCallInternalWithArgV(ExtendedAssembler *assembler)
353 {
354 __ BindAssemblerStub(RTSTUB_ID(JSProxyCallInternalWithArgV));
355 Register ccGlueReg = rdi;
356 Register jsccGlueReg = rax;
357 Register callTarget = rsi;
358 __ Movq(ccGlueReg, jsccGlueReg); // c++ calling convention as webkit_jscc calling convention
359 auto funcSlotOffSet = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1;
360 __ Movq(callTarget, Operand(rsp, funcSlotOffSet * FRAME_SLOT_SIZE)); // update callTarget slot
361 GenJSCall(assembler, false);
362 }
363
364 // * uint64_t JSCall(uintptr_t glue, uint32_t argc, JSTaggedType calltarget, JSTaggedType new,
365 // JSTaggedType this, arg[0], arg[1], arg[2], ..., arg[N-1])
366 // * webkit_jscc calling convention call js function()
367 //
368 // * OptimizedJSFunctionFrame layout description as the following:
369 // +--------------------------+
370 // | arg[N-1] |
371 // +--------------------------+
372 // | ... |
373 // +--------------------------+
374 // | arg[1] |
375 // +--------------------------+
376 // | arg[0] |
377 // +--------------------------+
378 // | this |
379 // +--------------------------+
380 // | new-target |
381 // +--------------------------+
382 // | call-target |
383 // +--------------------------+
384 // | argv |
385 // |--------------------------|
386 // | argc |
387 // |--------------------------| ---------------
388 // | returnAddr | ^
389 // sp ----> |--------------------------| |
390 // | callsiteFp | |
391 // |--------------------------| OptimizedJSFunctionFrame
392 // | frameType | |
393 // |--------------------------| |
394 // | call-target | v
395 // +--------------------------+ ---------------
JSCallNew(ExtendedAssembler * assembler)396 void OptimizedCall::JSCallNew(ExtendedAssembler *assembler)
397 {
398 __ BindAssemblerStub(RTSTUB_ID(JSCallNew));
399 GenJSCall(assembler, true);
400 }
401
JSCall(ExtendedAssembler * assembler)402 void OptimizedCall::JSCall(ExtendedAssembler *assembler)
403 {
404 __ BindAssemblerStub(RTSTUB_ID(JSCall));
405 GenJSCall(assembler, false);
406 }
407
GenJSCall(ExtendedAssembler * assembler,bool isNew)408 void OptimizedCall::GenJSCall(ExtendedAssembler *assembler, bool isNew)
409 {
410 Label jsCall;
411 Label lJSCallStart;
412 Label lNotJSFunction;
413 Label lNonCallable;
414 Label lJSFunctionCall;
415 Label lJSBoundFunction;
416 Label lJSProxy;
417 Label lCallNativeMethod;
418 Label lCallNativeCpp;
419 Label lCallNativeBuiltinStub;
420 Register glueReg = rax;
421 __ Bind(&jsCall);
422 {
423 __ Movq(glueReg, rdi);
424 glueReg = rdi;
425 auto funcSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1;
426 __ Movq(Operand(rsp, funcSlotOffset * FRAME_SLOT_SIZE), rax); // sp + 24 get jsFunc
427 }
428 __ Bind(&lJSCallStart);
429 Register jsFuncReg = rax;
430 {
431 JSCallCheck(assembler, jsFuncReg, &lNonCallable, &lNotJSFunction, &lJSFunctionCall);
432 }
433
434 __ Bind(&lNotJSFunction);
435 {
436 __ Cmpb(static_cast<uint8_t>(JSType::JS_BOUND_FUNCTION), rax); // IsBoundFunction
437 __ Je(&lJSBoundFunction);
438 __ Cmpb(static_cast<uint8_t>(JSType::JS_PROXY), rax); // IsJsProxy
439 __ Je(&lJSProxy);
440 }
441
442 __ Bind(&lNonCallable);
443 {
444 ThrowNonCallableInternal(assembler, glueReg);
445 }
446
447 __ Bind(&lJSFunctionCall);
448 jsFuncReg = rsi;
449 Register argc = r8;
450 Register methodCallField = rcx;
451 Register method = rdx;
452 Register argV = r9;
453 {
454 Label lCallConstructor;
455 Label lNotClass;
456 __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method
457 __ Movl(Operand(rsp, FRAME_SLOT_SIZE), argc); // skip return addr
458 __ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField); // get call field
459 __ Btq(MethodLiteral::IsNativeBit::START_BIT, methodCallField); // is native
460 __ Jb(&lCallNativeMethod);
461 if (!isNew) {
462 __ Btq(JSHClass::IsClassConstructorOrPrototypeBit::START_BIT, rax); // is CallConstructor
463 __ Jnb(&lNotClass);
464 __ Btq(JSHClass::ConstructorBit::START_BIT, rax); // is CallConstructor
465 __ Jb(&lCallConstructor);
466 }
467 __ Bind(&lNotClass);
468 __ Movq(rsp, argV);
469 auto argvSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1; // 1: return addr
470 __ Addq(argvSlotOffset * FRAME_SLOT_SIZE, argV); // skip return addr, argc and argv
471 __ Subq(Immediate(kungfu::ArgumentAccessor::GetFixArgsNum()), argc);
472 // argv + 24 get asm interpreter argv
473 __ Addq(kungfu::ArgumentAccessor::GetFixArgsNum() * FRAME_SLOT_SIZE, argV);
474 OptimizedCallAsmInterpreter(assembler);
475 __ Bind(&lCallConstructor);
476 {
477 __ Pushq(rbp);
478 __ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME)); // set frame type
479 __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
480 __ Pushq(0); // PushAlign
481 __ Pushq(0); // argc
482 __ Pushq(RTSTUB_ID(ThrowCallConstructorException)); // runtime id
483 __ Movq(glueReg, rax); // glue
484 __ Movq(kungfu::RuntimeStubCSigns::ID_CallRuntime, r10);
485 __ Movq(Operand(rax, r10, Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r10);
486 __ Callq(r10); // call CallRuntime
487 __ Addq(4 * FRAME_SLOT_SIZE, rsp); // 4: sp + 32 argv
488 __ Pop(rbp);
489 __ Ret();
490 }
491 }
492
493 __ Bind(&lCallNativeMethod);
494 {
495 Register nativePointer = rsi;
496 method = rax;
497 __ Movq(jsFuncReg, rdx);
498 __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method
499 __ Mov(Operand(method, Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET), nativePointer); // native pointer
500 __ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField); // get call field
501 __ Btq(MethodLiteral::IsFastBuiltinBit::START_BIT, methodCallField); // is builtin stub
502
503 if (!isNew) {
504 __ Jnb(&lCallNativeCpp);
505 __ Cmpl(NUM_MANDATORY_JSFUNC_ARGS + 3, argc); // 3:call0, call1, call2, call3
506 __ Jbe(&lCallNativeBuiltinStub);
507 } else {
508 __ Jb(&lCallNativeBuiltinStub);
509 }
510 }
511
512 __ Bind(&lCallNativeCpp);
513 {
514 __ Movq(glueReg, rax);
515 CallBuiltinTrampoline(assembler, r11);
516 }
517
518 __ Bind(&lCallNativeBuiltinStub);
519 {
520 Register methodExtraLiteralInfo = rax;
521 __ Mov(Operand(method, Method::EXTRA_LITERAL_INFO_OFFSET), methodExtraLiteralInfo); // get extra literal
522 __ Shr(MethodLiteral::BuiltinIdBits::START_BIT, methodExtraLiteralInfo);
523 __ Andl(((1LU << MethodLiteral::BuiltinIdBits::SIZE) - 1), methodExtraLiteralInfo); // get builtin stub id
524 if (!isNew) {
525 __ Cmpl(kungfu::BuiltinsStubCSigns::BUILTINS_CONSTRUCTOR_STUB_FIRST, methodExtraLiteralInfo);
526 __ Jnb(&lCallNativeCpp);
527 }
528
529 __ Movq(glueReg, rdi);
530 __ Movq(methodExtraLiteralInfo, r10);
531 __ Movq(Operand(glueReg, r10, Times8, JSThread::GlueData::GetBuiltinsStubEntriesOffset(false)), r10);
532
533 __ Movq(argc, r9);
534 __ Movq(Operand(rsp, QUADRUPLE_SLOT_SIZE), rcx); // newTarget
535 __ Movq(Operand(rsp, QUINTUPLE_SLOT_SIZE), r8); // this
536 __ Subq(NUM_MANDATORY_JSFUNC_ARGS, r9); // argc
537
538 Label lCall0;
539 Label lCall1;
540 Label lCall2;
541 Label lCall3;
542 argV = rax;
543
544 __ Movq(rsp, argV);
545 auto argvSlotOffset = kungfu::ArgumentAccessor::GetFixArgsNum() +
546 kungfu::ArgumentAccessor::GetExtraArgsNum() + 1; // 1: return addr
547 __ Addq(argvSlotOffset *FRAME_SLOT_SIZE, argV);
548 if (!isNew) {
549 PushAsmBridgeFrame(assembler);
550
551 __ Cmpl(0, r9); // 0: callarg0
552 __ Je(&lCall0);
553 __ Cmpl(1, r9); // 1: callarg1
554 __ Je(&lCall1);
555 __ Cmpl(2, r9); // 2: callarg2
556 __ Je(&lCall2);
557 __ Cmpl(3, r9); // 3: callarg3
558 __ Je(&lCall3);
559
560 __ Bind(&lCall0);
561 {
562 __ Pushq(JSTaggedValue::VALUE_UNDEFINED);
563 __ Pushq(JSTaggedValue::VALUE_UNDEFINED);
564 __ Pushq(JSTaggedValue::VALUE_UNDEFINED);
565 __ Callq(r10);
566 __ Addq(QUADRUPLE_SLOT_SIZE, rsp);
567 __ Pop(rbp);
568 __ Ret();
569 }
570
571 __ Bind(&lCall1);
572 {
573 __ Pushq(JSTaggedValue::VALUE_UNDEFINED);
574 __ Pushq(JSTaggedValue::VALUE_UNDEFINED);
575 __ Movq(Operand(argV, 0), r11); // arg0
576 __ Pushq(r11);
577 __ Callq(r10);
578 __ Addq(QUADRUPLE_SLOT_SIZE, rsp);
579 __ Pop(rbp);
580 __ Ret();
581 }
582
583 __ Bind(&lCall2);
584 {
585 __ Pushq(JSTaggedValue::VALUE_UNDEFINED);
586 __ Movq(Operand(argV, FRAME_SLOT_SIZE), r11); // arg1
587 __ Pushq(r11);
588 __ Movq(Operand(argV, 0), r11); // arg0
589 __ Pushq(r11);
590 __ Callq(r10);
591 __ Addq(QUADRUPLE_SLOT_SIZE, rsp);
592 __ Pop(rbp);
593 __ Ret();
594 }
595
596 __ Bind(&lCall3);
597 {
598 __ Movq(Operand(argV, DOUBLE_SLOT_SIZE), r11); // arg2
599 __ Pushq(r11);
600 __ Movq(Operand(argV, FRAME_SLOT_SIZE), r11); // arg1
601 __ Pushq(r11);
602 __ Movq(Operand(argV, 0), r11); // arg0
603 __ Pushq(r11);
604 __ Callq(r10);
605 __ Addq(QUADRUPLE_SLOT_SIZE, rsp);
606 __ Pop(rbp);
607 __ Ret();
608 }
609 } else {
610 CallBuiltinConstructorStub(assembler, r10, argV, glueReg, r11);
611 __ Int3();
612 }
613 }
614
615 __ Bind(&lJSBoundFunction);
616 {
617 JSBoundFunctionCallInternal(assembler, jsFuncReg, &jsCall);
618 }
619 __ Bind(&lJSProxy);
620 {
621 __ Mov(Operand(jsFuncReg, JSProxy::METHOD_OFFSET), method);
622 __ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField);
623 __ Mov(Operand(rsp, FRAME_SLOT_SIZE), argc);
624 __ Jmp(&lCallNativeMethod);
625 }
626 }
627
628 // After the callee function of common aot call deopt, use this bridge to deal with this aot call.
629 // calling convention: webkit_jsc
630 // Input structure:
631 // %rax - glue
632 // stack:
633 // +--------------------------+
634 // | arg[N-1] |
635 // +--------------------------+
636 // | ... |
637 // +--------------------------+
638 // | arg[1] |
639 // +--------------------------+
640 // | arg[0] |
641 // +--------------------------+
642 // | this |
643 // +--------------------------+
644 // | new-target |
645 // +--------------------------+
646 // | call-target |
647 // |--------------------------|
648 // | argv |
649 // |--------------------------|
650 // | argc |
651 // |--------------------------|
652 // | returnAddr |
653 // +--------------------------+ <---- sp
AOTCallToAsmInterBridge(ExtendedAssembler * assembler)654 void OptimizedCall::AOTCallToAsmInterBridge(ExtendedAssembler *assembler)
655 {
656 __ BindAssemblerStub(RTSTUB_ID(AOTCallToAsmInterBridge));
657 // params of c++ calling convention
658 Register glueReg = rdi;
659 Register jsFuncReg = rsi;
660 Register method = rdx;
661 Register methodCallField = rcx;
662 Register argc = r8;
663 Register argV = r9;
664
665 __ Movq(rax, glueReg);
666 __ Movq(Operand(rsp, TRIPLE_SLOT_SIZE), jsFuncReg);
667 __ Movq(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method
668 __ Movq(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField); // get call field
669 __ Movl(Operand(rsp, FRAME_SLOT_SIZE), argc); // skip return addr
670 __ Subq(Immediate(kungfu::ArgumentAccessor::GetFixArgsNum()), argc);
671 __ Movq(rsp, argV);
672 auto argvSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1; // 1: return addr
673 __ Addq(argvSlotOffset * FRAME_SLOT_SIZE, argV); // skip return addr and argc
674 __ Addq(kungfu::ArgumentAccessor::GetFixArgsNum() * FRAME_SLOT_SIZE, argV);
675 OptimizedCallAsmInterpreter(assembler);
676 }
677
678 // After the callee function of fast aot call deopt, use this bridge to deal with this fast aot call.
679 // Notice: no argc and new-target params compared with not-fast aot call because these params are not needed
680 // according to bytecode-analysis.
681 // Intruduction: use expected argc as actual argc below for these reasons:
682 // 1) when expected argc == actual argc, pass.
683 // 2) when expected argc > actual argc, undefineds have been pushed in OptimizedFastCallAndPushArgv.
684 // 3) when expected argc < actual argc, redundant params are useless according to bytecode-analysis, just abandon them.
685 // calling convention: c++ calling convention
686 // Input structure:
687 // %rdi - glue
688 // %rsi - call-target
689 // %rdx - this
690 // %rcx - arg0
691 // %r8 - arg1
692 // %r9 - arg2
693 // stack:
694 // +--------------------------+
695 // | arg[N-1] |
696 // +--------------------------+
697 // | ... |
698 // +--------------------------+
699 // | arg[3] |
700 // +--------------------------+
701 // | returnAddr |
702 // |--------------------------| <---- sp
FastCallToAsmInterBridge(ExtendedAssembler * assembler)703 void OptimizedCall::FastCallToAsmInterBridge(ExtendedAssembler *assembler)
704 {
705 __ BindAssemblerStub(RTSTUB_ID(FastCallToAsmInterBridge));
706 // Input
707 Register glueReg = rdi;
708 Register jsFuncReg = rsi;
709 Register thisReg = rdx;
710 Register maybeArg0 = rcx;
711 Register maybeArg1 = r8;
712 Register maybeArg2 = r9;
713
714 // Add a bridge frame to protect the stack map
715 PushAsmBridgeFrame(assembler);
716
717 Register tempMethod = __ AvailableRegister1();
718 Register tempCallField = __ AvailableRegister2();
719
720 __ Movq(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), tempMethod);
721 __ Movq(Operand(tempMethod, Method::CALL_FIELD_OFFSET), tempCallField);
722 // get expected num Args
723 Register tempArgc = tempCallField;
724 __ Shr(MethodLiteral::NumArgsBits::START_BIT, tempArgc);
725 __ Andl(((1LU << MethodLiteral::NumArgsBits::SIZE) - 1), tempArgc);
726
727 {
728 [[maybe_unused]] TempRegisterScope scope(assembler);
729 Register startSp = __ TempRegister();
730 __ Movq(rsp, startSp);
731
732 Label lCall0;
733 Label lCall1;
734 Label lCall2;
735 Label lCall3;
736 Label lPushCommonRegs;
737
738 __ Cmpl(0, tempArgc); // 0: callarg0
739 __ Je(&lCall0);
740 __ Cmpl(1, tempArgc); // 1: callarg1
741 __ Je(&lCall1);
742 __ Cmpl(2, tempArgc); // 2: callarg2
743 __ Je(&lCall2);
744 __ Cmpl(3, tempArgc); // 3: callarg3
745 __ Je(&lCall3);
746 // default: more than 3 args
747 {
748 __ Subq(Immediate(3), tempArgc); // 3: the first 3 args are not on stack
749 __ Addq(Immediate(TRIPLE_SLOT_SIZE), startSp); // skip bridge frame and return addr
750 CopyArgumentWithArgV(assembler, tempArgc, startSp);
751 __ Subq(Immediate(TRIPLE_SLOT_SIZE), startSp);
752 __ Jmp(&lCall3);
753 }
754
755 __ Bind(&lCall0);
756 {
757 __ Jmp(&lPushCommonRegs);
758 }
759
760 __ Bind(&lCall1);
761 {
762 __ Pushq(maybeArg0);
763 __ Jmp(&lPushCommonRegs);
764 }
765
766 __ Bind(&lCall2);
767 {
768 __ Pushq(maybeArg1);
769 __ Pushq(maybeArg0);
770 __ Jmp(&lPushCommonRegs);
771 }
772
773 __ Bind(&lCall3);
774 {
775 __ Pushq(maybeArg2);
776 __ Pushq(maybeArg1);
777 __ Pushq(maybeArg0);
778 __ Jmp(&lPushCommonRegs);
779 }
780
781 __ Bind(&lPushCommonRegs);
782 {
783 __ Pushq(thisReg);
784 __ Pushq(JSTaggedValue::VALUE_UNDEFINED); // newTarget
785 __ Pushq(jsFuncReg);
786 // fall through
787 }
788
789 // params of c++ calling convention
790 glueReg = rdi;
791 jsFuncReg = rsi;
792 Register method = rdx;
793 Register methodCallField = rcx;
794 Register argc = r8;
795 Register argV = r9;
796
797 // reload and prepare args for JSCallCommonEntry
798 __ Movq(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method);
799 __ Movq(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField);
800 __ Movq(methodCallField, argc);
801 __ Shr(MethodLiteral::NumArgsBits::START_BIT, argc);
802 __ Andl(((1LU << MethodLiteral::NumArgsBits::SIZE) - 1), argc);
803 __ Movq(rsp, argV);
804 __ Addq(Immediate(TRIPLE_SLOT_SIZE), argV); // skip func, newtarget and this
805
806 __ Pushq(startSp); // used for resume rsp
807 }
808
809 Label target;
810 PushAsmInterpBridgeFrame(assembler);
811 __ Callq(&target);
812 {
813 PopAsmInterpBridgeFrame(assembler);
814 Register startSp = __ AvailableRegister1();
815 __ Popq(startSp);
816 __ Movq(startSp, rsp);
817 PopAsmBridgeFrame(assembler);
818 __ Ret();
819 }
820 __ Bind(&target);
821 AsmInterpreterCall::JSCallCommonEntry(
822 assembler, JSCallMode::CALL_FROM_AOT, FrameTransitionType::OTHER_TO_OTHER);
823 }
824
PushAsmBridgeFrame(ExtendedAssembler * assembler)825 void OptimizedCall::PushAsmBridgeFrame(ExtendedAssembler *assembler)
826 {
827 __ Pushq(rbp);
828 __ Pushq(static_cast<int32_t>(FrameType::ASM_BRIDGE_FRAME));
829 __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
830 }
831
PopAsmBridgeFrame(ExtendedAssembler * assembler)832 void OptimizedCall::PopAsmBridgeFrame(ExtendedAssembler *assembler)
833 {
834 __ Addq(FRAME_SLOT_SIZE, rsp); // skip type
835 __ Popq(rbp);
836 }
837
JSCallCheck(ExtendedAssembler * assembler,Register jsFuncReg,Label * lNonCallable,Label * lNotJSFunction,Label * lJSFunctionCall)838 void OptimizedCall::JSCallCheck(ExtendedAssembler *assembler, Register jsFuncReg,
839 Label *lNonCallable, Label *lNotJSFunction, Label *lJSFunctionCall)
840 {
841 __ Movabs(JSTaggedValue::TAG_INT, rdx); // IsTaggedInt
842 __ And(jsFuncReg, rdx);
843 __ Cmp(0x0, rdx);
844 __ Jne(lNonCallable);
845 __ Cmp(0x0, jsFuncReg); // IsHole
846 __ Je(lNonCallable);
847 __ Movabs(JSTaggedValue::TAG_SPECIAL, rdx);
848 __ And(jsFuncReg, rdx); // IsSpecial
849 __ Cmp(0x0, rdx);
850 __ Jne(lNonCallable);
851
852 __ Movq(jsFuncReg, rsi); // save jsFunc
853 __ Movq(Operand(jsFuncReg, JSFunction::HCLASS_OFFSET), rax); // get jsHclass
854 Register jsHclassReg = rax;
855 __ Movl(Operand(jsHclassReg, JSHClass::BIT_FIELD_OFFSET), rax);
856 __ Btl(JSHClass::CallableBit::START_BIT, rax); // IsCallable
857 __ Jnb(lNonCallable);
858
859 __ Cmpb(static_cast<int32_t>(JSType::JS_FUNCTION_FIRST), rax);
860 __ Jb(lNotJSFunction);
861 __ Cmpb(static_cast<int32_t>(JSType::JS_FUNCTION_LAST), rax);
862 __ Jbe(lJSFunctionCall); // objecttype in (0x04 ~ 0x0c)
863 }
864
ThrowNonCallableInternal(ExtendedAssembler * assembler,Register glueReg)865 void OptimizedCall::ThrowNonCallableInternal(ExtendedAssembler *assembler, Register glueReg)
866 {
867 __ Pushq(rbp);
868 __ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME)); // set frame type
869 __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
870 __ Movq(MessageString::Message_NonCallable, rax);
871 __ Movabs(JSTaggedValue::TAG_INT, r10);
872 __ Orq(r10, rax);
873 __ Pushq(rax); // message id
874 __ Pushq(1); // argc
875 __ Pushq(RTSTUB_ID(ThrowTypeError)); // runtime id
876 __ Movq(glueReg, rax); // glue
877 __ Movq(kungfu::RuntimeStubCSigns::ID_CallRuntime, r10);
878 __ Movq(Operand(rax, r10, Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r10);
879 __ Callq(r10); // call CallRuntime
880 __ Movabs(JSTaggedValue::VALUE_EXCEPTION, rax); // return exception
881 __ Addq(4 * FRAME_SLOT_SIZE, rsp); // 4: sp + 32 argv
882 __ Pop(rbp);
883 __ Ret();
884 }
885
JSBoundFunctionCallInternal(ExtendedAssembler * assembler,Register jsFuncReg,Label * jsCall)886 void OptimizedCall::JSBoundFunctionCallInternal(ExtendedAssembler *assembler, Register jsFuncReg, Label *jsCall)
887 {
888 Label lAlign16Bytes2;
889 Label lCopyBoundArgument;
890 Label lCopyArgument2;
891 Label lPushCallTarget;
892 Label lCopyBoundArgumentLoop;
893 Label lPopFrame2;
894 Label slowCall;
895 Label aotCall;
896 Label popArgs;
897 Label isJsFunc;
898 Label isNotClass;
899 __ Pushq(rbp);
900 __ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME));
901 __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
902 __ Pushq(r10); // callee save
903 __ Movq(rsp, rdx);
904 __ Addq(QUADRUPLE_SLOT_SIZE, rdx); // skip return addr, prevFp, frame type and callee save
905 __ Mov(Operand(rdx, 0), rax); // get origin argc
906 __ Mov(Operand(rdx, FRAME_SLOT_SIZE), r9); // get origin argv
907 __ Movq(rax, r10);
908 // get bound target
909 __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_ARGUMENTS_OFFSET), rcx);
910 // get bound length
911 __ Mov(Operand(rcx, TaggedArray::LENGTH_OFFSET), rcx);
912 __ Addq(rcx, r10);
913
914 // 16 bytes align check
915 __ Testb(1, r10);
916 __ Je(&lAlign16Bytes2);
917 __ PushAlignBytes(); // push zero to align 16 bytes stack
918
919 __ Bind(&lAlign16Bytes2);
920 {
921 __ Subq(NUM_MANDATORY_JSFUNC_ARGS, rax);
922 __ Cmp(0, rax);
923 __ Je(&lCopyBoundArgument);
924 }
925
926 __ Bind(&lCopyArgument2);
927 {
928 __ Movq(Operand(rdx, rax, Scale::Times8,
929 (kungfu::ArgumentAccessor::GetFixArgsNum() + 1) * FRAME_SLOT_SIZE), rcx); // argv
930 __ Pushq(rcx);
931 __ Addq(-1, rax);
932 __ Jne(&lCopyArgument2);
933 }
934
935 __ Bind(&lCopyBoundArgument);
936 {
937 // get bound target
938 __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_ARGUMENTS_OFFSET), rdx);
939 // get bound length
940 __ Mov(Operand(rdx, TaggedArray::LENGTH_OFFSET), rax);
941 __ Addq(TaggedArray::DATA_OFFSET, rdx);
942 __ Cmp(0, rax);
943 __ Je(&lPushCallTarget);
944 }
945 __ Bind(&lCopyBoundArgumentLoop);
946 {
947 __ Addq(-1, rax);
948 __ Movq(Operand(rdx, rax, Scale::Times8, 0), rcx);
949 __ Pushq(rcx);
950 __ Jne(&lCopyBoundArgumentLoop);
951 }
952 __ Bind(&lPushCallTarget);
953 {
954 __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_THIS_OFFSET), r8); // thisObj
955 __ Pushq(r8);
956 __ Pushq(JSTaggedValue::VALUE_UNDEFINED); // newTarget
957 __ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_TARGET_OFFSET), rax); // callTarget
958 __ Pushq(rax);
959 __ Pushq(r9);
960 __ Pushq(r10); // push actual arguments
961 }
962 JSCallCheck(assembler, rax, &slowCall, &slowCall, &isJsFunc); // jsfunc -> rsi hclassfiled -> rax
963 __ Jmp(&slowCall);
964 Register jsfunc = rsi;
965 Register compiledCodeFlag = rcx;
966 __ Bind(&isJsFunc);
967 {
968 __ Btq(JSHClass::IsClassConstructorOrPrototypeBit::START_BIT, rax); // is CallConstructor
969 __ Jnb(&isNotClass);
970 __ Btq(JSHClass::ConstructorBit::START_BIT, rax);
971 __ Jb(&slowCall);
972 __ Bind(&isNotClass);
973 __ Movzwq(Operand(rsi, JSFunctionBase::BIT_FIELD_OFFSET), compiledCodeFlag); // compiled code flag
974 __ Btq(JSFunctionBase::IsCompiledCodeBit::START_BIT, compiledCodeFlag); // is compiled code
975 __ Jnb(&slowCall);
976 __ Bind(&aotCall);
977 {
978 // output: glue:rdi argc:rsi calltarget:rdx argv:rcx this:r8 newtarget:r9
979 __ Movq(jsfunc, rdx);
980 __ Movq(r10, rsi);
981 auto funcSlotOffSet = kungfu::ArgumentAccessor::GetFixArgsNum() +
982 kungfu::ArgumentAccessor::GetExtraArgsNum();
983 __ Leaq(Operand(rsp, funcSlotOffSet * FRAME_SLOT_SIZE), rcx); // 5: skip argc and argv func new this
984 __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9);
985 __ Movq(kungfu::CommonStubCSigns::JsBoundCallInternal, r10);
986 __ Movq(Operand(rdi, r10, Scale::Times8, JSThread::GlueData::GetCOStubEntriesOffset(false)), rax);
987 __ Callq(rax); // call JSCall
988 __ Jmp(&popArgs);
989 }
990 }
991 __ Bind(&slowCall);
992 {
993 __ Movq(rdi, rax);
994 __ Callq(jsCall); // call JSCall
995 __ Jmp(&popArgs);
996 }
997 __ Bind(&popArgs);
998 {
999 __ Pop(r10);
1000 __ Pop(r9);
1001 __ Leaq(Operand(r10, Scale::Times8, 0), rcx); // 8: disp
1002 __ Addq(rcx, rsp);
1003 __ Testb(1, r10); // stack 16bytes align check
1004 __ Je(&lPopFrame2);
1005 __ Addq(8, rsp); // 8: align byte
1006 }
1007 __ Bind(&lPopFrame2);
1008 {
1009 __ Pop(r10);
1010 __ Addq(8, rsp); // 8: sp + 8
1011 __ Pop(rbp);
1012 __ Ret();
1013 }
1014 }
1015
1016 // * uint64_t CallRuntime(uintptr_t glue, uint64_t runtime_id, uint64_t argc, uintptr_t arg0, ...)
1017 // * webkit_jscc calling convention call runtime_id's runtime function(c-abi)
1018 // * Arguments:
1019 // %rax - glue
1020 //
1021 // * Optimized-leaved-frame layout as the following:
1022 // +--------------------------+
1023 // | argv[N-1] |
1024 // |--------------------------|
1025 // | . . . . . |
1026 // |--------------------------|
1027 // | argv[0] |
1028 // +--------------------------+-------------
1029 // | argc | ^
1030 // |--------------------------| |
1031 // | RuntimeId | |
1032 // sp --> |--------------------------| OptimizedLeaveFrame
1033 // | ret-addr | |
1034 // |--------------------------| |
1035 // | prevFp | |
1036 // |--------------------------| |
1037 // | frameType | v
1038 // +--------------------------+-------------
1039
CallRuntime(ExtendedAssembler * assembler)1040 void OptimizedCall::CallRuntime(ExtendedAssembler *assembler)
1041 {
1042 __ BindAssemblerStub(RTSTUB_ID(CallRuntime));
1043 __ Pushq(rbp);
1044 __ Movq(rsp, Operand(rax, JSThread::GlueData::GetLeaveFrameOffset(false)));
1045 __ Pushq(static_cast<int32_t>(FrameType::LEAVE_FRAME));
1046 __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp); // 8: skip frame type
1047
1048 __ Pushq(r10);
1049 __ Pushq(rdx);
1050 __ Pushq(rax);
1051
1052 __ Movq(rbp, rdx);
1053 // 2: rbp & return address
1054 __ Addq(2 * FRAME_SLOT_SIZE, rdx);
1055
1056 __ Movq(Operand(rdx, 0), r10);
1057 __ Movq(Operand(rax, r10, Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r10);
1058 __ Movq(rax, rdi);
1059 // 8: argc
1060 __ Movq(Operand(rdx, FRAME_SLOT_SIZE), rsi);
1061 // 2: argv
1062 __ Addq(2 * FRAME_SLOT_SIZE, rdx);
1063 __ Callq(r10);
1064
1065 // 8: skip rax
1066 __ Addq(FRAME_SLOT_SIZE, rsp);
1067 __ Popq(rdx);
1068 __ Popq(r10);
1069
1070 // 8: skip frame type
1071 __ Addq(FRAME_SLOT_SIZE, rsp);
1072 __ Popq(rbp);
1073 __ Ret();
1074 }
1075
1076 // * uint64_t CallRuntimeWithArgv(uintptr_t glue, uint64_t runtime_id, uint64_t argc, uintptr_t argv)
1077 // * cc calling convention call runtime_id's runtion function(c-abi)
1078 // * Arguments:
1079 // %rdi - glue
1080 // %rsi - runtime_id
1081 // %edx - argc
1082 // %rcx - argv
1083 //
1084 // * Optimized-leaved-frame-with-argv layout as the following:
1085 // +--------------------------+
1086 // | argv[] |
1087 // +--------------------------+-------------
1088 // | argc | ^
1089 // |--------------------------| |
1090 // | RuntimeId | OptimizedWithArgvLeaveFrame
1091 // sp --> |--------------------------| |
1092 // | returnAddr | |
1093 // |--------------------------| |
1094 // | callsiteFp | |
1095 // |--------------------------| |
1096 // | frameType | v
1097 // +--------------------------+-------------
1098
CallRuntimeWithArgv(ExtendedAssembler * assembler)1099 void OptimizedCall::CallRuntimeWithArgv(ExtendedAssembler *assembler)
1100 {
1101 __ BindAssemblerStub(RTSTUB_ID(CallRuntimeWithArgv));
1102 Register glueReg = rdi;
1103 Register runtimeIdReg = rsi;
1104 Register argcReg = rdx;
1105 Register argvReg = rcx;
1106
1107 __ Movq(rsp, r8);
1108 Register returnAddrReg = r9;
1109 __ Movq(Operand(rsp, 0), returnAddrReg);
1110 __ Pushq(argvReg); // argv[]
1111 __ Pushq(argcReg); // argc
1112 __ Pushq(runtimeIdReg); // runtime_id
1113 __ Pushq(returnAddrReg); // returnAddr
1114
1115 // construct leave frame
1116 __ Pushq(rbp);
1117 __ Movq(rsp, Operand(glueReg, JSThread::GlueData::GetLeaveFrameOffset(false))); // save to thread->leaveFrame_
1118 __ Pushq(static_cast<int32_t>(FrameType::LEAVE_FRAME_WITH_ARGV));
1119 __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
1120
1121 __ Movq(Operand(glueReg, runtimeIdReg, Scale::Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r9);
1122 __ Movq(argcReg, rsi); // argc
1123 __ Movq(argvReg, rdx); // argv
1124 __ Pushq(r8);
1125 __ Callq(r9);
1126 __ Popq(r8);
1127 __ Addq(FRAME_SLOT_SIZE, rsp); // 8: skip type
1128 __ Popq(rbp);
1129 __ Movq(r8, rsp);
1130 __ Ret();
1131 }
1132
PushMandatoryJSArgs(ExtendedAssembler * assembler,Register jsfunc,Register thisObj,Register newTarget)1133 void OptimizedCall::PushMandatoryJSArgs(ExtendedAssembler *assembler, Register jsfunc,
1134 Register thisObj, Register newTarget)
1135 {
1136 __ Pushq(thisObj);
1137 __ Pushq(newTarget);
1138 __ Pushq(jsfunc);
1139 }
1140
1141 // output expectedNumArgs (r14)
PushArgsWithArgV(ExtendedAssembler * assembler,Register jsfunc,Register actualNumArgs,Register argV,Label * pushCallThis)1142 void OptimizedCall::PushArgsWithArgV(ExtendedAssembler *assembler, Register jsfunc,
1143 Register actualNumArgs, Register argV, Label *pushCallThis)
1144 {
1145 Register expectedNumArgs(r14); // output
1146 Register tmp(rax);
1147 Label align16Bytes;
1148 Label copyArguments;
1149 // get expected num Args
1150 __ Movq(Operand(jsfunc, JSFunctionBase::METHOD_OFFSET), tmp);
1151 __ Movq(Operand(tmp, Method::CALL_FIELD_OFFSET), tmp);
1152 __ Shr(MethodLiteral::NumArgsBits::START_BIT, tmp);
1153 __ Andl(((1LU << MethodLiteral::NumArgsBits::SIZE) - 1), tmp);
1154
1155 __ Mov(tmp, expectedNumArgs);
1156 __ Testb(1, expectedNumArgs);
1157 __ Jne(&align16Bytes);
1158 __ PushAlignBytes();
1159
1160 __ Bind(&align16Bytes);
1161 {
1162 __ Cmpq(actualNumArgs, expectedNumArgs);
1163 __ Jbe(©Arguments);
1164 __ Subq(actualNumArgs, tmp);
1165 PushUndefinedWithArgc(assembler, tmp);
1166 }
1167 __ Bind(©Arguments);
1168 {
1169 __ Cmpq(actualNumArgs, expectedNumArgs);
1170 __ Movq(actualNumArgs, tmp); // rax -> actualNumArgsReg
1171 __ CMovbe(expectedNumArgs, tmp);
1172 __ Cmpq(0, tmp);
1173 __ Je(pushCallThis);
1174 CopyArgumentWithArgV(assembler, tmp, argV);
1175 }
1176 }
1177
PopJSFunctionArgs(ExtendedAssembler * assembler,Register expectedNumArgs)1178 void OptimizedCall::PopJSFunctionArgs(ExtendedAssembler *assembler, Register expectedNumArgs)
1179 {
1180 Label align16Bytes;
1181 __ Testb(1, expectedNumArgs);
1182 __ Je(&align16Bytes);
1183 __ Addq(FRAME_SLOT_SIZE, rsp);
1184 __ Bind(&align16Bytes);
1185 __ Leaq(Operand(expectedNumArgs, Scale::Times8, 0), expectedNumArgs);
1186 __ Addq(DOUBLE_SLOT_SIZE, rsp); // 16: skip actual argc and actual argv
1187 __ Addq(expectedNumArgs, rsp);
1188 }
1189
PushJSFunctionEntryFrame(ExtendedAssembler * assembler,Register prevFp)1190 void OptimizedCall::PushJSFunctionEntryFrame(ExtendedAssembler *assembler, Register prevFp)
1191 {
1192 __ PushCppCalleeSaveRegisters();
1193 __ Pushq(rdi);
1194
1195 // construct optimized entry frame
1196 __ Pushq(rbp);
1197 __ Pushq(static_cast<int64_t>(FrameType::OPTIMIZED_ENTRY_FRAME));
1198 __ Pushq(prevFp);
1199 // 2: skip prevFp and frameType
1200 __ Leaq(Operand(rsp, 2 * FRAME_SLOT_SIZE), rbp);
1201 }
1202
PopJSFunctionEntryFrame(ExtendedAssembler * assembler,Register glue)1203 void OptimizedCall::PopJSFunctionEntryFrame(ExtendedAssembler *assembler, Register glue)
1204 {
1205 Register prevFp(rsi);
1206 __ Popq(prevFp);
1207 __ Addq(FRAME_SLOT_SIZE, rsp); // 8: frame type
1208 __ Popq(rbp);
1209 __ Popq(glue); // caller restore
1210 __ PopCppCalleeSaveRegisters(); // callee restore
1211 __ Movq(prevFp, Operand(glue, JSThread::GlueData::GetLeaveFrameOffset(false)));
1212 }
1213
1214 // * uint64_t PushOptimizedUnfoldArgVFrame(uintptr_t glue, uint32_t argc, JSTaggedType calltarget,
1215 // JSTaggedType new, JSTaggedType this, JSTaggedType, argV[])
1216 // * cc calling convention call js function()
1217 // * arguments:
1218 // %rdi - glue
1219 // %rsi - argc
1220 // %rdx - call-target
1221 // %rcx - new-target
1222 // %r8 - this
1223 // %r9 - argv
1224 //
1225 // * OptimizedUnfoldArgVFrame layout description as the following:
1226 // sp ----> |--------------------------| ---------------
1227 // | returnAddr | ^
1228 // currentFp--> |--------------------------| |
1229 // | prevFp | |
1230 // |--------------------------| OptimizedUnfoldArgVFrame
1231 // | frameType | |
1232 // |--------------------------| |
1233 // | currentFp | v
1234 // +--------------------------+ ---------------
1235
PushOptimizedUnfoldArgVFrame(ExtendedAssembler * assembler,Register callSiteSp)1236 void OptimizedCall::PushOptimizedUnfoldArgVFrame(ExtendedAssembler *assembler, Register callSiteSp)
1237 {
1238 __ Pushq(rbp);
1239 // construct frame
1240 __ Pushq(static_cast<int64_t>(FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME));
1241 __ Pushq(callSiteSp);
1242 // 2: skip callSiteSp and frameType
1243 __ Leaq(Operand(rsp, 2 * FRAME_SLOT_SIZE), rbp);
1244 }
1245
PopOptimizedUnfoldArgVFrame(ExtendedAssembler * assembler)1246 void OptimizedCall::PopOptimizedUnfoldArgVFrame(ExtendedAssembler *assembler)
1247 {
1248 Register sp(rsp);
1249 // 2 : 2 means pop call site sp and type
1250 __ Addq(Immediate(2 * FRAME_SLOT_SIZE), sp);
1251 __ Popq(rbp);
1252 }
1253
1254 // * uint64_t JSCallWithArgV(uintptr_t glue, uint32_t argc, JSTaggedType calltarget,
1255 // JSTaggedType new, JSTaggedType this, argV)
1256 // * cc calling convention call js function()
1257 // * arguments:
1258 // %rdi - glue
1259 // %rsi - argc
1260 // %rdx - call-target
1261 // %rcx - new-target
1262 // %r8 - this
1263 // %r9 - argv
1264 //
1265 // * OptimizedJSFunctionFrame layout description as the following:
1266 // +--------------------------+
1267 // | argn |
1268 // |--------------------------|
1269 // | argn - 1 |
1270 // |--------------------------|
1271 // | ..... |
1272 // |--------------------------|
1273 // | arg2 |
1274 // |--------------------------|
1275 // | arg1 |
1276 // sp ----> |--------------------------| ---------------
1277 // | returnAddr | ^
1278 // |--------------------------| |
1279 // | callsiteFp | |
1280 // |--------------------------| OptimizedJSFunctionFrame
1281 // | frameType | |
1282 // |--------------------------| |
1283 // | call-target | v
1284 // +--------------------------+ ---------------
1285
GenJSCallWithArgV(ExtendedAssembler * assembler,int id)1286 void OptimizedCall::GenJSCallWithArgV(ExtendedAssembler *assembler, int id)
1287 {
1288 Register sp(rsp);
1289 Register glue(rdi);
1290 Register actualNumArgs(rsi);
1291 Register jsfunc(rdx);
1292 Register newTarget(rcx);
1293 Register thisObj(r8);
1294 Register argV(r9);
1295 Register callsiteSp = __ AvailableRegister2();
1296 Label align16Bytes;
1297 Label pushCallThis;
1298
1299 __ Movq(sp, callsiteSp);
1300 __ Addq(Immediate(FRAME_SLOT_SIZE), callsiteSp); // 8 : 8 means skip pc to get last callsitesp
1301 PushOptimizedUnfoldArgVFrame(assembler, callsiteSp);
1302 __ Testb(1, actualNumArgs);
1303 __ Jne(&align16Bytes);
1304 __ PushAlignBytes();
1305 __ Bind(&align16Bytes);
1306 __ Cmp(Immediate(0), actualNumArgs);
1307 __ Jz(&pushCallThis);
1308 __ Mov(actualNumArgs, rax);
1309 CopyArgumentWithArgV(assembler, rax, argV);
1310 __ Bind(&pushCallThis);
1311 PushMandatoryJSArgs(assembler, jsfunc, thisObj, newTarget);
1312 __ Addq(Immediate(NUM_MANDATORY_JSFUNC_ARGS), actualNumArgs);
1313 __ Pushq(sp);
1314 __ Pushq(actualNumArgs);
1315 __ Movq(glue, rax);
1316 __ CallAssemblerStub(id, false);
1317 __ Mov(Operand(sp, 0), actualNumArgs);
1318 PopJSFunctionArgs(assembler, actualNumArgs);
1319 PopOptimizedUnfoldArgVFrame(assembler);
1320 __ Ret();
1321 }
1322
1323 // * uint64_t JSCallWithArgVAndPushArgv(uintptr_t glue, uint32_t argc, JSTaggedType calltarget,
1324 // JSTaggedType new, JSTaggedType this, argV)
1325 // * cc calling convention call js function()
1326 // * arguments:
1327 // %rdi - glue
1328 // %rsi - argc
1329 // %rdx - call-target
1330 // %rcx - new-target
1331 // %r8 - this
1332 // %r9 - argv
JSCallWithArgVAndPushArgv(ExtendedAssembler * assembler)1333 void OptimizedCall::JSCallWithArgVAndPushArgv(ExtendedAssembler *assembler)
1334 {
1335 __ BindAssemblerStub(RTSTUB_ID(JSCallWithArgVAndPushArgv));
1336 GenJSCallWithArgV(assembler, RTSTUB_ID(OptimizedCallAndPushArgv));
1337 }
1338
JSCallWithArgV(ExtendedAssembler * assembler)1339 void OptimizedCall::JSCallWithArgV(ExtendedAssembler *assembler)
1340 {
1341 __ BindAssemblerStub(RTSTUB_ID(JSCallWithArgV));
1342 GenJSCallWithArgV(assembler, RTSTUB_ID(CallOptimized));
1343 }
1344
SuperCallWithArgV(ExtendedAssembler * assembler)1345 void OptimizedCall::SuperCallWithArgV(ExtendedAssembler *assembler)
1346 {
1347 __ BindAssemblerStub(RTSTUB_ID(SuperCallWithArgV));
1348 GenJSCallWithArgV(assembler, RTSTUB_ID(JSCallNew));
1349 }
1350
CallOptimized(ExtendedAssembler * assembler)1351 void OptimizedCall::CallOptimized(ExtendedAssembler *assembler)
1352 {
1353 __ BindAssemblerStub(RTSTUB_ID(CallOptimized));
1354 Register jsFuncReg = rdi;
1355 Register method = r9;
1356 Register codeAddrReg = rsi;
1357 auto funcSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1; // 1: return addr
1358 __ Movq(Operand(rsp, funcSlotOffset * FRAME_SLOT_SIZE), jsFuncReg); // sp + 24 get jsFunc
1359 __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method
1360 __ Mov(Operand(jsFuncReg, JSFunctionBase::CODE_ENTRY_OFFSET), codeAddrReg);
1361 __ Jmp(codeAddrReg);
1362 }
1363
1364 // Input: %rdi - glue
1365 // %rsi - context
DeoptEnterAsmInterp(ExtendedAssembler * assembler)1366 void OptimizedCall::DeoptEnterAsmInterp(ExtendedAssembler *assembler)
1367 {
1368 // rdi
1369 Register glueRegister = __ GlueRegister();
1370 Register context = rsi;
1371 // rax, rdx, rcx, r8, r9, r10, r11 is free
1372 Register tempRegister = rax;
1373 Register opRegister = r10;
1374 Register outputCount = rdx;
1375 Register frameStateBase = rcx;
1376 Register depth = r11;
1377 Label loopBegin;
1378 Label stackOverflow;
1379 Label pushArgv;
1380 __ Movq(Operand(context, AsmStackContext::GetInlineDepthOffset(false)), depth);
1381 __ Leaq(Operand(context, AsmStackContext::GetSize(false)), context);
1382 __ Movq(Immediate(0), r12);
1383 __ Bind(&loopBegin);
1384 __ Movq(Operand(context, 0), outputCount);
1385 __ Leaq(Operand(context, FRAME_SLOT_SIZE), frameStateBase);
1386 __ Cmpq(0, r12);
1387 __ Je(&pushArgv);
1388 __ Movq(rsp, r8);
1389 __ Addq(AsmInterpretedFrame::GetSize(false), r8);
1390 __ Leaq(Operand(frameStateBase, AsmInterpretedFrame::GetBaseOffset(false)), r10);
1391 __ Movq(r8, Operand(r10, InterpretedFrameBase::GetPrevOffset(false)));
1392 __ Testq(15, rsp); // 15: low 4 bits must be 0b0000
1393 __ Jnz(&pushArgv);
1394 __ PushAlignBytes();
1395 __ Bind(&pushArgv);
1396 // update fp
1397 __ Movq(rsp, Operand(frameStateBase, AsmInterpretedFrame::GetFpOffset(false)));
1398 PushArgsWithArgvAndCheckStack(assembler, glueRegister, outputCount,
1399 frameStateBase, tempRegister, opRegister, &stackOverflow);
1400 __ Leaq(Operand(context, outputCount, Scale::Times8, FRAME_SLOT_SIZE), context);
1401 __ Addq(1, r12);
1402 __ Cmpq(r12, depth);
1403 __ Jae(&loopBegin);
1404 Register callTargetRegister = r8;
1405 Register methodRegister = r9;
1406 {
1407 // r13, rbp, r12, rbx, r14, rsi, rdi
1408 // glue sp pc constpool profile acc hotness
1409 __ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetFunctionOffset(false)), callTargetRegister);
1410 __ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetPcOffset(false)), r12);
1411 __ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetAccOffset(false)), rsi);
1412 __ Movq(Operand(callTargetRegister, JSFunctionBase::METHOD_OFFSET), methodRegister);
1413
1414 __ Leaq(Operand(rsp, AsmInterpretedFrame::GetSize(false)), opRegister);
1415 AsmInterpreterCall::DispatchCall(assembler, r12, opRegister, callTargetRegister, methodRegister, rsi);
1416 }
1417
1418 __ Bind(&stackOverflow);
1419 {
1420 [[maybe_unused]] TempRegisterScope scope(assembler);
1421 Register temp = __ TempRegister();
1422 AsmInterpreterCall::ThrowStackOverflowExceptionAndReturn(assembler,
1423 glueRegister, rsp, temp);
1424 }
1425 }
1426
1427 // Input: %rdi - glue
DeoptHandlerAsm(ExtendedAssembler * assembler)1428 void OptimizedCall::DeoptHandlerAsm(ExtendedAssembler *assembler)
1429 {
1430 __ BindAssemblerStub(RTSTUB_ID(DeoptHandlerAsm));
1431
1432 Register glueReg = rdi;
1433 PushAsmBridgeFrame(assembler);
1434 __ Push(glueReg);
1435 __ PushCppCalleeSaveRegisters();
1436
1437 __ Movq(rdi, rax); // glue
1438 Register deoptType = rsi;
1439 Register depth = rdx;
1440 __ Subq(FRAME_SLOT_SIZE, rsp);
1441 __ Pushq(depth);
1442 __ Pushq(deoptType); // argv[0]
1443 __ Pushq(2); // 2: argc
1444 __ Pushq(kungfu::RuntimeStubCSigns::ID_DeoptHandler);
1445 __ CallAssemblerStub(RTSTUB_ID(CallRuntime), false);
1446
1447 __ Addq(5 * FRAME_SLOT_SIZE, rsp); // 5: skip runtimeId argc deoptType depth align
1448
1449 Register context = rsi;
1450 __ Movq(rax, context);
1451
1452 Label target;
1453 __ PopCppCalleeSaveRegisters();
1454 __ Pop(glueReg);
1455
1456 Label stackOverflow;
1457 __ Cmpq(JSTaggedValue::VALUE_EXCEPTION, rax);
1458 __ Je(&stackOverflow);
1459
1460 __ Movq(Operand(context, AsmStackContext::GetCallerFpOffset(false)), rbp);
1461 __ Movq(Operand(context, AsmStackContext::GetCallFrameTopOffset(false)), rsp);
1462 __ Subq(FRAME_SLOT_SIZE, rsp); // skip lr
1463
1464 PushAsmInterpBridgeFrame(assembler);
1465 __ Callq(&target);
1466 PopAsmInterpBridgeFrame(assembler);
1467 __ Ret();
1468 __ Bind(&target);
1469 DeoptEnterAsmInterp(assembler);
1470 __ Int3();
1471
1472 __ Bind(&stackOverflow);
1473 {
1474 __ Movq(rdi, rax);
1475 __ Pushq(0); // argc
1476 __ Pushq(kungfu::RuntimeStubCSigns::ID_ThrowStackOverflowException);
1477 __ CallAssemblerStub(RTSTUB_ID(CallRuntime), false);
1478 __ Addq(FRAME_SLOT_SIZE * 3, rsp); // 3 : skip runtimeId argc & type
1479 __ Popq(rbp);
1480 __ Ret();
1481 }
1482 }
1483 #undef __
1484 } // namespace panda::ecmascript::x64
1485