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