2009-01-07 Sam Weinig <sam@webkit.org>
Reviewed by Geoffrey Garen.
<rdar://problem/6469060> Don't store exception information for a CodeBlock until first exception is thrown
Don't initially store exception information (lineNumber/expressionRange/getByIdExcecptionInfo)
in CodeBlocks blocks. Instead, re-parse for the data on demand and cache it then.
One important change that was needed to make this work was to pad op_get_global_var with nops to
be the same length as op_resolve_global, since one could be replaced for the other on re-parsing,
and we want to keep the offsets bytecode offsets the same.
1.3MB improvement on Membuster head.
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump): Update op_get_global_var to account for the padding.
(JSC::CodeBlock::dumpStatistics): Add more statistic dumping.
(JSC::CodeBlock::CodeBlock): Initialize m_exceptionInfo.
(JSC::CodeBlock::reparseForExceptionInfoIfNecessary): Re-parses the CodeBlocks
associated SourceCode and steals the ExceptionInfo from it.
(JSC::CodeBlock::lineNumberForBytecodeOffset): Creates the exception info on demand.
(JSC::CodeBlock::expressionRangeForBytecodeOffset): Ditto.
(JSC::CodeBlock::getByIdExceptionInfoForBytecodeOffset): Ditto.
* bytecode/CodeBlock.h:
(JSC::CodeBlock::numberOfExceptionHandlers): Updated to account for m_exceptionInfo indirection.
(JSC::CodeBlock::addExceptionHandler): Ditto.
(JSC::CodeBlock::exceptionHandler): Ditto.
(JSC::CodeBlock::clearExceptionInfo): Ditto.
(JSC::CodeBlock::addExpressionInfo): Ditto.
(JSC::CodeBlock::addGetByIdExceptionInfo): Ditto.
(JSC::CodeBlock::numberOfLineInfos): Ditto.
(JSC::CodeBlock::addLineInfo): Ditto.
(JSC::CodeBlock::lastLineInfo): Ditto.
* bytecode/Opcode.h: Change length of op_get_global_var to match op_resolve_global.
* bytecode/SamplingTool.cpp:
(JSC::SamplingTool::dump): Add comment indicating why it is okay not to pass a CallFrame.
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::generate): Clear the exception info after generation for Function and Eval
Code when not in regenerate for exception info mode.
(JSC::BytecodeGenerator::BytecodeGenerator): Initialize m_regeneratingForExceptionInfo to false.
(JSC::BytecodeGenerator::emitGetScopedVar): Pad op_get_global_var with 2 nops.
* bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::setRegeneratingForExcpeptionInfo): Added.
* interpreter/Interpreter.cpp:
(JSC::Interpreter::throwException): Pass the CallFrame to exception info accessors.
(JSC::Interpreter::privateExecute): Ditto.
(JSC::Interpreter::retrieveLastCaller): Ditto.
(JSC::Interpreter::cti_op_new_error): Ditto.
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass): Pass the current bytecode offset instead of hard coding the
line number, the stub will do the accessing if it gets called.
* parser/Nodes.cpp:
(JSC::ProgramNode::emitBytecode): Moved.
(JSC::ProgramNode::generateBytecode): Moved.
(JSC::EvalNode::create): Moved.
(JSC::EvalNode::bytecodeForExceptionInfoReparse): Added.
(JSC::FunctionBodyNode::generateBytecode): Rename reparse to reparseInPlace.
(JSC::FunctionBodyNode::bytecodeForExceptionInfoReparse): Addded.
* parser/Nodes.h:
(JSC::ScopeNode::features): Added getter.
* parser/Parser.cpp:
(JSC::Parser::reparseInPlace): Renamed from reparse.
* parser/Parser.h:
(JSC::Parser::reparse): Added. Re-parses the passed in Node into
a new Node.
* runtime/ExceptionHelpers.cpp:
(JSC::createUndefinedVariableError): Pass along CallFrame.
(JSC::createInvalidParamError): Ditto.
(JSC::createNotAConstructorError): Ditto.
(JSC::createNotAFunctionError): Ditto.
(JSC::createNotAnObjectError): Ditto.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@39697 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/JavaScriptCore/bytecode/CodeBlock.cpp b/JavaScriptCore/bytecode/CodeBlock.cpp
index 154e741..91fb1c0 100644
--- a/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
* Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca>
*
* Redistribution and use in source and binary forms, with or without
@@ -724,10 +724,11 @@
break;
}
case op_get_global_var: {
- int r0 = (++it)->u.operand;
- JSValuePtr scope = JSValuePtr((++it)->u.jsCell);
- int index = (++it)->u.operand;
+ int r0 = it[1].u.operand;
+ JSValuePtr scope = JSValuePtr(it[2].u.jsCell);
+ int index = it[3].u.operand;
printf("[%4d] get_global_var\t %s, %s, %d\n", location, registerName(r0).c_str(), valueToSourceString(exec, scope).ascii(), index);
+ it += OPCODE_LENGTH(op_get_global_var);
break;
}
case op_put_global_var: {
@@ -1091,9 +1092,6 @@
macro(identifiers) \
macro(functionExpressions) \
macro(constantRegisters) \
- macro(expressionInfo) \
- macro(lineInfo) \
- macro(getByIdExceptionInfo) \
macro(pcVector)
#define FOR_EACH_MEMBER_VECTOR_RARE_DATA(macro) \
@@ -1106,6 +1104,11 @@
macro(stringSwitchJumpTables) \
macro(functionRegisterInfos)
+#define FOR_EACH_MEMBER_VECTOR_EXCEPTION_INFO(macro) \
+ macro(expressionInfo) \
+ macro(lineInfo) \
+ macro(getByIdExceptionInfo)
+
template<typename T>
static size_t sizeInBytes(const Vector<T>& vector)
{
@@ -1118,6 +1121,7 @@
#define DEFINE_VARS(name) size_t name##IsNotEmpty = 0; size_t name##TotalSize = 0;
FOR_EACH_MEMBER_VECTOR(DEFINE_VARS)
FOR_EACH_MEMBER_VECTOR_RARE_DATA(DEFINE_VARS)
+ FOR_EACH_MEMBER_VECTOR_EXCEPTION_INFO(DEFINE_VARS)
#undef DEFINE_VARS
// Non-vector data members
@@ -1126,8 +1130,13 @@
size_t symbolTableIsNotEmpty = 0;
size_t symbolTableTotalSize = 0;
+ size_t hasExceptionInfo = 0;
size_t hasRareData = 0;
+ size_t isFunctionCode = 0;
+ size_t isGlobalCode = 0;
+ size_t isEvalCode = 0;
+
HashSet<CodeBlock*>::const_iterator end = liveCodeBlockSet.end();
for (HashSet<CodeBlock*>::const_iterator it = liveCodeBlockSet.begin(); it != end; ++it) {
CodeBlock* codeBlock = *it;
@@ -1141,6 +1150,13 @@
symbolTableTotalSize += (codeBlock->m_symbolTable.capacity() * (sizeof(SymbolTable::KeyType) + sizeof(SymbolTable::MappedType)));
}
+ if (codeBlock->m_exceptionInfo) {
+ hasExceptionInfo++;
+ #define GET_STATS(name) if (!codeBlock->m_exceptionInfo->m_##name.isEmpty()) { name##IsNotEmpty++; name##TotalSize += sizeInBytes(codeBlock->m_exceptionInfo->m_##name); }
+ FOR_EACH_MEMBER_VECTOR_EXCEPTION_INFO(GET_STATS)
+ #undef GET_STATS
+ }
+
if (codeBlock->m_rareData) {
hasRareData++;
#define GET_STATS(name) if (!codeBlock->m_rareData->m_##name.isEmpty()) { name##IsNotEmpty++; name##TotalSize += sizeInBytes(codeBlock->m_rareData->m_##name); }
@@ -1150,13 +1166,26 @@
if (!codeBlock->m_rareData->m_evalCodeCache.isEmpty())
evalCodeCacheIsNotEmpty++;
}
+
+ switch (codeBlock->codeType()) {
+ case FunctionCode:
+ ++isFunctionCode;
+ break;
+ case GlobalCode:
+ ++isGlobalCode;
+ break;
+ case EvalCode:
+ ++isEvalCode;
+ break;
+ }
}
size_t totalSize = 0;
#define GET_TOTAL_SIZE(name) totalSize += name##TotalSize;
- FOR_EACH_MEMBER_VECTOR(GET_TOTAL_SIZE)
- FOR_EACH_MEMBER_VECTOR_RARE_DATA(GET_TOTAL_SIZE)
+ FOR_EACH_MEMBER_VECTOR(GET_TOTAL_SIZE)
+ FOR_EACH_MEMBER_VECTOR_RARE_DATA(GET_TOTAL_SIZE)
+ FOR_EACH_MEMBER_VECTOR_EXCEPTION_INFO(GET_TOTAL_SIZE)
#undef GET_TOTAL_SIZE
totalSize += symbolTableTotalSize;
@@ -1167,11 +1196,17 @@
printf("Size of all CodeBlocks: %zu\n", totalSize);
printf("Average size of a CodeBlock: %zu\n", totalSize / liveCodeBlockSet.size());
- printf("Number of CodeBlocks with rare data: %zu\n", hasRareData);
+ printf("Number of FunctionCode CodeBlocks: %zu (%.3f%%)\n", isFunctionCode, static_cast<double>(isFunctionCode) * 100.0 / liveCodeBlockSet.size());
+ printf("Number of GlobalCode CodeBlocks: %zu (%.3f%%)\n", isGlobalCode, static_cast<double>(isGlobalCode) * 100.0 / liveCodeBlockSet.size());
+ printf("Number of EvalCode CodeBlocks: %zu (%.3f%%)\n", isEvalCode, static_cast<double>(isEvalCode) * 100.0 / liveCodeBlockSet.size());
+
+ printf("Number of CodeBlocks with exception info: %zu (%.3f%%)\n", hasExceptionInfo, static_cast<double>(hasExceptionInfo) * 100.0 / liveCodeBlockSet.size());
+ printf("Number of CodeBlocks with rare data: %zu (%.3f%%)\n", hasRareData, static_cast<double>(hasRareData) * 100.0 / liveCodeBlockSet.size());
#define PRINT_STATS(name) printf("Number of CodeBlocks with " #name ": %zu\n", name##IsNotEmpty); printf("Size of all " #name ": %zu\n", name##TotalSize);
FOR_EACH_MEMBER_VECTOR(PRINT_STATS)
FOR_EACH_MEMBER_VECTOR_RARE_DATA(PRINT_STATS)
+ FOR_EACH_MEMBER_VECTOR_EXCEPTION_INFO(PRINT_STATS)
#undef PRINT_STATS
printf("Number of CodeBlocks with evalCodeCache: %zu\n", evalCodeCacheIsNotEmpty);
@@ -1200,6 +1235,7 @@
, m_codeType(codeType)
, m_source(sourceProvider)
, m_sourceOffset(sourceOffset)
+ , m_exceptionInfo(new ExceptionInfo)
{
ASSERT(m_source);
@@ -1350,6 +1386,40 @@
}
}
+void CodeBlock::reparseForExceptionInfoIfNecessary(CallFrame* callFrame)
+{
+ if (m_exceptionInfo)
+ return;
+
+ ScopeChainNode* scopeChain = callFrame->scopeChain();
+
+ switch (m_codeType) {
+ case FunctionCode: {
+ FunctionBodyNode* ownerFunctionBodyNode = static_cast<FunctionBodyNode*>(m_ownerNode);
+ RefPtr<FunctionBodyNode> newFunctionBody = m_globalData->parser->reparse<FunctionBodyNode>(m_globalData, ownerFunctionBodyNode);
+ newFunctionBody->finishParsing(ownerFunctionBodyNode->copyParameters(), ownerFunctionBodyNode->parameterCount());
+ CodeBlock& newCodeBlock = newFunctionBody->bytecodeForExceptionInfoReparse(scopeChain);
+ ASSERT(newCodeBlock.m_exceptionInfo);
+ ASSERT(newCodeBlock.m_instructionCount == m_instructionCount);
+ m_exceptionInfo.set(newCodeBlock.m_exceptionInfo.release());
+ break;
+ }
+ case EvalCode: {
+ EvalNode* ownerEvalNode = static_cast<EvalNode*>(m_ownerNode);
+ RefPtr<EvalNode> newEvalBody = m_globalData->parser->reparse<EvalNode>(m_globalData, ownerEvalNode);
+ EvalCodeBlock& newCodeBlock = newEvalBody->bytecodeForExceptionInfoReparse(scopeChain);
+ ASSERT(newCodeBlock.m_exceptionInfo);
+ ASSERT(newCodeBlock.m_instructionCount == m_instructionCount);
+ m_exceptionInfo.set(newCodeBlock.m_exceptionInfo.release());
+ break;
+ }
+ default:
+ // CodeBlocks for Global code blocks are transient and therefore to not gain from
+ // from throwing out there exception information.
+ ASSERT_NOT_REACHED();
+ }
+}
+
HandlerInfo* CodeBlock::handlerForBytecodeOffset(unsigned bytecodeOffset)
{
ASSERT(bytecodeOffset < m_instructionCount);
@@ -1368,18 +1438,21 @@
return 0;
}
-int CodeBlock::lineNumberForBytecodeOffset(unsigned bytecodeOffset)
+int CodeBlock::lineNumberForBytecodeOffset(CallFrame* callFrame, unsigned bytecodeOffset)
{
ASSERT(bytecodeOffset < m_instructionCount);
- if (!m_lineInfo.size())
+ reparseForExceptionInfoIfNecessary(callFrame);
+ ASSERT(m_exceptionInfo);
+
+ if (!m_exceptionInfo->m_lineInfo.size())
return m_ownerNode->source().firstLine(); // Empty function
int low = 0;
- int high = m_lineInfo.size();
+ int high = m_exceptionInfo->m_lineInfo.size();
while (low < high) {
int mid = low + (high - low) / 2;
- if (m_lineInfo[mid].instructionOffset <= bytecodeOffset)
+ if (m_exceptionInfo->m_lineInfo[mid].instructionOffset <= bytecodeOffset)
low = mid + 1;
else
high = mid;
@@ -1387,26 +1460,29 @@
if (!low)
return m_ownerNode->source().firstLine();
- return m_lineInfo[low - 1].lineNumber;
+ return m_exceptionInfo->m_lineInfo[low - 1].lineNumber;
}
-int CodeBlock::expressionRangeForBytecodeOffset(unsigned bytecodeOffset, int& divot, int& startOffset, int& endOffset)
+int CodeBlock::expressionRangeForBytecodeOffset(CallFrame* callFrame, unsigned bytecodeOffset, int& divot, int& startOffset, int& endOffset)
{
ASSERT(bytecodeOffset < m_instructionCount);
- if (!m_expressionInfo.size()) {
+ reparseForExceptionInfoIfNecessary(callFrame);
+ ASSERT(m_exceptionInfo);
+
+ if (!m_exceptionInfo->m_expressionInfo.size()) {
// We didn't think anything could throw. Apparently we were wrong.
startOffset = 0;
endOffset = 0;
divot = 0;
- return lineNumberForBytecodeOffset(bytecodeOffset);
+ return lineNumberForBytecodeOffset(callFrame, bytecodeOffset);
}
int low = 0;
- int high = m_expressionInfo.size();
+ int high = m_exceptionInfo->m_expressionInfo.size();
while (low < high) {
int mid = low + (high - low) / 2;
- if (m_expressionInfo[mid].instructionOffset <= bytecodeOffset)
+ if (m_exceptionInfo->m_expressionInfo[mid].instructionOffset <= bytecodeOffset)
low = mid + 1;
else
high = mid;
@@ -1417,36 +1493,39 @@
startOffset = 0;
endOffset = 0;
divot = 0;
- return lineNumberForBytecodeOffset(bytecodeOffset);
+ return lineNumberForBytecodeOffset(callFrame, bytecodeOffset);
}
- startOffset = m_expressionInfo[low - 1].startOffset;
- endOffset = m_expressionInfo[low - 1].endOffset;
- divot = m_expressionInfo[low - 1].divotPoint + m_sourceOffset;
- return lineNumberForBytecodeOffset(bytecodeOffset);
+ startOffset = m_exceptionInfo->m_expressionInfo[low - 1].startOffset;
+ endOffset = m_exceptionInfo->m_expressionInfo[low - 1].endOffset;
+ divot = m_exceptionInfo->m_expressionInfo[low - 1].divotPoint + m_sourceOffset;
+ return lineNumberForBytecodeOffset(callFrame, bytecodeOffset);
}
-bool CodeBlock::getByIdExceptionInfoForBytecodeOffset(unsigned bytecodeOffset, OpcodeID& opcodeID)
+bool CodeBlock::getByIdExceptionInfoForBytecodeOffset(CallFrame* callFrame, unsigned bytecodeOffset, OpcodeID& opcodeID)
{
ASSERT(bytecodeOffset < m_instructionCount);
- if (!m_getByIdExceptionInfo.size())
+ reparseForExceptionInfoIfNecessary(callFrame);
+ ASSERT(m_exceptionInfo);
+
+ if (!m_exceptionInfo->m_getByIdExceptionInfo.size())
return false;
int low = 0;
- int high = m_getByIdExceptionInfo.size();
+ int high = m_exceptionInfo->m_getByIdExceptionInfo.size();
while (low < high) {
int mid = low + (high - low) / 2;
- if (m_getByIdExceptionInfo[mid].bytecodeOffset <= bytecodeOffset)
+ if (m_exceptionInfo->m_getByIdExceptionInfo[mid].bytecodeOffset <= bytecodeOffset)
low = mid + 1;
else
high = mid;
}
- if (!low || m_getByIdExceptionInfo[low - 1].bytecodeOffset != bytecodeOffset)
+ if (!low || m_exceptionInfo->m_getByIdExceptionInfo[low - 1].bytecodeOffset != bytecodeOffset)
return false;
- opcodeID = m_getByIdExceptionInfo[low - 1].isOpConstruct ? op_construct : op_instanceof;
+ opcodeID = m_exceptionInfo->m_getByIdExceptionInfo[low - 1].isOpConstruct ? op_construct : op_instanceof;
return true;
}
@@ -1499,14 +1578,16 @@
m_linkedCallerList.shrinkToFit();
#endif
- m_expressionInfo.shrinkToFit();
- m_lineInfo.shrinkToFit();
- m_getByIdExceptionInfo.shrinkToFit();
-
m_identifiers.shrinkToFit();
m_functionExpressions.shrinkToFit();
m_constantRegisters.shrinkToFit();
+ if (m_exceptionInfo) {
+ m_exceptionInfo->m_expressionInfo.shrinkToFit();
+ m_exceptionInfo->m_lineInfo.shrinkToFit();
+ m_exceptionInfo->m_getByIdExceptionInfo.shrinkToFit();
+ }
+
if (m_rareData) {
m_rareData->m_exceptionHandlers.shrinkToFit();
m_rareData->m_functions.shrinkToFit();