JSC should detect singleton functions
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 13 Apr 2015 22:13:12 +0000 (22:13 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 13 Apr 2015 22:13:12 +0000 (22:13 +0000)
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

83 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/create-lots-of-functions-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/create-lots-of-functions.html [new file with mode: 0644]
LayoutTests/js/regress/no-inline-constructor-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/no-inline-constructor.html [new file with mode: 0644]
LayoutTests/js/regress/script-tests/create-lots-of-functions.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/no-inline-constructor.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/singleton-scope.js [new file with mode: 0644]
LayoutTests/js/regress/singleton-scope-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/singleton-scope.html [new file with mode: 0644]
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecode/BytecodeList.json
Source/JavaScriptCore/bytecode/BytecodeUseDef.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecode/CodeOrigin.cpp
Source/JavaScriptCore/bytecode/CodeOrigin.h
Source/JavaScriptCore/bytecode/Instruction.h
Source/JavaScriptCore/bytecode/VariableWatchpointSet.h [deleted file]
Source/JavaScriptCore/bytecode/VariableWriteFireDetail.cpp [moved from Source/JavaScriptCore/bytecode/VariableWatchpointSet.cpp with 77% similarity]
Source/JavaScriptCore/bytecode/VariableWriteFireDetail.h [moved from Source/JavaScriptCore/bytecode/VariableWatchpointSetInlines.h with 62% similarity]
Source/JavaScriptCore/bytecode/Watchpoint.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.cpp
Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGVarargsForwardingPhase.cpp
Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/interpreter/Interpreter.h
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/jit/JITPropertyAccess.cpp
Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter.asm
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/CommonSlowPaths.h
Source/JavaScriptCore/runtime/Executable.cpp
Source/JavaScriptCore/runtime/Executable.h
Source/JavaScriptCore/runtime/InferredValue.cpp [new file with mode: 0644]
Source/JavaScriptCore/runtime/InferredValue.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/JSEnvironmentRecord.cpp
Source/JavaScriptCore/runtime/JSEnvironmentRecord.h
Source/JavaScriptCore/runtime/JSFunction.cpp
Source/JavaScriptCore/runtime/JSFunction.h
Source/JavaScriptCore/runtime/JSFunctionInlines.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/JSLexicalEnvironment.cpp
Source/JavaScriptCore/runtime/JSScope.h
Source/JavaScriptCore/runtime/JSSegmentedVariableObject.h
Source/JavaScriptCore/runtime/JSSymbolTableObject.h
Source/JavaScriptCore/runtime/PutPropertySlot.h
Source/JavaScriptCore/runtime/SymbolTable.cpp
Source/JavaScriptCore/runtime/SymbolTable.h
Source/JavaScriptCore/runtime/TypeProfiler.cpp
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h
Source/JavaScriptCore/tests/stress/infer-uninitialized-closure-var.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/singleton-scope-then-overwrite.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/singleton-scope-then-realloc-and-overwrite.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/singleton-scope-then-realloc.js [new file with mode: 0644]

index e8ffd83..f13d76f 100644 (file)
@@ -1,3 +1,20 @@
+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.
diff --git a/LayoutTests/js/regress/create-lots-of-functions-expected.txt b/LayoutTests/js/regress/create-lots-of-functions-expected.txt
new file mode 100644 (file)
index 0000000..13d49fa
--- /dev/null
@@ -0,0 +1,10 @@
+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
+
diff --git a/LayoutTests/js/regress/create-lots-of-functions.html b/LayoutTests/js/regress/create-lots-of-functions.html
new file mode 100644 (file)
index 0000000..f113b2b
--- /dev/null
@@ -0,0 +1,12 @@
+<!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>
diff --git a/LayoutTests/js/regress/no-inline-constructor-expected.txt b/LayoutTests/js/regress/no-inline-constructor-expected.txt
new file mode 100644 (file)
index 0000000..4300ba8
--- /dev/null
@@ -0,0 +1,10 @@
+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
+
diff --git a/LayoutTests/js/regress/no-inline-constructor.html b/LayoutTests/js/regress/no-inline-constructor.html
new file mode 100644 (file)
index 0000000..9c386c0
--- /dev/null
@@ -0,0 +1,12 @@
+<!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>
diff --git a/LayoutTests/js/regress/script-tests/create-lots-of-functions.js b/LayoutTests/js/regress/script-tests/create-lots-of-functions.js
new file mode 100644 (file)
index 0000000..0b4b735
--- /dev/null
@@ -0,0 +1,12 @@
+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;
+}
diff --git a/LayoutTests/js/regress/script-tests/no-inline-constructor.js b/LayoutTests/js/regress/script-tests/no-inline-constructor.js
new file mode 100644 (file)
index 0000000..e852eb5
--- /dev/null
@@ -0,0 +1,13 @@
+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;
+}
+
diff --git a/LayoutTests/js/regress/script-tests/singleton-scope.js b/LayoutTests/js/regress/script-tests/singleton-scope.js
new file mode 100644 (file)
index 0000000..2a8015e
--- /dev/null
@@ -0,0 +1,16 @@
+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);
diff --git a/LayoutTests/js/regress/singleton-scope-expected.txt b/LayoutTests/js/regress/singleton-scope-expected.txt
new file mode 100644 (file)
index 0000000..08d9676
--- /dev/null
@@ -0,0 +1,10 @@
+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
+
diff --git a/LayoutTests/js/regress/singleton-scope.html b/LayoutTests/js/regress/singleton-scope.html
new file mode 100644 (file)
index 0000000..bc09f0d
--- /dev/null
@@ -0,0 +1,12 @@
+<!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>
index 7919628..200b799 100644 (file)
@@ -105,7 +105,7 @@ set(JavaScriptCore_SOURCES
     bytecode/UnlinkedCodeBlock.cpp
     bytecode/UnlinkedInstructionStream.cpp
     bytecode/ValueRecovery.cpp
-    bytecode/VariableWatchpointSet.cpp
+    bytecode/VariableWriteFireDetail.cpp
     bytecode/VirtualRegister.cpp
     bytecode/Watchpoint.cpp
 
@@ -452,6 +452,7 @@ set(JavaScriptCore_RUNTIME_SOURCES
     runtime/GetterSetter.cpp
     runtime/Identifier.cpp
     runtime/IndexingType.cpp
+    runtime/InferredValue.cpp
     runtime/InitializeThreading.cpp
     runtime/IntendedStructureChain.cpp
     runtime/InternalFunction.cpp
index cb72ade..04c6834 100644 (file)
@@ -1,3 +1,257 @@
+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.
index 74330e7..85cc03e 100644 (file)
     <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" />
index ba2d136..3d2a9d1 100644 (file)
                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 */,
index 6b24391..2514126 100644 (file)
@@ -6,7 +6,6 @@
             { "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 },
index 85f2915..f207139 100644 (file)
@@ -50,7 +50,6 @@ void computeUsesForBytecodeOffset(
     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:
@@ -278,7 +277,6 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset,
     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);
index 06e49fe..d511663 100644 (file)
@@ -758,10 +758,6 @@ void CodeBlock::dumpBytecode(
             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;
@@ -1957,7 +1953,7 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
             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;
         }
 
@@ -1985,7 +1981,6 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
             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;
         }
 
@@ -2001,7 +1996,7 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
                     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;
@@ -2546,12 +2541,15 @@ void CodeBlock::finalizeUnconditionally()
                     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:
@@ -3815,6 +3813,18 @@ String CodeBlock::nameForRegister(VirtualRegister virtualRegister)
     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.
index e3b13dc..5bae3df 100644 (file)
@@ -386,17 +386,7 @@ public:
 
     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);
index 6e1dd7d..e848925 100644 (file)
@@ -141,6 +141,18 @@ void CodeOrigin::dumpInContext(PrintStream& out, DumpContext*) const
     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));
