Skip to content

Commit

Permalink
Simplify TCO check code
Browse files Browse the repository at this point in the history
Signed-off-by: HyukWoo Park <[email protected]>
  • Loading branch information
clover2123 authored and ksh8281 committed Nov 22, 2023
1 parent 11d9427 commit 1a5b10b
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 59 deletions.
2 changes: 2 additions & 0 deletions src/interpreter/ByteCode.h
Original file line number Diff line number Diff line change
Expand Up @@ -2020,6 +2020,7 @@ class CallReturn : public ByteCode {
, m_argumentCount(argumentCount)
{
}

ByteCodeRegisterIndex m_calleeIndex;
ByteCodeRegisterIndex m_argumentsStartIndex;
uint16_t m_argumentCount;
Expand All @@ -2045,6 +2046,7 @@ class TailRecursion : public ByteCode {
, m_argumentCount(argumentCount)
{
}

ByteCodeRegisterIndex m_calleeIndex;
ByteCodeRegisterIndex m_argumentsStartIndex;
uint16_t m_argumentCount;
Expand Down
110 changes: 56 additions & 54 deletions src/interpreter/ByteCodeInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1540,35 +1540,36 @@ Value Interpreter::interpret(ExecutionState* state, ByteCodeBlock* byteCodeBlock
TailRecursion* code = (TailRecursion*)programCounter;
const Value& callee = registerFile[code->m_calleeIndex];

if (UNLIKELY((callee != Value(state->resolveCallee())) || (state->initTCO() && (state->m_argc != code->m_argumentCount)))) {
if (UNLIKELY(callee != Value(state->lexicalEnvironment()->record()->asDeclarativeEnvironmentRecord()->asFunctionEnvironmentRecord()->functionObject()))) {
// goto slow path
return InterpreterSlowPath::tailRecursionSlowCase(*state, code, callee, registerFile);
}

// 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));

if (code->m_argumentCount) {
if (UNLIKELY(!state->initTCO())) {
// 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 = code->m_argumentCount;
Value* newArgs = ALLOCA(sizeof(Value) * code->m_argumentCount, Value);
state->setTCOArguments(newArgs);
}

// its safe to overwrite arguments because old arguments are no longer necessary
for (size_t i = 0; i < state->m_argc; i++) {
state->m_argv[i] = registerFile[code->m_argumentsStartIndex + i];
}
state->m_argc = code->m_argumentCount;
Value* newArgs = code->m_argumentCount ? ALLOCA(sizeof(Value) * code->m_argumentCount, Value) : nullptr;
state->setTCOArguments(newArgs);
}

state->setInitTCO();
// 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));
#ifndef NDEBUG
// check this value
if (state->inStrictMode()) {
ASSERT(registerFile[byteCodeBlock->m_requiredOperandRegisterNumber].isUndefined());
} else {
ASSERT(registerFile[byteCodeBlock->m_requiredOperandRegisterNumber] == Value(state->context()->globalObjectProxy()));
}
#endif

// set this value
registerFile[byteCodeBlock->m_requiredOperandRegisterNumber] = state->inStrictMode() ? Value() : state->context()->globalObjectProxy();
// its safe to overwrite arguments because old arguments are no longer necessary
for (size_t i = 0; i < state->m_argc; i++) {
state->m_argv[i] = registerFile[code->m_argumentsStartIndex + i];
}

// set programCounter
programCounter = reinterpret_cast<size_t>(byteCodeBlock->m_code.data());
Expand All @@ -1585,34 +1586,30 @@ Value Interpreter::interpret(ExecutionState* state, ByteCodeBlock* byteCodeBlock
const Value& callee = registerFile[code->m_calleeIndex];
const Value& receiver = registerFile[code->m_receiverIndex];

if (UNLIKELY((callee != Value(state->resolveCallee())) || (state->initTCO() && (state->m_argc != code->m_argumentCount)))) {
if (UNLIKELY(callee != Value(state->lexicalEnvironment()->record()->asDeclarativeEnvironmentRecord()->asFunctionEnvironmentRecord()->functionObject()))) {
// goto slow path
return InterpreterSlowPath::tailRecursionWithReceiverSlowCase(*state, code, callee, receiver, registerFile);
}

if (UNLIKELY(!state->initTCO())) {
// 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);
}

// fast tail recursion with receiver
ASSERT(callee.isPointerValue() && callee.asPointerValue()->isScriptFunctionObject());
ASSERT(callee.asPointerValue()->asScriptFunctionObject()->codeBlock() == byteCodeBlock->codeBlock());
ASSERT(!state->initTCO() || (state->m_argc == code->m_argumentCount));

if (code->m_argumentCount) {
// 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 = code->m_argumentCount;
Value* newArgs = ALLOCA(sizeof(Value) * code->m_argumentCount, Value);
state->setTCOArguments(newArgs);
}
ASSERT(state->initTCO() && (state->m_argc == code->m_argumentCount));

// its safe to overwrite arguments because old arguments are no longer necessary
for (size_t i = 0; i < state->m_argc; i++) {
state->m_argv[i] = registerFile[code->m_argumentsStartIndex + i];
}
// its safe to overwrite arguments because old arguments are no longer necessary
for (size_t i = 0; i < state->m_argc; i++) {
state->m_argv[i] = registerFile[code->m_argumentsStartIndex + i];
}

state->setInitTCO();

// set this value (receiver) // FIXME
// set this value (receiver)
if (state->inStrictMode()) {
registerFile[byteCodeBlock->m_requiredOperandRegisterNumber] = receiver;
} else {
Expand All @@ -1637,7 +1634,8 @@ Value Interpreter::interpret(ExecutionState* state, ByteCodeBlock* byteCodeBlock
TailRecursionInTry* code = (TailRecursionInTry*)programCounter;
const Value& callee = registerFile[code->m_calleeIndex];

if (UNLIKELY((callee != Value(state->resolveCallee())) || (state->m_argc != code->m_argumentCount))) {
if (UNLIKELY(callee != Value(state->resolveCallee()))) {
// should call resolveCallee because try-catch-finally block is executed in a sub-interpreter
// goto slow path
code->changeOpcode(Opcode::CallOpcode);
if (UNLIKELY(!callee.isPointerValue())) {
Expand All @@ -1653,7 +1651,6 @@ Value Interpreter::interpret(ExecutionState* state, ByteCodeBlock* byteCodeBlock

ASSERT(callee.isPointerValue() && callee.asPointerValue()->isScriptFunctionObject());
ASSERT(callee.asPointerValue()->asScriptFunctionObject()->codeBlock() == byteCodeBlock->codeBlock());
ASSERT(state->m_argc == code->m_argumentCount);
ASSERT(state->rareData()->controlFlowRecordVector() && state->rareData()->controlFlowRecordVector()->size());

// postpone recursion call
Expand Down Expand Up @@ -3228,26 +3225,31 @@ NEVER_INLINE Value InterpreterSlowPath::tryOperation(ExecutionState*& state, siz
ASSERT(!inPauserScope && !inPauserResumeProcess);
ASSERT(code->m_hasCatch || code->m_hasFinalizer);
ASSERT(record->m_value == state->resolveCallee());
#ifndef NDEBUG
// check this value
if (state->inStrictMode()) {
ASSERT(registerFile[byteCodeBlock->m_requiredOperandRegisterNumber].isUndefined());
} else {
ASSERT(registerFile[byteCodeBlock->m_requiredOperandRegisterNumber] == Value(state->context()->globalObjectProxy()));
}
#endif

Value callee = record->m_value;
size_t argCount = record->m_count;
size_t argStartIndex = record->m_outerLimitCount;
if (argCount) {
// 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())) {
Value* newArgs = (Value*)GC_MALLOC(sizeof(Value) * argCount);
state->setTCOArguments(newArgs);
}

// its safe to overwrite arguments because old arguments are no longer necessary
for (size_t i = 0; i < state->m_argc; i++) {
state->m_argv[i] = registerFile[argStartIndex + i];
}
// 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);
}

// set this value
registerFile[byteCodeBlock->m_requiredOperandRegisterNumber] = state->inStrictMode() ? Value() : state->context()->globalObjectProxy();
// its safe to overwrite arguments because old arguments are no longer necessary
ASSERT(state->m_argc == argCount);
for (size_t i = 0; i < state->m_argc; i++) {
state->m_argv[i] = registerFile[argStartIndex + i];
}

// set programCounter
programCounter = reinterpret_cast<size_t>(byteCodeBlock->m_code.data());
Expand Down
6 changes: 1 addition & 5 deletions src/runtime/ExecutionState.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,17 +253,13 @@ class ExecutionState : public gc {
return m_initTCO;
}

void setInitTCO()
{
m_initTCO = true;
}

void setTCOArguments(Value* argv)
{
// allocate a new argument buffer
// because tail call reuses this buffer which can modify caller's register file
ASSERT(!m_initTCO && !!argv);
m_argv = argv;
m_initTCO = true; // initialize of TCO done
}
#endif

Expand Down

0 comments on commit 1a5b10b

Please sign in to comment.