|
|
|
@ -102,6 +102,21 @@ void AssemblerStubs::CallRuntime(ExtendedAssembler *assembler)
|
|
|
|
|
__ Ret();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AssemblerStubs::IncreaseStackForArguments(ExtendedAssembler *assembler, Register argc, Register fp)
|
|
|
|
|
{
|
|
|
|
|
Register sp(SP);
|
|
|
|
|
__ Mov(fp, sp);
|
|
|
|
|
__ Add(argc, argc, Immediate(2)); // 2 : 2 means numArgs + env
|
|
|
|
|
__ Sub(fp, fp, Operand(argc, UXTW, 3));
|
|
|
|
|
Label aligned;
|
|
|
|
|
__ Tst(fp, LogicalImmediate::Create(0xf, RegXSize)); // 0xf: 0x1111
|
|
|
|
|
__ B(Condition::EQ, &aligned);
|
|
|
|
|
__ Sub(fp, fp, Immediate(FRAME_SLOT_SIZE));
|
|
|
|
|
__ Bind(&aligned);
|
|
|
|
|
__ Mov(sp, fp);
|
|
|
|
|
__ Add(fp, fp, Operand(argc, UXTW, 3));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// uint64_t JSFunctionEntry(uintptr_t glue, uintptr_t prevFp, uint32_t expectedNumArgs,
|
|
|
|
|
// uint32_t actualNumArgs, const JSTaggedType argV[], uintptr_t codeAddr)
|
|
|
|
|
// Input: %x0 - glue
|
|
|
|
@ -131,84 +146,54 @@ void AssemblerStubs::JSFunctionEntry(ExtendedAssembler *assembler)
|
|
|
|
|
Register argV(X4);
|
|
|
|
|
Register codeAddr(X5);
|
|
|
|
|
Register sp(SP);
|
|
|
|
|
Register fp(X29);
|
|
|
|
|
Register fp(X6);
|
|
|
|
|
Label copyArguments;
|
|
|
|
|
|
|
|
|
|
__ BindAssemblerStub(RTSTUB_ID(JSFunctionEntry));
|
|
|
|
|
__ Str(Register(X30), MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
__ CalleeSave();
|
|
|
|
|
__ Str(fp, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
__ Mov(fp, sp);
|
|
|
|
|
|
|
|
|
|
Register frameType(X19);
|
|
|
|
|
// construct frame
|
|
|
|
|
__ Mov(frameType, Immediate(static_cast<int64_t>(FrameType::OPTIMIZED_ENTRY_FRAME)));
|
|
|
|
|
__ Stp(prevFp, frameType, MemoryOperand(sp, -FRAME_SLOT_SIZE * 2, AddrMode::PREINDEX));
|
|
|
|
|
|
|
|
|
|
Label copyUndefined;
|
|
|
|
|
Label copyArguments;
|
|
|
|
|
Register tmp(X19, W);
|
|
|
|
|
__ Mov(glue, Register(X0));
|
|
|
|
|
__ Mov(tmp, expectedNumArgs.W());
|
|
|
|
|
__ Cmp(tmp, actualNumArgs.W());
|
|
|
|
|
__ B(Condition::LS, ©Arguments);
|
|
|
|
|
Register count(X9, W);
|
|
|
|
|
Register undefValue(X8);
|
|
|
|
|
__ Mov(count, tmp.W());
|
|
|
|
|
__ Mov(undefValue, Immediate(JSTaggedValue::VALUE_UNDEFINED));
|
|
|
|
|
|
|
|
|
|
__ Bind(©Undefined);
|
|
|
|
|
__ Sub(count, count, Immediate(1));
|
|
|
|
|
__ Cmp(count, actualNumArgs.W());
|
|
|
|
|
__ Str(undefValue, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
__ B(Condition::HI, ©Undefined);
|
|
|
|
|
PushAotEntryFrame(assembler, prevFp);
|
|
|
|
|
Register argC(X7);
|
|
|
|
|
__ Cmp(expectedNumArgs, actualNumArgs);
|
|
|
|
|
__ CMov(argC, expectedNumArgs, actualNumArgs, Condition::HI);
|
|
|
|
|
IncreaseStackForArguments(assembler, argC, fp);
|
|
|
|
|
|
|
|
|
|
Label invokeCompiledJSFunction;
|
|
|
|
|
__ Bind(©Arguments);
|
|
|
|
|
{
|
|
|
|
|
Register argVEnd(X9);
|
|
|
|
|
Register argC(X8, W);
|
|
|
|
|
Register argValue(X10);
|
|
|
|
|
Register env(X11);
|
|
|
|
|
Label copyArgLoop;
|
|
|
|
|
|
|
|
|
|
// expectedNumArgs <= actualNumArgs
|
|
|
|
|
__ Cmp(tmp.W(), actualNumArgs.W());
|
|
|
|
|
__ CMov(argC, tmp.W(), actualNumArgs.W(), Condition::LO);
|
|
|
|
|
__ Cbz(argC, &invokeCompiledJSFunction);
|
|
|
|
|
__ Sub(argVEnd.W(), argC, Immediate(1));
|
|
|
|
|
__ Add(argVEnd, argV, Operand(argVEnd.W(), UXTW, 3));
|
|
|
|
|
__ Ldr(env, MemoryOperand(argVEnd, FRAME_SLOT_SIZE));
|
|
|
|
|
|
|
|
|
|
__ Bind(©ArgLoop);
|
|
|
|
|
__ Ldr(argValue, MemoryOperand(argVEnd, -FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
|
|
|
|
|
__ Subs(argC, argC, Immediate(1));
|
|
|
|
|
__ Str(argValue, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
__ B(Condition::NE, ©ArgLoop);
|
|
|
|
|
TempRegister1Scope scope1(assembler);
|
|
|
|
|
TempRegister2Scope scope2(assembler);
|
|
|
|
|
Register argc = __ TempRegister1();
|
|
|
|
|
Register undefinedValue = __ TempRegister2();
|
|
|
|
|
__ Subs(argc, expectedNumArgs, Operand(actualNumArgs));
|
|
|
|
|
__ B(Condition::LS, ©Arguments);
|
|
|
|
|
PushUndefinedWithArgc(assembler, argc, undefinedValue, fp, nullptr);
|
|
|
|
|
}
|
|
|
|
|
__ Bind(©Arguments);
|
|
|
|
|
__ Cbz(actualNumArgs, &invokeCompiledJSFunction);
|
|
|
|
|
{
|
|
|
|
|
TempRegister1Scope scope1(assembler);
|
|
|
|
|
TempRegister2Scope scope2(assembler);
|
|
|
|
|
Register argc = __ TempRegister1();
|
|
|
|
|
Register argValue = __ TempRegister2();
|
|
|
|
|
__ Mov(argc, actualNumArgs);
|
|
|
|
|
__ PushArgsWithArgv(argc, argV, argValue, fp, &invokeCompiledJSFunction);
|
|
|
|
|
}
|
|
|
|
|
__ Bind(&invokeCompiledJSFunction);
|
|
|
|
|
{
|
|
|
|
|
Register env(X11);
|
|
|
|
|
__ Str(actualNumArgs, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
__ Str(env, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
TempRegister1Scope scope1(assembler);
|
|
|
|
|
Register env = __ TempRegister1();
|
|
|
|
|
__ Mov(Register(X19), expectedNumArgs);
|
|
|
|
|
__ Ldr(env, MemoryOperand(argV, actualNumArgs, UXTW, 3));
|
|
|
|
|
__ Str(actualNumArgs, MemoryOperand(sp, FRAME_SLOT_SIZE));
|
|
|
|
|
__ Str(env, MemoryOperand(sp, 0));
|
|
|
|
|
__ Blr(codeAddr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// pop argV
|
|
|
|
|
// 3 : 3 means argC * 8
|
|
|
|
|
__ Add(sp, sp, Operand(tmp, UXTW, 3));
|
|
|
|
|
__ Add(sp, sp, Immediate(FRAME_SLOT_SIZE * 2)); // 2: pop argc and env
|
|
|
|
|
__ Ldr(actualNumArgs, MemoryOperand(sp, FRAME_SLOT_SIZE));
|
|
|
|
|
PopAotArgs(assembler, Register(X19), actualNumArgs);
|
|
|
|
|
|
|
|
|
|
// pop prevLeaveFrameFp to restore thread->currentFrame_
|
|
|
|
|
__ Ldr(prevFp, MemoryOperand(sp, FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
|
|
|
|
|
__ Str(prevFp, MemoryOperand(glue, JSThread::GlueData::GetLeaveFrameOffset(false)));
|
|
|
|
|
|
|
|
|
|
// pop entry frame type and c-fp
|
|
|
|
|
__ Add(sp, sp, Immediate(FRAME_SLOT_SIZE));
|
|
|
|
|
__ Ldr(fp, MemoryOperand(sp, FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
|
|
|
|
|
|
|
|
|
|
__ CalleeRestore();
|
|
|
|
|
// restore return address
|
|
|
|
|
__ Ldr(Register(X30), MemoryOperand(sp, FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
|
|
|
|
|
PopAotEntryFrame(assembler, glue);
|
|
|
|
|
__ Ret();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -219,6 +204,7 @@ void AssemblerStubs::JSFunctionEntry(ExtendedAssembler *assembler)
|
|
|
|
|
// %w2 - actualNumArgs
|
|
|
|
|
// %x3 - codeAddr
|
|
|
|
|
// %x4 - argv
|
|
|
|
|
// %x5 - env
|
|
|
|
|
|
|
|
|
|
// sp[0 * 8] - argc
|
|
|
|
|
// sp[1 * 8] - argv[0]
|
|
|
|
@ -235,71 +221,54 @@ void AssemblerStubs::OptimizedCallOptimized(ExtendedAssembler *assembler)
|
|
|
|
|
{
|
|
|
|
|
__ BindAssemblerStub(RTSTUB_ID(OptimizedCallOptimized));
|
|
|
|
|
Register expectedNumArgs(X1);
|
|
|
|
|
Register sp(SP);
|
|
|
|
|
__ SaveFpAndLr();
|
|
|
|
|
// Construct frame
|
|
|
|
|
Register frameType(X6);
|
|
|
|
|
__ Mov(frameType, Immediate(static_cast<int64_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME)));
|
|
|
|
|
__ Str(frameType, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
Register env(X5);
|
|
|
|
|
__ Str(env, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
|
|
|
|
|
// callee save
|
|
|
|
|
Register tmp(X19);
|
|
|
|
|
__ Str(tmp, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
|
|
|
|
|
Register count(X6, W);
|
|
|
|
|
Register actualNumArgs(X2, W);
|
|
|
|
|
Register codeAddr(X3);
|
|
|
|
|
Register argV(X4);
|
|
|
|
|
Register env(X5);
|
|
|
|
|
Register fp(X6);
|
|
|
|
|
Register sp(SP);
|
|
|
|
|
Label copyArguments;
|
|
|
|
|
__ Mov(count, expectedNumArgs);
|
|
|
|
|
__ Cmp(count, actualNumArgs);
|
|
|
|
|
__ B(Condition::LS, ©Arguments);
|
|
|
|
|
|
|
|
|
|
Register undefValue(X8);
|
|
|
|
|
__ Mov(undefValue, Immediate(JSTaggedValue::VALUE_UNDEFINED));
|
|
|
|
|
Label copyUndefined;
|
|
|
|
|
__ Bind(©Undefined);
|
|
|
|
|
__ Sub(count, count, Immediate(1));
|
|
|
|
|
__ Str(undefValue, MemoryOperand(sp, -FRAME_SLOT_SIZE, PREINDEX));
|
|
|
|
|
__ Cmp(count, actualNumArgs);
|
|
|
|
|
__ B(Condition::HI, ©Undefined);
|
|
|
|
|
|
|
|
|
|
Label invokeCompiledJSFunction;
|
|
|
|
|
Register saveNumArgs(X19);
|
|
|
|
|
__ Bind(©Arguments);
|
|
|
|
|
{
|
|
|
|
|
__ Cmp(expectedNumArgs.W(), actualNumArgs.W());
|
|
|
|
|
__ CMov(count, expectedNumArgs.W(), actualNumArgs.W(), Condition::LO);
|
|
|
|
|
__ Cbz(count, &invokeCompiledJSFunction);
|
|
|
|
|
|
|
|
|
|
Register argVEnd(X4);
|
|
|
|
|
Register argValue(X10);
|
|
|
|
|
Label copyArgLoop;
|
|
|
|
|
__ Mov(saveNumArgs, expectedNumArgs);
|
|
|
|
|
__ Subs(count, count, Immediate(1));
|
|
|
|
|
// 3 : 3 means count * 8
|
|
|
|
|
__ Add(argVEnd, argVEnd, Operand(count, UXTW, 3));
|
|
|
|
|
__ Bind(©ArgLoop);
|
|
|
|
|
__ Ldr(argValue, MemoryOperand(argVEnd, -FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
|
|
|
|
|
__ Subs(count, count, Immediate(1));
|
|
|
|
|
__ Str(argValue, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
__ B(Condition::PL, ©ArgLoop);
|
|
|
|
|
// construct frame
|
|
|
|
|
PushOptimizedJSFunctionFrame(assembler);
|
|
|
|
|
Register argC(X7);
|
|
|
|
|
__ Cmp(expectedNumArgs, actualNumArgs);
|
|
|
|
|
__ CMov(argC, expectedNumArgs, actualNumArgs, Condition::HI);
|
|
|
|
|
IncreaseStackForArguments(assembler, argC, fp);
|
|
|
|
|
{
|
|
|
|
|
TempRegister1Scope scope1(assembler);
|
|
|
|
|
TempRegister2Scope scope2(assembler);
|
|
|
|
|
Register tmp = __ TempRegister1();
|
|
|
|
|
Register undefinedValue = __ TempRegister2();
|
|
|
|
|
__ Subs(tmp, expectedNumArgs, actualNumArgs);
|
|
|
|
|
__ B(Condition::LS, ©Arguments);
|
|
|
|
|
PushUndefinedWithArgc(assembler, tmp, undefinedValue, fp, nullptr);
|
|
|
|
|
}
|
|
|
|
|
__ Bind(©Arguments);
|
|
|
|
|
__ Cbz(actualNumArgs, &invokeCompiledJSFunction);
|
|
|
|
|
{
|
|
|
|
|
TempRegister1Scope scope1(assembler);
|
|
|
|
|
TempRegister2Scope scope2(assembler);
|
|
|
|
|
Register argc = __ TempRegister1();
|
|
|
|
|
Register argValue = __ TempRegister2();
|
|
|
|
|
__ Mov(argc, actualNumArgs);
|
|
|
|
|
__ PushArgsWithArgv(argc, argV, argValue, fp, &invokeCompiledJSFunction);
|
|
|
|
|
}
|
|
|
|
|
__ Bind(&invokeCompiledJSFunction);
|
|
|
|
|
{
|
|
|
|
|
__ Mov(Register(X19), expectedNumArgs);
|
|
|
|
|
__ Str(actualNumArgs, MemoryOperand(sp, FRAME_SLOT_SIZE));
|
|
|
|
|
__ Str(env, MemoryOperand(sp, 0));
|
|
|
|
|
__ Blr(codeAddr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Register codeAddr(X3);
|
|
|
|
|
__ Bind(&invokeCompiledJSFunction);
|
|
|
|
|
__ Str(actualNumArgs, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
__ Str(env, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
__ Blr(codeAddr);
|
|
|
|
|
// pop argv
|
|
|
|
|
// 3 : 3 means count * 8
|
|
|
|
|
__ Add(sp, sp, Operand(saveNumArgs, UXTW, 3));
|
|
|
|
|
__ Add(sp, sp, Immediate(FRAME_SLOT_SIZE * 2)); // 2: pop argc and env
|
|
|
|
|
// callee restore
|
|
|
|
|
__ Ldr(saveNumArgs, MemoryOperand(sp, FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
|
|
|
|
|
// desconstruct frame
|
|
|
|
|
__ Add(sp, sp, Immediate(FRAME_SLOT_SIZE * 2)); // 2: skip type and env
|
|
|
|
|
__ RestoreFpAndLr();
|
|
|
|
|
// pop argV argC
|
|
|
|
|
// 3 : 3 means argC * 8
|
|
|
|
|
__ Ldr(actualNumArgs, MemoryOperand(sp, FRAME_SLOT_SIZE));
|
|
|
|
|
PopAotArgs(assembler, Register(X19), actualNumArgs);
|
|
|
|
|
// pop prevLeaveFrameFp to restore thread->currentFrame_
|
|
|
|
|
PopOptimizedJSFunctionFrame(assembler);
|
|
|
|
|
__ Ret();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -317,6 +286,49 @@ void AssemblerStubs::OptimizedCallAsmInterpreter(ExtendedAssembler *assembler)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AssemblerStubs::PushLeaveFrame(ExtendedAssembler *assembler, Register glue, bool isBuiltin)
|
|
|
|
|
{
|
|
|
|
|
TempRegister2Scope temp2Scope(assembler);
|
|
|
|
|
Register frameType = __ TempRegister2();
|
|
|
|
|
Register fp(X6);
|
|
|
|
|
Register sp(SP);
|
|
|
|
|
Register prevfp(X29);
|
|
|
|
|
|
|
|
|
|
// construct leave frame
|
|
|
|
|
if (isBuiltin) {
|
|
|
|
|
__ Mov(frameType, Immediate(static_cast<int64_t>(FrameType::BUILTIN_CALL_LEAVE_FRAME)));
|
|
|
|
|
// current sp is not 16bytes aligned because native code address has pushed.
|
|
|
|
|
__ Mov(fp, sp);
|
|
|
|
|
__ Sub(sp, sp, Immediate(3 * FRAME_SLOT_SIZE)); // 3 : 3 for 16bytes align
|
|
|
|
|
__ Stp(prevfp, Register(X30), MemoryOperand(fp, -2 * FRAME_SLOT_SIZE, PREINDEX));
|
|
|
|
|
__ Str(frameType, MemoryOperand(fp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
__ Add(prevfp, sp, Immediate(FRAME_SLOT_SIZE));
|
|
|
|
|
} else {
|
|
|
|
|
__ Mov(frameType, Immediate(static_cast<int64_t>(FrameType::LEAVE_FRAME)));
|
|
|
|
|
__ SaveFpAndLr();
|
|
|
|
|
__ Stp(Register(X19), frameType, MemoryOperand(sp, -2 * FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
}
|
|
|
|
|
// save to thread currentLeaveFrame_;
|
|
|
|
|
__ Str(prevfp, MemoryOperand(glue, JSThread::GlueData::GetLeaveFrameOffset(false)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void AssemblerStubs::PopLeaveFrame(ExtendedAssembler *assembler, bool isBuiltin)
|
|
|
|
|
{
|
|
|
|
|
Register sp(SP);
|
|
|
|
|
Register fp(X6);
|
|
|
|
|
TempRegister2Scope temp2Scope(assembler);
|
|
|
|
|
Register frameType = __ TempRegister2();
|
|
|
|
|
if (isBuiltin) {
|
|
|
|
|
__ Add(fp, sp, Immediate(FRAME_SLOT_SIZE)); // skip frame type
|
|
|
|
|
__ Add(sp, sp, Immediate(3 * FRAME_SLOT_SIZE));
|
|
|
|
|
__ Ldp(Register(X29), Register(X30), MemoryOperand(fp, 2 * FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
|
|
|
|
|
} else {
|
|
|
|
|
__ Ldp(Register(X19), frameType, MemoryOperand(sp, 2 * FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
|
|
|
|
|
__ RestoreFpAndLr();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// uint64_t CallBuiltinTrampoline(uintptr_t glue, uintptr_t codeAddress, uint32_t argc, ...)
|
|
|
|
|
// webkit_jscc calling convention call runtime_id's runtion function(c-abi)
|
|
|
|
|
// Input:
|
|
|
|
@ -350,33 +362,20 @@ void AssemblerStubs::OptimizedCallAsmInterpreter(ExtendedAssembler *assembler)
|
|
|
|
|
|
|
|
|
|
void AssemblerStubs::CallBuiltinTrampoline(ExtendedAssembler *assembler)
|
|
|
|
|
{
|
|
|
|
|
__ BindAssemblerStub(RTSTUB_ID(CallBuiltinTrampoline));
|
|
|
|
|
__ SaveFpAndLr();
|
|
|
|
|
// save to thread currentLeaveFrame_;
|
|
|
|
|
Register fp(X29);
|
|
|
|
|
Register glue(X0);
|
|
|
|
|
Register sp(SP);
|
|
|
|
|
__ Str(fp, MemoryOperand(glue, JSThread::GlueData::GetLeaveFrameOffset(false)));
|
|
|
|
|
Register nativeFuncAddr(X4);
|
|
|
|
|
|
|
|
|
|
Register nativeFuncAddr(X19);
|
|
|
|
|
PushLeaveFrame(assembler, glue, true);
|
|
|
|
|
|
|
|
|
|
// construct leave frame and callee save
|
|
|
|
|
Register frameType(X1);
|
|
|
|
|
__ Mov(frameType, Immediate(static_cast<int64_t>(FrameType::BUILTIN_CALL_LEAVE_FRAME)));
|
|
|
|
|
// 2 : 2 means pair
|
|
|
|
|
__ Stp(nativeFuncAddr, frameType, MemoryOperand(sp, -FRAME_SLOT_SIZE * 2, AddrMode::PREINDEX));
|
|
|
|
|
|
|
|
|
|
// load runtime trampoline address
|
|
|
|
|
__ Ldr(nativeFuncAddr, MemoryOperand(fp, GetStackArgOffSetToFp(BuiltinsLeaveFrameArgId::CODE_ADDRESS)));
|
|
|
|
|
__ Str(glue, MemoryOperand(fp, GetStackArgOffSetToFp(BuiltinsLeaveFrameArgId::ENV))); // thread (instead of env)
|
|
|
|
|
|
|
|
|
|
__ Add(Register(X0), fp, Immediate(GetStackArgOffSetToFp(BuiltinsLeaveFrameArgId::ENV)));
|
|
|
|
|
__ Blr(nativeFuncAddr);
|
|
|
|
|
|
|
|
|
|
// descontruct leave frame and callee save register
|
|
|
|
|
__ Ldp(nativeFuncAddr, frameType, MemoryOperand(sp, 2 * FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
|
|
|
|
|
__ RestoreFpAndLr();
|
|
|
|
|
__ Add(sp, sp, Immediate(8)); // 8 : 8 skip native code address
|
|
|
|
|
PopLeaveFrame(assembler, true);
|
|
|
|
|
__ Add(sp, sp, Immediate(FRAME_SLOT_SIZE)); // skip native code address
|
|
|
|
|
__ Ret();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -470,7 +469,7 @@ void AssemblerStubs::JSCallBody(ExtendedAssembler *assembler, Register jsfunc)
|
|
|
|
|
__ Ldr(nativeFuncAddr, MemoryOperand(method, JSMethod::GetNativePointerOffset()));
|
|
|
|
|
// -8 : -8 means sp increase step
|
|
|
|
|
__ Str(nativeFuncAddr, MemoryOperand(sp, -8, AddrMode::PREINDEX));
|
|
|
|
|
__ CallAssemblerStub(RTSTUB_ID(CallBuiltinTrampoline), true);
|
|
|
|
|
CallBuiltinTrampoline(assembler);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__ Bind(&callOptimizedMethod);
|
|
|
|
@ -488,8 +487,7 @@ void AssemblerStubs::JSCallBody(ExtendedAssembler *assembler, Register jsfunc)
|
|
|
|
|
LogicalImmediate::Create(JSMethod::NumArgsBits::Mask() >> JSMethod::NumArgsBits::START_BIT, RegWSize));
|
|
|
|
|
__ Add(expectedNumArgs, callField.W(), Immediate(NUM_MANDATORY_JSFUNC_ARGS));
|
|
|
|
|
__ Cmp(arg2.W(), expectedNumArgs);
|
|
|
|
|
// argV = sp + 16
|
|
|
|
|
__ Add(argV, sp, Immediate(DOUBLE_SLOT_SIZE));
|
|
|
|
|
__ Add(argV, sp, Immediate(2 * FRAME_SLOT_SIZE)); // 2 : 2 means skip argc and env
|
|
|
|
|
__ Ldr(codeAddress, MemoryOperand(Register(X5), JSFunctionBase::CODE_ENTRY_OFFSET));
|
|
|
|
|
__ Ldr(env, MemoryOperand(sp, 0));
|
|
|
|
|
__ B(Condition::HS, &directCallCodeEntry);
|
|
|
|
@ -512,85 +510,68 @@ void AssemblerStubs::JSCallBody(ExtendedAssembler *assembler, Register jsfunc)
|
|
|
|
|
|
|
|
|
|
__ Bind(&jsBoundFunction);
|
|
|
|
|
{
|
|
|
|
|
__ SaveFpAndLr();
|
|
|
|
|
// construct frame
|
|
|
|
|
Register frameType(X5);
|
|
|
|
|
Register fp(X29);
|
|
|
|
|
__ Mov(frameType, Immediate(static_cast<int64_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME)));
|
|
|
|
|
__ Str(frameType, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
Register argVEnd(X6);
|
|
|
|
|
__ Add(argVEnd, fp, Immediate(GetStackArgOffSetToFp(1)));
|
|
|
|
|
__ Ldr(actualArgC, MemoryOperand(argVEnd, 0));
|
|
|
|
|
Register envReg(X11);
|
|
|
|
|
__ Ldr(envReg, MemoryOperand(argVEnd, -8)); // -8: get env
|
|
|
|
|
// callee save
|
|
|
|
|
Register tmp(X19);
|
|
|
|
|
__ Str(tmp, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
PushOptimizedJSFunctionFrame(assembler);
|
|
|
|
|
Register basefp(X29);
|
|
|
|
|
Register fp = __ AvailableRegister1();
|
|
|
|
|
Register env(X5);
|
|
|
|
|
|
|
|
|
|
Register argV(X6);
|
|
|
|
|
__ Add(argV, basefp, Immediate(GetStackArgOffSetToFp(0)));
|
|
|
|
|
__ Ldr(actualArgC, MemoryOperand(argV, FRAME_SLOT_SIZE));
|
|
|
|
|
__ Ldr(env, MemoryOperand(argV, 0));
|
|
|
|
|
|
|
|
|
|
Register boundLength(X2);
|
|
|
|
|
Register realArgC(X19, W);
|
|
|
|
|
Register realArgC(X7, W);
|
|
|
|
|
Label copyBoundArgument;
|
|
|
|
|
Label copyArgument;
|
|
|
|
|
Label pushCallTarget;
|
|
|
|
|
// get bound arguments
|
|
|
|
|
__ Ldr(boundLength, MemoryOperand(jsfunc, JSBoundFunction::BOUND_ARGUMENTS_OFFSET));
|
|
|
|
|
// get bound length
|
|
|
|
|
__ Ldr(boundLength, MemoryOperand(boundLength, TaggedArray::LENGTH_OFFSET));
|
|
|
|
|
__ Add(realArgC, boundLength.W(), actualArgC.W());
|
|
|
|
|
// 3 : 3 mean *8
|
|
|
|
|
__ Add(argVEnd, argVEnd, Operand(actualArgC.W(), UXTW, 3));
|
|
|
|
|
__ Mov(Register(X19), realArgC);
|
|
|
|
|
IncreaseStackForArguments(assembler, realArgC, fp);
|
|
|
|
|
__ Sub(actualArgC.W(), actualArgC.W(), Immediate(NUM_MANDATORY_JSFUNC_ARGS));
|
|
|
|
|
__ Cmp(actualArgC.W(), Immediate(0));
|
|
|
|
|
__ B(Condition::EQ, ©BoundArgument);
|
|
|
|
|
__ Bind(©Argument);
|
|
|
|
|
{
|
|
|
|
|
Register argValue(X5);
|
|
|
|
|
__ Ldr(argValue, MemoryOperand(argVEnd, -FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
|
|
|
|
|
__ Str(argValue, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
__ Sub(actualArgC.W(), actualArgC.W(), Immediate(1));
|
|
|
|
|
__ Cmp(actualArgC.W(), Immediate(0));
|
|
|
|
|
__ B(Condition::NE, ©Argument);
|
|
|
|
|
TempRegister1Scope scope1(assembler);
|
|
|
|
|
Register tmp = __ TempRegister1();
|
|
|
|
|
// 2 : 2 means numArgs and env
|
|
|
|
|
__ Add(argV, argV, Immediate((NUM_MANDATORY_JSFUNC_ARGS + 2) *FRAME_SLOT_SIZE));
|
|
|
|
|
__ PushArgsWithArgv(actualArgC, argV, tmp, fp, nullptr);
|
|
|
|
|
}
|
|
|
|
|
__ Bind(©BoundArgument);
|
|
|
|
|
{
|
|
|
|
|
Register boundArgs(X4);
|
|
|
|
|
Label copyBoundArgumentLoop;
|
|
|
|
|
__ Ldr(boundArgs, MemoryOperand(jsfunc, JSBoundFunction::BOUND_ARGUMENTS_OFFSET));
|
|
|
|
|
__ Add(boundArgs, boundArgs, Immediate(TaggedArray::DATA_OFFSET));
|
|
|
|
|
__ Cmp(boundLength.W(), Immediate(0));
|
|
|
|
|
__ B(Condition::EQ, &pushCallTarget);
|
|
|
|
|
__ Sub(boundLength.W(), boundLength.W(), Immediate(1));
|
|
|
|
|
// 3 : 3 means 2^3 = 8
|
|
|
|
|
__ Add(boundArgs, boundArgs, Operand(boundLength.W(), UXTW, 3));
|
|
|
|
|
__ Bind(©BoundArgumentLoop);
|
|
|
|
|
{
|
|
|
|
|
Register boundargValue(X5);
|
|
|
|
|
__ Ldr(boundargValue, MemoryOperand(boundArgs, -FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
|
|
|
|
|
__ Str(boundargValue, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
__ Subs(boundLength.W(), boundLength.W(), Immediate(1));
|
|
|
|
|
__ B(Condition::PL, ©BoundArgumentLoop);
|
|
|
|
|
TempRegister1Scope scope1(assembler);
|
|
|
|
|
Register tmp = __ TempRegister1();
|
|
|
|
|
__ PushArgsWithArgv(boundLength, boundArgs, tmp, fp, nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
__ Bind(&pushCallTarget);
|
|
|
|
|
{
|
|
|
|
|
Register thisObj(X5);
|
|
|
|
|
Register thisObj(X4);
|
|
|
|
|
Register newTarget(X6);
|
|
|
|
|
Register boundTarget(X7);
|
|
|
|
|
__ Ldr(thisObj, MemoryOperand(jsfunc, JSBoundFunction::BOUND_THIS_OFFSET));
|
|
|
|
|
__ Mov(newTarget, Immediate(JSTaggedValue::VALUE_UNDEFINED));
|
|
|
|
|
__ Stp(newTarget, thisObj, MemoryOperand(sp, -FRAME_SLOT_SIZE * 2, AddrMode::PREINDEX));
|
|
|
|
|
__ Stp(newTarget, thisObj, MemoryOperand(fp, -FRAME_SLOT_SIZE * 2, AddrMode::PREINDEX));
|
|
|
|
|
__ Ldr(boundTarget, MemoryOperand(jsfunc, JSBoundFunction::BOUND_TARGET_OFFSET));
|
|
|
|
|
// 2 : 2 means pair
|
|
|
|
|
__ Stp(realArgC.X(), boundTarget, MemoryOperand(sp, -FRAME_SLOT_SIZE * 2, AddrMode::PREINDEX));
|
|
|
|
|
__ Str(envReg, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
__ Stp(Register(X19), boundTarget, MemoryOperand(fp, -FRAME_SLOT_SIZE * 2, AddrMode::PREINDEX));
|
|
|
|
|
__ Str(env, MemoryOperand(fp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
}
|
|
|
|
|
__ CallAssemblerStub(RTSTUB_ID(JSCall), false);
|
|
|
|
|
__ Add(sp, sp, Immediate(FRAME_SLOT_SIZE * 2)); // 2: skip argc and env
|
|
|
|
|
// 3 : 3 means 2^3 = 8
|
|
|
|
|
__ Add(sp, sp, Operand(realArgC, UXTW, 3));
|
|
|
|
|
__ Ldr(tmp, MemoryOperand(sp, FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
|
|
|
|
|
__ Add(sp, sp, Immediate(FRAME_SLOT_SIZE));
|
|
|
|
|
__ RestoreFpAndLr();
|
|
|
|
|
|
|
|
|
|
PopAotArgs(assembler, Register(X19), Register(X19));
|
|
|
|
|
PopOptimizedJSFunctionFrame(assembler);
|
|
|
|
|
__ Ret();
|
|
|
|
|
}
|
|
|
|
|
__ Bind(&jsProxy);
|
|
|
|
@ -1830,11 +1811,26 @@ void AssemblerStubs::PushMandatoryJSArgs(ExtendedAssembler *assembler, Register
|
|
|
|
|
__ Str(jsfunc, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AssemblerStubs::PopAotArgs(ExtendedAssembler *assembler, Register expectedNumArgs)
|
|
|
|
|
void AssemblerStubs::PopAotArgs(ExtendedAssembler *assembler, Register expectedNumArgs, Register actualNumArgs)
|
|
|
|
|
{
|
|
|
|
|
Register sp(SP);
|
|
|
|
|
__ Add(sp, sp, Operand(expectedNumArgs, UXTW, 3)); // 3 : 3 means *8
|
|
|
|
|
Register fp(X6);
|
|
|
|
|
Label aligned;
|
|
|
|
|
if (expectedNumArgs != actualNumArgs) {
|
|
|
|
|
TempRegister1Scope scop1(assembler);
|
|
|
|
|
Register tmp = __ TempRegister1();
|
|
|
|
|
__ Cmp(expectedNumArgs, actualNumArgs);
|
|
|
|
|
__ CMov(tmp, expectedNumArgs, actualNumArgs, Condition::HI);
|
|
|
|
|
__ Add(sp, sp, Operand(tmp, UXTW, 3)); // 3 : 3 means *8
|
|
|
|
|
} else {
|
|
|
|
|
__ Add(sp, sp, Operand(expectedNumArgs, UXTW, 3)); // 3 : 3 means *8
|
|
|
|
|
}
|
|
|
|
|
__ Add(sp, sp, Immediate(2 * FRAME_SLOT_SIZE)); // 2 : 2 means skip numArgs and env
|
|
|
|
|
__ Mov(fp, sp);
|
|
|
|
|
__ Tst(fp, LogicalImmediate::Create(0xf, RegXSize)); // 0xf: 0x1111
|
|
|
|
|
__ B(Condition::EQ, &aligned);
|
|
|
|
|
__ Add(sp, sp, Immediate(FRAME_SLOT_SIZE));
|
|
|
|
|
__ Bind(&aligned);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AssemblerStubs::PushAotEntryFrame(ExtendedAssembler *assembler, Register prevFp)
|
|
|
|
@ -1842,16 +1838,13 @@ void AssemblerStubs::PushAotEntryFrame(ExtendedAssembler *assembler, Register pr
|
|
|
|
|
Register fp(X29);
|
|
|
|
|
Register sp(SP);
|
|
|
|
|
TempRegister2Scope temp2Scope(assembler);
|
|
|
|
|
__ Str(Register(X30), MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
__ CalleeSave();
|
|
|
|
|
__ Str(fp, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
__ Mov(fp, sp);
|
|
|
|
|
|
|
|
|
|
__ SaveFpAndLr();
|
|
|
|
|
Register frameType = __ TempRegister2();
|
|
|
|
|
// construct frame
|
|
|
|
|
__ Mov(frameType, Immediate(static_cast<int64_t>(FrameType::OPTIMIZED_ENTRY_FRAME)));
|
|
|
|
|
// 2 : 2 means pairs
|
|
|
|
|
__ Stp(prevFp, frameType, MemoryOperand(sp, -FRAME_SLOT_SIZE * 2, AddrMode::PREINDEX));
|
|
|
|
|
__ CalleeSave();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AssemblerStubs::PopAotEntryFrame(ExtendedAssembler *assembler, Register glue)
|
|
|
|
@ -1859,59 +1852,38 @@ void AssemblerStubs::PopAotEntryFrame(ExtendedAssembler *assembler, Register glu
|
|
|
|
|
Register fp(X29);
|
|
|
|
|
Register sp(SP);
|
|
|
|
|
Register prevFp(X1);
|
|
|
|
|
__ CalleeRestore();
|
|
|
|
|
|
|
|
|
|
// pop prevLeaveFrameFp to restore thread->currentFrame_
|
|
|
|
|
__ Ldr(prevFp, MemoryOperand(sp, FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
|
|
|
|
|
__ Str(prevFp, MemoryOperand(glue, JSThread::GlueData::GetLeaveFrameOffset(false)));
|
|
|
|
|
|
|
|
|
|
// pop entry frame type and c-fp
|
|
|
|
|
// pop entry frame type
|
|
|
|
|
__ Add(sp, sp, Immediate(FRAME_SLOT_SIZE));
|
|
|
|
|
__ Ldr(fp, MemoryOperand(sp, FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
|
|
|
|
|
|
|
|
|
|
__ CalleeRestore();
|
|
|
|
|
// restore return address
|
|
|
|
|
__ Ldr(Register(X30), MemoryOperand(sp, FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
|
|
|
|
|
__ RestoreFpAndLr();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AssemblerStubs::CallOptimizedJSFunction(ExtendedAssembler *assembler)
|
|
|
|
|
void AssemblerStubs::PushOptimizedJSFunctionFrame(ExtendedAssembler *assembler)
|
|
|
|
|
{
|
|
|
|
|
__ BindAssemblerStub(RTSTUB_ID(CallOptimizedJSFunction));
|
|
|
|
|
Register sp(SP);
|
|
|
|
|
Register glue(X0);
|
|
|
|
|
Register prevFp(X1);
|
|
|
|
|
Register jsfunc(X2);
|
|
|
|
|
Register actualNumArgs(X3);
|
|
|
|
|
Register thisObj(X4);
|
|
|
|
|
Register newTarget(X5);
|
|
|
|
|
Register arg0(X6);
|
|
|
|
|
Register arg1(X7);
|
|
|
|
|
Register codeAddr = __ AvailableRegister1();
|
|
|
|
|
Register expectedNumArgs(X19);
|
|
|
|
|
Label pushCallThis;
|
|
|
|
|
Register argV(prevFp);
|
|
|
|
|
// save arg0, arg1 to stack
|
|
|
|
|
// 2 : 2 means pairs
|
|
|
|
|
__ Stp(arg0, arg1, MemoryOperand(sp, -2 * FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
{
|
|
|
|
|
TempRegister1Scope temp1Scope(assembler);
|
|
|
|
|
Register tmp = __ TempRegister1();
|
|
|
|
|
__ Mov(tmp, sp);
|
|
|
|
|
PushAotEntryFrame(assembler, prevFp);
|
|
|
|
|
__ Mov(argV, tmp);
|
|
|
|
|
}
|
|
|
|
|
TempRegister2Scope temp2Scope(assembler);
|
|
|
|
|
Register frameType = __ TempRegister2();
|
|
|
|
|
__ SaveFpAndLr();
|
|
|
|
|
// construct frame
|
|
|
|
|
__ Mov(frameType, Immediate(static_cast<int64_t>(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME)));
|
|
|
|
|
// 2 : 2 means pairs. X19 means calleesave and 16bytes align
|
|
|
|
|
__ Stp(Register(X19), frameType, MemoryOperand(sp, -FRAME_SLOT_SIZE * 2, AddrMode::PREINDEX));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PushArgsWithArgV(assembler, jsfunc, actualNumArgs, argV, &pushCallThis);
|
|
|
|
|
__ Bind(&pushCallThis);
|
|
|
|
|
__ Add(expectedNumArgs, expectedNumArgs, Immediate(NUM_MANDATORY_JSFUNC_ARGS));
|
|
|
|
|
__ Add(actualNumArgs, actualNumArgs, Immediate(NUM_MANDATORY_JSFUNC_ARGS));
|
|
|
|
|
PushMandatoryJSArgs(assembler, jsfunc, thisObj, newTarget);
|
|
|
|
|
__ Str(actualNumArgs, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
|
|
|
|
|
__ Ldr(codeAddr, MemoryOperand(jsfunc, JSFunctionBase::CODE_ENTRY_OFFSET));
|
|
|
|
|
__ Blr(codeAddr); // then call jsFunction
|
|
|
|
|
PopAotArgs(assembler, expectedNumArgs);
|
|
|
|
|
PopAotEntryFrame(assembler, glue);
|
|
|
|
|
// pop arg0, arg1 from stack
|
|
|
|
|
__ Add(sp, sp, Immediate(2 * FRAME_SLOT_SIZE)); // 2 : 2 means pairs
|
|
|
|
|
__ Ret();
|
|
|
|
|
void AssemblerStubs::PopOptimizedJSFunctionFrame(ExtendedAssembler *assembler)
|
|
|
|
|
{
|
|
|
|
|
TempRegister2Scope temp2Scope(assembler);
|
|
|
|
|
Register sp(SP);
|
|
|
|
|
Register frameType = __ TempRegister2();
|
|
|
|
|
// 2 : 2 means pop call site sp and type
|
|
|
|
|
__ Ldp(Register(X19), frameType, MemoryOperand(sp, FRAME_SLOT_SIZE * 2, AddrMode::POSTINDEX));
|
|
|
|
|
__ RestoreFpAndLr();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AssemblerStubs::PushOptimizedFrame(ExtendedAssembler *assembler, Register callSiteSp)
|
|
|
|
@ -1928,9 +1900,7 @@ void AssemblerStubs::PushOptimizedFrame(ExtendedAssembler *assembler, Register c
|
|
|
|
|
|
|
|
|
|
void AssemblerStubs::PopOptimizedFrame(ExtendedAssembler *assembler)
|
|
|
|
|
{
|
|
|
|
|
Register fp(X29);
|
|
|
|
|
Register sp(SP);
|
|
|
|
|
Register prevFp(X1);
|
|
|
|
|
// 2 : 2 means pop call site sp and type
|
|
|
|
|
__ Add(sp, sp, Immediate(2 * FRAME_SLOT_SIZE));
|
|
|
|
|
__ RestoreFpAndLr();
|
|
|
|
@ -1952,10 +1922,12 @@ void AssemblerStubs::JSCallWithArgV(ExtendedAssembler *assembler)
|
|
|
|
|
__ Mov(callsiteSp, sp);
|
|
|
|
|
PushOptimizedFrame(assembler, callsiteSp);
|
|
|
|
|
__ Cbz(actualNumArgs, &pushCallThis);
|
|
|
|
|
TempRegister1Scope scope1(assembler);
|
|
|
|
|
Register tmp = __ TempRegister1();
|
|
|
|
|
__ Mov(tmp, actualNumArgs);
|
|
|
|
|
CopyArgumentWithArgV(assembler, tmp, argV);
|
|
|
|
|
{
|
|
|
|
|
TempRegister1Scope scope1(assembler);
|
|
|
|
|
Register tmp = __ TempRegister1();
|
|
|
|
|
__ Mov(tmp, actualNumArgs);
|
|
|
|
|
CopyArgumentWithArgV(assembler, tmp, argV);
|
|
|
|
|
}
|
|
|
|
|
__ Bind(&pushCallThis);
|
|
|
|
|
PushMandatoryJSArgs(assembler, jsfunc, thisObj, newTarget);
|
|
|
|
|
__ Add(actualNumArgs, actualNumArgs, Immediate(NUM_MANDATORY_JSFUNC_ARGS));
|
|
|
|
@ -1963,7 +1935,7 @@ void AssemblerStubs::JSCallWithArgV(ExtendedAssembler *assembler)
|
|
|
|
|
|
|
|
|
|
__ CallAssemblerStub(RTSTUB_ID(JSCall), false);
|
|
|
|
|
__ Ldr(actualNumArgs, MemoryOperand(sp, 0));
|
|
|
|
|
PopAotArgs(assembler, actualNumArgs);
|
|
|
|
|
PopAotArgs(assembler, actualNumArgs, actualNumArgs);
|
|
|
|
|
PopOptimizedFrame(assembler);
|
|
|
|
|
__ Ret();
|
|
|
|
|
}
|
|
|
|
|