Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce ExecutionState size and compute stacklimit correctly #1275

Merged
merged 2 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/es-actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,10 @@ jobs:
env:
GC_FREE_SPACE_DIVISOR: 1
run: $RUNNER --arch=x86 --engine="$GITHUB_WORKSPACE/out/release/x86/escargot" ${{ matrix.tc }}
- if: ${{ failure() }}
uses: mxschmitt/action-tmate@v3
timeout-minutes: 15


build-test-on-x64-release:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -330,6 +334,9 @@ jobs:
export LANG=en_US.UTF-8
locale
$RUNNER --arch=x86_64 --engine="$GITHUB_WORKSPACE/out/release/x64/escargot" ${{ matrix.tc }}
- if: ${{ failure() }}
uses: mxschmitt/action-tmate@v3
timeout-minutes: 15

build-test-on-x86-x64-debug:
runs-on: ubuntu-latest
Expand Down
1 change: 0 additions & 1 deletion build/target.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ ELSEIF (${ESCARGOT_HOST} STREQUAL "darwin" AND ${ESCARGOT_ARCH} STREQUAL "x64")
SET (ENV{PKG_CONFIG_PATH} "/usr/local/opt/icu4c/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}")
ELSEIF (${ESCARGOT_HOST} STREQUAL "windows")
SET (ESCARGOT_LDFLAGS ${ESCARGOT_LDFLAGS} icu.lib)
SET (ESCARGOT_CXXFLAGS ${ESCARGOT_CXXFLAGS} -DSTACK_LIMIT_FROM_BASE=1048576) # in windows, default stack limit is 1MB
IF ((${ESCARGOT_ARCH} STREQUAL "x64") OR (${ESCARGOT_ARCH} STREQUAL "x86_64"))
SET (ESCARGOT_BUILD_64BIT ON)
SET (ESCARGOT_BUILD_64BIT_LARGE ON)
Expand Down
17 changes: 13 additions & 4 deletions src/Escargot.h
Original file line number Diff line number Diff line change
Expand Up @@ -523,18 +523,26 @@ typedef uint16_t LexicalBlockIndex;
#define STACK_GROWS_DOWN
#endif

#ifndef STACK_LIMIT_FROM_BASE
#define STACK_LIMIT_FROM_BASE (1024 * 1024 * 3) // 3MB
#ifndef STACK_FREESPACE_FROM_LIMIT
#define STACK_FREESPACE_FROM_LIMIT (1024 * 256) // 256KB
#endif

#ifndef STACK_USAGE_LIMIT
#ifdef ESCARGOT_ENABLE_TEST
#define STACK_USAGE_LIMIT (1024 * 1024 * 2) // 2MB
#else
#define STACK_USAGE_LIMIT (1024 * 1024 * 4) // 4MB
#endif
#endif

