WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
thingy
blah.patch (text/plain), 51.94 KB, created by
Filip Pizlo
on 2013-11-19 10:29:56 PST
(
hide
)
Description:
thingy
Filename:
MIME Type:
Creator:
Filip Pizlo
Created:
2013-11-19 10:29:56 PST
Size:
51.94 KB
patch
obsolete
>Index: Source/JavaScriptCore/ChangeLog >=================================================================== >--- Source/JavaScriptCore/ChangeLog (revision 159479) >+++ Source/JavaScriptCore/ChangeLog (working copy) >@@ -1,3 +1,180 @@ >+2013-11-18 Filip Pizlo <fpizlo@apple.com> >+ >+ Infer constant global variables >+ https://bugs.webkit.org/show_bug.cgi?id=124464 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ All global variables that are candidates for watchpoint-based constant inference (i.e. >+ not 'const' variables) will now have WatchpointSet's associated with them and those >+ are used to drive the inference by tracking three states of each variable: >+ >+ Uninitialized: the variable's value is Undefined and the WatchpointSet state is >+ ClearWatchpoint. >+ >+ Initialized: the variable's value was set to something (could even be explicitly set >+ to Undefined) and the WatchpointSet state is IsWatching. >+ >+ Invalidated: the variable's value was set to something else (could even be the same >+ thing as before but the point is that a put operation did execute again) and the >+ WatchpointSet is IsInvalidated. >+ >+ If the compiler tries to compile a GetGlobalVar and the WatchpointSet state is >+ IsWatching, then the current value of the variable can be folded in place of the get, >+ and a watchpoint on the variable can be registered. >+ >+ We handle race conditions between the mutator and compiler by mandating that: >+ >+ - The mutator changes the WatchpointSet state after executing the put. >+ >+ - The compiler checks the WatchpointSet state prior to reading the value. >+ >+ The concrete algorithm used by the mutator is: >+ >+ 1. Store the new value into the variable. >+ --- Execute a store-store fence. >+ 2. Bump the state (ClearWatchpoing becomes IsWatching, IsWatching becomes >+ IsInvalidated) >+ >+ The concrete algorithm that the compiler uses is: >+ >+ 1. Load the state. If it's *not* IsWatching, then give up on constant inference. >+ --- Execute a load-load fence. >+ 2. Load the value of the variable and use that for folding, while also registering >+ a DesiredWatchpoint. The various parts of this step can be done in any order. >+ >+ The desired watchpoint registration will fail if the watchpoint set is already >+ invalidated. Now consider the following interesting interleavings; note that the >+ fences are omitted since they are just there to ensure sequential consistency: >+ >+ Uninitialized->M1->M2->C1->C2: Compiler sees IsWatching because of the mutator's store >+ operation, and the variable is folded. The fencing ensures that C2 sees the value >+ stored in M1 - i.e. we fold on the value that will actually be watchpointed. If >+ before the compilation is installed the mutator executes another store then we >+ will be sure that it will be a complete sequence of M1+M2 since compilations get >+ installed at safepoints and never "in the middle" of a put_to_scope. Hence that >+ compilation installation will be invalidated. If the M1+M2 sequence happens after >+ the code is installed, then the code will be invalidated by triggering a jettison. >+ >+ Uninitialized->M1->C1->C2->M2: Compiler sees Uninitialized and will not fold. This is >+ a sensible outcome since if the compiler read the variable's value, it would have >+ seen Undefined. >+ >+ Uninitialized->C1->C2->M1->M2: Compiler sees Uninitialized and will not fold. >+ Uninitialized->C1->M1->C2->M2: Compiler sees Uninitialized and will not fold. >+ Uninitialized->C1->M1->M2->C2: Compiler sees Uninitialized and will not fold. >+ Uninitialized->M1->C1->M2->C2: Compiler sees Uninitialized and will not fold. >+ >+ IsWatched->M1->M2->C1->C2: Compiler sees IsInvalidated and will not fold. >+ >+ IsWatched->M1->C1->C2->M2: Compiler will fold, but will also register a desired >+ watchpoint, and that watchpoint will get invalidated before the code is installed. >+ >+ IsWatched->M1->C1->M2->C2: As above, will fold but the code will get invalidated. >+ IsWatched->C1->C2->M1->M2: As above, will fold but the code will get invalidated. >+ IsWatched->C1->M1->C2->M2: As above, will fold but the code will get invalidated. >+ IsWatched->C1->M1->M2->C2: As above, will fold but the code will get invalidated. >+ >+ Note that this kind of reasoning shows why having the mutator first bump the state and >+ then store the new value would be wrong. If we had done that (M1 = bump state, M2 = >+ execute put) then we could have the following deadly interleavings: >+ >+ Uninitialized->M1->C1->C2->M2: >+ Uninitialized->M1->C1->M2->C2: Mutator bumps the state to IsWatched and then the >+ compiler folds Undefined, since M2 hasn't executed yet. Although C2 will set the >+ watchpoint, M1 didn't notify it - it mearly initiated watching. M2 then stores a >+ value other than Undefined, and you're toast. >+ >+ You could fix this sort of thing by making the Desired Watchpoints machinery more >+ sophisticated, for example having it track the value that was folded; if the global >+ variable's value was later found to be different then we could invalidate the >+ compilation. I decided to instead just use the right interleaving. >+ >+ This is a 0.5% speed-up on SunSpider, mostly due to a 20% speed-up on math-cordic. >+ It's a 0.6% slow-down on LongSpider, mostly due to a 25% slow-down on 3d-cube. This is >+ because 3d-cube takes global variable assignment slow paths very often. Note that this >+ 3d-cube slow-down doesn't manifest as much in SunSpider (only 6% there). This patch is >+ also a 1.5% speed-up on V8v7 and a 2.8% speed-up on Octane v1, mostly due to deltablue >+ (3.7%), richards (4%), and mandreel (26%). This is a 2% speed-up on Kraken, mostly due >+ to a 17.5% speed-up on imaging-gaussian-blur. Something that really illustrates the >+ slam-dunk-itude of this patch is the wide range of speed-ups on JSRegress. Casual JS >+ programming often leads to global-var-based idioms and those variables tend to be >+ assigned once, leading to excellent constant folding opportunities in an optimizing >+ JIT. This is very evident in the speed-ups on JSRegress. >+ >+ This still needs some porting and build love, but otherwise it's ready to review. >+ >+ * assembler/MacroAssemblerX86Common.h: >+ (JSC::MacroAssemblerX86Common::memoryFence): >+ * assembler/MacroAssemblerX86_64.h: >+ (JSC::MacroAssemblerX86_64::load8): >+ * assembler/X86Assembler.h: >+ (JSC::X86Assembler::mfence): >+ (JSC::X86Assembler::X86InstructionFormatter::threeByteOp): >+ * bytecode/CodeBlock.cpp: >+ (JSC::CodeBlock::CodeBlock): >+ * bytecode/Watchpoint.cpp: >+ (JSC::WatchpointSet::WatchpointSet): >+ (JSC::WatchpointSet::add): >+ (JSC::WatchpointSet::notifyWriteSlow): >+ * bytecode/Watchpoint.h: >+ (JSC::WatchpointSet::state): >+ (JSC::WatchpointSet::isStillValid): >+ (JSC::WatchpointSet::addressOfSetIsNotEmpty): >+ * dfg/DFGAbstractInterpreterInlines.h: >+ (JSC::DFG::::executeEffects): >+ * dfg/DFGByteCodeParser.cpp: >+ (JSC::DFG::ByteCodeParser::getJSConstantForValue): >+ (JSC::DFG::ByteCodeParser::getJSConstant): >+ (JSC::DFG::ByteCodeParser::parseBlock): >+ * dfg/DFGClobberize.h: >+ (JSC::DFG::clobberize): >+ * dfg/DFGFixupPhase.cpp: >+ (JSC::DFG::FixupPhase::fixupNode): >+ * dfg/DFGNode.h: >+ (JSC::DFG::Node::isStronglyProvedConstantIn): >+ (JSC::DFG::Node::hasIdentifierNumberForCheck): >+ (JSC::DFG::Node::hasRegisterPointer): >+ * dfg/DFGNodeFlags.h: >+ * dfg/DFGNodeType.h: >+ * dfg/DFGOperations.cpp: >+ * dfg/DFGOperations.h: >+ * dfg/DFGPredictionPropagationPhase.cpp: >+ (JSC::DFG::PredictionPropagationPhase::propagate): >+ * dfg/DFGSafeToExecute.h: >+ (JSC::DFG::safeToExecute): >+ * dfg/DFGSpeculativeJIT.cpp: >+ (JSC::DFG::SpeculativeJIT::compileNotifyPutGlobalVar): >+ * dfg/DFGSpeculativeJIT.h: >+ (JSC::DFG::SpeculativeJIT::callOperation): >+ * dfg/DFGSpeculativeJIT64.cpp: >+ (JSC::DFG::SpeculativeJIT::compile): >+ * jit/JIT.h: >+ * jit/JITOperations.h: >+ * jit/JITPropertyAccess.cpp: >+ (JSC::JIT::emitPutGlobalVar): >+ (JSC::JIT::emit_op_put_to_scope): >+ (JSC::JIT::emitSlow_op_put_to_scope): >+ * llint/LowLevelInterpreter64.asm: >+ * offlineasm/arm.rb: >+ * offlineasm/arm64.rb: >+ * offlineasm/cloop.rb: >+ * offlineasm/instructions.rb: >+ * offlineasm/x86.rb: >+ * runtime/JSGlobalObject.cpp: >+ (JSC::JSGlobalObject::addGlobalVar): >+ * runtime/JSGlobalObject.h: >+ (JSC::JSGlobalObject::addVar): >+ (JSC::JSGlobalObject::addConst): >+ (JSC::JSGlobalObject::addFunction): >+ * runtime/JSScope.cpp: >+ (JSC::abstractAccess): >+ * runtime/SymbolTable.cpp: >+ (JSC::SymbolTableEntry::couldBeWatched): >+ (JSC::SymbolTableEntry::prepareToWatch): >+ (JSC::SymbolTableEntry::notifyWriteSlow): >+ * runtime/SymbolTable.h: >+ > 2013-11-18 Michael Saboff <msaboff@apple.com> > > ARM64 CRASH: Debug builds crash in emitPointerValidation() >Index: Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h >=================================================================== >--- Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h (revision 159462) >+++ Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h (working copy) >@@ -624,6 +624,25 @@ public: > #endif > m_assembler.movb_rm(src, address.offset, address.base, address.index, address.scale); > } >+ >+ void store8(RegisterID src, Address address) >+ { >+#if CPU(X86) >+ // On 32-bit x86 we can only store from the first 4 registers; >+ // esp..edi are mapped to the 'h' registers! >+ if (src >= 4) { >+ // Pick a temporary register. >+ RegisterID temp = getUnusedRegister(address); >+ >+ // Swap to the temporary register to perform the store. >+ swap(src, temp); >+ m_assembler.movb_rm(temp, address.offset, address.base); >+ swap(src, temp); >+ return; >+ } >+#endif >+ m_assembler.movb_rm(src, address.offset, address.base); >+ } > > void store16(RegisterID src, BaseIndex address) > { >@@ -1419,6 +1438,11 @@ public: > { > m_assembler.nop(); > } >+ >+ void memoryFence() >+ { >+ m_assembler.mfence(); >+ } > > static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) > { >Index: Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h >=================================================================== >--- Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h (revision 159462) >+++ Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h (working copy) >@@ -47,6 +47,7 @@ public: > using MacroAssemblerX86Common::branchAdd32; > using MacroAssemblerX86Common::or32; > using MacroAssemblerX86Common::sub32; >+ using MacroAssemblerX86Common::load8; > using MacroAssemblerX86Common::load32; > using MacroAssemblerX86Common::store32; > using MacroAssemblerX86Common::store8; >@@ -91,6 +92,12 @@ public: > move(TrustedImmPtr(address.m_ptr), scratchRegister); > sub32(imm, Address(scratchRegister)); > } >+ >+ void load8(const void* address, RegisterID dest) >+ { >+ move(TrustedImmPtr(address), dest); >+ load8(dest, dest); >+ } > > void load32(const void* address, RegisterID dest) > { >@@ -126,6 +133,12 @@ public: > store8(imm, Address(scratchRegister)); > } > >+ void store8(RegisterID reg, void* address) >+ { >+ move(TrustedImmPtr(address), scratchRegister); >+ store8(reg, Address(scratchRegister)); >+ } >+ > Call call() > { > DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); >Index: Source/JavaScriptCore/assembler/X86Assembler.h >=================================================================== >--- Source/JavaScriptCore/assembler/X86Assembler.h (revision 159462) >+++ Source/JavaScriptCore/assembler/X86Assembler.h (working copy) >@@ -267,6 +267,7 @@ private: > OP2_MOVD_EdVd = 0x7E, > OP2_JCC_rel32 = 0x80, > OP_SETCC = 0x90, >+ OP2_3BYTE_ESCAPE = 0xAE, > OP2_IMUL_GvEv = 0xAF, > OP2_MOVZX_GvEb = 0xB6, > OP2_MOVSX_GvEb = 0xBE, >@@ -277,6 +278,10 @@ private: > OP2_PSRLQ_UdqIb = 0x73, > OP2_POR_VdqWdq = 0XEB, > } TwoByteOpcodeID; >+ >+ typedef enum { >+ OP3_MFENCE = 0xF0, >+ } ThreeByteOpcodeID; > > TwoByteOpcodeID jccRel32(Condition cond) > { >@@ -1303,6 +1308,11 @@ public: > m_formatter.immediate8(imm); > } > >+ void movb_rm(RegisterID src, int offset, RegisterID base) >+ { >+ m_formatter.oneByteOp8(OP_MOV_EbGb, src, base, offset); >+ } >+ > void movb_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) > { > m_formatter.oneByteOp8(OP_MOV_EbGb, src, base, index, scale, offset); >@@ -1873,7 +1883,7 @@ public: > m_formatter.prefix(PRE_SSE_F2); > m_formatter.twoByteOp(OP2_SQRTSD_VsdWsd, (RegisterID)dst, (RegisterID)src); > } >- >+ > // Misc instructions: > > void int3() >@@ -1890,6 +1900,11 @@ public: > { > m_formatter.prefix(PRE_PREDICT_BRANCH_NOT_TAKEN); > } >+ >+ void mfence() >+ { >+ m_formatter.threeByteOp(OP3_MFENCE); >+ } > > // Assembler admin methods: > >@@ -2301,6 +2316,14 @@ private: > } > #endif > >+ void threeByteOp(ThreeByteOpcodeID opcode) >+ { >+ m_buffer.ensureSpace(maxInstructionSize); >+ m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); >+ m_buffer.putByteUnchecked(OP2_3BYTE_ESCAPE); >+ m_buffer.putByteUnchecked(opcode); >+ } >+ > #if CPU(X86_64) > // Quad-word-sized operands: > // >@@ -2413,6 +2436,14 @@ private: > registerModRM(reg, rm); > } > >+ void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) >+ { >+ m_buffer.ensureSpace(maxInstructionSize); >+ emitRexIf(byteRegRequiresRex(reg) || byteRegRequiresRex(base), reg, 0, base); >+ m_buffer.putByteUnchecked(opcode); >+ memoryModRM(reg, base, offset); >+ } >+ > void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) > { > m_buffer.ensureSpace(maxInstructionSize); >Index: Source/JavaScriptCore/bytecode/CodeBlock.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/CodeBlock.cpp (revision 159462) >+++ Source/JavaScriptCore/bytecode/CodeBlock.cpp (working copy) >@@ -1837,9 +1837,6 @@ CodeBlock::CodeBlock(ScriptExecutable* o > if (entry.isNull()) > break; > >- // It's likely that we'll write to this var, so notify now and avoid the overhead of doing so at runtime. >- entry.notifyWrite(); >- > instructions[i + 0] = vm()->interpreter->getOpcode(op_init_global_const); > instructions[i + 1] = &m_globalObject->registerAt(entry.getIndex()); > break; >Index: Source/JavaScriptCore/bytecode/Watchpoint.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/Watchpoint.cpp (revision 159462) >+++ Source/JavaScriptCore/bytecode/Watchpoint.cpp (working copy) >@@ -40,6 +40,7 @@ Watchpoint::~Watchpoint() > > WatchpointSet::WatchpointSet(WatchpointState state) > : m_state(state) >+ , m_setIsNotEmpty(false) > { > } > >@@ -60,6 +61,7 @@ void WatchpointSet::add(Watchpoint* watc > if (!watchpoint) > return; > m_set.push(watchpoint); >+ m_setIsNotEmpty = true; > m_state = IsWatched; > } > >@@ -67,6 +69,7 @@ void WatchpointSet::notifyWriteSlow() > { > ASSERT(state() == IsWatched); > >+ WTF::storeStoreFence(); > fireAllWatchpoints(); > m_state = IsInvalidated; > WTF::storeStoreFence(); >Index: Source/JavaScriptCore/bytecode/Watchpoint.h >=================================================================== >--- Source/JavaScriptCore/bytecode/Watchpoint.h (revision 159462) >+++ Source/JavaScriptCore/bytecode/Watchpoint.h (working copy) >@@ -59,7 +59,18 @@ public: > WatchpointSet(WatchpointState); > ~WatchpointSet(); // Note that this will not fire any of the watchpoints; if you need to know when a WatchpointSet dies then you need a separate mechanism for this. > >- WatchpointState state() const { return static_cast<WatchpointState>(m_state); } >+ // It is safe to call this from another thread. It may return an old >+ // state. Guarantees that if *first* read the state() of the thing being >+ // watched and it returned IsWatched and *second* you actually read its >+ // value then it's safe to assume that if the state being watched changes >+ // then also the watchpoint state() will change to IsInvalidated. >+ WatchpointState state() const >+ { >+ WTF::loadLoadFence(); >+ WatchpointState result = static_cast<WatchpointState>(m_state); >+ WTF::loadLoadFence(); >+ return result; >+ } > > // It is safe to call this from another thread. It may return true > // even if the set actually had been invalidated, but that ought to happen >@@ -69,7 +80,6 @@ public: > // issuing a load-load fence prior to querying the state. > bool isStillValid() const > { >- WTF::loadLoadFence(); > return state() != IsInvalidated; > } > // Like isStillValid(), may be called from another thread. >@@ -99,6 +109,7 @@ public: > } > > int8_t* addressOfState() { return &m_state; } >+ int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; } > > JS_EXPORT_PRIVATE void notifyWriteSlow(); // Call only if you've checked isWatched. > >@@ -109,6 +120,7 @@ private: > > SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint>> m_set; > int8_t m_state; >+ int8_t m_setIsNotEmpty; > }; > > // InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which >Index: Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (revision 159462) >+++ Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (working copy) >@@ -1511,6 +1511,7 @@ bool AbstractInterpreter<AbstractStateTy > break; > > case PutGlobalVar: >+ case NotifyPutGlobalVar: > break; > > case CheckHasInstance: >Index: Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (revision 159462) >+++ Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (working copy) >@@ -516,7 +516,7 @@ private: > // constant folding. I.e. creating constants using this if we had constant > // field inference would be a bad idea, since the bytecode parser's folding > // doesn't handle liveness preservation. >- Node* getJSConstantForValue(JSValue constantValue) >+ Node* getJSConstantForValue(JSValue constantValue, NodeFlags flags = NodeIsStaticConstant) > { > unsigned constantIndex; > if (!m_codeBlock->findConstant(constantValue, constantIndex)) { >@@ -526,16 +526,17 @@ private: > > ASSERT(m_constants.size() == m_codeBlock->numberOfConstantRegisters()); > >- return getJSConstant(constantIndex); >+ return getJSConstant(constantIndex, flags); > } > >- Node* getJSConstant(unsigned constant) >+ Node* getJSConstant(unsigned constant, NodeFlags flags = NodeIsStaticConstant) > { > Node* node = m_constants[constant].asJSValue; > if (node) > return node; > > Node* result = addToGraph(JSConstant, OpInfo(constant)); >+ result->mergeFlags(flags); > m_constants[constant].asJSValue = result; > return result; > } >@@ -3100,7 +3101,10 @@ bool ByteCodeParser::parseBlock(unsigned > > addToGraph(GlobalVarWatchpoint, OpInfo(operand), OpInfo(identifierNumber)); > JSValue specificValue = globalObject->registerAt(entry.getIndex()).get(); >- set(VirtualRegister(dst), cellConstant(specificValue.asCell())); >+ if (specificValue.isCell()) >+ set(VirtualRegister(dst), cellConstant(specificValue.asCell())); >+ else >+ set(VirtualRegister(dst), getJSConstantForValue(specificValue, 0)); > break; > } > case ClosureVar: >@@ -3123,12 +3127,13 @@ bool ByteCodeParser::parseBlock(unsigned > ResolveType resolveType = ResolveModeAndType(currentInstruction[4].u.operand).type(); > StringImpl* uid = m_graph.identifiers()[identifierNumber]; > >- Structure* structure; >+ Structure* structure = 0; >+ WatchpointSet* watchpoints = 0; > uintptr_t operand; > { > ConcurrentJITLocker locker(m_inlineStackTop->m_profiledBlock->m_lock); > if (resolveType == GlobalVar || resolveType == GlobalVarWithVarInjectionChecks) >- structure = 0; >+ watchpoints = currentInstruction[5].u.watchpointSet; > else > structure = currentInstruction[5].u.structure.get(); > operand = reinterpret_cast<uintptr_t>(currentInstruction[6].u.pointer); >@@ -3153,10 +3158,11 @@ bool ByteCodeParser::parseBlock(unsigned > } > case GlobalVar: > case GlobalVarWithVarInjectionChecks: { >- addToGraph(Phantom, get(VirtualRegister(scope))); > SymbolTableEntry entry = globalObject->symbolTable()->get(uid); >- ASSERT(!entry.couldBeWatched() || !m_graph.watchpoints().isStillValid(entry.watchpointSet())); >+ ASSERT(watchpoints == entry.watchpointSet()); > addToGraph(PutGlobalVar, OpInfo(operand), get(VirtualRegister(value))); >+ if (watchpoints->state() != IsInvalidated) >+ addToGraph(NotifyPutGlobalVar, OpInfo(operand), OpInfo(identifierNumber)); > // Keep scope alive until after put. > addToGraph(Phantom, get(VirtualRegister(scope))); > break; >Index: Source/JavaScriptCore/dfg/DFGClobberize.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGClobberize.h (revision 159462) >+++ Source/JavaScriptCore/dfg/DFGClobberize.h (working copy) >@@ -142,6 +142,10 @@ void clobberize(Graph& graph, Node* node > case InvalidationPoint: > write(SideState); > return; >+ >+ case NotifyPutGlobalVar: >+ write(Watchpoint_fire); >+ return; > > case CreateActivation: > case CreateArguments: >Index: Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (revision 159462) >+++ Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (working copy) >@@ -903,6 +903,7 @@ private: > case GetClosureVar: > case GetGlobalVar: > case PutGlobalVar: >+ case NotifyPutGlobalVar: > case GlobalVarWatchpoint: > case VarInjectionWatchpoint: > case AllocationProfileWatchpoint: >Index: Source/JavaScriptCore/dfg/DFGNode.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGNode.h (revision 159462) >+++ Source/JavaScriptCore/dfg/DFGNode.h (working copy) >@@ -359,7 +359,8 @@ struct Node { > > bool isStronglyProvedConstantIn(InlineCallFrame* inlineCallFrame) > { >- return isConstant() && codeOrigin.inlineCallFrame == inlineCallFrame; >+ return !!(flags() & NodeIsStaticConstant) >+ && codeOrigin.inlineCallFrame == inlineCallFrame; > } > > bool isStronglyProvedConstantIn(const CodeOrigin& codeOrigin) >@@ -749,7 +750,7 @@ struct Node { > > bool hasIdentifierNumberForCheck() > { >- return op() == GlobalVarWatchpoint; >+ return op() == GlobalVarWatchpoint || op() == NotifyPutGlobalVar; > } > > unsigned identifierNumberForCheck() >@@ -760,7 +761,7 @@ struct Node { > > bool hasRegisterPointer() > { >- return op() == GetGlobalVar || op() == PutGlobalVar || op() == GlobalVarWatchpoint; >+ return op() == GetGlobalVar || op() == PutGlobalVar || op() == GlobalVarWatchpoint || op() == NotifyPutGlobalVar; > } > > WriteBarrier<Unknown>* registerPointer() >Index: Source/JavaScriptCore/dfg/DFGNodeFlags.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGNodeFlags.h (revision 159462) >+++ Source/JavaScriptCore/dfg/DFGNodeFlags.h (working copy) >@@ -70,6 +70,8 @@ namespace JSC { namespace DFG { > > #define NodeExitsForward 0x8000 > >+#define NodeIsStaticConstant 0x10000 // Used only by the parser, to determine if a constant arose statically and hence could be folded at parse-time. >+ > typedef uint32_t NodeFlags; > > static inline bool bytecodeUsesAsNumber(NodeFlags flags) >Index: Source/JavaScriptCore/dfg/DFGNodeType.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGNodeType.h (revision 159462) >+++ Source/JavaScriptCore/dfg/DFGNodeType.h (working copy) >@@ -184,6 +184,7 @@ namespace JSC { namespace DFG { > macro(PutClosureVar, NodeMustGenerate) \ > macro(GetGlobalVar, NodeResultJS) \ > macro(PutGlobalVar, NodeMustGenerate) \ >+ macro(NotifyPutGlobalVar, NodeMustGenerate) \ > macro(GlobalVarWatchpoint, NodeMustGenerate) \ > macro(VarInjectionWatchpoint, NodeMustGenerate) \ > macro(CheckFunction, NodeMustGenerate) \ >Index: Source/JavaScriptCore/dfg/DFGOperations.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGOperations.cpp (revision 159462) >+++ Source/JavaScriptCore/dfg/DFGOperations.cpp (working copy) >@@ -998,6 +998,14 @@ char* JIT_OPERATION operationSwitchStrin > return static_cast<char*>(exec->codeBlock()->stringSwitchJumpTable(tableIndex).ctiForValue(string->value(exec).impl()).executableAddress()); > } > >+void JIT_OPERATION operationNotifyWrite(ExecState* exec, WatchpointSet* set) >+{ >+ VM& vm = exec->vm(); >+ NativeCallFrameTracer tracer(&vm, exec); >+ >+ set->notifyWrite(); >+} >+ > double JIT_OPERATION operationFModOnInts(int32_t a, int32_t b) > { > return fmod(a, b); >Index: Source/JavaScriptCore/dfg/DFGOperations.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGOperations.h (revision 159462) >+++ Source/JavaScriptCore/dfg/DFGOperations.h (working copy) >@@ -125,6 +125,7 @@ JSCell* JIT_OPERATION operationMakeRope2 > JSCell* JIT_OPERATION operationMakeRope3(ExecState*, JSString*, JSString*, JSString*); > char* JIT_OPERATION operationFindSwitchImmTargetForDouble(ExecState*, EncodedJSValue, size_t tableIndex); > char* JIT_OPERATION operationSwitchString(ExecState*, size_t tableIndex, JSString*); >+void JIT_OPERATION operationNotifyWrite(ExecState*, WatchpointSet*); > > #if ENABLE(FTL_JIT) > // FIXME: Make calls work well. Currently they're a pure regression. >Index: Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp (revision 159462) >+++ Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp (working copy) >@@ -515,7 +515,9 @@ private: > case CheckTierUpInLoop: > case CheckTierUpAtReturn: > case CheckTierUpAndOSREnter: >- case InvalidationPoint: { >+ case InvalidationPoint: >+ case Int52ToValue: >+ case Int52ToDouble: { > // This node should never be visible at this stage of compilation. It is > // inserted by fixup(), which follows this phase. > RELEASE_ASSERT_NOT_REACHED(); >@@ -580,8 +582,7 @@ private: > case CheckWatchdogTimer: > case Unreachable: > case LoopHint: >- case Int52ToValue: >- case Int52ToDouble: >+ case NotifyPutGlobalVar: > break; > > // This gets ignored because it already has a prediction. >Index: Source/JavaScriptCore/dfg/DFGSafeToExecute.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSafeToExecute.h (revision 159462) >+++ Source/JavaScriptCore/dfg/DFGSafeToExecute.h (working copy) >@@ -242,6 +242,7 @@ bool safeToExecute(AbstractStateType& st > case Int52ToDouble: > case Int52ToValue: > case InvalidationPoint: >+ case NotifyPutGlobalVar: > return true; > > case GetByVal: >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (revision 159462) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (working copy) >@@ -4368,6 +4368,11 @@ void SpeculativeJIT::compile(Node* node) > noResult(node); > break; > } >+ >+ case NotifyPutGlobalVar: { >+ compileNotifyPutGlobalVar(node); >+ break; >+ } > > case VarInjectionWatchpoint: { > noResult(node); >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (revision 159462) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (working copy) >@@ -5601,6 +5601,44 @@ void SpeculativeJIT::emitSwitch(Node* no > RELEASE_ASSERT_NOT_REACHED(); > } > >+void SpeculativeJIT::compileNotifyPutGlobalVar(Node* node) >+{ >+ WatchpointSet* set = m_jit.globalObjectFor(node->codeOrigin)->symbolTable()->get( >+ m_jit.graph().identifiers()[node->identifierNumberForCheck()]).watchpointSet(); >+ >+ GPRTemporary temp(this); >+ GPRReg tempGPR = temp.gpr(); >+ >+ m_jit.load8(set->addressOfState(), tempGPR); >+ >+ JITCompiler::JumpList ready; >+ >+ ready.append(m_jit.branch32(JITCompiler::Equal, tempGPR, TrustedImm32(IsInvalidated))); >+ >+ if (set->state() == ClearWatchpoint) { >+ JITCompiler::Jump isWatched = >+ m_jit.branch32(JITCompiler::NotEqual, tempGPR, TrustedImm32(ClearWatchpoint)); >+ >+ m_jit.store8(TrustedImm32(IsWatched), set->addressOfState()); >+ ready.append(m_jit.jump()); >+ >+ isWatched.link(&m_jit); >+ } >+ >+ JITCompiler::Jump slowCase = m_jit.branchTest8( >+ JITCompiler::NonZero, JITCompiler::AbsoluteAddress(set->addressOfSetIsNotEmpty())); >+ m_jit.store8(TrustedImm32(IsInvalidated), set->addressOfState()); >+ >+ ready.link(&m_jit); >+ >+ addSlowPathGenerator( >+ slowPathCall(slowCase, this, operationNotifyWrite, NoResult, set)); >+ >+ m_jit.memoryFence(); >+ >+ noResult(node); >+} >+ > void SpeculativeJIT::addBranch(const MacroAssembler::JumpList& jump, BasicBlock* destination) > { > for (unsigned i = jump.jumps().size(); i--;) >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (revision 159462) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (working copy) >@@ -1084,9 +1084,9 @@ public: > return appendCallWithExceptionCheck(operation); > } > >- JITCompiler::Call callOperation(V_JITOperation_W operation, WatchpointSet* watchpointSet) >+ JITCompiler::Call callOperation(V_JITOperation_EW operation, WatchpointSet* watchpointSet) > { >- m_jit.setupArguments(TrustedImmPtr(watchpointSet)); >+ m_jit.setupArgumentsWithExecState(TrustedImmPtr(watchpointSet)); > return appendCall(operation); > } > >@@ -2045,6 +2045,8 @@ public: > void compileNewFunctionExpression(Node*); > bool compileRegExpExec(Node*); > >+ void compileNotifyPutGlobalVar(Node*); >+ > // size can be an immediate or a register, and must be in bytes. If size is a register, > // it must be a different register than resultGPR. Emits code that place a pointer to > // the end of the allocation. The returned jump is the jump to the slow path. >Index: Source/JavaScriptCore/jit/JIT.h >=================================================================== >--- Source/JavaScriptCore/jit/JIT.h (revision 159462) >+++ Source/JavaScriptCore/jit/JIT.h (working copy) >@@ -613,7 +613,7 @@ namespace JSC { > void emitGetGlobalVar(uintptr_t operand); > void emitGetClosureVar(int scope, uintptr_t operand); > void emitPutGlobalProperty(uintptr_t* operandSlot, int value); >- void emitPutGlobalVar(uintptr_t operand, int value); >+ void emitPutGlobalVar(uintptr_t operand, int value, WatchpointSet*); > void emitPutClosureVar(int scope, uintptr_t operand, int value); > > void emitInitRegister(int dst); >Index: Source/JavaScriptCore/jit/JITOperations.h >=================================================================== >--- Source/JavaScriptCore/jit/JITOperations.h (revision 159462) >+++ Source/JavaScriptCore/jit/JITOperations.h (working copy) >@@ -154,7 +154,7 @@ typedef void JIT_OPERATION (*V_JITOperat > typedef void JIT_OPERATION (*V_JITOperation_EPc)(ExecState*, Instruction*); > typedef void JIT_OPERATION (*V_JITOperation_EPZJ)(ExecState*, void*, int32_t, EncodedJSValue); > typedef void JIT_OPERATION (*V_JITOperation_ESsiJJI)(ExecState*, StructureStubInfo*, EncodedJSValue, EncodedJSValue, StringImpl*); >-typedef void JIT_OPERATION (*V_JITOperation_W)(WatchpointSet*); >+typedef void JIT_OPERATION (*V_JITOperation_EW)(ExecState*, WatchpointSet*); > typedef void JIT_OPERATION (*V_JITOperation_EZ)(ExecState*, int32_t); > typedef void JIT_OPERATION (*V_JITOperation_EVm)(ExecState*, VM*); > typedef char* JIT_OPERATION (*P_JITOperation_E)(ExecState*); >Index: Source/JavaScriptCore/jit/JITPropertyAccess.cpp >=================================================================== >--- Source/JavaScriptCore/jit/JITPropertyAccess.cpp (revision 159462) >+++ Source/JavaScriptCore/jit/JITPropertyAccess.cpp (working copy) >@@ -771,10 +771,36 @@ void JIT::emitPutGlobalProperty(uintptr_ > storePtr(regT2, BaseIndex(regT0, regT1, TimesEight, (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue))); > } > >-void JIT::emitPutGlobalVar(uintptr_t operand, int value) >+void JIT::emitPutGlobalVar(uintptr_t operand, int value, WatchpointSet* set) > { >+ if (set && set->state() != IsInvalidated) { >+ load8(set->addressOfState(), regT1); >+ >+ JumpList ready; >+ >+ ready.append(branch32(Equal, regT1, TrustedImm32(IsInvalidated))); >+ >+ if (set->state() == ClearWatchpoint) { >+ Jump isWatched = branch32(NotEqual, regT1, TrustedImm32(ClearWatchpoint)); >+ >+ move(TrustedImm32(IsWatched), regT1); >+ ready.append(jump()); >+ >+ isWatched.link(this); >+ } >+ >+ addSlowCase(branchTest8(NonZero, AbsoluteAddress(set->addressOfSetIsNotEmpty()))); >+ move(TrustedImm32(IsInvalidated), regT1); >+ ready.link(this); >+ } >+ > emitGetVirtualRegister(value, regT0); > storePtr(regT0, reinterpret_cast<void*>(operand)); >+ >+ if (set && set->state() != IsInvalidated) { >+ memoryFence(); >+ store8(regT1, set->addressOfState()); >+ } > } > > void JIT::emitPutClosureVar(int scope, uintptr_t operand, int value) >@@ -802,7 +828,7 @@ void JIT::emit_op_put_to_scope(Instructi > case GlobalVar: > case GlobalVarWithVarInjectionChecks: > emitVarInjectionCheck(needsVarInjectionChecks(resolveType)); >- emitPutGlobalVar(*operandSlot, value); >+ emitPutGlobalVar(*operandSlot, value, currentInstruction[5].u.watchpointSet); > break; > case ClosureVar: > case ClosureVarWithVarInjectionChecks: >@@ -818,10 +844,16 @@ void JIT::emit_op_put_to_scope(Instructi > void JIT::emitSlow_op_put_to_scope(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) > { > ResolveType resolveType = ResolveModeAndType(currentInstruction[4].u.operand).type(); >- if (resolveType == GlobalVar || resolveType == ClosureVar) >+ unsigned linkCount = 0; >+ if (resolveType != GlobalVar && resolveType != ClosureVar) >+ linkCount++; >+ if ((resolveType == GlobalVar || resolveType == GlobalVarWithVarInjectionChecks) >+ && currentInstruction[5].u.watchpointSet->state() != IsInvalidated) >+ linkCount++; >+ if (!linkCount) > return; >- >- linkSlowCase(iter); >+ while (linkCount--) >+ linkSlowCase(iter); > callOperation(operationPutToScope, currentInstruction); > } > >Index: Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >=================================================================== >--- Source/JavaScriptCore/llint/LowLevelInterpreter64.asm (revision 159462) >+++ Source/JavaScriptCore/llint/LowLevelInterpreter64.asm (working copy) >@@ -1955,10 +1955,22 @@ macro putProperty() > end > > macro putGlobalVar() >+ loadpFromInstruction(5, t2) >+ loadb WatchpointSet::m_state[t2], t3 >+ bieq t3, IsInvalidated, .ready >+ bineq t3, ClearWatchpoint, .needToInvalidate >+ move IsWatched, t3 >+ jmp .ready >+.needToInvalidate: >+ btbnz WatchpointSet::m_setIsNotEmpty[t2], .pDynamic >+ move IsInvalidated, t3 >+.ready: > loadisFromInstruction(3, t0) > loadConstantOrVariable(t0, t1) > loadpFromInstruction(6, t0) > storeq t1, [t0] >+ memfence >+ storeb t3, WatchpointSet::m_state[t2] > end > > macro putClosureVar() >Index: Source/JavaScriptCore/offlineasm/arm64.rb >=================================================================== >--- Source/JavaScriptCore/offlineasm/arm64.rb (revision 159462) >+++ Source/JavaScriptCore/offlineasm/arm64.rb (working copy) >@@ -807,6 +807,8 @@ class Instruction > operands[0].arm64EmitLea(operands[1], :ptr) > when "smulli" > $asm.puts "smaddl #{operands[2].arm64Operand(:ptr)}, #{operands[0].arm64Operand(:int)}, #{operands[1].arm64Operand(:int)}, xzr" >+ when "memfence" >+ $asm.puts "dmb sy" > else > lowerDefault > end >Index: Source/JavaScriptCore/offlineasm/arm.rb >=================================================================== >--- Source/JavaScriptCore/offlineasm/arm.rb (revision 159462) >+++ Source/JavaScriptCore/offlineasm/arm.rb (working copy) >@@ -597,6 +597,8 @@ class Instruction > when "smulli" > raise "Wrong number of arguments to smull in #{self.inspect} at #{codeOriginString}" unless operands.length == 4 > $asm.puts "smull #{operands[2].armOperand}, #{operands[3].armOperand}, #{operands[0].armOperand}, #{operands[1].armOperand}" >+ when "memfence" >+ $asm.puts "dmb sy" > else > lowerDefault > end >Index: Source/JavaScriptCore/offlineasm/cloop.rb >=================================================================== >--- Source/JavaScriptCore/offlineasm/cloop.rb (revision 159462) >+++ Source/JavaScriptCore/offlineasm/cloop.rb (working copy) >@@ -1091,7 +1091,8 @@ class Instruction > cloopEmitOpAndBranch(operands, "|", :int32, "== 0") > when "borrinz" > cloopEmitOpAndBranch(operands, "|", :int32, "!= 0") >- >+ >+ when "memfence" > when "pushCalleeSaves" > when "popCalleeSaves" > >Index: Source/JavaScriptCore/offlineasm/instructions.rb >=================================================================== >--- Source/JavaScriptCore/offlineasm/instructions.rb (revision 159462) >+++ Source/JavaScriptCore/offlineasm/instructions.rb (working copy) >@@ -250,7 +250,8 @@ MACRO_INSTRUCTIONS = > "leai", > "leap", > "pushCalleeSaves", >- "popCalleeSaves" >+ "popCalleeSaves", >+ "memfence" > ] > > X86_INSTRUCTIONS = >Index: Source/JavaScriptCore/offlineasm/x86.rb >=================================================================== >--- Source/JavaScriptCore/offlineasm/x86.rb (revision 159462) >+++ Source/JavaScriptCore/offlineasm/x86.rb (working copy) >@@ -1363,6 +1363,8 @@ class Instruction > $asm.puts "leal #{operands[0].x86AddressOperand(:int)}, #{operands[1].x86Operand(:int)}" > when "leap" > $asm.puts "lea#{x86Suffix(:ptr)} #{operands[0].x86AddressOperand(:ptr)}, #{operands[1].x86Operand(:ptr)}" >+ when "memfence" >+ $asm.puts "mfence" > else > lowerDefault > end >Index: Source/JavaScriptCore/runtime/JSGlobalObject.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/JSGlobalObject.cpp (revision 159462) >+++ Source/JavaScriptCore/runtime/JSGlobalObject.cpp (working copy) >@@ -210,20 +210,20 @@ bool JSGlobalObject::defineOwnProperty(J > return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow); > } > >-int JSGlobalObject::addGlobalVar(const Identifier& ident, ConstantMode constantMode, FunctionMode functionMode) >+int JSGlobalObject::addGlobalVar(const Identifier& ident, ConstantMode constantMode, VarAddMode varAddMode) > { > ConcurrentJITLocker locker(symbolTable()->m_lock); > int index = symbolTable()->size(locker); > SymbolTableEntry newEntry(index, (constantMode == IsConstant) ? ReadOnly : 0); >- if (functionMode == IsFunctionToSpecialize) >- newEntry.attemptToWatch(); >+ if (constantMode == IsVariable) >+ newEntry.prepareToWatch(SymbolTableEntry::NotInitialized); > SymbolTable::Map::AddResult result = symbolTable()->add(locker, ident.impl(), newEntry); > if (result.isNewEntry) > addRegisters(1); >- else { >- result.iterator->value.notifyWrite(); >+ else > index = result.iterator->value.getIndex(); >- } >+ if (constantMode == IsVariable && varAddMode == AddToInitialize) >+ result.iterator->value.notifyWrite(); > return index; > } > >Index: Source/JavaScriptCore/runtime/JSGlobalObject.h >=================================================================== >--- Source/JavaScriptCore/runtime/JSGlobalObject.h (revision 159462) >+++ Source/JavaScriptCore/runtime/JSGlobalObject.h (working copy) >@@ -289,8 +289,8 @@ protected: > } > > enum ConstantMode { IsConstant, IsVariable }; >- enum FunctionMode { IsFunctionToSpecialize, NotFunctionOrNotSpecializable }; >- int addGlobalVar(const Identifier&, ConstantMode, FunctionMode); >+ enum VarAddMode { AddUninitialized, AddToInitialize }; >+ int addGlobalVar(const Identifier&, ConstantMode, VarAddMode); > > public: > JS_EXPORT_PRIVATE ~JSGlobalObject(); >@@ -315,17 +315,17 @@ public: > void addVar(ExecState* exec, const Identifier& propertyName) > { > if (!hasProperty(exec, propertyName)) >- addGlobalVar(propertyName, IsVariable, NotFunctionOrNotSpecializable); >+ addGlobalVar(propertyName, IsVariable, AddUninitialized); > } > void addConst(ExecState* exec, const Identifier& propertyName) > { > if (!hasProperty(exec, propertyName)) >- addGlobalVar(propertyName, IsConstant, NotFunctionOrNotSpecializable); >+ addGlobalVar(propertyName, IsConstant, AddUninitialized); > } > void addFunction(ExecState* exec, const Identifier& propertyName, JSValue value) > { >- bool propertyDidExist = removeDirect(exec->vm(), propertyName); // Newly declared functions overwrite existing properties. >- int index = addGlobalVar(propertyName, IsVariable, !propertyDidExist ? IsFunctionToSpecialize : NotFunctionOrNotSpecializable); >+ removeDirect(exec->vm(), propertyName); // Newly declared functions overwrite existing properties. >+ int index = addGlobalVar(propertyName, IsVariable, AddToInitialize); > registerAt(index).set(exec->vm(), this, value); > } > >Index: Source/JavaScriptCore/runtime/JSScope.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/JSScope.cpp (revision 159462) >+++ Source/JavaScriptCore/runtime/JSScope.cpp (working copy) >@@ -77,15 +77,10 @@ static inline bool abstractAccess(ExecSt > if (JSGlobalObject* globalObject = jsDynamicCast<JSGlobalObject*>(scope)) { > SymbolTableEntry entry = globalObject->symbolTable()->get(ident.impl()); > if (!entry.isNull()) { >- if (getOrPut == Put) { >- if (entry.isReadOnly()) { >- // We know the property will be at global scope, but we don't know how to cache it. >- op = ResolveOp(Dynamic, 0, 0, 0, 0); >- return true; >- } >- >- // It's likely that we'll write to this var, so notify now and avoid the overhead of doing so at runtime. >- entry.notifyWrite(); >+ if (getOrPut == Put && entry.isReadOnly()) { >+ // We know the property will be at global scope, but we don't know how to cache it. >+ op = ResolveOp(Dynamic, 0, 0, 0, 0); >+ return true; > } > > op = ResolveOp( >Index: Source/JavaScriptCore/runtime/JSSymbolTableObject.h >=================================================================== >--- Source/JavaScriptCore/runtime/JSSymbolTableObject.h (revision 159462) >+++ Source/JavaScriptCore/runtime/JSSymbolTableObject.h (working copy) >@@ -123,6 +123,7 @@ inline bool symbolTablePut( > ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(object)); > > WriteBarrierBase<Unknown>* reg; >+ WatchpointSet* set = 0; > { > SymbolTable& symbolTable = *object->symbolTable(); > GCSafeConcurrentJITLocker locker(symbolTable.m_lock, exec->vm().heap); >@@ -137,14 +138,15 @@ inline bool symbolTablePut( > throwTypeError(exec, StrictModeReadonlyPropertyWriteError); > return true; > } >- if (UNLIKELY(wasFat)) >- iter->value.notifyWrite(); >+ set = iter->value.watchpointSet(); > reg = &object->registerAt(fastEntry.getIndex()); > } > // I'd prefer we not hold lock while executing barriers, since I prefer to reserve > // the right for barriers to be able to trigger GC. And I don't want to hold VM > // locks while GC'ing. > reg->set(vm, object, value); >+ if (set) >+ set->notifyWrite(); > return true; > } > >@@ -156,6 +158,7 @@ inline bool symbolTablePutWithAttributes > ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(object)); > > WriteBarrierBase<Unknown>* reg; >+ WatchpointSet* set = 0; > { > SymbolTable& symbolTable = *object->symbolTable(); > ConcurrentJITLocker locker(symbolTable.m_lock); >@@ -164,11 +167,13 @@ inline bool symbolTablePutWithAttributes > return false; > SymbolTableEntry& entry = iter->value; > ASSERT(!entry.isNull()); >- entry.notifyWrite(); >+ set = entry.watchpointSet(); > entry.setAttributes(attributes); > reg = &object->registerAt(entry.getIndex()); > } > reg->set(vm, object, value); >+ if (set) >+ set->notifyWrite(); > return true; > } > >Index: Source/JavaScriptCore/runtime/SymbolTable.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/SymbolTable.cpp (revision 159462) >+++ Source/JavaScriptCore/runtime/SymbolTable.cpp (working copy) >@@ -65,14 +65,15 @@ bool SymbolTableEntry::couldBeWatched() > WatchpointSet* watchpoints = fatEntry()->m_watchpoints.get(); > if (!watchpoints) > return false; >- return watchpoints->isStillValid(); >+ return watchpoints->state() == IsWatched; > } > >-void SymbolTableEntry::attemptToWatch() >+void SymbolTableEntry::prepareToWatch(WatchState state) > { > FatEntry* entry = inflate(); >- if (!entry->m_watchpoints) >- entry->m_watchpoints = adoptRef(new WatchpointSet(IsWatched)); >+ ASSERT(!entry->m_watchpoints); >+ entry->m_watchpoints = adoptRef( >+ new WatchpointSet(state == AlreadyInitialized ? IsWatched : ClearWatchpoint)); > } > > void SymbolTableEntry::addWatchpoint(Watchpoint* watchpoint) >@@ -86,6 +87,12 @@ void SymbolTableEntry::notifyWriteSlow() > WatchpointSet* watchpoints = fatEntry()->m_watchpoints.get(); > if (!watchpoints) > return; >+ >+ if (watchpoints->state() == ClearWatchpoint) { >+ watchpoints->startWatching(); >+ return; >+ } >+ > watchpoints->notifyWrite(); > } > >Index: Source/JavaScriptCore/runtime/SymbolTable.h >=================================================================== >--- Source/JavaScriptCore/runtime/SymbolTable.h (revision 159462) >+++ Source/JavaScriptCore/runtime/SymbolTable.h (working copy) >@@ -220,15 +220,8 @@ struct SymbolTableEntry { > > 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(); >+ enum WatchState { NotInitialized, AlreadyInitialized }; >+ void prepareToWatch(WatchState); > > void addWatchpoint(Watchpoint*); > >Index: LayoutTests/ChangeLog >=================================================================== >--- LayoutTests/ChangeLog (revision 159479) >+++ LayoutTests/ChangeLog (working copy) >@@ -1,3 +1,16 @@ >+2013-11-18 Filip Pizlo <fpizlo@apple.com> >+ >+ Infer constant global variables >+ https://bugs.webkit.org/show_bug.cgi?id=124464 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * js/regress/global-var-const-infer-expected.txt: Added. >+ * js/regress/global-var-const-infer.html: Added. >+ * js/regress/script-tests/global-var-const-infer.js: Added. >+ (foo): >+ (check): >+ > 2013-11-18 Sun-woo Nam <sunny.nam@samsung.com> > > [EFL] Layout tests need to be rebaselined. >Index: LayoutTests/js/regress/global-var-const-infer-expected.txt >=================================================================== >--- LayoutTests/js/regress/global-var-const-infer-expected.txt (revision 0) >+++ LayoutTests/js/regress/global-var-const-infer-expected.txt (working copy) >@@ -0,0 +1,10 @@ >+JSRegress/global-var-const-infer >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+PASS no exception thrown >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >Index: LayoutTests/js/regress/global-var-const-infer.html >=================================================================== >--- LayoutTests/js/regress/global-var-const-infer.html (revision 0) >+++ LayoutTests/js/regress/global-var-const-infer.html (working copy) >@@ -0,0 +1,12 @@ >+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> >+<html> >+<head> >+<script src="../resources/js-test-pre.js"></script> >+</head> >+<body> >+<script src="resources/regress-pre.js"></script> >+<script src="script-tests/global-var-const-infer.js"></script> >+<script src="resources/regress-post.js"></script> >+<script src="../resources/js-test-post.js"></script> >+</body> >+</html> >Index: LayoutTests/js/regress/script-tests/global-var-const-infer.js >=================================================================== >--- LayoutTests/js/regress/script-tests/global-var-const-infer.js (revision 0) >+++ LayoutTests/js/regress/script-tests/global-var-const-infer.js (working copy) >@@ -0,0 +1,27 @@ >+function foo() { >+ return a + b; >+} >+ >+noInline(foo); >+ >+var a = 4; >+var b = 5; >+ >+function check(actual, expected) { >+ if (actual == expected) >+ return; >+ throw "Error: expected " + expected + " but got " + actual; >+} >+ >+for (var i = 0; i < 100; ++i) >+ check(foo(), 9); >+ >+a = 6; >+ >+for (var i = 0; i < 1000; ++i) >+ check(foo(), 11); >+ >+b = 7; >+ >+for (var i = 0; i < 10000; ++i) >+ check(foo(), 13);
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 124464
:
217173
|
217174
|
217258
|
217271
|
217311
|
217321
|
217331
|
217347