https://bugs.webkit.org/show_bug.cgi?id=143232
Source/JavaScriptCore:
Reviewed by Geoffrey Garen.
This started out as an attempt to make constructors faster by detecting when a constructor is a
singleton. The idea is that each FunctionExecutable has a VariableWatchpointSet - a watchpoint
along with an inferred value - that detects if only one JSFunction has been allocated for that
executable, and if so, what that JSFunction is. Then, inside the code for the FunctionExecutable,
if the watchpoint set has an inferred value (i.e. it's been initialized and it is still valid),
we can constant-fold GetCallee.
Unfortunately, constructors don't use GetCallee anymore, so that didn't pan out. But in the
process I realized a bunch of things:
- This allows us to completely eliminate the GetCallee/GetScope sequence that we still sometimes
had even in code where our singleton-closure detection worked. That's because singleton-closure
inference worked at the op_resolve_scope, and that op_resolve_scope still needed to keep alive
the incoming scope in case we OSR exit. But by constant-folding GetCallee, that sequence
disappears. OSR exit can rematerialize the callee or the scope by just knowing their constant
values.
- Singleton detection should be a reusable thing. So, I got rid of VariableWatchpointSet and
created InferredValue. InferredValue is a cell, so it can handle its own GC magic.
FunctionExecutable uses an InferredValue to tell you about singleton JSFunctions.
- The old singleton-scope detection in op_resolve_scope is better abstracted as a SymbolTable
detecting a singleton JSSymbolTableObject. So, SymbolTable uses an InferredValue to tell you
about singleton JSSymbolTableObjects. It's curious that we want to have singleton detection in
SymbolTable if we already have it in FunctionExecutable. This comes into play in two ways.
First, it means that the DFG can realize sooner that a resolve_scope resolves to a constant
scope. Ths saves compile times and it allows prediction propagation to benefit from the
constant folding. Second, it means that we will detect a singleton scope even if it is
referenced from a non-singleton scope that is nearer to us in the scope chain. This refactoring
allows us to eliminate the function reentry watchpoint.
- This allows us to use a normal WatchpointSet, instead of a VariableWatchpointSet, for inferring
constant values in scopes. Previously when the DFG inferred that a closure variable was
constant, it wouldn't know which closure that variable was in and so it couldn't just load that
value. But now we are first inferring that the function is a singleton, which means that we
know exactly what scope it points to, and we can load the value from the scope. Using a
WatchpointSet instead of a VariableWatchpointSet saves some memory and simplifies a bunch of
code. This also means that now, the only user of VariableWatchpointSet is FunctionExecutable.
I've tweaked the code of VariableWatchpointSet to reduce its power to just be what
FunctionExecutable wants.
This also has the effect of simplifying the implementation of block scoping. Prior to this
change, block scoping would have needed to have some story for the function reentry watchpoint on
any nested symbol table. That's totally weird to think about; it's not really a function reentry
but a scope reentry. Now we don't have to think about this. Constant inference on nested scopes
will "just work": if we prove that we know the constant value of the scope then the machinery
kicks in, otherwise it doesn't.
This is a small Octane and AsmBench speed-up. AsmBench sees 1% while Octane sees sub-1%.
* CMakeLists.txt:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::CodeBlock):
(JSC::CodeBlock::finalizeUnconditionally):
(JSC::CodeBlock::valueProfileForBytecodeOffset):
* bytecode/CodeBlock.h:
(JSC::CodeBlock::valueProfileForBytecodeOffset): Deleted.
* bytecode/CodeOrigin.cpp:
(JSC::InlineCallFrame::calleeConstant):
(JSC::InlineCallFrame::visitAggregate):
* bytecode/CodeOrigin.h:
(JSC::InlineCallFrame::calleeConstant): Deleted.
(JSC::InlineCallFrame::visitAggregate): Deleted.
* bytecode/Instruction.h:
* bytecode/VariableWatchpointSet.cpp: Removed.
* bytecode/VariableWatchpointSet.h: Removed.
* bytecode/VariableWatchpointSetInlines.h: Removed.
* bytecode/VariableWriteFireDetail.cpp: Added.
(JSC::VariableWriteFireDetail::dump):
(JSC::VariableWriteFireDetail::touch):
* bytecode/VariableWriteFireDetail.h: Added.
(JSC::VariableWriteFireDetail::VariableWriteFireDetail):
* bytecode/Watchpoint.h:
(JSC::WatchpointSet::stateOnJSThread):
(JSC::WatchpointSet::startWatching):
(JSC::WatchpointSet::fireAll):
(JSC::WatchpointSet::touch):
(JSC::WatchpointSet::invalidate):
(JSC::InlineWatchpointSet::stateOnJSThread):
(JSC::InlineWatchpointSet::state):
(JSC::InlineWatchpointSet::hasBeenInvalidated):
(JSC::InlineWatchpointSet::invalidate):
(JSC::InlineWatchpointSet::touch):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::get):
(JSC::DFG::ByteCodeParser::parseBlock):
(JSC::DFG::ByteCodeParser::getScope): Deleted.
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDesiredWatchpoints.cpp:
(JSC::DFG::InferredValueAdaptor::add):
(JSC::DFG::DesiredWatchpoints::addLazily):
(JSC::DFG::DesiredWatchpoints::reallyAdd):
(JSC::DFG::DesiredWatchpoints::areStillValid):
* dfg/DFGDesiredWatchpoints.h:
(JSC::DFG::InferredValueAdaptor::hasBeenInvalidated):
(JSC::DFG::DesiredWatchpoints::isWatched):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
(JSC::DFG::Graph::tryGetConstantClosureVar):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasWatchpointSet):
(JSC::DFG::Node::watchpointSet):
(JSC::DFG::Node::hasVariableWatchpointSet): Deleted.
(JSC::DFG::Node::variableWatchpointSet): Deleted.
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileNewFunction):
(JSC::DFG::SpeculativeJIT::compileCreateActivation):
(JSC::DFG::SpeculativeJIT::compileNotifyWrite):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGVarargsForwardingPhase.cpp:
* ftl/FTLIntrinsicRepository.h:
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileCreateActivation):
(JSC::FTL::LowerDFGToLLVM::compileNewFunction):
(JSC::FTL::LowerDFGToLLVM::compileNotifyWrite):
* interpreter/Interpreter.cpp:
(JSC::StackFrame::friendlySourceURL):
(JSC::StackFrame::friendlyFunctionName):
* interpreter/Interpreter.h:
(JSC::StackFrame::friendlySourceURL): Deleted.
(JSC::StackFrame::friendlyFunctionName): Deleted.
* jit/JIT.cpp:
(JSC::JIT::emitNotifyWrite):
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_touch_entry): Deleted.
* jit/JITOperations.cpp:
* jit/JITOperations.h:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emitPutGlobalVar):
(JSC::JIT::emitPutClosureVar):
(JSC::JIT::emitNotifyWrite): Deleted.
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emitPutGlobalVar):
(JSC::JIT::emitPutClosureVar):
(JSC::JIT::emitNotifyWrite): Deleted.
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL): Deleted.
* runtime/CommonSlowPaths.h:
* runtime/Executable.cpp:
(JSC::FunctionExecutable::finishCreation):
(JSC::FunctionExecutable::visitChildren):
* runtime/Executable.h:
(JSC::FunctionExecutable::singletonFunction):
* runtime/InferredValue.cpp: Added.
(JSC::InferredValue::create):
(JSC::InferredValue::destroy):
(JSC::InferredValue::createStructure):
(JSC::InferredValue::visitChildren):
(JSC::InferredValue::InferredValue):
(JSC::InferredValue::~InferredValue):
(JSC::InferredValue::notifyWriteSlow):
(JSC::InferredValue::ValueCleanup::ValueCleanup):
(JSC::InferredValue::ValueCleanup::~ValueCleanup):
(JSC::InferredValue::ValueCleanup::finalizeUnconditionally):
* runtime/InferredValue.h: Added.
(JSC::InferredValue::inferredValue):
(JSC::InferredValue::state):
(JSC::InferredValue::isStillValid):
(JSC::InferredValue::hasBeenInvalidated):
(JSC::InferredValue::add):
(JSC::InferredValue::notifyWrite):
(JSC::InferredValue::invalidate):
* runtime/JSEnvironmentRecord.cpp:
(JSC::JSEnvironmentRecord::visitChildren):
* runtime/JSEnvironmentRecord.h:
(JSC::JSEnvironmentRecord::isValid):
(JSC::JSEnvironmentRecord::finishCreation):
* runtime/JSFunction.cpp:
(JSC::JSFunction::create):
* runtime/JSFunction.h:
(JSC::JSFunction::createWithInvalidatedReallocationWatchpoint):
(JSC::JSFunction::createImpl):
(JSC::JSFunction::create): Deleted.
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::addGlobalVar):
(JSC::JSGlobalObject::addFunction):
* runtime/JSGlobalObject.h:
* runtime/JSLexicalEnvironment.cpp:
(JSC::JSLexicalEnvironment::symbolTablePut):
* runtime/JSScope.h:
(JSC::ResolveOp::ResolveOp):
* runtime/JSSegmentedVariableObject.h:
(JSC::JSSegmentedVariableObject::finishCreation):
* runtime/JSSymbolTableObject.h:
(JSC::JSSymbolTableObject::JSSymbolTableObject):
(JSC::JSSymbolTableObject::setSymbolTable):
(JSC::symbolTablePut):
(JSC::symbolTablePutWithAttributes):
* runtime/PutPropertySlot.h:
* runtime/SymbolTable.cpp:
(JSC::SymbolTableEntry::prepareToWatch):
(JSC::SymbolTable::SymbolTable):
(JSC::SymbolTable::finishCreation):
(JSC::SymbolTable::visitChildren):
(JSC::SymbolTableEntry::inferredValue): Deleted.
(JSC::SymbolTableEntry::notifyWriteSlow): Deleted.
(JSC::SymbolTable::WatchpointCleanup::WatchpointCleanup): Deleted.
(JSC::SymbolTable::WatchpointCleanup::~WatchpointCleanup): Deleted.
(JSC::SymbolTable::WatchpointCleanup::finalizeUnconditionally): Deleted.
* runtime/SymbolTable.h:
(JSC::SymbolTableEntry::disableWatching):
(JSC::SymbolTableEntry::watchpointSet):
(JSC::SymbolTable::singletonScope):
(JSC::SymbolTableEntry::notifyWrite): Deleted.
* runtime/TypeProfiler.cpp:
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:
* tests/stress/infer-uninitialized-closure-var.js: Added.
(foo.f):
(foo):
* tests/stress/singleton-scope-then-overwrite.js: Added.
(foo.f):
(foo):
* tests/stress/singleton-scope-then-realloc-and-overwrite.js: Added.
(foo):
* tests/stress/singleton-scope-then-realloc.js: Added.
(foo):
LayoutTests:
Reviewed by Geoffrey Garen and Michael Saboff.
* js/regress/create-lots-of-functions-expected.txt: Added.
* js/regress/create-lots-of-functions.html: Added.
* js/regress/no-inline-constructor-expected.txt: Added.
* js/regress/no-inline-constructor.html: Added.
* js/regress/script-tests/create-lots-of-functions.js: Added.
* js/regress/script-tests/no-inline-constructor.js: Added.
* js/regress/script-tests/singleton-scope.js: Added.
* js/regress/singleton-scope-expected.txt: Added.
* js/regress/singleton-scope.html: Added.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@182759
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2015-04-08 Filip Pizlo <fpizlo@apple.com>
+
+ JSC should detect singleton functions
+ https://bugs.webkit.org/show_bug.cgi?id=143232
+
+ Reviewed by Geoffrey Garen and Michael Saboff.
+
+ * js/regress/create-lots-of-functions-expected.txt: Added.
+ * js/regress/create-lots-of-functions.html: Added.
+ * js/regress/no-inline-constructor-expected.txt: Added.
+ * js/regress/no-inline-constructor.html: Added.
+ * js/regress/script-tests/create-lots-of-functions.js: Added.
+ * js/regress/script-tests/no-inline-constructor.js: Added.
+ * js/regress/script-tests/singleton-scope.js: Added.
+ * js/regress/singleton-scope-expected.txt: Added.
+ * js/regress/singleton-scope.html: Added.
+
2015-04-13 Simon Fraser <simon.fraser@apple.com>
Add missing layout test result.
--- /dev/null
+JSRegress/create-lots-of-functions
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/create-lots-of-functions.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
--- /dev/null
+JSRegress/no-inline-constructor
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/no-inline-constructor.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
--- /dev/null
+function foo() {
+ var result = 0;
+ for (var i = 0; i < 100; ++i)
+ result += (function() { return i })();
+ return result;
+}
+
+for (var i = 0; i < 10000; ++i) {
+ var result = foo();
+ if (result != 4950)
+ throw "Error: bad result: " + result;
+}
--- /dev/null
+function Foo() {
+ var o = {f:{f:{f:{f:42}}}};
+ this.f = 42;
+}
+
+noInline(Foo);
+
+for (var i = 0; i < 3000000; ++i) {
+ var result = new Foo();
+ if (result.f != 42)
+ throw "Error: bad result: " + result.f;
+}
+
--- /dev/null
+function foo() {
+ var x = 42;
+ var y = 43;
+ return function(z) {
+ var result = x + y;
+ x += z;
+ y += z;
+ return result;
+ }
+}
+
+var f = foo();
+noInline(f);
+
+for (var i = 0; i < 10000000; ++i)
+ f(1);
--- /dev/null
+JSRegress/singleton-scope
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/singleton-scope.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
bytecode/UnlinkedCodeBlock.cpp
bytecode/UnlinkedInstructionStream.cpp
bytecode/ValueRecovery.cpp
- bytecode/VariableWatchpointSet.cpp
+ bytecode/VariableWriteFireDetail.cpp
bytecode/VirtualRegister.cpp
bytecode/Watchpoint.cpp
runtime/GetterSetter.cpp
runtime/Identifier.cpp
runtime/IndexingType.cpp
+ runtime/InferredValue.cpp
runtime/InitializeThreading.cpp
runtime/IntendedStructureChain.cpp
runtime/InternalFunction.cpp
+2015-04-08 Filip Pizlo <fpizlo@apple.com>
+
+ JSC should detect singleton functions
+ https://bugs.webkit.org/show_bug.cgi?id=143232
+
+ Reviewed by Geoffrey Garen.
+
+ This started out as an attempt to make constructors faster by detecting when a constructor is a
+ singleton. The idea is that each FunctionExecutable has a VariableWatchpointSet - a watchpoint
+ along with an inferred value - that detects if only one JSFunction has been allocated for that
+ executable, and if so, what that JSFunction is. Then, inside the code for the FunctionExecutable,
+ if the watchpoint set has an inferred value (i.e. it's been initialized and it is still valid),
+ we can constant-fold GetCallee.
+
+ Unfortunately, constructors don't use GetCallee anymore, so that didn't pan out. But in the
+ process I realized a bunch of things:
+
+ - This allows us to completely eliminate the GetCallee/GetScope sequence that we still sometimes
+ had even in code where our singleton-closure detection worked. That's because singleton-closure
+ inference worked at the op_resolve_scope, and that op_resolve_scope still needed to keep alive
+ the incoming scope in case we OSR exit. But by constant-folding GetCallee, that sequence
+ disappears. OSR exit can rematerialize the callee or the scope by just knowing their constant
+ values.
+
+ - Singleton detection should be a reusable thing. So, I got rid of VariableWatchpointSet and
+ created InferredValue. InferredValue is a cell, so it can handle its own GC magic.
+ FunctionExecutable uses an InferredValue to tell you about singleton JSFunctions.
+
+ - The old singleton-scope detection in op_resolve_scope is better abstracted as a SymbolTable
+ detecting a singleton JSSymbolTableObject. So, SymbolTable uses an InferredValue to tell you
+ about singleton JSSymbolTableObjects. It's curious that we want to have singleton detection in
+ SymbolTable if we already have it in FunctionExecutable. This comes into play in two ways.
+ First, it means that the DFG can realize sooner that a resolve_scope resolves to a constant
+ scope. Ths saves compile times and it allows prediction propagation to benefit from the
+ constant folding. Second, it means that we will detect a singleton scope even if it is
+ referenced from a non-singleton scope that is nearer to us in the scope chain. This refactoring
+ allows us to eliminate the function reentry watchpoint.
+
+ - This allows us to use a normal WatchpointSet, instead of a VariableWatchpointSet, for inferring
+ constant values in scopes. Previously when the DFG inferred that a closure variable was
+ constant, it wouldn't know which closure that variable was in and so it couldn't just load that
+ value. But now we are first inferring that the function is a singleton, which means that we
+ know exactly what scope it points to, and we can load the value from the scope. Using a
+ WatchpointSet instead of a VariableWatchpointSet saves some memory and simplifies a bunch of
+ code. This also means that now, the only user of VariableWatchpointSet is FunctionExecutable.
+ I've tweaked the code of VariableWatchpointSet to reduce its power to just be what
+ FunctionExecutable wants.
+
+ This also has the effect of simplifying the implementation of block scoping. Prior to this
+ change, block scoping would have needed to have some story for the function reentry watchpoint on
+ any nested symbol table. That's totally weird to think about; it's not really a function reentry
+ but a scope reentry. Now we don't have to think about this. Constant inference on nested scopes
+ will "just work": if we prove that we know the constant value of the scope then the machinery
+ kicks in, otherwise it doesn't.
+
+ This is a small Octane and AsmBench speed-up. AsmBench sees 1% while Octane sees sub-1%.
+
+ * CMakeLists.txt:
+ * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+ * bytecode/BytecodeList.json:
+ * bytecode/BytecodeUseDef.h:
+ (JSC::computeUsesForBytecodeOffset):
+ (JSC::computeDefsForBytecodeOffset):
+ * bytecode/CodeBlock.cpp:
+ (JSC::CodeBlock::dumpBytecode):
+ (JSC::CodeBlock::CodeBlock):
+ (JSC::CodeBlock::finalizeUnconditionally):
+ (JSC::CodeBlock::valueProfileForBytecodeOffset):
+ * bytecode/CodeBlock.h:
+ (JSC::CodeBlock::valueProfileForBytecodeOffset): Deleted.
+ * bytecode/CodeOrigin.cpp:
+ (JSC::InlineCallFrame::calleeConstant):
+ (JSC::InlineCallFrame::visitAggregate):
+ * bytecode/CodeOrigin.h:
+ (JSC::InlineCallFrame::calleeConstant): Deleted.
+ (JSC::InlineCallFrame::visitAggregate): Deleted.
+ * bytecode/Instruction.h:
+ * bytecode/VariableWatchpointSet.cpp: Removed.
+ * bytecode/VariableWatchpointSet.h: Removed.
+ * bytecode/VariableWatchpointSetInlines.h: Removed.
+ * bytecode/VariableWriteFireDetail.cpp: Added.
+ (JSC::VariableWriteFireDetail::dump):
+ (JSC::VariableWriteFireDetail::touch):
+ * bytecode/VariableWriteFireDetail.h: Added.
+ (JSC::VariableWriteFireDetail::VariableWriteFireDetail):
+ * bytecode/Watchpoint.h:
+ (JSC::WatchpointSet::stateOnJSThread):
+ (JSC::WatchpointSet::startWatching):
+ (JSC::WatchpointSet::fireAll):
+ (JSC::WatchpointSet::touch):
+ (JSC::WatchpointSet::invalidate):
+ (JSC::InlineWatchpointSet::stateOnJSThread):
+ (JSC::InlineWatchpointSet::state):
+ (JSC::InlineWatchpointSet::hasBeenInvalidated):
+ (JSC::InlineWatchpointSet::invalidate):
+ (JSC::InlineWatchpointSet::touch):
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::BytecodeGenerator):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::get):
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ (JSC::DFG::ByteCodeParser::getScope): Deleted.
+ * dfg/DFGCapabilities.cpp:
+ (JSC::DFG::capabilityLevel):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGDesiredWatchpoints.cpp:
+ (JSC::DFG::InferredValueAdaptor::add):
+ (JSC::DFG::DesiredWatchpoints::addLazily):
+ (JSC::DFG::DesiredWatchpoints::reallyAdd):
+ (JSC::DFG::DesiredWatchpoints::areStillValid):
+ * dfg/DFGDesiredWatchpoints.h:
+ (JSC::DFG::InferredValueAdaptor::hasBeenInvalidated):
+ (JSC::DFG::DesiredWatchpoints::isWatched):
+ * dfg/DFGGraph.cpp:
+ (JSC::DFG::Graph::dump):
+ (JSC::DFG::Graph::tryGetConstantClosureVar):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::hasWatchpointSet):
+ (JSC::DFG::Node::watchpointSet):
+ (JSC::DFG::Node::hasVariableWatchpointSet): Deleted.
+ (JSC::DFG::Node::variableWatchpointSet): Deleted.
+ * dfg/DFGOperations.cpp:
+ * dfg/DFGOperations.h:
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileNewFunction):
+ (JSC::DFG::SpeculativeJIT::compileCreateActivation):
+ (JSC::DFG::SpeculativeJIT::compileNotifyWrite):
+ * dfg/DFGSpeculativeJIT.h:
+ (JSC::DFG::SpeculativeJIT::callOperation):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGVarargsForwardingPhase.cpp:
+ * ftl/FTLIntrinsicRepository.h:
+ * ftl/FTLLowerDFGToLLVM.cpp:
+ (JSC::FTL::LowerDFGToLLVM::compileCreateActivation):
+ (JSC::FTL::LowerDFGToLLVM::compileNewFunction):
+ (JSC::FTL::LowerDFGToLLVM::compileNotifyWrite):
+ * interpreter/Interpreter.cpp:
+ (JSC::StackFrame::friendlySourceURL):
+ (JSC::StackFrame::friendlyFunctionName):
+ * interpreter/Interpreter.h:
+ (JSC::StackFrame::friendlySourceURL): Deleted.
+ (JSC::StackFrame::friendlyFunctionName): Deleted.
+ * jit/JIT.cpp:
+ (JSC::JIT::emitNotifyWrite):
+ (JSC::JIT::privateCompileMainPass):
+ * jit/JIT.h:
+ * jit/JITOpcodes.cpp:
+ (JSC::JIT::emit_op_touch_entry): Deleted.
+ * jit/JITOperations.cpp:
+ * jit/JITOperations.h:
+ * jit/JITPropertyAccess.cpp:
+ (JSC::JIT::emitPutGlobalVar):
+ (JSC::JIT::emitPutClosureVar):
+ (JSC::JIT::emitNotifyWrite): Deleted.
+ * jit/JITPropertyAccess32_64.cpp:
+ (JSC::JIT::emitPutGlobalVar):
+ (JSC::JIT::emitPutClosureVar):
+ (JSC::JIT::emitNotifyWrite): Deleted.
+ * llint/LLIntSlowPaths.cpp:
+ (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+ * llint/LowLevelInterpreter.asm:
+ * llint/LowLevelInterpreter32_64.asm:
+ * llint/LowLevelInterpreter64.asm:
+ * runtime/CommonSlowPaths.cpp:
+ (JSC::SLOW_PATH_DECL): Deleted.
+ * runtime/CommonSlowPaths.h:
+ * runtime/Executable.cpp:
+ (JSC::FunctionExecutable::finishCreation):
+ (JSC::FunctionExecutable::visitChildren):
+ * runtime/Executable.h:
+ (JSC::FunctionExecutable::singletonFunction):
+ * runtime/InferredValue.cpp: Added.
+ (JSC::InferredValue::create):
+ (JSC::InferredValue::destroy):
+ (JSC::InferredValue::createStructure):
+ (JSC::InferredValue::visitChildren):
+ (JSC::InferredValue::InferredValue):
+ (JSC::InferredValue::~InferredValue):
+ (JSC::InferredValue::notifyWriteSlow):
+ (JSC::InferredValue::ValueCleanup::ValueCleanup):
+ (JSC::InferredValue::ValueCleanup::~ValueCleanup):
+ (JSC::InferredValue::ValueCleanup::finalizeUnconditionally):
+ * runtime/InferredValue.h: Added.
+ (JSC::InferredValue::inferredValue):
+ (JSC::InferredValue::state):
+ (JSC::InferredValue::isStillValid):
+ (JSC::InferredValue::hasBeenInvalidated):
+ (JSC::InferredValue::add):
+ (JSC::InferredValue::notifyWrite):
+ (JSC::InferredValue::invalidate):
+ * runtime/JSEnvironmentRecord.cpp:
+ (JSC::JSEnvironmentRecord::visitChildren):
+ * runtime/JSEnvironmentRecord.h:
+ (JSC::JSEnvironmentRecord::isValid):
+ (JSC::JSEnvironmentRecord::finishCreation):
+ * runtime/JSFunction.cpp:
+ (JSC::JSFunction::create):
+ * runtime/JSFunction.h:
+ (JSC::JSFunction::createWithInvalidatedReallocationWatchpoint):
+ (JSC::JSFunction::createImpl):
+ (JSC::JSFunction::create): Deleted.
+ * runtime/JSGlobalObject.cpp:
+ (JSC::JSGlobalObject::addGlobalVar):
+ (JSC::JSGlobalObject::addFunction):
+ * runtime/JSGlobalObject.h:
+ * runtime/JSLexicalEnvironment.cpp:
+ (JSC::JSLexicalEnvironment::symbolTablePut):
+ * runtime/JSScope.h:
+ (JSC::ResolveOp::ResolveOp):
+ * runtime/JSSegmentedVariableObject.h:
+ (JSC::JSSegmentedVariableObject::finishCreation):
+ * runtime/JSSymbolTableObject.h:
+ (JSC::JSSymbolTableObject::JSSymbolTableObject):
+ (JSC::JSSymbolTableObject::setSymbolTable):
+ (JSC::symbolTablePut):
+ (JSC::symbolTablePutWithAttributes):
+ * runtime/PutPropertySlot.h:
+ * runtime/SymbolTable.cpp:
+ (JSC::SymbolTableEntry::prepareToWatch):
+ (JSC::SymbolTable::SymbolTable):
+ (JSC::SymbolTable::finishCreation):
+ (JSC::SymbolTable::visitChildren):
+ (JSC::SymbolTableEntry::inferredValue): Deleted.
+ (JSC::SymbolTableEntry::notifyWriteSlow): Deleted.
+ (JSC::SymbolTable::WatchpointCleanup::WatchpointCleanup): Deleted.
+ (JSC::SymbolTable::WatchpointCleanup::~WatchpointCleanup): Deleted.
+ (JSC::SymbolTable::WatchpointCleanup::finalizeUnconditionally): Deleted.
+ * runtime/SymbolTable.h:
+ (JSC::SymbolTableEntry::disableWatching):
+ (JSC::SymbolTableEntry::watchpointSet):
+ (JSC::SymbolTable::singletonScope):
+ (JSC::SymbolTableEntry::notifyWrite): Deleted.
+ * runtime/TypeProfiler.cpp:
+ * runtime/VM.cpp:
+ (JSC::VM::VM):
+ * runtime/VM.h:
+ * tests/stress/infer-uninitialized-closure-var.js: Added.
+ (foo.f):
+ (foo):
+ * tests/stress/singleton-scope-then-overwrite.js: Added.
+ (foo.f):
+ (foo):
+ * tests/stress/singleton-scope-then-realloc-and-overwrite.js: Added.
+ (foo):
+ * tests/stress/singleton-scope-then-realloc.js: Added.
+ (foo):
+
2015-04-13 Andreas Kling <akling@apple.com>
Don't segregate heap objects based on Structure immortality.
<ClCompile Include="..\bytecode\UnlinkedCodeBlock.cpp" />
<ClCompile Include="..\bytecode\UnlinkedInstructionStream.cpp" />
<ClCompile Include="..\bytecode\ValueRecovery.cpp" />
- <ClCompile Include="..\bytecode\VariableWatchpointSet.cpp" />
+ <ClCompile Include="..\bytecode\VariableWriteFireDetail.cpp" />
<ClCompile Include="..\bytecode\VirtualRegister.cpp" />
<ClCompile Include="..\bytecode\Watchpoint.cpp" />
<ClCompile Include="..\bytecompiler\BytecodeGenerator.cpp" />
<ClCompile Include="..\runtime\GetterSetter.cpp" />
<ClCompile Include="..\runtime\Identifier.cpp" />
<ClCompile Include="..\runtime\IndexingType.cpp" />
+ <ClCompile Include="..\runtime\InferredValue.cpp" />
<ClCompile Include="..\runtime\InitializeThreading.cpp" />
<ClCompile Include="..\runtime\IntendedStructureChain.cpp" />
<ClCompile Include="..\runtime\InternalFunction.cpp" />
<ClInclude Include="..\bytecode\UnlinkedInstructionStream.h" />
<ClInclude Include="..\bytecode\ValueProfile.h" />
<ClInclude Include="..\bytecode\ValueRecovery.h" />
- <ClInclude Include="..\bytecode\VariableWatchpointSet.h" />
+ <ClInclude Include="..\bytecode\VariableWriteFireDetail.h" />
<ClInclude Include="..\bytecode\VirtualRegister.h" />
<ClInclude Include="..\bytecode\Watchpoint.h" />
<ClInclude Include="..\bytecompiler\BytecodeGenerator.h" />
<ClInclude Include="..\runtime\IndexingHeader.h" />
<ClInclude Include="..\runtime\IndexingHeaderInlines.h" />
<ClInclude Include="..\runtime\IndexingType.h" />
+ <ClInclude Include="..\runtime\InferredValue.h" />
<ClInclude Include="..\runtime\InitializeThreading.h" />
<ClInclude Include="..\runtime\Int16Array.h" />
<ClInclude Include="..\runtime\Int32Array.h" />
0F6B1CC61862C47800845D97 /* FTLUnwindInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F6B1CC21862C47800845D97 /* FTLUnwindInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F6B1CC918641DF800845D97 /* ArityCheckFailReturnThunks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F6B1CC718641DF800845D97 /* ArityCheckFailReturnThunks.cpp */; };
0F6B1CCA18641DF800845D97 /* ArityCheckFailReturnThunks.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F6B1CC818641DF800845D97 /* ArityCheckFailReturnThunks.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 0F6C73501AC9F99F00BE1682 /* VariableWriteFireDetail.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F6C734E1AC9F99F00BE1682 /* VariableWriteFireDetail.cpp */; };
+ 0F6C73511AC9F99F00BE1682 /* VariableWriteFireDetail.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F6C734F1AC9F99F00BE1682 /* VariableWriteFireDetail.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F6E845A19030BEF00562741 /* DFGVariableAccessData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F6E845919030BEF00562741 /* DFGVariableAccessData.cpp */; };
0F6FC750196110A800E1D02D /* ComplexGetStatus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F6FC74E196110A800E1D02D /* ComplexGetStatus.cpp */; };
0F6FC751196110A800E1D02D /* ComplexGetStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F6FC74F196110A800E1D02D /* ComplexGetStatus.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F8F94421667633500D61971 /* CodeType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F943F1667632D00D61971 /* CodeType.cpp */; };
0F8F94441667635400D61971 /* JITCode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F94431667635200D61971 /* JITCode.cpp */; };
0F8F9446166764F100D61971 /* CodeOrigin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F9445166764EE00D61971 /* CodeOrigin.cpp */; };
- 0F9181C718415CA50057B669 /* VariableWatchpointSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F9181C618415CA50057B669 /* VariableWatchpointSet.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F919D0C157EE09F004A4E7D /* JSSymbolTableObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F919D09157EE09D004A4E7D /* JSSymbolTableObject.cpp */; };
0F919D0D157EE0A2004A4E7D /* JSSymbolTableObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F919D0A157EE09D004A4E7D /* JSSymbolTableObject.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F919D10157F3329004A4E7D /* JSSegmentedVariableObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F919D0E157F3327004A4E7D /* JSSegmentedVariableObject.cpp */; };
0FC97F4018202119002C9B26 /* DFGJumpReplacement.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC97F3A18202119002C9B26 /* DFGJumpReplacement.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FC97F4118202119002C9B26 /* DFGWatchpointCollectionPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FC97F3B18202119002C9B26 /* DFGWatchpointCollectionPhase.cpp */; };
0FC97F4218202119002C9B26 /* DFGWatchpointCollectionPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC97F3C18202119002C9B26 /* DFGWatchpointCollectionPhase.h */; settings = {ATTRIBUTES = (Private, ); }; };
- 0FCA9113195E66A000426438 /* VariableWatchpointSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FCA9112195E66A000426438 /* VariableWatchpointSet.cpp */; };
0FCCAE4516D0CF7400D0C65B /* ParserError.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FCCAE4316D0CF6E00D0C65B /* ParserError.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FCEFAAB1804C13E00472CE4 /* FTLSaveRestore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FCEFAA91804C13E00472CE4 /* FTLSaveRestore.cpp */; };
0FCEFAAC1804C13E00472CE4 /* FTLSaveRestore.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FCEFAAA1804C13E00472CE4 /* FTLSaveRestore.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FF729BE166AD360000F5BA3 /* ProfilerExecutionCounter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF7299E166AD347000F5BA3 /* ProfilerExecutionCounter.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FF729BF166AD360000F5BA3 /* ProfilerOrigin.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF729A0166AD347000F5BA3 /* ProfilerOrigin.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FF729C0166AD360000F5BA3 /* ProfilerOriginStack.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF729A2166AD347000F5BA3 /* ProfilerOriginStack.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 0FF8BDEA1AD4CF7100DFE884 /* InferredValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FF8BDE81AD4CF7100DFE884 /* InferredValue.cpp */; };
+ 0FF8BDEB1AD4CF7100DFE884 /* InferredValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF8BDE91AD4CF7100DFE884 /* InferredValue.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FF922D414F46B410041A24E /* LLIntOffsetsExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F4680A114BA7F8200BFE272 /* LLIntOffsetsExtractor.cpp */; };
0FFA549716B8835000B3A982 /* A64DOpcode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 652A3A221651C69700A80AFE /* A64DOpcode.cpp */; };
0FFA549816B8835300B3A982 /* A64DOpcode.h in Headers */ = {isa = PBXBuildFile; fileRef = 652A3A231651C69700A80AFE /* A64DOpcode.h */; settings = {ATTRIBUTES = (Private, ); }; };
FE20CE9E15F04A9500DF3430 /* LLIntCLoop.h in Headers */ = {isa = PBXBuildFile; fileRef = FE20CE9C15F04A9500DF3430 /* LLIntCLoop.h */; settings = {ATTRIBUTES = (Private, ); }; };
FE4A331F15BD2E07006F54F3 /* VMInspector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE4A331D15BD2E07006F54F3 /* VMInspector.cpp */; };
FE4A332015BD2E07006F54F3 /* VMInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = FE4A331E15BD2E07006F54F3 /* VMInspector.h */; settings = {ATTRIBUTES = (Private, ); }; };
- FE5248F9191442D900B7FDE4 /* VariableWatchpointSetInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = FE5248F8191442D900B7FDE4 /* VariableWatchpointSetInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
FE5932A7183C5A2600A1ECCC /* VMEntryScope.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE5932A5183C5A2600A1ECCC /* VMEntryScope.cpp */; };
FE5932A8183C5A2600A1ECCC /* VMEntryScope.h in Headers */ = {isa = PBXBuildFile; fileRef = FE5932A6183C5A2600A1ECCC /* VMEntryScope.h */; settings = {ATTRIBUTES = (Private, ); }; };
FE7BA60F1A1A7CEC00F1F7B4 /* HeapVerifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE7BA60D1A1A7CEC00F1F7B4 /* HeapVerifier.cpp */; };
0F6B1CC21862C47800845D97 /* FTLUnwindInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLUnwindInfo.h; path = ftl/FTLUnwindInfo.h; sourceTree = "<group>"; };
0F6B1CC718641DF800845D97 /* ArityCheckFailReturnThunks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ArityCheckFailReturnThunks.cpp; sourceTree = "<group>"; };
0F6B1CC818641DF800845D97 /* ArityCheckFailReturnThunks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArityCheckFailReturnThunks.h; sourceTree = "<group>"; };
+ 0F6C734E1AC9F99F00BE1682 /* VariableWriteFireDetail.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VariableWriteFireDetail.cpp; sourceTree = "<group>"; };
+ 0F6C734F1AC9F99F00BE1682 /* VariableWriteFireDetail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VariableWriteFireDetail.h; sourceTree = "<group>"; };
0F6E845919030BEF00562741 /* DFGVariableAccessData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGVariableAccessData.cpp; path = dfg/DFGVariableAccessData.cpp; sourceTree = "<group>"; };
0F6FC74E196110A800E1D02D /* ComplexGetStatus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ComplexGetStatus.cpp; sourceTree = "<group>"; };
0F6FC74F196110A800E1D02D /* ComplexGetStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComplexGetStatus.h; sourceTree = "<group>"; };
0F8F943F1667632D00D61971 /* CodeType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CodeType.cpp; sourceTree = "<group>"; };
0F8F94431667635200D61971 /* JITCode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JITCode.cpp; sourceTree = "<group>"; };
0F8F9445166764EE00D61971 /* CodeOrigin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CodeOrigin.cpp; sourceTree = "<group>"; };
- 0F9181C618415CA50057B669 /* VariableWatchpointSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VariableWatchpointSet.h; sourceTree = "<group>"; };
0F919D09157EE09D004A4E7D /* JSSymbolTableObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSSymbolTableObject.cpp; sourceTree = "<group>"; };
0F919D0A157EE09D004A4E7D /* JSSymbolTableObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSSymbolTableObject.h; sourceTree = "<group>"; };
0F919D0E157F3327004A4E7D /* JSSegmentedVariableObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSSegmentedVariableObject.cpp; sourceTree = "<group>"; };
0FC97F3A18202119002C9B26 /* DFGJumpReplacement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGJumpReplacement.h; path = dfg/DFGJumpReplacement.h; sourceTree = "<group>"; };
0FC97F3B18202119002C9B26 /* DFGWatchpointCollectionPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGWatchpointCollectionPhase.cpp; path = dfg/DFGWatchpointCollectionPhase.cpp; sourceTree = "<group>"; };
0FC97F3C18202119002C9B26 /* DFGWatchpointCollectionPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGWatchpointCollectionPhase.h; path = dfg/DFGWatchpointCollectionPhase.h; sourceTree = "<group>"; };
- 0FCA9112195E66A000426438 /* VariableWatchpointSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VariableWatchpointSet.cpp; sourceTree = "<group>"; };
0FCB408515C0A3C30048932B /* SlotVisitorInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SlotVisitorInlines.h; sourceTree = "<group>"; };
0FCCAE4316D0CF6E00D0C65B /* ParserError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParserError.h; sourceTree = "<group>"; };
0FCEFAA91804C13E00472CE4 /* FTLSaveRestore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLSaveRestore.cpp; path = ftl/FTLSaveRestore.cpp; sourceTree = "<group>"; };
0FF729A0166AD347000F5BA3 /* ProfilerOrigin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProfilerOrigin.h; path = profiler/ProfilerOrigin.h; sourceTree = "<group>"; };
0FF729A1166AD347000F5BA3 /* ProfilerOriginStack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ProfilerOriginStack.cpp; path = profiler/ProfilerOriginStack.cpp; sourceTree = "<group>"; };
0FF729A2166AD347000F5BA3 /* ProfilerOriginStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProfilerOriginStack.h; path = profiler/ProfilerOriginStack.h; sourceTree = "<group>"; };
+ 0FF8BDE81AD4CF7100DFE884 /* InferredValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InferredValue.cpp; sourceTree = "<group>"; };
+ 0FF8BDE91AD4CF7100DFE884 /* InferredValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InferredValue.h; sourceTree = "<group>"; };
0FF922CF14F46B130041A24E /* JSCLLIntOffsetsExtractor */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = JSCLLIntOffsetsExtractor; sourceTree = BUILT_PRODUCTS_DIR; };
0FFC99D0184EC8AD009C10AB /* ConstantMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConstantMode.h; sourceTree = "<group>"; };
0FFC99D2184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ArrayBufferNeuteringWatchpoint.cpp; sourceTree = "<group>"; };
FE20CE9C15F04A9500DF3430 /* LLIntCLoop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LLIntCLoop.h; path = llint/LLIntCLoop.h; sourceTree = "<group>"; };
FE4A331D15BD2E07006F54F3 /* VMInspector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VMInspector.cpp; sourceTree = "<group>"; };
FE4A331E15BD2E07006F54F3 /* VMInspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VMInspector.h; sourceTree = "<group>"; };
- FE5248F8191442D900B7FDE4 /* VariableWatchpointSetInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VariableWatchpointSetInlines.h; sourceTree = "<group>"; };
FE5932A5183C5A2600A1ECCC /* VMEntryScope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VMEntryScope.cpp; sourceTree = "<group>"; };
FE5932A6183C5A2600A1ECCC /* VMEntryScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VMEntryScope.h; sourceTree = "<group>"; };
FE7BA60D1A1A7CEC00F1F7B4 /* HeapVerifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HeapVerifier.cpp; sourceTree = "<group>"; };
0FB7F38E15ED8E3800F167B2 /* IndexingHeaderInlines.h */,
0F13E04C16164A1B00DC8DE7 /* IndexingType.cpp */,
0FB7F38F15ED8E3800F167B2 /* IndexingType.h */,
+ 0FF8BDE81AD4CF7100DFE884 /* InferredValue.cpp */,
+ 0FF8BDE91AD4CF7100DFE884 /* InferredValue.h */,
E178636C0D9BEEC300D74E75 /* InitializeThreading.cpp */,
E178633F0D9BEC0000D74E75 /* InitializeThreading.h */,
A7A8AF2B17ADB5F3005AB174 /* Int8Array.h */,
0F963B3613FC6FDE0002D9B2 /* ValueProfile.h */,
0F24E55717F74EDB00ABB217 /* ValueRecovery.cpp */,
0F426A451460CBAB00131F8F /* ValueRecovery.h */,
- 0FCA9112195E66A000426438 /* VariableWatchpointSet.cpp */,
- 0F9181C618415CA50057B669 /* VariableWatchpointSet.h */,
- FE5248F8191442D900B7FDE4 /* VariableWatchpointSetInlines.h */,
+ 0F6C734E1AC9F99F00BE1682 /* VariableWriteFireDetail.cpp */,
+ 0F6C734F1AC9F99F00BE1682 /* VariableWriteFireDetail.h */,
0F20C2581A8013AB00DA3229 /* VirtualRegister.cpp */,
0F426A461460CBAB00131F8F /* VirtualRegister.h */,
0F919D2215853CDE004A4E7D /* Watchpoint.cpp */,
C4F4B6F51A05C984005CAB76 /* generate_objc_protocol_types_implementation.py in Headers */,
A584032018BFFBE1005A0811 /* InspectorAgent.h in Headers */,
2AABCDE718EF294200002096 /* GCLogging.h in Headers */,
- FE5248F9191442D900B7FDE4 /* VariableWatchpointSetInlines.h in Headers */,
FE7BA6101A1A7CEC00F1F7B4 /* HeapVerifier.h in Headers */,
A5EF9B141A1D43F600702E90 /* generate_cpp_backend_dispatcher_header.py in Headers */,
C2DA778318E259990066FCB6 /* HeapInlines.h in Headers */,
A790DD6E182F499700588807 /* SetIteratorPrototype.h in Headers */,
A7299DA217D12848005F5FF9 /* SetPrototype.h in Headers */,
86AE64AA135E5E1C00963012 /* SH4Assembler.h in Headers */,
+ 0FF8BDEB1AD4CF7100DFE884 /* InferredValue.h in Headers */,
0F2B670517B6B5AB00A7AE3F /* SimpleTypedArrayController.h in Headers */,
14BA78F113AAB88F005B7C2C /* SlotVisitor.h in Headers */,
0FB17661196B8F9E0091052A /* DFGHeapLocation.h in Headers */,
A709F2F017A0AC0400512E98 /* SlowPathCall.h in Headers */,
933040040E6A749400786E6A /* SmallStrings.h in Headers */,
BC18C4640E16F5CD00B34460 /* SourceCode.h in Headers */,
+ 0F6C73511AC9F99F00BE1682 /* VariableWriteFireDetail.h in Headers */,
BC18C4630E16F5CD00B34460 /* SourceProvider.h in Headers */,
70EC0EC31AA0D7DA00B6AAFA /* JSStringIterator.h in Headers */,
E49DC16C12EF294E00184A1F /* SourceProviderCache.h in Headers */,
0F2E892C16D028AD009E4FD2 /* UnusedPointer.h in Headers */,
0F963B3813FC6FE90002D9B2 /* ValueProfile.h in Headers */,
0F426A481460CBB300131F8F /* ValueRecovery.h in Headers */,
- 0F9181C718415CA50057B669 /* VariableWatchpointSet.h in Headers */,
0F9C5E5F18E35F5E00D431C3 /* FTLDWARFRegister.h in Headers */,
0F426A491460CBB700131F8F /* VirtualRegister.h in Headers */,
BC18C4200E16F5CD00B34460 /* VM.h in Headers */,
A77F1821164088B200640A47 /* CodeCache.cpp in Sources */,
0F8F9446166764F100D61971 /* CodeOrigin.cpp in Sources */,
86B5826714D2796C00A9C306 /* CodeProfile.cpp in Sources */,
+ 0F6C73501AC9F99F00BE1682 /* VariableWriteFireDetail.cpp in Sources */,
86B5826914D2797000A9C306 /* CodeProfiling.cpp in Sources */,
0F8F943C1667631300D61971 /* CodeSpecializationKind.cpp in Sources */,
0F8F94421667633500D61971 /* CodeType.cpp in Sources */,
C2981FD817BAEE4B00A3BC98 /* DFGDesiredWeakReferences.cpp in Sources */,
C2981FDC17BAFF4400A3BC98 /* DFGDesiredWriteBarriers.cpp in Sources */,
0FF427641591A1CC004CB9FF /* DFGDisassembler.cpp in Sources */,
- 0FCA9113195E66A000426438 /* VariableWatchpointSet.cpp in Sources */,
0FD81AD2154FB4EE00983E72 /* DFGDominators.cpp in Sources */,
0FD3C82614115D4000FD81CB /* DFGDriver.cpp in Sources */,
0F2B9CE219D0BA7D00B1D1B5 /* DFGAvailabilityMap.cpp in Sources */,
0F2B66FE17B6B5AB00A7AE3F /* JSTypedArrays.cpp in Sources */,
0FF054F91AC35B4400E5BE57 /* ExecutableAllocationFuzz.cpp in Sources */,
86E3C61A167BABEE006D760A /* JSValue.mm in Sources */,
+ 0FF8BDEA1AD4CF7100DFE884 /* InferredValue.cpp in Sources */,
14BD5A320A3E91F600BAF59C /* JSValueRef.cpp in Sources */,
147F39D7107EC37600427A48 /* JSEnvironmentRecord.cpp in Sources */,
86E3C61C167BABEE006D760A /* JSVirtualMachine.mm in Sources */,
{ "name" : "op_enter", "length" : 1 },
{ "name" : "op_create_lexical_environment", "length" : 3 },
{ "name" : "op_get_scope", "length" : 2 },
- { "name" : "op_touch_entry", "length" : 1 },
{ "name" : "op_create_direct_arguments", "length" : 2 },
{ "name" : "op_create_scoped_arguments", "length" : 3 },
{ "name" : "op_create_out_of_band_arguments", "length" : 2 },
case op_new_object:
case op_enter:
case op_catch:
- case op_touch_entry:
case op_profile_control_flow:
case op_create_direct_arguments:
case op_create_out_of_band_arguments:
case op_put_by_index:
case op_profile_type:
case op_profile_control_flow:
- case op_touch_entry:
case op_put_to_arguments:
#define LLINT_HELPER_OPCODES(opcode, length) case opcode:
FOR_EACH_LLINT_OPCODE_EXTENSION(LLINT_HELPER_OPCODES);
printLocationAndOp(out, exec, location, it, "enter");
break;
}
- case op_touch_entry: {
- printLocationAndOp(out, exec, location, it, "touch_entry");
- break;
- }
case op_create_lexical_environment: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
instructions[i + 4].u.operand = op.type;
instructions[i + 5].u.operand = op.depth;
if (op.lexicalEnvironment)
- instructions[i + 6].u.lexicalEnvironment.set(*vm(), ownerExecutable, op.lexicalEnvironment);
+ instructions[i + 6].u.symbolTable.set(*vm(), ownerExecutable, op.lexicalEnvironment->symbolTable());
break;
}
else if (op.structure)
instructions[i + 5].u.structure.set(*vm(), ownerExecutable, op.structure);
instructions[i + 6].u.pointer = reinterpret_cast<void*>(op.operand);
-
break;
}
ConcurrentJITLocker locker(m_symbolTable->m_lock);
SymbolTable::Map::iterator iter = m_symbolTable->find(locker, uid);
ASSERT(iter != m_symbolTable->end(locker));
- iter->value.prepareToWatch(symbolTable());
+ iter->value.prepareToWatch();
instructions[i + 5].u.watchpointSet = iter->value.watchpointSet();
} else
instructions[i + 5].u.watchpointSet = nullptr;
curInstruction[3].u.toThisStatus, ToThisClearedByGC);
break;
case op_resolve_scope: {
- WriteBarrierBase<JSLexicalEnvironment>& lexicalEnvironment = curInstruction[6].u.lexicalEnvironment;
- if (!lexicalEnvironment || Heap::isMarked(lexicalEnvironment.get()))
+ // Right now this isn't strictly necessary. Any symbol tables that this will refer to
+ // are for outer functions, and we refer to those functions strongly, and they refer
+ // to the symbol table strongly. But it's nice to be on the safe side.
+ WriteBarrierBase<SymbolTable>& symbolTable = curInstruction[6].u.symbolTable;
+ if (!symbolTable || Heap::isMarked(symbolTable.get()))
break;
if (Options::verboseOSR())
- dataLogF("Clearing dead lexicalEnvironment %p.\n", lexicalEnvironment.get());
- lexicalEnvironment.clear();
+ dataLogF("Clearing dead symbolTable %p.\n", symbolTable.get());
+ symbolTable.clear();
break;
}
case op_get_from_scope:
return "";
}
+ValueProfile* CodeBlock::valueProfileForBytecodeOffset(int bytecodeOffset)
+{
+ ValueProfile* result = binarySearch<ValueProfile, int>(
+ m_valueProfiles, m_valueProfiles.size(), bytecodeOffset,
+ getValueProfileBytecodeOffset<ValueProfile>);
+ ASSERT(result->m_bytecodeOffset != -1);
+ ASSERT(instructions()[bytecodeOffset + opcodeLength(
+ m_vm->interpreter->getOpcodeID(
+ instructions()[bytecodeOffset].u.opcode)) - 1].u.profile == result);
+ return result;
+}
+
void CodeBlock::validate()
{
BytecodeLivenessAnalysis liveness(this); // Compute directly from scratch so it doesn't effect CodeBlock footprint.
unsigned numberOfValueProfiles() { return m_valueProfiles.size(); }
ValueProfile* valueProfile(int index) { return &m_valueProfiles[index]; }
- ValueProfile* valueProfileForBytecodeOffset(int bytecodeOffset)
- {
- ValueProfile* result = binarySearch<ValueProfile, int>(
- m_valueProfiles, m_valueProfiles.size(), bytecodeOffset,
- getValueProfileBytecodeOffset<ValueProfile>);
- ASSERT(result->m_bytecodeOffset != -1);
- ASSERT(instructions()[bytecodeOffset + opcodeLength(
- m_vm->interpreter->getOpcodeID(
- instructions()[bytecodeOffset].u.opcode)) - 1].u.profile == result);
- return result;
- }
+ ValueProfile* valueProfileForBytecodeOffset(int bytecodeOffset);
SpeculatedType valueProfilePredictionForBytecodeOffset(const ConcurrentJITLocker& locker, int bytecodeOffset)
{
return valueProfileForBytecodeOffset(bytecodeOffset)->computeUpdatedPrediction(locker);
dump(out);
}
+JSFunction* InlineCallFrame::calleeConstant() const
+{
+ if (calleeRecovery.isConstant())
+ return jsCast<JSFunction*>(calleeRecovery.constant());
+ return nullptr;
+}
+
+void InlineCallFrame::visitAggregate(SlotVisitor& visitor)
+{
+ visitor.append(&executable);
+}
+
JSFunction* InlineCallFrame::calleeForCallFrame(ExecState* exec) const
{
return jsCast<JSFunction*>(calleeRecovery.recover(exec));
#include "CodeBlockHash.h"
#include "CodeSpecializationKind.h"
-#include "JSFunction.h"
#include "ValueRecovery.h"
#include "WriteBarrier.h"
#include <wtf/BitVector.h>
CodeSpecializationKind specializationKind() const { return specializationKindFor(static_cast<Kind>(kind)); }
- JSFunction* calleeConstant() const
- {
- if (calleeRecovery.isConstant())
- return jsCast<JSFunction*>(calleeRecovery.constant());
- return 0;
- }
-
- void visitAggregate(SlotVisitor& visitor)
- {
- visitor.append(&executable);
- }
+ JSFunction* calleeConstant() const;
+ void visitAggregate(SlotVisitor&);
// Get the callee given a machine call frame to which this InlineCallFrame belongs.
JSFunction* calleeForCallFrame(ExecState*) const;
#include "BasicBlockLocation.h"
#include "MacroAssembler.h"
#include "Opcode.h"
+#include "SymbolTable.h"
#include "TypeLocation.h"
#include "PropertySlot.h"
#include "SpecialPointer.h"
class ArrayAllocationProfile;
class ArrayProfile;
class ObjectAllocationProfile;
-class VariableWatchpointSet;
+class WatchpointSet;
struct LLIntCallLinkInfo;
struct ValueProfile;
Opcode opcode;
int operand;
WriteBarrierBase<Structure> structure;
+ WriteBarrierBase<SymbolTable> symbolTable;
WriteBarrierBase<StructureChain> structureChain;
WriteBarrierBase<JSCell> jsCell;
WriteBarrier<Unknown>* variablePointer;
ArrayProfile* arrayProfile;
ArrayAllocationProfile* arrayAllocationProfile;
ObjectAllocationProfile* objectAllocationProfile;
- VariableWatchpointSet* watchpointSet;
- WriteBarrierBase<JSLexicalEnvironment> lexicalEnvironment;
+ WatchpointSet* watchpointSet;
void* pointer;
bool* predicatePointer;
ToThisStatus toThisStatus;
+++ /dev/null
-/*
- * Copyright (C) 2012-2014 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef VariableWatchpointSet_h
-#define VariableWatchpointSet_h
-
-#include "Watchpoint.h"
-#include "WriteBarrier.h"
-
-namespace JSC {
-
-class JSObject;
-class SymbolTable;
-
-class VariableWriteFireDetail : public FireDetail {
-public:
- VariableWriteFireDetail(JSObject* object, const PropertyName& name)
- : m_object(object)
- , m_name(name)
- {
- }
-
- virtual void dump(PrintStream&) const override;
-
-private:
- JSObject* m_object;
- const PropertyName& m_name;
-};
-
-class VariableWatchpointSet : public WatchpointSet {
- friend class LLIntOffsetsExtractor;
-public:
- VariableWatchpointSet(SymbolTable& symbolTable)
- : WatchpointSet(ClearWatchpoint)
- , m_symbolTable(symbolTable)
- {
- }
-
- ~VariableWatchpointSet() { }
-
- // For the purpose of deciding whether or not to watch this variable, you only need
- // to inspect inferredValue(). If this returns something other than the empty
- // value, then it means that at all future safepoints, this watchpoint set will be
- // in one of these states:
- //
- // IsWatched: in this case, the variable's value must still be the
- // inferredValue.
- //
- // IsInvalidated: in this case the variable's value may be anything but you'll
- // either notice that it's invalidated and not install the watchpoint, or
- // you will have been notified that the watchpoint was fired.
- JSValue inferredValue() const { return m_inferredValue.get(); }
-
- void notifyWrite(VM&, JSValue, const FireDetail&);
- JS_EXPORT_PRIVATE void notifyWrite(VM&, JSValue, JSObject* baseObject, const PropertyName&);
- void notifyWrite(VM&, JSValue, const char* reason);
-
- void invalidate(const FireDetail& detail)
- {
- m_inferredValue.clear();
- WatchpointSet::invalidate(detail);
- }
-
- void finalizeUnconditionally(const FireDetail& detail)
- {
- ASSERT(!!m_inferredValue == (state() == IsWatched));
- if (!m_inferredValue)
- return;
- JSValue inferredValue = m_inferredValue.get();
- if (!inferredValue.isCell())
- return;
- JSCell* cell = inferredValue.asCell();
- if (Heap::isMarked(cell))
- return;
- invalidate(detail);
- }
-
- WriteBarrier<Unknown>* addressOfInferredValue() { return &m_inferredValue; }
-
-private:
- SymbolTable& m_symbolTable;
- WriteBarrier<Unknown> m_inferredValue;
-};
-
-} // namespace JSC
-
-#endif // VariableWatchpointSet_h
-
/*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
*/
#include "config.h"
-#include "VariableWatchpointSet.h"
+#include "VariableWriteFireDetail.h"
#include "JSCInlines.h"
out.print("Write to ", m_name, " in ", JSValue(m_object));
}
-void VariableWatchpointSet::notifyWrite(VM& vm, JSValue value, JSObject* baseObject, const PropertyName& propertyName)
+void VariableWriteFireDetail::touch(WatchpointSet* set, JSObject* object, const PropertyName& name)
{
- notifyWrite(vm, value, VariableWriteFireDetail(baseObject, propertyName));
-}
-
-void VariableWatchpointSet::notifyWrite(VM& vm, JSValue value, const char* reason)
-{
- notifyWrite(vm, value, StringFireDetail(reason));
+ set->touch(VariableWriteFireDetail(object, name));
}
} // namespace JSC
/*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef VariableWatchpointSetInlines_h
-#define VariableWatchpointSetInlines_h
+#ifndef VariableWriteFireDetail_h
+#define VariableWriteFireDetail_h
-#include "SymbolTable.h"
-#include "VariableWatchpointSet.h"
+#include "Watchpoint.h"
namespace JSC {
-inline void VariableWatchpointSet::notifyWrite(VM& vm, JSValue value, const FireDetail& detail)
-{
- ASSERT(!!value);
- switch (state()) {
- case ClearWatchpoint:
- m_inferredValue.set(vm, &m_symbolTable, value);
- startWatching();
- return;
-
- case IsWatched:
- ASSERT(!!m_inferredValue);
- if (value == m_inferredValue.get())
- return;
- invalidate(detail);
- return;
-
- case IsInvalidated:
- ASSERT(!m_inferredValue);
- return;
+class JSObject;
+class PropertyName;
+
+class VariableWriteFireDetail : public FireDetail {
+public:
+ VariableWriteFireDetail(JSObject* object, const PropertyName& name)
+ : m_object(object)
+ , m_name(name)
+ {
}
-
- ASSERT_NOT_REACHED();
-}
+
+ virtual void dump(PrintStream&) const override;
+
+ JS_EXPORT_PRIVATE static void touch(WatchpointSet*, JSObject*, const PropertyName&);
+
+private:
+ JSObject* m_object;
+ const PropertyName& m_name;
+};
} // namespace JSC
-#endif // VariableWatchpointSetInlines_h
+#endif // VariableWriteFireDetail_h
/*
- * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
namespace JSC {
class FireDetail {
+ void* operator new(size_t) = delete;
+
public:
FireDetail()
{
JS_EXPORT_PRIVATE WatchpointSet(WatchpointState);
JS_EXPORT_PRIVATE ~WatchpointSet(); // Note that this will not fire any of the watchpoints; if you need to know when a WatchpointSet dies then you need a separate mechanism for this.
+ // Fast way of getting the state, which only works from the main thread.
+ WatchpointState stateOnJSThread() const
+ {
+ return static_cast<WatchpointState>(m_state);
+ }
+
// It is safe to call this from another thread. It may return an old
// state. Guarantees that if *first* read the state() of the thing being
// watched and it returned IsWatched and *second* you actually read its
// set watchpoints that we believe will actually be fired.
void startWatching()
{
- ASSERT(state() != IsInvalidated);
+ ASSERT(m_state != IsInvalidated);
+ if (m_state == IsWatched)
+ return;
+ WTF::storeStoreFence();
m_state = IsWatched;
+ WTF::storeStoreFence();
}
void fireAll(const FireDetail& detail)
{
- if (LIKELY(state() != IsWatched))
+ if (LIKELY(m_state != IsWatched))
return;
fireAllSlow(detail);
}
void fireAll(const char* reason)
{
- if (LIKELY(state() != IsWatched))
+ if (LIKELY(m_state != IsWatched))
return;
fireAllSlow(reason);
}
fireAll(detail);
}
+ void touch(const char* reason)
+ {
+ touch(StringFireDetail(reason));
+ }
+
void invalidate(const FireDetail& detail)
{
if (state() == IsWatched)
fireAll(detail);
m_state = IsInvalidated;
}
-
+
+ void invalidate(const char* reason)
+ {
+ invalidate(StringFireDetail(reason));
+ }
+
int8_t* addressOfState() { return &m_state; }
int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; }
freeFat();
}
+ // Fast way of getting the state, which only works from the main thread.
+ WatchpointState stateOnJSThread() const
+ {
+ uintptr_t data = m_data;
+ if (isFat(data))
+ return fat(data)->stateOnJSThread();
+ return decodeState(data);
+ }
+
+ // It is safe to call this from another thread. It may return a prior state,
+ // but that should be fine since you should only perform actions based on the
+ // state if you also add a watchpoint.
+ WatchpointState state() const
+ {
+ WTF::loadLoadFence();
+ uintptr_t data = m_data;
+ WTF::loadLoadFence();
+ if (isFat(data))
+ return fat(data)->state();
+ return decodeState(data);
+ }
+
// It is safe to call this from another thread. It may return false
// even if the set actually had been invalidated, but that ought to happen
// only in the case of races, and should be rare.
bool hasBeenInvalidated() const
{
- WTF::loadLoadFence();
- uintptr_t data = m_data;
- if (isFat(data)) {
- WTF::loadLoadFence();
- return fat(data)->hasBeenInvalidated();
- }
- return decodeState(data) == IsInvalidated;
+ return state() == IsInvalidated;
}
// Like hasBeenInvalidated(), may be called from another thread.
WTF::storeStoreFence();
}
+ void invalidate(const FireDetail& detail)
+ {
+ if (isFat())
+ fat()->invalidate(detail);
+ else
+ m_data = encodeState(IsInvalidated);
+ }
+
JS_EXPORT_PRIVATE void fireAll(const char* reason);
void touch(const FireDetail& detail)
fat()->touch(detail);
return;
}
- if (decodeState(m_data) == ClearWatchpoint)
+ uintptr_t data = m_data;
+ if (decodeState(data) == IsInvalidated)
+ return;
+ WTF::storeStoreFence();
+ if (decodeState(data) == ClearWatchpoint)
m_data = encodeState(IsWatched);
else
m_data = encodeState(IsInvalidated);
}
}
- if (m_symbolTable->scopeSize())
- emitOpcode(op_touch_entry);
-
if (isConstructor()) {
if (constructorKind() == ConstructorKind::Derived) {
m_newTargetRegister = addVar();
}
case CreateThis: {
+ // FIXME: We can fold this to NewObject if the incoming callee is a constant.
forNode(node).setType(SpecFinalObject);
break;
}
break;
case GetCallee:
+ if (FunctionExecutable* executable = jsDynamicCast<FunctionExecutable*>(m_codeBlock->ownerExecutable())) {
+ InferredValue* singleton = executable->singletonFunction();
+ if (JSValue value = singleton->inferredValue()) {
+ m_graph.watchpoints().addLazily(singleton);
+ JSFunction* function = jsCast<JSFunction*>(value);
+ setConstant(node, *m_graph.freeze(function));
+ break;
+ }
+ }
forNode(node).setType(SpecFunction);
break;
bool isDirect);
void emitChecks(const ConstantStructureCheckVector&);
- Node* getScope(VirtualRegister scopeChain, unsigned skipCount);
-
void prepareToParseBlock();
void clearCaches();
if (operand.offset() == JSStack::Callee)
return weakJSConstant(callee);
}
- } else if (operand.offset() == JSStack::Callee)
+ } else if (operand.offset() == JSStack::Callee) {
+ // We have to do some constant-folding here because this enables CreateThis folding. Note
+ // that we don't have such watchpoint-based folding for inlined uses of Callee, since in that
+ // case if the function is a singleton then we already know it.
+ if (FunctionExecutable* executable = jsDynamicCast<FunctionExecutable*>(m_codeBlock->ownerExecutable())) {
+ InferredValue* singleton = executable->singletonFunction();
+ if (JSValue value = singleton->inferredValue()) {
+ m_graph.watchpoints().addLazily(singleton);
+ JSFunction* function = jsCast<JSFunction*>(value);
+ return weakJSConstant(function);
+ }
+ }
return addToGraph(GetCallee);
+ }
return getDirect(m_inlineStackTop->remapOperand(operand));
}
m_constants.resize(0);
}
-Node* ByteCodeParser::getScope(VirtualRegister scopeChain, unsigned skipCount)
-{
- Node* localBase = get(scopeChain);
- for (unsigned n = skipCount; n--;)
- localBase = addToGraph(SkipScope, localBase);
- return localBase;
-}
-
bool ByteCodeParser::parseBlock(unsigned limit)
{
bool shouldContinueParsing = true;
NEXT_OPCODE(op_enter);
}
- case op_touch_entry:
- if (m_inlineStackTop->m_codeBlock->symbolTable()->m_functionEnteredOnce.isStillValid())
- addToGraph(ForceOSRExit);
- NEXT_OPCODE(op_touch_entry);
-
case op_to_this: {
Node* op1 = getThis();
if (op1->op() != ToThis) {
case LocalClosureVar:
case ClosureVar:
case ClosureVarWithVarInjectionChecks: {
- JSLexicalEnvironment* lexicalEnvironment = currentInstruction[6].u.lexicalEnvironment.get();
- if (lexicalEnvironment
- && lexicalEnvironment->symbolTable()->m_functionEnteredOnce.isStillValid()) {
- m_graph.watchpoints().addLazily(lexicalEnvironment->symbolTable()->m_functionEnteredOnce);
- addToGraph(Phantom, getDirect(m_inlineStackTop->remapOperand(VirtualRegister(currentInstruction[2].u.operand))));
- set(VirtualRegister(dst), weakJSConstant(lexicalEnvironment));
+ Node* localBase = get(VirtualRegister(currentInstruction[2].u.operand));
+ addToGraph(Phantom, localBase); // OSR exit cannot handle resolve_scope on a DCE'd scope.
+
+ // We have various forms of constant folding here. This is necessary to avoid
+ // spurious recompiles in dead-but-foldable code.
+ if (SymbolTable* symbolTable = currentInstruction[6].u.symbolTable.get()) {
+ InferredValue* singleton = symbolTable->singletonScope();
+ if (JSValue value = singleton->inferredValue()) {
+ m_graph.watchpoints().addLazily(singleton);
+ set(VirtualRegister(dst), weakJSConstant(value));
+ break;
+ }
+ }
+ if (JSScope* scope = localBase->dynamicCastConstant<JSScope*>()) {
+ for (unsigned n = depth; n--;)
+ scope = scope->next();
+ set(VirtualRegister(dst), weakJSConstant(scope));
break;
}
- set(VirtualRegister(dst), getScope(VirtualRegister(currentInstruction[2].u.operand), depth));
- if (inlineCallFrame())
- addToGraph(Phantom, getDirect(m_inlineStackTop->remapOperand(VirtualRegister(currentInstruction[2].u.operand))));
+ for (unsigned n = depth; n--;)
+ localBase = addToGraph(SkipScope, localBase);
+ set(VirtualRegister(dst), localBase);
break;
}
case Dynamic:
case GlobalVar:
case GlobalVarWithVarInjectionChecks: {
addToGraph(Phantom, get(VirtualRegister(scope)));
- ConcurrentJITLocker locker(globalObject->symbolTable()->m_lock);
- SymbolTableEntry entry = globalObject->symbolTable()->get(locker, uid);
- VariableWatchpointSet* watchpointSet = entry.watchpointSet();
- JSValue inferredValue =
- watchpointSet ? watchpointSet->inferredValue() : JSValue();
- if (!inferredValue) {
- SpeculatedType prediction = getPrediction();
- set(VirtualRegister(dst), addToGraph(GetGlobalVar, OpInfo(operand), OpInfo(prediction)));
- break;
+ WatchpointSet* watchpointSet;
+ ScopeOffset offset;
+ {
+ ConcurrentJITLocker locker(globalObject->symbolTable()->m_lock);
+ SymbolTableEntry entry = globalObject->symbolTable()->get(locker, uid);
+ watchpointSet = entry.watchpointSet();
+ offset = entry.scopeOffset();
+ }
+ if (watchpointSet && watchpointSet->state() == IsWatched) {
+ // This has a fun concurrency story. There is the possibility of a race in two
+ // directions:
+ //
+ // We see that the set IsWatched, but in the meantime it gets invalidated: this is
+ // fine because if we saw that it IsWatched then we add a watchpoint. If it gets
+ // invalidated, then this compilation is invalidated. Note that in the meantime we
+ // may load an absurd value from the global object. It's fine to load an absurd
+ // value if the compilation is invalidated anyway.
+ //
+ // We see that the set IsWatched, but the value isn't yet initialized: this isn't
+ // possible because of the ordering of operations.
+ //
+ // Here's how we order operations:
+ //
+ // Main thread stores to the global object: always store a value first, and only
+ // after that do we touch the watchpoint set. There is a fence in the touch, that
+ // ensures that the store to the global object always happens before the touch on the
+ // set.
+ //
+ // Compilation thread: always first load the state of the watchpoint set, and then
+ // load the value. The WatchpointSet::state() method does fences for us to ensure
+ // that the load of the state happens before our load of the value.
+ //
+ // Finalizing compilation: this happens on the main thread and synchronously checks
+ // validity of all watchpoint sets.
+ //
+ // We will only perform optimizations if the load of the state yields IsWatched. That
+ // means that at least one store would have happened to initialize the original value
+ // of the variable (that is, the value we'd like to constant fold to). There may be
+ // other stores that happen after that, but those stores will invalidate the
+ // watchpoint set and also the compilation.
+
+ // Note that we need to use the operand, which is a direct pointer at the global,
+ // rather than looking up the global by doing variableAt(offset). That's because the
+ // internal data structures of JSSegmentedVariableObject are not thread-safe even
+ // though accessing the global itself is. The segmentation involves a vector spine
+ // that resizes with malloc/free, so if new globals unrelated to the one we are
+ // reading are added, we might access freed memory if we do variableAt().
+ WriteBarrier<Unknown>* pointer = bitwise_cast<WriteBarrier<Unknown>*>(operand);
+
+ ASSERT(globalObject->findVariableIndex(pointer) == offset);
+
+ JSValue value = pointer->get();
+ if (value) {
+ m_graph.watchpoints().addLazily(watchpointSet);
+ set(VirtualRegister(dst), weakJSConstant(value));
+ break;
+ }
}
- m_graph.watchpoints().addLazily(watchpointSet);
- set(VirtualRegister(dst), weakJSConstant(inferredValue));
+ SpeculatedType prediction = getPrediction();
+ set(VirtualRegister(dst), addToGraph(GetGlobalVar, OpInfo(operand), OpInfo(prediction)));
break;
}
case LocalClosureVar:
// won't be able to handle an Undefined scope.
addToGraph(Phantom, scopeNode);
+ // Constant folding in the bytecode parser is important for performance. This may not
+ // have executed yet. If it hasn't, then we won't have a prediction. Lacking a
+ // prediction, we'd otherwise think that it has to exit. Then when it did execute, we
+ // would recompile. But if we can fold it here, we avoid the exit.
if (JSValue value = m_graph.tryGetConstantClosureVar(scopeNode, ScopeOffset(operand))) {
set(VirtualRegister(dst), weakJSConstant(value));
break;
uid = nullptr;
Structure* structure = nullptr;
- VariableWatchpointSet* watchpoints = nullptr;
+ WatchpointSet* watchpoints = nullptr;
uintptr_t operand;
{
ConcurrentJITLocker locker(m_inlineStackTop->m_profiledBlock->m_lock);
}
Node* valueNode = get(VirtualRegister(value));
addToGraph(PutGlobalVar, OpInfo(operand), valueNode);
- if (watchpoints && watchpoints->state() != IsInvalidated)
- addToGraph(NotifyWrite, OpInfo(watchpoints), valueNode);
+ if (watchpoints && watchpoints->state() != IsInvalidated) {
+ // Must happen after the store. See comment for GetGlobalVar.
+ addToGraph(NotifyWrite, OpInfo(watchpoints));
+ }
// Keep scope alive until after put.
addToGraph(Phantom, get(VirtualRegister(scope)));
break;
Node* scopeNode = get(VirtualRegister(scope));
Node* valueNode = get(VirtualRegister(value));
- if (watchpoints && watchpoints->state() != IsInvalidated)
- addToGraph(NotifyWrite, OpInfo(watchpoints), valueNode);
-
addToGraph(PutClosureVar, OpInfo(operand), scopeNode, valueNode);
+
+ if (watchpoints && watchpoints->state() != IsInvalidated) {
+ // Must happen after the store. See comment for GetGlobalVar.
+ addToGraph(NotifyWrite, OpInfo(watchpoints));
+ }
break;
}
case Dynamic:
switch (opcodeID) {
case op_enter:
- case op_touch_entry:
case op_to_this:
case op_check_tdz:
case op_create_this:
write(SideState);
return;
- case CreateActivation:
+ case CreateActivation: {
+ SymbolTable* table = graph.symbolTableFor(node->origin.semantic);
+ if (table->singletonScope()->isStillValid())
+ write(Watchpoint_fire);
read(HeapObjectCount);
write(HeapObjectCount);
return;
+ }
case CreateDirectArguments:
case CreateScopedArguments:
case NewStringObject:
case PhantomNewObject:
case MaterializeNewObject:
- case NewFunction:
read(HeapObjectCount);
write(HeapObjectCount);
return;
+ case NewFunction:
+ if (node->castOperand<FunctionExecutable*>()->singletonFunction()->isStillValid())
+ write(Watchpoint_fire);
+ read(HeapObjectCount);
+ write(HeapObjectCount);
+ return;
+
case RegExpExec:
case RegExpTest:
read(RegExpState);
/*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
codeBlock->vm()->heap.addReference(neuteringWatchpoint, view->buffer());
}
+void InferredValueAdaptor::add(
+ CodeBlock* codeBlock, InferredValue* inferredValue, Watchpoint* watchpoint)
+{
+ codeBlock->addConstant(inferredValue); // For common users, it doesn't really matter if it's weak or not. If references to it go away, we go away, too.
+ inferredValue->add(watchpoint);
+}
+
DesiredWatchpoints::DesiredWatchpoints() { }
DesiredWatchpoints::~DesiredWatchpoints() { }
m_inlineSets.addLazily(&set);
}
+void DesiredWatchpoints::addLazily(InferredValue* inferredValue)
+{
+ m_inferredValues.addLazily(inferredValue);
+}
+
void DesiredWatchpoints::addLazily(JSArrayBufferView* view)
{
m_bufferViews.addLazily(view);
{
m_sets.reallyAdd(codeBlock, commonData);
m_inlineSets.reallyAdd(codeBlock, commonData);
+ m_inferredValues.reallyAdd(codeBlock, commonData);
m_bufferViews.reallyAdd(codeBlock, commonData);
}
{
return m_sets.areStillValid()
&& m_inlineSets.areStillValid()
+ && m_inferredValues.areStillValid()
&& m_bufferViews.areStillValid();
}
/*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#include "CodeOrigin.h"
#include "DFGCommonData.h"
+#include "InferredValue.h"
#include "JSArrayBufferView.h"
#include "Watchpoint.h"
#include <wtf/HashMap.h>
static bool hasBeenInvalidated(T* set) { return set->hasBeenInvalidated(); }
};
+struct InferredValueAdaptor {
+ static void add(CodeBlock*, InferredValue*, Watchpoint*);
+ static bool hasBeenInvalidated(InferredValue* inferredValue)
+ {
+ return inferredValue->hasBeenInvalidated();
+ }
+};
+
struct ArrayBufferViewWatchpointAdaptor {
static void add(CodeBlock*, JSArrayBufferView*, Watchpoint*);
static bool hasBeenInvalidated(JSArrayBufferView* view)
void addLazily(WatchpointSet*);
void addLazily(InlineWatchpointSet&);
+ void addLazily(InferredValue*);
void addLazily(JSArrayBufferView*);
bool consider(Structure*);
{
return m_inlineSets.isWatched(&set);
}
+ bool isWatched(InferredValue* inferredValue)
+ {
+ return m_inferredValues.isWatched(inferredValue);
+ }
bool isWatched(JSArrayBufferView* view)
{
return m_bufferViews.isWatched(view);
private:
GenericDesiredWatchpoints<WatchpointSet> m_sets;
GenericDesiredWatchpoints<InlineWatchpointSet> m_inlineSets;
+ GenericDesiredWatchpoints<InferredValue, InferredValueAdaptor> m_inferredValues;
GenericDesiredWatchpoints<JSArrayBufferView, ArrayBufferViewWatchpointAdaptor> m_bufferViews;
};
out.print(comma, "^", node->phi()->index());
if (node->hasExecutionCounter())
out.print(comma, RawPointer(node->executionCounter()));
- if (node->hasVariableWatchpointSet())
- out.print(comma, RawPointer(node->variableWatchpointSet()));
+ if (node->hasWatchpointSet())
+ out.print(comma, RawPointer(node->watchpointSet()));
if (node->hasStoragePointer())
out.print(comma, RawPointer(node->storagePointer()));
if (node->hasObjectMaterializationData())
JSValue Graph::tryGetConstantClosureVar(JSValue base, ScopeOffset offset)
{
+ // This has an awesome concurrency story. See comment for GetGlobalVar in ByteCodeParser.
+
if (!base)
return JSValue();
return JSValue();
SymbolTable* symbolTable = activation->symbolTable();
- ConcurrentJITLocker locker(symbolTable->m_lock);
-
- if (symbolTable->m_functionEnteredOnce.hasBeenInvalidated())
- return JSValue();
-
- SymbolTableEntry* entry = symbolTable->entryFor(locker, offset);
- if (!entry)
- return JSValue();
-
- VariableWatchpointSet* set = entry->watchpointSet();
- if (!set)
- return JSValue();
-
- JSValue value = set->inferredValue();
- if (!value)
- return JSValue();
+ JSValue value;
+ WatchpointSet* set;
+ {
+ ConcurrentJITLocker locker(symbolTable->m_lock);
+
+ SymbolTableEntry* entry = symbolTable->entryFor(locker, offset);
+ if (!entry)
+ return JSValue();
+
+ set = entry->watchpointSet();
+ if (!set)
+ return JSValue();
+
+ if (set->state() != IsWatched)
+ return JSValue();
+
+ ASSERT(entry->scopeOffset() == offset);
+ value = activation->variableAt(offset).get();
+ if (!value)
+ return JSValue();
+ }
- watchpoints().addLazily(symbolTable->m_functionEnteredOnce);
watchpoints().addLazily(set);
return value;
m_opInfo = bitwise_cast<uintptr_t>(value);
}
- bool hasVariableWatchpointSet()
+ bool hasWatchpointSet()
{
return op() == NotifyWrite;
}
- VariableWatchpointSet* variableWatchpointSet()
+ WatchpointSet* watchpointSet()
{
- return reinterpret_cast<VariableWatchpointSet*>(m_opInfo);
+ return reinterpret_cast<WatchpointSet*>(m_opInfo);
}
bool hasStoragePointer()
return static_cast<char*>(exec->codeBlock()->stringSwitchJumpTable(tableIndex).ctiForValue(string->value(exec).impl()).executableAddress());
}
-void JIT_OPERATION operationNotifyWrite(ExecState* exec, VariableWatchpointSet* set, EncodedJSValue encodedValue)
+void JIT_OPERATION operationNotifyWrite(ExecState* exec, WatchpointSet* set)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
- JSValue value = JSValue::decode(encodedValue);
- set->notifyWrite(vm, value, "Executed NotifyWrite");
+ set->touch("Executed NotifyWrite");
}
void JIT_OPERATION operationThrowStackOverflowForVarargs(ExecState* exec)
JSCell* JIT_OPERATION operationMakeRope3(ExecState*, JSString*, JSString*, JSString*);
char* JIT_OPERATION operationFindSwitchImmTargetForDouble(ExecState*, EncodedJSValue, size_t tableIndex);
char* JIT_OPERATION operationSwitchString(ExecState*, size_t tableIndex, JSString*);
-void JIT_OPERATION operationNotifyWrite(ExecState*, VariableWatchpointSet*, EncodedJSValue);
+void JIT_OPERATION operationNotifyWrite(ExecState*, WatchpointSet*);
void JIT_OPERATION operationThrowStackOverflowForVarargs(ExecState*) WTF_INTERNAL;
int32_t JIT_OPERATION operationSizeOfVarargs(ExecState*, EncodedJSValue arguments, int32_t firstVarArgOffset);
void JIT_OPERATION operationLoadVarargs(ExecState*, int32_t firstElementDest, EncodedJSValue arguments, int32_t offset, int32_t length, int32_t mandatoryMinimum);
SpeculateCellOperand scope(this, node->child1());
GPRReg scopeGPR = scope.gpr();
flushRegisters();
- callOperation(
- operationNewFunction, resultGPR, scopeGPR, node->castOperand<FunctionExecutable*>());
+ FunctionExecutable* executable = node->castOperand<FunctionExecutable*>();
+ J_JITOperation_EJscC function;
+ if (executable->singletonFunction()->isStillValid())
+ function = operationNewFunction;
+ else
+ function = operationNewFunctionWithInvalidatedReallocationWatchpoint;
+ callOperation(function, resultGPR, scopeGPR, executable);
cellResult(resultGPR, node);
}
void SpeculativeJIT::compileCreateActivation(Node* node)
{
+ SymbolTable* table = m_jit.graph().symbolTableFor(node->origin.semantic);
+ Structure* structure = m_jit.graph().globalObjectFor(
+ node->origin.semantic)->activationStructure();
+
SpeculateCellOperand scope(this, node->child1());
+ GPRReg scopeGPR = scope.gpr();
+
+ if (table->singletonScope()->isStillValid()) {
+ GPRFlushedCallResult result(this);
+ GPRReg resultGPR = result.gpr();
+
+ flushRegisters();
+
+ callOperation(operationCreateActivationDirect, resultGPR, structure, scopeGPR, table);
+ cellResult(resultGPR, node);
+ return;
+ }
+
GPRTemporary result(this);
GPRTemporary scratch1(this);
GPRTemporary scratch2(this);
- GPRReg scopeGPR = scope.gpr();
GPRReg resultGPR = result.gpr();
GPRReg scratch1GPR = scratch1.gpr();
GPRReg scratch2GPR = scratch2.gpr();
- SymbolTable* table = m_jit.graph().symbolTableFor(node->origin.semantic);
- Structure* structure = m_jit.graph().globalObjectFor(
- node->origin.semantic)->activationStructure();
-
JITCompiler::JumpList slowPath;
emitAllocateJSObjectWithKnownSize<JSLexicalEnvironment>(
resultGPR, TrustedImmPtr(structure), TrustedImmPtr(0), scratch1GPR, scratch2GPR,
cellResult(resultGPR, node);
}
+void SpeculativeJIT::compileNotifyWrite(Node* node)
+{
+ WatchpointSet* set = node->watchpointSet();
+
+ JITCompiler::Jump slowCase = m_jit.branch8(
+ JITCompiler::NotEqual,
+ JITCompiler::AbsoluteAddress(set->addressOfState()),
+ TrustedImm32(IsInvalidated));
+
+ addSlowPathGenerator(
+ slowPathCall(slowCase, this, operationNotifyWrite, NoResult, set));
+
+ noResult(node);
+}
+
bool SpeculativeJIT::compileRegExpExec(Node* node)
{
unsigned branchIndexInBlock = detectPeepHoleBranch();
return appendCallWithExceptionCheckSetResult(operation, result);
}
+ JITCompiler::Call callOperation(V_JITOperation_EWs operation, WatchpointSet* watchpointSet)
+ {
+ m_jit.setupArgumentsWithExecState(TrustedImmPtr(watchpointSet));
+ return appendCall(operation);
+ }
+
#if USE(JSVALUE64)
JITCompiler::Call callOperation(J_JITOperation_E operation, GPRReg result)
{
return appendCallWithExceptionCheck(operation);
}
- JITCompiler::Call callOperation(V_JITOperation_EVwsJ operation, VariableWatchpointSet* watchpointSet, GPRReg arg)
- {
- m_jit.setupArgumentsWithExecState(TrustedImmPtr(watchpointSet), arg);
- return appendCall(operation);
- }
-
JITCompiler::Call callOperation(D_JITOperation_EJ operation, FPRReg result, GPRReg arg1)
{
m_jit.setupArgumentsWithExecState(arg1);
return appendCallWithExceptionCheck(operation);
}
- JITCompiler::Call callOperation(V_JITOperation_EVwsJ operation, VariableWatchpointSet* watchpointSet, GPRReg argTag, GPRReg argPayload)
- {
- m_jit.setupArgumentsWithExecState(TrustedImmPtr(watchpointSet), argPayload, argTag);
- return appendCall(operation);
- }
-
JITCompiler::Call callOperation(D_JITOperation_EJ operation, FPRReg result, GPRReg arg1Tag, GPRReg arg1Payload)
{
m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag);
void compilePutToArguments(Node*);
void compileCreateScopedArguments(Node*);
void compileCreateClonedArguments(Node*);
+ void compileNotifyWrite(Node*);
bool compileRegExpExec(Node*);
JITCompiler::Jump branchIsCell(JSValueRegs);
}
case NotifyWrite: {
- VariableWatchpointSet* set = node->variableWatchpointSet();
-
- JSValueOperand value(this, node->child1());
- GPRReg valueTagGPR = value.tagGPR();
- GPRReg valuePayloadGPR = value.payloadGPR();
-
- GPRTemporary temp(this);
- GPRReg tempGPR = temp.gpr();
-
- m_jit.load8(set->addressOfState(), tempGPR);
-
- JITCompiler::Jump isDone = m_jit.branch32(JITCompiler::Equal, tempGPR, TrustedImm32(IsInvalidated));
- JITCompiler::JumpList notifySlow;
- notifySlow.append(m_jit.branch32(
- JITCompiler::NotEqual,
- JITCompiler::AbsoluteAddress(set->addressOfInferredValue()->payloadPointer()),
- valuePayloadGPR));
- notifySlow.append(m_jit.branch32(
- JITCompiler::NotEqual,
- JITCompiler::AbsoluteAddress(set->addressOfInferredValue()->tagPointer()),
- valueTagGPR));
- addSlowPathGenerator(
- slowPathCall(notifySlow, this, operationNotifyWrite, NoResult, set, valueTagGPR, valuePayloadGPR));
- isDone.link(&m_jit);
-
- noResult(node);
+ compileNotifyWrite(node);
break;
}
}
case NotifyWrite: {
- VariableWatchpointSet* set = node->variableWatchpointSet();
-
- JSValueOperand value(this, node->child1());
- GPRReg valueGPR = value.gpr();
-
- GPRTemporary temp(this);
- GPRReg tempGPR = temp.gpr();
-
- m_jit.load8(set->addressOfState(), tempGPR);
-
- JITCompiler::Jump isDone =
- m_jit.branch32(JITCompiler::Equal, tempGPR, TrustedImm32(IsInvalidated));
- JITCompiler::Jump slowCase = m_jit.branch64(JITCompiler::NotEqual,
- JITCompiler::AbsoluteAddress(set->addressOfInferredValue()), valueGPR);
- isDone.link(&m_jit);
-
- addSlowPathGenerator(
- slowPathCall(slowCase, this, operationNotifyWrite, NoResult, set, valueGPR));
-
- noResult(node);
+ compileNotifyWrite(node);
break;
}
#include "DFGClobberize.h"
#include "DFGGraph.h"
#include "DFGPhase.h"
+#include "JSCInlines.h"
namespace JSC { namespace DFG {
macro(V_JITOperation_EOZJ, functionType(voidType, intPtr, intPtr, int32, int64)) \
macro(V_JITOperation_EC, functionType(voidType, intPtr, intPtr)) \
macro(V_JITOperation_ECb, functionType(voidType, intPtr, intPtr)) \
- macro(V_JITOperation_EVwsJ, functionType(voidType, intPtr, intPtr, int64)) \
+ macro(V_JITOperation_EWs, functionType(voidType, intPtr, intPtr)) \
macro(V_JITOperation_EZJZZZ, functionType(voidType, intPtr, int32, int64, int32, int32, int32)) \
macro(V_JITOperation_J, functionType(voidType, int64)) \
macro(V_JITOperation_Z, functionType(voidType, int32)) \
SymbolTable* table = m_graph.symbolTableFor(m_node->origin.semantic);
Structure* structure = m_graph.globalObjectFor(m_node->origin.semantic)->activationStructure();
+ if (table->singletonScope()->isStillValid()) {
+ LValue callResult = vmCall(
+ m_out.operation(operationCreateActivationDirect), m_callFrame, weakPointer(structure),
+ scope, weakPointer(table));
+ setJSValue(callResult);
+ return;
+ }
+
LBasicBlock slowPath = FTL_NEW_BLOCK(m_out, ("CreateActivation slow path"));
LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("CreateActivation continuation"));
void compileNewFunction()
{
+ FunctionExecutable* executable = m_node->castOperand<FunctionExecutable*>();
+ J_JITOperation_EJscC function;
+ if (executable->singletonFunction()->isStillValid())
+ function = operationNewFunction;
+ else
+ function = operationNewFunctionWithInvalidatedReallocationWatchpoint;
+
LValue result = vmCall(
- m_out.operation(operationNewFunction), m_callFrame,
- lowCell(m_node->child1()), weakPointer(m_node->castOperand<FunctionExecutable*>()));
+ m_out.operation(function), m_callFrame, lowCell(m_node->child1()),
+ weakPointer(executable));
setJSValue(result);
}
void compileNotifyWrite()
{
- VariableWatchpointSet* set = m_node->variableWatchpointSet();
-
- LValue value = lowJSValue(m_node->child1());
+ WatchpointSet* set = m_node->watchpointSet();
LBasicBlock isNotInvalidated = FTL_NEW_BLOCK(m_out, ("NotifyWrite not invalidated case"));
- LBasicBlock notifySlow = FTL_NEW_BLOCK(m_out, ("NotifyWrite notify slow case"));
LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("NotifyWrite continuation"));
LValue state = m_out.load8(m_out.absolute(set->addressOfState()));
-
m_out.branch(
m_out.equal(state, m_out.constInt8(IsInvalidated)),
usually(continuation), rarely(isNotInvalidated));
- LBasicBlock lastNext = m_out.appendTo(isNotInvalidated, notifySlow);
-
- m_out.branch(
- m_out.equal(value, m_out.load64(m_out.absolute(set->addressOfInferredValue()))),
- unsure(continuation), unsure(notifySlow));
-
- m_out.appendTo(notifySlow, continuation);
+ LBasicBlock lastNext = m_out.appendTo(isNotInvalidated, continuation);
- vmCall(m_out.operation(operationNotifyWrite), m_callFrame, m_out.constIntPtr(set), value);
+ vmCall(m_out.operation(operationNotifyWrite), m_callFrame, m_out.constIntPtr(set));
m_out.jump(continuation);
m_out.appendTo(continuation, lastNext);
namespace JSC {
+String StackFrame::friendlySourceURL() const
+{
+ String traceLine;
+
+ switch (codeType) {
+ case StackFrameEvalCode:
+ case StackFrameFunctionCode:
+ case StackFrameGlobalCode:
+ if (!sourceURL.isEmpty())
+ traceLine = sourceURL.impl();
+ break;
+ case StackFrameNativeCode:
+ traceLine = "[native code]";
+ break;
+ }
+ return traceLine.isNull() ? emptyString() : traceLine;
+}
+
+String StackFrame::friendlyFunctionName(CallFrame* callFrame) const
+{
+ String traceLine;
+ JSObject* stackFrameCallee = callee.get();
+
+ switch (codeType) {
+ case StackFrameEvalCode:
+ traceLine = "eval code";
+ break;
+ case StackFrameNativeCode:
+ if (callee)
+ traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl();
+ break;
+ case StackFrameFunctionCode:
+ traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl();
+ break;
+ case StackFrameGlobalCode:
+ traceLine = "global code";
+ break;
+ }
+ return traceLine.isNull() ? emptyString() : traceLine;
+}
+
JSValue eval(CallFrame* callFrame)
{
if (!callFrame->argumentCount())
#include "ArgList.h"
#include "JSCJSValue.h"
#include "JSCell.h"
-#include "JSFunction.h"
#include "JSObject.h"
#include "JSStack.h"
#include "LLIntData.h"
class ExecutableBase;
class FunctionExecutable;
class VM;
+ class JSFunction;
class JSGlobalObject;
class LLIntOffsetsExtractor;
class ProgramExecutable;
unsigned bytecodeOffset;
String sourceURL;
JS_EXPORT_PRIVATE String toString(CallFrame*);
- String friendlySourceURL() const
- {
- String traceLine;
-
- switch (codeType) {
- case StackFrameEvalCode:
- case StackFrameFunctionCode:
- case StackFrameGlobalCode:
- if (!sourceURL.isEmpty())
- traceLine = sourceURL.impl();
- break;
- case StackFrameNativeCode:
- traceLine = "[native code]";
- break;
- }
- return traceLine.isNull() ? emptyString() : traceLine;
- }
- String friendlyFunctionName(CallFrame* callFrame) const
- {
- String traceLine;
- JSObject* stackFrameCallee = callee.get();
-
- switch (codeType) {
- case StackFrameEvalCode:
- traceLine = "eval code";
- break;
- case StackFrameNativeCode:
- if (callee)
- traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl();
- break;
- case StackFrameFunctionCode:
- traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl();
- break;
- case StackFrameGlobalCode:
- traceLine = "global code";
- break;
- }
- return traceLine.isNull() ? emptyString() : traceLine;
- }
+ String friendlySourceURL() const;
+ String friendlyFunctionName(CallFrame*) const;
JS_EXPORT_PRIVATE void computeLineAndColumn(unsigned& line, unsigned& column);
private:
}
#endif
+void JIT::emitNotifyWrite(WatchpointSet* set)
+{
+ if (!set || set->state() == IsInvalidated)
+ return;
+
+ addSlowCase(branch8(NotEqual, AbsoluteAddress(set->addressOfState()), TrustedImm32(IsInvalidated)));
+}
+
void JIT::assertStackPointerOffset()
{
if (ASSERT_DISABLED)
DEFINE_SLOW_OP(is_object_or_null)
DEFINE_SLOW_OP(typeof)
- DEFINE_OP(op_touch_entry)
DEFINE_OP(op_add)
DEFINE_OP(op_bitand)
DEFINE_OP(op_bitor)
void assertStackPointerOffset();
- void emit_op_touch_entry(Instruction*);
void emit_op_add(Instruction*);
void emit_op_bitand(Instruction*);
void emit_op_bitor(Instruction*);
void emitGetGlobalVar(uintptr_t operand);
void emitGetClosureVar(int scope, uintptr_t operand);
void emitPutGlobalProperty(uintptr_t* operandSlot, int value);
-#if USE(JSVALUE64)
- void emitNotifyWrite(RegisterID value, RegisterID scratch, VariableWatchpointSet*);
-#else
- void emitNotifyWrite(RegisterID tag, RegisterID payload, RegisterID scratch, VariableWatchpointSet*);
-#endif
- void emitPutGlobalVar(uintptr_t operand, int value, VariableWatchpointSet*);
- void emitPutClosureVar(int scope, uintptr_t operand, int value, VariableWatchpointSet*);
+ void emitNotifyWrite(WatchpointSet*);
+ void emitPutGlobalVar(uintptr_t operand, int value, WatchpointSet*);
+ void emitPutClosureVar(int scope, uintptr_t operand, int value, WatchpointSet*);
void emitInitRegister(int dst);
#endif // USE(JSVALUE64)
-void JIT::emit_op_touch_entry(Instruction* currentInstruction)
-{
- if (m_codeBlock->symbolTable()->m_functionEnteredOnce.hasBeenInvalidated())
- return;
-
- JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_touch_entry);
- slowPathCall.call();
-}
-
void JIT::emit_op_loop_hint(Instruction*)
{
// Emit the JIT optimization check:
return JSValue::encode(JSFunction::create(vm, static_cast<FunctionExecutable*>(functionExecutable), scope));
}
+EncodedJSValue JIT_OPERATION operationNewFunctionWithInvalidatedReallocationWatchpoint(ExecState* exec, JSScope* scope, JSCell* functionExecutable)
+{
+ ASSERT(functionExecutable->inherits(FunctionExecutable::info()));
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+ return JSValue::encode(JSFunction::createWithInvalidatedReallocationWatchpoint(vm, static_cast<FunctionExecutable*>(functionExecutable), scope));
+}
+
JSCell* JIT_OPERATION operationNewObject(ExecState* exec, Structure* structure)
{
VM* vm = &exec->vm();
if (modeAndType.type() == LocalClosureVar) {
JSLexicalEnvironment* environment = jsCast<JSLexicalEnvironment*>(scope);
environment->variableAt(ScopeOffset(pc[6].u.operand)).set(vm, environment, value);
- if (VariableWatchpointSet* set = pc[5].u.watchpointSet)
- set->notifyWrite(vm, value, "Executed op_put_scope<LocalClosureVar>");
+ if (WatchpointSet* set = pc[5].u.watchpointSet)
+ set->touch("Executed op_put_scope<LocalClosureVar>");
return;
}
if (modeAndType.mode() == ThrowIfNotFound && !scope->hasProperty(exec, ident)) {
#include "PutKind.h"
#include "SpillRegistersMode.h"
#include "StructureStubInfo.h"
-#include "VariableWatchpointSet.h"
namespace JSC {
Symtab: SymbolTable*
V: void
Vm: VM*
- Vws: VariableWatchpointSet*
+ Ws: WatchpointSet*
Z: int32_t
*/
typedef void JIT_OPERATION (*V_JITOperation_EPc)(ExecState*, Instruction*);
typedef void JIT_OPERATION (*V_JITOperation_EPZJ)(ExecState*, void*, int32_t, EncodedJSValue);
typedef void JIT_OPERATION (*V_JITOperation_ESsiJJI)(ExecState*, StructureStubInfo*, EncodedJSValue, EncodedJSValue, StringImpl*);
-typedef void JIT_OPERATION (*V_JITOperation_EVwsJ)(ExecState*, VariableWatchpointSet*, EncodedJSValue);
+typedef void JIT_OPERATION (*V_JITOperation_EWs)(ExecState*, WatchpointSet*);
typedef void JIT_OPERATION (*V_JITOperation_EZ)(ExecState*, int32_t);
typedef void JIT_OPERATION (*V_JITOperation_EZJ)(ExecState*, int32_t, EncodedJSValue);
typedef void JIT_OPERATION (*V_JITOperation_EZJZZZ)(ExecState*, int32_t, EncodedJSValue, int32_t, int32_t, int32_t);
EncodedJSValue JIT_OPERATION operationNewArrayBufferWithProfile(ExecState*, ArrayAllocationProfile*, const JSValue* values, int32_t size) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationNewArrayWithSizeAndProfile(ExecState*, ArrayAllocationProfile*, EncodedJSValue size) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationNewFunction(ExecState*, JSScope*, JSCell*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationNewFunctionWithInvalidatedReallocationWatchpoint(ExecState*, JSScope*, JSCell*) WTF_INTERNAL;
JSCell* JIT_OPERATION operationNewObject(ExecState*, Structure*) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationNewRegexp(ExecState*, void*) WTF_INTERNAL;
void JIT_OPERATION operationHandleWatchdogTimer(ExecState*) WTF_INTERNAL;
storePtr(regT2, BaseIndex(regT0, regT1, TimesEight, (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue)));
}
-void JIT::emitNotifyWrite(RegisterID value, RegisterID scratch, VariableWatchpointSet* set)
-{
- if (!set || set->state() == IsInvalidated)
- return;
-
- load8(set->addressOfState(), scratch);
- Jump isDone = branch32(Equal, scratch, TrustedImm32(IsInvalidated));
- addSlowCase(branch64(NotEqual, AbsoluteAddress(set->addressOfInferredValue()), value));
- isDone.link(this);
-}
-
-void JIT::emitPutGlobalVar(uintptr_t operand, int value, VariableWatchpointSet* set)
+void JIT::emitPutGlobalVar(uintptr_t operand, int value, WatchpointSet* set)
{
emitGetVirtualRegister(value, regT0);
- emitNotifyWrite(regT0, regT1, set);
+ emitNotifyWrite(set);
storePtr(regT0, reinterpret_cast<void*>(operand));
}
-void JIT::emitPutClosureVar(int scope, uintptr_t operand, int value, VariableWatchpointSet* set)
+void JIT::emitPutClosureVar(int scope, uintptr_t operand, int value, WatchpointSet* set)
{
emitGetVirtualRegister(value, regT1);
emitGetVirtualRegister(scope, regT0);
- emitNotifyWrite(regT1, regT2, set);
+ emitNotifyWrite(set);
storePtr(regT1, Address(regT0, JSEnvironmentRecord::offsetOfVariables() + operand * sizeof(Register)));
}
store32(regT2, BaseIndex(regT0, regT1, TimesEight, (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)));
}
-void JIT::emitNotifyWrite(RegisterID tag, RegisterID payload, RegisterID scratch, VariableWatchpointSet* set)
-{
- if (!set || set->state() == IsInvalidated)
- return;
-
- load8(set->addressOfState(), scratch);
- Jump isDone = branch32(Equal, scratch, TrustedImm32(IsInvalidated));
-
- JumpList notifySlow = branch32(
- NotEqual, AbsoluteAddress(set->addressOfInferredValue()->payloadPointer()), payload);
- notifySlow.append(branch32(
- NotEqual, AbsoluteAddress(set->addressOfInferredValue()->tagPointer()), tag));
- addSlowCase(notifySlow);
-
- isDone.link(this);
-}
-
-void JIT::emitPutGlobalVar(uintptr_t operand, int value, VariableWatchpointSet* set)
+void JIT::emitPutGlobalVar(uintptr_t operand, int value, WatchpointSet* set)
{
emitLoad(value, regT1, regT0);
- emitNotifyWrite(regT1, regT0, regT2, set);
+ emitNotifyWrite(set);
store32(regT1, reinterpret_cast<char*>(operand) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag));
store32(regT0, reinterpret_cast<char*>(operand) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload));
}
-void JIT::emitPutClosureVar(int scope, uintptr_t operand, int value, VariableWatchpointSet* set)
+void JIT::emitPutClosureVar(int scope, uintptr_t operand, int value, WatchpointSet* set)
{
emitLoad(value, regT3, regT2);
emitLoad(scope, regT1, regT0);
- emitNotifyWrite(regT3, regT2, regT4, set);
+ emitNotifyWrite(set);
store32(regT3, Address(regT0, JSEnvironmentRecord::offsetOfVariables() + operand * sizeof(Register) + TagOffset));
store32(regT2, Address(regT0, JSEnvironmentRecord::offsetOfVariables() + operand * sizeof(Register) + PayloadOffset));
}
linkCount++;
if ((resolveType == GlobalVar || resolveType == GlobalVarWithVarInjectionChecks || resolveType == LocalClosureVar)
&& currentInstruction[5].u.watchpointSet->state() != IsInvalidated)
- linkCount += 2;
+ linkCount++;
if (!linkCount)
return;
while (linkCount--)
if (modeAndType.type() == LocalClosureVar) {
JSLexicalEnvironment* environment = jsCast<JSLexicalEnvironment*>(scope);
environment->variableAt(ScopeOffset(pc[6].u.operand)).set(vm, environment, value);
- if (VariableWatchpointSet* set = pc[5].u.watchpointSet)
- set->notifyWrite(vm, value, "Executed op_put_scope<LocalClosureVar>");
+
+ // Have to do this *after* the write, because if this puts the set into IsWatched, then we need
+ // to have already changed the value of the variable. Otherwise we might watch and constant-fold
+ // to the Undefined value from before the assignment.
+ if (WatchpointSet* set = pc[5].u.watchpointSet)
+ set->touch("Executed op_put_scope<LocalClosureVar>");
LLINT_END();
}
continuation(scratch1)
end
+macro notifyWrite(set, slow)
+ bbneq WatchpointSet::m_state[set], IsInvalidated, slow
+end
+
macro checkSwitchToJIT(increment, action)
loadp CodeBlock[cfr], t0
baddis increment, CodeBlock::m_llintExecuteCounter + BaselineExecutionCounter::m_counter[t0], .continue
# Value-representation-agnostic code.
-_llint_op_touch_entry:
- traceExecution()
- callSlowPath(_slow_path_touch_entry)
- dispatch(1)
-
-
_llint_op_create_direct_arguments:
traceExecution()
callSlowPath(_slow_path_create_direct_arguments)
dispatch(3)
-macro notifyWrite(set, valueTag, valuePayload, scratch, slow)
- loadb VariableWatchpointSet::m_state[set], scratch
- bieq scratch, IsInvalidated, .done
- bineq valuePayload, VariableWatchpointSet::m_inferredValue + PayloadOffset[set], slow
- bineq valueTag, VariableWatchpointSet::m_inferredValue + TagOffset[set], slow
-.done:
-end
-
_llint_op_not:
traceExecution()
loadi 8[PC], t0
loadisFromInstruction(3, t0)
loadConstantOrVariable(t0, t1, t2)
loadpFromInstruction(5, t3)
- notifyWrite(t3, t1, t2, t0, .pDynamic)
+ notifyWrite(t3, .pDynamic)
loadpFromInstruction(6, t0)
storei t1, TagOffset[t0]
storei t2, PayloadOffset[t0]
loadConstantOrVariable(t1, t2, t3)
loadpFromInstruction(5, t4)
btpz t4, .noVariableWatchpointSet
- notifyWrite(t4, t2, t3, t1, .pDynamic)
+ notifyWrite(t4, .pDynamic)
.noVariableWatchpointSet:
loadisFromInstruction(6, t1)
storei t2, JSEnvironmentRecord_variables + TagOffset[t0, t1, 8]
dispatch(3)
-macro notifyWrite(set, value, scratch, slow)
- loadb VariableWatchpointSet::m_state[set], scratch
- bieq scratch, IsInvalidated, .done
- bqneq value, VariableWatchpointSet::m_inferredValue[set], slow
-.done:
-end
-
_llint_op_not:
traceExecution()
loadisFromInstruction(2, t0)
loadisFromInstruction(3, t0)
loadConstantOrVariable(t0, t1)
loadpFromInstruction(5, t2)
- notifyWrite(t2, t1, t0, .pDynamic)
loadpFromInstruction(6, t0)
+ notifyWrite(t2, .pDynamic)
storeq t1, [t0]
end
loadConstantOrVariable(t1, t2)
loadpFromInstruction(5, t3)
btpz t3, .noVariableWatchpointSet
- notifyWrite(t3, t2, t1, .pDynamic)
+ notifyWrite(t3, .pDynamic)
.noVariableWatchpointSet:
loadisFromInstruction(6, t1)
storeq t2, JSEnvironmentRecord_variables[t0, t1, 8]
#include "ScopedArguments.h"
#include "StructureRareDataInlines.h"
#include "TypeProfilerLog.h"
-#include "VariableWatchpointSetInlines.h"
#include <wtf/StringPrintStream.h>
namespace JSC {
RETURN_TWO(0, setupArityCheckData(vm, slotsToAdd));
}
-SLOW_PATH_DECL(slow_path_touch_entry)
-{
- BEGIN();
- exec->codeBlock()->symbolTable()->m_functionEnteredOnce.touch("Function (re)entered");
- END();
-}
-
SLOW_PATH_DECL(slow_path_create_direct_arguments)
{
BEGIN();
SLOW_PATH_HIDDEN_DECL(slow_path_call_arityCheck);
SLOW_PATH_HIDDEN_DECL(slow_path_construct_arityCheck);
-SLOW_PATH_HIDDEN_DECL(slow_path_touch_entry);
SLOW_PATH_HIDDEN_DECL(slow_path_create_direct_arguments);
SLOW_PATH_HIDDEN_DECL(slow_path_create_scoped_arguments);
SLOW_PATH_HIDDEN_DECL(slow_path_create_out_of_band_arguments);
m_typeProfilingEndOffset = unlinkedExecutable->typeProfilingEndOffset();
}
+void FunctionExecutable::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ m_singletonFunction.set(vm, this, InferredValue::create(vm));
+}
+
void FunctionExecutable::destroy(JSCell* cell)
{
static_cast<FunctionExecutable*>(cell)->FunctionExecutable::~FunctionExecutable();
if (thisObject->m_codeBlockForConstruct)
thisObject->m_codeBlockForConstruct->visitAggregate(visitor);
visitor.append(&thisObject->m_unlinkedExecutable);
+ visitor.append(&thisObject->m_singletonFunction);
}
SymbolTable* FunctionExecutable::symbolTable(CodeSpecializationKind kind)
#include "CompilationResult.h"
#include "DFGPlan.h"
#include "HandlerInfo.h"
-#include "JSFunction.h"
-#include "Interpreter.h"
+#include "InferredValue.h"
#include "JITCode.h"
#include "JSGlobalObject.h"
#include "RegisterPreservationMode.h"
void unlinkCalls();
void clearCode();
+
+ InferredValue* singletonFunction() { return m_singletonFunction.get(); }
private:
FunctionExecutable(
VM&, const SourceCode&, UnlinkedFunctionExecutable*, unsigned firstLine,
unsigned lastLine, unsigned startColumn, unsigned endColumn);
+
+ void finishCreation(VM&);
bool isCompiling()
{
}
friend class ScriptExecutable;
-
+
WriteBarrier<UnlinkedFunctionExecutable> m_unlinkedExecutable;
RefPtr<FunctionCodeBlock> m_codeBlockForCall;
RefPtr<FunctionCodeBlock> m_codeBlockForConstruct;
RefPtr<TypeSet> m_returnStatementTypeSet;
unsigned m_parametersStartOffset;
+ WriteBarrier<InferredValue> m_singletonFunction;
};
inline void ExecutableBase::clearCodeVirtual(ExecutableBase* executable)
--- /dev/null
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "InferredValue.h"
+
+#include "JSCInlines.h"
+
+namespace JSC {
+
+const ClassInfo InferredValue::s_info = { "InferredValue", 0, 0, CREATE_METHOD_TABLE(InferredValue) };
+
+InferredValue* InferredValue::create(VM& vm)
+{
+ InferredValue* result = new (NotNull, allocateCell<InferredValue>(vm.heap)) InferredValue(vm);
+ result->finishCreation(vm);
+ return result;
+}
+
+void InferredValue::destroy(JSCell* cell)
+{
+ InferredValue* inferredValue = static_cast<InferredValue*>(cell);
+ inferredValue->InferredValue::~InferredValue();
+}
+
+Structure* InferredValue::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+{
+ return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info());
+}
+
+void InferredValue::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+ InferredValue* inferredValue = jsCast<InferredValue*>(cell);
+
+ if (inferredValue->m_set.hasBeenInvalidated()) {
+ inferredValue->m_cleanup = nullptr;
+ return;
+ }
+
+ if (!inferredValue->m_value)
+ return;
+ if (!inferredValue->m_value.get().isCell())
+ return;
+
+ if (!inferredValue->m_cleanup)
+ inferredValue->m_cleanup = std::make_unique<ValueCleanup>(inferredValue);
+ visitor.addUnconditionalFinalizer(inferredValue->m_cleanup.get());
+}
+
+InferredValue::InferredValue(VM& vm)
+ : Base(vm, vm.inferredValueStructure.get())
+ , m_set(ClearWatchpoint)
+{
+}
+
+InferredValue::~InferredValue()
+{
+}
+
+void InferredValue::notifyWriteSlow(VM& vm, JSValue value, const FireDetail& detail)
+{
+ ASSERT(!!value);
+ switch (m_set.state()) {
+ case ClearWatchpoint:
+ m_value.set(vm, this, value);
+ m_set.startWatching();
+ return;
+
+ case IsWatched:
+ ASSERT(!!m_value);
+ if (m_value.get() == value)
+ return;
+ invalidate(detail);
+ return;
+
+ case IsInvalidated:
+ ASSERT_NOT_REACHED();
+ return;
+ }
+
+ ASSERT_NOT_REACHED();
+}
+
+void InferredValue::notifyWriteSlow(VM& vm, JSValue value, const char* reason)
+{
+ notifyWriteSlow(vm, value, StringFireDetail(reason));
+}
+
+InferredValue::ValueCleanup::ValueCleanup(InferredValue* owner)
+ : m_owner(owner)
+{
+}
+
+InferredValue::ValueCleanup::~ValueCleanup()
+{
+}
+
+void InferredValue::ValueCleanup::finalizeUnconditionally()
+{
+ ASSERT(m_owner->m_value);
+ ASSERT(m_owner->m_value.get().isCell());
+
+ if (Heap::isMarked(m_owner->m_value.get().asCell()))
+ return;
+
+ m_owner->invalidate(StringFireDetail("InferredValue clean-up during GC"));
+}
+
+} // namespace JSC
+
--- /dev/null
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef InferredValue_h
+#define InferredValue_h
+
+#include "JSCell.h"
+#include "Watchpoint.h"
+#include "WriteBarrier.h"
+
+namespace JSC {
+
+// Allocate one of these if you'd like to infer a constant value. Writes to the value should use
+// notifyWrite(). So long as exactly one value had ever been written and invalidate() has never been
+// called, and you register a watchpoint, you can rely on the inferredValue() being the one true
+// value.
+//
+// Commonly used for inferring singletons - in that case each allocation does notifyWrite(). But you
+// can use it for other things as well.
+
+class InferredValue : public JSCell {
+public:
+ typedef JSCell Base;
+
+ static InferredValue* create(VM&);
+
+ static const bool needsDestruction = true;
+ static const bool hasImmortalStructure = true;
+ static void destroy(JSCell*);
+
+ static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype);
+
+ static void visitChildren(JSCell*, SlotVisitor&);
+
+ DECLARE_INFO;
+
+ // For the purpose of deciding whether or not to watch this variable, you only need
+ // to inspect inferredValue(). If this returns something other than the empty
+ // value, then it means that at all future safepoints, this watchpoint set will be
+ // in one of these states:
+ //
+ // IsWatched: in this case, the variable's value must still be the
+ // inferredValue.
+ //
+ // IsInvalidated: in this case the variable's value may be anything but you'll
+ // either notice that it's invalidated and not install the watchpoint, or
+ // you will have been notified that the watchpoint was fired.
+ JSValue inferredValue() { return m_value.get(); }
+
+ // Forwards some WatchpointSet methods.
+ WatchpointState state() const { return m_set.state(); }
+ bool isStillValid() const { return m_set.isStillValid(); }
+ bool hasBeenInvalidated() const { return m_set.hasBeenInvalidated(); }
+ void add(Watchpoint* watchpoint) { m_set.add(watchpoint); }
+
+ void notifyWrite(VM& vm, JSValue value, const FireDetail& detail)
+ {
+ if (LIKELY(m_set.stateOnJSThread() == IsInvalidated))
+ return;
+ notifyWriteSlow(vm, value, detail);
+ }
+
+ void notifyWrite(VM& vm, JSValue value, const char* reason)
+ {
+ if (LIKELY(m_set.stateOnJSThread() == IsInvalidated))
+ return;
+ notifyWriteSlow(vm, value, reason);
+ }
+
+ void invalidate(const FireDetail& detail)
+ {
+ m_value.clear();
+ m_set.invalidate(detail);
+ }
+
+protected:
+ static const unsigned StructureFlags = StructureIsImmortal | Base::StructureFlags;
+
+private:
+ InferredValue(VM&);
+ ~InferredValue();
+
+ JS_EXPORT_PRIVATE void notifyWriteSlow(VM&, JSValue, const FireDetail&);
+ JS_EXPORT_PRIVATE void notifyWriteSlow(VM&, JSValue, const char* reason);
+
+ // We could have used Weak<>. But we want arbitrary JSValues, not just cells. It's also somewhat
+ // convenient to have eager notification of death.
+ class ValueCleanup : public UnconditionalFinalizer {
+ WTF_MAKE_FAST_ALLOCATED;
+
+ public:
+ ValueCleanup(InferredValue*);
+ virtual ~ValueCleanup();
+
+ protected:
+ void finalizeUnconditionally() override;
+
+ private:
+ InferredValue* m_owner;
+ };
+
+ friend class ValueCleanup;
+
+ InlineWatchpointSet m_set;
+ WriteBarrier<Unknown> m_value;
+ std::unique_ptr<ValueCleanup> m_cleanup;
+};
+
+// FIXME: We could have an InlineInferredValue, which only allocates the InferredValue object when
+// a notifyWrite() transitions us towards watching, and then clears the reference (allowing the object
+// to die) when we get invalidated.
+
+} // namespace JSC
+
+#endif // InferredValue_h
+
JSEnvironmentRecord* thisObject = jsCast<JSEnvironmentRecord*>(cell);
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
Base::visitChildren(thisObject, visitor);
- visitor.appendValues(thisObject->variables(), thisObject->m_symbolTable->scopeSize());
+ visitor.appendValues(thisObject->variables(), thisObject->symbolTable()->scopeSize());
}
} // namespace JSC
bool isValid(ScopeOffset offset)
{
- return !!offset && offset.offset() < m_symbolTable->scopeSize();
+ return !!offset && offset.offset() < symbolTable()->scopeSize();
}
WriteBarrierBase<Unknown>& variableAt(ScopeOffset offset)
void finishCreation(VM& vm)
{
finishCreationUninitialized(vm);
- for (unsigned i = m_symbolTable->scopeSize(); i--;) {
+ for (unsigned i = symbolTable()->scopeSize(); i--;) {
// Filling this with undefined is useful because that's what variables start out as.
variableAt(ScopeOffset(i)).setUndefined();
}
return isHostFunction();
}
+JSFunction* JSFunction::create(VM& vm, FunctionExecutable* executable, JSScope* scope)
+{
+ JSFunction* result = createImpl(vm, executable, scope);
+ executable->singletonFunction()->notifyWrite(vm, result, "Allocating a function");
+ return result;
+}
+
JSFunction* JSFunction::create(VM& vm, JSGlobalObject* globalObject, int length, const String& name, NativeFunction nativeFunction, Intrinsic intrinsic, NativeFunction nativeConstructor)
{
NativeExecutable* executable;
const static unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames;
JS_EXPORT_PRIVATE static JSFunction* create(VM&, JSGlobalObject*, int length, const String& name, NativeFunction, Intrinsic = NoIntrinsic, NativeFunction nativeConstructor = callHostFunctionAsConstructor);
+
+ static JSFunction* createWithInvalidatedReallocationWatchpoint(VM&, FunctionExecutable*, JSScope*);
- static JSFunction* create(VM& vm, FunctionExecutable* executable, JSScope* scope)
- {
- JSFunction* function = new (NotNull, allocateCell<JSFunction>(vm.heap)) JSFunction(vm, executable, scope);
- ASSERT(function->structure()->globalObject());
- function->finishCreation(vm);
- return function;
- }
+ static JSFunction* create(VM&, FunctionExecutable*, JSScope*);
static JSFunction* createBuiltinFunction(VM&, FunctionExecutable*, JSGlobalObject*);
static void visitChildren(JSCell*, SlotVisitor&);
private:
+ static JSFunction* createImpl(VM& vm, FunctionExecutable* executable, JSScope* scope)
+ {
+ JSFunction* function = new (NotNull, allocateCell<JSFunction>(vm.heap)) JSFunction(vm, executable, scope);
+ ASSERT(function->structure()->globalObject());
+ function->finishCreation(vm);
+ return function;
+ }
+
friend class LLIntOffsetsExtractor;
static EncodedJSValue argumentsGetter(ExecState*, JSObject*, EncodedJSValue, PropertyName);
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
namespace JSC {
+inline JSFunction* JSFunction::createWithInvalidatedReallocationWatchpoint(
+ VM& vm, FunctionExecutable* executable, JSScope* scope)
+{
+ ASSERT(executable->singletonFunction()->hasBeenInvalidated());
+ return createImpl(vm, executable, scope);
+}
+
inline JSFunction::JSFunction(VM& vm, FunctionExecutable* executable, JSScope* scope)
: Base(vm, scope, scope->globalObject()->functionStructure())
, m_executable(vm, this, executable)
#include "Symbol.h"
#include "SymbolConstructor.h"
#include "SymbolPrototype.h"
-#include "VariableWatchpointSetInlines.h"
+#include "VariableWriteFireDetail.h"
#include "WeakGCMapInlines.h"
#include "WeakMapConstructor.h"
#include "WeakMapPrototype.h"
ScopeOffset offset = symbolTable()->takeNextScopeOffset(locker);
SymbolTableEntry newEntry(VarOffset(offset), (constantMode == IsConstant) ? ReadOnly : 0);
if (constantMode == IsVariable)
- newEntry.prepareToWatch(symbolTable());
+ newEntry.prepareToWatch();
else
newEntry.disableWatching();
symbolTable()->add(locker, ident.impl(), newEntry);
NewGlobalVar var = addGlobalVar(propertyName, IsVariable);
variableAt(var.offset).set(exec->vm(), this, value);
if (var.set)
- var.set->notifyWrite(vm, value, VariableWriteFireDetail(this, propertyName));
+ var.set->touch(VariableWriteFireDetail(this, propertyName));
}
static inline JSObject* lastInPrototypeChain(JSObject* object)
struct NewGlobalVar {
ScopeOffset offset;
- VariableWatchpointSet* set;
+ WatchpointSet* set;
};
NewGlobalVar addGlobalVar(const Identifier&, ConstantMode);
ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
WriteBarrierBase<Unknown>* reg;
+ WatchpointSet* set;
{
GCSafeConcurrentJITLocker locker(symbolTable()->m_lock, exec->vm().heap);
SymbolTable::Map::iterator iter = symbolTable()->find(locker, propertyName.uid());
// Defend against the inspector asking for a var after it has been optimized out.
if (!isValid(offset))
return false;
- if (VariableWatchpointSet* set = iter->value.watchpointSet())
- set->invalidate(VariableWriteFireDetail(this, propertyName)); // Don't mess around - if we had found this statically, we would have invalidated it.
+ set = iter->value.watchpointSet();
reg = &variableAt(offset);
}
reg->set(vm, this, value);
+ if (set)
+ set->invalidate(VariableWriteFireDetail(this, propertyName)); // Don't mess around - if we had found this statically, we would have invalidated it.
return true;
}
namespace JSC {
class ScopeChainIterator;
-class VariableWatchpointSet;
+class WatchpointSet;
enum ResolveMode {
ThrowIfNotFound,
}
struct ResolveOp {
- ResolveOp(ResolveType type, size_t depth, Structure* structure, JSLexicalEnvironment* lexicalEnvironment, VariableWatchpointSet* watchpointSet, uintptr_t operand)
+ ResolveOp(ResolveType type, size_t depth, Structure* structure, JSLexicalEnvironment* lexicalEnvironment, WatchpointSet* watchpointSet, uintptr_t operand)
: type(type)
, depth(depth)
, structure(structure)
size_t depth;
Structure* structure;
JSLexicalEnvironment* lexicalEnvironment;
- VariableWatchpointSet* watchpointSet;
+ WatchpointSet* watchpointSet;
uintptr_t operand;
};
public:
typedef JSSymbolTableObject Base;
+ // This is not thread-safe, since m_variables is a segmented vector, and its spine can resize with
+ // malloc/free if new variables - unrelated to the one you are accessing - are added. You can get
+ // around this by grabbing m_lock, or finding some other way to get to the variable pointer (global
+ // variable access bytecode instructions will have a direct pointer already).
WriteBarrier<Unknown>& variableAt(ScopeOffset offset) { return m_variables[offset.offset()]; }
// This is a slow method call, which searches the register bank to find the index
void finishCreation(VM& vm)
{
Base::finishCreation(vm);
- m_symbolTable.set(vm, this, SymbolTable::create(vm));
+ setSymbolTable(vm, SymbolTable::create(vm));
}
SegmentedVector<WriteBarrier<Unknown>, 16> m_variables;
#include "JSScope.h"
#include "PropertyDescriptor.h"
#include "SymbolTable.h"
-#include "VariableWatchpointSetInlines.h"
+#include "VariableWriteFireDetail.h"
namespace JSC {
: Base(vm, structure, scope)
{
ASSERT(symbolTable);
+ setSymbolTable(vm, symbolTable);
+ }
+
+ void setSymbolTable(VM& vm, SymbolTable* symbolTable)
+ {
+ ASSERT(!m_symbolTable);
+ symbolTable->singletonScope()->notifyWrite(vm, this, "Allocated a scope");
m_symbolTable.set(vm, this, symbolTable);
}
-
+
static void visitChildren(JSCell*, SlotVisitor&);
-
+
+private:
WriteBarrier<SymbolTable> m_symbolTable;
};
ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(object));
WriteBarrierBase<Unknown>* reg;
+ WatchpointSet* set;
{
SymbolTable& symbolTable = *object->symbolTable();
// FIXME: This is very suspicious. We shouldn't need a GC-safe lock here.
throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
return true;
}
- if (VariableWatchpointSet* set = iter->value.watchpointSet()) {
- // FIXME: It's strange that we're doing this while holding the symbol table's lock.
- // https://bugs.webkit.org/show_bug.cgi?id=134601
- set->notifyWrite(vm, value, object, propertyName);
- }
+ set = iter->value.watchpointSet();
reg = &object->variableAt(fastEntry.scopeOffset());
}
// I'd prefer we not hold lock while executing barriers, since I prefer to reserve
// the right for barriers to be able to trigger GC. And I don't want to hold VM
// locks while GC'ing.
reg->set(vm, object, value);
+ if (set)
+ VariableWriteFireDetail::touch(set, object, propertyName);
return true;
}
ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(object));
WriteBarrierBase<Unknown>* reg;
+ WatchpointSet* set;
{
SymbolTable& symbolTable = *object->symbolTable();
ConcurrentJITLocker locker(symbolTable.m_lock);
return false;
SymbolTableEntry& entry = iter->value;
ASSERT(!entry.isNull());
- if (VariableWatchpointSet* set = entry.watchpointSet())
- set->notifyWrite(vm, value, object, propertyName);
+ set = entry.watchpointSet();
entry.setAttributes(attributes);
reg = &object->variableAt(entry.scopeOffset());
}
reg->set(vm, object, value);
+ if (set)
+ VariableWriteFireDetail::touch(set, object, propertyName);
return true;
}
#define PutPropertySlot_h
#include "JSCJSValue.h"
+#include "PropertyOffset.h"
#include <wtf/Assertions.h>
#include "JSCInlines.h"
#include "SlotVisitorInlines.h"
#include "TypeProfiler.h"
-#include "VariableWatchpointSetInlines.h"
namespace JSC {
delete fatEntry();
}
-JSValue SymbolTableEntry::inferredValue()
-{
- if (!isFat())
- return JSValue();
- return fatEntry()->m_watchpoints->inferredValue();
-}
-
-void SymbolTableEntry::prepareToWatch(SymbolTable* symbolTable)
+void SymbolTableEntry::prepareToWatch()
{
if (!isWatchable())
return;
FatEntry* entry = inflate();
if (entry->m_watchpoints)
return;
- entry->m_watchpoints = adoptRef(new VariableWatchpointSet(*symbolTable));
+ entry->m_watchpoints = adoptRef(new WatchpointSet(ClearWatchpoint));
}
void SymbolTableEntry::addWatchpoint(Watchpoint* watchpoint)
fatEntry()->m_watchpoints->add(watchpoint);
}
-void SymbolTableEntry::notifyWriteSlow(VM& vm, JSValue value, const FireDetail& detail)
-{
- VariableWatchpointSet* watchpoints = fatEntry()->m_watchpoints.get();
- if (!watchpoints)
- return;
-
- watchpoints->notifyWrite(vm, value, detail);
-}
-
SymbolTableEntry::FatEntry* SymbolTableEntry::inflateSlow()
{
FatEntry* entry = new FatEntry(m_bits);
SymbolTable::SymbolTable(VM& vm)
: JSCell(vm, vm.symbolTableStructure.get())
, m_usesNonStrictEval(false)
- , m_functionEnteredOnce(ClearWatchpoint)
{
}
SymbolTable::~SymbolTable() { }
+void SymbolTable::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ m_singletonScope.set(vm, this, InferredValue::create(vm));
+}
+
void SymbolTable::visitChildren(JSCell* thisCell, SlotVisitor& visitor)
{
SymbolTable* thisSymbolTable = jsCast<SymbolTable*>(thisCell);
visitor.append(&thisSymbolTable->m_arguments);
-
- if (!thisSymbolTable->m_watchpointCleanup) {
- thisSymbolTable->m_watchpointCleanup =
- std::make_unique<WatchpointCleanup>(thisSymbolTable);
- }
-
- visitor.addUnconditionalFinalizer(thisSymbolTable->m_watchpointCleanup.get());
+ visitor.append(&thisSymbolTable->m_singletonScope);
// Save some memory. This is O(n) to rebuild and we do so on the fly.
ConcurrentJITLocker locker(thisSymbolTable->m_lock);
thisSymbolTable->m_localToEntry = nullptr;
}
-SymbolTable::WatchpointCleanup::WatchpointCleanup(SymbolTable* symbolTable)
- : m_symbolTable(symbolTable)
-{
-}
-
-SymbolTable::WatchpointCleanup::~WatchpointCleanup() { }
-
-void SymbolTable::WatchpointCleanup::finalizeUnconditionally()
-{
- StringFireDetail detail("Symbol table clean-up during GC");
- Map::iterator iter = m_symbolTable->m_map.begin();
- Map::iterator end = m_symbolTable->m_map.end();
- for (; iter != end; ++iter) {
- if (VariableWatchpointSet* set = iter->value.watchpointSet())
- set->finalizeUnconditionally(detail);
- }
-}
-
const SymbolTable::LocalToEntryVec& SymbolTable::localToEntry(const ConcurrentJITLocker&)
{
if (UNLIKELY(!m_localToEntry)) {
#include "ConcurrentJITLock.h"
#include "ConstantMode.h"
+#include "InferredValue.h"
#include "JSObject.h"
#include "ScopedArgumentsTable.h"
#include "TypeLocation.h"
#include "VarOffset.h"
-#include "VariableWatchpointSet.h"
+#include "Watchpoint.h"
#include <memory>
#include <wtf/HashTraits.h>
#include <wtf/text/StringImpl.h>
namespace JSC {
+class SymbolTable;
+
static ALWAYS_INLINE int missingSymbolMarker() { return std::numeric_limits<int>::max(); }
// The bit twiddling in this class assumes that every register index is a
// counted pointer to a shared WatchpointSet. Thus, in-place edits of the
// WatchpointSet will manifest in all copies. Here's a picture:
//
-// SymbolTableEntry --> FatEntry --> VariableWatchpointSet
+// SymbolTableEntry --> FatEntry --> WatchpointSet
//
// If you make a copy of a SymbolTableEntry, you will have:
//
-// original: SymbolTableEntry --> FatEntry --> VariableWatchpointSet
+// original: SymbolTableEntry --> FatEntry --> WatchpointSet
// copy: SymbolTableEntry --> FatEntry -----^
struct SymbolTableEntry {
return bits() & DontEnumFlag;
}
- JSValue inferredValue();
-
void disableWatching()
{
+ if (WatchpointSet* set = watchpointSet())
+ set->invalidate("Disabling watching in symbol table");
if (varOffset().isScope())
pack(varOffset(), false, isReadOnly(), isDontEnum());
}
- void prepareToWatch(SymbolTable*);
+ void prepareToWatch();
void addWatchpoint(Watchpoint*);
- VariableWatchpointSet* watchpointSet()
+ // This watchpoint set is initialized clear, and goes through the following state transitions:
+ //
+ // First write to this var, in any scope that has this symbol table: Clear->IsWatched.
+ //
+ // Second write to this var, in any scope that has this symbol table: IsWatched->IsInvalidated.
+ //
+ // We ensure that we touch the set (i.e. trigger its state transition) after we do the write. This
+ // means that if you're in the compiler thread, and you:
+ //
+ // 1) Observe that the set IsWatched and commit to adding your watchpoint.
+ // 2) Load a value from any scope that has this watchpoint set.
+ //
+ // Then you can be sure that that value is either going to be the correct value for that var forever,
+ // or the watchpoint set will invalidate and you'll get fired.
+ //
+ // It's possible to write a program that first creates multiple scopes with the same var, and then
+ // initializes that var in just one of them. This means that a compilation could constant-fold to one
+ // of the scopes that still has an undefined value for this variable. That's fine, because at that
+ // point any write to any of the instances of that variable would fire the watchpoint.
+ WatchpointSet* watchpointSet()
{
if (!isFat())
return 0;
return fatEntry()->m_watchpoints.get();
}
- ALWAYS_INLINE void notifyWrite(VM& vm, JSValue value, const FireDetail& detail)
- {
- if (LIKELY(!isFat()))
- return;
- notifyWriteSlow(vm, value, detail);
- }
-
private:
static const intptr_t SlimFlag = 0x1;
static const intptr_t ReadOnlyFlag = 0x2;
intptr_t m_bits; // always has FatFlag set and exactly matches what the bits would have been if this wasn't fat.
- RefPtr<VariableWatchpointSet> m_watchpoints;
+ RefPtr<WatchpointSet> m_watchpoints;
};
SymbolTableEntry& copySlow(const SymbolTableEntry&);
SymbolTable* cloneScopePart(VM&);
void prepareForTypeProfiling(const ConcurrentJITLocker&);
+
+ InferredValue* singletonScope() { return m_singletonScope.get(); }
static void visitChildren(JSCell*, SlotVisitor&);
DECLARE_EXPORT_INFO;
private:
- class WatchpointCleanup : public UnconditionalFinalizer {
- public:
- WatchpointCleanup(SymbolTable*);
- virtual ~WatchpointCleanup();
-
- protected:
- virtual void finalizeUnconditionally() override;
-
- private:
- SymbolTable* m_symbolTable;
- };
-
JS_EXPORT_PRIVATE SymbolTable(VM&);
~SymbolTable();
+
+ JS_EXPORT_PRIVATE void finishCreation(VM&);
Map m_map;
ScopeOffset m_maxScopeOffset;
bool m_usesNonStrictEval;
WriteBarrier<ScopedArgumentsTable> m_arguments;
+ WriteBarrier<InferredValue> m_singletonScope;
- std::unique_ptr<WatchpointCleanup> m_watchpointCleanup;
std::unique_ptr<LocalToEntryVec> m_localToEntry;
public:
- InlineWatchpointSet m_functionEnteredOnce;
-
mutable ConcurrentJITLock m_lock;
};
#include "InspectorProtocolObjects.h"
#include "TypeLocation.h"
+#include <wtf/text/StringBuilder.h>
namespace JSC {
unlinkedFunctionCodeBlockStructure.set(*this, UnlinkedFunctionCodeBlock::createStructure(*this, 0, jsNull()));
propertyTableStructure.set(*this, PropertyTable::createStructure(*this, 0, jsNull()));
weakMapDataStructure.set(*this, WeakMapData::createStructure(*this, 0, jsNull()));
+ inferredValueStructure.set(*this, InferredValue::createStructure(*this, 0, jsNull()));
#if ENABLE(PROMISES)
promiseDeferredStructure.set(*this, JSPromiseDeferred::createStructure(*this, 0, jsNull()));
promiseReactionStructure.set(*this, JSPromiseReaction::createStructure(*this, 0, jsNull()));
Strong<Structure> unlinkedFunctionCodeBlockStructure;
Strong<Structure> propertyTableStructure;
Strong<Structure> weakMapDataStructure;
+ Strong<Structure> inferredValueStructure;
#if ENABLE(PROMISES)
Strong<Structure> promiseDeferredStructure;
Strong<Structure> promiseReactionStructure;
--- /dev/null
+function foo(p) {
+ var x;
+
+ noInline(f);
+
+ if (p) {
+ var f = function() { return x; }
+
+ foo(false);
+
+ for (var i = 0; i < 10000; ++i) {
+ var result = f();
+ if (result !== void 0)
+ throw "Error: bad result (1): " + result;
+ }
+
+ x = 43;
+
+ var result = f();
+ if (result != 43)
+ throw "Error: bad result (2): " + result;
+ } else
+ x = 42;
+}
+
+foo(true);
--- /dev/null
+function foo(a) {
+ var x = a + 1;
+ var f = function(a) {
+ return x + a;
+ };
+ noInline(f);
+ for (var i = 0; i < 10000; ++i) {
+ var result = f(i);
+ if (result != a + 1 + i)
+ throw "Error: bad result: " + result;
+ }
+ x = 999;
+ var result = f(1);
+ if (result != 999 + 1)
+ throw "Error: bad result: " + result;
+}
+
+noInline(foo);
+for (var i = 0; i < 3; ++i)
+ foo(42 + i);
--- /dev/null
+function foo(a) {
+ var x = a + 1;
+ return function(a) {
+ return x + a;
+ };
+}
+
+var f = foo(42);
+noInline(f);
+
+for (var i = 0; i < 10000; ++i) {
+ var result = f(i);
+ if (result != 42 + 1 + i)
+ throw "Error: bad result: " + result;
+}
+
+var f = foo(43);
+var result = f(1);
+if (result != 43 + 1 + 1)
+ throw "Error: bad result: " + result;
--- /dev/null
+function foo(a) {
+ var x = a + 1;
+ return function(a) {
+ return x += a;
+ };
+}
+
+var f = foo(42);
+noInline(f);
+
+for (var i = 0; i < 10000; ++i) {
+ var result = f(1);
+ if (result != 42 + 1 + i + 1)
+ throw "Error: bad result: " + result;
+}
+
+var f = foo(43);
+var result = f(1);
+if (result != 43 + 1 + 1)
+ throw "Error: bad result: " + result;