index 85ac926..d1879a3 100644 (file)
@@ -28,7 +28,6 @@
 
 #include "CodeBlockHash.h"
 #include "CodeSpecializationKind.h"
-#include "JSFunction.h"
 #include "ValueRecovery.h"
 #include "WriteBarrier.h"
 #include <wtf/BitVector.h>
@@ -207,17 +206,8 @@ struct InlineCallFrame {
     
     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;
index b973eb4..b42e429 100644 (file)
@@ -32,6 +32,7 @@
 #include "BasicBlockLocation.h"
 #include "MacroAssembler.h"
 #include "Opcode.h"
+#include "SymbolTable.h"
 #include "TypeLocation.h"
 #include "PropertySlot.h"
 #include "SpecialPointer.h"
@@ -46,7 +47,7 @@ namespace JSC {
 class ArrayAllocationProfile;
 class ArrayProfile;
 class ObjectAllocationProfile;
-class VariableWatchpointSet;
+class WatchpointSet;
 struct LLIntCallLinkInfo;
 struct ValueProfile;
 
@@ -106,6 +107,7 @@ struct Instruction {
         Opcode opcode;
         int operand;
         WriteBarrierBase<Structure> structure;
+        WriteBarrierBase<SymbolTable> symbolTable;
         WriteBarrierBase<StructureChain> structureChain;
         WriteBarrierBase<JSCell> jsCell;
         WriteBarrier<Unknown>* variablePointer;
@@ -117,8 +119,7 @@ struct Instruction {
         ArrayProfile* arrayProfile;
         ArrayAllocationProfile* arrayAllocationProfile;
         ObjectAllocationProfile* objectAllocationProfile;
-        VariableWatchpointSet* watchpointSet;
-        WriteBarrierBase<JSLexicalEnvironment> lexicalEnvironment;
+        WatchpointSet* watchpointSet;
         void* pointer;
         bool* predicatePointer;
         ToThisStatus toThisStatus;
diff --git a/Source/JavaScriptCore/bytecode/VariableWatchpointSet.h b/Source/JavaScriptCore/bytecode/VariableWatchpointSet.h
deleted file mode 100644 (file)
index dc7c890..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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
-
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -24,7 +24,7 @@
  */
 
 #include "config.h"
-#include "VariableWatchpointSet.h"
+#include "VariableWriteFireDetail.h"
 
 #include "JSCInlines.h"
 
@@ -35,14 +35,9 @@ void VariableWriteFireDetail::dump(PrintStream& out) const
     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
@@ -1,5 +1,5 @@
 /*
- * 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
index 4956e5f..8857317 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -34,6 +34,8 @@
 namespace JSC {
 
 class FireDetail {
+    void* operator new(size_t) = delete;
+    
 public:
     FireDetail()
     {
@@ -87,6 +89,12 @@ public:
     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
@@ -125,20 +133,24 @@ public:
     // 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);
     }
@@ -151,13 +163,23 @@ public:
             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; }
     
@@ -209,18 +231,34 @@ public:
         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.
@@ -253,6 +291,14 @@ public:
         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)
@@ -261,7 +307,11 @@ public:
             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);
index 591998a..6e198ca 100644 (file)
@@ -466,9 +466,6 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke
         }
     }
     
-    if (m_symbolTable->scopeSize())
-        emitOpcode(op_touch_entry);
-
     if (isConstructor()) {
         if (constructorKind() == ConstructorKind::Derived) {
             m_newTargetRegister = addVar();
index 8d15305..c347db6 100644 (file)
@@ -1342,6 +1342,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     }
 
     case CreateThis: {
+        // FIXME: We can fold this to NewObject if the incoming callee is a constant.
         forNode(node).setType(SpecFinalObject);
         break;
     }
@@ -1401,6 +1402,15 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         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;
         
index 651e95b..d973885 100644 (file)
@@ -215,8 +215,6 @@ private:
         bool isDirect);
     void emitChecks(const ConstantStructureCheckVector&);
 
-    Node* getScope(VirtualRegister scopeChain, unsigned skipCount);
-    
     void prepareToParseBlock();
     void clearCaches();
 
@@ -279,8 +277,20 @@ private:
                 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));
     }
@@ -2534,14 +2544,6 @@ void ByteCodeParser::clearCaches()
     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;
@@ -2611,11 +2613,6 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             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) {
@@ -3393,17 +3390,28 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             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:
@@ -3455,19 +3463,67 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             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:
@@ -3484,6 +3540,10 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 // 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;
@@ -3514,7 +3574,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 uid = nullptr;
             
             Structure* structure = nullptr;
-            VariableWatchpointSet* watchpoints = nullptr;
+            WatchpointSet* watchpoints = nullptr;
             uintptr_t operand;
             {
                 ConcurrentJITLocker locker(m_inlineStackTop->m_profiledBlock->m_lock);
@@ -3557,8 +3617,10 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 }
                 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;
@@ -3569,10 +3631,12 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 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:
index 8ef4dcf..32220dd 100644 (file)
@@ -96,7 +96,6 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
     
     switch (opcodeID) {
     case op_enter:
-    case op_touch_entry:
     case op_to_this:
     case op_check_tdz:
     case op_create_this:
index 831d8b7..a726bd7 100644 (file)
@@ -311,10 +311,14 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         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:
@@ -858,11 +862,17 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     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);
index 3155da4..7f5b01c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -44,6 +44,13 @@ void ArrayBufferViewWatchpointAdaptor::add(
     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() { }
 
@@ -57,6 +64,11 @@ void DesiredWatchpoints::addLazily(InlineWatchpointSet& set)
     m_inlineSets.addLazily(&set);
 }
 
+void DesiredWatchpoints::addLazily(InferredValue* inferredValue)
+{
+    m_inferredValues.addLazily(inferredValue);
+}
+
 void DesiredWatchpoints::addLazily(JSArrayBufferView* view)
 {
     m_bufferViews.addLazily(view);
@@ -74,6 +86,7 @@ void DesiredWatchpoints::reallyAdd(CodeBlock* codeBlock, CommonData& commonData)
 {
     m_sets.reallyAdd(codeBlock, commonData);
     m_inlineSets.reallyAdd(codeBlock, commonData);
+    m_inferredValues.reallyAdd(codeBlock, commonData);
     m_bufferViews.reallyAdd(codeBlock, commonData);
 }
 
@@ -81,6 +94,7 @@ bool DesiredWatchpoints::areStillValid() const
 {
     return m_sets.areStillValid()
         && m_inlineSets.areStillValid()
+        && m_inferredValues.areStillValid()
         && m_bufferViews.areStillValid();
 }
 
index b330f0a..ce89a04 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -30,6 +30,7 @@
 
 #include "CodeOrigin.h"
 #include "DFGCommonData.h"
+#include "InferredValue.h"
 #include "JSArrayBufferView.h"
 #include "Watchpoint.h"
 #include <wtf/HashMap.h>
@@ -50,6 +51,14 @@ struct GenericSetAdaptor {
     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)
@@ -119,6 +128,7 @@ public:
     
     void addLazily(WatchpointSet*);
     void addLazily(InlineWatchpointSet&);
+    void addLazily(InferredValue*);
     void addLazily(JSArrayBufferView*);
     
     bool consider(Structure*);
@@ -135,6 +145,10 @@ public:
     {
         return m_inlineSets.isWatched(&set);
     }
+    bool isWatched(InferredValue* inferredValue)
+    {
+        return m_inferredValues.isWatched(inferredValue);
+    }
     bool isWatched(JSArrayBufferView* view)
     {
         return m_bufferViews.isWatched(view);
@@ -143,6 +157,7 @@ public:
 private:
     GenericDesiredWatchpoints<WatchpointSet> m_sets;
     GenericDesiredWatchpoints<InlineWatchpointSet> m_inlineSets;
+    GenericDesiredWatchpoints<InferredValue, InferredValueAdaptor> m_inferredValues;
     GenericDesiredWatchpoints<JSArrayBufferView, ArrayBufferViewWatchpointAdaptor> m_bufferViews;
 };
 
index cf9df62..95a031c 100644 (file)
@@ -311,8 +311,8 @@ void Graph::dump(PrintStream& out, const char* prefix, Node* node, DumpContext*
         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())
@@ -1026,6 +1026,8 @@ JSValue Graph::tryGetConstantProperty(const AbstractValue& base, PropertyOffset
 
 JSValue Graph::tryGetConstantClosureVar(JSValue base, ScopeOffset offset)
 {
+    // This has an awesome concurrency story. See comment for GetGlobalVar in ByteCodeParser.
+    
     if (!base)
         return JSValue();
     
@@ -1034,24 +1036,28 @@ JSValue Graph::tryGetConstantClosureVar(JSValue base, ScopeOffset offset)
         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;
index c75af88..cc4964f 100644 (file)
@@ -1219,14 +1219,14 @@ struct Node {
         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()
index ff16903..b7a6049 100644 (file)
@@ -1077,13 +1077,12 @@ char* JIT_OPERATION operationSwitchString(ExecState* exec, size_t tableIndex, JS
     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)
index 1d5a792..76fffaf 100644 (file)
@@ -127,7 +127,7 @@ JSCell* JIT_OPERATION operationMakeRope2(ExecState*, JSString*, JSString*);
 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);
index 1611765..a095442 100644 (file)
@@ -4367,8 +4367,13 @@ void SpeculativeJIT::compileNewFunction(Node* node)
     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);
 }
 
@@ -4436,19 +4441,31 @@ void SpeculativeJIT::compileForwardVarargs(Node* 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,
@@ -4714,6 +4731,21 @@ void SpeculativeJIT::compileCreateClonedArguments(Node* node)
     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();
index 605f2bc..70d1d95 100644 (file)
@@ -1200,6 +1200,12 @@ public:
         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)
     {
@@ -1460,12 +1466,6 @@ public:
         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);
@@ -1776,12 +1776,6 @@ public:
         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);
@@ -2213,6 +2207,7 @@ public:
     void compilePutToArguments(Node*);
     void compileCreateScopedArguments(Node*);
     void compileCreateClonedArguments(Node*);
+    void compileNotifyWrite(Node*);
     bool compileRegExpExec(Node*);
     
     JITCompiler::Jump branchIsCell(JSValueRegs);
index 2ac4e14..26e52d7 100644 (file)
@@ -3994,32 +3994,7 @@ void SpeculativeJIT::compile(Node* node)
     }
 
     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;
     }
 
index e44d14a..2b3d1db 100644 (file)
@@ -4019,26 +4019,7 @@ void SpeculativeJIT::compile(Node* node)
     }
 
     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;
     }
 
index 2e742ee..e27f4d5 100644 (file)
@@ -32,6 +32,7 @@
 #include "DFGClobberize.h"
 #include "DFGGraph.h"
 #include "DFGPhase.h"
+#include "JSCInlines.h"
 
 namespace JSC { namespace DFG {
 
index 7a413fc..4eeab5a 100644 (file)
@@ -105,7 +105,7 @@ namespace JSC { namespace FTL {
     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)) \
index e39d0df..28e51d1 100644 (file)
@@ -2877,6 +2877,14 @@ private:
         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"));
         
@@ -2912,9 +2920,16 @@ private:
     
     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);
     }
     
@@ -3783,29 +3798,19 @@ private:
     
     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);
index 3092fcb..17dd3b4 100644 (file)
@@ -89,6 +89,47 @@ using namespace std;
 
 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())
index f02e143..99369cd 100644 (file)
@@ -33,7 +33,6 @@
 #include "ArgList.h"
 #include "JSCJSValue.h"
 #include "JSCell.h"
-#include "JSFunction.h"
 #include "JSObject.h"
 #include "JSStack.h"
 #include "LLIntData.h"
@@ -51,6 +50,7 @@ namespace JSC {
     class ExecutableBase;
     class FunctionExecutable;
     class VM;
+    class JSFunction;
     class JSGlobalObject;
     class LLIntOffsetsExtractor;
     class ProgramExecutable;
@@ -90,45 +90,8 @@ namespace JSC {
         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:
index 42a82d4..8cd9fb1 100644 (file)
@@ -105,6 +105,14 @@ void JIT::emitEnterOptimizationCheck()
 }
 #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)
@@ -189,7 +197,6 @@ void JIT::privateCompileMainPass()
         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)
index ee4855d..01a4129 100644 (file)
@@ -457,7 +457,6 @@ namespace JSC {
         
         void assertStackPointerOffset();
 
-        void emit_op_touch_entry(Instruction*);
         void emit_op_add(Instruction*);
         void emit_op_bitand(Instruction*);
         void emit_op_bitor(Instruction*);
@@ -640,13 +639,9 @@ namespace JSC {
         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);
 
index d3d580e..9834806 100644 (file)
@@ -879,15 +879,6 @@ void JIT::emitSlow_op_to_number(Instruction* currentInstruction, Vector<SlowCase
 
 #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: 
index 5e90d48..be34180 100644 (file)
@@ -952,6 +952,14 @@ EncodedJSValue JIT_OPERATION operationNewFunction(ExecState* exec, JSScope* scop
     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();
@@ -1722,8 +1730,8 @@ void JIT_OPERATION operationPutToScope(ExecState* exec, Instruction* bytecodePC)
     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)) {
index 4decef4..c3fad5d 100644 (file)
@@ -37,7 +37,6 @@
 #include "PutKind.h"
 #include "SpillRegistersMode.h"
 #include "StructureStubInfo.h"
-#include "VariableWatchpointSet.h"
 
 
 namespace JSC {
@@ -85,7 +84,7 @@ extern "C" {
     Symtab: SymbolTable*
     V: void
     Vm: VM*
-    Vws: VariableWatchpointSet*
+    Ws: WatchpointSet*
     Z: int32_t
 */
 
@@ -191,7 +190,7 @@ typedef void JIT_OPERATION (*V_JITOperation_EOZJ)(ExecState*, JSObject*, 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);
@@ -278,6 +277,7 @@ EncodedJSValue JIT_OPERATION operationNewArrayWithProfile(ExecState*, ArrayAlloc
 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;
index d7ee66c..03b59f4 100644 (file)
@@ -728,29 +728,18 @@ void JIT::emitPutGlobalProperty(uintptr_t* operandSlot, int value)
     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)));
 }
 
index 567484d..251a689 100644 (file)
@@ -750,36 +750,19 @@ void JIT::emitPutGlobalProperty(uintptr_t* operandSlot, int value)
     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));
 }