#ifdef STACK_GROWS_DOWN
#define CHECK_STACK_OVERFLOW(state) \
if (UNLIKELY(state.stackLimit() > (size_t)currentStackPointer())) { \
if (UNLIKELY(ThreadLocal::stackLimit() > (size_t)currentStackPointer())) { \
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Maximum call stack size exceeded"); \
}
#else
#define CHECK_STACK_OVERFLOW(state) \
if (UNLIKELY(state.stackLimit() < (size_t)currentStackPointer())) { \
if (UNLIKELY(ThreadLocal::stackLimit() < (size_t)currentStackPointer())) { \
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Maximum call stack size exceeded"); \
}
#endif
Expand Down Expand Up @@ -581,5 +589,6 @@ using HashMap = tsl::robin_map<Key, T, Hash, KeyEqual, Allocator, StoreHash, Gro
#include "heap/Heap.h"
#include "util/Util.h"
#include "util/Optional.h"
#include "runtime/ThreadLocal.h"

#endif
9 changes: 3 additions & 6 deletions src/api/EscargotPublic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3065,17 +3065,14 @@ StackOverflowDisabler::StackOverflowDisabler(ExecutionStateRef* es)

// We assume that StackOverflowDisabler should not be nested-called
ASSERT(m_originStackLimit != newStackLimit);
ASSERT(m_originStackLimit == state->context()->vmInstance()->stackLimit());
state->m_stackLimit = newStackLimit;
state->context()->vmInstance()->m_stackLimit = newStackLimit;
ASSERT(m_originStackLimit == ThreadLocal::stackLimit());
ThreadLocal::g_stackLimit = newStackLimit;
}

StackOverflowDisabler::~StackOverflowDisabler()
{
ASSERT(!!m_executionState);
ExecutionState* state = toImpl(m_executionState);
state->m_stackLimit = m_originStackLimit;
state->context()->vmInstance()->m_stackLimit = m_originStackLimit;
ThreadLocal::g_stackLimit = m_originStackLimit;
}

OptionalRef<FunctionObjectRef> ExecutionStateRef::resolveCallee()
Expand Down
2 changes: 1 addition & 1 deletion src/api/EscargotPublic.h
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ class ESCARGOT_EXPORT Evaluator {
};

// temporally disable StackOverflow check
// StackOverflowDisabler only unlocks the predefined stack limit (STACK_LIMIT_FROM_BASE: 3MB)
// StackOverflowDisabler only unlocks the predefined stack limit (STACK_USAGE_LIMIT: 4MB)
// should be carefully used because it cannot prevent the system-stackoverflow exception
class ESCARGOT_EXPORT StackOverflowDisabler {
public:
Expand Down
2 changes: 2 additions & 0 deletions src/codecache/CodeCacheReaderWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,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((uint16_t)block->m_requiredOperandRegisterNumber);
m_buffer.put((uint16_t)block->m_requiredTotalRegisterNumber);

Expand Down Expand Up @@ -855,6 +856,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_requiredOperandRegisterNumber = m_buffer.get<uint16_t>();
block->m_requiredTotalRegisterNumber = m_buffer.get<uint16_t>();

Expand Down
3 changes: 3 additions & 0 deletions src/interpreter/ByteCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ void SetGlobalVariable::dump()
ByteCodeBlock::ByteCodeBlock()
: m_shouldClearStack(false)
, m_isOwnerMayFreed(false)
, m_needsExtendedExectuionState(false)
, m_requiredOperandRegisterNumber(2)
, m_requiredTotalRegisterNumber(0)
, m_inlineCacheDataSize(0)
Expand Down Expand Up @@ -128,6 +129,7 @@ static void clearByteCodeBlock(ByteCodeBlock* self)
ByteCodeBlock::ByteCodeBlock(InterpretedCodeBlock* codeBlock)
: m_shouldClearStack(false)
, m_isOwnerMayFreed(false)
, m_needsExtendedExectuionState(false)
, m_requiredOperandRegisterNumber(2)
, m_requiredTotalRegisterNumber(0)
, m_inlineCacheDataSize(0)
Expand Down Expand Up @@ -288,6 +290,7 @@ ByteCodeBlock::ByteCodeLexicalBlockContext ByteCodeBlock::pushLexicalBlock(ByteC
ctx.lexicalBlockSetupStartPosition = currentCodeSize();
context->m_recursiveStatementStack.push_back(std::make_pair(ByteCodeGenerateContext::Block, ctx.lexicalBlockSetupStartPosition));
this->pushCode(BlockOperation(ByteCodeLOC(node->m_loc.index), blockInfo), context, SIZE_MAX);
context->m_needsExtendedExecutionState = true;
}

if (initFunctionDeclarationInside) {
Expand Down
6 changes: 6 additions & 0 deletions src/interpreter/ByteCode.h
Original file line number Diff line number Diff line change
Expand Up @@ -3154,12 +3154,18 @@ class ByteCodeBlock : public gc {
return siz;
}

bool needsExtendedExecutionState() const
{
return m_needsExtendedExectuionState;
}

ExtendedNodeLOC computeNodeLOCFromByteCode(Context* c, size_t codePosition, InterpretedCodeBlock* cb, ByteCodeLOCData* locData);
ExtendedNodeLOC computeNodeLOC(StringView src, ExtendedNodeLOC sourceElementStart, size_t index);
void fillLOCData(Context* c, ByteCodeLOCData* locData);

bool m_shouldClearStack : 1;
bool m_isOwnerMayFreed : 1;
bool m_needsExtendedExectuionState : 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"
Expand Down
8 changes: 5 additions & 3 deletions src/interpreter/ByteCodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ ByteCodeGenerateContext::ByteCodeGenerateContext(InterpretedCodeBlock* codeBlock
, m_codeBlock(codeBlock)
, m_byteCodeBlock(byteCodeBlock)
, m_locData(nullptr)
, m_stackLimit(codeBlock->context()->vmInstance()->stackLimit())
, m_isGlobalScope(isGlobalScope)
, m_isEvalCode(isEvalCode)
, m_isOutermostContext(true)
Expand All @@ -55,6 +54,7 @@ ByteCodeGenerateContext::ByteCodeGenerateContext(InterpretedCodeBlock* codeBlock
#if defined(ENABLE_TCO)
, m_tcoDisabled(false)
#endif
, m_needsExtendedExecutionState(codeBlock->isAsync() || codeBlock->isGenerator())
, m_registerStack(new std::vector<ByteCodeRegisterIndex>())
, m_lexicallyDeclaredNames(new std::vector<std::pair<size_t, AtomicString>>())
, m_positionToContinue(0)
Expand Down Expand Up @@ -132,6 +132,7 @@ void ByteCodeGenerateContext::morphJumpPositionIntoComplexCase(ByteCodeBlock* cb
m_byteCodeBlock->m_jumpFlowRecordData.pushBack(JumpFlowRecord((cb->peekCode<Jump>(codePos)->m_jumpPosition), iter->second, outerLimitCount));
new (cb->m_code.data() + codePos) JumpComplexCase(index);
m_complexCaseStatementPositions.erase(iter);
m_needsExtendedExecutionState = true;
}
}

Expand Down Expand Up @@ -280,6 +281,7 @@ ByteCodeBlock* ByteCodeGenerator::generateByteCode(Context* context, Interpreted
}

block->m_requiredTotalRegisterNumber = block->m_requiredOperandRegisterNumber + codeBlock->totalStackAllocatedVariableSize() + block->m_numeralLiteralData.size();
block->m_needsExtendedExectuionState = ctx.m_needsExtendedExecutionState;

#if defined(ENABLE_CODE_CACHE)
// cache bytecode right before relocation
Expand Down Expand Up @@ -310,9 +312,9 @@ void ByteCodeGenerator::collectByteCodeLOCData(Context* context, InterpretedCode
Node* ast = nullptr;
if (codeBlock->isGlobalCodeBlock() || codeBlock->isEvalCode()) {
ast = esprima::parseProgram(context, codeBlock->src(), esprima::generateClassInfoFrom(context, codeBlock->parent()),
codeBlock->script()->isModule(), codeBlock->isStrict(), codeBlock->inWith(), SIZE_MAX, false, false, false, true);
codeBlock->script()->isModule(), codeBlock->isStrict(), codeBlock->inWith(), false, false, false, true);
} else {
ast = esprima::parseSingleFunction(context, codeBlock, SIZE_MAX);
ast = esprima::parseSingleFunction(context, codeBlock);
}

// Generate ByteCode
Expand Down
27 changes: 16 additions & 11 deletions src/interpreter/ByteCodeGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ struct ByteCodeGenerateContext {
, m_codeBlock(contextBefore.m_codeBlock)
, m_byteCodeBlock(contextBefore.m_byteCodeBlock)
, m_locData(contextBefore.m_locData)
, m_stackLimit(contextBefore.m_stackLimit)
, m_isGlobalScope(contextBefore.m_isGlobalScope)
, m_isEvalCode(contextBefore.m_isEvalCode)
, m_isOutermostContext(false)
Expand All @@ -91,6 +90,7 @@ struct ByteCodeGenerateContext {
#if defined(ENABLE_TCO)
, m_tcoDisabled(contextBefore.m_tcoDisabled)
#endif
, m_needsExtendedExecutionState(contextBefore.m_needsExtendedExecutionState)
, m_registerStack(contextBefore.m_registerStack)
, m_lexicallyDeclaredNames(contextBefore.m_lexicallyDeclaredNames)
, m_positionToContinue(contextBefore.m_positionToContinue)
Expand All @@ -108,6 +108,7 @@ struct ByteCodeGenerateContext {
#endif /* ESCARGOT_DEBUGGER */
{
ASSERT(m_complexJumpBreakIgnoreCount == m_complexJumpContinueIgnoreCount);
checkStack();
}

~ByteCodeGenerateContext()
Expand All @@ -124,6 +125,7 @@ struct ByteCodeGenerateContext {
ctx.m_positionToContinue = m_positionToContinue;
ctx.m_lexicalBlockIndex = m_lexicalBlockIndex;
ctx.m_classInfo = m_classInfo;
ctx.m_needsExtendedExecutionState = ctx.m_needsExtendedExecutionState | m_needsExtendedExecutionState;

m_breakStatementPositions.clear();
m_continueStatementPositions.clear();
Expand Down Expand Up @@ -224,14 +226,7 @@ struct ByteCodeGenerateContext {
if (UNLIKELY(m_baseRegisterCount >= REGULAR_REGISTER_LIMIT)) {
throw "register limit exceed while generate byte code";
}
#ifdef STACK_GROWS_DOWN
if (UNLIKELY(m_stackLimit > (size_t)currentStackPointer())) {
#else
if (UNLIKELY(m_stackLimit < (size_t)currentStackPointer())) {
#endif
throw "native stack limit exceed while generate byte code";
}

checkStack();
m_registerStack->push_back(m_baseRegisterCount);
m_baseRegisterCount++;
return m_registerStack->back();
Expand Down Expand Up @@ -326,6 +321,17 @@ struct ByteCodeGenerateContext {
}
#endif

ALWAYS_INLINE void checkStack()
{
#ifdef STACK_GROWS_DOWN
if (UNLIKELY(ThreadLocal::stackLimit() > (size_t)currentStackPointer())) {
#else
if (UNLIKELY(ThreadLocal::stackLimit() < (size_t)currentStackPointer())) {
#endif
throw "native stack limit exceed while generate byte code";
}
}

#ifdef ESCARGOT_DEBUGGER
void calculateBreakpointLocation(size_t index, ExtendedNodeLOC sourceElementStart);
void insertBreakpoint(size_t index, Node* node);
Expand All @@ -339,8 +345,6 @@ struct ByteCodeGenerateContext {
ByteCodeBlock* m_byteCodeBlock;
std::vector<std::pair<size_t, size_t>, std::allocator<std::pair<size_t, size_t>>>* m_locData; // used only for calculating location info

size_t m_stackLimit;

bool m_isGlobalScope : 1;
bool m_isEvalCode : 1;
bool m_isOutermostContext : 1;
Expand All @@ -359,6 +363,7 @@ struct ByteCodeGenerateContext {
#if defined(ENABLE_TCO)
bool m_tcoDisabled : 1; // disable tail call optimizaiton (TCO) for some conditions
#endif
bool m_needsExtendedExecutionState : 1;

std::shared_ptr<std::vector<ByteCodeRegisterIndex>> m_registerStack;
std::shared_ptr<std::vector<std::pair<size_t, AtomicString>>> m_lexicallyDeclaredNames;
Expand Down
Loading