Commit 1db480d3 authored by oliver@apple.com's avatar oliver@apple.com
Browse files

2011-06-27 Oliver Hunt <oliver@apple.com>

        Reviewed by Geoffrey Garen.

        Support throwing away non-running code even while other code is running
        https://bugs.webkit.org/show_bug.cgi?id=63485

        Add a function to CodeBlock to support unlinking direct linked callsites,
        and then with that in place add logic to discard code from any function
        that is not currently on the stack.

        The unlinking completely reverts any optimized call sites, such that they
        may be relinked again in future.

        * JavaScriptCore.exp:
        * bytecode/CodeBlock.cpp:
        (JSC::CodeBlock::unlinkCalls):
        (JSC::CodeBlock::clearEvalCache):
        * bytecode/CodeBlock.h:
        (JSC::CallLinkInfo::CallLinkInfo):
        (JSC::CallLinkInfo::unlink):
        * bytecode/EvalCodeCache.h:
        (JSC::EvalCodeCache::clear):
        * heap/Heap.cpp:
        (JSC::Heap::getConservativeRegisterRoots):
        * heap/Heap.h:
        * jit/JIT.cpp:
        (JSC::JIT::privateCompile):
        * jit/JIT.h:
        * jit/JITCall.cpp:
        (JSC::JIT::compileOpCall):
        * jit/JITWriteBarrier.h:
        (JSC::JITWriteBarrierBase::clear):
        * jsc.cpp:
        (GlobalObject::GlobalObject):
        (functionReleaseExecutableMemory):
        * runtime/Executable.cpp:
        (JSC::EvalExecutable::unlinkCalls):
        (JSC::ProgramExecutable::unlinkCalls):
        (JSC::FunctionExecutable::discardCode):
        (JSC::FunctionExecutable::unlinkCalls):
        * runtime/Executable.h:
        * runtime/JSGlobalData.cpp:
        (JSC::SafeRecompiler::returnValue):
        (JSC::SafeRecompiler::operator()):
        (JSC::JSGlobalData::releaseExecutableMemory):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@89885 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 95b9e082
2011-06-27 Oliver Hunt <oliver@apple.com>
Reviewed by Geoffrey Garen.
Support throwing away non-running code even while other code is running
https://bugs.webkit.org/show_bug.cgi?id=63485
Add a function to CodeBlock to support unlinking direct linked callsites,
and then with that in place add logic to discard code from any function
that is not currently on the stack.
The unlinking completely reverts any optimized call sites, such that they
may be relinked again in future.
* JavaScriptCore.exp:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::unlinkCalls):
(JSC::CodeBlock::clearEvalCache):
* bytecode/CodeBlock.h:
(JSC::CallLinkInfo::CallLinkInfo):
(JSC::CallLinkInfo::unlink):
* bytecode/EvalCodeCache.h:
(JSC::EvalCodeCache::clear):
* heap/Heap.cpp:
(JSC::Heap::getConservativeRegisterRoots):
* heap/Heap.h:
* jit/JIT.cpp:
(JSC::JIT::privateCompile):
* jit/JIT.h:
* jit/JITCall.cpp:
(JSC::JIT::compileOpCall):
* jit/JITWriteBarrier.h:
(JSC::JITWriteBarrierBase::clear):
* jsc.cpp:
(GlobalObject::GlobalObject):
(functionReleaseExecutableMemory):
* runtime/Executable.cpp:
(JSC::EvalExecutable::unlinkCalls):
(JSC::ProgramExecutable::unlinkCalls):
(JSC::FunctionExecutable::discardCode):
(JSC::FunctionExecutable::unlinkCalls):
* runtime/Executable.h:
* runtime/JSGlobalData.cpp:
(JSC::SafeRecompiler::returnValue):
(JSC::SafeRecompiler::operator()):
(JSC::JSGlobalData::releaseExecutableMemory):
2011-06-27 Gavin Barraclough <barraclough@apple.com>
 