@@ -826,7 +809,7 @@ void JIT::emitSlow_op_put_to_scope(Instruction* currentInstruction, Vector<SlowC
         linkCount++;
     if ((resolveType == GlobalVar || resolveType == GlobalVarWithVarInjectionChecks || resolveType == LocalClosureVar)
         && currentInstruction[5].u.watchpointSet->state() != IsInvalidated)
-        linkCount += 2;
+        linkCount++;
     if (!linkCount)
         return;
     while (linkCount--)
index 27b9869..069755b 100644 (file)
@@ -1402,8 +1402,12 @@ LLINT_SLOW_PATH_DECL(slow_path_put_to_scope)
     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();
     }
 
index fda91ed..384f0c7 100644 (file)
@@ -519,6 +519,10 @@ macro skipIfIsRememberedOrInEden(cell, scratch1, scratch2, continuation)
     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
@@ -920,12 +924,6 @@ end
 
 
 # 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)
index ca91e8d..3636760 100644 (file)
@@ -805,14 +805,6 @@ _llint_op_mov:
     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
@@ -2204,7 +2196,7 @@ macro putGlobalVar()
     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]
@@ -2223,7 +2215,7 @@ macro putLocalClosureVar()
     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]
index de57062..f92816b 100644 (file)
@@ -690,13 +690,6 @@ _llint_op_mov:
     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)
@@ -2069,8 +2062,8 @@ macro putGlobalVar()
     loadisFromInstruction(3, t0)
     loadConstantOrVariable(t0, t1)
     loadpFromInstruction(5, t2)
-    notifyWrite(t2, t1, t0, .pDynamic)
     loadpFromInstruction(6, t0)
+    notifyWrite(t2, .pDynamic)
     storeq t1, [t0]
 end
 
@@ -2086,7 +2079,7 @@ macro putLocalClosureVar()
     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]
index 653ebdb..ea376e9 100644 (file)
@@ -55,7 +55,6 @@
 #include "ScopedArguments.h"
 #include "StructureRareDataInlines.h"
 #include "TypeProfilerLog.h"
-#include "VariableWatchpointSetInlines.h"
 #include <wtf/StringPrintStream.h>
 
 namespace JSC {
@@ -206,13 +205,6 @@ SLOW_PATH_DECL(slow_path_construct_arityCheck)
     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();
index f60bbc8..0f67e63 100644 (file)
@@ -181,7 +181,6 @@ SLOW_PATH_DECL(name) WTF_INTERNAL
     
 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);
index 343e8bd..394db0e 100644 (file)
@@ -414,6 +414,12 @@ FunctionExecutable::FunctionExecutable(VM& vm, const SourceCode& source,
     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();
@@ -567,6 +573,7 @@ void FunctionExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor)
     if (thisObject->m_codeBlockForConstruct)
         thisObject->m_codeBlockForConstruct->visitAggregate(visitor);
     visitor.append(&thisObject->m_unlinkedExecutable);
+    visitor.append(&thisObject->m_singletonFunction);
 }
 
 SymbolTable* FunctionExecutable::symbolTable(CodeSpecializationKind kind)
index ba5f155..52852fb 100644 (file)
@@ -33,8 +33,7 @@
 #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"
@@ -653,11 +652,15 @@ public:
     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()
     {
@@ -671,12 +674,13 @@ private:
     }
 
     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)
diff --git a/Source/JavaScriptCore/runtime/InferredValue.cpp b/Source/JavaScriptCore/runtime/InferredValue.cpp
new file mode 100644 (file)
index 0000000..73c6bc7
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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
+
diff --git a/Source/JavaScriptCore/runtime/InferredValue.h b/Source/JavaScriptCore/runtime/InferredValue.h
new file mode 100644 (file)
index 0000000..852244c
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * 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
+
index 152b044..f1d7695 100644 (file)
@@ -40,7 +40,7 @@ void JSEnvironmentRecord::visitChildren(JSCell* cell, SlotVisitor& visitor)
     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
index 4b2c0df..35807ba 100644 (file)
@@ -54,7 +54,7 @@ public:
     
     bool isValid(ScopeOffset offset)
     {
-        return !!offset && offset.offset() < m_symbolTable->scopeSize();
+        return !!offset && offset.offset() < symbolTable()->scopeSize();
     }
     
     WriteBarrierBase<Unknown>& variableAt(ScopeOffset offset)
@@ -103,7 +103,7 @@ protected:
     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();
         }
index 7c3ac2b..40add08 100644 (file)
@@ -59,6 +59,13 @@ bool JSFunction::isHostFunctionNonInline() const
     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;
index 58aa9bd..ef47776 100644 (file)
@@ -60,14 +60,10 @@ public:
     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*);
 
@@ -148,6 +144,14 @@ protected:
     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);
index 37b281a..fcf3897 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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)
index 17949a1..ff21cb9 100644 (file)
 #include "Symbol.h"
 #include "SymbolConstructor.h"
 #include "SymbolPrototype.h"
-#include "VariableWatchpointSetInlines.h"
+#include "VariableWriteFireDetail.h"
 #include "WeakGCMapInlines.h"
 #include "WeakMapConstructor.h"
 #include "WeakMapPrototype.h"
@@ -489,7 +489,7 @@ JSGlobalObject::NewGlobalVar JSGlobalObject::addGlobalVar(const Identifier& iden
     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);
@@ -510,7 +510,7 @@ void JSGlobalObject::addFunction(ExecState* exec, const Identifier& propertyName
     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)
index 32c291c..2ca5899 100644 (file)
@@ -335,7 +335,7 @@ protected:
 
     struct NewGlobalVar {
         ScopeOffset offset;
-        VariableWatchpointSet* set;
+        WatchpointSet* set;
     };
     NewGlobalVar addGlobalVar(const Identifier&, ConstantMode);
 
