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