12013-11-18 Filip Pizlo <fpizlo@apple.com>
2
3 Infer constant global variables
4 https://bugs.webkit.org/show_bug.cgi?id=124464
5
6 Reviewed by NOBODY (OOPS!).
7
8 All global variables that are candidates for watchpoint-based constant inference (i.e.
9 not 'const' variables) will now have WatchpointSet's associated with them and those
10 are used to drive the inference by tracking three states of each variable:
11
12 Uninitialized: the variable's value is Undefined and the WatchpointSet state is
13 ClearWatchpoint.
14
15 Initialized: the variable's value was set to something (could even be explicitly set
16 to Undefined) and the WatchpointSet state is IsWatching.
17
18 Invalidated: the variable's value was set to something else (could even be the same
19 thing as before but the point is that a put operation did execute again) and the
20 WatchpointSet is IsInvalidated.
21
22 If the compiler tries to compile a GetGlobalVar and the WatchpointSet state is
23 IsWatching, then the current value of the variable can be folded in place of the get,
24 and a watchpoint on the variable can be registered.
25
26 We handle race conditions between the mutator and compiler by mandating that:
27
28 - The mutator changes the WatchpointSet state after executing the put.
29
30 - The compiler checks the WatchpointSet state prior to reading the value.
31
32 The concrete algorithm used by the mutator is:
33
34 1. Store the new value into the variable.
35 --- Execute a store-store fence.
36 2. Bump the state (ClearWatchpoing becomes IsWatching, IsWatching becomes
37 IsInvalidated)
38
39 The concrete algorithm that the compiler uses is:
40
41 1. Load the state. If it's *not* IsWatching, then give up on constant inference.
42 --- Execute a load-load fence.
43 2. Load the value of the variable and use that for folding, while also registering
44 a DesiredWatchpoint. The various parts of this step can be done in any order.
45
46 The desired watchpoint registration will fail if the watchpoint set is already
47 invalidated. Now consider the following interesting interleavings; note that the
48 fences are omitted since they are just there to ensure sequential consistency:
49
50 Uninitialized->M1->M2->C1->C2: Compiler sees IsWatching because of the mutator's store
51 operation, and the variable is folded. The fencing ensures that C2 sees the value
52 stored in M1 - i.e. we fold on the value that will actually be watchpointed. If
53 before the compilation is installed the mutator executes another store then we
54 will be sure that it will be a complete sequence of M1+M2 since compilations get
55 installed at safepoints and never "in the middle" of a put_to_scope. Hence that
56 compilation installation will be invalidated. If the M1+M2 sequence happens after
57 the code is installed, then the code will be invalidated by triggering a jettison.
58
59 Uninitialized->M1->C1->C2->M2: Compiler sees Uninitialized and will not fold. This is
60 a sensible outcome since if the compiler read the variable's value, it would have
61 seen Undefined.
62
63 Uninitialized->C1->C2->M1->M2: Compiler sees Uninitialized and will not fold.
64 Uninitialized->C1->M1->C2->M2: Compiler sees Uninitialized and will not fold.
65 Uninitialized->C1->M1->M2->C2: Compiler sees Uninitialized and will not fold.
66 Uninitialized->M1->C1->M2->C2: Compiler sees Uninitialized and will not fold.
67
68 IsWatched->M1->M2->C1->C2: Compiler sees IsInvalidated and will not fold.
69
70 IsWatched->M1->C1->C2->M2: Compiler will fold, but will also register a desired
71 watchpoint, and that watchpoint will get invalidated before the code is installed.
72
73 IsWatched->M1->C1->M2->C2: As above, will fold but the code will get invalidated.
74 IsWatched->C1->C2->M1->M2: As above, will fold but the code will get invalidated.
75 IsWatched->C1->M1->C2->M2: As above, will fold but the code will get invalidated.
76 IsWatched->C1->M1->M2->C2: As above, will fold but the code will get invalidated.
77
78 Note that this kind of reasoning shows why having the mutator first bump the state and
79 then store the new value would be wrong. If we had done that (M1 = bump state, M2 =
80 execute put) then we could have the following deadly interleavings:
81
82 Uninitialized->M1->C1->C2->M2:
83 Uninitialized->M1->C1->M2->C2: Mutator bumps the state to IsWatched and then the
84 compiler folds Undefined, since M2 hasn't executed yet. Although C2 will set the
85 watchpoint, M1 didn't notify it - it mearly initiated watching. M2 then stores a
86 value other than Undefined, and you're toast.
87
88 You could fix this sort of thing by making the Desired Watchpoints machinery more
89 sophisticated, for example having it track the value that was folded; if the global
90 variable's value was later found to be different then we could invalidate the
91 compilation. I decided to instead just use the right interleaving.
92
93 This is a 0.5% speed-up on SunSpider, mostly due to a 20% speed-up on math-cordic.
94 It's a 0.6% slow-down on LongSpider, mostly due to a 25% slow-down on 3d-cube. This is
95 because 3d-cube takes global variable assignment slow paths very often. Note that this
96 3d-cube slow-down doesn't manifest as much in SunSpider (only 6% there). This patch is
97 also a 1.5% speed-up on V8v7 and a 2.8% speed-up on Octane v1, mostly due to deltablue
98 (3.7%), richards (4%), and mandreel (26%). This is a 2% speed-up on Kraken, mostly due
99 to a 17.5% speed-up on imaging-gaussian-blur. Something that really illustrates the
100 slam-dunk-itude of this patch is the wide range of speed-ups on JSRegress. Casual JS
101 programming often leads to global-var-based idioms and those variables tend to be
102 assigned once, leading to excellent constant folding opportunities in an optimizing
103 JIT. This is very evident in the speed-ups on JSRegress.
104
105 This still needs some porting and build love, but otherwise it's ready to review.
106
107 * assembler/MacroAssemblerX86Common.h:
108 (JSC::MacroAssemblerX86Common::memoryFence):
109 * assembler/MacroAssemblerX86_64.h:
110 (JSC::MacroAssemblerX86_64::load8):
111 * assembler/X86Assembler.h:
112 (JSC::X86Assembler::mfence):
113 (JSC::X86Assembler::X86InstructionFormatter::threeByteOp):
114 * bytecode/CodeBlock.cpp:
115 (JSC::CodeBlock::CodeBlock):
116 * bytecode/Watchpoint.cpp:
117 (JSC::WatchpointSet::WatchpointSet):
118 (JSC::WatchpointSet::add):
119 (JSC::WatchpointSet::notifyWriteSlow):
120 * bytecode/Watchpoint.h:
121 (JSC::WatchpointSet::state):
122 (JSC::WatchpointSet::isStillValid):
123 (JSC::WatchpointSet::addressOfSetIsNotEmpty):
124 * dfg/DFGAbstractInterpreterInlines.h:
125 (JSC::DFG::::executeEffects):
126 * dfg/DFGByteCodeParser.cpp:
127 (JSC::DFG::ByteCodeParser::getJSConstantForValue):
128 (JSC::DFG::ByteCodeParser::getJSConstant):
129 (JSC::DFG::ByteCodeParser::parseBlock):
130 * dfg/DFGClobberize.h:
131 (JSC::DFG::clobberize):
132 * dfg/DFGFixupPhase.cpp:
133 (JSC::DFG::FixupPhase::fixupNode):
134 * dfg/DFGNode.h:
135 (JSC::DFG::Node::isStronglyProvedConstantIn):
136 (JSC::DFG::Node::hasIdentifierNumberForCheck):
137 (JSC::DFG::Node::hasRegisterPointer):
138 * dfg/DFGNodeFlags.h:
139 * dfg/DFGNodeType.h:
140 * dfg/DFGOperations.cpp:
141 * dfg/DFGOperations.h:
142 * dfg/DFGPredictionPropagationPhase.cpp:
143 (JSC::DFG::PredictionPropagationPhase::propagate):
144 * dfg/DFGSafeToExecute.h:
145 (JSC::DFG::safeToExecute):
146 * dfg/DFGSpeculativeJIT.cpp:
147 (JSC::DFG::SpeculativeJIT::compileNotifyPutGlobalVar):
148 * dfg/DFGSpeculativeJIT.h:
149 (JSC::DFG::SpeculativeJIT::callOperation):
150 * dfg/DFGSpeculativeJIT64.cpp:
151 (JSC::DFG::SpeculativeJIT::compile):
152 * jit/JIT.h:
153 * jit/JITOperations.h:
154 * jit/JITPropertyAccess.cpp:
155 (JSC::JIT::emitPutGlobalVar):
156 (JSC::JIT::emit_op_put_to_scope):
157 (JSC::JIT::emitSlow_op_put_to_scope):
158 * llint/LowLevelInterpreter64.asm:
159 * offlineasm/arm.rb:
160 * offlineasm/arm64.rb:
161 * offlineasm/cloop.rb:
162 * offlineasm/instructions.rb:
163 * offlineasm/x86.rb:
164 * runtime/JSGlobalObject.cpp:
165 (JSC::JSGlobalObject::addGlobalVar):
166 * runtime/JSGlobalObject.h:
167 (JSC::JSGlobalObject::addVar):
168 (JSC::JSGlobalObject::addConst):
169 (JSC::JSGlobalObject::addFunction):
170 * runtime/JSScope.cpp:
171 (JSC::abstractAccess):
172 * runtime/SymbolTable.cpp:
173 (JSC::SymbolTableEntry::couldBeWatched):
174 (JSC::SymbolTableEntry::prepareToWatch):
175 (JSC::SymbolTableEntry::notifyWriteSlow):
176 * runtime/SymbolTable.h:
177