index ea9947f..183f484 100644 (file)
@@ -77,6 +77,7 @@ inline bool JSLexicalEnvironment::symbolTablePut(ExecState* exec, PropertyName p
     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());
@@ -92,11 +93,12 @@ inline bool JSLexicalEnvironment::symbolTablePut(ExecState* exec, PropertyName p
         // 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;
 }
 
index df581a0..5d0eb4b 100644 (file)
@@ -31,7 +31,7 @@
 namespace JSC {
 
 class ScopeChainIterator;
-class VariableWatchpointSet;
+class WatchpointSet;
 
 enum ResolveMode {
     ThrowIfNotFound,
@@ -102,7 +102,7 @@ inline bool needsVarInjectionChecks(ResolveType type)
 }
 
 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)
@@ -116,7 +116,7 @@ struct ResolveOp {
     size_t depth;
     Structure* structure;
     JSLexicalEnvironment* lexicalEnvironment;
-    VariableWatchpointSet* watchpointSet;
+    WatchpointSet* watchpointSet;
     uintptr_t operand;
 };
 
index 0f03745..feaf0bb 100644 (file)
@@ -57,6 +57,10 @@ class JSSegmentedVariableObject : public JSSymbolTableObject {
 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
@@ -86,7 +90,7 @@ protected:
     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;
index d92509a..9fe8384 100644 (file)
@@ -32,7 +32,7 @@
 #include "JSScope.h"
 #include "PropertyDescriptor.h"
 #include "SymbolTable.h"
-#include "VariableWatchpointSetInlines.h"
+#include "VariableWriteFireDetail.h"
 
 namespace JSC {
 
@@ -60,11 +60,19 @@ protected:
         : 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;
 };
 
@@ -125,6 +133,7 @@ inline bool symbolTablePut(
     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.
@@ -141,17 +150,15 @@ inline bool symbolTablePut(
                 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;
 }
 
@@ -163,6 +170,7 @@ inline bool symbolTablePutWithAttributes(
     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(object));
 
     WriteBarrierBase<Unknown>* reg;
+    WatchpointSet* set;
     {
         SymbolTable& symbolTable = *object->symbolTable();
         ConcurrentJITLocker locker(symbolTable.m_lock);
@@ -171,12 +179,13 @@ inline bool symbolTablePutWithAttributes(
             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;
 }
 
index fdf87d5..fd32d95 100644 (file)
@@ -28,6 +28,7 @@
 #define PutPropertySlot_h
 
 #include "JSCJSValue.h"
+#include "PropertyOffset.h"
 
 #include <wtf/Assertions.h>
 
index 3a7e7e8..5932e61 100644 (file)
@@ -33,7 +33,6 @@
 #include "JSCInlines.h"
 #include "SlotVisitorInlines.h"
 #include "TypeProfiler.h"
-#include "VariableWatchpointSetInlines.h"
 
 namespace JSC {
 
@@ -60,21 +59,14 @@ void SymbolTableEntry::freeFatEntrySlow()
     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)
@@ -82,15 +74,6 @@ 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);
@@ -101,48 +84,29 @@ SymbolTableEntry::FatEntry* SymbolTableEntry::inflateSlow()
 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)) {
index 37cbb24..c592454 100644 (file)
 
 #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
@@ -63,11 +66,11 @@ static ALWAYS_INLINE int missingSymbolMarker() { return std::numeric_limits<int>
 // 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 {
@@ -260,32 +263,44 @@ public:
         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;
@@ -308,7 +323,7 @@ private:
         
         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&);
@@ -637,26 +652,18 @@ public:
     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;
@@ -671,13 +678,11 @@ private:
     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;
 };
 