Reviewed by Oliver Hunt.
......
......@@ -133,6 +133,7 @@ __ZN3JSC12JSGlobalData14resetDateCacheEv
__ZN3JSC12JSGlobalData14sharedInstanceEv
__ZN3JSC12JSGlobalData15dumpRegExpTraceEv
__ZN3JSC12JSGlobalData22clearBuiltinStructuresEv
__ZN3JSC12JSGlobalData23releaseExecutableMemoryEv
__ZN3JSC12JSGlobalData6createENS_15ThreadStackTypeE
__ZN3JSC12JSGlobalDataD1Ev
__ZN3JSC12RegExpObject6s_infoE
......
......@@ -38,6 +38,7 @@
#include "JSFunction.h"
#include "JSStaticScopeObject.h"
#include "JSValue.h"
#include "RepatchBuffer.h"
#include "UStringConcatenate.h"
#include <stdio.h>
#include <wtf/StringExtras.h>
......@@ -1676,5 +1677,27 @@ void CodeBlock::createActivation(CallFrame* callFrame)
callFrame->uncheckedR(activationRegister()) = JSValue(activation);
callFrame->setScopeChain(callFrame->scopeChain()->push(activation));
}
#if ENABLE(JIT)
void CodeBlock::unlinkCalls()
{
if (!(m_callLinkInfos.size() || m_methodCallLinkInfos.size()))
return;
RepatchBuffer repatchBuffer(this);
for (size_t i = 0; i < m_callLinkInfos.size(); i++) {
if (!m_callLinkInfos[i].isLinked())
continue;
repatchBuffer.relink(m_callLinkInfos[i].callReturnLocation, m_callLinkInfos[i].isCall ? m_globalData->jitStubs->ctiVirtualCallLink() : m_globalData->jitStubs->ctiVirtualConstructLink());
m_callLinkInfos[i].unlink();
}
}
#endif
void CodeBlock::clearEvalCache()
{
if (!m_rareData)
return;
m_rareData->m_evalCodeCache.clear();
}
} // namespace JSC
......@@ -98,6 +98,7 @@ namespace JSC {
struct CallLinkInfo {
CallLinkInfo()
: hasSeenShouldRepatch(false)
, isCall(false)
{
}
......@@ -105,9 +106,15 @@ namespace JSC {
CodeLocationDataLabelPtr hotPathBegin;
CodeLocationNearCall hotPathOther;
JITWriteBarrier<JSFunction> callee;
bool hasSeenShouldRepatch;
bool hasSeenShouldRepatch : 1;
bool isCall : 1;
bool isLinked() { return callee; }
void unlink()
{
hasSeenShouldRepatch = false;
callee.clear();
}
bool seenOnce()
{
......@@ -270,7 +277,10 @@ namespace JSC {
return 1;
return binarySearch<CallReturnOffsetToBytecodeOffset, unsigned, getCallReturnOffset>(callIndices.begin(), callIndices.size(), getJITCode().offsetOf(returnAddress.value()))->bytecodeOffset;
}
void unlinkCalls();
#endif
#if ENABLE(INTERPRETER)
unsigned bytecodeOffset(Instruction* returnAddress)
{
......@@ -340,6 +350,8 @@ namespace JSC {
void createActivation(CallFrame*);
void clearEvalCache();
#if ENABLE(INTERPRETER)
void addPropertyAccessInstruction(unsigned propertyAccessInstruction)
{
......
......@@ -69,6 +69,11 @@ namespace JSC {
void visitAggregate(SlotVisitor&);
void clear()
{
m_cacheMap.clear();
}
private:
static const unsigned maxCacheableSourceLength = 256;
static const int maxCacheEntries = 64;
......
......@@ -395,6 +395,23 @@ inline RegisterFile& Heap::registerFile()
return m_globalData->interpreter->registerFile();
}
void Heap::getConservativeRegisterRoots(HashSet<JSCell*>& roots)
{
ASSERT(isValidThreadState(m_globalData));
if (m_operationInProgress != NoOperation)
CRASH();
m_operationInProgress = Collection;
ConservativeRoots registerFileRoots(&m_blocks);
registerFile().gatherConservativeRoots(registerFileRoots);
size_t registerFileRootCount = registerFileRoots.size();
JSCell** registerRoots = registerFileRoots.roots();
for (size_t i = 0; i < registerFileRootCount; i++) {
setMarked(registerRoots[i]);
roots.add(registerRoots[i]);
}
m_operationInProgress = NoOperation;
}
void Heap::markRoots()
{
ASSERT(isValidThreadState(m_globalData));
......
......@@ -114,6 +114,7 @@ namespace JSC {
HandleSlot allocateLocalHandle() { return m_handleStack.push(); }
HandleStack* handleStack() { return &m_handleStack; }
void getConservativeRegisterRoots(HashSet<JSCell*>& roots);
private:
typedef HashSet<MarkedBlock*>::iterator BlockIterator;
......
......@@ -576,6 +576,7 @@ JITCode JIT::privateCompile(CodePtr* functionEntryArityCheck)
}
for (unsigned i = 0; i < m_codeBlock->numberOfCallLinkInfos(); ++i) {
CallLinkInfo& info = m_codeBlock->callLinkInfo(i);
info.isCall = m_callStructureStubCompilationInfo[i].isCall;
info.callReturnLocation = patchBuffer.locationOfNearCall(m_callStructureStubCompilationInfo[i].callReturnLocation);
info.hotPathBegin = patchBuffer.locationOf(m_callStructureStubCompilationInfo[i].hotPathBegin);
info.hotPathOther = patchBuffer.locationOfNearCall(m_callStructureStubCompilationInfo[i].hotPathOther);
......
......@@ -149,6 +149,7 @@ namespace JSC {
MacroAssembler::DataLabelPtr hotPathBegin;
MacroAssembler::Call hotPathOther;
MacroAssembler::Call callReturnLocation;
bool isCall;
};
struct MethodCallCompilationInfo {
......
......@@ -133,6 +133,7 @@ void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned ca
addSlowCase(jumpToSlow);
ASSERT_JIT_OFFSET(differenceBetween(addressOfLinkedFunctionCheck, jumpToSlow), patchOffsetOpCallCompareToJump);
m_callStructureStubCompilationInfo[callLinkInfoIndex].hotPathBegin = addressOfLinkedFunctionCheck;
m_callStructureStubCompilationInfo[callLinkInfoIndex].isCall = opcodeID != op_construct;
// The following is the fast case, only used whan a callee can be linked.
......
......@@ -68,6 +68,8 @@ public:
return m_location;
}
void clear() { clear(0); }
protected:
JITWriteBarrierBase()
{
......@@ -94,6 +96,14 @@ protected:
}
private:
void clear(void* clearedValue)
{
if (!m_location)
return;
if (m_location.executableAddress() != JITWriteBarrierFlag)
MacroAssembler::repatchPointer(m_location, clearedValue);
}
CodeLocationDataLabelPtr m_location;
};
......
......@@ -74,6 +74,7 @@ static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>&
static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionRun(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionLoad(ExecState*);
......@@ -154,6 +155,9 @@ GlobalObject::GlobalObject(JSGlobalData& globalData, Structure* structure, const
putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "print"), functionPrint));
putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 0, Identifier(globalExec(), "quit"), functionQuit));
putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 0, Identifier(globalExec(), "gc"), functionGC));
#ifndef NDEBUG
putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 0, Identifier(globalExec(), "releaseExecutableMemory"), functionReleaseExecutableMemory));
#endif
putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "version"), functionVersion));
putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "run"), functionRun));
putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "load"), functionLoad));
......@@ -198,6 +202,15 @@ EncodedJSValue JSC_HOST_CALL functionGC(ExecState* exec)
return JSValue::encode(jsUndefined());
}
#ifndef NDEBUG
EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState* exec)
{
JSLock lock(SilenceAssertionsOnly);
exec->globalData().releaseExecutableMemory();
return JSValue::encode(jsUndefined());
}
#endif
EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*)
{
// We need this function for compatibility with the Mozilla JS tests but for now
......
......@@ -64,6 +64,8 @@ NativeExecutable::~NativeExecutable()
{
}
const ClassInfo ScriptExecutable::s_info = { "ScriptExecutable", &ExecutableBase::s_info, 0, 0 };
const ClassInfo EvalExecutable::s_info = { "EvalExecutable", &ScriptExecutable::s_info, 0, 0 };
EvalExecutable::EvalExecutable(ExecState* exec, const SourceCode& source, bool inStrictContext)
......@@ -169,6 +171,16 @@ void EvalExecutable::visitChildren(SlotVisitor& visitor)
m_evalCodeBlock->visitAggregate(visitor);
}
void EvalExecutable::unlinkCalls()
{
#if ENABLE(JIT)
if (!m_jitCodeForCall)
return;
ASSERT(m_evalCodeBlock);
m_evalCodeBlock->unlinkCalls();
#endif
}
JSObject* ProgramExecutable::checkSyntax(ExecState* exec)
{
JSObject* exception = 0;
......@@ -226,6 +238,16 @@ JSObject* ProgramExecutable::compileInternal(ExecState* exec, ScopeChainNode* sc
return 0;
}
void ProgramExecutable::unlinkCalls()
{
#if ENABLE(JIT)
if (!m_jitCodeForCall)
return;
ASSERT(m_programCodeBlock);
m_programCodeBlock->unlinkCalls();
#endif
}
#if ENABLE(JIT)
static bool tryDFGCompile(JSGlobalData* globalData, CodeBlock* codeBlock, JITCode& jitCode, MacroAssemblerCodePtr& jitCodeWithArityCheck)
{
......@@ -382,13 +404,41 @@ void FunctionExecutable::visitChildren(SlotVisitor& visitor)
void FunctionExecutable::discardCode()
{
#if ENABLE(JIT)
// These first two checks are to handle the rare case where
// we are trying to evict code for a function during its
// codegen.
if (!m_jitCodeForCall && m_codeBlockForCall)
return;
if (!m_jitCodeForConstruct && m_codeBlockForConstruct)
return;
m_jitCodeForCall = JITCode();
m_jitCodeForConstruct = JITCode();
m_jitCodeForCallWithArityCheck = MacroAssemblerCodePtr();
m_jitCodeForConstructWithArityCheck = MacroAssemblerCodePtr();
#endif
if (m_codeBlockForCall)
m_codeBlockForCall->clearEvalCache();
m_codeBlockForCall.clear();
if (m_codeBlockForConstruct)
m_codeBlockForConstruct->clearEvalCache();
m_codeBlockForConstruct.clear();
m_numParametersForCall = NUM_PARAMETERS_NOT_COMPILED;
m_numParametersForConstruct = NUM_PARAMETERS_NOT_COMPILED;
}
void FunctionExecutable::unlinkCalls()
{
#if ENABLE(JIT)
m_jitCodeForCall = JITCode();
m_jitCodeForConstruct = JITCode();
if (!!m_jitCodeForCall) {
ASSERT(m_codeBlockForCall);
m_codeBlockForCall->unlinkCalls();
}
if (!!m_jitCodeForConstruct) {
ASSERT(m_codeBlockForConstruct);
m_codeBlockForConstruct->unlinkCalls();
}
#endif
}
......
......@@ -202,6 +202,9 @@ namespace JSC {
bool needsActivation() const { return m_hasCapturedVariables || m_features & (EvalFeature | WithFeature | CatchFeature); }
bool isStrictMode() const { return m_features & StrictModeFeature; }
virtual void unlinkCalls() = 0;
static const ClassInfo s_info;
protected:
void recordParse(CodeFeatures features, bool hasCapturedVariables, int firstLine, int lastLine)
{
......@@ -259,6 +262,7 @@ namespace JSC {
JSObject* compileInternal(ExecState*, ScopeChainNode*);
virtual void visitChildren(SlotVisitor&);
void unlinkCalls();
OwnPtr<EvalCodeBlock> m_evalCodeBlock;
};
......@@ -310,6 +314,7 @@ namespace JSC {
JSObject* compileInternal(ExecState*, ScopeChainNode*);
virtual void visitChildren(SlotVisitor&);
void unlinkCalls();
OwnPtr<ProgramCodeBlock> m_programCodeBlock;
};
......@@ -411,6 +416,7 @@ namespace JSC {
static const unsigned StructureFlags = OverridesVisitChildren | ScriptExecutable::StructureFlags;
unsigned m_numCapturedVariables : 31;
bool m_forceUsesArguments : 1;
void unlinkCalls();
RefPtr<FunctionParameters> m_parameters;
OwnPtr<FunctionCodeBlock> m_codeBlockForCall;
......
......@@ -450,11 +450,48 @@ void JSGlobalData::recompileAllJSFunctions()
heap.forEachCell<Recompiler>();
}
struct StackPreservingRecompiler : public MarkedBlock::VoidFunctor {
HashSet<FunctionExecutable*> currentlyExecutingFunctions;
void operator()(JSCell* cell)
{
if (!cell->inherits(&FunctionExecutable::s_info))
return;
FunctionExecutable* executable = static_cast<FunctionExecutable*>(cell);
if (currentlyExecutingFunctions.contains(executable))
return;
executable->discardCode();
}
};
void JSGlobalData::releaseExecutableMemory()
{
if (!dynamicGlobalObject)
recompileAllJSFunctions();
if (dynamicGlobalObject) {
StackPreservingRecompiler recompiler;
HashSet<JSCell*> roots;
heap.getConservativeRegisterRoots(roots);
HashSet<JSCell*>::iterator end = roots.end();
for (HashSet<JSCell*>::iterator ptr = roots.begin(); ptr != end; ++ptr) {
ScriptExecutable* executable = 0;
JSCell* cell = *ptr;
if (cell->inherits(&ScriptExecutable::s_info))
executable = static_cast<ScriptExecutable*>(*ptr);
else if (cell->inherits(&JSFunction::s_info)) {
JSFunction* function = asFunction(*ptr);
if (function->isHostFunction())
continue;
executable = function->jsExecutable();
} else
continue;
ASSERT(executable->inherits(&ScriptExecutable::s_info));
executable->unlinkCalls();
if (executable->inherits(&FunctionExecutable::s_info))
recompiler.currentlyExecutingFunctions.add(static_cast<FunctionExecutable*>(executable));
}
heap.forEachCell<StackPreservingRecompiler>(recompiler);
}
m_regExpCache->invalidateCode();
heap.collectAllGarbage();
}
void releaseExecutableMemory(JSGlobalData& globalData)
......
......@@ -199,6 +199,9 @@ int RegExp::match(JSGlobalData& globalData, const UString& s, int startOffset, V
void RegExp::invalidateCode()
{
if (!m_representation)
return;
m_state = NotCompiled;
m_representation.clear();
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment