* Copyright (c) 2022-2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecmascript/compiler/assembler/x64/assembler_x64.h"
#include "ecmascript/compiler/trampoline/x64/common_call.h"
#include "ecmascript/deoptimizer/deoptimizer.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/message_string.h"
namespace panda::ecmascript::x64 {
#define __ assembler->
void OptimizedCall::JSFunctionEntry(ExtendedAssembler *assembler)
{
__ BindAssemblerStub(RTSTUB_ID(JSFunctionEntry));
Register glueReg = rdi;
Register argv = rdx;
Register prevFpReg = rcx;
Register needPushArgv = r8;
Label lJSCallWithArgVAndPushArgv;
Label lPopFrame;
PushJSFunctionEntryFrame(assembler, prevFpReg);
__ UpdateReadBarrier(glueReg);
__ Movq(argv, rbx);
__ Movq(needPushArgv, r12);
__ Movq(Operand(rbx, 0), rdx);
__ Movq(Operand(rbx, FRAME_SLOT_SIZE), rcx);
__ Movq(Operand(rbx, DOUBLE_SLOT_SIZE), r8);
__ Addq(TRIPLE_SLOT_SIZE, rbx);
__ Movq(rbx, r9);
__ Cmp(1, r12);
__ Je(&lJSCallWithArgVAndPushArgv);
__ CallAssemblerStub(RTSTUB_ID(JSCallWithArgV), false);
__ Jmp(&lPopFrame);
__ Bind(&lJSCallWithArgVAndPushArgv);
__ CallAssemblerStub(RTSTUB_ID(JSCallWithArgVAndPushArgv), false);
__ Bind(&lPopFrame);
__ Popq(prevFpReg);
__ Addq(FRAME_SLOT_SIZE, rsp);
__ Popq(rbp);
__ Popq(glueReg);
__ PopCppCalleeSaveRegisters();
__ Movq(prevFpReg, Operand(glueReg, JSThread::GlueData::GetLeaveFrameOffset(false)));
__ Ret();
}
void OptimizedCall::OptimizedCallAndPushArgv(ExtendedAssembler *assembler)
{
__ BindAssemblerStub(RTSTUB_ID(OptimizedCallAndPushArgv));
Register jsFuncReg = rdi;
Register method = r9;
Register codeAddrReg = rsi;
auto funcSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1;
__ Movq(Operand(rsp, funcSlotOffset * FRAME_SLOT_SIZE), jsFuncReg);
__ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method);
__ Mov(Operand(jsFuncReg, JSFunctionBase::CODE_ENTRY_OFFSET), codeAddrReg);
Register methodCallField = rcx;
__ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField);
__ Shr(Method::NumArgsBits::START_BIT, methodCallField);
__ Andl(((1LU << Method::NumArgsBits::SIZE) - 1), methodCallField);
__ Addl(NUM_MANDATORY_JSFUNC_ARGS, methodCallField);
__ Movl(Operand(rsp, FRAME_SLOT_SIZE), rdx);
__ Movq(rsp, r8);
Register argvReg = r8;
__ Addq(funcSlotOffset * FRAME_SLOT_SIZE, argvReg);
Register expectedNumArgsReg = rcx;
Register actualNumArgsReg = rdx;
__ Pushq(rbp);
__ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME));
__ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
__ Pushq(r14);
__ Pushq(rbx);
__ Pushq(rax);
Label lCopyExtraAument1;
Label lCopyLoop1;
Label lCopyLoop2;
Label lPopFrame1;
Label pushUndefined;
Label commonCall;
__ Cmpq(expectedNumArgsReg, actualNumArgsReg);
__ Jb(&pushUndefined);
__ Movl(actualNumArgsReg, r14);
__ Testb(1, r14);
__ Je(&lCopyLoop2);
__ Pushq(0);
__ Bind(&lCopyLoop2);
__ Movq(Operand(argvReg, r14, Scale::Times8, -FRAME_SLOT_SIZE), rbx);
__ Pushq(rbx);
__ Addq(-1, r14);
__ Jne(&lCopyLoop2);
__ Movl(actualNumArgsReg, r14);
__ Jmp(&commonCall);
__ Bind(&pushUndefined);
__ Movl(expectedNumArgsReg, r14);
__ Testb(1, r14);
__ Je(&lCopyExtraAument1);
__ Pushq(0);
__ Bind(&lCopyExtraAument1);
__ Pushq(JSTaggedValue::VALUE_UNDEFINED);
__ Addq(-1, expectedNumArgsReg);
__ Cmpq(actualNumArgsReg, expectedNumArgsReg);
__ Ja(&lCopyExtraAument1);
__ Bind(&lCopyLoop1);
__ Movq(Operand(argvReg, expectedNumArgsReg, Scale::Times8, -FRAME_SLOT_SIZE), rbx);
__ Pushq(rbx);
__ Addq(-1, expectedNumArgsReg);
__ Jne(&lCopyLoop1);
__ Jmp(&commonCall);
__ Bind(&commonCall);
__ Pushq(rsp);
__ Pushq(actualNumArgsReg);
__ Callq(codeAddrReg);
__ Leaq(Operand(r14, Scale::Times8, 0), codeAddrReg);
__ Addq(codeAddrReg, rsp);
__ Addq(FRAME_SLOT_SIZE, rsp);
__ Addq(FRAME_SLOT_SIZE, rsp);
__ Testb(1, r14);
__ Je(&lPopFrame1);
__ Addq(8, rsp);
__ Bind(&lPopFrame1);
__ Addq(8, rsp);
__ Popq(rbx);
__ Popq(r14);
__ Addq(FRAME_SLOT_SIZE, rsp);
__ Pop(rbp);
__ Ret();
}
void OptimizedCall::OptimizedCallAsmInterpreter(ExtendedAssembler *assembler)
{
Label target;
PushAsmInterpBridgeFrame(assembler);
__ Callq(&target);
PopAsmInterpBridgeFrame(assembler);
__ Ret();
__ Bind(&target);
AsmInterpreterCall::JSCallCommonEntry(
assembler, JSCallMode::CALL_FROM_AOT, FrameTransitionType::OTHER_TO_OTHER);
}
void OptimizedCall::RemoveArgv(ExtendedAssembler *assembler, Register temp)
{
__ Movq(Operand(rsp, FRAME_SLOT_SIZE), temp);
__ Movq(temp, Operand(rsp, DOUBLE_SLOT_SIZE));
__ Movq(Operand(rsp, 0), temp);
__ Movq(temp, Operand(rsp, FRAME_SLOT_SIZE));
__ Addq(FRAME_SLOT_SIZE, rsp);
}
void OptimizedCall::CallBuiltinTrampoline(ExtendedAssembler *assembler, Register temp)
{
Register glueReg = rax;
Register nativeCode = rsi;
RemoveArgv(assembler, temp);
__ Movq(Operand(rsp, 0), rdx);
__ Movq(glueReg, Operand(rsp, 0));
__ Push(rdx);
AsmInterpreterCall::PushBuiltinFrame(assembler, glueReg, FrameType::BUILTIN_CALL_LEAVE_FRAME);
__ Leaq(Operand(rbp, DOUBLE_SLOT_SIZE), rdi);
__ PushAlignBytes();
#ifdef ENABLE_CMC_IR_FIX_REGISTER
Register calleeSaveGlue = r15;
__ Movq(glueReg, calleeSaveGlue);
#endif
AsmInterpreterCall::CallNativeInternal(assembler, nativeCode);
__ Movq(Operand(rsp, DOUBLE_SLOT_SIZE), temp);
__ Movq(Immediate(0), Operand(rsp, DOUBLE_SLOT_SIZE));
__ Movq(temp, Operand(rsp, FRAME_SLOT_SIZE));
__ Ret();
}
void OptimizedCall::CallBuiltinConstructorStub(ExtendedAssembler *assembler, Register builtinStub, Register argv,
Register glue, Register temp)
{
RemoveArgv(assembler, temp);
__ Movq(Operand(rsp, 0), temp);
__ Movq(glue, Operand(rsp, 0));
__ Push(temp);
AsmInterpreterCall::PushBuiltinFrame(assembler, glue, FrameType::BUILTIN_CALL_LEAVE_FRAME);
__ Push(argv);
__ Callq(builtinStub);
__ Movq(rbp, rsp);
__ Pop(rbp);
__ Movq(Operand(rsp, DOUBLE_SLOT_SIZE), temp);
__ Movq(Immediate(0), Operand(rsp, DOUBLE_SLOT_SIZE));
__ Movq(temp, Operand(rsp, FRAME_SLOT_SIZE));
__ Ret();
}
void OptimizedCall::JSProxyCallInternalWithArgV(ExtendedAssembler *assembler)
{
__ BindAssemblerStub(RTSTUB_ID(JSProxyCallInternalWithArgV));
Register ccGlueReg = rdi;
Register jsccGlueReg = rax;
Register callTarget = rsi;
__ Movq(ccGlueReg, jsccGlueReg);
auto funcSlotOffSet = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1;
__ Movq(callTarget, Operand(rsp, funcSlotOffSet * FRAME_SLOT_SIZE));
GenJSCall(assembler, false);
}
void OptimizedCall::JSCallNew(ExtendedAssembler *assembler)
{
__ BindAssemblerStub(RTSTUB_ID(JSCallNew));
GenJSCall(assembler, true);
}
void OptimizedCall::JSCall(ExtendedAssembler *assembler)
{
__ BindAssemblerStub(RTSTUB_ID(JSCall));
GenJSCall(assembler, false);
}
void OptimizedCall::GenJSCall(ExtendedAssembler *assembler, bool isNew)
{
Label jsCall;
Label lJSCallStart;
Label lNotJSFunction;
Label lNonCallable;
Label lJSFunctionCall;
Label lJSBoundFunction;
Label lJSProxy;
Label lCallNativeMethod;
Label lCallNativeCpp;
Label lCallNativeBuiltinStub;
Register glueReg = rax;
__ Bind(&jsCall);
{
__ Movq(glueReg, rdi);
glueReg = rdi;
auto funcSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1;
__ Movq(Operand(rsp, funcSlotOffset * FRAME_SLOT_SIZE), rax);
}
__ Bind(&lJSCallStart);
Register jsFuncReg = rax;
{
JSCallCheck(assembler, jsFuncReg, &lNonCallable, &lNotJSFunction, &lJSFunctionCall);
}
__ Bind(&lNotJSFunction);
{
__ Cmpb(static_cast<uint8_t>(JSType::JS_BOUND_FUNCTION), rax);
__ Je(&lJSBoundFunction);
__ Cmpb(static_cast<uint8_t>(JSType::JS_PROXY), rax);
__ Je(&lJSProxy);
}
__ Bind(&lNonCallable);
{
ThrowNonCallableInternal(assembler, glueReg);
}
__ Bind(&lJSFunctionCall);
jsFuncReg = rsi;
Register argc = r8;
Register methodCallField = rcx;
Register method = rdx;
Register argV = r9;
{
Label lCallConstructor;
Label lNotClass;
__ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method);
__ Movl(Operand(rsp, FRAME_SLOT_SIZE), argc);
__ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField);
__ Btq(Method::IsNativeBit::START_BIT, methodCallField);
__ Jb(&lCallNativeMethod);
if (!isNew) {
__ Btq(JSHClass::IsClassConstructorOrPrototypeBit::START_BIT, rax);
__ Jnb(&lNotClass);
__ Btq(JSHClass::ConstructorBit::START_BIT, rax);
__ Jb(&lCallConstructor);
}
__ Bind(&lNotClass);
__ Movq(rsp, argV);
auto argvSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1;
__ Addq(argvSlotOffset * FRAME_SLOT_SIZE, argV);
__ Subq(Immediate(kungfu::ArgumentAccessor::GetFixArgsNum()), argc);
__ Addq(kungfu::ArgumentAccessor::GetFixArgsNum() * FRAME_SLOT_SIZE, argV);
OptimizedCallAsmInterpreter(assembler);
__ Bind(&lCallConstructor);
{
__ Pushq(rbp);
__ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME));
__ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
__ Pushq(0);
__ Pushq(0);
__ Pushq(RTSTUB_ID(ThrowCallConstructorException));
__ Movq(glueReg, rax);
__ Movq(kungfu::RuntimeStubCSigns::ID_CallRuntime, r10);
__ Movq(Operand(rax, r10, Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r10);
__ Callq(r10);
__ Addq(4 * FRAME_SLOT_SIZE, rsp);
__ Pop(rbp);
__ Ret();
}
}
__ Bind(&lCallNativeMethod);
{
Register nativePointer = rsi;
method = rax;
__ Movq(jsFuncReg, rdx);
__ Mov(Operand(rdx, JSFunctionBase::METHOD_OFFSET), method);
__ Mov(Operand(rdx, JSFunctionBase::CODE_ENTRY_OFFSET), nativePointer);
__ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField);
__ Btq(Method::IsFastBuiltinBit::START_BIT, methodCallField);
if (!isNew) {
__ Jnb(&lCallNativeCpp);
__ Cmpl(NUM_MANDATORY_JSFUNC_ARGS + 3, argc);
__ Jbe(&lCallNativeBuiltinStub);
} else {
__ Jb(&lCallNativeBuiltinStub);
}
}
__ Bind(&lCallNativeCpp);
{
__ Movq(glueReg, rax);
CallBuiltinTrampoline(assembler, r11);
}
__ Bind(&lCallNativeBuiltinStub);
{
Register methodExtraLiteralInfo = rax;
__ Mov(Operand(method, Method::EXTRA_LITERAL_INFO_OFFSET), methodExtraLiteralInfo);
__ Shr(Method::BuiltinIdBits::START_BIT, methodExtraLiteralInfo);
__ Andl(((1LU << Method::BuiltinIdBits::SIZE) - 1), methodExtraLiteralInfo);
if (!isNew) {
__ Cmpl(BUILTINS_STUB_ID(BUILTINS_CONSTRUCTOR_STUB_FIRST), methodExtraLiteralInfo);
__ Jnb(&lCallNativeCpp);
}
__ Movq(glueReg, rdi);
__ Movq(methodExtraLiteralInfo, r10);
__ Movq(Operand(glueReg, r10, Times8, JSThread::GlueData::GetBuiltinsStubEntriesOffset(false)), r10);
__ Movq(argc, r9);
__ Movq(Operand(rsp, QUADRUPLE_SLOT_SIZE), rcx);
__ Movq(Operand(rsp, QUINTUPLE_SLOT_SIZE), r8);
__ Subq(NUM_MANDATORY_JSFUNC_ARGS, r9);
Label lCall0;
Label lCall1;
Label lCall2;
Label lCall3;
argV = rax;
__ Movq(rsp, argV);
auto argvSlotOffset = kungfu::ArgumentAccessor::GetFixArgsNum() +
kungfu::ArgumentAccessor::GetExtraArgsNum() + 1;
__ Addq(argvSlotOffset *FRAME_SLOT_SIZE, argV);
if (!isNew) {
PushAsmBridgeFrame(assembler);
__ Cmpl(0, r9);
__ Je(&lCall0);
__ Cmpl(1, r9);
__ Je(&lCall1);
__ Cmpl(2, r9);
__ Je(&lCall2);
__ Cmpl(3, r9);
__ Je(&lCall3);
__ Bind(&lCall0);
{
__ Pushq(JSTaggedValue::VALUE_UNDEFINED);
__ Pushq(JSTaggedValue::VALUE_UNDEFINED);
__ Pushq(JSTaggedValue::VALUE_UNDEFINED);
__ Callq(r10);
__ Addq(QUADRUPLE_SLOT_SIZE, rsp);
__ Pop(rbp);
__ Ret();
}
__ Bind(&lCall1);
{
__ Pushq(JSTaggedValue::VALUE_UNDEFINED);
__ Pushq(JSTaggedValue::VALUE_UNDEFINED);
__ Movq(Operand(argV, 0), r11);
__ Pushq(r11);
__ Callq(r10);
__ Addq(QUADRUPLE_SLOT_SIZE, rsp);
__ Pop(rbp);
__ Ret();
}
__ Bind(&lCall2);
{
__ Pushq(JSTaggedValue::VALUE_UNDEFINED);
__ Movq(Operand(argV, FRAME_SLOT_SIZE), r11);
__ Pushq(r11);
__ Movq(Operand(argV, 0), r11);
__ Pushq(r11);
__ Callq(r10);
__ Addq(QUADRUPLE_SLOT_SIZE, rsp);
__ Pop(rbp);
__ Ret();
}
__ Bind(&lCall3);
{
__ Movq(Operand(argV, DOUBLE_SLOT_SIZE), r11);
__ Pushq(r11);
__ Movq(Operand(argV, FRAME_SLOT_SIZE), r11);
__ Pushq(r11);
__ Movq(Operand(argV, 0), r11);
__ Pushq(r11);
__ Callq(r10);
__ Addq(QUADRUPLE_SLOT_SIZE, rsp);
__ Pop(rbp);
__ Ret();
}
} else {
CallBuiltinConstructorStub(assembler, r10, argV, glueReg, r11);
__ Int3();
}
}
__ Bind(&lJSBoundFunction);
{
JSBoundFunctionCallInternal(assembler, jsFuncReg, &jsCall);
}
__ Bind(&lJSProxy);
{
Register nativePointer = rsi;
__ Mov(Operand(jsFuncReg, JSProxy::METHOD_OFFSET), method);
__ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField);
__ Mov(Operand(rsp, FRAME_SLOT_SIZE), argc);
__ Movq(Operand(method, Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET), nativePointer);
__ Jmp(&lCallNativeCpp);
}
}
void OptimizedCall::AOTCallToAsmInterBridge(ExtendedAssembler *assembler)
{
__ BindAssemblerStub(RTSTUB_ID(AOTCallToAsmInterBridge));
Register glueReg = rdi;
Register jsFuncReg = rsi;
Register method = rdx;
Register methodCallField = rcx;
Register argc = r8;
Register argV = r9;
__ Movq(rax, glueReg);
__ Movq(Operand(rsp, TRIPLE_SLOT_SIZE), jsFuncReg);
__ Movq(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method);
__ Movq(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField);
__ Movl(Operand(rsp, FRAME_SLOT_SIZE), argc);
__ Subq(Immediate(kungfu::ArgumentAccessor::GetFixArgsNum()), argc);
__ Movq(rsp, argV);
auto argvSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1;
__ Addq(argvSlotOffset * FRAME_SLOT_SIZE, argV);
__ Addq(kungfu::ArgumentAccessor::GetFixArgsNum() * FRAME_SLOT_SIZE, argV);
OptimizedCallAsmInterpreter(assembler);
}
void OptimizedCall::FastCallToAsmInterBridge(ExtendedAssembler *assembler)
{
__ BindAssemblerStub(RTSTUB_ID(FastCallToAsmInterBridge));
Register glueReg = rdi;
Register jsFuncReg = rsi;
Register thisReg = rdx;
Register maybeArg0 = rcx;
Register maybeArg1 = r8;
Register maybeArg2 = r9;
PushAsmBridgeFrame(assembler);
Register tempMethod = __ AvailableRegister1();
Register tempCallField = __ AvailableRegister2();
__ Movq(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), tempMethod);
__ Movq(Operand(tempMethod, Method::CALL_FIELD_OFFSET), tempCallField);
Register tempArgc = tempCallField;
__ Shr(Method::NumArgsBits::START_BIT, tempArgc);
__ Andl(((1LU << Method::NumArgsBits::SIZE) - 1), tempArgc);
{
[[maybe_unused]] TempRegisterScope scope(assembler);
Register startSp = __ TempRegister();
__ Movq(rsp, startSp);
Label lCall0;
Label lCall1;
Label lCall2;
Label lCall3;
Label lPushCommonRegs;
__ Cmpl(0, tempArgc);
__ Je(&lCall0);
__ Cmpl(1, tempArgc);
__ Je(&lCall1);
__ Cmpl(2, tempArgc);
__ Je(&lCall2);
__ Cmpl(3, tempArgc);
__ Je(&lCall3);
{
__ Subq(Immediate(3), tempArgc);
__ Addq(Immediate(TRIPLE_SLOT_SIZE), startSp);
CopyArgumentWithArgV(assembler, tempArgc, startSp);
__ Subq(Immediate(TRIPLE_SLOT_SIZE), startSp);
__ Jmp(&lCall3);
}
__ Bind(&lCall0);
{
__ Jmp(&lPushCommonRegs);
}
__ Bind(&lCall1);
{
__ Pushq(maybeArg0);
__ Jmp(&lPushCommonRegs);
}
__ Bind(&lCall2);
{
__ Pushq(maybeArg1);
__ Pushq(maybeArg0);
__ Jmp(&lPushCommonRegs);
}
__ Bind(&lCall3);
{
__ Pushq(maybeArg2);
__ Pushq(maybeArg1);
__ Pushq(maybeArg0);
__ Jmp(&lPushCommonRegs);
}
__ Bind(&lPushCommonRegs);
{
__ Pushq(thisReg);
__ Pushq(JSTaggedValue::VALUE_UNDEFINED);
__ Pushq(jsFuncReg);
}
glueReg = rdi;
jsFuncReg = rsi;
Register method = rdx;
Register methodCallField = rcx;
Register argc = r8;
Register argV = r9;
__ Movq(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method);
__ Movq(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField);
__ Movq(methodCallField, argc);
__ Shr(Method::NumArgsBits::START_BIT, argc);
__ Andl(((1LU << Method::NumArgsBits::SIZE) - 1), argc);
__ Movq(rsp, argV);
__ Addq(Immediate(TRIPLE_SLOT_SIZE), argV);
__ Pushq(startSp);
}
Label target;
PushAsmInterpBridgeFrame(assembler);
__ Callq(&target);
{
PopAsmInterpBridgeFrame(assembler);
Register startSp = __ AvailableRegister1();
__ Popq(startSp);
__ Movq(startSp, rsp);
PopAsmBridgeFrame(assembler);
__ Ret();
}
__ Bind(&target);
AsmInterpreterCall::JSCallCommonEntry(
assembler, JSCallMode::CALL_FROM_AOT, FrameTransitionType::OTHER_TO_OTHER);
}
void OptimizedCall::JSCallCheck(ExtendedAssembler *assembler, Register jsFuncReg,
Label *lNonCallable, Label *lNotJSFunction, Label *lJSFunctionCall)
{
__ Movq(jsFuncReg, rsi);
__ Movabs(JSTaggedValue::TAG_INT, rdx);
__ And(jsFuncReg, rdx);
__ Cmp(0x0, rdx);
__ Jne(lNonCallable);
__ Cmp(0x0, jsFuncReg);
__ Je(lNonCallable);
__ Movabs(JSTaggedValue::TAG_SPECIAL, rdx);
__ And(jsFuncReg, rdx);
__ Cmp(0x0, rdx);
__ Jne(lNonCallable);
__ Movq(Operand(rsi, JSFunction::HCLASS_OFFSET), rax);
Register maskRegister = rdx;
__ Movabs(TaggedObject::GC_STATE_MASK, maskRegister);
__ And(maskRegister, rax);
Register jsHclassReg = rax;
__ Movl(Operand(jsHclassReg, JSHClass::BIT_FIELD_OFFSET), rax);
__ Btl(JSHClass::CallableBit::START_BIT, rax);
__ Jnb(lNonCallable);
__ Cmpb(static_cast<int32_t>(JSType::JS_FUNCTION_FIRST), rax);
__ Jb(lNotJSFunction);
__ Cmpb(static_cast<int32_t>(JSType::JS_FUNCTION_LAST), rax);
__ Jbe(lJSFunctionCall);
}
void OptimizedCall::ThrowNonCallableInternal(ExtendedAssembler *assembler, Register glueReg)
{
__ Pushq(rbp);
__ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME));
__ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
__ Pushq(rsi);
__ Pushq(1);
__ Pushq(RTSTUB_ID(ThrowNotCallableException));
__ Movq(glueReg, rax);
__ Movq(kungfu::RuntimeStubCSigns::ID_CallRuntime, r10);
__ Movq(Operand(rax, r10, Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r10);
__ Callq(r10);
__ Movabs(JSTaggedValue::VALUE_EXCEPTION, rax);
__ Addq(4 * FRAME_SLOT_SIZE, rsp);
__ Pop(rbp);
__ Ret();
}
void OptimizedCall::JSBoundFunctionCallInternal(ExtendedAssembler *assembler, Register jsFuncReg, Label *jsCall)
{
Label lAlign16Bytes2;
Label lCopyBoundArgument;
Label lCopyArgument2;
Label lPushCallTarget;
Label lCopyBoundArgumentLoop;
Label lPopFrame2;
Label slowCall;
Label aotCall;
Label popArgs;
Label isJsFunc;
Label isNotClass;
__ Pushq(rbp);
__ Pushq(static_cast<int32_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME));
__ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
__ Pushq(r10);
__ Movq(rsp, rdx);
__ Addq(QUADRUPLE_SLOT_SIZE, rdx);
__ Mov(Operand(rdx, 0), rax);
__ Mov(Operand(rdx, FRAME_SLOT_SIZE), r9);
__ Movq(rax, r10);
__ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_ARGUMENTS_OFFSET), rcx);
__ Mov(Operand(rcx, TaggedArray::LENGTH_OFFSET), rcx);
__ Addq(rcx, r10);
__ Testb(1, r10);
__ Je(&lAlign16Bytes2);
__ PushAlignBytes();
__ Bind(&lAlign16Bytes2);
{
__ Subq(NUM_MANDATORY_JSFUNC_ARGS, rax);
__ Cmp(0, rax);
__ Je(&lCopyBoundArgument);
}
__ Bind(&lCopyArgument2);
{
__ Movq(Operand(rdx, rax, Scale::Times8,
(kungfu::ArgumentAccessor::GetFixArgsNum() + 1) * FRAME_SLOT_SIZE), rcx);
__ Pushq(rcx);
__ Addq(-1, rax);
__ Jne(&lCopyArgument2);
}
__ Bind(&lCopyBoundArgument);
{
__ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_ARGUMENTS_OFFSET), rdx);
__ Mov(Operand(rdx, TaggedArray::LENGTH_OFFSET), rax);
__ Addq(TaggedArray::DATA_OFFSET, rdx);
__ Cmp(0, rax);
__ Je(&lPushCallTarget);
}
__ Bind(&lCopyBoundArgumentLoop);
{
__ Addq(-1, rax);
__ Movq(Operand(rdx, rax, Scale::Times8, 0), rcx);
__ Pushq(rcx);
__ Jne(&lCopyBoundArgumentLoop);
}
__ Bind(&lPushCallTarget);
{
__ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_THIS_OFFSET), r8);
__ Pushq(r8);
__ Pushq(JSTaggedValue::VALUE_UNDEFINED);
__ Mov(Operand(jsFuncReg, JSBoundFunction::BOUND_TARGET_OFFSET), rax);
__ Pushq(rax);
__ Pushq(r9);
__ Pushq(r10);
}
JSCallCheck(assembler, rax, &slowCall, &slowCall, &isJsFunc);
__ Jmp(&slowCall);
Register jsfunc = rsi;
Register compiledCodeFlag = rcx;
__ Bind(&isJsFunc);
{
__ Btq(JSHClass::IsClassConstructorOrPrototypeBit::START_BIT, rax);
__ Jnb(&isNotClass);
__ Btq(JSHClass::ConstructorBit::START_BIT, rax);
__ Jb(&slowCall);
__ Bind(&isNotClass);
__ Movzwq(Operand(rsi, JSFunctionBase::BIT_FIELD_OFFSET), compiledCodeFlag);
__ Btq(JSFunctionBase::IsCompiledCodeBit::START_BIT, compiledCodeFlag);
__ Jnb(&slowCall);
__ Bind(&aotCall);
{
#if ECMASCRIPT_ENABLE_ARK_STEED
__ Movq(jsfunc, rdx);
__ Movq(r10, rsi);
__ Subq(NUM_MANDATORY_JSFUNC_ARGS, rsi);
auto funcSlotOffSet = kungfu::ArgumentAccessor::GetFixArgsNum() +
kungfu::ArgumentAccessor::GetExtraArgsNum();
__ Leaq(Operand(rsp, funcSlotOffSet * FRAME_SLOT_SIZE), r9);
__ Movq(JSTaggedValue::VALUE_UNDEFINED, rcx);
__ Movq(RTSTUB_ID(SteedCallWithArgVAndPushArgv), r11);
__ Movq(Operand(rdi, r11, Scale::Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r11);
__ Callq(r11);
#else
__ Movq(jsfunc, rdx);
__ Movq(r10, rsi);
auto funcSlotOffSet = kungfu::ArgumentAccessor::GetFixArgsNum() +
kungfu::ArgumentAccessor::GetExtraArgsNum();
__ Leaq(Operand(rsp, funcSlotOffSet * FRAME_SLOT_SIZE), rcx);
__ Movq(JSTaggedValue::VALUE_UNDEFINED, r9);
__ Movq(kungfu::CommonStubCSigns::JsBoundCallInternal, r10);
__ Movq(Operand(rdi, r10, Scale::Times8, JSThread::GlueData::GetCOStubEntriesOffset(false)), rax);
__ Callq(rax);
#endif
__ Jmp(&popArgs);
}
}
__ Bind(&slowCall);
{
__ Movq(rdi, rax);
__ Callq(jsCall);
__ Jmp(&popArgs);
}
__ Bind(&popArgs);
{
__ Pop(r10);
__ Pop(r9);
__ Leaq(Operand(r10, Scale::Times8, 0), rcx);
__ Addq(rcx, rsp);
__ Testb(1, r10);
__ Je(&lPopFrame2);
__ Addq(8, rsp);
}
__ Bind(&lPopFrame2);
{
__ Pop(r10);
__ Addq(8, rsp);
__ Pop(rbp);
__ Ret();
}
}
void OptimizedCall::CallRuntime(ExtendedAssembler *assembler)
{
__ BindAssemblerStub(RTSTUB_ID(CallRuntime));
__ Pushq(rbp);
__ Movq(rsp, Operand(rax, JSThread::GlueData::GetLeaveFrameOffset(false)));
__ Pushq(static_cast<int32_t>(FrameType::LEAVE_FRAME));
__ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
__ Pushq(r10);
__ Pushq(rdx);
__ Pushq(rax);
__ Movq(rbp, rdx);
__ Addq(2 * FRAME_SLOT_SIZE, rdx);
__ Movq(Operand(rdx, 0), r10);
__ Movq(Operand(rax, r10, Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r10);
__ Movq(rax, rdi);
__ Movq(Operand(rdx, FRAME_SLOT_SIZE), rsi);
__ Addq(2 * FRAME_SLOT_SIZE, rdx);
#ifdef ENABLE_CMC_IR_FIX_REGISTER
__ Movq(rax, r15);
#endif
__ Callq(r10);
__ UpdateReadBarrier();
__ Addq(FRAME_SLOT_SIZE, rsp);
__ Popq(rdx);
__ Popq(r10);
__ Addq(FRAME_SLOT_SIZE, rsp);
__ Popq(rbp);
__ Ret();
}
void OptimizedCall::CallRuntimeWithArgv(ExtendedAssembler *assembler)
{
__ BindAssemblerStub(RTSTUB_ID(CallRuntimeWithArgv));
Register glueReg = rdi;
Register runtimeIdReg = rsi;
Register argcReg = rdx;
Register argvReg = rcx;
__ Movq(rsp, r8);
Register returnAddrReg = r9;
__ Movq(Operand(rsp, 0), returnAddrReg);
__ Pushq(argvReg);
__ Pushq(argcReg);
__ Pushq(runtimeIdReg);
__ Pushq(returnAddrReg);
__ Pushq(rbp);
__ Movq(rsp, Operand(glueReg, JSThread::GlueData::GetLeaveFrameOffset(false)));
__ Pushq(static_cast<int32_t>(FrameType::LEAVE_FRAME_WITH_ARGV));
__ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp);
__ Movq(Operand(glueReg, runtimeIdReg, Scale::Times8, JSThread::GlueData::GetRTStubEntriesOffset(false)), r9);
__ Movq(argcReg, rsi);
__ Movq(argvReg, rdx);
__ Pushq(r8);
#ifdef ENABLE_CMC_IR_FIX_REGISTER
__ Movq(glueReg, r15);
#endif
__ Callq(r9);
__ UpdateReadBarrier();
__ Popq(r8);
__ Addq(FRAME_SLOT_SIZE, rsp);
__ Popq(rbp);
__ Movq(r8, rsp);
__ Ret();
}
void OptimizedCall::PushMandatoryJSArgs(ExtendedAssembler *assembler, Register jsfunc,
Register thisObj, Register newTarget)
{
__ Pushq(thisObj);
__ Pushq(newTarget);
__ Pushq(jsfunc);
}
void OptimizedCall::PushArgsWithArgV(ExtendedAssembler *assembler, Register jsfunc,
Register actualNumArgs, Register argV, Label *pushCallThis)
{
Register expectedNumArgs(r14);
Register tmp(rax);
Label align16Bytes;
Label copyArguments;
__ Movq(Operand(jsfunc, JSFunctionBase::METHOD_OFFSET), tmp);
__ Movq(Operand(tmp, Method::CALL_FIELD_OFFSET), tmp);
__ Shr(Method::NumArgsBits::START_BIT, tmp);
__ Andl(((1LU << Method::NumArgsBits::SIZE) - 1), tmp);
__ Mov(tmp, expectedNumArgs);
__ Testb(1, expectedNumArgs);
__ Jne(&align16Bytes);
__ PushAlignBytes();
__ Bind(&align16Bytes);
{
__ Cmpq(actualNumArgs, expectedNumArgs);
__ Jbe(©Arguments);
__ Subq(actualNumArgs, tmp);
PushUndefinedWithArgc(assembler, tmp);
}
__ Bind(©Arguments);
{
__ Cmpq(actualNumArgs, expectedNumArgs);
__ Movq(actualNumArgs, tmp);
__ CMovbe(expectedNumArgs, tmp);
__ Cmpq(0, tmp);
__ Je(pushCallThis);
CopyArgumentWithArgV(assembler, tmp, argV);
}
}
void OptimizedCall::PopJSFunctionArgs(ExtendedAssembler *assembler, Register expectedNumArgs)
{
Label align16Bytes;
__ Testb(1, expectedNumArgs);
__ Je(&align16Bytes);
__ Addq(FRAME_SLOT_SIZE, rsp);
__ Bind(&align16Bytes);
__ Leaq(Operand(expectedNumArgs, Scale::Times8, 0), expectedNumArgs);
__ Addq(DOUBLE_SLOT_SIZE, rsp);
__ Addq(expectedNumArgs, rsp);
}
void OptimizedCall::PushJSFunctionEntryFrame(ExtendedAssembler *assembler, Register prevFp)
{
__ PushCppCalleeSaveRegisters();
__ Pushq(rdi);
__ Pushq(rbp);
__ Pushq(static_cast<int64_t>(FrameType::OPTIMIZED_ENTRY_FRAME));
__ Pushq(prevFp);
__ Leaq(Operand(rsp, 2 * FRAME_SLOT_SIZE), rbp);
}
void OptimizedCall::PopJSFunctionEntryFrame(ExtendedAssembler *assembler, Register glue)
{
Register prevFp(rsi);
__ Popq(prevFp);
__ Addq(FRAME_SLOT_SIZE, rsp);
__ Popq(rbp);
__ Popq(glue);
__ PopCppCalleeSaveRegisters();
__ Movq(prevFp, Operand(glue, JSThread::GlueData::GetLeaveFrameOffset(false)));
}
void OptimizedCall::PushOptimizedUnfoldArgVFrame(ExtendedAssembler *assembler, Register callSiteSp)
{
__ Pushq(rbp);
__ Pushq(static_cast<int64_t>(FrameType::OPTIMIZED_JS_FUNCTION_UNFOLD_ARGV_FRAME));
__ Pushq(callSiteSp);
__ Leaq(Operand(rsp, 2 * FRAME_SLOT_SIZE), rbp);
}
void OptimizedCall::PopOptimizedUnfoldArgVFrame(ExtendedAssembler *assembler)
{
Register sp(rsp);
__ Addq(Immediate(2 * FRAME_SLOT_SIZE), sp);
__ Popq(rbp);
}
void OptimizedCall::GenJSCallWithArgV(ExtendedAssembler *assembler, int id)
{
Register sp(rsp);
Register glue(rdi);
Register actualNumArgs(rsi);
Register jsfunc(rdx);
Register newTarget(rcx);
Register thisObj(r8);
Register argV(r9);
Register callsiteSp = __ AvailableRegister2();
Label align16Bytes;
Label pushCallThis;
__ Movq(sp, callsiteSp);
__ Addq(Immediate(FRAME_SLOT_SIZE), callsiteSp);
PushOptimizedUnfoldArgVFrame(assembler, callsiteSp);
__ Testb(1, actualNumArgs);
__ Jne(&align16Bytes);
__ PushAlignBytes();
__ Bind(&align16Bytes);
__ Cmp(Immediate(0), actualNumArgs);
__ Jz(&pushCallThis);
__ Mov(actualNumArgs, rax);
CopyArgumentWithArgV(assembler, rax, argV);
__ Bind(&pushCallThis);
PushMandatoryJSArgs(assembler, jsfunc, thisObj, newTarget);
__ Addq(Immediate(NUM_MANDATORY_JSFUNC_ARGS), actualNumArgs);
__ Pushq(sp);
__ Pushq(actualNumArgs);
__ Movq(glue, rax);
__ CallAssemblerStub(id, false);
__ Mov(Operand(sp, 0), actualNumArgs);
PopJSFunctionArgs(assembler, actualNumArgs);
PopOptimizedUnfoldArgVFrame(assembler);
__ Ret();
}
void OptimizedCall::JSCallWithArgVAndPushArgv(ExtendedAssembler *assembler)
{
__ BindAssemblerStub(RTSTUB_ID(JSCallWithArgVAndPushArgv));
GenJSCallWithArgV(assembler, RTSTUB_ID(OptimizedCallAndPushArgv));
}
void OptimizedCall::JSCallWithArgV(ExtendedAssembler *assembler)
{
__ BindAssemblerStub(RTSTUB_ID(JSCallWithArgV));
GenJSCallWithArgV(assembler, RTSTUB_ID(CallOptimized));
}
void OptimizedCall::SuperCallWithArgV(ExtendedAssembler *assembler)
{
__ BindAssemblerStub(RTSTUB_ID(SuperCallWithArgV));
GenJSCallWithArgV(assembler, RTSTUB_ID(JSCallNew));
}
void OptimizedCall::CallOptimized(ExtendedAssembler *assembler)
{
__ BindAssemblerStub(RTSTUB_ID(CallOptimized));
Register jsFuncReg = rdi;
Register method = r9;
Register codeAddrReg = rsi;
auto funcSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1;
__ Movq(Operand(rsp, funcSlotOffset * FRAME_SLOT_SIZE), jsFuncReg);
__ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method);
__ Mov(Operand(jsFuncReg, JSFunctionBase::CODE_ENTRY_OFFSET), codeAddrReg);
__ Jmp(codeAddrReg);
}
void OptimizedCall::DeoptEnterAsmInterpOrBaseline(ExtendedAssembler *assembler)
{
Register glueRegister = __ GlueRegister();
Register context = rsi;
Register tempRegister = rax;
Register opRegister = r10;
Register outputCount = rdx;
Register frameStateBase = rcx;
Register depth = r11;
Register hasExceptionRegister = r15;
Label loopBegin;
Label stackOverflow;
Label gotoExceptionHandler;
Label pushArgv;
__ Movq(Operand(context, AsmStackContext::GetHasExceptionOffset(false)), hasExceptionRegister);
__ Movq(Operand(context, AsmStackContext::GetInlineDepthOffset(false)), depth);
__ Leaq(Operand(context, AsmStackContext::GetSize(false)), context);
__ Movq(Immediate(0), r12);
__ Bind(&loopBegin);
__ Movq(Operand(context, 0), outputCount);
__ Leaq(Operand(context, FRAME_SLOT_SIZE), frameStateBase);
__ Cmpq(0, r12);
__ Je(&pushArgv);
__ Movq(rsp, r8);
__ Addq(AsmInterpretedFrame::GetSize(false), r8);
__ Leaq(Operand(frameStateBase, AsmInterpretedFrame::GetBaseOffset(false)), r10);
__ Movq(r8, Operand(r10, InterpretedFrameBase::GetPrevOffset(false)));
__ Testq(15, rsp);
__ Jnz(&pushArgv);
__ PushAlignBytes();
__ Bind(&pushArgv);
__ Movq(rsp, Operand(frameStateBase, AsmInterpretedFrame::GetFpOffset(false)));
PushArgsWithArgvAndCheckStack(assembler, glueRegister, outputCount,
frameStateBase, tempRegister, opRegister, &stackOverflow);
__ Leaq(Operand(context, outputCount, Scale::Times8, FRAME_SLOT_SIZE), context);
__ Addq(1, r12);
__ Cmpq(r12, depth);
__ Jae(&loopBegin);
Register callTargetRegister = r8;
Register methodRegister = r9;
__ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetFunctionOffset(false)), callTargetRegister);
__ Movq(Operand(callTargetRegister, JSFunction::BASELINECODE_OFFSET), opRegister);
Label baselineCodeUndefined;
__ Cmpq(JSTaggedValue::Undefined().GetRawData(), opRegister);
__ Je(&baselineCodeUndefined);
__ Cmpq(JSTaggedValue::Hole().GetRawData(), opRegister);
__ Je(&baselineCodeUndefined);
{
Register newSpRegister = r11;
__ Leaq(Operand(rsp, AsmInterpretedFrame::GetSize(false)), newSpRegister);
Label stackAligned;
__ Testq(15, rsp);
__ Jz(&stackAligned);
__ PushAlignBytes();
__ Bind(&stackAligned);
Register bytecodePc = opRegister;
__ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetPcOffset(false)), bytecodePc);
Register func = callTargetRegister;
__ Push(glueRegister);
__ Push(callTargetRegister);
__ Push(newSpRegister);
__ PushCppCalleeSaveRegisters();
__ Movq(glueRegister, rax);
__ Pushq(bytecodePc);
__ Pushq(func);
__ Pushq(2);
__ Pushq(kungfu::RuntimeStubCSigns::ID_GetNativePcOfstForBaseline);
__ CallAssemblerStub(RTSTUB_ID(CallRuntime), false);
__ Addq(4 * FRAME_SLOT_SIZE, rsp);
__ PopCppCalleeSaveRegisters();
__ Pop(newSpRegister);
__ Pop(callTargetRegister);
__ Pop(glueRegister);
__ Movq(glueRegister, r13);
__ Movq(Operand(callTargetRegister, JSFunctionBase::METHOD_OFFSET), methodRegister);
__ Movq(methodRegister, rbx);
__ Movq(newSpRegister, rbp);
const int32_t pcOffsetFromSp = -24;
__ Movabs(std::numeric_limits<uint64_t>::max(), opRegister);
__ Movq(opRegister, Operand(rbp, pcOffsetFromSp));
__ Jmp(rax);
}
__ Bind(&baselineCodeUndefined);
{
__ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetFunctionOffset(false)), callTargetRegister);
__ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetPcOffset(false)), r12);
__ Movq(Operand(frameStateBase, AsmInterpretedFrame::GetAccOffset(false)), rsi);
__ Movq(Operand(callTargetRegister, JSFunctionBase::METHOD_OFFSET), methodRegister);
__ Leaq(Operand(rsp, AsmInterpretedFrame::GetSize(false)), opRegister);
__ Cmpq(0, hasExceptionRegister);
__ Jne(&gotoExceptionHandler);
AsmInterpreterCall::DispatchCall(assembler, r12, opRegister, callTargetRegister, methodRegister, rsi, false);
__ Bind(&gotoExceptionHandler);
AsmInterpreterCall::DispatchCall(assembler, r12, opRegister, callTargetRegister, methodRegister, rsi, true);
}
__ Bind(&stackOverflow);
{
[[maybe_unused]] TempRegisterScope scope(assembler);
Register temp = __ TempRegister();
AsmInterpreterCall::ThrowStackOverflowExceptionAndReturnToAsmInterpBridgeFrame(assembler,
glueRegister, rsp, temp);
}
}
void OptimizedCall::DeoptPushAsmInterpBridgeFrame(ExtendedAssembler *assembler, Register context)
{
Label processLazyDeopt;
Label exit;
Register frameTypeRegister = r8;
__ Movq(Operand(context, AsmStackContext::GetIsFrameLazyDeoptOffset(false)), frameTypeRegister);
__ Cmpq(0, frameTypeRegister);
__ Jne(&processLazyDeopt);
{
__ Pushq(static_cast<int64_t>(FrameType::ASM_INTERPRETER_BRIDGE_FRAME));
__ Jmp(&exit);
}
__ Bind(&processLazyDeopt);
{
__ Pushq((static_cast<uint64_t>(FrameType::ASM_INTERPRETER_BRIDGE_FRAME) |
(1ULL << FrameIterator::LAZY_DEOPT_FLAG_BIT)));
}
__ Bind(&exit);
__ Pushq(rbp);
__ Pushq(0);
__ Leaq(Operand(rsp, 24), rbp);
__ PushAlignBytes();
if (!assembler->FromInterpreterHandler()) {
__ PushCppCalleeSaveRegisters();
}
}
void OptimizedCall::DeoptHandlerAsm(ExtendedAssembler *assembler)
{
__ BindAssemblerStub(RTSTUB_ID(DeoptHandlerAsm));
Register glueReg = rdi;
PushAsmBridgeFrame(assembler);
__ Push(glueReg);
__ PushCppCalleeSaveRegisters();
Register deoptType = rsi;
Register maybeAcc = rdx;
{
TempRegisterScope scope(assembler);
Register mark = __ TempRegister();
__ Movabs(JSTaggedValue::TAG_INT, mark);
__ Andl(-1, deoptType);
__ Orq(mark, deoptType);
}
__ Movq(rdi, rax);
__ Subq(FRAME_SLOT_SIZE, rsp);
__ Pushq(maybeAcc);
__ Pushq(deoptType);
__ Pushq(2);
__ Pushq(kungfu::RuntimeStubCSigns::ID_DeoptHandler);
__ CallAssemblerStub(RTSTUB_ID(CallRuntime), false);
__ Addq(5 * FRAME_SLOT_SIZE, rsp);
Register context = rsi;
__ Movq(rax, context);
Label target;
__ PopCppCalleeSaveRegisters();
__ Pop(glueReg);
Label stackOverflow;
__ Cmpq(JSTaggedValue::VALUE_EXCEPTION, rax);
__ Je(&stackOverflow);
__ Movq(Operand(context, AsmStackContext::GetCallerFpOffset(false)), rbp);
__ Movq(Operand(context, AsmStackContext::GetCallFrameTopOffset(false)), rsp);
__ Subq(FRAME_SLOT_SIZE, rsp);
DeoptPushAsmInterpBridgeFrame(assembler, context);
__ Callq(&target);
PopAsmInterpBridgeFrame(assembler);
__ Ret();
__ Bind(&target);
DeoptEnterAsmInterpOrBaseline(assembler);
__ Int3();
__ Bind(&stackOverflow);
{
__ Movq(rdi, rax);
__ Pushq(0);
__ Pushq(kungfu::RuntimeStubCSigns::ID_ThrowStackOverflowException);
__ CallAssemblerStub(RTSTUB_ID(CallRuntime), false);
__ Addq(FRAME_SLOT_SIZE * 3, rsp);
__ Popq(rbp);
__ Ret();
}
}
#undef __
}