index e5bac86..6ce607c 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "InspectorProtocolObjects.h"
 #include "TypeLocation.h"
+#include <wtf/text/StringBuilder.h>
 
 namespace JSC {
 
index 61e4582..1322df2 100644 (file)
@@ -237,6 +237,7 @@ VM::VM(VMType vmType, HeapType heapType)
     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()));
index a46a376..e2fa10a 100644 (file)
@@ -270,6 +270,7 @@ public:
     Strong<Structure> unlinkedFunctionCodeBlockStructure;
     Strong<Structure> propertyTableStructure;
     Strong<Structure> weakMapDataStructure;
+    Strong<Structure> inferredValueStructure;
 #if ENABLE(PROMISES)
     Strong<Structure> promiseDeferredStructure;
     Strong<Structure> promiseReactionStructure;
diff --git a/Source/JavaScriptCore/tests/stress/infer-uninitialized-closure-var.js b/Source/JavaScriptCore/tests/stress/infer-uninitialized-closure-var.js
new file mode 100644 (file)
index 0000000..aa64af4
--- /dev/null
@@ -0,0 +1,26 @@
+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);
diff --git a/Source/JavaScriptCore/tests/stress/singleton-scope-then-overwrite.js b/Source/JavaScriptCore/tests/stress/singleton-scope-then-overwrite.js
new file mode 100644 (file)
index 0000000..7f87304
--- /dev/null
@@ -0,0 +1,20 @@
+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);
diff --git a/Source/JavaScriptCore/tests/stress/singleton-scope-then-realloc-and-overwrite.js b/Source/JavaScriptCore/tests/stress/singleton-scope-then-realloc-and-overwrite.js
new file mode 100644 (file)
index 0000000..d15c579
--- /dev/null
@@ -0,0 +1,20 @@
+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;
diff --git a/Source/JavaScriptCore/tests/stress/singleton-scope-then-realloc.js b/Source/JavaScriptCore/tests/stress/singleton-scope-then-realloc.js
new file mode 100644 (file)
index 0000000..a6e1dcc
--- /dev/null
@@ -0,0 +1,20 @@
+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;