mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
Implement general tail call optimization
Signed-off-by: HyukWoo Park <hyukwoo.park@samsung.com>
This commit is contained in:
parent
65558531f8
commit
023b7ea014
27 changed files with 368 additions and 89 deletions
|
|
@ -568,6 +568,11 @@ typedef uint16_t LexicalBlockIndex;
|
|||
#define REGEXP_CACHE_SIZE_MAX 64
|
||||
#endif
|
||||
|
||||
// maximum number of tail call arguments allowed
|
||||
#ifndef TCO_ARGUMENT_COUNT_LIMIT
|
||||
#define TCO_ARGUMENT_COUNT_LIMIT 8
|
||||
#endif
|
||||
|
||||
#include <tsl/robin_set.h>
|
||||
template <class Key, class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ void CodeCacheWriter::storeByteCodeBlock(ByteCodeBlock* block)
|
|||
m_buffer.ensureSize(2 * sizeof(bool) + 2 * sizeof(uint16_t));
|
||||
m_buffer.put(block->m_shouldClearStack);
|
||||
m_buffer.put(block->m_isOwnerMayFreed);
|
||||
m_buffer.put(block->m_needsExtendedExectuionState);
|
||||
m_buffer.put(block->m_needsExtendedExecutionState);
|
||||
m_buffer.put((uint16_t)block->m_requiredOperandRegisterNumber);
|
||||
m_buffer.put((uint16_t)block->m_requiredTotalRegisterNumber);
|
||||
|
||||
|
|
@ -859,7 +859,7 @@ ByteCodeBlock* CodeCacheReader::loadByteCodeBlock(Context* context, InterpretedC
|
|||
|
||||
block->m_shouldClearStack = m_buffer.get<bool>();
|
||||
block->m_isOwnerMayFreed = m_buffer.get<bool>();
|
||||
block->m_needsExtendedExectuionState = m_buffer.get<bool>();
|
||||
block->m_needsExtendedExecutionState = m_buffer.get<bool>();
|
||||
block->m_requiredOperandRegisterNumber = m_buffer.get<uint16_t>();
|
||||
block->m_requiredTotalRegisterNumber = m_buffer.get<uint16_t>();
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ void SetGlobalVariable::dump()
|
|||
ByteCodeBlock::ByteCodeBlock()
|
||||
: m_shouldClearStack(false)
|
||||
, m_isOwnerMayFreed(false)
|
||||
, m_needsExtendedExectuionState(false)
|
||||
, m_needsExtendedExecutionState(false)
|
||||
, m_requiredOperandRegisterNumber(2)
|
||||
, m_requiredTotalRegisterNumber(0)
|
||||
, m_inlineCacheDataSize(0)
|
||||
|
|
@ -129,7 +129,7 @@ static void clearByteCodeBlock(ByteCodeBlock* self)
|
|||
ByteCodeBlock::ByteCodeBlock(InterpretedCodeBlock* codeBlock)
|
||||
: m_shouldClearStack(false)
|
||||
, m_isOwnerMayFreed(false)
|
||||
, m_needsExtendedExectuionState(false)
|
||||
, m_needsExtendedExecutionState(false)
|
||||
, m_requiredOperandRegisterNumber(2)
|
||||
, m_requiredTotalRegisterNumber(0)
|
||||
, m_inlineCacheDataSize(0)
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ struct GlobalVariableAccessCacheItem;
|
|||
#if defined(ENABLE_TCO)
|
||||
#define FOR_EACH_BYTECODE_TCO_OP(F) \
|
||||
F(CallReturn) \
|
||||
F(TailCall) \
|
||||
F(TailRecursion) \
|
||||
F(TailRecursionInTry)
|
||||
#else
|
||||
|
|
@ -2158,6 +2159,53 @@ public:
|
|||
#endif
|
||||
};
|
||||
|
||||
class TailCall : public ByteCode {
|
||||
public:
|
||||
TailCall(const ByteCodeLOC& loc, const size_t calleeIndex, const size_t argumentsStartIndex, const size_t argumentCount)
|
||||
: ByteCode(Opcode::TailCallOpcode, loc)
|
||||
, m_receiverIndex(REGISTER_LIMIT)
|
||||
, m_calleeIndex(calleeIndex)
|
||||
, m_argumentsStartIndex(argumentsStartIndex)
|
||||
, m_argumentCount(argumentCount)
|
||||
{
|
||||
// tail call without receiver
|
||||
}
|
||||
|
||||
TailCall(const ByteCodeLOC& loc, const size_t receiverIndex, const size_t calleeIndex, const size_t argumentsStartIndex, const size_t argumentCount)
|
||||
: ByteCode(Opcode::TailCallOpcode, loc)
|
||||
, m_receiverIndex(receiverIndex)
|
||||
, m_calleeIndex(calleeIndex)
|
||||
, m_argumentsStartIndex(argumentsStartIndex)
|
||||
, m_argumentCount(argumentCount)
|
||||
{
|
||||
// tail call with receiver
|
||||
}
|
||||
|
||||
ByteCodeRegisterIndex m_receiverIndex;
|
||||
ByteCodeRegisterIndex m_calleeIndex;
|
||||
ByteCodeRegisterIndex m_argumentsStartIndex;
|
||||
uint16_t m_argumentCount;
|
||||
|
||||
#ifndef NDEBUG
|
||||
void dump()
|
||||
{
|
||||
if (m_receiverIndex != REGISTER_LIMIT) {
|
||||
if (m_argumentCount) {
|
||||
printf("tail call r%u.r%u(r%u-r%u)", m_receiverIndex, m_calleeIndex, m_argumentsStartIndex, m_argumentsStartIndex + m_argumentCount);
|
||||
} else {
|
||||
printf("tail call r%u.r%u()", m_receiverIndex, m_calleeIndex);
|
||||
}
|
||||
} else {
|
||||
if (m_argumentCount) {
|
||||
printf("tail call r%u(r%u-r%u)", m_calleeIndex, m_argumentsStartIndex, m_argumentsStartIndex + m_argumentCount);
|
||||
} else {
|
||||
printf("tail call r%u()", m_calleeIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class TailRecursion : public ByteCode {
|
||||
public:
|
||||
TailRecursion(const ByteCodeLOC& loc, const size_t calleeIndex, const size_t argumentsStartIndex, const size_t argumentCount)
|
||||
|
|
@ -2167,7 +2215,7 @@ public:
|
|||
, m_argumentsStartIndex(argumentsStartIndex)
|
||||
, m_argumentCount(argumentCount)
|
||||
{
|
||||
// tail recursion call without receiver
|
||||
// tail recursion without receiver
|
||||
}
|
||||
|
||||
TailRecursion(const ByteCodeLOC& loc, const size_t receiverIndex, const size_t calleeIndex, const size_t argumentsStartIndex, const size_t argumentCount)
|
||||
|
|
@ -2177,7 +2225,7 @@ public:
|
|||
, m_argumentsStartIndex(argumentsStartIndex)
|
||||
, m_argumentCount(argumentCount)
|
||||
{
|
||||
// tail recursion call with receiver
|
||||
// tail recursion with receiver
|
||||
}
|
||||
|
||||
ByteCodeRegisterIndex m_receiverIndex;
|
||||
|
|
@ -2232,6 +2280,7 @@ public:
|
|||
#endif
|
||||
};
|
||||
|
||||
COMPILE_ASSERT(sizeof(CallReturn) == sizeof(TailCall), "");
|
||||
COMPILE_ASSERT(sizeof(CallReturn) == sizeof(TailRecursion), "");
|
||||
COMPILE_ASSERT(sizeof(Call) == sizeof(TailRecursionInTry), "");
|
||||
#endif
|
||||
|
|
@ -3242,7 +3291,7 @@ public:
|
|||
|
||||
bool needsExtendedExecutionState() const
|
||||
{
|
||||
return m_needsExtendedExectuionState;
|
||||
return m_needsExtendedExecutionState;
|
||||
}
|
||||
|
||||
ExtendedNodeLOC computeNodeLOCFromByteCode(Context* c, size_t codePosition, InterpretedCodeBlock* cb, ByteCodeLOCData* locData);
|
||||
|
|
@ -3251,7 +3300,7 @@ public:
|
|||
|
||||
bool m_shouldClearStack : 1;
|
||||
bool m_isOwnerMayFreed : 1;
|
||||
bool m_needsExtendedExectuionState : 1;
|
||||
bool m_needsExtendedExecutionState : 1;
|
||||
// number of bytecode registers used for bytecode operation like adding...moving...
|
||||
ByteCodeRegisterIndex m_requiredOperandRegisterNumber : REGISTER_INDEX_IN_BIT;
|
||||
// precomputed value of total register number which is "m_requiredTotalRegisterNumber + stack allocated variables size"
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ ByteCodeBlock* ByteCodeGenerator::generateByteCode(Context* context, Interpreted
|
|||
|
||||
block->m_code.shrinkToFit();
|
||||
block->m_requiredTotalRegisterNumber = block->m_requiredOperandRegisterNumber + codeBlock->totalStackAllocatedVariableSize() + block->m_numeralLiteralData.size();
|
||||
block->m_needsExtendedExectuionState = ctx.m_needsExtendedExecutionState;
|
||||
block->m_needsExtendedExecutionState = ctx.m_needsExtendedExecutionState;
|
||||
|
||||
#if defined(ENABLE_CODE_CACHE)
|
||||
// cache bytecode right before relocation
|
||||
|
|
@ -612,6 +612,13 @@ void ByteCodeGenerator::relocateByteCode(ByteCodeBlock* block)
|
|||
ASSIGN_STACKINDEX_IF_NEEDED(cd->m_argumentsStartIndex, stackBase, stackBaseWillBe, stackVariableSize);
|
||||
break;
|
||||
}
|
||||
case TailCallOpcode: {
|
||||
TailCall* cd = (TailCall*)currentCode;
|
||||
ASSIGN_STACKINDEX_IF_NEEDED(cd->m_receiverIndex, stackBase, stackBaseWillBe, stackVariableSize);
|
||||
ASSIGN_STACKINDEX_IF_NEEDED(cd->m_calleeIndex, stackBase, stackBaseWillBe, stackVariableSize);
|
||||
ASSIGN_STACKINDEX_IF_NEEDED(cd->m_argumentsStartIndex, stackBase, stackBaseWillBe, stackVariableSize);
|
||||
break;
|
||||
}
|
||||
case TailRecursionOpcode: {
|
||||
TailRecursion* cd = (TailRecursion*)currentCode;
|
||||
ASSIGN_STACKINDEX_IF_NEEDED(cd->m_receiverIndex, stackBase, stackBaseWillBe, stackVariableSize);
|
||||
|
|
|
|||
|
|
@ -51,6 +51,20 @@
|
|||
#include "parser/ScriptParser.h"
|
||||
#include "CheckedArithmetic.h"
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
#include "runtime/FunctionObjectInlines.h"
|
||||
|
||||
namespace Escargot {
|
||||
MAY_THREAD_LOCAL Value* Interpreter::tcoBuffer;
|
||||
|
||||
void Interpreter::initTCOBuffer()
|
||||
{
|
||||
ASSERT(!Interpreter::tcoBuffer);
|
||||
Interpreter::tcoBuffer = (Value*)GC_MALLOC_UNCOLLECTABLE(sizeof(Value) * TCO_ARGUMENT_COUNT_LIMIT);
|
||||
}
|
||||
} // namespace Escargot
|
||||
#endif
|
||||
|
||||
#if defined(ESCARGOT_COMPUTED_GOTO_INTERPRETER) && !defined(ESCARGOT_COMPUTED_GOTO_INTERPRETER_INIT_WITH_NULL)
|
||||
extern char FillOpcodeTableAsmLbl[];
|
||||
const void* FillOpcodeTableAddress[] = { &FillOpcodeTableAsmLbl[0] };
|
||||
|
|
@ -191,7 +205,9 @@ public:
|
|||
static int evaluateImportAssertionOperation(ExecutionState& state, const Value& options);
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
static Value tailRecursionSlowCase(ExecutionState& state, TailRecursion* code, const Value& callee, const Value& receiver, Value* registerFile);
|
||||
static Value tailRecursionSlowCase(ExecutionState& state, TailRecursion* code, ByteCodeBlock* byteCodeBlock, const Value& callee, Value* registerFile);
|
||||
static Value prepareTailCallOptimization(ExecutionState* state, TailCall* code, ScriptFunctionObject* callee, ByteCodeBlock*& callerBlock, size_t& programCounter, const Value* registerFile);
|
||||
static Value tailCallSlowCase(ExecutionState& state, TailCall* code, const Value& callee, Value* registerFile);
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
|
@ -1525,33 +1541,31 @@ Value Interpreter::interpret(ExecutionState* state, ByteCodeBlock* byteCodeBlock
|
|||
}
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
// TCO : tail recursion case
|
||||
// Tail recursion
|
||||
DEFINE_OPCODE(TailRecursion)
|
||||
:
|
||||
{
|
||||
TailRecursion* code = (TailRecursion*)programCounter;
|
||||
const Value& callee = registerFile[code->m_calleeIndex];
|
||||
const Value& receiver = (code->m_receiverIndex == REGISTER_LIMIT) ? Value() : registerFile[code->m_receiverIndex];
|
||||
|
||||
if (UNLIKELY(callee != Value(state->lexicalEnvironment()->record()->asDeclarativeEnvironmentRecord()->asFunctionEnvironmentRecord()->functionObject()))) {
|
||||
// goto slow path
|
||||
return InterpreterSlowPath::tailRecursionSlowCase(*state, code, callee, receiver, registerFile);
|
||||
return InterpreterSlowPath::tailRecursionSlowCase(*state, code, byteCodeBlock, callee, registerFile);
|
||||
}
|
||||
|
||||
if (UNLIKELY(!state->initTCO())) {
|
||||
if (UNLIKELY(!state->inTCO())) {
|
||||
// At the start of tail call, we need to allocate a buffer for arguments
|
||||
// because recursive tail call reuses this buffer
|
||||
state->m_argc = code->m_argumentCount;
|
||||
Value* newArgs = code->m_argumentCount ? ALLOCA(sizeof(Value) * code->m_argumentCount, Value) : nullptr;
|
||||
state->setTCOArguments(newArgs);
|
||||
state->initTCOWithBuffer(Interpreter::tcoBuffer);
|
||||
}
|
||||
state->m_argc = code->m_argumentCount;
|
||||
|
||||
// fast tail recursion
|
||||
ASSERT(callee.isPointerValue() && callee.asPointerValue()->isScriptFunctionObject());
|
||||
ASSERT(callee.asPointerValue()->asScriptFunctionObject()->codeBlock() == byteCodeBlock->codeBlock());
|
||||
ASSERT(state->initTCO() && (state->m_argc == code->m_argumentCount));
|
||||
ASSERT(state->inTCO() && (state->m_argc <= TCO_ARGUMENT_COUNT_LIMIT));
|
||||
#ifndef NDEBUG
|
||||
// check this value for call without receiver
|
||||
// check this value
|
||||
if (code->m_receiverIndex == REGISTER_LIMIT) {
|
||||
if (state->inStrictMode()) {
|
||||
ASSERT(registerFile[byteCodeBlock->m_requiredOperandRegisterNumber].isUndefined());
|
||||
|
|
@ -1566,8 +1580,8 @@ Value Interpreter::interpret(ExecutionState* state, ByteCodeBlock* byteCodeBlock
|
|||
state->m_argv[i] = registerFile[code->m_argumentsStartIndex + i];
|
||||
}
|
||||
|
||||
// set this value (receiver)
|
||||
if (code->m_receiverIndex != REGISTER_LIMIT) {
|
||||
const Value& receiver = registerFile[code->m_receiverIndex];
|
||||
if (state->inStrictMode()) {
|
||||
registerFile[byteCodeBlock->m_requiredOperandRegisterNumber] = receiver;
|
||||
} else {
|
||||
|
|
@ -1586,6 +1600,49 @@ Value Interpreter::interpret(ExecutionState* state, ByteCodeBlock* byteCodeBlock
|
|||
NEXT_INSTRUCTION();
|
||||
}
|
||||
|
||||
// TCO : general tail call optimization
|
||||
DEFINE_OPCODE(TailCall)
|
||||
:
|
||||
{
|
||||
TailCall* code = (TailCall*)programCounter;
|
||||
ASSERT(state->lexicalEnvironment()->record()->asDeclarativeEnvironmentRecord()->isFunctionEnvironmentRecord());
|
||||
ASSERT(byteCodeBlock->m_codeBlock->isTailCallTarget(code->m_argumentCount));
|
||||
ASSERT(code->m_argumentCount <= TCO_ARGUMENT_COUNT_LIMIT);
|
||||
|
||||
const Value& calleeValue = registerFile[code->m_calleeIndex];
|
||||
|
||||
if (calleeValue.isPointerValue() && calleeValue.asPointerValue()->canBeTailCallTargetRuntime(code->m_argumentCount)) {
|
||||
Value thisValue = InterpreterSlowPath::prepareTailCallOptimization(state, code, calleeValue.asPointerValue()->asScriptFunctionObject(), byteCodeBlock, programCounter, registerFile);
|
||||
if (!thisValue.isEmpty()) {
|
||||
ASSERT(byteCodeBlock == calleeValue.asPointerValue()->asScriptFunctionObject()->interpretedCodeBlock()->byteCodeBlock());
|
||||
ASSERT(programCounter == (size_t)byteCodeBlock->m_code.data());
|
||||
ASSERT(state->m_programCounter == &programCounter);
|
||||
|
||||
if (UNLIKELY(!state->lexicalEnvironment())) {
|
||||
// should allocate environment stuctures on the stack
|
||||
ScriptFunctionObject* callee = calleeValue.asPointerValue()->asScriptFunctionObject();
|
||||
FunctionEnvironmentRecord* record = new (alloca(sizeof(FunctionEnvironmentRecordOnStack<false, false>))) FunctionEnvironmentRecordOnStack<false, false>(callee);
|
||||
LexicalEnvironment* lexEnv = new (alloca(sizeof(LexicalEnvironment))) LexicalEnvironment(record, callee->outerEnvironment()
|
||||
#ifndef NDEBUG
|
||||
,
|
||||
false
|
||||
#endif
|
||||
);
|
||||
state->m_lexicalEnvironment = lexEnv;
|
||||
}
|
||||
|
||||
// set this value
|
||||
registerFile[byteCodeBlock->m_requiredOperandRegisterNumber] = thisValue;
|
||||
|
||||
// directly jump to the first bytecode of callee
|
||||
NEXT_INSTRUCTION();
|
||||
}
|
||||
}
|
||||
|
||||
// goto slow path
|
||||
return InterpreterSlowPath::tailCallSlowCase(*state, code, calleeValue, registerFile);
|
||||
}
|
||||
|
||||
// TCO : tail recursion case in catch or finally block
|
||||
DEFINE_OPCODE(TailRecursionInTry)
|
||||
:
|
||||
|
|
@ -3378,11 +3435,11 @@ NEVER_INLINE Value InterpreterSlowPath::tryOperation(ExecutionState*& state, siz
|
|||
size_t argStartIndex = record->m_outerLimitCount;
|
||||
// At the start of tail call, we need to allocate a buffer for arguments
|
||||
// because recursive tail call reuses this buffer
|
||||
if (UNLIKELY(!state->initTCO())) {
|
||||
state->m_argc = argCount;
|
||||
Value* newArgs = argCount ? (Value*)GC_MALLOC(sizeof(Value) * argCount) : nullptr;
|
||||
state->setTCOArguments(newArgs);
|
||||
if (UNLIKELY(!state->inTCO())) {
|
||||
ASSERT(Interpreter::tcoBuffer);
|
||||
state->initTCOWithBuffer(Interpreter::tcoBuffer);
|
||||
}
|
||||
state->m_argc = argCount;
|
||||
|
||||
// its safe to overwrite arguments because old arguments are no longer necessary
|
||||
ASSERT(state->m_argc == argCount);
|
||||
|
|
@ -4918,9 +4975,114 @@ NEVER_INLINE int InterpreterSlowPath::evaluateImportAssertionOperation(Execution
|
|||
}
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
NEVER_INLINE Value InterpreterSlowPath::tailRecursionSlowCase(ExecutionState& state, TailRecursion* code, const Value& callee, const Value& receiver, Value* registerFile)
|
||||
NEVER_INLINE Value InterpreterSlowPath::tailRecursionSlowCase(ExecutionState& state, TailRecursion* code, ByteCodeBlock* byteCodeBlock, const Value& callee, Value* registerFile)
|
||||
{
|
||||
// fail to tail recursion
|
||||
// fix the caller's call site to TailCall
|
||||
code->changeOpcode(Opcode::TailCallOpcode);
|
||||
byteCodeBlock->codeBlock()->disableTailRecursion();
|
||||
|
||||
// if PointerValue is not callable, PointerValue::call function throws builtin error
|
||||
// https://www.ecma-international.org/ecma-262/6.0/#sec-call
|
||||
// If IsCallable(F) is false, throw a TypeError exception.
|
||||
if (UNLIKELY(!callee.isPointerValue())) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::NOT_Callable);
|
||||
}
|
||||
|
||||
const Value& receiver = (code->m_receiverIndex == REGISTER_LIMIT) ? Value() : registerFile[code->m_receiverIndex];
|
||||
return callee.asPointerValue()->call(state, receiver, code->m_argumentCount, ®isterFile[code->m_argumentsStartIndex]);
|
||||
}
|
||||
|
||||
NEVER_INLINE Value InterpreterSlowPath::prepareTailCallOptimization(ExecutionState* state, TailCall* code, ScriptFunctionObject* callee, ByteCodeBlock*& callerByteBlock, size_t& programCounter, const Value* registerFile)
|
||||
{
|
||||
ASSERT(!callee->isScriptArrowFunctionObject() && !!callerByteBlock);
|
||||
ASSERT(state->m_programCounter == &programCounter);
|
||||
ASSERT(Interpreter::tcoBuffer);
|
||||
ASSERT(code->m_argumentCount <= TCO_ARGUMENT_COUNT_LIMIT);
|
||||
|
||||
InterpretedCodeBlock* calleeBlock = callee->interpretedCodeBlock();
|
||||
if (!calleeBlock->byteCodeBlock()) {
|
||||
// if callee doesn't have ByteCode yet, generate it
|
||||
callee->generateByteCodeBlock(*state);
|
||||
}
|
||||
|
||||
ByteCodeBlock* calleeByteBlock = calleeBlock->byteCodeBlock();
|
||||
if (!calleeByteBlock->needsExtendedExecutionState() && (callerByteBlock->m_requiredTotalRegisterNumber >= calleeByteBlock->m_requiredTotalRegisterNumber)) {
|
||||
// Note) any element of registerFile should not be modified in this function
|
||||
|
||||
const Value& receiver = (code->m_receiverIndex == REGISTER_LIMIT) ? Value() : registerFile[code->m_receiverIndex];
|
||||
Context* context = calleeBlock->context();
|
||||
bool isStrict = calleeBlock->isStrict();
|
||||
bool isTailRecursion = (calleeByteBlock == callerByteBlock) && (!callerByteBlock->codeBlock()->isTailRecursionDisabled());
|
||||
|
||||
// tail recursion reuses environment structures
|
||||
if (isTailRecursion) {
|
||||
// convert to fast tail recursion
|
||||
code->changeOpcode(Opcode::TailRecursionOpcode);
|
||||
} else {
|
||||
FunctionEnvironmentRecord* record = nullptr;
|
||||
LexicalEnvironment* lexEnv = nullptr;
|
||||
if (!calleeBlock->canAllocateEnvironmentOnStack()) {
|
||||
// cannot reuse environment structures
|
||||
// should create new environments
|
||||
ASSERT(!callee->isScriptSimpleFunctionObject());
|
||||
record = FunctionObjectProcessCallGenerator::createFunctionEnvironmentRecord<ScriptFunctionObject, false, false>(*state, callee, calleeBlock);
|
||||
lexEnv = new LexicalEnvironment(record, callee->outerEnvironment());
|
||||
} else if (callerByteBlock->codeBlock()->canAllocateEnvironmentOnStack()) {
|
||||
// reuse caller's environment structures
|
||||
ASSERT(state->lexicalEnvironment()->record()->asDeclarativeEnvironmentRecord()->asFunctionEnvironmentRecord()->isFunctionEnvironmentRecordOnStack());
|
||||
record = new (state->lexicalEnvironment()->record()) FunctionEnvironmentRecordOnStack<false, false>(callee);
|
||||
lexEnv = new (state->lexicalEnvironment()) LexicalEnvironment(record, callee->outerEnvironment()
|
||||
#ifndef NDEBUG
|
||||
,
|
||||
false
|
||||
#endif
|
||||
);
|
||||
}
|
||||
// other case, environment structures need to be newly allocated on the stack using alloca method
|
||||
// this will be handled right after this slow path
|
||||
|
||||
ExecutionState* newState = new (state) ExecutionState(context, state->parent(), lexEnv, 0, nullptr, isStrict);
|
||||
newState->m_programCounter = &programCounter;
|
||||
ASSERT(state == newState);
|
||||
}
|
||||
|
||||
|
||||
if (!state->inTCO()) {
|
||||
// At the start of tail call, we need to set a buffer for arguments
|
||||
// because tail call reuses this buffer
|
||||
state->initTCOWithBuffer(Interpreter::tcoBuffer);
|
||||
}
|
||||
state->m_argc = code->m_argumentCount;
|
||||
|
||||
// rewrite arguments info on ExecutionState
|
||||
for (size_t i = 0; i < code->m_argumentCount; i++) {
|
||||
state->m_argv[i] = registerFile[code->m_argumentsStartIndex + i];
|
||||
}
|
||||
|
||||
// get this value
|
||||
Value thisValue;
|
||||
if (code->m_receiverIndex == REGISTER_LIMIT) {
|
||||
thisValue = isStrict ? Value() : context->globalObjectProxy();
|
||||
} else {
|
||||
thisValue = isStrict ? receiver : (receiver.isUndefinedOrNull() ? context->globalObjectProxy() : receiver.toObject(*state));
|
||||
}
|
||||
|
||||
// rewrite call environment
|
||||
ASSERT(state->m_programCounter == &programCounter);
|
||||
callerByteBlock = calleeByteBlock;
|
||||
programCounter = reinterpret_cast<size_t>(calleeByteBlock->m_code.data());
|
||||
|
||||
return thisValue;
|
||||
}
|
||||
|
||||
// empty value represents invalid tail call
|
||||
return Value(Value::EmptyValue);
|
||||
}
|
||||
|
||||
NEVER_INLINE Value InterpreterSlowPath::tailCallSlowCase(ExecutionState& state, TailCall* code, const Value& callee, Value* registerFile)
|
||||
{
|
||||
// fail to tail Call
|
||||
// convert to CallReturn
|
||||
code->changeOpcode(Opcode::CallReturnOpcode);
|
||||
|
||||
|
|
@ -4931,6 +5093,7 @@ NEVER_INLINE Value InterpreterSlowPath::tailRecursionSlowCase(ExecutionState& st
|
|||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::NOT_Callable);
|
||||
}
|
||||
|
||||
const Value& receiver = (code->m_receiverIndex == REGISTER_LIMIT) ? Value() : registerFile[code->m_receiverIndex];
|
||||
return callee.asPointerValue()->call(state, receiver, code->m_argumentCount, ®isterFile[code->m_argumentsStartIndex]);
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -40,6 +40,12 @@ public:
|
|||
};
|
||||
|
||||
static Value interpret(ExecutionState* state, ByteCodeBlock* byteCodeBlock, size_t programCounter, Value* registerFile);
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
static void initTCOBuffer();
|
||||
|
||||
static MAY_THREAD_LOCAL Value* tcoBuffer;
|
||||
#endif
|
||||
};
|
||||
} // namespace Escargot
|
||||
|
||||
|
|
|
|||
|
|
@ -289,6 +289,9 @@ InterpretedCodeBlock::InterpretedCodeBlock(Context* ctx, Script* script)
|
|||
, m_allowSuperProperty(false)
|
||||
, m_allowArguments(false)
|
||||
, m_hasDynamicSourceCode(false)
|
||||
#if defined(ENABLE_TCO)
|
||||
, m_isTailRecursionDisabled(false)
|
||||
#endif
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
, m_markDebugging(ctx->inDebuggingCodeMode())
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -742,25 +742,17 @@ public:
|
|||
}
|
||||
|
||||
// for TCO
|
||||
bool isTailRecursionTarget(size_t argc, const AtomicString& calleeName) const
|
||||
bool isTailCallTarget(size_t argc) const
|
||||
{
|
||||
// global scope cannot create a return statement, neither tail recursion
|
||||
// global scope cannot create a return statement, neither tail call
|
||||
ASSERT(!isGlobalScope());
|
||||
|
||||
// check argc
|
||||
if (m_parameterCount != argc) {
|
||||
if (argc > TCO_ARGUMENT_COUNT_LIMIT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef ESCARGOT_ENABLE_TEST
|
||||
// check callee name
|
||||
// this check is disabled in test build to pass test262 tco-related test cases
|
||||
if (m_functionName.string()->length() && m_functionName != calleeName) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return (!m_canAllocateVariablesOnStack || m_isArrowFunctionExpression || m_isClassConstructor || m_isDerivedClassConstructor || m_isClassMethod || m_isClassStaticMethod || m_isGenerator || m_isAsync || m_usesArgumentsObject) != true;
|
||||
// skip arrow functions because arrow functions are rarely invoked in tail call
|
||||
return m_canAllocateVariablesOnStack && !m_isArrowFunctionExpression && !m_isClassConstructor && !m_isDerivedClassConstructor && !m_isClassMethod && !m_isClassStaticMethod && !m_isGenerator && !m_isAsync && !m_usesArgumentsObject;
|
||||
}
|
||||
|
||||
bool usesArgumentsObject() const
|
||||
|
|
@ -803,6 +795,18 @@ public:
|
|||
m_hasDynamicSourceCode = true;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
bool isTailRecursionDisabled() const
|
||||
{
|
||||
return m_isTailRecursionDisabled;
|
||||
}
|
||||
|
||||
void disableTailRecursion()
|
||||
{
|
||||
m_isTailRecursionDisabled = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
bool markDebugging() const
|
||||
{
|
||||
|
|
@ -1008,6 +1012,9 @@ protected:
|
|||
bool m_allowArguments : 1;
|
||||
// represent if its source code is created dynamically by createDynamicFunctionScript
|
||||
bool m_hasDynamicSourceCode : 1;
|
||||
#if defined(ENABLE_TCO)
|
||||
bool m_isTailRecursionDisabled : 1;
|
||||
#endif
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
// mark that this InterpretedCodeBlock should generate debugging bytecode (breakpoint)
|
||||
bool m_markDebugging : 1;
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ public:
|
|||
}
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCall) override
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCallForm) override
|
||||
{
|
||||
bool isSlow = !canUseDirectRegister(context, m_left, m_right);
|
||||
bool directBefore = context->m_canSkipCopyToRegister;
|
||||
|
|
@ -73,9 +73,9 @@ public:
|
|||
if (UNLIKELY(m_left->isLiteral())) {
|
||||
bool boolVal = m_left->asLiteral()->value().toBoolean();
|
||||
if (boolVal) {
|
||||
m_right->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCall);
|
||||
m_right->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCallForm);
|
||||
} else {
|
||||
m_left->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCall);
|
||||
m_left->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCallForm);
|
||||
}
|
||||
} else {
|
||||
m_left->generateExpressionByteCode(codeBlock, context, dstRegister);
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ public:
|
|||
}
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCall) override
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCallForm) override
|
||||
{
|
||||
bool isSlow = !canUseDirectRegister(context, m_left, m_right);
|
||||
bool directBefore = context->m_canSkipCopyToRegister;
|
||||
|
|
@ -73,9 +73,9 @@ public:
|
|||
if (UNLIKELY(m_left->isLiteral())) {
|
||||
bool boolVal = m_left->asLiteral()->value().toBoolean();
|
||||
if (boolVal) {
|
||||
m_left->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCall);
|
||||
m_left->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCallForm);
|
||||
} else {
|
||||
m_right->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCall);
|
||||
m_right->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCallForm);
|
||||
}
|
||||
} else {
|
||||
m_left->generateExpressionByteCode(codeBlock, context, dstRegister);
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public:
|
|||
}
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCall) override
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCallForm) override
|
||||
{
|
||||
bool isSlow = !canUseDirectRegister(context, m_left, m_right);
|
||||
bool directBefore = context->m_canSkipCopyToRegister;
|
||||
|
|
@ -68,7 +68,7 @@ public:
|
|||
size_t pos = codeBlock->lastCodePosition<JumpIfUndefinedOrNull>();
|
||||
|
||||
// try TCO only for right hand-side because we need to check the result of left hand-side expression
|
||||
m_right->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCall);
|
||||
m_right->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCallForm);
|
||||
codeBlock->peekCode<JumpIfUndefinedOrNull>(pos)->m_jumpPosition = codeBlock->currentCodeSize();
|
||||
|
||||
context->m_canSkipCopyToRegister = directBefore;
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ public:
|
|||
}
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCall) override
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCallForm) override
|
||||
{
|
||||
if (m_callee->isIdentifier() && m_callee->asIdentifier()->name().string()->equals("eval")) {
|
||||
ByteCodeRegisterIndex evalIndex = context->getRegister();
|
||||
|
|
@ -415,15 +415,15 @@ public:
|
|||
context, this->m_loc.index);
|
||||
} else if (isCalleeHasReceiver) {
|
||||
if (dstRegister == context->m_returnRegister) {
|
||||
// Try tail recursion optimization (TCO)
|
||||
isTailCall = true;
|
||||
// try tail call optimization (TCO)
|
||||
isTailCallForm = true;
|
||||
const AtomicString& calleeName = m_callee->asMemberExpression()->property()->isIdentifier() ? m_callee->asMemberExpression()->property()->asIdentifier()->name() : AtomicString();
|
||||
bool isTailRecursion = context->m_codeBlock->isTailRecursionTarget(m_arguments.size(), calleeName);
|
||||
if (UNLIKELY(context->tryCatchWithBlockStatementCount())) {
|
||||
codeBlock->pushCode(CallWithReceiver(ByteCodeLOC(m_loc.index), receiverIndex, calleeIndex, argumentsStartIndex, dstRegister, m_arguments.size()), context, this->m_loc.index);
|
||||
} else {
|
||||
if (isTailRecursion) {
|
||||
codeBlock->pushCode(TailRecursion(ByteCodeLOC(m_loc.index), receiverIndex, calleeIndex, argumentsStartIndex, m_arguments.size()), context, this->m_loc.index);
|
||||
if (context->m_codeBlock->isTailCallTarget(m_arguments.size())) {
|
||||
// tail call
|
||||
codeBlock->pushCode(TailCall(ByteCodeLOC(m_loc.index), receiverIndex, calleeIndex, argumentsStartIndex, m_arguments.size()), context, this->m_loc.index);
|
||||
} else {
|
||||
codeBlock->pushCode(CallReturn(ByteCodeLOC(m_loc.index), receiverIndex, calleeIndex, argumentsStartIndex, m_arguments.size()), context, this->m_loc.index);
|
||||
}
|
||||
|
|
@ -433,19 +433,21 @@ public:
|
|||
}
|
||||
} else {
|
||||
if (dstRegister == context->m_returnRegister) {
|
||||
// Try tail recursion optimization (TCO)
|
||||
isTailCall = true;
|
||||
// try tail call optimization (TCO)
|
||||
isTailCallForm = true;
|
||||
const AtomicString& calleeName = m_callee->isIdentifier() ? m_callee->asIdentifier()->name() : AtomicString();
|
||||
bool isTailRecursion = context->m_codeBlock->isTailRecursionTarget(m_arguments.size(), calleeName);
|
||||
bool isTailCallTarget = context->m_codeBlock->isTailCallTarget(m_arguments.size());
|
||||
if (UNLIKELY(context->tryCatchWithBlockStatementCount())) {
|
||||
if (isTailRecursion) {
|
||||
if (isTailCallTarget) {
|
||||
// try tail recursion
|
||||
codeBlock->pushCode(TailRecursionInTry(ByteCodeLOC(m_loc.index), calleeIndex, argumentsStartIndex, dstRegister, m_arguments.size()), context, this->m_loc.index);
|
||||
} else {
|
||||
codeBlock->pushCode(Call(ByteCodeLOC(m_loc.index), calleeIndex, argumentsStartIndex, dstRegister, m_arguments.size()), context, this->m_loc.index);
|
||||
}
|
||||
} else {
|
||||
if (isTailRecursion) {
|
||||
codeBlock->pushCode(TailRecursion(ByteCodeLOC(m_loc.index), calleeIndex, argumentsStartIndex, m_arguments.size()), context, this->m_loc.index);
|
||||
if (isTailCallTarget) {
|
||||
// tail call
|
||||
codeBlock->pushCode(TailCall(ByteCodeLOC(m_loc.index), calleeIndex, argumentsStartIndex, m_arguments.size()), context, this->m_loc.index);
|
||||
} else {
|
||||
codeBlock->pushCode(CallReturn(ByteCodeLOC(m_loc.index), calleeIndex, argumentsStartIndex, m_arguments.size()), context, this->m_loc.index);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ public:
|
|||
}
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCall) override
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCallForm) override
|
||||
{
|
||||
size_t testReg = m_test->getRegister(codeBlock, context);
|
||||
m_test->generateExpressionByteCode(codeBlock, context, testReg);
|
||||
|
|
@ -68,13 +68,13 @@ public:
|
|||
context->giveUpRegister();
|
||||
|
||||
size_t jumpPosForTestIsFalse = codeBlock->lastCodePosition<JumpIfFalse>();
|
||||
m_consequente->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCall);
|
||||
m_consequente->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCallForm);
|
||||
codeBlock->pushCode(Jump(ByteCodeLOC(m_loc.index), SIZE_MAX), context, this->m_loc.index);
|
||||
JumpIfFalse* jumpForTestIsFalse = codeBlock->peekCode<JumpIfFalse>(jumpPosForTestIsFalse);
|
||||
size_t jumpPosForEndOfConsequence = codeBlock->lastCodePosition<Jump>();
|
||||
|
||||
jumpForTestIsFalse->m_jumpPosition = codeBlock->currentCodeSize();
|
||||
m_alternate->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCall);
|
||||
m_alternate->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCallForm);
|
||||
|
||||
Jump* jumpForEndOfConsequence = codeBlock->peekCode<Jump>(jumpPosForEndOfConsequence);
|
||||
jumpForEndOfConsequence->m_jumpPosition = codeBlock->currentCodeSize();
|
||||
|
|
|
|||
|
|
@ -316,9 +316,9 @@ public:
|
|||
}
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCall)
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCallForm)
|
||||
{
|
||||
UNUSED_PARAMETER(isTailCall);
|
||||
UNUSED_PARAMETER(isTailCallForm);
|
||||
generateExpressionByteCode(codeBlock, context, dstRegister);
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -54,9 +54,9 @@ public:
|
|||
#if defined(ENABLE_TCO)
|
||||
if (!context->m_tcoDisabled && (context->tryCatchWithBlockStatementCount() == 1) && ((context->m_recursiveStatementStack.back().first == ByteCodeGenerateContext::Catch) || (context->m_recursiveStatementStack.back().first == ByteCodeGenerateContext::Finally))) {
|
||||
// consider tail recursion (TCO) for catch, finally block within depth 1
|
||||
bool isTailCall = false;
|
||||
bool isTailCallForm = false;
|
||||
context->setReturnRegister(index);
|
||||
m_argument->generateTCOExpressionByteCode(codeBlock, context, index, isTailCall);
|
||||
m_argument->generateTCOExpressionByteCode(codeBlock, context, index, isTailCallForm);
|
||||
context->setReturnRegister(SIZE_MAX);
|
||||
} else {
|
||||
m_argument->generateExpressionByteCode(codeBlock, context, index);
|
||||
|
|
@ -74,7 +74,7 @@ public:
|
|||
}
|
||||
context->giveUpRegister();
|
||||
} else {
|
||||
bool isTailCall = false;
|
||||
bool isTailCallForm = false;
|
||||
size_t r;
|
||||
if (m_argument) {
|
||||
r = m_argument->getRegister(codeBlock, context);
|
||||
|
|
@ -83,7 +83,7 @@ public:
|
|||
// consider tail recursion (TCO)
|
||||
ASSERT(!context->m_tcoDisabled);
|
||||
context->setReturnRegister(r);
|
||||
m_argument->generateTCOExpressionByteCode(codeBlock, context, r, isTailCall);
|
||||
m_argument->generateTCOExpressionByteCode(codeBlock, context, r, isTailCallForm);
|
||||
context->setReturnRegister(SIZE_MAX);
|
||||
#else
|
||||
m_argument->generateExpressionByteCode(codeBlock, context, r);
|
||||
|
|
@ -94,7 +94,7 @@ public:
|
|||
}
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
if (!isTailCall || (m_argument->type() != CallExpression))
|
||||
if (!isTailCallForm || (m_argument->type() != CallExpression))
|
||||
// skip End bytecode only if it directly returns the result of tail call
|
||||
#endif
|
||||
codeBlock->pushCode(End(ByteCodeLOC(m_loc.index), r), context, this->m_loc.index);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public:
|
|||
}
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCall) override
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCallForm) override
|
||||
{
|
||||
ASSERT(m_expressions.size());
|
||||
ByteCodeRegisterIndex r = 0;
|
||||
|
|
@ -59,7 +59,7 @@ public:
|
|||
}
|
||||
|
||||
// directly store the result of the last expression on to dstRegister
|
||||
m_expressions.back()->astNode()->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCall);
|
||||
m_expressions.back()->astNode()->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCallForm);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -53,9 +53,9 @@ public:
|
|||
}
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCall) override
|
||||
virtual void generateTCOExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister, bool& isTailCallForm) override
|
||||
{
|
||||
return m_convertedExpression->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCall);
|
||||
return m_convertedExpression->generateTCOExpressionByteCode(codeBlock, context, dstRegister, isTailCallForm);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -4839,7 +4839,6 @@ public:
|
|||
template <class ASTBuilder>
|
||||
ASTNode parseDebuggerStatement(ASTBuilder& builder)
|
||||
{
|
||||
ESCARGOT_LOG_ERROR("debugger keyword is not supported yet");
|
||||
MetaNode node = this->createNode();
|
||||
this->expectKeyword(KeywordKind::DebuggerKeyword);
|
||||
this->consumeSemicolon();
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ ExecutionState::ExecutionState()
|
|||
, m_onFinally(false)
|
||||
#endif
|
||||
#if defined(ENABLE_TCO)
|
||||
, m_initTCO(false)
|
||||
, m_inTCO(false)
|
||||
#endif
|
||||
, m_argc(0)
|
||||
, m_argv(nullptr)
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ public:
|
|||
, m_onFinally(false)
|
||||
#endif
|
||||
#if defined(ENABLE_TCO)
|
||||
, m_initTCO(false)
|
||||
, m_inTCO(false)
|
||||
#endif
|
||||
, m_argc(parent->argc())
|
||||
, m_argv(parent->argv())
|
||||
|
|
@ -119,7 +119,7 @@ public:
|
|||
, m_onFinally(false)
|
||||
#endif
|
||||
#if defined(ENABLE_TCO)
|
||||
, m_initTCO(false)
|
||||
, m_inTCO(false)
|
||||
#endif
|
||||
, m_argc(0)
|
||||
, m_argv(nullptr)
|
||||
|
|
@ -141,7 +141,7 @@ public:
|
|||
, m_onFinally(false)
|
||||
#endif
|
||||
#if defined(ENABLE_TCO)
|
||||
, m_initTCO(false)
|
||||
, m_inTCO(false)
|
||||
#endif
|
||||
, m_argc(argc)
|
||||
, m_argv(argv)
|
||||
|
|
@ -163,7 +163,7 @@ public:
|
|||
, m_onFinally(false)
|
||||
#endif
|
||||
#if defined(ENABLE_TCO)
|
||||
, m_initTCO(false)
|
||||
, m_inTCO(false)
|
||||
#endif
|
||||
, m_argc(argc)
|
||||
, m_argv(argv)
|
||||
|
|
@ -248,18 +248,17 @@ public:
|
|||
#endif
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
bool initTCO() const
|
||||
bool inTCO() const
|
||||
{
|
||||
return m_initTCO;
|
||||
return m_inTCO;
|
||||
}
|
||||
|
||||
void setTCOArguments(Value* argv)
|
||||
void initTCOWithBuffer(Value* argv)
|
||||
{
|
||||
// allocate a new argument buffer
|
||||
// because tail call reuses this buffer which can modify caller's register file
|
||||
ASSERT(!m_initTCO);
|
||||
// initialize arguments buffer for tail call
|
||||
ASSERT(!m_inTCO);
|
||||
m_argv = argv;
|
||||
m_initTCO = true; // initialize of TCO done
|
||||
m_inTCO = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -339,7 +338,7 @@ protected:
|
|||
bool m_onFinally : 1;
|
||||
#endif
|
||||
#if defined(ENABLE_TCO)
|
||||
bool m_initTCO : 1;
|
||||
bool m_inTCO : 1;
|
||||
#endif
|
||||
|
||||
#ifdef ESCARGOT_32
|
||||
|
|
|
|||
|
|
@ -224,11 +224,18 @@ public:
|
|||
if (UNLIKELY(blk->m_shouldClearStack)) {
|
||||
clearStack<512>();
|
||||
}
|
||||
#if defined(ENABLE_TCO)
|
||||
if (!isConstructCall) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename FunctionObjectType, bool hasNewTargetOnEnvironment, bool canBindThisValueOnEnvironment>
|
||||
static NEVER_INLINE FunctionEnvironmentRecord* createFunctionEnvironmentRecord(ExecutionState& state, FunctionObjectType* self, InterpretedCodeBlock* codeBlock)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "PointerValue.h"
|
||||
#include "FunctionObject.h"
|
||||
#include "ErrorObject.h"
|
||||
#include "ScriptFunctionObject.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
|
|
@ -37,6 +38,13 @@ size_t PointerValue::g_objectRareDataTag;
|
|||
DECLARE_SCRIPTSIMPLEFUNCTION_LIST(DEFINE_SCRIPTSIMPLEFUNCTION_TAGS);
|
||||
#undef DEFINE_SCRIPTSIMPLEFUNCTION_TAGS
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
bool PointerValue::canBeTailCallTargetRuntime(size_t argc)
|
||||
{
|
||||
return isScriptFunctionObject() && asScriptFunctionObject()->interpretedCodeBlock()->isTailCallTarget(argc);
|
||||
}
|
||||
#endif
|
||||
|
||||
Value PointerValue::call(ExecutionState& state, const Value& thisValue, const size_t argc, Value* argv)
|
||||
{
|
||||
ASSERT(!isCallable());
|
||||
|
|
|
|||
|
|
@ -630,6 +630,10 @@ public:
|
|||
RELEASE_ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
bool canBeTailCallTargetRuntime(size_t argc);
|
||||
#endif
|
||||
|
||||
String* asString()
|
||||
{
|
||||
ASSERT(isString());
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ namespace Escargot {
|
|||
|
||||
class ScriptFunctionObject : public FunctionObject {
|
||||
friend class Script;
|
||||
friend class Interpreter;
|
||||
friend class InterpreterSlowPath;
|
||||
friend class FunctionObjectProcessCallGenerator;
|
||||
friend class Global;
|
||||
|
|
|
|||
|
|
@ -99,6 +99,17 @@ protected:
|
|||
}
|
||||
}
|
||||
|
||||
#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>();
|
||||
|
|
@ -106,6 +117,7 @@ protected:
|
|||
} else {
|
||||
return Interpreter::interpret(&newState, blk, programStart, registerFile);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual Value construct(ExecutionState& state, const size_t argc, Value* argv, Object* newTarget) override
|
||||
|
|
@ -163,7 +175,6 @@ protected:
|
|||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@
|
|||
#include "runtime/ReloadableString.h"
|
||||
#include "intl/Intl.h"
|
||||
#include "interpreter/ByteCode.h"
|
||||
#if defined(ENABLE_TCO)
|
||||
#include "interpreter/ByteCodeInterpreter.h"
|
||||
#endif
|
||||
#if defined(ENABLE_CODE_CACHE)
|
||||
#include "codecache/CodeCache.h"
|
||||
#endif
|
||||
|
|
@ -398,7 +401,6 @@ VMInstance::VMInstance(const char* locale, const char* timezone, const char* bas
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
// add gc event callback
|
||||
GCEventListenerSet& list = ThreadLocal::gcEventListenerSet();
|
||||
list.ensureMarkStartListeners()->push_back(std::make_pair(vmMarkStartCallback, this));
|
||||
|
|
@ -480,6 +482,12 @@ VMInstance::VMInstance(const char* locale, const char* timezone, const char* bas
|
|||
|
||||
m_jobQueue = new JobQueue();
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
if (!Interpreter::tcoBuffer) {
|
||||
Interpreter::initTCOBuffer();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_CODE_CACHE)
|
||||
if (UNLIKELY(!baseCacheDir || strlen(baseCacheDir) == 0)) {
|
||||
const char* homeDir = getenv("HOME");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue