DFG should be able to set watchpoints on global variables
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 13 Jun 2012 20:53:52 +0000 (20:53 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 13 Jun 2012 20:53:52 +0000 (20:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=88692

Source/JavaScriptCore:

Reviewed by Geoffrey Garen.

Rolling back in after fixing Windows build issues, and implementing
branchTest8 for the Qt port's strange assemblers.

This implements global variable constant folding by allowing the optimizing
compiler to set a "watchpoint" on globals that it wishes to constant fold.
If the watchpoint fires, then an OSR exit is forced by overwriting the
machine code that the optimizing compiler generated with a jump.

As such, this patch is adding quite a bit of stuff:

- Jump replacement on those hardware targets supported by the optimizing
  JIT. It is now possible to patch in a jump instruction over any recorded
  watchpoint label. The jump must be "local" in the sense that it must be
  within the range of the largest jump distance supported by a one
  instruction jump.

- WatchpointSets and Watchpoints. A Watchpoint is a doubly-linked list node
  that records the location where a jump must be inserted and the
  destination to which it should jump. Watchpoints can be added to a
  WatchpointSet. The WatchpointSet can be fired all at once, which plants
  all jumps. WatchpointSet also remembers if it had ever been invalidated,
  which allows for monotonicity: we typically don't want to optimize using
  watchpoints on something for which watchpoints had previously fired. The
  act of notifying a WatchpointSet has a trivial fast path in case no
  Watchpoints are registered (one-byte load+branch).

- SpeculativeJIT::speculationWatchpoint(). It's like speculationCheck(),
  except that you don't have to emit branches. But, you need to know what
  WatchpointSet to add the resulting Watchpoint to. Not everything that
  you could write a speculationCheck() for will have a WatchpointSet that
  would get notified if the condition you were speculating against became
  invalid.

- SymbolTableEntry now has the ability to refer to a WatchpointSet. It can
  do so without incurring any space overhead for those entries that don't
  have WatchpointSets.

- The bytecode generator infers all global function variables to be
  watchable, and makes all stores perform the WatchpointSet's write check,
  and marks all loads as being potentially watchable (i.e. you can compile
  them to a watchpoint and a constant).

Put together, this allows for fully sleazy inlining of calls to globally
declared functions. The inline prologue will no longer contain the load of
the function, or any checks of the function you're calling. I.e. it's
pretty much like the kind of inlining you would see in Java or C++.
Furthermore, the watchpointing functionality is built to be fairly general,
and should allow setting watchpoints on all sorts of interesting things
in the future.

The sleazy inlining means that we will now sometimes inline in code paths
that have never executed. Previously, to inline we would have either had
to have executed the call (to read the call's inline cache) or have
executed the method check (to read the method check's inline cache). Now,
we might inline when the callee is a watched global variable. This
revealed some humorous bugs. First, constant folding disagreed with CFA
over what kinds of operations can clobber (example: code path A is dead
but stores a String into variable X, all other code paths store 0 into
X, and then you do CompareEq(X, 0) - CFA will say that this is a non-
clobbering constant, but constant folding thought it was clobbering
because it saw the String prediction). Second, inlining would crash if
the inline callee had not been compiled. This patch fixes both bugs,
since otherwise run-javascriptcore-tests would report regressions.

* CMakeLists.txt:
* GNUmakefile.list.am:
* JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def:
* JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* Target.pri:
* assembler/ARMv7Assembler.h:
(ARMv7Assembler):
(JSC::ARMv7Assembler::ARMv7Assembler):
(JSC::ARMv7Assembler::labelForWatchpoint):
(JSC::ARMv7Assembler::label):
(JSC::ARMv7Assembler::replaceWithJump):
(JSC::ARMv7Assembler::maxJumpReplacementSize):
* assembler/AbstractMacroAssembler.h:
(JSC):
(AbstractMacroAssembler):
(Label):
(JSC::AbstractMacroAssembler::watchpointLabel):
(JSC::AbstractMacroAssembler::readPointer):
* assembler/AssemblerBuffer.h:
* assembler/MacroAssemblerARM.h:
(JSC::MacroAssemblerARM::branchTest8):
(MacroAssemblerARM):
(JSC::MacroAssemblerARM::replaceWithJump):
(JSC::MacroAssemblerARM::maxJumpReplacementSize):
* assembler/MacroAssemblerARMv7.h:
(JSC::MacroAssemblerARMv7::load8Signed):
(JSC::MacroAssemblerARMv7::load16Signed):
(MacroAssemblerARMv7):
(JSC::MacroAssemblerARMv7::replaceWithJump):
(JSC::MacroAssemblerARMv7::maxJumpReplacementSize):
(JSC::MacroAssemblerARMv7::branchTest8):
(JSC::MacroAssemblerARMv7::jump):
(JSC::MacroAssemblerARMv7::makeBranch):
* assembler/MacroAssemblerMIPS.h:
(JSC::MacroAssemblerMIPS::branchTest8):
(MacroAssemblerMIPS):
(JSC::MacroAssemblerMIPS::replaceWithJump):
(JSC::MacroAssemblerMIPS::maxJumpReplacementSize):
* assembler/MacroAssemblerSH4.h:
(JSC::MacroAssemblerSH4::branchTest8):
(MacroAssemblerSH4):
(JSC::MacroAssemblerSH4::replaceWithJump):
(JSC::MacroAssemblerSH4::maxJumpReplacementSize):
* assembler/MacroAssemblerX86.h:
(MacroAssemblerX86):
(JSC::MacroAssemblerX86::branchTest8):
* assembler/MacroAssemblerX86Common.h:
(JSC::MacroAssemblerX86Common::replaceWithJump):
(MacroAssemblerX86Common):
(JSC::MacroAssemblerX86Common::maxJumpReplacementSize):
* assembler/MacroAssemblerX86_64.h:
(MacroAssemblerX86_64):
(JSC::MacroAssemblerX86_64::branchTest8):
* assembler/X86Assembler.h:
(JSC::X86Assembler::X86Assembler):
(X86Assembler):
(JSC::X86Assembler::cmpb_im):
(JSC::X86Assembler::testb_im):
(JSC::X86Assembler::labelForWatchpoint):
(JSC::X86Assembler::label):
(JSC::X86Assembler::replaceWithJump):
(JSC::X86Assembler::maxJumpReplacementSize):
(JSC::X86Assembler::X86InstructionFormatter::memoryModRM):
* bytecode/CodeBlock.cpp:
(JSC):
(JSC::CodeBlock::printGetByIdCacheStatus):
(JSC::CodeBlock::dump):
* bytecode/CodeBlock.h:
(JSC::CodeBlock::appendOSRExit):
(JSC::CodeBlock::appendSpeculationRecovery):
(CodeBlock):
(JSC::CodeBlock::appendWatchpoint):
(JSC::CodeBlock::numberOfWatchpoints):
(JSC::CodeBlock::watchpoint):
(DFGData):
* bytecode/DFGExitProfile.h:
(JSC::DFG::exitKindToString):
(JSC::DFG::exitKindIsCountable):
* bytecode/GetByIdStatus.cpp:
(JSC::GetByIdStatus::computeForChain):
* bytecode/Instruction.h:
(Instruction):
(JSC::Instruction::Instruction):
* bytecode/Opcode.h:
(JSC):
(JSC::padOpcodeName):
* bytecode/Watchpoint.cpp: Added.
(JSC):
(JSC::Watchpoint::~Watchpoint):
(JSC::Watchpoint::correctLabels):
(JSC::Watchpoint::fire):
(JSC::WatchpointSet::WatchpointSet):
(JSC::WatchpointSet::~WatchpointSet):
(JSC::WatchpointSet::add):
(JSC::WatchpointSet::notifyWriteSlow):
(JSC::WatchpointSet::fireAllWatchpoints):
* bytecode/Watchpoint.h: Added.
(JSC):
(Watchpoint):
(JSC::Watchpoint::Watchpoint):
(JSC::Watchpoint::setDestination):
(WatchpointSet):
(JSC::WatchpointSet::isStillValid):
(JSC::WatchpointSet::hasBeenInvalidated):
(JSC::WatchpointSet::startWatching):
(JSC::WatchpointSet::notifyWrite):
(JSC::WatchpointSet::addressOfIsWatched):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::ResolveResult::checkValidity):
(JSC::BytecodeGenerator::addGlobalVar):
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::resolve):
(JSC::BytecodeGenerator::emitResolve):
(JSC::BytecodeGenerator::emitResolveWithBase):
(JSC::BytecodeGenerator::emitResolveWithThis):
(JSC::BytecodeGenerator::emitGetStaticVar):
(JSC::BytecodeGenerator::emitPutStaticVar):
* bytecompiler/BytecodeGenerator.h:
(BytecodeGenerator):
* bytecompiler/NodesCodegen.cpp:
(JSC::FunctionCallResolveNode::emitBytecode):
(JSC::PostfixResolveNode::emitBytecode):
(JSC::PrefixResolveNode::emitBytecode):
(JSC::ReadModifyResolveNode::emitBytecode):
(JSC::AssignResolveNode::emitBytecode):
(JSC::ConstDeclNode::emitCodeSingle):
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
(JSC::DFG::AbstractState::clobberStructures):
* dfg/DFGAbstractState.h:
(AbstractState):
(JSC::DFG::AbstractState::didClobber):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleInlining):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCCallHelpers.h:
(CCallHelpers):
(JSC::DFG::CCallHelpers::setupArguments):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::globalVarWatchpointElimination):
(CSEPhase):
(JSC::DFG::CSEPhase::globalVarStoreElimination):
(JSC::DFG::CSEPhase::performNodeCSE):
* dfg/DFGCapabilities.h:
(JSC::DFG::canCompileOpcode):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::run):
* dfg/DFGCorrectableJumpPoint.h:
(JSC::DFG::CorrectableJumpPoint::isSet):
(CorrectableJumpPoint):
* dfg/DFGJITCompiler.cpp:
(JSC::DFG::JITCompiler::linkOSRExits):
(JSC::DFG::JITCompiler::link):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasIdentifierNumberForCheck):
(Node):
(JSC::DFG::Node::identifierNumberForCheck):
(JSC::DFG::Node::hasRegisterPointer):
* dfg/DFGNodeType.h:
(DFG):
* dfg/DFGOSRExit.cpp:
(JSC::DFG::OSRExit::OSRExit):
* dfg/DFGOSRExit.h:
(OSRExit):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
(JSC::DFG::SpeculativeJIT::appendCall):
(SpeculativeJIT):
(JSC::DFG::SpeculativeJIT::speculationWatchpoint):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* interpreter/Interpreter.cpp:
(JSC::Interpreter::privateExecute):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):
* jit/JIT.h:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emit_op_put_global_var_check):
(JSC):
(JSC::JIT::emitSlow_op_put_global_var_check):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emit_op_put_global_var_check):
(JSC):
(JSC::JIT::emitSlow_op_put_global_var_check):
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):
(JSC):
* jit/JITStubs.h:
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
(LLInt):
* llint/LLIntSlowPaths.h:
(LLInt):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/JSObject.cpp:
(JSC::JSObject::removeDirect):
* runtime/JSObject.h:
(JSObject):
* runtime/JSSymbolTableObject.h:
(JSC::symbolTableGet):
(JSC::symbolTablePut):
(JSC::symbolTablePutWithAttributes):
* runtime/SymbolTable.cpp: Added.
(JSC):
(JSC::SymbolTableEntry::copySlow):
(JSC::SymbolTableEntry::freeFatEntrySlow):
(JSC::SymbolTableEntry::couldBeWatched):
(JSC::SymbolTableEntry::attemptToWatch):
(JSC::SymbolTableEntry::addressOfIsWatched):
(JSC::SymbolTableEntry::addWatchpoint):
(JSC::SymbolTableEntry::notifyWriteSlow):
(JSC::SymbolTableEntry::inflateSlow):
* runtime/SymbolTable.h:
(JSC):
(SymbolTableEntry):
(Fast):
(JSC::SymbolTableEntry::Fast::Fast):
(JSC::SymbolTableEntry::Fast::isNull):
(JSC::SymbolTableEntry::Fast::getIndex):
(JSC::SymbolTableEntry::Fast::isReadOnly):
(JSC::SymbolTableEntry::Fast::getAttributes):
(JSC::SymbolTableEntry::Fast::isFat):
(JSC::SymbolTableEntry::SymbolTableEntry):
(JSC::SymbolTableEntry::~SymbolTableEntry):
(JSC::SymbolTableEntry::operator=):
(JSC::SymbolTableEntry::isNull):
(JSC::SymbolTableEntry::getIndex):
(JSC::SymbolTableEntry::getFast):
(JSC::SymbolTableEntry::getAttributes):
(JSC::SymbolTableEntry::isReadOnly):
(JSC::SymbolTableEntry::watchpointSet):
(JSC::SymbolTableEntry::notifyWrite):
(FatEntry):
(JSC::SymbolTableEntry::FatEntry::FatEntry):
(JSC::SymbolTableEntry::isFat):
(JSC::SymbolTableEntry::fatEntry):
(JSC::SymbolTableEntry::inflate):
(JSC::SymbolTableEntry::bits):
(JSC::SymbolTableEntry::freeFatEntry):
(JSC::SymbolTableEntry::pack):
(JSC::SymbolTableEntry::isValidIndex):

Source/WTF:

Reviewed by Geoffrey Garen.

Added ability to set the inline capacity of segmented vectors.

Also added the ability ot ASSERT_NOT_REACHED() without having to
propagate NO_RETURN macros, which would be a show-stopper for code
that is conditionally unreachable.

* wtf/Assertions.h:
(UNREACHABLE_FOR_PLATFORM):
* wtf/SegmentedVector.h:
(WTF):
(SegmentedVectorIterator):
(WTF::SegmentedVectorIterator::operator=):
(WTF::SegmentedVectorIterator::SegmentedVectorIterator):
(SegmentedVector):

LayoutTests:

Rubber stamped by Geoffrey Garen.

Added a test for watchpoints. Also updated the jsc-test-list to include the latest
tests.

* fast/js/dfg-call-function-hit-watchpoint-expected.txt: Added.
* fast/js/dfg-call-function-hit-watchpoint.html: Added.
* fast/js/jsc-test-list:
* fast/js/script-tests/dfg-call-function-hit-watchpoint.js: Added.
(foo):
(bar):
(.foo):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@120244 268f45cc-cd09-0410-ab3c-d52691b4dbfc

72 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/js/dfg-call-function-hit-watchpoint-expected.txt [new file with mode: 0644]
LayoutTests/fast/js/dfg-call-function-hit-watchpoint.html [new file with mode: 0644]
LayoutTests/fast/js/jsc-test-list
LayoutTests/fast/js/script-tests/dfg-call-function-hit-watchpoint.js [new file with mode: 0644]
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/GNUmakefile.list.am
Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def
Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/Target.pri
Source/JavaScriptCore/assembler/ARMv7Assembler.h
Source/JavaScriptCore/assembler/AbstractMacroAssembler.h
Source/JavaScriptCore/assembler/AssemblerBuffer.h
Source/JavaScriptCore/assembler/MacroAssemblerARM.h
Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h
Source/JavaScriptCore/assembler/MacroAssemblerMIPS.h
Source/JavaScriptCore/assembler/MacroAssemblerSH4.h
Source/JavaScriptCore/assembler/MacroAssemblerX86.h
Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h
Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h
Source/JavaScriptCore/assembler/X86Assembler.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecode/DFGExitProfile.h
Source/JavaScriptCore/bytecode/GetByIdStatus.cpp
Source/JavaScriptCore/bytecode/Instruction.h
Source/JavaScriptCore/bytecode/Opcode.h
Source/JavaScriptCore/bytecode/Watchpoint.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/Watchpoint.h [new file with mode: 0644]
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/dfg/DFGAbstractState.cpp
Source/JavaScriptCore/dfg/DFGAbstractState.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCCallHelpers.h
Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGCorrectableJumpPoint.h
Source/JavaScriptCore/dfg/DFGJITCompiler.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOSRExit.cpp
Source/JavaScriptCore/dfg/DFGOSRExit.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITPropertyAccess.cpp
Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
Source/JavaScriptCore/jit/JITStubs.cpp
Source/JavaScriptCore/jit/JITStubs.h
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.h
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/runtime/JSObject.cpp
Source/JavaScriptCore/runtime/JSObject.h
Source/JavaScriptCore/runtime/JSSymbolTableObject.h
Source/JavaScriptCore/runtime/SymbolTable.cpp [new file with mode: 0644]
Source/JavaScriptCore/runtime/SymbolTable.h
Source/WTF/ChangeLog
Source/WTF/wtf/Assertions.h
Source/WTF/wtf/SegmentedVector.h

index a193486..10a457c 100644 (file)
@@ -1,3 +1,21 @@
+2012-06-12  Filip Pizlo  <fpizlo@apple.com>
+
+        DFG should be able to set watchpoints on global variables
+        https://bugs.webkit.org/show_bug.cgi?id=88692
+
+        Rubber stamped by Geoffrey Garen.
+        
+        Added a test for watchpoints. Also updated the jsc-test-list to include the latest
+        tests.
+
+        * fast/js/dfg-call-function-hit-watchpoint-expected.txt: Added.
+        * fast/js/dfg-call-function-hit-watchpoint.html: Added.
+        * fast/js/jsc-test-list:
+        * fast/js/script-tests/dfg-call-function-hit-watchpoint.js: Added.
+        (foo):
+        (bar):
+        (.foo):
+
 2012-06-13  Zan Dobersek  <zandobersek@gmail.com>
 
         Unreviewed GTK gardening after r120228, fix an incorrect test expectation.
diff --git a/LayoutTests/fast/js/dfg-call-function-hit-watchpoint-expected.txt b/LayoutTests/fast/js/dfg-call-function-hit-watchpoint-expected.txt
new file mode 100644 (file)
index 0000000..b05cdfb
--- /dev/null
@@ -0,0 +1,209 @@
+Tests correctness of function calls when the function is overwritten.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS bar(i, i + 1) is 1
+PASS bar(i, i + 1) is 3
+PASS bar(i, i + 1) is 5
+PASS bar(i, i + 1) is 7
+PASS bar(i, i + 1) is 9
+PASS bar(i, i + 1) is 11
+PASS bar(i, i + 1) is 13
+PASS bar(i, i + 1) is 15
+PASS bar(i, i + 1) is 17
+PASS bar(i, i + 1) is 19
+PASS bar(i, i + 1) is 21
+PASS bar(i, i + 1) is 23
+PASS bar(i, i + 1) is 25
+PASS bar(i, i + 1) is 27
+PASS bar(i, i + 1) is 29
+PASS bar(i, i + 1) is 31
+PASS bar(i, i + 1) is 33
+PASS bar(i, i + 1) is 35
+PASS bar(i, i + 1) is 37
+PASS bar(i, i + 1) is 39
+PASS bar(i, i + 1) is 41
+PASS bar(i, i + 1) is 43
+PASS bar(i, i + 1) is 45
+PASS bar(i, i + 1) is 47
+PASS bar(i, i + 1) is 49
+PASS bar(i, i + 1) is 51
+PASS bar(i, i + 1) is 53
+PASS bar(i, i + 1) is 55
+PASS bar(i, i + 1) is 57
+PASS bar(i, i + 1) is 59
+PASS bar(i, i + 1) is 61
+PASS bar(i, i + 1) is 63
+PASS bar(i, i + 1) is 65
+PASS bar(i, i + 1) is 67
+PASS bar(i, i + 1) is 69
+PASS bar(i, i + 1) is 71
+PASS bar(i, i + 1) is 73
+PASS bar(i, i + 1) is 75
+PASS bar(i, i + 1) is 77
+PASS bar(i, i + 1) is 79
+PASS bar(i, i + 1) is 81
+PASS bar(i, i + 1) is 83
+PASS bar(i, i + 1) is 85
+PASS bar(i, i + 1) is 87
+PASS bar(i, i + 1) is 89
+PASS bar(i, i + 1) is 91
+PASS bar(i, i + 1) is 93
+PASS bar(i, i + 1) is 95
+PASS bar(i, i + 1) is 97
+PASS bar(i, i + 1) is 99
+PASS bar(i, i + 1) is 101
+PASS bar(i, i + 1) is 103
+PASS bar(i, i + 1) is 105
+PASS bar(i, i + 1) is 107
+PASS bar(i, i + 1) is 109
+PASS bar(i, i + 1) is 111
+PASS bar(i, i + 1) is 113
+PASS bar(i, i + 1) is 115
+PASS bar(i, i + 1) is 117
+PASS bar(i, i + 1) is 119
+PASS bar(i, i + 1) is 121
+PASS bar(i, i + 1) is 123
+PASS bar(i, i + 1) is 125
+PASS bar(i, i + 1) is 127
+PASS bar(i, i + 1) is 129
+PASS bar(i, i + 1) is 131
+PASS bar(i, i + 1) is 133
+PASS bar(i, i + 1) is 135
+PASS bar(i, i + 1) is 137
+PASS bar(i, i + 1) is 139
+PASS bar(i, i + 1) is 141
+PASS bar(i, i + 1) is 143
+PASS bar(i, i + 1) is 145
+PASS bar(i, i + 1) is 147
+PASS bar(i, i + 1) is 149
+PASS bar(i, i + 1) is 151
+PASS bar(i, i + 1) is 153
+PASS bar(i, i + 1) is 155
+PASS bar(i, i + 1) is 157
+PASS bar(i, i + 1) is 159
+PASS bar(i, i + 1) is 161
+PASS bar(i, i + 1) is 163
+PASS bar(i, i + 1) is 165
+PASS bar(i, i + 1) is 167
+PASS bar(i, i + 1) is 169
+PASS bar(i, i + 1) is 171
+PASS bar(i, i + 1) is 173
+PASS bar(i, i + 1) is 175
+PASS bar(i, i + 1) is 177
+PASS bar(i, i + 1) is 179
+PASS bar(i, i + 1) is 181
+PASS bar(i, i + 1) is 183
+PASS bar(i, i + 1) is 185
+PASS bar(i, i + 1) is 187
+PASS bar(i, i + 1) is 189
+PASS bar(i, i + 1) is 191
+PASS bar(i, i + 1) is 193
+PASS bar(i, i + 1) is 195
+PASS bar(i, i + 1) is 197
+PASS bar(i, i + 1) is 199
+PASS bar(i, i + 1) is 201
+PASS bar(i, i + 1) is 203
+PASS bar(i, i + 1) is 205
+PASS bar(i, i + 1) is 207
+PASS bar(i, i + 1) is 209
+PASS bar(i, i + 1) is 211
+PASS bar(i, i + 1) is 213
+PASS bar(i, i + 1) is 215
+PASS bar(i, i + 1) is 217
+PASS bar(i, i + 1) is 219
+PASS bar(i, i + 1) is 221
+PASS bar(i, i + 1) is 223
+PASS bar(i, i + 1) is 225
+PASS bar(i, i + 1) is 227
+PASS bar(i, i + 1) is 229
+PASS bar(i, i + 1) is 231
+PASS bar(i, i + 1) is 233
+PASS bar(i, i + 1) is 235
+PASS bar(i, i + 1) is 237
+PASS bar(i, i + 1) is 239
+PASS bar(i, i + 1) is 241
+PASS bar(i, i + 1) is 243
+PASS bar(i, i + 1) is 245
+PASS bar(i, i + 1) is 247
+PASS bar(i, i + 1) is 249
+PASS bar(i, i + 1) is 251
+PASS bar(i, i + 1) is 253
+PASS bar(i, i + 1) is 255
+PASS bar(i, i + 1) is 257
+PASS bar(i, i + 1) is 259
+PASS bar(i, i + 1) is 261
+PASS bar(i, i + 1) is 263
+PASS bar(i, i + 1) is 265
+PASS bar(i, i + 1) is 267
+PASS bar(i, i + 1) is 269
+PASS bar(i, i + 1) is 271
+PASS bar(i, i + 1) is 273
+PASS bar(i, i + 1) is 275
+PASS bar(i, i + 1) is 277
+PASS bar(i, i + 1) is 279
+PASS bar(i, i + 1) is 281
+PASS bar(i, i + 1) is 283
+PASS bar(i, i + 1) is 285
+PASS bar(i, i + 1) is 287
+PASS bar(i, i + 1) is 289
+PASS bar(i, i + 1) is 291
+PASS bar(i, i + 1) is 293
+PASS bar(i, i + 1) is 295
+PASS bar(i, i + 1) is 297
+PASS bar(i, i + 1) is 299
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS bar(i, i + 1) is -1
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/js/dfg-call-function-hit-watchpoint.html b/LayoutTests/fast/js/dfg-call-function-hit-watchpoint.html
new file mode 100644 (file)
index 0000000..03c6ee6
--- /dev/null
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="script-tests/dfg-call-function-hit-watchpoint.js"></script>
+<script src="resources/js-test-post.js"></script>
+</body>
+</html>
index 189bf31..c37424c 100644 (file)
@@ -64,23 +64,45 @@ fast/js/debugger
 fast/js/declaration-in-block
 fast/js/delete-getters-setters
 fast/js/delete-then-put
-fast/js/dfg-arguments-osr-exit
-fast/js/dfg-arguments-mixed-alias
+fast/js/dfg-add-not-number
+fast/js/dfg-arguments-alias-escape
+fast/js/dfg-arguments-alias-one-block-osr-exit
+fast/js/dfg-arguments-alias-one-block-overwrite-arguments
+fast/js/dfg-arguments-alias-one-block-overwrite
+fast/js/dfg-arguments-alias-one-block
+fast/js/dfg-arguments-alias
 fast/js/dfg-arguments-cross-code-origin
+fast/js/dfg-arguments-mixed-alias
+fast/js/dfg-arguments-osr-exit
+fast/js/dfg-arguments-out-of-bounds
 fast/js/dfg-arguments-unexpected-escape
-fast/js/dfg-arguments-alias
-fast/js/dfg-arguments-alias-escape
 fast/js/dfg-array-length-dead
+fast/js/dfg-branch-not-fail
+fast/js/dfg-check-two-structures
+fast/js/dfg-constant-fold-first-local-read-after-block-merge
 fast/js/dfg-convert-this-dom-window
+fast/js/dfg-cse-cfa-discrepancy
+fast/js/dfg-dead-min-one-arg
+fast/js/dfg-dead-min-two-args
+fast/js/dfg-dead-speculation
+fast/js/dfg-dead-variable-on-exit
+fast/js/dfg-double-use-of-post-simplification-double-prediction
 fast/js/dfg-double-vote-fuzz
 fast/js/dfg-exception
 fast/js/dfg-float32array
 fast/js/dfg-float64array
+fast/js/dfg-flush-get-local
 fast/js/dfg-get-by-val-clobber
 fast/js/dfg-get-by-val-getter-cse
 fast/js/dfg-getter-throw
 fast/js/dfg-getter
+fast/js/dfg-inline-arguments-become-double
+fast/js/dfg-inline-arguments-become-int32
 fast/js/dfg-inline-arguments-int32
+fast/js/dfg-inline-arguments-osr-exit-and-capture
+fast/js/dfg-inline-arguments-out-of-bounds
+fast/js/dfg-inline-arguments-reset-changetype
+fast/js/dfg-inline-arguments-reset
 fast/js/dfg-inline-arguments-simple
 fast/js/dfg-inline-arguments-use-directly-from-inlined-code
 fast/js/dfg-inline-arguments-use-from-all-the-places-broken
@@ -96,6 +118,7 @@ fast/js/dfg-inline-unused-this-method-check
 fast/js/dfg-inline-unused-this
 fast/js/dfg-inlining-reg-alloc
 fast/js/dfg-int16array
+fast/js/dfg-int32-to-double-on-known-number
 fast/js/dfg-int32array-overflow-values
 fast/js/dfg-int32array
 fast/js/dfg-int8array
@@ -105,15 +128,24 @@ fast/js/dfg-intrinsic-unused-this-method-check
 fast/js/dfg-intrinsic-unused-this
 fast/js/dfg-min-max
 fast/js/dfg-multiply
+fast/js/dfg-negative-array-index
+fast/js/dfg-obvious-constant-cfa
 fast/js/dfg-other-branch
 fast/js/dfg-poison-fuzz
+fast/js/dfg-proto-access-inline-osr-exit
+fast/js/dfg-put-by-id-prototype-check
+fast/js/dfg-putbyval-cfa-clobber
 fast/js/dfg-string-stricteq
+fast/js/dfg-tear-off-arguments-not-activation
 fast/js/dfg-uint16array
-fast/js/dfg-uint32array-overflow-values
-fast/js/dfg-uint32-to-number
 fast/js/dfg-uint32-to-number-on-captured-variable
+fast/js/dfg-uint32-to-number
+fast/js/dfg-uint32array-overflow-constant
+fast/js/dfg-uint32array-overflow-values
+fast/js/dfg-uint32array
 fast/js/dfg-uint8array
 fast/js/dfg-uint8clampedarray
+fast/js/dfg-weak-js-constant-silent-fill
 fast/js/dictionary-no-cache
 fast/js/dictionary-prototype-caching
 fast/js/do-while-semicolon
@@ -126,6 +158,7 @@ fast/js/eval-var-decl
 fast/js/exception-expression-offset
 fast/js/exception-for-nonobject
 fast/js/exception-linenums
+fast/js/exception-propagate-from-dfg-to-llint
 fast/js/exception-properties
 fast/js/exception-try-finally-scope-error
 fast/js/exception-with-handler-inside-eval-with-dynamic-scope
diff --git a/LayoutTests/fast/js/script-tests/dfg-call-function-hit-watchpoint.js b/LayoutTests/fast/js/script-tests/dfg-call-function-hit-watchpoint.js
new file mode 100644 (file)
index 0000000..43aa170
--- /dev/null
@@ -0,0 +1,23 @@
+description(
+"Tests correctness of function calls when the function is overwritten."
+);
+
+function foo(a, b) {
+    return a + b;
+}
+
+function bar(a, b) {
+    return foo(a, b);
+}
+
+for (var i = 0; i < 200; ++i) {
+    if (i == 150)
+        foo = function(a, b) { return a - b; }
+    var expected;
+    if (i < 150)
+        expected = i + i + 1;
+    else
+        expected = -1;
+    shouldBe("bar(i, i + 1)", "" + expected);
+}
+
index 084f660..05cd7d7 100644 (file)
@@ -53,6 +53,7 @@ SET(JavaScriptCore_SOURCES
     bytecode/PutByIdStatus.cpp
     bytecode/SamplingTool.cpp
     bytecode/StructureStubInfo.cpp
+    bytecode/Watchpoint.cpp
 
     bytecompiler/BytecodeGenerator.cpp
     bytecompiler/NodesCodegen.cpp
@@ -231,6 +232,7 @@ SET(JavaScriptCore_SOURCES
     runtime/StringRecursionChecker.cpp
     runtime/Structure.cpp
     runtime/StructureChain.cpp
+    runtime/SymbolTable.cpp
     runtime/TimeoutChecker.cpp
     runtime/UString.cpp
 
index 5e5a80b..6387eb6 100644 (file)
@@ -1,3 +1,325 @@
+2012-06-13  Filip Pizlo  <fpizlo@apple.com>
+
+        DFG should be able to set watchpoints on global variables
+        https://bugs.webkit.org/show_bug.cgi?id=88692
+
+        Reviewed by Geoffrey Garen.
+        
+        Rolling back in after fixing Windows build issues, and implementing
+        branchTest8 for the Qt port's strange assemblers.
+        
+        This implements global variable constant folding by allowing the optimizing
+        compiler to set a "watchpoint" on globals that it wishes to constant fold.
+        If the watchpoint fires, then an OSR exit is forced by overwriting the
+        machine code that the optimizing compiler generated with a jump.
+        
+        As such, this patch is adding quite a bit of stuff:
+        
+        - Jump replacement on those hardware targets supported by the optimizing
+          JIT. It is now possible to patch in a jump instruction over any recorded
+          watchpoint label. The jump must be "local" in the sense that it must be
+          within the range of the largest jump distance supported by a one
+          instruction jump.
+          
+        - WatchpointSets and Watchpoints. A Watchpoint is a doubly-linked list node
+          that records the location where a jump must be inserted and the
+          destination to which it should jump. Watchpoints can be added to a
+          WatchpointSet. The WatchpointSet can be fired all at once, which plants
+          all jumps. WatchpointSet also remembers if it had ever been invalidated,
+          which allows for monotonicity: we typically don't want to optimize using
+          watchpoints on something for which watchpoints had previously fired. The
+          act of notifying a WatchpointSet has a trivial fast path in case no
+          Watchpoints are registered (one-byte load+branch).
+        
+        - SpeculativeJIT::speculationWatchpoint(). It's like speculationCheck(),
+          except that you don't have to emit branches. But, you need to know what
+          WatchpointSet to add the resulting Watchpoint to. Not everything that
+          you could write a speculationCheck() for will have a WatchpointSet that
+          would get notified if the condition you were speculating against became
+          invalid.
+          
+        - SymbolTableEntry now has the ability to refer to a WatchpointSet. It can
+          do so without incurring any space overhead for those entries that don't
+          have WatchpointSets.
+          
+        - The bytecode generator infers all global function variables to be
+          watchable, and makes all stores perform the WatchpointSet's write check,
+          and marks all loads as being potentially watchable (i.e. you can compile
+          them to a watchpoint and a constant).
+        
+        Put together, this allows for fully sleazy inlining of calls to globally
+        declared functions. The inline prologue will no longer contain the load of
+        the function, or any checks of the function you're calling. I.e. it's
+        pretty much like the kind of inlining you would see in Java or C++.
+        Furthermore, the watchpointing functionality is built to be fairly general,
+        and should allow setting watchpoints on all sorts of interesting things
+        in the future.
+        
+        The sleazy inlining means that we will now sometimes inline in code paths
+        that have never executed. Previously, to inline we would have either had
+        to have executed the call (to read the call's inline cache) or have
+        executed the method check (to read the method check's inline cache). Now,
+        we might inline when the callee is a watched global variable. This
+        revealed some humorous bugs. First, constant folding disagreed with CFA
+        over what kinds of operations can clobber (example: code path A is dead
+        but stores a String into variable X, all other code paths store 0 into
+        X, and then you do CompareEq(X, 0) - CFA will say that this is a non-
+        clobbering constant, but constant folding thought it was clobbering
+        because it saw the String prediction). Second, inlining would crash if
+        the inline callee had not been compiled. This patch fixes both bugs,
+        since otherwise run-javascriptcore-tests would report regressions.
+
+        * CMakeLists.txt:
+        * GNUmakefile.list.am:
+        * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def:
+        * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * Target.pri:
+        * assembler/ARMv7Assembler.h:
+        (ARMv7Assembler):
+        (JSC::ARMv7Assembler::ARMv7Assembler):
+        (JSC::ARMv7Assembler::labelForWatchpoint):
+        (JSC::ARMv7Assembler::label):
+        (JSC::ARMv7Assembler::replaceWithJump):
+        (JSC::ARMv7Assembler::maxJumpReplacementSize):
+        * assembler/AbstractMacroAssembler.h:
+        (JSC):
+        (AbstractMacroAssembler):
+        (Label):
+        (JSC::AbstractMacroAssembler::watchpointLabel):
+        (JSC::AbstractMacroAssembler::readPointer):
+        * assembler/AssemblerBuffer.h:
+        * assembler/MacroAssemblerARM.h:
+        (JSC::MacroAssemblerARM::branchTest8):
+        (MacroAssemblerARM):
+        (JSC::MacroAssemblerARM::replaceWithJump):
+        (JSC::MacroAssemblerARM::maxJumpReplacementSize):
+        * assembler/MacroAssemblerARMv7.h:
+        (JSC::MacroAssemblerARMv7::load8Signed):
+        (JSC::MacroAssemblerARMv7::load16Signed):
+        (MacroAssemblerARMv7):
+        (JSC::MacroAssemblerARMv7::replaceWithJump):
+        (JSC::MacroAssemblerARMv7::maxJumpReplacementSize):
+        (JSC::MacroAssemblerARMv7::branchTest8):
+        (JSC::MacroAssemblerARMv7::jump):
+        (JSC::MacroAssemblerARMv7::makeBranch):
+        * assembler/MacroAssemblerMIPS.h:
+        (JSC::MacroAssemblerMIPS::branchTest8):
+        (MacroAssemblerMIPS):
+        (JSC::MacroAssemblerMIPS::replaceWithJump):
+        (JSC::MacroAssemblerMIPS::maxJumpReplacementSize):
+        * assembler/MacroAssemblerSH4.h:
+        (JSC::MacroAssemblerSH4::branchTest8):
+        (MacroAssemblerSH4):
+        (JSC::MacroAssemblerSH4::replaceWithJump):
+        (JSC::MacroAssemblerSH4::maxJumpReplacementSize):
+        * assembler/MacroAssemblerX86.h:
+        (MacroAssemblerX86):
+        (JSC::MacroAssemblerX86::branchTest8):
+        * assembler/MacroAssemblerX86Common.h:
+        (JSC::MacroAssemblerX86Common::replaceWithJump):
+        (MacroAssemblerX86Common):
+        (JSC::MacroAssemblerX86Common::maxJumpReplacementSize):
+        * assembler/MacroAssemblerX86_64.h:
+        (MacroAssemblerX86_64):
+        (JSC::MacroAssemblerX86_64::branchTest8):
+        * assembler/X86Assembler.h:
+        (JSC::X86Assembler::X86Assembler):
+        (X86Assembler):
+        (JSC::X86Assembler::cmpb_im):
+        (JSC::X86Assembler::testb_im):
+        (JSC::X86Assembler::labelForWatchpoint):
+        (JSC::X86Assembler::label):
+        (JSC::X86Assembler::replaceWithJump):
+        (JSC::X86Assembler::maxJumpReplacementSize):
+        (JSC::X86Assembler::X86InstructionFormatter::memoryModRM):
+        * bytecode/CodeBlock.cpp:
+        (JSC):
+        (JSC::CodeBlock::printGetByIdCacheStatus):
+        (JSC::CodeBlock::dump):
+        * bytecode/CodeBlock.h:
+        (JSC::CodeBlock::appendOSRExit):
+        (JSC::CodeBlock::appendSpeculationRecovery):
+        (CodeBlock):
+        (JSC::CodeBlock::appendWatchpoint):
+        (JSC::CodeBlock::numberOfWatchpoints):
+        (JSC::CodeBlock::watchpoint):
+        (DFGData):
+        * bytecode/DFGExitProfile.h:
+        (JSC::DFG::exitKindToString):
+        (JSC::DFG::exitKindIsCountable):
+        * bytecode/GetByIdStatus.cpp:
+        (JSC::GetByIdStatus::computeForChain):
+        * bytecode/Instruction.h:
+        (Instruction):
+        (JSC::Instruction::Instruction):
+        * bytecode/Opcode.h:
+        (JSC):
+        (JSC::padOpcodeName):
+        * bytecode/Watchpoint.cpp: Added.
+        (JSC):
+        (JSC::Watchpoint::~Watchpoint):
+        (JSC::Watchpoint::correctLabels):
+        (JSC::Watchpoint::fire):
+        (JSC::WatchpointSet::WatchpointSet):
+        (JSC::WatchpointSet::~WatchpointSet):
+        (JSC::WatchpointSet::add):
+        (JSC::WatchpointSet::notifyWriteSlow):
+        (JSC::WatchpointSet::fireAllWatchpoints):
+        * bytecode/Watchpoint.h: Added.
+        (JSC):
+        (Watchpoint):
+        (JSC::Watchpoint::Watchpoint):
+        (JSC::Watchpoint::setDestination):
+        (WatchpointSet):
+        (JSC::WatchpointSet::isStillValid):
+        (JSC::WatchpointSet::hasBeenInvalidated):
+        (JSC::WatchpointSet::startWatching):
+        (JSC::WatchpointSet::notifyWrite):
+        (JSC::WatchpointSet::addressOfIsWatched):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::ResolveResult::checkValidity):
+        (JSC::BytecodeGenerator::addGlobalVar):
+        (JSC::BytecodeGenerator::BytecodeGenerator):
+        (JSC::BytecodeGenerator::resolve):
+        (JSC::BytecodeGenerator::emitResolve):
+        (JSC::BytecodeGenerator::emitResolveWithBase):
+        (JSC::BytecodeGenerator::emitResolveWithThis):
+        (JSC::BytecodeGenerator::emitGetStaticVar):
+        (JSC::BytecodeGenerator::emitPutStaticVar):
+        * bytecompiler/BytecodeGenerator.h:
+        (BytecodeGenerator):
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::FunctionCallResolveNode::emitBytecode):
+        (JSC::PostfixResolveNode::emitBytecode):
+        (JSC::PrefixResolveNode::emitBytecode):
+        (JSC::ReadModifyResolveNode::emitBytecode):
+        (JSC::AssignResolveNode::emitBytecode):
+        (JSC::ConstDeclNode::emitCodeSingle):
+        * dfg/DFGAbstractState.cpp:
+        (JSC::DFG::AbstractState::execute):
+        (JSC::DFG::AbstractState::clobberStructures):
+        * dfg/DFGAbstractState.h:
+        (AbstractState):
+        (JSC::DFG::AbstractState::didClobber):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleInlining):
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGCCallHelpers.h:
+        (CCallHelpers):
+        (JSC::DFG::CCallHelpers::setupArguments):
+        * dfg/DFGCSEPhase.cpp:
+        (JSC::DFG::CSEPhase::globalVarWatchpointElimination):
+        (CSEPhase):
+        (JSC::DFG::CSEPhase::globalVarStoreElimination):
+        (JSC::DFG::CSEPhase::performNodeCSE):
+        * dfg/DFGCapabilities.h:
+        (JSC::DFG::canCompileOpcode):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::run):
+        * dfg/DFGCorrectableJumpPoint.h:
+        (JSC::DFG::CorrectableJumpPoint::isSet):
+        (CorrectableJumpPoint):
+        * dfg/DFGJITCompiler.cpp:
+        (JSC::DFG::JITCompiler::linkOSRExits):
+        (JSC::DFG::JITCompiler::link):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasIdentifierNumberForCheck):
+        (Node):
+        (JSC::DFG::Node::identifierNumberForCheck):
+        (JSC::DFG::Node::hasRegisterPointer):
+        * dfg/DFGNodeType.h:
+        (DFG):
+        * dfg/DFGOSRExit.cpp:
+        (JSC::DFG::OSRExit::OSRExit):
+        * dfg/DFGOSRExit.h:
+        (OSRExit):
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        (JSC::DFG::SpeculativeJIT::appendCall):
+        (SpeculativeJIT):
+        (JSC::DFG::SpeculativeJIT::speculationWatchpoint):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * interpreter/Interpreter.cpp:
+        (JSC::Interpreter::privateExecute):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        (JSC::JIT::privateCompileSlowCases):
+        * jit/JIT.h:
+        * jit/JITPropertyAccess.cpp:
+        (JSC::JIT::emit_op_put_global_var_check):
+        (JSC):
+        (JSC::JIT::emitSlow_op_put_global_var_check):
+        * jit/JITPropertyAccess32_64.cpp:
+        (JSC::JIT::emit_op_put_global_var_check):
+        (JSC):
+        (JSC::JIT::emitSlow_op_put_global_var_check):
+        * jit/JITStubs.cpp:
+        (JSC::DEFINE_STUB_FUNCTION):
+        (JSC):
+        * jit/JITStubs.h:
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        (LLInt):
+        * llint/LLIntSlowPaths.h:
+        (LLInt):
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * runtime/JSObject.cpp:
+        (JSC::JSObject::removeDirect):
+        * runtime/JSObject.h:
+        (JSObject):
+        * runtime/JSSymbolTableObject.h:
+        (JSC::symbolTableGet):
+        (JSC::symbolTablePut):
+        (JSC::symbolTablePutWithAttributes):
+        * runtime/SymbolTable.cpp: Added.
+        (JSC):
+        (JSC::SymbolTableEntry::copySlow):
+        (JSC::SymbolTableEntry::freeFatEntrySlow):
+        (JSC::SymbolTableEntry::couldBeWatched):
+        (JSC::SymbolTableEntry::attemptToWatch):
+        (JSC::SymbolTableEntry::addressOfIsWatched):
+        (JSC::SymbolTableEntry::addWatchpoint):
+        (JSC::SymbolTableEntry::notifyWriteSlow):
+        (JSC::SymbolTableEntry::inflateSlow):
+        * runtime/SymbolTable.h:
+        (JSC):
+        (SymbolTableEntry):
+        (Fast):
+        (JSC::SymbolTableEntry::Fast::Fast):
+        (JSC::SymbolTableEntry::Fast::isNull):
+        (JSC::SymbolTableEntry::Fast::getIndex):
+        (JSC::SymbolTableEntry::Fast::isReadOnly):
+        (JSC::SymbolTableEntry::Fast::getAttributes):
+        (JSC::SymbolTableEntry::Fast::isFat):
+        (JSC::SymbolTableEntry::SymbolTableEntry):
+        (JSC::SymbolTableEntry::~SymbolTableEntry):
+        (JSC::SymbolTableEntry::operator=):
+        (JSC::SymbolTableEntry::isNull):
+        (JSC::SymbolTableEntry::getIndex):
+        (JSC::SymbolTableEntry::getFast):
+        (JSC::SymbolTableEntry::getAttributes):
+        (JSC::SymbolTableEntry::isReadOnly):
+        (JSC::SymbolTableEntry::watchpointSet):
+        (JSC::SymbolTableEntry::notifyWrite):
+        (FatEntry):
+        (JSC::SymbolTableEntry::FatEntry::FatEntry):
+        (JSC::SymbolTableEntry::isFat):
+        (JSC::SymbolTableEntry::fatEntry):
+        (JSC::SymbolTableEntry::inflate):
+        (JSC::SymbolTableEntry::bits):
+        (JSC::SymbolTableEntry::freeFatEntry):
+        (JSC::SymbolTableEntry::pack):
+        (JSC::SymbolTableEntry::isValidIndex):
+
 2012-06-13  Sheriff Bot  <webkit.review.bot@gmail.com>
 
         Unreviewed, rolling out r120172.
index aaf8b37..83c85ed 100644 (file)
@@ -133,6 +133,8 @@ javascriptcore_sources += \
        Source/JavaScriptCore/bytecode/ValueProfile.h \
        Source/JavaScriptCore/bytecode/ValueRecovery.h \
        Source/JavaScriptCore/bytecode/VirtualRegister.h \
+       Source/JavaScriptCore/bytecode/Watchpoint.cpp \
+       Source/JavaScriptCore/bytecode/Watchpoint.h \
        Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp \
        Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h \
        Source/JavaScriptCore/bytecompiler/Label.h \
@@ -603,6 +605,7 @@ javascriptcore_sources += \
        Source/JavaScriptCore/runtime/Structure.cpp \
        Source/JavaScriptCore/runtime/Structure.h \
        Source/JavaScriptCore/runtime/StructureTransitionTable.h \
+       Source/JavaScriptCore/runtime/SymbolTable.cpp \
        Source/JavaScriptCore/runtime/SymbolTable.h \
        Source/JavaScriptCore/runtime/Terminator.h \
        Source/JavaScriptCore/runtime/TimeoutChecker.cpp \
index a2900b5..365038c 100755 (executable)
@@ -181,6 +181,7 @@ EXPORTS
     ?finishCreation@RegExpObject@JSC@@IAEXPAVJSGlobalObject@2@@Z
     ?finishCreation@StringObject@JSC@@IAEXAAVJSGlobalData@2@PAVJSString@2@@Z
     ?focus@Profile@JSC@@QAEXPBVProfileNode@2@@Z
+    ?freeFatEntrySlow@SymbolTableEntry@JSC@@AAEXXZ
     ?from@Identifier@JSC@@SA?AV12@PAVExecState@2@H@Z
     ?from@Identifier@JSC@@SA?AV12@PAVExecState@2@I@Z
     ?functionGetter@PropertySlot@JSC@@ABE?AVJSValue@2@PAVExecState@2@@Z
@@ -246,6 +247,7 @@ EXPORTS
     ?name@JSFunction@JSC@@QAEABVUString@2@PAVExecState@2@@Z
     ?neuter@ArrayBufferView@WTF@@MAEXXZ
     ?newUninitialized@CString@WTF@@SA?AV12@IAAPAD@Z
+    ?notifyWriteSlow@SymbolTableEntry@JSC@@AAEXXZ
     ?nullptr@@3Vnullptr_t@std@@A
     ?number@String@WTF@@SA?AV12@NII@Z
     ?number@UString@JSC@@SA?AV12@H@Z
index 0a3fab8..96c3159 100644 (file)
                                >
                        </File>
                        <File
+                               RelativePath="..\..\runtime\SymbolTable.cpp"
+                               >
+                       </File>
+                       <File
                                RelativePath="..\..\runtime\SymbolTable.h"
                                >
                        </File>
                                RelativePath="..\..\bytecode\ValueProfile.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\..\bytecode\Watchpoint.cpp"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\..\bytecode\Watchpoint.h"
+                               >
+                       </File>
                </Filter>
                <Filter
                        Name="debugger"
index 497c899..ea5b577 100644 (file)
                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 */; };
                0F919D11157F332C004A4E7D /* JSSegmentedVariableObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F919D0F157F3327004A4E7D /* JSSegmentedVariableObject.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0F919D2515853CE0004A4E7D /* Watchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F919D2215853CDE004A4E7D /* Watchpoint.cpp */; };
+               0F919D2615853CE3004A4E7D /* Watchpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F919D2315853CDE004A4E7D /* Watchpoint.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0F919D2815856773004A4E7D /* SymbolTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F919D2715856770004A4E7D /* SymbolTable.cpp */; };
                0F93329D14CA7DC30085F3C6 /* CallLinkStatus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F93329314CA7DC10085F3C6 /* CallLinkStatus.cpp */; };
                0F93329E14CA7DC50085F3C6 /* CallLinkStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F93329414CA7DC10085F3C6 /* CallLinkStatus.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F93329F14CA7DCA0085F3C6 /* GetByIdStatus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F93329514CA7DC10085F3C6 /* GetByIdStatus.cpp */; };
                7E4EE70F0EBB7A5B005934AA /* StructureChain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7E4EE70E0EBB7A5B005934AA /* StructureChain.cpp */; };
                7EFF00640EC05A9A00AA7C93 /* NodeInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 7EFF00630EC05A9A00AA7C93 /* NodeInfo.h */; };
                840480131021A1D9008E7F01 /* JSAPIValueWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = BC0894D60FAFBA2D00001865 /* JSAPIValueWrapper.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               860161E30F3A83C100F84710 /* AbstractMacroAssembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 860161DF0F3A83C100F84710 /* AbstractMacroAssembler.h */; };
-               860161E40F3A83C100F84710 /* MacroAssemblerX86.h in Headers */ = {isa = PBXBuildFile; fileRef = 860161E00F3A83C100F84710 /* MacroAssemblerX86.h */; };
-               860161E50F3A83C100F84710 /* MacroAssemblerX86_64.h in Headers */ = {isa = PBXBuildFile; fileRef = 860161E10F3A83C100F84710 /* MacroAssemblerX86_64.h */; };
-               860161E60F3A83C100F84710 /* MacroAssemblerX86Common.h in Headers */ = {isa = PBXBuildFile; fileRef = 860161E20F3A83C100F84710 /* MacroAssemblerX86Common.h */; };
+               860161E30F3A83C100F84710 /* AbstractMacroAssembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 860161DF0F3A83C100F84710 /* AbstractMacroAssembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               860161E40F3A83C100F84710 /* MacroAssemblerX86.h in Headers */ = {isa = PBXBuildFile; fileRef = 860161E00F3A83C100F84710 /* MacroAssemblerX86.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               860161E50F3A83C100F84710 /* MacroAssemblerX86_64.h in Headers */ = {isa = PBXBuildFile; fileRef = 860161E10F3A83C100F84710 /* MacroAssemblerX86_64.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               860161E60F3A83C100F84710 /* MacroAssemblerX86Common.h in Headers */ = {isa = PBXBuildFile; fileRef = 860161E20F3A83C100F84710 /* MacroAssemblerX86Common.h */; settings = {ATTRIBUTES = (Private, ); }; };
                8604F505143CE1C200B295F5 /* JSGlobalThis.h in Headers */ = {isa = PBXBuildFile; fileRef = 8604F503143CE1C100B295F5 /* JSGlobalThis.h */; settings = {ATTRIBUTES = (Private, ); }; };
                860BD801148EA6F200112B2F /* Intrinsic.h in Headers */ = {isa = PBXBuildFile; fileRef = 86BF642A148DB2B5004DE36A /* Intrinsic.h */; settings = {ATTRIBUTES = (Private, ); }; };
                8612E4CD152389EC00C836BE /* MatchResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 8612E4CB1522918400C836BE /* MatchResult.h */; settings = {ATTRIBUTES = (Private, ); }; };
                869D04AF1193B54D00803475 /* CachedTranscendentalFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 869D04AE1193B54D00803475 /* CachedTranscendentalFunction.h */; settings = {ATTRIBUTES = (Private, ); }; };
                869EBCB70E8C6D4A008722CC /* ResultType.h in Headers */ = {isa = PBXBuildFile; fileRef = 869EBCB60E8C6D4A008722CC /* ResultType.h */; settings = {ATTRIBUTES = (Private, ); }; };
                86A90ED00EE7D51F00AB350D /* JITArithmetic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86A90ECF0EE7D51F00AB350D /* JITArithmetic.cpp */; };
-               86ADD1450FDDEA980006EEC2 /* ARMv7Assembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 86ADD1430FDDEA980006EEC2 /* ARMv7Assembler.h */; };
-               86ADD1460FDDEA980006EEC2 /* MacroAssemblerARMv7.h in Headers */ = {isa = PBXBuildFile; fileRef = 86ADD1440FDDEA980006EEC2 /* MacroAssemblerARMv7.h */; };
+               86ADD1450FDDEA980006EEC2 /* ARMv7Assembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 86ADD1430FDDEA980006EEC2 /* ARMv7Assembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               86ADD1460FDDEA980006EEC2 /* MacroAssemblerARMv7.h in Headers */ = {isa = PBXBuildFile; fileRef = 86ADD1440FDDEA980006EEC2 /* MacroAssemblerARMv7.h */; settings = {ATTRIBUTES = (Private, ); }; };
                86AE64A8135E5E1C00963012 /* MacroAssemblerSH4.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86AE64A5135E5E1C00963012 /* MacroAssemblerSH4.cpp */; };
-               86AE64A9135E5E1C00963012 /* MacroAssemblerSH4.h in Headers */ = {isa = PBXBuildFile; fileRef = 86AE64A6135E5E1C00963012 /* MacroAssemblerSH4.h */; };
-               86AE64AA135E5E1C00963012 /* SH4Assembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 86AE64A7135E5E1C00963012 /* SH4Assembler.h */; };
+               86AE64A9135E5E1C00963012 /* MacroAssemblerSH4.h in Headers */ = {isa = PBXBuildFile; fileRef = 86AE64A6135E5E1C00963012 /* MacroAssemblerSH4.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               86AE64AA135E5E1C00963012 /* SH4Assembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 86AE64A7135E5E1C00963012 /* SH4Assembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
                86AE6C4D136A11E400963012 /* DFGFPRInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 86AE6C4B136A11E400963012 /* DFGFPRInfo.h */; };
                86AE6C4E136A11E400963012 /* DFGGPRInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 86AE6C4C136A11E400963012 /* DFGGPRInfo.h */; };
                86B5826714D2796C00A9C306 /* CodeProfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86B5822E14D2373B00A9C306 /* CodeProfile.cpp */; };
                86BB09C1138E381B0056702F /* DFGRepatch.h in Headers */ = {isa = PBXBuildFile; fileRef = 86BB09BF138E381B0056702F /* DFGRepatch.h */; };
                86C36EEA0EE1289D00B3DF59 /* MacroAssembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C36EE90EE1289D00B3DF59 /* MacroAssembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
                86C568E011A213EE0007F7F0 /* MacroAssemblerARM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86C568DD11A213EE0007F7F0 /* MacroAssemblerARM.cpp */; };
-               86C568E111A213EE0007F7F0 /* MacroAssemblerMIPS.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C568DE11A213EE0007F7F0 /* MacroAssemblerMIPS.h */; };
-               86C568E211A213EE0007F7F0 /* MIPSAssembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C568DF11A213EE0007F7F0 /* MIPSAssembler.h */; };
+               86C568E111A213EE0007F7F0 /* MacroAssemblerMIPS.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C568DE11A213EE0007F7F0 /* MacroAssemblerMIPS.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               86C568E211A213EE0007F7F0 /* MIPSAssembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C568DF11A213EE0007F7F0 /* MIPSAssembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
                86CA032E1038E8440028A609 /* Executable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86CA032D1038E8440028A609 /* Executable.cpp */; };
                86CAFEE31035DDE60028A609 /* Executable.h in Headers */ = {isa = PBXBuildFile; fileRef = 86CAFEE21035DDE60028A609 /* Executable.h */; settings = {ATTRIBUTES = (Private, ); }; };
                86CC85A10EE79A4700288682 /* JITInlineMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = 86CC85A00EE79A4700288682 /* JITInlineMethods.h */; };
                86CC85C40EE7A89400288682 /* JITPropertyAccess.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86CC85C30EE7A89400288682 /* JITPropertyAccess.cpp */; };
                86CCEFDE0F413F8900FD7F9E /* JITCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 86CCEFDD0F413F8900FD7F9E /* JITCode.h */; settings = {ATTRIBUTES = (Private, ); }; };
                86D3B2C310156BDE002865E7 /* ARMAssembler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86D3B2BF10156BDE002865E7 /* ARMAssembler.cpp */; };
-               86D3B2C410156BDE002865E7 /* ARMAssembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B2C010156BDE002865E7 /* ARMAssembler.h */; };
-               86D3B2C510156BDE002865E7 /* AssemblerBufferWithConstantPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B2C110156BDE002865E7 /* AssemblerBufferWithConstantPool.h */; };
-               86D3B2C610156BDE002865E7 /* MacroAssemblerARM.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B2C210156BDE002865E7 /* MacroAssemblerARM.h */; };
-               86D3B3C310159D7F002865E7 /* LinkBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B3C110159D7F002865E7 /* LinkBuffer.h */; };
-               86D3B3C410159D7F002865E7 /* RepatchBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B3C210159D7F002865E7 /* RepatchBuffer.h */; };
+               86D3B2C410156BDE002865E7 /* ARMAssembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B2C010156BDE002865E7 /* ARMAssembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               86D3B2C510156BDE002865E7 /* AssemblerBufferWithConstantPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B2C110156BDE002865E7 /* AssemblerBufferWithConstantPool.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               86D3B2C610156BDE002865E7 /* MacroAssemblerARM.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B2C210156BDE002865E7 /* MacroAssemblerARM.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               86D3B3C310159D7F002865E7 /* LinkBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B3C110159D7F002865E7 /* LinkBuffer.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               86D3B3C410159D7F002865E7 /* RepatchBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B3C210159D7F002865E7 /* RepatchBuffer.h */; settings = {ATTRIBUTES = (Private, ); }; };
                86DB64640F95C6FC00D7D921 /* ExecutableAllocatorFixedVMPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86DB64630F95C6FC00D7D921 /* ExecutableAllocatorFixedVMPool.cpp */; };
-               86E116B10FE75AC800B512BC /* CodeLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E116B00FE75AC800B512BC /* CodeLocation.h */; };
+               86E116B10FE75AC800B512BC /* CodeLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E116B00FE75AC800B512BC /* CodeLocation.h */; settings = {ATTRIBUTES = (Private, ); }; };
                86E85539111B9968001AF51E /* JSStringBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E85538111B9968001AF51E /* JSStringBuilder.h */; };
                86EBF2FF1560F06A008E9222 /* NameConstructor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86EBF2F91560F036008E9222 /* NameConstructor.cpp */; };
                86EBF3001560F06A008E9222 /* NameConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = 86EBF2FA1560F036008E9222 /* NameConstructor.h */; };
                95F6E6950E5B5F970091E860 /* JSProfilerPrivate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95988BA90E477BEC00D28D4D /* JSProfilerPrivate.cpp */; };
                960097A60EBABB58007A7297 /* LabelScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 960097A50EBABB58007A7297 /* LabelScope.h */; };
                960626960FB8EC02009798AB /* JITStubCall.h in Headers */ = {isa = PBXBuildFile; fileRef = 960626950FB8EC02009798AB /* JITStubCall.h */; };
-               9688CB150ED12B4E001D649F /* AssemblerBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 9688CB130ED12B4E001D649F /* AssemblerBuffer.h */; };
-               9688CB160ED12B4E001D649F /* X86Assembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 9688CB140ED12B4E001D649F /* X86Assembler.h */; };
+               9688CB150ED12B4E001D649F /* AssemblerBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 9688CB130ED12B4E001D649F /* AssemblerBuffer.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               9688CB160ED12B4E001D649F /* X86Assembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 9688CB140ED12B4E001D649F /* X86Assembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
                969A07230ED1CE3300F1F681 /* BytecodeGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 969A07210ED1CE3300F1F681 /* BytecodeGenerator.h */; };
                969A072A0ED1CE6900F1F681 /* Label.h in Headers */ = {isa = PBXBuildFile; fileRef = 969A07270ED1CE6900F1F681 /* Label.h */; };
                969A072B0ED1CE6900F1F681 /* RegisterID.h in Headers */ = {isa = PBXBuildFile; fileRef = 969A07280ED1CE6900F1F681 /* RegisterID.h */; };
                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>"; };
                0F919D0F157F3327004A4E7D /* JSSegmentedVariableObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSSegmentedVariableObject.h; sourceTree = "<group>"; };
+               0F919D2215853CDE004A4E7D /* Watchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Watchpoint.cpp; sourceTree = "<group>"; };
+               0F919D2315853CDE004A4E7D /* Watchpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Watchpoint.h; sourceTree = "<group>"; };
+               0F919D2715856770004A4E7D /* SymbolTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SymbolTable.cpp; sourceTree = "<group>"; };
                0F93329314CA7DC10085F3C6 /* CallLinkStatus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CallLinkStatus.cpp; sourceTree = "<group>"; };
                0F93329414CA7DC10085F3C6 /* CallLinkStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallLinkStatus.h; sourceTree = "<group>"; };
                0F93329514CA7DC10085F3C6 /* GetByIdStatus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GetByIdStatus.cpp; sourceTree = "<group>"; };
                                7E4EE70E0EBB7A5B005934AA /* StructureChain.cpp */,
                                7E4EE7080EBB7963005934AA /* StructureChain.h */,
                                BC9041470EB9250900FE26FA /* StructureTransitionTable.h */,
+                               0F919D2715856770004A4E7D /* SymbolTable.cpp */,
                                14A396A60CD2933100B5B4FF /* SymbolTable.h */,
                                97F6903A1169DF7F00A6BB46 /* Terminator.h */,
                                14A42E3D0F4F60EE00599099 /* TimeoutChecker.cpp */,
                                0F963B3613FC6FDE0002D9B2 /* ValueProfile.h */,
                                0F426A451460CBAB00131F8F /* ValueRecovery.h */,
                                0F426A461460CBAB00131F8F /* VirtualRegister.h */,
+                               0F919D2215853CDE004A4E7D /* Watchpoint.cpp */,
+                               0F919D2315853CDE004A4E7D /* Watchpoint.h */,
                        );
                        path = bytecode;
                        sourceTree = "<group>";
                                0F16015E156198C900C2587C /* DFGArgumentsSimplificationPhase.h in Headers */,
                                0F919D0D157EE0A2004A4E7D /* JSSymbolTableObject.h in Headers */,
                                0F919D11157F332C004A4E7D /* JSSegmentedVariableObject.h in Headers */,
