Commit 8646834a authored by fpizlo@apple.com's avatar fpizlo@apple.com

Restructure global variable constant inference so that it could work for any...

Restructure global variable constant inference so that it could work for any kind of symbol table variable
https://bugs.webkit.org/show_bug.cgi?id=124760

Reviewed by Oliver Hunt.
        
This changes the way global variable constant inference works so that it can be reused
for closure variable constant inference. Some of the premises that originally motivated
this patch are somewhat wrong, but it led to some simplifications anyway and I suspect
that we'll be able to fix those premises in the future. The main point of this patch is
to make it easy to reuse global variable constant inference for closure variable
constant inference, and this will be possible provided we can also either (a) infer
one-shot closures (easy) or (b) infer closure variables that are always assigned prior
to first use.
        
One of the things that this patch is meant to enable is constant inference for closure
variables that may be part of a multi-shot closure. Closure variables may be
instantiated multiple times, like:
        
    function foo() {
        var WIDTH = 45;
        function bar() {
            ... use WIDTH ...
        }
        ...
    }
        
Even if foo() is called many times and WIDTH is assigned to multiple times, that
doesn't change the fact that it's a constant. The goal of closure variable constant
inference is to catch any case where a closure variable has been assigned at least once
and its value has never changed. This patch doesn't implement that, but it does change
global variable constant inference to have most of the powers needed to do that. Note
that most likely we will use this functionality only to implement constant inference
for one-shot closures, but the resulting machinery is still simpler than what we had
before.
        
This involves three changes:
        
    - The watchpoint object now contains the inferred value. This involves creating a
      new kind of watchpoint set, the VariableWatchpointSet. We will reuse this object
      for closure variables.
        
    - Writing to a variable that is watchpointed still involves these three states that
      we proceed through monotonically (Uninitialized->Initialized->Invalidated) but
      now, the Initialized->Invalidated state transition only happens if we change the
      variable's value, rather than store to the variable. Repeatedly storing the same
      value won't change the variable's state.
        
    - On 64-bit systems (the only systems on which we do concurrent JIT), you no longer
      need fancy fencing to get a consistent view of the watchpoint in the JIT. The
      state of the VariableWatchpointSet for the purposes of constant folding is
      entirely encapsulated in the VariableWatchpointSet::m_inferredValue. If that is
      JSValue() then you cannot fold (either because the set is uninitialized or
      because it's invalidated - doesn't matter which); on the other hand if the value
      is anything other than JSValue() then you can fold, and that's the value you fold
      to. Simple!
        
This also changes the way that DFG IR deals with variable watchpoints. It's now
oblivious to global variables. You install a watchpoint using VariableWatchpoint and
you notify write using NotifyWrite. Easy!
        
Note that this will requires some more tweaks because of the fact that op_enter will
store Undefined into every captured variable. Hence it won't even work for one-shot
closures. One-shot closures are easily fixed by introducing another state (so we'll
have Uninitialized->Undefined->Initialized->Invalidated). Multi-shot closures will
require static analysis. One-shot closures are clearly a higher priority.

* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/Instruction.h:
* bytecode/VariableWatchpointSet.h: Added.
(JSC::VariableWatchpointSet::VariableWatchpointSet):
(JSC::VariableWatchpointSet::~VariableWatchpointSet):
(JSC::VariableWatchpointSet::inferredValue):
(JSC::VariableWatchpointSet::notifyWrite):
(JSC::VariableWatchpointSet::invalidate):
(JSC::VariableWatchpointSet::finalizeUnconditionally):
(JSC::VariableWatchpointSet::addressOfInferredValue):
* bytecode/Watchpoint.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::performNodeCSE):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasRegisterPointer):
(JSC::DFG::Node::hasVariableWatchpointSet):
(JSC::DFG::Node::variableWatchpointSet):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileArithMod):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGWatchpointCollectionPhase.cpp:
(JSC::DFG::WatchpointCollectionPhase::handle):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::compileNotifyWrite):
* jit/JIT.h:
* jit/JITOperations.h:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emitNotifyWrite):
(JSC::JIT::emitPutGlobalVar):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emitNotifyWrite):
(JSC::JIT::emitPutGlobalVar):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::addGlobalVar):
(JSC::JSGlobalObject::addFunction):
* runtime/JSGlobalObject.h:
* runtime/JSScope.h:
(JSC::ResolveOp::ResolveOp):
* runtime/JSSymbolTableObject.h:
(JSC::symbolTablePut):
(JSC::symbolTablePutWithAttributes):
* runtime/SymbolTable.cpp:
(JSC::SymbolTableEntry::inferredValue):
(JSC::SymbolTableEntry::prepareToWatch):
(JSC::SymbolTableEntry::addWatchpoint):
(JSC::SymbolTableEntry::notifyWriteSlow):
(JSC::SymbolTable::visitChildren):
(JSC::SymbolTable::WatchpointCleanup::WatchpointCleanup):
(JSC::SymbolTable::WatchpointCleanup::~WatchpointCleanup):
(JSC::SymbolTable::WatchpointCleanup::finalizeUnconditionally):
* runtime/SymbolTable.h:
(JSC::SymbolTableEntry::watchpointSet):
(JSC::SymbolTableEntry::notifyWrite):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@159798 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent a1f8f70d
2013-11-26 Filip Pizlo <fpizlo@apple.com>
Restructure global variable constant inference so that it could work for any kind of symbol table variable
https://bugs.webkit.org/show_bug.cgi?id=124760
Reviewed by Oliver Hunt.
This changes the way global variable constant inference works so that it can be reused
for closure variable constant inference. Some of the premises that originally motivated
this patch are somewhat wrong, but it led to some simplifications anyway and I suspect
that we'll be able to fix those premises in the future. The main point of this patch is
to make it easy to reuse global variable constant inference for closure variable
constant inference, and this will be possible provided we can also either (a) infer
one-shot closures (easy) or (b) infer closure variables that are always assigned prior
to first use.
One of the things that this patch is meant to enable is constant inference for closure
variables that may be part of a multi-shot closure. Closure variables may be
instantiated multiple times, like:
function foo() {
var WIDTH = 45;
function bar() {
... use WIDTH ...
}
...
}
Even if foo() is called many times and WIDTH is assigned to multiple times, that
doesn't change the fact that it's a constant. The goal of closure variable constant
inference is to catch any case where a closure variable has been assigned at least once
and its value has never changed. This patch doesn't implement that, but it does change
global variable constant inference to have most of the powers needed to do that. Note
that most likely we will use this functionality only to implement constant inference
for one-shot closures, but the resulting machinery is still simpler than what we had
before.
This involves three changes:
- The watchpoint object now contains the inferred value. This involves creating a
new kind of watchpoint set, the VariableWatchpointSet. We will reuse this object
for closure variables.
- Writing to a variable that is watchpointed still involves these three states that
we proceed through monotonically (Uninitialized->Initialized->Invalidated) but
now, the Initialized->Invalidated state transition only happens if we change the
variable's value, rather than store to the variable. Repeatedly storing the same
value won't change the variable's state.
- On 64-bit systems (the only systems on which we do concurrent JIT), you no longer
need fancy fencing to get a consistent view of the watchpoint in the JIT. The
state of the VariableWatchpointSet for the purposes of constant folding is
entirely encapsulated in the VariableWatchpointSet::m_inferredValue. If that is
JSValue() then you cannot fold (either because the set is uninitialized or
because it's invalidated - doesn't matter which); on the other hand if the value
is anything other than JSValue() then you can fold, and that's the value you fold
to. Simple!
This also changes the way that DFG IR deals with variable watchpoints. It's now
oblivious to global variables. You install a watchpoint using VariableWatchpoint and
you notify write using NotifyWrite. Easy!
Note that this will requires some more tweaks because of the fact that op_enter will
store Undefined into every captured variable. Hence it won't even work for one-shot
closures. One-shot closures are easily fixed by introducing another state (so we'll
have Uninitialized->Undefined->Initialized->Invalidated). Multi-shot closures will
require static analysis. One-shot closures are clearly a higher priority.
* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/Instruction.h:
* bytecode/VariableWatchpointSet.h: Added.
(JSC::VariableWatchpointSet::VariableWatchpointSet):
(JSC::VariableWatchpointSet::~VariableWatchpointSet):
(JSC::VariableWatchpointSet::inferredValue):
(JSC::VariableWatchpointSet::notifyWrite):
(JSC::VariableWatchpointSet::invalidate):
(JSC::VariableWatchpointSet::finalizeUnconditionally):
(JSC::VariableWatchpointSet::addressOfInferredValue):
* bytecode/Watchpoint.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::performNodeCSE):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasRegisterPointer):
(JSC::DFG::Node::hasVariableWatchpointSet):
(JSC::DFG::Node::variableWatchpointSet):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileArithMod):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGWatchpointCollectionPhase.cpp:
(JSC::DFG::WatchpointCollectionPhase::handle):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::compileNotifyWrite):
* jit/JIT.h:
* jit/JITOperations.h:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emitNotifyWrite):
(JSC::JIT::emitPutGlobalVar):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emitNotifyWrite):
(JSC::JIT::emitPutGlobalVar):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::addGlobalVar):
(JSC::JSGlobalObject::addFunction):
* runtime/JSGlobalObject.h:
* runtime/JSScope.h:
(JSC::ResolveOp::ResolveOp):
* runtime/JSSymbolTableObject.h:
(JSC::symbolTablePut):
(JSC::symbolTablePutWithAttributes):
* runtime/SymbolTable.cpp:
(JSC::SymbolTableEntry::inferredValue):
(JSC::SymbolTableEntry::prepareToWatch):
(JSC::SymbolTableEntry::addWatchpoint):
(JSC::SymbolTableEntry::notifyWriteSlow):
(JSC::SymbolTable::visitChildren):
(JSC::SymbolTable::WatchpointCleanup::WatchpointCleanup):
(JSC::SymbolTable::WatchpointCleanup::~WatchpointCleanup):
(JSC::SymbolTable::WatchpointCleanup::finalizeUnconditionally):
* runtime/SymbolTable.h:
(JSC::SymbolTableEntry::watchpointSet):
(JSC::SymbolTableEntry::notifyWrite):
2013-11-24 Filip Pizlo <fpizlo@apple.com>
Create a new SymbolTable every time code is loaded so that the watchpoints don't get reused
......
......@@ -181,6 +181,7 @@ javascriptcore_sources += \
Source/JavaScriptCore/bytecode/ValueProfile.h \
Source/JavaScriptCore/bytecode/ValueRecovery.cpp \
Source/JavaScriptCore/bytecode/ValueRecovery.h \
Source/JavaScriptCore/bytecode/VariableWatchpointSet.h \
Source/JavaScriptCore/bytecode/VirtualRegister.h \
Source/JavaScriptCore/bytecode/Watchpoint.cpp \
Source/JavaScriptCore/bytecode/Watchpoint.h \
......
......@@ -795,6 +795,7 @@
<ClInclude Include="..\bytecode\UnlinkedCodeBlock.h" />
<ClInclude Include="..\bytecode\ValueProfile.h" />
<ClInclude Include="..\bytecode\ValueRecovery.h" />
<ClInclude Include="..\bytecode\VariableWatchpointSet.h" />
<ClInclude Include="..\bytecode\VirtualRegister.h" />
<ClInclude Include="..\bytecode\Watchpoint.h" />
<ClInclude Include="..\bytecompiler\BytecodeGenerator.h" />
......
......@@ -300,6 +300,7 @@
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 */; };
......@@ -1593,6 +1594,7 @@
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>"; };
......@@ -4132,6 +4134,7 @@
0F963B3613FC6FDE0002D9B2 /* ValueProfile.h */,
0F24E55717F74EDB00ABB217 /* ValueRecovery.cpp */,
0F426A451460CBAB00131F8F /* ValueRecovery.h */,
0F9181C618415CA50057B669 /* VariableWatchpointSet.h */,
0F426A461460CBAB00131F8F /* VirtualRegister.h */,
0F919D2215853CDE004A4E7D /* Watchpoint.cpp */,
0F919D2315853CDE004A4E7D /* Watchpoint.h */,
......@@ -4375,6 +4378,7 @@
0FC097A2146B28CC00CF2442 /* DFGThunks.h in Headers */,
0FD8A32817D51F5700CA2C40 /* DFGTierUpCheckInjectionPhase.h in Headers */,
0FD8A32A17D51F5700CA2C40 /* DFGToFTLDeferredCompilationCallback.h in Headers */,
0F9181C718415CA50057B669 /* VariableWatchpointSet.h in Headers */,
0FD8A32C17D51F5700CA2C40 /* DFGToFTLForOSREntryDeferredCompilationCallback.h in Headers */,
0F63943F15C75F19006A597C /* DFGTypeCheckHoistingPhase.h in Headers */,
0FBE0F7716C1DB120082C5E8 /* DFGUnificationPhase.h in Headers */,
......
......@@ -43,6 +43,7 @@ namespace JSC {
class ArrayAllocationProfile;
class ArrayProfile;
class ObjectAllocationProfile;
class VariableWatchpointSet;
struct LLIntCallLinkInfo;
struct ValueProfile;
......@@ -115,7 +116,7 @@ struct Instruction {
ArrayProfile* arrayProfile;
ArrayAllocationProfile* arrayAllocationProfile;
ObjectAllocationProfile* objectAllocationProfile;
WatchpointSet* watchpointSet;
VariableWatchpointSet* watchpointSet;
void* pointer;
bool* predicatePointer;
} u;
......
/*
* Copyright (C) 2012, 2013 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 VariableWatchpointSet : public WatchpointSet {
friend class LLIntOffsetsExtractor;
public:
VariableWatchpointSet()
: WatchpointSet(ClearWatchpoint)
{
}
~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; }
void notifyWrite(JSValue value)
{
ASSERT(!!value);
switch (state()) {
case ClearWatchpoint:
m_inferredValue = value;
startWatching();
return;
case IsWatched:
ASSERT(!!m_inferredValue);
if (value == m_inferredValue)
return;
invalidate();
return;
case IsInvalidated:
ASSERT(!m_inferredValue);
return;
}
ASSERT_NOT_REACHED();
}
void invalidate()
{
ASSERT(!!m_inferredValue);
ASSERT(state() == IsWatched);
m_inferredValue = JSValue();
fireAll();
}
void finalizeUnconditionally()
{
ASSERT(!!m_inferredValue == (state() == IsWatched));
if (!m_inferredValue)
return;
if (!m_inferredValue.isCell())
return;
JSCell* cell = m_inferredValue.asCell();
if (Heap::isMarked(cell))
return;
invalidate();
}
JSValue* addressOfInferredValue() { return &m_inferredValue; }
private:
JSValue m_inferredValue;
};
} // namespace JSC
#endif // VariableWatchpointSet_h
......@@ -108,14 +108,6 @@ public:
fireAllSlow();
}
void notifyWrite()
{
if (state() == ClearWatchpoint)
startWatching();
else
fireAll();
}
int8_t* addressOfState() { return &m_state; }
int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; }
......
......@@ -1505,13 +1505,13 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
forNode(node).makeHeapTop();
break;
case GlobalVarWatchpoint:
case VariableWatchpoint:
case VarInjectionWatchpoint:
node->setCanExit(true);
break;
case PutGlobalVar:
case NotifyPutGlobalVar:
case NotifyWrite:
break;
case CheckHasInstance:
......
......@@ -3094,13 +3094,15 @@ bool ByteCodeParser::parseBlock(unsigned limit)
case GlobalVarWithVarInjectionChecks: {
addToGraph(Phantom, get(VirtualRegister(scope)));
SymbolTableEntry entry = globalObject->symbolTable()->get(uid);
if (!entry.couldBeWatched() || !m_graph.watchpoints().isStillValid(entry.watchpointSet())) {
VariableWatchpointSet* watchpointSet = entry.watchpointSet();
JSValue specificValue =
watchpointSet ? watchpointSet->inferredValue() : JSValue();
if (!specificValue) {
set(VirtualRegister(dst), addToGraph(GetGlobalVar, OpInfo(operand), OpInfo(prediction)));
break;
}
addToGraph(GlobalVarWatchpoint, OpInfo(operand), OpInfo(identifierNumber));
JSValue specificValue = globalObject->registerAt(entry.getIndex()).get();
addToGraph(VariableWatchpoint, OpInfo(watchpointSet));
if (specificValue.isCell())
set(VirtualRegister(dst), cellConstant(specificValue.asCell()));
else
......@@ -3128,7 +3130,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
StringImpl* uid = m_graph.identifiers()[identifierNumber];
Structure* structure = 0;
WatchpointSet* watchpoints = 0;
VariableWatchpointSet* watchpoints = 0;
uintptr_t operand;
{
ConcurrentJITLocker locker(m_inlineStackTop->m_profiledBlock->m_lock);
......@@ -3160,9 +3162,10 @@ bool ByteCodeParser::parseBlock(unsigned limit)
case GlobalVarWithVarInjectionChecks: {
SymbolTableEntry entry = globalObject->symbolTable()->get(uid);
ASSERT(watchpoints == entry.watchpointSet());
addToGraph(PutGlobalVar, OpInfo(operand), get(VirtualRegister(value)));
Node* valueNode = get(VirtualRegister(value));
addToGraph(PutGlobalVar, OpInfo(operand), valueNode);
if (watchpoints->state() != IsInvalidated)
addToGraph(NotifyPutGlobalVar, OpInfo(operand), OpInfo(identifierNumber));
addToGraph(NotifyWrite, OpInfo(watchpoints), valueNode);
// Keep scope alive until after put.
addToGraph(Phantom, get(VirtualRegister(scope)));
break;
......
......@@ -260,28 +260,6 @@ private:
return 0;
}
bool globalVarWatchpointElimination(WriteBarrier<Unknown>* registerPointer)
{
for (unsigned i = m_indexInBlock; i--;) {
Node* node = m_currentBlock->at(i);
switch (node->op()) {
case GlobalVarWatchpoint:
if (node->registerPointer() == registerPointer)
return true;
break;
case PutGlobalVar:
if (node->registerPointer() == registerPointer)
return false;
break;
default:
break;
}
if (m_graph.clobbersWorld(node))
break;
}
return false;
}
bool varInjectionWatchpointElimination()
{
for (unsigned i = m_indexInBlock; i--;) {
......@@ -1229,13 +1207,6 @@ private:
break;
}
case GlobalVarWatchpoint:
if (cseMode == StoreElimination)
break;
if (globalVarWatchpointElimination(node->registerPointer()))
eliminate();
break;
case VarInjectionWatchpoint:
if (cseMode == StoreElimination)
break;
......
......@@ -143,8 +143,13 @@ void clobberize(Graph& graph, Node* node, ReadFunctor& read, WriteFunctor& write
write(SideState);
return;
case NotifyPutGlobalVar:
case VariableWatchpoint:
read(Watchpoint_fire);
return;
case NotifyWrite:
write(Watchpoint_fire);
write(SideState);
return;
case CreateActivation:
......@@ -520,7 +525,6 @@ void clobberize(Graph& graph, Node* node, ReadFunctor& read, WriteFunctor& write
return;
case GetGlobalVar:
case GlobalVarWatchpoint:
read(AbstractHeap(Absolute, node->registerPointer()));
return;
......
......@@ -918,8 +918,8 @@ private:
case GetClosureVar:
case GetGlobalVar:
case PutGlobalVar:
case NotifyPutGlobalVar:
case GlobalVarWatchpoint:
case NotifyWrite:
case VariableWatchpoint:
case VarInjectionWatchpoint:
case AllocationProfileWatchpoint:
case Call:
......
......@@ -748,20 +748,9 @@ struct Node {
return m_opInfo;
}
bool hasIdentifierNumberForCheck()
{
return op() == GlobalVarWatchpoint || op() == NotifyPutGlobalVar;
}
unsigned identifierNumberForCheck()
{
ASSERT(hasIdentifierNumberForCheck());
return m_opInfo2;
}
bool hasRegisterPointer()
{
return op() == GetGlobalVar || op() == PutGlobalVar || op() == GlobalVarWatchpoint || op() == NotifyPutGlobalVar;
return op() == GetGlobalVar || op() == PutGlobalVar;
}
WriteBarrier<Unknown>* registerPointer()
......@@ -973,6 +962,16 @@ struct Node {
{
return jsCast<ExecutableBase*>(reinterpret_cast<JSCell*>(m_opInfo));
}
bool hasVariableWatchpointSet()
{
return op() == NotifyWrite || op() == VariableWatchpoint;
}
VariableWatchpointSet* variableWatchpointSet()
{
return reinterpret_cast<VariableWatchpointSet*>(m_opInfo);
}
bool hasStructureTransitionData()
{
......
......@@ -184,8 +184,8 @@ namespace JSC { namespace DFG {
macro(PutClosureVar, NodeMustGenerate) \
macro(GetGlobalVar, NodeResultJS) \
macro(PutGlobalVar, NodeMustGenerate) \
macro(NotifyPutGlobalVar, NodeMustGenerate) \
macro(GlobalVarWatchpoint, NodeMustGenerate) \
macro(NotifyWrite, NodeMustGenerate) \
macro(VariableWatchpoint, NodeMustGenerate) \
macro(VarInjectionWatchpoint, NodeMustGenerate) \
macro(CheckFunction, NodeMustGenerate) \
macro(AllocationProfileWatchpoint, NodeMustGenerate) \
......
......@@ -998,12 +998,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, WatchpointSet* set)
void JIT_OPERATION operationInvalidate(ExecState* exec, VariableWatchpointSet* set)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
set->notifyWrite();
set->invalidate();
}
double JIT_OPERATION operationFModOnInts(int32_t a, int32_t b)
......
......@@ -125,7 +125,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*, WatchpointSet*);
void JIT_OPERATION operationInvalidate(ExecState*, VariableWatchpointSet*);
#if ENABLE(FTL_JIT)
// FIXME: Make calls work well. Currently they're a pure regression.
......
......@@ -574,7 +574,7 @@ private:
case TearOffActivation:
case TearOffArguments:
case CheckArgumentsNotCreated:
case GlobalVarWatchpoint:
case VariableWatchpoint:
case VarInjectionWatchpoint:
case AllocationProfileWatchpoint:
case Phantom:
......@@ -582,7 +582,7 @@ private:
case CheckWatchdogTimer:
case Unreachable:
case LoopHint:
case NotifyPutGlobalVar:
case NotifyWrite:
break;
// This gets ignored because it already has a prediction.
......
......@@ -172,7 +172,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
case PutClosureVar:
case GetGlobalVar:
case PutGlobalVar:
case GlobalVarWatchpoint:
case VariableWatchpoint:
case VarInjectionWatchpoint:
case CheckFunction:
case AllocationProfileWatchpoint:
......@@ -242,7 +242,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
case Int52ToDouble:
case Int52ToValue:
case InvalidationPoint:
case NotifyPutGlobalVar:
case NotifyWrite:
return true;
case GetByVal:
......
......@@ -5600,44 +5600,6 @@ void SpeculativeJIT::emitSwitch(Node* node)
RELEASE_ASSERT_NOT_REACHED();
}
void SpeculativeJIT::compileNotifyPutGlobalVar(Node* node)
{
WatchpointSet* set = m_jit.globalObjectFor(node->codeOrigin)->symbolTable()->get(
m_jit.graph().identifiers()[node->identifierNumberForCheck()]).watchpointSet();
GPRTemporary temp(this);
GPRReg tempGPR = temp.gpr();
m_jit.load8(set->addressOfState(), tempGPR);
JITCompiler::JumpList ready;
ready.append(m_jit.branch32(JITCompiler::Equal, tempGPR, TrustedImm32(IsInvalidated)));
m_jit.memoryFence();
if (set->state() == ClearWatchpoint) {
JITCompiler::Jump isWatched =
m_jit.branch32(JITCompiler::NotEqual, tempGPR, TrustedImm32(ClearWatchpoint));
m_jit.store8(TrustedImm32(IsWatched), set->addressOfState());