escargot/src/runtime/ScriptSimpleFunctionObject.h
HyukWoo Park 023b7ea014 Implement general tail call optimization
Signed-off-by: HyukWoo Park <hyukwoo.park@samsung.com>
2024-02-20 17:16:10 +09:00

192 lines
7.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2022-present Samsung Electronics Co., Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#ifndef __EscargotScriptSimpleFunctionObject__
#define __EscargotScriptSimpleFunctionObject__
#include "runtime/ScriptFunctionObject.h"
#include "runtime/Environment.h"
#include "runtime/EnvironmentRecord.h"
#include "runtime/ErrorObject.h"
#include "runtime/VMInstance.h"
#include "interpreter/ByteCode.h"
#include "interpreter/ByteCodeGenerator.h"
#include "interpreter/ByteCodeInterpreter.h"
namespace Escargot {
// ScriptSimpleFunctionObject currently supports only 4 registerFileSize (4, 8, 16, 24)
template <bool isStrict = false, bool shouldClearStack = false, unsigned registerFileSize = 4>
class ScriptSimpleFunctionObject : public ScriptFunctionObject {
friend class Global;
protected:
ScriptSimpleFunctionObject() // ctor for reading tag
: ScriptFunctionObject()
{
}
virtual bool isScriptSimpleFunctionObject() const override
{
return true;
}
virtual Value call(ExecutionState& state, const Value& thisValue, const size_t argc, Value* argv) override
{
CHECK_STACK_OVERFLOW(state);
ASSERT(codeBlock()->isInterpretedCodeBlock());
InterpretedCodeBlock* codeBlock = interpretedCodeBlock();
// prepare ByteCodeBlock if needed
if (UNLIKELY(codeBlock->byteCodeBlock() == nullptr)) {
generateByteCodeBlock(state);
}
ByteCodeBlock* blk = codeBlock->byteCodeBlock();
Context* ctx = codeBlock->context();
const size_t registerSize = blk->m_requiredOperandRegisterNumber;
const size_t programStart = reinterpret_cast<const size_t>(blk->m_code.data());
#if !defined(NDEBUG)
const size_t stackStorageSize = codeBlock->totalStackAllocatedVariableSize();
const size_t literalStorageSize = blk->m_numeralLiteralData.size();
ASSERT(codeBlock->isStrict() == isStrict);
ASSERT(blk->m_requiredOperandRegisterNumber + stackStorageSize + literalStorageSize <= registerFileSize);
#endif
// prepare env, ec
ASSERT(codeBlock->canAllocateEnvironmentOnStack());
FunctionEnvironmentRecordOnStack<false, false> record(this);
LexicalEnvironment lexEnv(&record, outerEnvironment()
#ifndef NDEBUG
,
false
#endif
);
// keep ByteCodeBlock pointer in registerFileBuffer
char registerFileBuffer[sizeof(Value) * registerFileSize + sizeof(size_t)];
Value* registerFile = reinterpret_cast<Value*>(registerFileBuffer);
memcpy(registerFileBuffer + sizeof(Value) * registerFileSize, &blk, sizeof(size_t));
Value* stackStorage = registerFile + registerSize;
ExecutionState newState(ctx, &state, &lexEnv, argc, argv, isStrict);
if (isStrict) {
stackStorage[0] = thisValue;
} else {
if (thisValue.isUndefinedOrNull()) {
stackStorage[0] = newState.context()->globalObjectProxy();
} else {
stackStorage[0] = thisValue.toObject(newState);
}
}
#if defined(ENABLE_TCO)
const Value returnValue = Interpreter::interpret(&newState, blk, programStart, registerFile);
if (shouldClearStack) {
clearStack<512>();
}
if (UNLIKELY(newState.inTCO())) {
// callee has been called in tail call, so reset the argument buffer
memset(Interpreter::tcoBuffer, 0, sizeof(Value) * TCO_ARGUMENT_COUNT_LIMIT);
}
return returnValue;
#else
if (shouldClearStack) {
const Value returnValue = Interpreter::interpret(&newState, blk, programStart, registerFile);
clearStack<512>();
return returnValue;
} else {
return Interpreter::interpret(&newState, blk, programStart, registerFile);
}
#endif
}
virtual Value construct(ExecutionState& state, const size_t argc, Value* argv, Object* newTarget) override
{
// Assert: Type(newTarget) is Object.
ASSERT(newTarget->isObject());
ASSERT(newTarget->isConstructor());
// Let kind be Fs [[ConstructorKind]] internal slot.
ASSERT(constructorKind() == ConstructorKind::Base); // this is always `Base` because we define ScriptClassConsturctor::construct
CHECK_STACK_OVERFLOW(state);
// Let thisArgument be ? OrdinaryCreateFromConstructor(newTarget, "%ObjectPrototype%").
Object* proto = Object::getPrototypeFromConstructor(state, newTarget, [](ExecutionState& state, Context* constructorRealm) -> Object* {
return constructorRealm->globalObject()->objectPrototype();
});
// Set the [[Prototype]] internal slot of obj to proto.
Object* thisArgument = new Object(state, proto);
ASSERT(codeBlock()->isInterpretedCodeBlock());
InterpretedCodeBlock* codeBlock = interpretedCodeBlock();
// prepare ByteCodeBlock if needed
if (UNLIKELY(codeBlock->byteCodeBlock() == nullptr)) {
generateByteCodeBlock(state);
}
ByteCodeBlock* blk = codeBlock->byteCodeBlock();
Context* ctx = codeBlock->context();
const size_t registerSize = blk->m_requiredOperandRegisterNumber;
#if !defined(NDEBUG)
const size_t stackStorageSize = codeBlock->totalStackAllocatedVariableSize();
const size_t literalStorageSize = blk->m_numeralLiteralData.size();
ASSERT(codeBlock->isStrict() == isStrict);
ASSERT(blk->m_requiredOperandRegisterNumber + stackStorageSize + literalStorageSize <= registerFileSize);
#endif
// prepare env, ec
ASSERT(codeBlock->canAllocateEnvironmentOnStack());
FunctionEnvironmentRecordOnStack<false, true> record(this);
LexicalEnvironment lexEnv(&record, outerEnvironment()
#ifndef NDEBUG
,
false
#endif
);
// keep ByteCodeBlock pointer in registerFileBuffer
char registerFileBuffer[sizeof(Value) * registerFileSize + sizeof(size_t)];
Value* registerFile = reinterpret_cast<Value*>(registerFileBuffer);
memcpy(registerFileBuffer + sizeof(Value) * registerFileSize, &blk, sizeof(size_t));
Value* stackStorage = registerFile + registerSize;
ExecutionState newState(ctx, &state, &lexEnv, argc, argv, isStrict);
stackStorage[0] = thisArgument;
record.setNewTarget(newTarget);
const Value returnValue = Interpreter::interpret(&newState, blk, reinterpret_cast<const size_t>(blk->m_code.data()), registerFile);
if (shouldClearStack) {
clearStack<512>();
}
return returnValue.isObject() ? returnValue : thisArgument;
}
};
COMPILE_ASSERT(sizeof(ScriptSimpleFunctionObject<>) == sizeof(ScriptFunctionObject), "");
} // namespace Escargot
#endif