+                               0F919D2615853CE3004A4E7D /* Watchpoint.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                C25F8BCD157544A900245B71 /* IncrementalSweeper.cpp in Sources */,
                                0F919D0C157EE09F004A4E7D /* JSSymbolTableObject.cpp in Sources */,
                                0F919D10157F3329004A4E7D /* JSSegmentedVariableObject.cpp in Sources */,
+                               0F919D2515853CE0004A4E7D /* Watchpoint.cpp in Sources */,
+                               0F919D2815856773004A4E7D /* SymbolTable.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index 2a21163..ec82012 100644 (file)
@@ -62,10 +62,11 @@ SOURCES += \
     bytecode/MethodOfGettingAValueProfile.cpp \
     bytecode/Opcode.cpp \
     bytecode/PolymorphicPutByIdList.cpp \
-    bytecode/SpeculatedType.cpp \
     bytecode/PutByIdStatus.cpp \
     bytecode/SamplingTool.cpp \
+    bytecode/SpeculatedType.cpp \
     bytecode/StructureStubInfo.cpp \
+    bytecode/Watchpoint.cpp \
     bytecompiler/BytecodeGenerator.cpp \
     bytecompiler/NodesCodegen.cpp \
     heap/CopiedSpace.cpp \
@@ -236,6 +237,7 @@ SOURCES += \
     runtime/StringRecursionChecker.cpp \
     runtime/StructureChain.cpp \
     runtime/Structure.cpp \
+    runtime/SymbolTable.cpp \
     runtime/TimeoutChecker.cpp \
     runtime/UString.cpp \
     tools/CodeProfile.cpp \
index 0cbe799..95c812c 100644 (file)
@@ -483,6 +483,12 @@ public:
         JumpLinkType m_linkType : 8;
         Condition m_condition : 16;
     };
+    
+    ARMv7Assembler()
+        : m_indexOfLastWatchpoint(INT_MIN)
+        , m_indexOfTailOfLastWatchpoint(INT_MIN)
+    {
+    }
 
 private:
 
@@ -1820,10 +1826,25 @@ public:
     {
         m_formatter.oneWordOp8Imm8(OP_NOP_T1, 0);
     }
+    
+    AssemblerLabel labelForWatchpoint()
+    {
+        AssemblerLabel result = m_formatter.label();
+        if (static_cast<int>(result.m_offset) != m_indexOfLastWatchpoint)
+            result = label();
+        m_indexOfLastWatchpoint = result.m_offset;
+        m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize();
+        return result;
+    }
 
     AssemblerLabel label()
     {
-        return m_formatter.label();
+        AssemblerLabel result = m_formatter.label();
+        while (UNLIKELY(static_cast<int>(result.m_offset) < m_indexOfTailOfLastWatchpoint)) {
+            nop();
+            result = m_formatter.label();
+        }
+        return result;
     }
     
     AssemblerLabel align(int alignment)
@@ -2067,6 +2088,30 @@ public:
     {
         return reinterpret_cast<void*>(readInt32(where));
     }
+    
+    static void replaceWithJump(void* instructionStart, void* to)
+    {
+        ASSERT(!(bitwise_cast<uintptr_t>(instructionStart) & 1));
+        ASSERT(!(bitwise_cast<uintptr_t>(to) & 1));
+        uint16_t* ptr = reinterpret_cast<uint16_t*>(instructionStart) + 2;
+        
+        // Ensure that we're not in one of those errata-triggering thingies. If we are, then
+        // prepend a nop.
+        bool spansTwo4K = ((reinterpret_cast<intptr_t>(ptr) & 0xfff) == 0x002);
+        
+        if (spansTwo4K) {
+            ptr[-2] = OP_NOP_T1;
+            ptr++;
+        }
+
+        linkJumpT4(ptr, to);
+        cacheFlush(ptr - 2, sizeof(uint16_t) * 2);
+    }
+    
+    static ptrdiff_t maxJumpReplacementSize()
+    {
+        return 6;
+    }
 
     unsigned debugOffset() { return m_formatter.debugOffset(); }
 
@@ -2604,6 +2649,8 @@ private:
 
     Vector<LinkRecord> m_jumpsToLink;
     Vector<int32_t> m_offsets;
+    int m_indexOfLastWatchpoint;
+    int m_indexOfTailOfLastWatchpoint;
 };
 
 } // namespace JSC
index a0039cb..27b8a58 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2012 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -48,6 +48,7 @@ namespace JSC {
 
 class LinkBuffer;
 class RepatchBuffer;
+class Watchpoint;
 namespace DFG {
 class CorrectableJumpPoint;
 }
@@ -70,7 +71,6 @@ public:
     // The following types are used as operands to MacroAssembler operations,
     // describing immediate  and memory operands to the instructions to be planted.
 
-
     enum Scale {
         TimesOne,
         TimesTwo,
@@ -279,6 +279,7 @@ public:
         friend class Jump;
         friend class MacroAssemblerCodeRef;
         friend class LinkBuffer;
+        friend class Watchpoint;
 
     public:
         Label()
@@ -559,6 +560,13 @@ public:
         return Label(this);
     }
     
+    Label watchpointLabel()
+    {
+        Label result;
+        result.m_label = m_assembler.labelForWatchpoint();
+        return result;
+    }
+    
     Label align()
     {
         m_assembler.align(16);
@@ -655,18 +663,6 @@ protected:
     {
         return AssemblerType::readPointer(dataLabelPtr.dataLocation());
     }
-    
-    static void unreachableForPlatform()
-    {
-#if COMPILER(CLANG)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wmissing-noreturn"
-        ASSERT_NOT_REACHED();
-#pragma clang diagnostic pop
-#else
-        ASSERT_NOT_REACHED();
-#endif
-    }
 };
 
 } // namespace JSC
index d1deef2..6bc1b39 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2012 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 #if ENABLE(ASSEMBLER)
 
+#include "ExecutableAllocator.h"
 #include "JITCompilationEffort.h"
 #include "JSGlobalData.h"
 #include "stdint.h"
 #include <string.h>
-#include <jit/ExecutableAllocator.h>
 #include <wtf/Assertions.h>
 #include <wtf/FastMalloc.h>
 #include <wtf/StdLibExtras.h>
index 1775cb4..8ea29e3 100644 (file)
@@ -514,6 +514,13 @@ public:
         return branchTest32(cond, ARMRegisters::S1, mask);
     }
 
+    Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1))
+    {
+        move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1);
+        load8(Address(ARMRegisters::S1), ARMRegisters::S1);
+        return branchTest32(cond, ARMRegisters::S1, mask);
+    }
+
     Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask)
     {
         ASSERT((cond == Zero) || (cond == NonZero));
@@ -1010,6 +1017,17 @@ public:
         return FunctionPtr(reinterpret_cast<void(*)()>(ARMAssembler::readCallTarget(call.dataLocation())));
     }
 
+    static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination)
+    {
+        ASSERT_NOT_REACHED();
+    }
+    
+    static ptrdiff_t maxJumpReplacementSize()
+    {
+        ASSERT_NOT_REACHED();
+        return 0;
+    }
+
 protected:
     ARMAssembler::Condition ARMCondition(RelationalCondition cond)
     {
index 3b62cb5..6c0feff 100644 (file)
@@ -608,7 +608,7 @@ public:
 
     void load8Signed(ImplicitAddress, RegisterID)
     {
-        unreachableForPlatform();
+        UNREACHABLE_FOR_PLATFORM();
     }
 
     void load8(BaseIndex address, RegisterID dest)
@@ -674,7 +674,7 @@ public:
     
     void load16Signed(ImplicitAddress, RegisterID)
     {
-        unreachableForPlatform();
+        UNREACHABLE_FOR_PLATFORM();
     }
 
     DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address)
@@ -1186,6 +1186,16 @@ public:
     {
         m_assembler.nop();
     }
+    
+    static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination)
+    {
+        ARMv7Assembler::replaceWithJump(instructionStart.dataLocation(), destination.dataLocation());
+    }
+    
+    static ptrdiff_t maxJumpReplacementSize()
+    {
+        return ARMv7Assembler::maxJumpReplacementSize();
+    }
 
     // Forwards / external control flow operations:
     //
@@ -1356,6 +1366,14 @@ public:
         return branchTest32(cond, addressTempRegister, mask);
     }
 
+    Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1))
+    {
+        // use addressTempRegister incase the branchTest8 we call uses dataTempRegister. :-/
+        move(TrustedImmPtr(address.m_ptr), addressTempRegister);
+        load8(Address(addressTempRegister), addressTempRegister);
+        return branchTest32(cond, addressTempRegister, mask);
+    }
+
     void jump(RegisterID target)
     {
         m_assembler.bx(target);
@@ -1679,12 +1697,14 @@ public:
 protected:
     ALWAYS_INLINE Jump jump()
     {
+        m_assembler.label(); // Force nop-padding if we're in the middle of a watchpoint.
         moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister);
         return Jump(m_assembler.bx(dataTempRegister), m_makeJumpPatchable ? ARMv7Assembler::JumpNoConditionFixedSize : ARMv7Assembler::JumpNoCondition);
     }
 
     ALWAYS_INLINE Jump makeBranch(ARMv7Assembler::Condition cond)
     {
+        m_assembler.label(); // Force nop-padding if we're in the middle of a watchpoint.
         m_assembler.it(cond, true, true);
         moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister);
         return Jump(m_assembler.bx(dataTempRegister), m_makeJumpPatchable ? ARMv7Assembler::JumpConditionFixedSize : ARMv7Assembler::JumpCondition, cond);
index f9c3457..5adcf9b 100644 (file)
@@ -1149,6 +1149,13 @@ public:
         return branchTest32(cond, dataTempRegister, mask);
     }
 
+    Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1))
+    {
+        move(TrustedImmPtr(address.m_ptr), dataTempRegister);
+        load8(Address(dataTempRegister), dataTempRegister);
+        return branchTest32(cond, dataTempRegister, mask);
+    }
+
     Jump jump()
     {
         return branchEqual(MIPSRegisters::zero, MIPSRegisters::zero);
@@ -1868,6 +1875,17 @@ public:
         return FunctionPtr(reinterpret_cast<void(*)()>(MIPSAssembler::readCallTarget(call.dataLocation())));
     }
 
+    static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination)
+    {
+        ASSERT_NOT_REACHED();
+    }
+    
+    static ptrdiff_t maxJumpReplacementSize()
+    {
+        ASSERT_NOT_REACHED();
+        return 0;
+    }
+
 private:
     // If m_fixedWidth is true, we will generate a fixed number of instructions.
     // Otherwise, we can emit any number of instructions.
index c132ad6..badf35f 100644 (file)
@@ -1293,6 +1293,16 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest)
         return jmp;
     }
 
+    Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1))
+    {
+        RegisterID addressTempRegister = claimScratch();
+        move(TrustedImmPtr(address.m_ptr), addressTempRegister);
+        load8(Address(addressTempRegister), addressTempRegister);
+        Jump jmp = branchTest32(cond, addressTempRegister, mask);
+        releaseScratch(addressTempRegister);
+        return jmp;
+    }
+
     void signExtend32ToPtr(RegisterID src, RegisterID dest)
     {
         if (src != dest)
@@ -1971,6 +1981,17 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest)
         return FunctionPtr(reinterpret_cast<void(*)()>(SH4Assembler::readCallTarget(call.dataLocation())));
     }
 
+    static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination)
+    {
+        ASSERT_NOT_REACHED();
+    }
+    
+    static ptrdiff_t maxJumpReplacementSize()
+    {
+        ASSERT_NOT_REACHED();
+        return 0;
+    }
+
 protected:
     SH4Assembler::Condition SH4Condition(RelationalCondition cond)
     {
index 088fe19..3ea40c9 100644 (file)
@@ -51,6 +51,7 @@ public:
     using MacroAssemblerX86Common::loadDouble;
     using MacroAssemblerX86Common::storeDouble;
     using MacroAssemblerX86Common::convertInt32ToDouble;
+    using MacroAssemblerX86Common::branchTest8;
 
     void add32(TrustedImm32 imm, RegisterID src, RegisterID dest)
     {
@@ -165,6 +166,16 @@ public:
         m_assembler.movl_i32r(initialValue.asIntptr(), dest);
         return DataLabelPtr(this);
     }
+    
+    Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1))
+    {
+        ASSERT(mask.m_value >= -128 && mask.m_value <= 255);
+        if (mask.m_value == -1)
+            m_assembler.cmpb_im(0, address.m_ptr);
+        else
+            m_assembler.testb_im(mask.m_value, address.m_ptr);
+        return Jump(m_assembler.jCC(x86Condition(cond)));
+    }
 
     Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0))
     {
index e398dcd..432489d 100644 (file)
@@ -1408,6 +1408,16 @@ public:
         m_assembler.nop();
     }
 
+    static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination)
+    {
+        X86Assembler::replaceWithJump(instructionStart.executableAddress(), destination.executableAddress());
+    }
+    
+    static ptrdiff_t maxJumpReplacementSize()
+    {
+        return X86Assembler::maxJumpReplacementSize();
+    }
+
 protected:
     X86Assembler::Condition x86Condition(RelationalCondition cond)
     {
index 41479f9..fa95b33 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2012 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -513,6 +513,12 @@ public:
         MacroAssemblerX86Common::move(addr, scratchRegister);
         return MacroAssemblerX86Common::branchTest8(cond, BaseIndex(scratchRegister, address.base, TimesOne), mask);
     }
+    
+    Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1))
+    {
+        MacroAssemblerX86Common::move(TrustedImmPtr(address.m_ptr), scratchRegister);
+        return MacroAssemblerX86Common::branchTest8(cond, Address(scratchRegister), mask);
+    }
 
     static bool supportsFloatingPoint() { return true; }
     // See comment on MacroAssemblerARMv7::supportsFloatingPointTruncate()
index ff8d25b..8c56069 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2012 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -247,6 +247,8 @@ private:
 public:
 
     X86Assembler()
+        : m_indexOfLastWatchpoint(INT_MIN)
+        , m_indexOfTailOfLastWatchpoint(INT_MIN)
     {
     }
 
@@ -798,6 +800,14 @@ public:
         m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, index, scale, offset);
         m_formatter.immediate8(imm);
     }
+    
+#if CPU(X86)
+    void cmpb_im(int imm, const void* addr)
+    {
+        m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, addr);
+        m_formatter.immediate8(imm);
+    }
+#endif
 
     void cmpl_im(int imm, int offset, RegisterID base, RegisterID index, int scale)
     {
@@ -948,6 +958,14 @@ public:
         m_formatter.immediate8(imm);
     }
 
+#if CPU(X86)
+    void testb_im(int imm, const void* addr)
+    {
+        m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, addr);
+        m_formatter.immediate8(imm);
+    }
+#endif
+
     void testl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale)
     {
         m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset);
@@ -1702,10 +1720,25 @@ public:
     {
         return m_formatter.codeSize();
     }
+    
+    AssemblerLabel labelForWatchpoint()
+    {
+        AssemblerLabel result = m_formatter.label();
+        if (static_cast<int>(result.m_offset) != m_indexOfLastWatchpoint)
+            result = label();
+        m_indexOfLastWatchpoint = result.m_offset;
+        m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize();
+        return result;
+    }
 
     AssemblerLabel label()
     {
-        return m_formatter.label();
+        AssemblerLabel result = m_formatter.label();
+        while (UNLIKELY(static_cast<int>(result.m_offset) < m_indexOfTailOfLastWatchpoint)) {
+            nop();
+            result = m_formatter.label();
+        }
+        return result;
     }
 
     AssemblerLabel align(int alignment)
@@ -1787,6 +1820,20 @@ public:
         return reinterpret_cast<void**>(where)[-1];
     }
 
+    static void replaceWithJump(void* instructionStart, void* to)
+    {
+        uint8_t* ptr = reinterpret_cast<uint8_t*>(instructionStart);
+        uint8_t* dstPtr = reinterpret_cast<uint8_t*>(to);
+        intptr_t distance = (intptr_t)(dstPtr - (ptr + 5));
+        ptr[0] = static_cast<uint8_t>(OP_JMP_rel32);
+        *reinterpret_cast<int32_t*>(ptr + 1) = static_cast<int32_t>(distance);
+    }
+    
+    static ptrdiff_t maxJumpReplacementSize()
+    {
+        return 5;
+    }
+    
     static unsigned getCallReturnOffset(AssemblerLabel call)
     {
         ASSERT(call.isSet());
@@ -2339,6 +2386,8 @@ private:
 
         AssemblerBuffer m_buffer;
     } m_formatter;
+    int m_indexOfLastWatchpoint;
+    int m_indexOfTailOfLastWatchpoint;
 };
 
 } // namespace JSC
index 9100109..5f4006c 100644 (file)
@@ -228,6 +228,7 @@ void CodeBlock::printGetByIdOp(ExecState* exec, int location, Vector<Instruction
     it += 5;
 }
 
+#if ENABLE(JIT) || ENABLE(LLINT) // unused in some configurations
 static void dumpStructure(const char* name, ExecState* exec, Structure* structure, Identifier& ident)
 {
     if (!structure)
@@ -239,7 +240,9 @@ static void dumpStructure(const char* name, ExecState* exec, Structure* structur
     if (offset != notFound)
         dataLog(" (offset = %lu)", static_cast<unsigned long>(offset));
 }
+#endif
 
+#if ENABLE(JIT) // unused when not ENABLE(JIT), leading to silly warnings
 static void dumpChain(ExecState* exec, StructureChain* chain, Identifier& ident)
 {
     dataLog("chain = %p: [", chain);
@@ -255,6 +258,7 @@ static void dumpChain(ExecState* exec, StructureChain* chain, Identifier& ident)
     }
     dataLog("]");
 }
+#endif
 
 void CodeBlock::printGetByIdCacheStatus(ExecState* exec, int location)
 {
@@ -265,6 +269,8 @@ void CodeBlock::printGetByIdCacheStatus(ExecState* exec, int location)
     
     Identifier& ident = identifier(instruction[3].u.operand);
     
+    UNUSED_PARAM(ident); // tell the compiler to shut up in certain platform configurations.
+    
 #if ENABLE(LLINT)
     Structure* structure = instruction[4].u.structure.get();
     dataLog(" llint(");
@@ -928,12 +934,28 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
             it++;
             break;
         }
+        case op_get_global_var_watchable: {
+            int r0 = (++it)->u.operand;
+            WriteBarrier<Unknown>* registerPointer = (++it)->u.registerPointer;
+            dataLog("[%4d] get_global_var_watchable\t %s, g%d(%p)\n", location, registerName(exec, r0).data(), m_globalObject->findRegisterIndex(registerPointer), registerPointer);
+            it++;
+            it++;
+            break;
+        }
         case op_put_global_var: {
             WriteBarrier<Unknown>* registerPointer = (++it)->u.registerPointer;
             int r0 = (++it)->u.operand;
             dataLog("[%4d] put_global_var\t g%d(%p), %s\n", location, m_globalObject->findRegisterIndex(registerPointer), registerPointer, registerName(exec, r0).data());
             break;
         }
+        case op_put_global_var_check: {
+            WriteBarrier<Unknown>* registerPointer = (++it)->u.registerPointer;
+            int r0 = (++it)->u.operand;
+            dataLog("[%4d] put_global_var_check\t g%d(%p), %s\n", location, m_globalObject->findRegisterIndex(registerPointer), registerPointer, registerName(exec, r0).data());
+            it++;
+            it++;
+            break;
+        }
         case op_resolve_base: {
             int r0 = (++it)->u.operand;
             int id0 = (++it)->u.operand;
@@ -1306,7 +1328,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
         }
         case op_call_put_result: {
             int r0 = (++it)->u.operand;
-            dataLog("[%4d] op_call_put_result\t\t %s\n", location, registerName(exec, r0).data());
+            dataLog("[%4d] call_put_result\t\t %s\n", location, registerName(exec, r0).data());
             it++;
             break;
         }
index 7420e23..aa724fc 100644 (file)
@@ -62,6 +62,7 @@
 #include "UString.h"
 #include "UnconditionalFinalizer.h"
 #include "ValueProfile.h"
+#include "Watchpoint.h"
 #include <wtf/RefCountedArray.h>
 #include <wtf/FastAllocBase.h>
 #include <wtf/PassOwnPtr.h>
@@ -273,10 +274,12 @@ namespace JSC {
             return result;
         }
         
-        void appendOSRExit(const DFG::OSRExit& osrExit)
+        unsigned appendOSRExit(const DFG::OSRExit& osrExit)
         {
             createDFGDataIfNecessary();
+            unsigned result = m_dfgData->osrExit.size();
             m_dfgData->osrExit.append(osrExit);
+            return result;
         }
         
         DFG::OSRExit& lastOSRExit()
@@ -284,10 +287,20 @@ namespace JSC {
             return m_dfgData->osrExit.last();
         }
         
-        void appendSpeculationRecovery(const DFG::SpeculationRecovery& recovery)
+        unsigned appendSpeculationRecovery(const DFG::SpeculationRecovery& recovery)
         {
             createDFGDataIfNecessary();
+            unsigned result = m_dfgData->speculationRecovery.size();
             m_dfgData->speculationRecovery.append(recovery);
+            return result;
+        }
+        
+        unsigned appendWatchpoint(const Watchpoint& watchpoint)
+        {
+            createDFGDataIfNecessary();
+            unsigned result = m_dfgData->watchpoints.size();
+            m_dfgData->watchpoints.append(watchpoint);
+            return result;
         }
         
         unsigned numberOfOSRExits()
@@ -304,6 +317,13 @@ namespace JSC {
             return m_dfgData->speculationRecovery.size();
         }
         
+        unsigned numberOfWatchpoints()
+        {
+            if (!m_dfgData)
+                return 0;
+            return m_dfgData->watchpoints.size();
+        }
+        
         DFG::OSRExit& osrExit(unsigned index)
         {
             return m_dfgData->osrExit[index];
@@ -314,6 +334,11 @@ namespace JSC {
             return m_dfgData->speculationRecovery[index];
         }
         
+        Watchpoint& watchpoint(unsigned index)
+        {
+            return m_dfgData->watchpoints[index];
+        }
+        
         void appendWeakReference(JSCell* target)
         {
             createDFGDataIfNecessary();
@@ -1238,6 +1263,7 @@ namespace JSC {
             Vector<DFG::OSREntryData> osrEntry;
             SegmentedVector<DFG::OSRExit, 8> osrExit;
             Vector<DFG::SpeculationRecovery> speculationRecovery;
+            SegmentedVector<Watchpoint, 1, 0> watchpoints;
             Vector<WeakReferenceTransition> transitions;
             Vector<WriteBarrier<JSCell> > weakReferences;
             bool mayBeExecuting;
index 09f9ee0..d751ce6 100644 (file)
@@ -41,6 +41,7 @@ enum ExitKind {
     InadequateCoverage, // We exited because we ended up in code that didn't have profiling coverage.
     ArgumentsEscaped, // We exited because arguments escaped but we didn't expect them to.
     Uncountable, // We exited for none of the above reasons, and we should not count it. Most uses of this should be viewed as a FIXME.
+    UncountableWatchpoint // We exited because of a watchpoint, which isn't counted because watchpoints do tracking themselves.
 };
 
 inline const char* exitKindToString(ExitKind kind)
@@ -58,6 +59,12 @@ inline const char* exitKindToString(ExitKind kind)
         return "NegativeZero";
     case InadequateCoverage:
         return "InadequateCoverage";
+    case ArgumentsEscaped:
+        return "ArgumentsEscaped";
+    case Uncountable:
+        return "Uncountable";
+    case UncountableWatchpoint:
+        return "UncountableWatchpoint";
     default:
         return "Unknown";
     }
@@ -70,6 +77,7 @@ inline bool exitKindIsCountable(ExitKind kind)
         ASSERT_NOT_REACHED();
     case BadType:
     case Uncountable:
+    case UncountableWatchpoint:
         return false;
     default:
         return true;
index 6a9b2b6..a62a43f 100644 (file)
@@ -98,7 +98,7 @@ void GetByIdStatus::computeForChain(GetByIdStatus& result, CodeBlock* profiledBl
     UNUSED_PARAM(profiledBlock);
     UNUSED_PARAM(ident);
     UNUSED_PARAM(structure);
-    ASSERT_NOT_REACHED();
+    UNREACHABLE_FOR_PLATFORM();
 #endif
 }
 
index af45510..6e76512 100644 (file)
@@ -193,6 +193,8 @@ namespace JSC {
         Instruction(ValueProfile* profile) { u.profile = profile; }
         
         Instruction(WriteBarrier<Unknown>* registerPointer) { u.registerPointer = registerPointer; }
+        
+        Instruction(bool* predicatePointer) { u.predicatePointer = predicatePointer; }
 
         union {
             Opcode opcode;
@@ -205,6 +207,7 @@ namespace JSC {
             LLIntCallLinkInfo* callLinkInfo;
             ValueProfile* profile;
             void* pointer;
+            bool* predicatePointer;
         } u;
         
     private:
index aa83d9b..e0cff16 100644 (file)
@@ -100,7 +100,9 @@ namespace JSC {
         macro(op_get_scoped_var, 5) /* has value profiling */ \
         macro(op_put_scoped_var, 4) \
         macro(op_get_global_var, 4) /* has value profiling */ \
+        macro(op_get_global_var_watchable, 5) /* has value profiling */ \
         macro(op_put_global_var, 3) \
+        macro(op_put_global_var_check, 5) \
         macro(op_resolve_base, 5) /* has value profiling */ \
         macro(op_ensure_property_exists, 3) \
         macro(op_resolve_with_base, 5) /* has value profiling */ \
diff --git a/Source/JavaScriptCore/bytecode/Watchpoint.cpp b/Source/JavaScriptCore/bytecode/Watchpoint.cpp
new file mode 100644 (file)
index 0000000..ea61d40
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012 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.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "Watchpoint.h"
+
+#include "LinkBuffer.h"
+
+namespace JSC {
+
+Watchpoint::~Watchpoint()
+{
+    if (isOnList())
+        remove();
+}
+
+#if ENABLE(JIT)
+void Watchpoint::correctLabels(LinkBuffer& linkBuffer)
+{
+    MacroAssembler::Label label;
+    label.m_label.m_offset = m_source;
+    m_source = bitwise_cast<uintptr_t>(linkBuffer.locationOf(label).dataLocation());
+    label.m_label.m_offset = m_destination;
+    m_destination = bitwise_cast<uintptr_t>(linkBuffer.locationOf(label).dataLocation());
+}
+#endif
+
+void Watchpoint::fire()
+{
+#if ENABLE(JIT)
+    MacroAssembler::replaceWithJump(
+        CodeLocationLabel(bitwise_cast<void*>(m_source)),
+        CodeLocationLabel(bitwise_cast<void*>(m_destination)));
+    if (isOnList())
+        remove();
+#else
+    UNREACHABLE_FOR_PLATFORM();
+#endif
+}
+
+WatchpointSet::WatchpointSet()
+    : m_isWatched(false)
+    , m_isInvalidated(false)
+{
+}
+
+WatchpointSet::~WatchpointSet()
+{
+    // Fire all watchpoints. This is necessary because it is possible, say with
+    // structure watchpoints, for the watchpoint set owner to die while the
+    // watchpoint owners are still live.
+    fireAllWatchpoints();
+}
+
+void WatchpointSet::add(Watchpoint* watchpoint)
+{
+    if (!watchpoint)
+        return;
+    m_set.push(watchpoint);
+    m_isWatched = true;
+}
+
+void WatchpointSet::notifyWriteSlow()
+{
+    ASSERT(m_isWatched);
+    
+    fireAllWatchpoints();
+    m_isWatched = false;
+    m_isInvalidated = true;
+}
+
+void WatchpointSet::fireAllWatchpoints()
+{
+    while (!m_set.isEmpty())
+        m_set.begin()->fire();
+}
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/bytecode/Watchpoint.h b/Source/JavaScriptCore/bytecode/Watchpoint.h
new file mode 100644 (file)
index 0000000..335575c
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2012 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.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 Watchpoint_h
+#define Watchpoint_h
+
+#include "CodeLocation.h"
+#include "MacroAssembler.h"
+#include <wtf/RefCounted.h>
+#include <wtf/SentinelLinkedList.h>
+
+namespace JSC {
+
+class Watchpoint : public BasicRawSentinelNode<Watchpoint> {
+public:
+    Watchpoint()
+        : m_source(std::numeric_limits<uintptr_t>::max())
+        , m_destination(std::numeric_limits<uintptr_t>::max())
+    {
+    }
+
+#if ENABLE(JIT)
+    Watchpoint(MacroAssembler::Label source)
+        : m_source(source.m_label.m_offset)
+        , m_destination(std::numeric_limits<uintptr_t>::max())
+    {
+    }
+    
+    void setDestination(MacroAssembler::Label destination)
+    {
+        m_destination = destination.m_label.m_offset;
+    }
+    
+    void correctLabels(LinkBuffer&);
+#endif
+    
+    ~Watchpoint();
+    
+    void fire();
+    
+private:
+    uintptr_t m_source;
+    uintptr_t m_destination;
+};
+
+class WatchpointSet : public RefCounted<WatchpointSet> {
+public:
+    WatchpointSet();
+    ~WatchpointSet();
+    
+    bool isStillValid() const { return !m_isInvalidated; }
+    bool hasBeenInvalidated() const { return m_isInvalidated; }
+    
+    // As a convenience, this will ignore 0. That's because code paths in the DFG
+    // that create speculation watchpoints may choose to bail out if speculation
+    // had already been terminated.
+    void add(Watchpoint*);
+    
+    // Force the watchpoint set to behave as if it was being watched even if no
+    // watchpoints have been installed. This will result in invalidation if the
+    // watchpoint would have fired. That's a pretty good indication that you
+    // probably don't want to set watchpoints, since we typically don't want to
+    // set watchpoints that we believe will actually be fired.
+    void startWatching() { m_isWatched = true; }
+    
+    void notifyWrite()
+    {
+        if (!m_isWatched)
+            return;
+        notifyWriteSlow();
+    }
+    
+    bool* addressOfIsWatched() { return &m_isWatched; }
+    
+    void notifyWriteSlow(); // Call only if you've checked isWatched.
+    
+private:
+    void fireAllWatchpoints();
+    
+    SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint> > m_set;
+    bool m_isWatched;
+    bool m_isInvalidated;
+};
+
+} // namespace JSC
+
+#endif // Watchpoint_h
+
index c4f7faf..8969a7f 100644 (file)
@@ -148,6 +148,7 @@ void ResolveResult::checkValidity()
         return;
     case IndexedGlobal:
     case ReadOnlyIndexedGlobal:
+    case WatchedIndexedGlobal:
     case DynamicIndexedGlobal:
     case DynamicReadOnlyIndexedGlobal:
         ASSERT(m_index != missingSymbolMarker());
@@ -216,13 +217,19 @@ bool BytecodeGenerator::addVar(const Identifier& ident, bool isConstant, Registe
     return true;
 }
 
-int BytecodeGenerator::addGlobalVar(const Identifier& ident, bool isConstant)
+int BytecodeGenerator::addGlobalVar(
+    const Identifier& ident, ConstantMode constantMode, FunctionMode functionMode)
 {
+    UNUSED_PARAM(functionMode);
     int index = symbolTable().size();
-    SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0);
+    SymbolTableEntry newEntry(index, (constantMode == IsConstant) ? ReadOnly : 0);
+    if (functionMode == IsFunctionToSpecialize)
+        newEntry.attemptToWatch();
     SymbolTable::AddResult result = symbolTable().add(ident.impl(), newEntry);
-    if (!result.isNewEntry)
+    if (!result.isNewEntry) {
+        result.iterator->second.notifyWrite();
         index = result.iterator->second.getIndex();
+    }
     return index;
 }
 
@@ -289,17 +296,23 @@ BytecodeGenerator::BytecodeGenerator(ProgramNode* programNode, ScopeChainNode* s
 
     for (size_t i = 0; i < functionStack.size(); ++i) {
         FunctionBodyNode* function = functionStack[i];
-        globalObject->removeDirect(*m_globalData, function->ident()); // Newly declared functions overwrite existing properties.
-
+        bool propertyDidExist = 
+            globalObject->removeDirect(*m_globalData, function->ident()); // Newly declared functions overwrite existing properties.
+        
         JSValue value = JSFunction::create(exec, makeFunction(exec, function), scopeChain);
-        int index = addGlobalVar(function->ident(), false);
+        int index = addGlobalVar(
+            function->ident(), IsVariable,
+            !propertyDidExist ? IsFunctionToSpecialize : NotFunctionOrNotSpecializable);
         globalObject->registerAt(index).set(*m_globalData, globalObject, value);
     }
 
     for (size_t i = 0; i < varStack.size(); ++i) {
         if (globalObject->hasProperty(exec, *varStack[i].first))
             continue;
-        addGlobalVar(*varStack[i].first, varStack[i].second & DeclarationStacks::IsConstant);
+        addGlobalVar(
+            *varStack[i].first,
+            (varStack[i].second & DeclarationStacks::IsConstant) ? IsConstant : IsVariable,
+            NotFunctionOrNotSpecializable);
     }
 }
 
@@ -1202,7 +1215,9 @@ ResolveResult BytecodeGenerator::resolve(const Identifier& property)
             if (++iter == end) {
                 if (flags & ResolveResult::DynamicFlag)
                     return ResolveResult::dynamicIndexedGlobalResolve(entry.getIndex(), depth, currentScope, flags);
-                return ResolveResult::indexedGlobalResolve(entry.getIndex(), currentScope, flags);
+                return ResolveResult::indexedGlobalResolve(
+                    entry.getIndex(), currentScope,
+                    flags | (entry.couldBeWatched() ? ResolveResult::WatchedFlag : 0));
             }
 #if !ASSERT_DISABLED
             if (JSActivation* activation = jsDynamicCast<JSActivation*>(currentVariableObject))
@@ -1296,7 +1311,7 @@ bool BytecodeGenerator::shouldAvoidResolveGlobal()
 RegisterID* BytecodeGenerator::emitResolve(RegisterID* dst, const ResolveResult& resolveResult, const Identifier& property)
 {
     if (resolveResult.isStatic())
-        return emitGetStaticVar(dst, resolveResult);
+        return emitGetStaticVar(dst, resolveResult, property);
     
     if (resolveResult.isGlobal() && !shouldAvoidResolveGlobal()) {
 #if ENABLE(JIT)
@@ -1379,7 +1394,7 @@ RegisterID* BytecodeGenerator::emitResolveWithBase(RegisterID* baseDst, Register
 
         if (resolveResult.isStatic()) {
             // Directly index the property lookup across multiple scopes.
-            emitGetStaticVar(propDst, resolveResult);
+            emitGetStaticVar(propDst, resolveResult, property);
             return baseDst;
         }
 
@@ -1418,7 +1433,7 @@ RegisterID* BytecodeGenerator::emitResolveWithThis(RegisterID* baseDst, Register
 {
     if (resolveResult.isStatic()) {
         emitLoad(baseDst, jsUndefined());
-        emitGetStaticVar(propDst, resolveResult);
+        emitGetStaticVar(propDst, resolveResult, property);
         return baseDst;
     }
 
@@ -1436,7 +1451,7 @@ RegisterID* BytecodeGenerator::emitResolveWithThis(RegisterID* baseDst, Register
     return emitResolve(propDst, resolveResult, property);
 }
 
-RegisterID* BytecodeGenerator::emitGetStaticVar(RegisterID* dst, const ResolveResult& resolveResult)
+RegisterID* BytecodeGenerator::emitGetStaticVar(RegisterID* dst, const ResolveResult& resolveResult, const Identifier& identifier)
 {
     ValueProfile* profile = 0;
 
@@ -1472,13 +1487,24 @@ RegisterID* BytecodeGenerator::emitGetStaticVar(RegisterID* dst, const ResolveRe
         instructions().append(profile);
         return dst;
 
+    case ResolveResult::WatchedIndexedGlobal:
+        // Skip the peephole for now. It's not clear that it's profitable given
+        // the DFG's capabilities, and the fact that if it's watchable then we
+        // don't expect to see any put_global_var's anyway.
+        profile = emitProfiledOpcode(op_get_global_var_watchable);
+        instructions().append(dst->index());
+        instructions().append(resolveResult.registerPointer());
+        instructions().append(addConstant(identifier)); // For the benefit of the DFG.
+        instructions().append(profile);
+        return dst;
+
     default:
         ASSERT_NOT_REACHED();
         return 0;
     }
 }
 
-RegisterID* BytecodeGenerator::emitPutStaticVar(const ResolveResult& resolveResult, RegisterID* value)
+RegisterID* BytecodeGenerator::emitPutStaticVar(const ResolveResult& resolveResult, const Identifier& identifier, RegisterID* value)
 {
     switch (resolveResult.type()) {
     case ResolveResult::Register:
@@ -1499,6 +1525,14 @@ RegisterID* BytecodeGenerator::emitPutStaticVar(const ResolveResult& resolveResu
         instructions().append(resolveResult.registerPointer());
         instructions().append(value->index());
         return value;
+        
+    case ResolveResult::WatchedIndexedGlobal:
+        emitOpcode(op_put_global_var_check);
+        instructions().append(resolveResult.registerPointer());
+        instructions().append(value->index());
+        instructions().append(jsCast<JSGlobalObject*>(resolveResult.globalObject())->symbolTable().get(identifier.impl()).addressOfIsWatched());
+        instructions().append(addConstant(identifier));
+        return value;
 
     default:
         ASSERT_NOT_REACHED();
index 53256f3..8c3fc7b 100644 (file)
@@ -111,7 +111,9 @@ namespace JSC {
             // The resolved binding is immutable.
             ReadOnlyFlag = 0x20,
             // The base object is the global object.
-            GlobalFlag = 0x40
+            GlobalFlag = 0x40,
+            // The property is being watched, so writes should be special.
+            WatchedFlag = 0x80
         };
         enum Type {
             // The property is local, and stored in a register.
@@ -131,6 +133,8 @@ namespace JSC {
             // binding created with "var" at the top level. At runtime we'll
             // just index into the global object.
             IndexedGlobal = IndexedFlag | GlobalFlag | StaticFlag,
+            // Like IndexedGlobal, but the property is being watched.
+            WatchedIndexedGlobal = IndexedFlag | GlobalFlag | StaticFlag | WatchedFlag,
             // Like IndexedGlobal, but the property is also read-only, like NaN,
             // Infinity, or undefined.
             ReadOnlyIndexedGlobal = IndexedFlag | ReadOnlyFlag | GlobalFlag | StaticFlag,
@@ -441,8 +445,8 @@ namespace JSC {
         RegisterID* emitTypeOf(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_typeof, dst, src); }
         RegisterID* emitIn(RegisterID* dst, RegisterID* property, RegisterID* base) { return emitBinaryOp(op_in, dst, property, base, OperandTypes()); }
 
-        RegisterID* emitGetStaticVar(RegisterID* dst, const ResolveResult&);
-        RegisterID* emitPutStaticVar(const ResolveResult&, RegisterID* value);
+        RegisterID* emitGetStaticVar(RegisterID* dst, const ResolveResult&, const Identifier&);
+        RegisterID* emitPutStaticVar(const ResolveResult&, const Identifier&, RegisterID* value);
 
         RegisterID* emitResolve(RegisterID* dst, const ResolveResult&, const Identifier& property);
         RegisterID* emitResolveBase(RegisterID* dst, const ResolveResult&, const Identifier& property);
@@ -574,7 +578,9 @@ namespace JSC {
         }
 
         // Returns the index of the added var.
-        int addGlobalVar(const Identifier&, bool isConstant);
+        enum ConstantMode { IsConstant, IsVariable };
+        enum FunctionMode { IsFunctionToSpecialize, NotFunctionOrNotSpecializable };
+        int addGlobalVar(const Identifier&, ConstantMode, FunctionMode);
 
         void addParameter(const Identifier&, int parameterIndex);
         
index 4f113f7..d243e8c 100644 (file)
@@ -430,7 +430,7 @@ RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator,
     if (resolveResult.isStatic()) {
         RefPtr<RegisterID> func = generator.newTemporary();
         CallArguments callArguments(generator, m_args);
-        generator.emitGetStaticVar(func.get(), resolveResult);
+        generator.emitGetStaticVar(func.get(), resolveResult, m_ident);
         generator.emitLoad(callArguments.thisRegister(), jsUndefined());
         return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), callArguments, divot(), startOffset(), endOffset());
     }
@@ -618,7 +618,7 @@ RegisterID* PostfixResolveNode::emitBytecode(BytecodeGenerator& generator, Regis
     }
 
     if (resolveResult.isStatic() && !resolveResult.isReadOnly()) {
-        RefPtr<RegisterID> value = generator.emitGetStaticVar(generator.newTemporary(), resolveResult);
+        RefPtr<RegisterID> value = generator.emitGetStaticVar(generator.newTemporary(), resolveResult, m_ident);
         RegisterID* oldValue;
         if (dst == generator.ignoredResult()) {
             oldValue = 0;
@@ -626,7 +626,7 @@ RegisterID* PostfixResolveNode::emitBytecode(BytecodeGenerator& generator, Regis
         } else {
             oldValue = emitPostIncOrDec(generator, generator.finalDestination(dst), value.get(), m_operator);
         }
-        generator.emitPutStaticVar(resolveResult, value.get());
+        generator.emitPutStaticVar(resolveResult, m_ident, value.get());
         return oldValue;
     }
     
@@ -803,9 +803,9 @@ RegisterID* PrefixResolveNode::emitBytecode(BytecodeGenerator& generator, Regist
     }
 
     if (resolveResult.isStatic() && !resolveResult.isReadOnly()) {
-        RefPtr<RegisterID> propDst = generator.emitGetStaticVar(generator.tempDestination(dst), resolveResult);
+        RefPtr<RegisterID> propDst = generator.emitGetStaticVar(generator.tempDestination(dst), resolveResult, m_ident);
         emitPreIncOrDec(generator, propDst.get(), m_operator);
-        generator.emitPutStaticVar(resolveResult, propDst.get());
+        generator.emitPutStaticVar(resolveResult, m_ident, propDst.get());
         return generator.moveToDestinationIfNeeded(dst, propDst.get());
     }
 
@@ -1226,9 +1226,9 @@ RegisterID* ReadModifyResolveNode::emitBytecode(BytecodeGenerator& generator, Re
     }
 
     if (resolveResult.isStatic() && !resolveResult.isReadOnly()) {
-        RefPtr<RegisterID> src1 = generator.emitGetStaticVar(generator.tempDestination(dst), resolveResult);
+        RefPtr<RegisterID> src1 = generator.emitGetStaticVar(generator.tempDestination(dst), resolveResult, m_ident);
         RegisterID* result = emitReadModifyAssignment(generator, generator.finalDestination(dst, src1.get()), src1.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor()));
-        generator.emitPutStaticVar(resolveResult, result);
+        generator.emitPutStaticVar(resolveResult, m_ident, result);
         return result;
     }
 
@@ -1256,7 +1256,7 @@ RegisterID* AssignResolveNode::emitBytecode(BytecodeGenerator& generator, Regist
         if (dst == generator.ignoredResult())
             dst = 0;
         RegisterID* value = generator.emitNode(dst, m_right);
-        generator.emitPutStaticVar(resolveResult, value);
+        generator.emitPutStaticVar(resolveResult, m_ident, value);
         return value;
     }
 
@@ -1361,7 +1361,7 @@ RegisterID* ConstDeclNode::emitCodeSingle(BytecodeGenerator& generator)
     RefPtr<RegisterID> value = m_init ? generator.emitNode(m_init) : generator.emitLoad(0, jsUndefined());
 
     if (resolveResult.isStatic())
-        return generator.emitPutStaticVar(resolveResult, value.get());
+        return generator.emitPutStaticVar(resolveResult, m_ident, value.get());
     
     if (generator.codeType() != EvalCode)
         return value.get();
index 4186120..51516f9 100644 (file)
@@ -219,7 +219,9 @@ bool AbstractState::execute(unsigned indexInBlock)
 {
     ASSERT(m_block);
     ASSERT(m_isValid);
-        
+    
+    m_didClobber = false;
+    
     NodeIndex nodeIndex = m_block->at(indexInBlock);
     Node& node = m_graph[nodeIndex];
         
@@ -237,8 +239,12 @@ bool AbstractState::execute(unsigned indexInBlock)
             
     case GetLocal: {
         VariableAccessData* variableAccessData = node.variableAccessData();
+        if (variableAccessData->prediction() == SpecNone) {
+            m_isValid = false;
+            node.setCanExit(true);
+            break;
+        }
         bool canExit = false;
-        canExit |= variableAccessData->prediction() == SpecNone;
         AbstractValue value = m_variables.operand(variableAccessData->local());
         if (!variableAccessData->isCaptured()) {
             if (value.isClear())
@@ -1476,7 +1482,7 @@ bool AbstractState::execute(unsigned indexInBlock)
         forNode(node.child1()).filter(SpecFunction);
         // FIXME: Should be able to propagate the fact that we know what the function is.
         break;
-            
+        
     case PutById:
     case PutByIdDirect:
         node.setCanExit(true);
@@ -1488,8 +1494,13 @@ bool AbstractState::execute(unsigned indexInBlock)
         node.setCanExit(false);
         forNode(nodeIndex).makeTop();
         break;
+        
+    case GlobalVarWatchpoint:
+        node.setCanExit(true);
+        break;
             
     case PutGlobalVar:
+    case PutGlobalVarCheck:
         node.setCanExit(false);
         break;
             
@@ -1578,6 +1589,7 @@ inline void AbstractState::clobberStructures(unsigned indexInBlock)
     for (size_t i = m_variables.numberOfLocals(); i--;)
         m_variables.local(i).clobberStructures();
     m_haveStructures = false;
+    m_didClobber = true;
 }
 
 inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, AbstractValue& inVariable, NodeIndex nodeIndex)
index 68f16a1..9bb74cd 100644 (file)
@@ -193,6 +193,9 @@ public:
     // of Throw.
     bool execute(unsigned);
     
+    // Did the last executed node clobber the world?
+    bool didClobber() const { return m_didClobber; }
+    
     // Is the execution state still valid? This will be false if execute() has
     // returned false previously.
     bool isValid() const { return m_isValid; }
@@ -274,6 +277,7 @@ private:
     bool m_foundConstants;
     
     bool m_isValid;
+    bool m_didClobber;
     
     BranchDirection m_branchDirection; // This is only set for blocks that end in Branch and that execute to completion (i.e. m_isValid == true).
 };
index 6b8c9ce..5be5555 100644 (file)
@@ -1234,6 +1234,9 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c
     // Does the code block's size match the heuristics/requirements for being
     // an inline candidate?
     CodeBlock* profiledBlock = executable->profiledCodeBlockFor(kind);
+    if (!profiledBlock)
+        return false;
+    
     if (!mightInlineFunctionFor(profiledBlock, kind))
         return false;
     
@@ -2251,13 +2254,61 @@ bool ByteCodeParser::parseBlock(unsigned limit)
         case op_get_global_var: {
             SpeculatedType prediction = getPrediction();
             
+            JSGlobalObject* globalObject = m_inlineStackTop->m_codeBlock->globalObject();
+
             NodeIndex getGlobalVar = addToGraph(
                 GetGlobalVar,
-                OpInfo(m_inlineStackTop->m_codeBlock->globalObject()->assertRegisterIsInThisObject(currentInstruction[2].u.registerPointer)),
+                OpInfo(globalObject->assertRegisterIsInThisObject(currentInstruction[2].u.registerPointer)),
                 OpInfo(prediction));
             set(currentInstruction[1].u.operand, getGlobalVar);
             NEXT_OPCODE(op_get_global_var);
         }
+                    
+        case op_get_global_var_watchable: {
+            SpeculatedType prediction = getPrediction();
+            
+            JSGlobalObject* globalObject = m_inlineStackTop->m_codeBlock->globalObject();
+            
+            unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[currentInstruction[3].u.operand];
+            Identifier identifier = m_codeBlock->identifier(identifierNumber);
+            SymbolTableEntry entry = globalObject->symbolTable().get(identifier.impl());
+            if (!entry.couldBeWatched()) {
+                NodeIndex getGlobalVar = addToGraph(
+                    GetGlobalVar,
+                    OpInfo(globalObject->assertRegisterIsInThisObject(currentInstruction[2].u.registerPointer)),
+                    OpInfo(prediction));
+                set(currentInstruction[1].u.operand, getGlobalVar);
+                NEXT_OPCODE(op_get_global_var_watchable);
+            }
+            
+            // The watchpoint is still intact! This means that we will get notified if the
+            // current value in the global variable changes. So, we can inline that value.
+            // Moreover, currently we can assume that this value is a JSFunction*, which
+            // implies that it's a cell. This simplifies things, since in general we'd have
+            // to use a JSConstant for non-cells and a WeakJSConstant for cells. So instead
+            // of having both cases we just assert that the value is a cell.
+            
+            // NB. If it wasn't for CSE, GlobalVarWatchpoint would have no need for the
+            // register pointer. But CSE tracks effects on global variables by comparing
+            // register pointers. Because CSE executes multiple times while the backend
+            // executes once, we use the following performance trade-off:
+            // - The node refers directly to the register pointer to make CSE super cheap.
+            // - To perform backend code generation, the node only contains the identifier
+            //   number, from which it is possible to get (via a few average-time O(1)
+            //   lookups) to the WatchpointSet.
+            
+            addToGraph(
+                GlobalVarWatchpoint,
+                OpInfo(globalObject->assertRegisterIsInThisObject(currentInstruction[2].u.registerPointer)),
+                OpInfo(identifierNumber));
+            
+            JSValue specificValue = globalObject->registerAt(entry.getIndex()).get();
+            ASSERT(specificValue.isCell());
+            set(currentInstruction[1].u.operand,
+                addToGraph(WeakJSConstant, OpInfo(specificValue.asCell())));
+            
+            NEXT_OPCODE(op_get_global_var_watchable);
+        }
 
         case op_put_global_var: {
             NodeIndex value = get(currentInstruction[2].u.operand);
@@ -2268,6 +2319,28 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             NEXT_OPCODE(op_put_global_var);
         }
 
+        case op_put_global_var_check: {
+            NodeIndex value = get(currentInstruction[2].u.operand);
+            CodeBlock* codeBlock = m_inlineStackTop->m_codeBlock;
+            JSGlobalObject* globalObject = codeBlock->globalObject();
+            unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[currentInstruction[4].u.operand];
+            Identifier identifier = m_codeBlock->identifier(identifierNumber);
+            SymbolTableEntry entry = globalObject->symbolTable().get(identifier.impl());
+            if (!entry.couldBeWatched()) {
+                addToGraph(
+                    PutGlobalVar,
+                    OpInfo(globalObject->assertRegisterIsInThisObject(currentInstruction[1].u.registerPointer)),
+                    value);
+                NEXT_OPCODE(op_put_global_var_check);
+            }
+            addToGraph(
+                PutGlobalVarCheck,
+                OpInfo(codeBlock->globalObject()->assertRegisterIsInThisObject(currentInstruction[1].u.registerPointer)),
+                OpInfo(identifierNumber),
+                value);
+            NEXT_OPCODE(op_put_global_var_check);
+        }
+
         // === Block terminators. ===
 
         case op_jmp: {
index 5bc45d8..4cacd45 100644 (file)
@@ -94,6 +94,12 @@ public:
         addCallArgument(arg1);
         addCallArgument(arg2);
     }
+    
+    ALWAYS_INLINE void setupArguments(TrustedImmPtr arg1)
+    {
+        resetCallArguments();
+        addCallArgument(arg1);
+    }
 
     ALWAYS_INLINE void setupArgumentsExecState()
     {
@@ -433,6 +439,11 @@ public:
     {
         setupTwoStubArgs<GPRInfo::argumentGPR0, GPRInfo::argumentGPR1>(arg1, arg2);
     }
+    
+    ALWAYS_INLINE void setupArguments(TrustedImmPtr arg1)
+    {
+        move(arg1, GPRInfo::argumentGPR0);
+    }
 
     ALWAYS_INLINE void setupArgumentsExecState()
     {
index f26de01..6f58b6a 100644 (file)
@@ -217,6 +217,29 @@ private:
         return NoNode;
     }
     
+    bool globalVarWatchpointElimination(WriteBarrier<Unknown>* registerPointer)
+    {
+        for (unsigned i = m_indexInBlock; i--;) {
+            NodeIndex index = m_currentBlock->at(i);
+            Node& node = m_graph[index];
+            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(index))
+                break;
+        }
+        return false;
+    }
+    
     NodeIndex globalVarStoreElimination(WriteBarrier<Unknown>* registerPointer)
     {
         for (unsigned i = m_indexInBlock; i--;) {
@@ -226,6 +249,7 @@ private:
                 continue;
             switch (node.op()) {
             case PutGlobalVar:
+            case PutGlobalVarCheck:
                 if (node.registerPointer() == registerPointer)
                     return index;
                 break;
@@ -985,7 +1009,13 @@ private:
             setReplacement(globalVarLoadElimination(node.registerPointer()));
             break;
             
+        case GlobalVarWatchpoint:
+            if (globalVarWatchpointElimination(node.registerPointer()))
+                eliminate();
+            break;
+            
         case PutGlobalVar:
+        case PutGlobalVarCheck:
             if (m_fixpointState == FixpointNotConverged)
                 break;
             eliminate(globalVarStoreElimination(node.registerPointer()));
index 694e886..027b0f7 100644 (file)
@@ -123,7 +123,9 @@ inline CapabilityLevel canCompileOpcode(OpcodeID opcodeID, CodeBlock*, Instructi
     case op_put_by_id_transition_direct:
     case op_put_by_id_transition_normal:
     case op_get_global_var:
+    case op_get_global_var_watchable:
     case op_put_global_var:
+    case op_put_global_var_check:
     case op_jmp:
     case op_loop:
     case op_jtrue:
index 7ab2528..9e6720c 100644 (file)
@@ -56,6 +56,9 @@ public:
                 continue;
             if (!block->cfaFoundConstants)
                 continue;
+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
+            dataLog("Constant folding considering Block #%u.\n", blockIndex);
+#endif
             state.beginBasicBlock(block);
             for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
                 if (!state.isValid())
@@ -90,7 +93,7 @@ public:
                 
                 state.execute(indexInBlock);
                 if (!node.shouldGenerate()
-                    || m_graph.clobbersWorld(node)
+                    || state.didClobber()
                     || node.hasConstant())
                     continue;
                 JSValue value = state.forNode(nodeIndex).value();
index bfa1496..93cb49c 100644 (file)
@@ -66,6 +66,11 @@ public:
 #endif
     }
     
+    bool isSet()
+    {
+        return m_codeOffset != std::numeric_limits<uint32_t>::max();
+    }
+    
     void switchToLateJump(MacroAssembler::PatchableJump check)
     {
 #ifndef NDEBUG
index 54b5aae..b8b33f2 100644 (file)
@@ -44,7 +44,11 @@ void JITCompiler::linkOSRExits()
 {
     for (unsigned i = 0; i < codeBlock()->numberOfOSRExits(); ++i) {
         OSRExit& exit = codeBlock()->osrExit(i);
-        exit.m_check.initialJump().link(this);
+        ASSERT(!exit.m_check.isSet() == (exit.m_watchpointIndex != std::numeric_limits<unsigned>::max()));
+        if (exit.m_watchpointIndex == std::numeric_limits<unsigned>::max())
+            exit.m_check.initialJump().link(this);
+        else
+            codeBlock()->watchpoint(exit.m_watchpointIndex).setDestination(label());
         jitAssertHasValidCallFrame();
         store32(TrustedImm32(i), &globalData()->osrExitIndex);
         exit.m_check.switchToLateJump(patchableJump());
@@ -190,6 +194,8 @@ void JITCompiler::link(LinkBuffer& linkBuffer)
         OSRExit& exit = codeBlock()->osrExit(i);
         linkBuffer.link(exit.m_check.lateJump(), target);
         exit.m_check.correctLateJump(linkBuffer);
+        if (exit.m_watchpointIndex != std::numeric_limits<unsigned>::max())
+            codeBlock()->watchpoint(exit.m_watchpointIndex).correctLabels(linkBuffer);
     }
     
     codeBlock()->shrinkToFit(CodeBlock::LateShrink);
index 29783c8..b96f69f 100644 (file)
@@ -430,9 +430,20 @@ struct Node {
         return m_opInfo;
     }
     
+    bool hasIdentifierNumberForCheck()
+    {
+        return op() == GlobalVarWatchpoint || op() == PutGlobalVarCheck;
+    }
+    
+    unsigned identifierNumberForCheck()
+    {
+        ASSERT(hasIdentifierNumberForCheck());
+        return m_opInfo2;
+    }
+    
     bool hasRegisterPointer()
     {
-        return op() == GetGlobalVar || op() == PutGlobalVar;
+        return op() == GetGlobalVar || op() == PutGlobalVar || op() == GlobalVarWatchpoint || op() == PutGlobalVarCheck;
     }
     
     WriteBarrier<Unknown>* registerPointer()
index 743f879..9a81bd3 100644 (file)
@@ -141,6 +141,8 @@ namespace JSC { namespace DFG {
     macro(PutScopedVar, NodeMustGenerate | NodeClobbersWorld) \
     macro(GetGlobalVar, NodeResultJS | NodeMustGenerate) \
     macro(PutGlobalVar, NodeMustGenerate) \
+    macro(GlobalVarWatchpoint, NodeMustGenerate) \
+    macro(PutGlobalVarCheck, NodeMustGenerate) \
     macro(CheckFunction, NodeMustGenerate) \
     \
     /* Optimizations for array mutation. */\
index bcb98a1..d0e0de9 100644 (file)
@@ -51,6 +51,7 @@ OSRExit::OSRExit(ExitKind kind, JSValueSource jsValueSource, MethodOfGettingAVal
     , m_codeOrigin(jit->m_codeOriginForOSR)
     , m_codeOriginForExitProfile(m_codeOrigin)
     , m_recoveryIndex(recoveryIndex)
+    , m_watchpointIndex(std::numeric_limits<unsigned>::max())
     , m_kind(kind)
     , m_count(0)
     , m_arguments(jit->m_arguments.size())
index 841fddd..683f260 100644 (file)
@@ -96,6 +96,7 @@ struct OSRExit {
     CodeOrigin m_codeOriginForExitProfile;
     
     unsigned m_recoveryIndex;
+    unsigned m_watchpointIndex;
     
     ExitKind m_kind;
     uint32_t m_count;
index ec3d4cc..06a1cf8 100644 (file)
@@ -924,6 +924,11 @@ void* DFG_OPERATION operationVirtualConstruct(ExecState* execCallee)
     return virtualFor(execCallee, CodeForConstruct);
 }
 
+void DFG_OPERATION operationNotifyGlobalVarWrite(WatchpointSet* watchpointSet)
+{
+    watchpointSet->notifyWrite();
+}
+
 EncodedJSValue DFG_OPERATION operationResolve(ExecState* exec, Identifier* propertyName)
 {
     JSGlobalData* globalData = &exec->globalData();
index 58aa68b..38166a8 100644 (file)
@@ -100,6 +100,7 @@ typedef void DFG_OPERATION (*V_DFGOperation_EJCI)(ExecState*, EncodedJSValue, JS
 typedef void DFG_OPERATION (*V_DFGOperation_EJJJ)(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue);
 typedef void DFG_OPERATION (*V_DFGOperation_EJPP)(ExecState*, EncodedJSValue, EncodedJSValue, void*);
 typedef void DFG_OPERATION (*V_DFGOperation_EPZJ)(ExecState*, void*, int32_t, EncodedJSValue);
+typedef void DFG_OPERATION (*V_DFGOperation_W)(WatchpointSet*);
 typedef void* DFG_OPERATION (*P_DFGOperation_E)(ExecState*);
 
 // These routines are provide callbacks out to C++ implementations of operations too complex to JIT.
@@ -116,6 +117,7 @@ EncodedJSValue DFG_OPERATION operationGetByIdProtoBuildList(ExecState*, EncodedJ
 EncodedJSValue DFG_OPERATION operationGetByIdOptimize(ExecState*, EncodedJSValue, Identifier*) WTF_INTERNAL;
 EncodedJSValue DFG_OPERATION operationCallCustomGetter(ExecState*, JSCell*, PropertySlot::GetValueFunc, Identifier*) WTF_INTERNAL;
 EncodedJSValue DFG_OPERATION operationCallGetter(ExecState*, JSCell*, JSCell*) WTF_INTERNAL;
+void DFG_OPERATION operationNotifyGlobalVarWrite(WatchpointSet* watchpointSet) WTF_INTERNAL;
 EncodedJSValue DFG_OPERATION operationResolve(ExecState*, Identifier*) WTF_INTERNAL;
 EncodedJSValue DFG_OPERATION operationResolveBase(ExecState*, Identifier*) WTF_INTERNAL;
 EncodedJSValue DFG_OPERATION operationResolveBaseStrictPut(ExecState*, Identifier*) WTF_INTERNAL;
index 86ee7a3..0d44afb 100644 (file)
@@ -491,7 +491,8 @@ private:
             break;
         }
             
-        case PutGlobalVar: {
+        case PutGlobalVar:
+        case PutGlobalVarCheck: {
             changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
             break;
         }
@@ -669,6 +670,7 @@ private:
         case TearOffArguments:
         case CheckNumber:
         case CheckArgumentsNotCreated:
+        case GlobalVarWatchpoint:
             changed |= mergeDefaultFlags(node);
             break;
             
index 74fe706..9337846 100644 (file)
@@ -1414,6 +1414,11 @@ public:
         m_jit.setupArgumentsWithExecState(arg1, TrustedImm32(arg2));
         return appendCallWithExceptionCheck(operation);
     }
+    JITCompiler::Call callOperation(V_DFGOperation_W operation, WatchpointSet* watchpointSet)
+    {
+        m_jit.setupArguments(TrustedImmPtr(watchpointSet));
+        return appendCall(operation);
+    }
     template<typename FunctionType, typename ArgumentType1>
     JITCompiler::Call callOperation(FunctionType operation, NoResultTag, ArgumentType1 arg1)
     {
@@ -1672,6 +1677,11 @@ public:
         m_jit.setupArgumentsWithExecState(arg1, arg2, EABI_32BIT_DUMMY_ARG arg3Payload, arg3Tag);
         return appendCallWithExceptionCheck(operation);
     }
+    JITCompiler::Call callOperation(V_DFGOperation_W operation, WatchpointSet* watchpointSet)
+    {
+        m_jit.setupArguments(TrustedImmPtr(watchpointSet));
+        return appendCall(operation);
+    }
     template<typename FunctionType, typename ArgumentType1>
     JITCompiler::Call callOperation(FunctionType operation, NoResultTag, ArgumentType1 arg1)
     {
@@ -1793,6 +1803,11 @@ public:
         m_jit.move(GPRInfo::returnValueGPR, result);
         return call;
     }
+    JITCompiler::Call appendCall(const FunctionPtr& function)
+    {
+        prepareForExternalCall();
+        return m_jit.appendCall(function);
+    }
     JITCompiler::Call appendCallWithExceptionCheckSetResult(const FunctionPtr& function, GPRReg result1, GPRReg result2)
     {
         JITCompiler::Call call = appendCallWithExceptionCheck(function);
@@ -2193,6 +2208,32 @@ public:
         ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
         speculationCheck(kind, jsValueSource, nodeUse.index(), jumpToFail, recovery);
     }
+    // Use this like you would use speculationCheck(), except that you don't pass it a jump
+    // (because you don't have to execute a branch; that's kind of the whole point), and you
+    // must register the returned Watchpoint with something relevant. In general, this should
+    // be used with extreme care. Use speculationCheck() unless you've got an amazing reason
+    // not to.
+    Watchpoint* speculationWatchpoint(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex)
+    {
+        if (!m_compileOkay)
+            return 0;
+        ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
+        OSRExit& exit = m_jit.codeBlock()->osrExit(
+            m_jit.codeBlock()->appendOSRExit(
+                OSRExit(kind, jsValueSource,
+                        m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex),
+                        JITCompiler::Jump(), this)));
+        exit.m_watchpointIndex = m_jit.codeBlock()->appendWatchpoint(
+            Watchpoint(m_jit.watchpointLabel()));
+        return &m_jit.codeBlock()->watchpoint(exit.m_watchpointIndex);
+    }
+    // The default for speculation watchpoints is that they're uncounted, because the
+    // act of firing a watchpoint invalidates it. So, future recompilations will not
+    // attempt to set this watchpoint again.
+    Watchpoint* speculationWatchpoint()
+    {
+        return speculationWatchpoint(UncountableWatchpoint, JSValueSource(), NoNode);
+    }
     void forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail, const ValueRecovery& valueRecovery)
     {
         ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
index 6e4b548..9cfa28f 100644 (file)
@@ -3596,6 +3596,62 @@ void SpeculativeJIT::compile(Node& node)
         break;
     }
 
+    case PutGlobalVarCheck: {
+        JSValueOperand value(this, node.child1());
+        
+        WatchpointSet* watchpointSet =
+            m_jit.globalObjectFor(node.codeOrigin)->symbolTable().get(
+                identifier(node.identifierNumberForCheck())->impl()).watchpointSet();
+        addSlowPathGenerator(
+            slowPathCall(
+                m_jit.branchTest8(
+                    JITCompiler::NonZero,
+                    JITCompiler::AbsoluteAddress(watchpointSet->addressOfIsWatched())),
+                this, operationNotifyGlobalVarWrite, NoResult, watchpointSet));
+        
+        if (Heap::isWriteBarrierEnabled()) {
+            GPRTemporary scratch(this);
+            GPRReg scratchReg = scratch.gpr();
+            
+            writeBarrier(m_jit.globalObjectFor(node.codeOrigin), value.tagGPR(), node.child1(), WriteBarrierForVariableAccess, scratchReg);
+        }
+
+        // FIXME: if we happen to have a spare register - and _ONLY_ if we happen to have
+        // a spare register - a good optimization would be to put the register pointer into
+        // a register and then do a zero offset store followed by a four-offset store (or
+        // vice-versa depending on endianness).
+        m_jit.store32(value.tagGPR(), node.registerPointer()->tagPointer());
+        m_jit.store32(value.payloadGPR(), node.registerPointer()->payloadPointer());
+
+        noResult(m_compileIndex);
+        break;
+    }
+        
+    case GlobalVarWatchpoint: {
+        m_jit.globalObjectFor(node.codeOrigin)->symbolTable().get(
+            identifier(node.identifierNumberForCheck())->impl()).addWatchpoint(
+                speculationWatchpoint());
+        
+#if DFG_ENABLE(JIT_ASSERT)
+        GPRTemporary scratch(this);
+        GPRReg scratchGPR = scratch.gpr();
+        m_jit.load32(node.registerPointer()->tagPointer(), scratchGPR);
+        JITCompiler::Jump notOK = m_jit.branch32(
+            JITCompiler::NotEqual, scratchGPR,
+            TrustedImm32(node.registerPointer()->get().tag()));
+        m_jit.load32(node.registerPointer()->payloadPointer(), scratchGPR);
+        JITCompiler::Jump ok = m_jit.branch32(
+            JITCompiler::Equal, scratchGPR,
+            TrustedImm32(node.registerPointer()->get().payload()));
+        notOK.link(&m_jit);
+        m_jit.breakpoint();
+        ok.link(&m_jit);
+#endif
+        
+        noResult(m_compileIndex);
+        break;
+    }
+
     case CheckHasInstance: {
         SpeculateCellOperand base(this, node.child1());
         GPRTemporary structure(this);
index c8c927d..48b3e9a 100644 (file)
@@ -3615,6 +3615,52 @@ void SpeculativeJIT::compile(Node& node)
         break;
     }
 
+    case PutGlobalVarCheck: {
+        JSValueOperand value(this, node.child1());
+        
+        WatchpointSet* watchpointSet =
+            m_jit.globalObjectFor(node.codeOrigin)->symbolTable().get(
+                identifier(node.identifierNumberForCheck())->impl()).watchpointSet();
+        addSlowPathGenerator(
+            slowPathCall(
+                m_jit.branchTest8(
+                    JITCompiler::NonZero,
+                    JITCompiler::AbsoluteAddress(watchpointSet->addressOfIsWatched())),
+                this, operationNotifyGlobalVarWrite, NoResult, watchpointSet));
+        
+        if (Heap::isWriteBarrierEnabled()) {
+            GPRTemporary scratch(this);
+            GPRReg scratchReg = scratch.gpr();
+            
+            writeBarrier(m_jit.globalObjectFor(node.codeOrigin), value.gpr(), node.child1(), WriteBarrierForVariableAccess, scratchReg);
+        }
+        
+        m_jit.storePtr(value.gpr(), node.registerPointer());
+
+        noResult(m_compileIndex);
+        break;
+    }
+        
+    case GlobalVarWatchpoint: {
+        m_jit.globalObjectFor(node.codeOrigin)->symbolTable().get(
+            identifier(node.identifierNumberForCheck())->impl()).addWatchpoint(
+                speculationWatchpoint());
+        
+#if DFG_ENABLE(JIT_ASSERT)
+        GPRTemporary scratch(this);
+        GPRReg scratchGPR = scratch.gpr();
+        m_jit.loadPtr(node.registerPointer(), scratchGPR);
+        JITCompiler::Jump ok = m_jit.branchPtr(
+            JITCompiler::Equal, scratchGPR,
+            TrustedImmPtr(bitwise_cast<void*>(JSValue::encode(node.registerPointer()->get()))));
+        m_jit.breakpoint();
+        ok.link(&m_jit);
+#endif
+        
+        noResult(m_compileIndex);
+        break;
+    }
+
     case CheckHasInstance: {
         SpeculateCellOperand base(this, node.child1());
         GPRTemporary structure(this);
index e6364e0..b8610e7 100644 (file)
@@ -2871,6 +2871,18 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi
         vPC += OPCODE_LENGTH(op_get_global_var);
         NEXT_INSTRUCTION();
     }
+    DEFINE_OPCODE(op_get_global_var_watchable) {
+        /* get_global_var_watchable dst(r) globalObject(c) registerPointer(n)
+
+           Gets the global var at global slot index and places it in register dst.
+         */
+        int dst = vPC[1].u.operand;
+        WriteBarrier<Unknown>* registerPointer = vPC[2].u.registerPointer;
+
+        callFrame->uncheckedR(dst) = registerPointer->get();
+        vPC += OPCODE_LENGTH(op_get_global_var_watchable);
+        NEXT_INSTRUCTION();
+    }
     DEFINE_OPCODE(op_put_global_var) {
         /* put_global_var globalObject(c) registerPointer(n) value(r)
          
@@ -2885,6 +2897,22 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi
         vPC += OPCODE_LENGTH(op_put_global_var);
         NEXT_INSTRUCTION();
     }
+    DEFINE_OPCODE(op_put_global_var_check) {
+        /* put_global_var_check globalObject(c) registerPointer(n) value(r)
+         
+           Puts value into global slot index. In JIT configurations this will
+           perform a watchpoint check. If we're running with the old interpreter,
+           this is not necessary; the interpreter never uses these watchpoints.
+         */
+        JSGlobalObject* scope = codeBlock->globalObject();
+        ASSERT(scope->isGlobalObject());
+        WriteBarrier<Unknown>* registerPointer = vPC[1].u.registerPointer;
+        int value = vPC[2].u.operand;
+        
+        registerPointer->set(*globalData, scope, callFrame->r(value).jsValue());
+        vPC += OPCODE_LENGTH(op_put_global_var_check);
+        NEXT_INSTRUCTION();
+    }
     DEFINE_OPCODE(op_get_scoped_var) {
         /* get_scoped_var dst(r) index(n) skip(n)
 
index ff5615f..09afd7a 100644 (file)
@@ -259,6 +259,7 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_get_by_val)
         DEFINE_OP(op_get_argument_by_val)
         DEFINE_OP(op_get_by_pname)
+        DEFINE_OP(op_get_global_var_watchable)
         DEFINE_OP(op_get_global_var)
         DEFINE_OP(op_get_pnames)
         DEFINE_OP(op_get_scoped_var)
@@ -324,6 +325,7 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_put_by_val)
         DEFINE_OP(op_put_getter_setter)
         DEFINE_OP(op_put_global_var)
+        DEFINE_OP(op_put_global_var_check)
         DEFINE_OP(op_put_scoped_var)
         DEFINE_OP(op_resolve)
         DEFINE_OP(op_resolve_base)
@@ -481,6 +483,7 @@ void JIT::privateCompileSlowCases()
         case op_put_by_id_transition_normal:
         DEFINE_SLOWCASE_OP(op_put_by_id)
         DEFINE_SLOWCASE_OP(op_put_by_val)
+        DEFINE_SLOWCASE_OP(op_put_global_var_check);
         DEFINE_SLOWCASE_OP(op_resolve_global)
         DEFINE_SLOWCASE_OP(op_resolve_global_dynamic)
         DEFINE_SLOWCASE_OP(op_rshift)
index d114310..6491edb 100644 (file)
@@ -463,7 +463,7 @@ namespace JSC {
         bool isMapped(int virtualRegisterIndex);
         bool getMappedPayload(int virtualRegisterIndex, RegisterID& payload);
         bool getMappedTag(int virtualRegisterIndex, RegisterID& tag);
-
+        
         void emitJumpSlowCaseIfNotJSCell(int virtualRegisterIndex);
         void emitJumpSlowCaseIfNotJSCell(int virtualRegisterIndex, RegisterID tag);
 
@@ -599,6 +599,7 @@ namespace JSC {
         void emit_op_get_argument_by_val(Instruction*);
         void emit_op_get_by_pname(Instruction*);
         void emit_op_get_global_var(Instruction*);
+        void emit_op_get_global_var_watchable(Instruction* instruction) { emit_op_get_global_var(instruction); }
         void emit_op_get_scoped_var(Instruction*);
         void emit_op_init_lazy_reg(Instruction*);
         void emit_op_check_has_instance(Instruction*);
@@ -662,6 +663,7 @@ namespace JSC {
         void emit_op_put_by_val(Instruction*);
         void emit_op_put_getter_setter(Instruction*);
         void emit_op_put_global_var(Instruction*);
+        void emit_op_put_global_var_check(Instruction*);
         void emit_op_put_scoped_var(Instruction*);
         void emit_op_resolve(Instruction*);
         void emit_op_resolve_base(Instruction*);
@@ -739,6 +741,7 @@ namespace JSC {
         void emitSlow_op_pre_inc(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_put_by_id(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_put_by_val(Instruction*, Vector<SlowCaseEntry>::iterator&);
+        void emitSlow_op_put_global_var_check(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_resolve_global(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_resolve_global_dynamic(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_rshift(Instruction*, Vector<SlowCaseEntry>::iterator&);
index b64f162..4fb40aa 100644 (file)
@@ -1027,6 +1027,29 @@ void JIT::emit_op_put_global_var(Instruction* currentInstruction)
         emitWriteBarrier(globalObject, regT0, regT2, ShouldFilterImmediates, WriteBarrierForVariableAccess);
 }
 
+void JIT::emit_op_put_global_var_check(Instruction* currentInstruction)
+{
+    emitGetVirtualRegister(currentInstruction[2].u.operand, regT0);
+    
+    addSlowCase(branchTest8(NonZero, AbsoluteAddress(currentInstruction[3].u.predicatePointer)));
+
+    JSGlobalObject* globalObject = m_codeBlock->globalObject();
+    
+    storePtr(regT0, currentInstruction[1].u.registerPointer);
+    if (Heap::isWriteBarrierEnabled())
+        emitWriteBarrier(globalObject, regT0, regT2, ShouldFilterImmediates, WriteBarrierForVariableAccess);
+}
+
+void JIT::emitSlow_op_put_global_var_check(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+    linkSlowCase(iter);
+    
+    JITStubCall stubCall(this, cti_op_put_global_var_check);
+    stubCall.addArgument(regT0);
+    stubCall.addArgument(TrustedImm32(currentInstruction[4].u.operand));
+    stubCall.call();
+}
+
 void JIT::resetPatchGetById(RepatchBuffer& repatchBuffer, StructureStubInfo* stubInfo)
 {
     repatchBuffer.relink(stubInfo->callReturnLocation, cti_op_get_by_id);
index 78b17d7..5bec19e 100644 (file)
@@ -1090,6 +1090,37 @@ void JIT::emit_op_put_global_var(Instruction* currentInstruction)
     map(m_bytecodeOffset + OPCODE_LENGTH(op_put_global_var), value, regT1, regT0);
 }
 
+void JIT::emit_op_put_global_var_check(Instruction* currentInstruction)
+{
+    WriteBarrier<Unknown>* registerPointer = currentInstruction[1].u.registerPointer;
+    int value = currentInstruction[2].u.operand;
+    
+    JSGlobalObject* globalObject = m_codeBlock->globalObject();
+    
+    emitLoad(value, regT1, regT0);
+    
+    addSlowCase(branchTest8(NonZero, AbsoluteAddress(currentInstruction[3].u.predicatePointer)));
+    
+    if (Heap::isWriteBarrierEnabled()) {
+        move(TrustedImmPtr(globalObject), regT2);
+        emitWriteBarrier(globalObject, regT1, regT3, ShouldFilterImmediates, WriteBarrierForVariableAccess);
+    }
+    
+    store32(regT1, registerPointer->tagPointer());
+    store32(regT0, registerPointer->payloadPointer());
+    unmap();
+}
+
+void JIT::emitSlow_op_put_global_var_check(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+    linkSlowCase(iter);
+    
+    JITStubCall stubCall(this, cti_op_put_global_var_check);
+    stubCall.addArgument(regT1, regT0);
+    stubCall.addArgument(TrustedImm32(currentInstruction[4].u.operand));
+    stubCall.call();
+}
+
 void JIT::resetPatchGetById(RepatchBuffer& repatchBuffer, StructureStubInfo* stubInfo)
 {
     repatchBuffer.relink(stubInfo->callReturnLocation, cti_op_get_by_id);
index 98a56c1..f83fbd4 100644 (file)
@@ -2377,6 +2377,15 @@ DEFINE_STUB_FUNCTION(JSObject*, op_new_array_buffer)
     return constructArray(stackFrame.callFrame, stackFrame.callFrame->codeBlock()->constantBuffer(stackFrame.args[0].int32()), stackFrame.args[1].int32());
 }
 
+DEFINE_STUB_FUNCTION(void, op_put_global_var_check)
+{
+    STUB_INIT_STACK_FRAME(stackFrame);
+    
+    CallFrame* callFrame = stackFrame.callFrame;
+    CodeBlock* codeBlock = callFrame->codeBlock();
+    symbolTablePut(codeBlock->globalObject(), callFrame, codeBlock->identifier(stackFrame.args[1].int32()), stackFrame.args[0].jsValue(), true);
+}
+
 DEFINE_STUB_FUNCTION(EncodedJSValue, op_resolve)
 {
     STUB_INIT_STACK_FRAME(stackFrame);
index a6f7947..2517760 100644 (file)
@@ -445,6 +445,7 @@ extern "C" {
     void JIT_STUB cti_op_put_by_index(STUB_ARGS_DECLARATION) WTF_INTERNAL;
     void JIT_STUB cti_op_put_by_val(STUB_ARGS_DECLARATION) WTF_INTERNAL;
     void JIT_STUB cti_op_put_getter_setter(STUB_ARGS_DECLARATION) WTF_INTERNAL;
+    void JIT_STUB cti_op_put_global_var_check(STUB_ARGS_DECLARATION) WTF_INTERNAL;
     void JIT_STUB cti_op_tear_off_activation(STUB_ARGS_DECLARATION) WTF_INTERNAL;
     void JIT_STUB cti_op_tear_off_arguments(STUB_ARGS_DECLARATION) WTF_INTERNAL;
     void JIT_STUB cti_op_throw_reference_error(STUB_ARGS_DECLARATION) WTF_INTERNAL;
index 7133b0d..07fbf62 100644 (file)
@@ -475,8 +475,10 @@ LLINT_SLOW_PATH_DECL(slow_path_convert_this)
     LLINT_BEGIN();
     JSValue v1 = LLINT_OP(1).jsValue();
     ASSERT(v1.isPrimitive());
+#if ENABLE(VALUE_PROFILER)
     pc[OPCODE_LENGTH(op_convert_this) - 1].u.profile->m_buckets[0] =
         JSValue::encode(v1.structureOrUndefined());
+#endif
     LLINT_RETURN(v1.toThisObject(exec));
 }
 
@@ -856,6 +858,14 @@ LLINT_SLOW_PATH_DECL(slow_path_resolve_with_this)
     LLINT_END();
 }
 
+LLINT_SLOW_PATH_DECL(slow_path_put_global_var_check)
+{
+    LLINT_BEGIN();
+    CodeBlock* codeBlock = exec->codeBlock();
+    symbolTablePut(codeBlock->globalObject(), exec, codeBlock->identifier(pc[4].u.operand), LLINT_OP_C(2).jsValue(), true);
+    LLINT_END();
+}
+
 LLINT_SLOW_PATH_DECL(slow_path_get_by_id)
 {
     LLINT_BEGIN();
index 3553462..2e069d0 100644 (file)
@@ -147,6 +147,7 @@ LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_resolve_base);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_ensure_property_exists);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_resolve_with_base);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_resolve_with_this);
+LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_global_var_check);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_by_id);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_arguments_length);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_by_id);
index 9f540f8..d27fd82 100644 (file)
@@ -1039,7 +1039,7 @@ _llint_op_put_scoped_var:
     dispatch(4)
 
 
-_llint_op_get_global_var:
+macro getGlobalVar(size)
     traceExecution()
     loadp 8[PC], t0
     loadi 4[PC], t3
@@ -1047,9 +1047,17 @@ _llint_op_get_global_var:
     loadi PayloadOffset[t0], t1
     storei t2, TagOffset[cfr, t3, 8]
     storei t1, PayloadOffset[cfr, t3, 8]
-    loadi 12[PC], t3
+    loadi (size - 1) * 4[PC], t3
     valueProfile(t2, t1, t3)
-    dispatch(4)
+    dispatch(size)
+end
+
+_llint_op_get_global_var:
+    getGlobalVar(4)
+
+
+_llint_op_get_global_var_watchable:
+    getGlobalVar(5)
 
 
 _llint_op_put_global_var:
@@ -1063,6 +1071,22 @@ _llint_op_put_global_var:
     dispatch(3)
 
 
+_llint_op_put_global_var_check:
+    traceExecution()
+    loadp 12[PC], t2
+    loadi 8[PC], t1
+    loadi 4[PC], t0
+    btbnz [t2], .opPutGlobalVarCheckSlow
+    loadConstantOrVariable(t1, t2, t3)
+    writeBarrier(t2, t3)
+    storei t2, TagOffset[t0]
+    storei t3, PayloadOffset[t0]
+    dispatch(5)
+.opPutGlobalVarCheckSlow:
+    callSlowPath(_llint_slow_path_put_global_var_check)
+    dispatch(5)
+
+
 _llint_op_get_by_id:
     traceExecution()
     # We only do monomorphic get_by_id caching for now, and we do not modify the
index 4fe4d5e..a153586 100644 (file)
@@ -893,15 +893,23 @@ _llint_op_put_scoped_var:
     dispatch(4)
 
 
-_llint_op_get_global_var:
+macro getGlobalVar(size)
     traceExecution()
     loadp 16[PB, PC, 8], t0
     loadis 8[PB, PC, 8], t3
     loadp [t0], t1
     storep t1, [cfr, t3, 8]
-    loadp 24[PB, PC, 8], t0
+    loadp (size - 1) * 8[PB, PC, 8], t0
     valueProfile(t1, t0)
-    dispatch(4)
+    dispatch(size)
+end
+
+_llint_op_get_global_var:
+    getGlobalVar(4)
+
+
+_llint_op_get_global_var_watchable:
+    getGlobalVar(5)
 
 
 _llint_op_put_global_var:
@@ -914,6 +922,21 @@ _llint_op_put_global_var:
     dispatch(3)
 
 
+_llint_op_put_global_var_check:
+    traceExecution()
+    loadp 24[PB, PC, 8], t2
+    loadis 16[PB, PC, 8], t1
+    loadp 8[PB, PC, 8], t0
+    btbnz [t2], .opPutGlobalVarCheckSlow
+    loadConstantOrVariable(t1, t2)
+    writeBarrier(t2)
+    storep t2, [t0]
+    dispatch(5)
+.opPutGlobalVarCheckSlow:
+    callSlowPath(_llint_slow_path_put_global_var_check)
+    dispatch(5)
+
+
 _llint_op_get_by_id:
     traceExecution()
     # We only do monomorphic get_by_id caching for now, and we do not modify the
index 525c00b..aa71b8a 100644 (file)
@@ -504,22 +504,25 @@ void JSObject::reifyStaticFunctionsForDelete(ExecState* exec)
     structure()->setStaticFunctionsReified();
 }
 
-void JSObject::removeDirect(JSGlobalData& globalData, PropertyName propertyName)
+bool JSObject::removeDirect(JSGlobalData& globalData, PropertyName propertyName)
 {
     if (structure()->get(globalData, propertyName) == WTF::notFound)
-        return;
+        return false;
 
     size_t offset;
     if (structure()->isUncacheableDictionary()) {
         offset = structure()->removePropertyWithoutTransition(globalData, propertyName);
-        if (offset != WTF::notFound)
-            putUndefinedAtDirectOffset(offset);
-        return;
+        if (offset == WTF::notFound)
+            return false;
+        putUndefinedAtDirectOffset(offset);
+        return true;
     }
 
     setStructure(globalData, Structure::removePropertyTransition(globalData, structure(), propertyName, offset));
-    if (offset != WTF::notFound)
-        putUndefinedAtDirectOffset(offset);
+    if (offset == WTF::notFound)
+        return false;
+    putUndefinedAtDirectOffset(offset);
+    return true;
 }
 
 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase<Unknown>* location)
index 67aa151..ac8601d 100644 (file)
@@ -176,7 +176,7 @@ namespace JSC {
 
         void transitionTo(JSGlobalData&, Structure*);
 
-        void removeDirect(JSGlobalData&, PropertyName);
+        bool removeDirect(JSGlobalData&, PropertyName); // Return true if anything is removed.
         bool hasCustomProperties() { return structure()->didTransition(); }
         bool hasGetterSetterProperties() { return structure()->hasGetterSetterProperties(); }
 
index a933177..2bbe21d 100644 (file)
@@ -72,9 +72,12 @@ template<typename SymbolTableObjectType>
 inline bool symbolTableGet(
     SymbolTableObjectType* object, PropertyName propertyName, PropertySlot& slot)
 {
-    SymbolTableEntry entry = object->symbolTable().inlineGet(propertyName.publicName());
-    if (entry.isNull())
+    SymbolTable& symbolTable = object->symbolTable();
+    SymbolTable::iterator iter = symbolTable.find(propertyName.publicName());
+    if (iter == symbolTable.end())
         return false;
+    SymbolTableEntry::Fast entry = iter->second;
+    ASSERT(!entry.isNull());
     slot.setValue(object->registerAt(entry.getIndex()).get());
     return true;
 }
@@ -83,9 +86,12 @@ template<typename SymbolTableObjectType>
 inline bool symbolTableGet(
     SymbolTableObjectType* object, PropertyName propertyName, PropertyDescriptor& descriptor)
 {
-    SymbolTableEntry entry = object->symbolTable().inlineGet(propertyName.publicName());
-    if (entry.isNull())
+    SymbolTable& symbolTable = object->symbolTable();
+    SymbolTable::iterator iter = symbolTable.find(propertyName.publicName());
+    if (iter == symbolTable.end())
         return false;
+    SymbolTableEntry::Fast entry = iter->second;
+    ASSERT(!entry.isNull());
     descriptor.setDescriptor(
         object->registerAt(entry.getIndex()).get(), entry.getAttributes() | DontDelete);
     return true;
@@ -96,9 +102,12 @@ inline bool symbolTableGet(
     SymbolTableObjectType* object, PropertyName propertyName, PropertySlot& slot,
     bool& slotIsWriteable)
 {
-    SymbolTableEntry entry = object->symbolTable().inlineGet(propertyName.publicName());
-    if (entry.isNull())
+    SymbolTable& symbolTable = object->symbolTable();
+    SymbolTable::iterator iter = symbolTable.find(propertyName.publicName());
+    if (iter == symbolTable.end())
         return false;
+    SymbolTableEntry::Fast entry = iter->second;
+    ASSERT(!entry.isNull());
     slot.setValue(object->registerAt(entry.getIndex()).get());
     slotIsWriteable = !entry.isReadOnly();
     return true;
@@ -112,15 +121,21 @@ inline bool symbolTablePut(
     JSGlobalData& globalData = exec->globalData();
     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(object));
     
-    SymbolTableEntry entry = object->symbolTable().inlineGet(propertyName.publicName());
-    if (entry.isNull())
+    SymbolTable& symbolTable = object->symbolTable();
+    SymbolTable::iterator iter = symbolTable.find(propertyName.publicName());
+    if (iter == symbolTable.end())
         return false;
-    if (entry.isReadOnly()) {
+    bool wasFat;
+    SymbolTableEntry::Fast fastEntry = iter->second.getFast(wasFat);
+    ASSERT(!fastEntry.isNull());
+    if (fastEntry.isReadOnly()) {
         if (shouldThrow)
             throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
         return true;
     }
-    object->registerAt(entry.getIndex()).set(globalData, object, value);
+    if (UNLIKELY(wasFat))
+        iter->second.notifyWrite();
+    object->registerAt(fastEntry.getIndex()).set(globalData, object, value);
     return true;
 }
 
@@ -136,6 +151,7 @@ inline bool symbolTablePutWithAttributes(
         return false;
     SymbolTableEntry& entry = iter->second;
     ASSERT(!entry.isNull());
+    entry.notifyWrite();
     entry.setAttributes(attributes);
     object->registerAt(entry.getIndex()).set(globalData, object, value);
     return true;
diff --git a/Source/JavaScriptCore/runtime/SymbolTable.cpp b/Source/JavaScriptCore/runtime/SymbolTable.cpp
new file mode 100644 (file)
index 0000000..99e2f23
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012 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.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "SymbolTable.h"
+
+namespace JSC {
+
+SymbolTableEntry& SymbolTableEntry::copySlow(const SymbolTableEntry& other)
+{
+    ASSERT(other.isFat());
+    FatEntry* newFatEntry = new FatEntry(*other.fatEntry());
+    freeFatEntry();
+    m_bits = bitwise_cast<intptr_t>(newFatEntry) | FatFlag;
+    return *this;
+}
+
+void SymbolTableEntry::freeFatEntrySlow()
+{
+    ASSERT(isFat());
+    delete fatEntry();
+}
+
+bool SymbolTableEntry::couldBeWatched()
+{
+    if (!isFat())
+        return false;
+    WatchpointSet* watchpoints = fatEntry()->m_watchpoints.get();
+    if (!watchpoints)
+        return false;
+    return watchpoints->isStillValid();
+}
+
+void SymbolTableEntry::attemptToWatch()
+{
+    FatEntry* entry = inflate();
+    if (!entry->m_watchpoints) {
+        entry->m_watchpoints = adoptRef(new WatchpointSet());
+        entry->m_watchpoints->startWatching();
+    }
+}
+
+bool* SymbolTableEntry::addressOfIsWatched()
+{
+    ASSERT(couldBeWatched());
+    return fatEntry()->m_watchpoints->addressOfIsWatched();
+}
+
+void SymbolTableEntry::addWatchpoint(Watchpoint* watchpoint)
+{
+    ASSERT(couldBeWatched());
+    fatEntry()->m_watchpoints->add(watchpoint);
+}
+
+void SymbolTableEntry::notifyWriteSlow()
+{
+    WatchpointSet* watchpoints = fatEntry()->m_watchpoints.get();
+    if (!watchpoints)
+        return;
+    watchpoints->notifyWrite();
+}
+
+SymbolTableEntry::FatEntry* SymbolTableEntry::inflateSlow()
+{
+    FatEntry* entry = new FatEntry(m_bits);
+    m_bits = bitwise_cast<intptr_t>(entry) | FatFlag;
+    return entry;
+}
+
+} // namespace JSC
+
index f540a12..9ddc32c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 2008, 2012 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 #include "JSObject.h"
 #include "UString.h"
+#include "Watchpoint.h"
 #include <wtf/AlwaysInline.h>
 #include <wtf/HashTraits.h>
 
 namespace JSC {
 
+    class Watchpoint;
+    class WatchpointSet;
+
     static ALWAYS_INLINE int missingSymbolMarker() { return std::numeric_limits<int>::max(); }
 
     // The bit twiddling in this class assumes that every register index is a
     // reasonably small positive or negative number, and therefore has its high
     // four bits all set or all unset.
 
+    // In addition to implementing semantics-mandated variable attributes and
+    // implementation-mandated variable indexing, this class also implements
+    // watchpoints to be used for JIT optimizations. Because watchpoints are
+    // meant to be relatively rare, this class optimizes heavily for the case
+    // that they are not being used. To that end, this class uses the thin-fat
+    // idiom: either it is thin, in which case it contains an in-place encoded
+    // word that consists of attributes, the index, and a bit saying that it is
+    // thin; or it is fat, in which case it contains a pointer to a malloc'd
+    // data structure and a bit saying that it is fat. The malloc'd data
+    // structure will be malloced a second time upon copy, to preserve the
+    // property that in-place edits to SymbolTableEntry do not manifest in any
+    // copies. However, the malloc'd FatEntry data structure contains a ref-
+    // counted pointer to a shared WatchpointSet. Thus, in-place edits of the
+    // WatchpointSet will manifest in all copies. Here's a picture:
+    //
+    // SymbolTableEntry --> FatEntry --> WatchpointSet
+    //
+    // If you make a copy of a SymbolTableEntry, you will have:
+    //
+    // original: SymbolTableEntry --> FatEntry --> WatchpointSet
+    // copy:     SymbolTableEntry --> FatEntry -----^
+
     struct SymbolTableEntry {
+        // Use the SymbolTableEntry::Fast class, either via implicit cast or by calling
+        // getFast(), when you (1) only care about isNull(), getIndex(), and isReadOnly(),
+        // and (2) you are in a hot path where you need to minimize the number of times
+        // that you branch on isFat() when getting the bits().
+        class Fast {
+        public:
+            Fast()
+                : m_bits(0)
+            {
+            }
+            
+            ALWAYS_INLINE Fast(const SymbolTableEntry& entry)
+                : m_bits(entry.bits())
+            {
+            }
+        
+            bool isNull() const
+            {
+                return !m_bits;
+            }
+
+            int getIndex() const
+            {
+                return static_cast<int>(m_bits >> FlagBits);
+            }
+        
+            bool isReadOnly() const
+            {
+                return m_bits & ReadOnlyFlag;
+            }
+            
+            unsigned getAttributes() const
+            {
+                unsigned attributes = 0;
+                if (m_bits & ReadOnlyFlag)
+                    attributes |= ReadOnly;
+                if (m_bits & DontEnumFlag)
+                    attributes |= DontEnum;
+                return attributes;
+            }
+
+            bool isFat() const
+            {
+                return m_bits & FatFlag;
+            }
+            
+        private:
+            friend struct SymbolTableEntry;
+            intptr_t m_bits;
+        };
+
         SymbolTableEntry()
             : m_bits(0)
         {
         }
 
         SymbolTableEntry(int index)
+            : m_bits(0)
         {
             ASSERT(isValidIndex(index));
             pack(index, false, false);
         }
 
         SymbolTableEntry(int index, unsigned attributes)
+            : m_bits(0)
         {
             ASSERT(isValidIndex(index));
             pack(index, attributes & ReadOnly, attributes & DontEnum);
         }
         
+        ~SymbolTableEntry()
+        {
+            freeFatEntry();
+        }
+        
+        SymbolTableEntry(const SymbolTableEntry& other)
+            : m_bits(0)
+        {
+            *this = other;
+        }
+        
+        SymbolTableEntry& operator=(const SymbolTableEntry& other)
+        {
+            if (UNLIKELY(other.isFat()))
+                return copySlow(other);
+            freeFatEntry();
+            m_bits = other.m_bits;
+            return *this;
+        }
+        
         bool isNull() const
         {
-            return !m_bits;
+            return !bits();
         }
 
         int getIndex() const
         {
-            return m_bits >> FlagBits;
+            return static_cast<int>(bits() >> FlagBits);
         }
-
+        
+        ALWAYS_INLINE Fast getFast() const
+        {
+            return Fast(*this);
+        }
+        
+        ALWAYS_INLINE Fast getFast(bool& wasFat) const
+        {
+            Fast result;
+            wasFat = isFat();
+            if (wasFat)
+                result.m_bits = fatEntry()->m_bits;
+            else
+                result.m_bits = m_bits;
+            return result;
+        }
+        
         unsigned getAttributes() const
         {
-            unsigned attributes = 0;
-            if (m_bits & ReadOnlyFlag)
-                attributes |= ReadOnly;
-            if (m_bits & DontEnumFlag)
-                attributes |= DontEnum;
-            return attributes;
+            return getFast().getAttributes();
         }
 
         void setAttributes(unsigned attributes)
@@ -87,30 +197,125 @@ namespace JSC {
 
         bool isReadOnly() const
         {
-            return m_bits & ReadOnlyFlag;
+            return bits() & ReadOnlyFlag;
         }
-
+        
+        bool couldBeWatched();
+        
+        // Notify an opportunity to create a watchpoint for a variable. This is
+        // idempotent and fail-silent. It is idempotent in the sense that if
+        // a watchpoint set had already been created, then another one will not
+        // be created. Hence two calls to this method have the same effect as
+        // one call. It is also fail-silent, in the sense that if a watchpoint
+        // set had been created and had already been invalidated, then this will
+        // just return. This means that couldBeWatched() may return false even
+        // immediately after a call to attemptToWatch().
+        void attemptToWatch();
+        
+        bool* addressOfIsWatched();
+        
+        void addWatchpoint(Watchpoint*);
+        
+        WatchpointSet* watchpointSet()
+        {
+            return fatEntry()->m_watchpoints.get();
+        }
+        
+        ALWAYS_INLINE void notifyWrite()
+        {
+            if (LIKELY(!isFat()))
+                return;
+            notifyWriteSlow();
+        }
+        
     private:
-        static const unsigned ReadOnlyFlag = 0x1;
-        static const unsigned DontEnumFlag = 0x2;
-        static const unsigned NotNullFlag = 0x4;
-        static const unsigned FlagBits = 3;
+        static const intptr_t FatFlag = 0x1;
+        static const intptr_t ReadOnlyFlag = 0x2;
+        static const intptr_t DontEnumFlag = 0x4;
+        static const intptr_t NotNullFlag = 0x8;
+        static const intptr_t FlagBits = 4;
+        
+        class FatEntry {
+            WTF_MAKE_FAST_ALLOCATED;
+        public:
+            FatEntry(intptr_t bits)
+                : m_bits(bits | FatFlag)
+            {
+            }
+            
+            intptr_t m_bits; // always has FatFlag set and exactly matches what the bits would have been if this wasn't fat.
+            
+            RefPtr<WatchpointSet> m_watchpoints;
+        };
+        
+        SymbolTableEntry& copySlow(const SymbolTableEntry&);
+        JS_EXPORT_PRIVATE void notifyWriteSlow();
+        
+        bool isFat() const
+        {
+            return m_bits & FatFlag;
+        }
+        
+        const FatEntry* fatEntry() const
+        {
+            ASSERT(isFat());
+            return bitwise_cast<const FatEntry*>(m_bits & ~FatFlag);
+        }
+        
+        FatEntry* fatEntry()
+        {
+            ASSERT(isFat());
+            return bitwise_cast<FatEntry*>(m_bits & ~FatFlag);
+        }
+        
+        FatEntry* inflate()
+        {
+            if (LIKELY(isFat()))
+                return fatEntry();
+            return inflateSlow();
+        }
+        
+        FatEntry* inflateSlow();
+        
+        ALWAYS_INLINE intptr_t bits() const
+        {
+            if (isFat())
+                return fatEntry()->m_bits;
+            return m_bits;
+        }
+        
+        ALWAYS_INLINE intptr_t& bits()
+        {
+            if (isFat())
+                return fatEntry()->m_bits;
+            return m_bits;
+        }
+        
+        void freeFatEntry()
+        {
+            if (LIKELY(!isFat()))
+                return;
+            freeFatEntrySlow();
+        }
+        
+        void freeFatEntrySlow();
 
         void pack(int index, bool readOnly, bool dontEnum)
         {
-            m_bits = (index << FlagBits) | NotNullFlag;
+            intptr_t& bitsRef = bits();
+            bitsRef = (static_cast<intptr_t>(index) << FlagBits) | NotNullFlag;
             if (readOnly)
-                m_bits |= ReadOnlyFlag;
+                bitsRef |= ReadOnlyFlag;
             if (dontEnum)
-                m_bits |= DontEnumFlag;
+                bitsRef |= DontEnumFlag;
         }
         
         bool isValidIndex(int index)
         {
-            return ((index << FlagBits) >> FlagBits) == index;
+            return ((static_cast<intptr_t>(index) << FlagBits) >> FlagBits) == static_cast<intptr_t>(index);
         }
 
-        int m_bits;
+        intptr_t m_bits;
     };
 
     struct SymbolTableIndexHashTraits : HashTraits<SymbolTableEntry> {
index 198b851..cb70da4 100644 (file)
@@ -1,3 +1,25 @@
+2012-06-13  Filip Pizlo  <fpizlo@apple.com>
+
+        DFG should be able to set watchpoints on global variables
+        https://bugs.webkit.org/show_bug.cgi?id=88692
+
+        Reviewed by Geoffrey Garen.
+        
+        Added ability to set the inline capacity of segmented vectors.
+        
+        Also added the ability ot ASSERT_NOT_REACHED() without having to
+        propagate NO_RETURN macros, which would be a show-stopper for code
+        that is conditionally unreachable.
+
+        * wtf/Assertions.h:
+        (UNREACHABLE_FOR_PLATFORM):
+        * wtf/SegmentedVector.h:
+        (WTF):
+        (SegmentedVectorIterator):
+        (WTF::SegmentedVectorIterator::operator=):
+        (WTF::SegmentedVectorIterator::SegmentedVectorIterator):
+        (SegmentedVector):
+
 2012-06-13  Sheriff Bot  <webkit.review.bot@gmail.com>
 
         Unreviewed, rolling out r120172.
index b78951e..6238a2c 100644 (file)
@@ -376,4 +376,20 @@ while (0)
 #define LOG_VERBOSE(channel, ...) WTFLogVerbose(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, &JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__)
 #endif
 
+/* UNREACHABLE_FOR_PLATFORM */
+
+#if COMPILER(CLANG)
+// This would be a macro except that its use of #pragma works best around
+// a function. Hence it uses macro naming convention.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-noreturn"
+static inline void UNREACHABLE_FOR_PLATFORM()
+{
+    ASSERT_NOT_REACHED();
+}
+#pragma clang diagnostic pop
+#else
+#define UNREACHABLE_FOR_PLATFORM() ASSERT_NOT_REACHED()
+#endif
+
 #endif /* WTF_Assertions_h */
index 99d5f94..63d89d7 100644 (file)
 namespace WTF {
 
     // An iterator for SegmentedVector. It supports only the pre ++ operator
-    template <typename T, size_t SegmentSize> class SegmentedVector;
-    template <typename T, size_t SegmentSize> class SegmentedVectorIterator {
+    template <typename T, size_t SegmentSize = 8, size_t InlineCapacity = 32> class SegmentedVector;
+    template <typename T, size_t SegmentSize = 8, size_t InlineCapacity = 32> class SegmentedVectorIterator {
     private:
-        friend class SegmentedVector<T, SegmentSize>;
+        friend class SegmentedVector<T, SegmentSize, InlineCapacity>;
     public:
-        typedef SegmentedVectorIterator<T, SegmentSize> Iterator;
+        typedef SegmentedVectorIterator<T, SegmentSize, InlineCapacity> Iterator;
 
         ~SegmentedVectorIterator() { }
 
@@ -75,7 +75,7 @@ namespace WTF {
             return m_index != other.m_index || m_segment != other.m_segment || &m_vector != &other.m_vector;
         }
 
-        SegmentedVectorIterator& operator=(const SegmentedVectorIterator<T, SegmentSize>& other)
+        SegmentedVectorIterator& operator=(const SegmentedVectorIterator<T, SegmentSize, InlineCapacity>& other)
         {
             m_vector = other.m_vector;
             m_segment = other.m_segment;
@@ -84,25 +84,28 @@ namespace WTF {
         }
 
     private:
-        SegmentedVectorIterator(SegmentedVector<T, SegmentSize>& vector, size_t segment, size_t index)
+        SegmentedVectorIterator(SegmentedVector<T, SegmentSize, InlineCapacity>& vector, size_t segment, size_t index)
             : m_vector(vector)
             , m_segment(segment)
             , m_index(index)
         {
         }
 
-        SegmentedVector<T, SegmentSize>& m_vector;
+        SegmentedVector<T, SegmentSize, InlineCapacity>& m_vector;
         size_t m_segment;
         size_t m_index;
     };
 
     // SegmentedVector is just like Vector, but it doesn't move the values
     // stored in its buffer when it grows. Therefore, it is safe to keep
-    // pointers into a SegmentedVector.
-    template <typename T, size_t SegmentSize> class SegmentedVector {
-        friend class SegmentedVectorIterator<T, SegmentSize>;
+    // pointers into a SegmentedVector. The default tuning values are
+    // optimized for segmented vectors that get large; you may want to use
+    // SegmentedVector<thingy, 1, 0> if you don't expect a lot of entries.
+    template <typename T, size_t SegmentSize, size_t InlineCapacity>
+    class SegmentedVector {
+        friend class SegmentedVectorIterator<T, SegmentSize, InlineCapacity>;
     public:
-        typedef SegmentedVectorIterator<T, SegmentSize> Iterator;
+        typedef SegmentedVectorIterator<T, SegmentSize, InlineCapacity> Iterator;
 
         SegmentedVector()
             : m_size(0)
@@ -250,7 +253,7 @@ namespace WTF {
 
         size_t m_size;
         Segment m_inlineSegment;
-        Vector<Segment*, 32> m_segments;
+        Vector<Segment*, InlineCapacity> m_segments;
     };
 
 } // namespace WTF