Unreviewed, rolling out r156474.
[WebKit-https.git] / Source / JavaScriptCore / dfg / DFGVariableEventStream.cpp
1 /*
2  * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "DFGVariableEventStream.h"
28
29 #if ENABLE(DFG_JIT)
30
31 #include "CodeBlock.h"
32 #include "DFGJITCode.h"
33 #include "DFGValueSource.h"
34 #include "Operations.h"
35 #include <wtf/DataLog.h>
36 #include <wtf/HashMap.h>
37
38 namespace JSC { namespace DFG {
39
40 void VariableEventStream::logEvent(const VariableEvent& event)
41 {
42     dataLogF("seq#%u:", static_cast<unsigned>(size()));
43     event.dump(WTF::dataFile());
44     dataLogF(" ");
45 }
46
47 namespace {
48
49 struct MinifiedGenerationInfo {
50     bool filled; // true -> in gpr/fpr/pair, false -> spilled
51     VariableRepresentation u;
52     DataFormat format;
53     
54     MinifiedGenerationInfo()
55         : format(DataFormatNone)
56     {
57     }
58     
59     void update(const VariableEvent& event)
60     {
61         switch (event.kind()) {
62         case BirthToFill:
63         case Fill:
64             filled = true;
65             break;
66         case BirthToSpill:
67         case Spill:
68             filled = false;
69             break;
70         case Death:
71             format = DataFormatNone;
72             return;
73         default:
74             return;
75         }
76         
77         u = event.variableRepresentation();
78         format = event.dataFormat();
79     }
80 };
81
82 } // namespace
83
84 bool VariableEventStream::tryToSetConstantRecovery(ValueRecovery& recovery, CodeBlock* codeBlock, MinifiedNode* node) const
85 {
86     if (!node)
87         return false;
88     
89     if (node->hasConstantNumber()) {
90         recovery = ValueRecovery::constant(
91             codeBlock->constantRegister(
92                 FirstConstantRegisterIndex + node->constantNumber()).get());
93         return true;
94     }
95     
96     if (node->hasWeakConstant()) {
97         recovery = ValueRecovery::constant(node->weakConstant());
98         return true;
99     }
100     
101     if (node->op() == PhantomArguments) {
102         recovery = ValueRecovery::argumentsThatWereNotCreated();
103         return true;
104     }
105     
106     return false;
107 }
108
109 void VariableEventStream::reconstruct(
110     CodeBlock* codeBlock, CodeOrigin codeOrigin, MinifiedGraph& graph,
111     unsigned index, Operands<ValueRecovery>& valueRecoveries) const
112 {
113     ASSERT(codeBlock->jitType() == JITCode::DFGJIT);
114     CodeBlock* baselineCodeBlock = codeBlock->baselineVersion();
115     
116     unsigned numVariables;
117     if (codeOrigin.inlineCallFrame)
118         numVariables = baselineCodeBlockForInlineCallFrame(codeOrigin.inlineCallFrame)->m_numCalleeRegisters + operandToLocal(codeOrigin.inlineCallFrame->stackOffset);
119     else
120         numVariables = baselineCodeBlock->m_numCalleeRegisters;
121     
122     // Crazy special case: if we're at index == 0 then this must be an argument check
123     // failure, in which case all variables are already set up. The recoveries should
124     // reflect this.
125     if (!index) {
126         valueRecoveries = Operands<ValueRecovery>(codeBlock->numParameters(), numVariables);
127         for (size_t i = 0; i < valueRecoveries.size(); ++i)
128             valueRecoveries[i] = ValueRecovery::alreadyInJSStack();
129         return;
130     }
131     
132     // Step 1: Find the last checkpoint, and figure out the number of virtual registers as we go.
133     unsigned startIndex = index - 1;
134     while (at(startIndex).kind() != Reset)
135         startIndex--;
136     
137 #if DFG_ENABLE(DEBUG_VERBOSE)
138     dataLogF("Computing OSR exit recoveries starting at seq#%u.\n", startIndex);
139 #endif
140
141     // Step 2: Create a mock-up of the DFG's state and execute the events.
142     Operands<ValueSource> operandSources(codeBlock->numParameters(), numVariables);
143     HashMap<MinifiedID, MinifiedGenerationInfo> generationInfos;
144     for (unsigned i = startIndex; i < index; ++i) {
145         const VariableEvent& event = at(i);
146         switch (event.kind()) {
147         case Reset:
148             // nothing to do.
149             break;
150         case BirthToFill:
151         case BirthToSpill: {
152             MinifiedGenerationInfo info;
153             info.update(event);
154             generationInfos.add(event.id(), info);
155             break;
156         }
157         case Fill:
158         case Spill:
159         case Death: {
160             HashMap<MinifiedID, MinifiedGenerationInfo>::iterator iter = generationInfos.find(event.id());
161             ASSERT(iter != generationInfos.end());
162             iter->value.update(event);
163             break;
164         }
165         case MovHintEvent:
166             if (operandSources.hasOperand(event.operand()))
167                 operandSources.setOperand(event.operand(), ValueSource(event.id()));
168             break;
169         case SetLocalEvent:
170             if (operandSources.hasOperand(event.operand()))
171                 operandSources.setOperand(event.operand(), ValueSource::forDataFormat(event.dataFormat()));
172             break;
173         default:
174             RELEASE_ASSERT_NOT_REACHED();
175             break;
176         }
177     }
178     
179     // Step 3: Compute value recoveries!
180     valueRecoveries = Operands<ValueRecovery>(codeBlock->numParameters(), numVariables);
181     for (unsigned i = 0; i < operandSources.size(); ++i) {
182         ValueSource& source = operandSources[i];
183         if (source.isTriviallyRecoverable()) {
184             valueRecoveries[i] = source.valueRecovery();
185             continue;
186         }
187         
188         ASSERT(source.kind() == HaveNode);
189         MinifiedNode* node = graph.at(source.id());
190         if (tryToSetConstantRecovery(valueRecoveries[i], codeBlock, node))
191             continue;
192         
193         MinifiedGenerationInfo info = generationInfos.get(source.id());
194         if (info.format == DataFormatNone) {
195             // Try to see if there is an alternate node that would contain the value we want.
196             //
197             // Backward rewiring refers to:
198             //
199             //     a: Something(...)
200             //     b: Id(@a) // some identity function
201             //     c: SetLocal(@b)
202             //
203             // Where we find @b being dead, but @a is still alive.
204             //
205             // Forward rewiring refers to:
206             //
207             //     a: Something(...)
208             //     b: SetLocal(@a)
209             //     c: Id(@a) // some identity function
210             //
211             // Where we find @a being dead, but @b is still alive.
212             
213             bool found = false;
214             
215             if (node && permitsOSRBackwardRewiring(node->op())) {
216                 MinifiedID id = node->child1();
217                 if (tryToSetConstantRecovery(valueRecoveries[i], codeBlock, graph.at(id)))
218                     continue;
219                 info = generationInfos.get(id);
220                 if (info.format != DataFormatNone)
221                     found = true;
222             }
223             
224             if (!found) {
225                 MinifiedID bestID;
226                 unsigned bestScore = 0;
227                 
228                 HashMap<MinifiedID, MinifiedGenerationInfo>::iterator iter = generationInfos.begin();
229                 HashMap<MinifiedID, MinifiedGenerationInfo>::iterator end = generationInfos.end();
230                 for (; iter != end; ++iter) {
231                     MinifiedID id = iter->key;
232                     node = graph.at(id);
233                     if (!node)
234                         continue;
235                     if (!node->hasChild1())
236                         continue;
237                     if (node->child1() != source.id())
238                         continue;
239                     if (iter->value.format == DataFormatNone)
240                         continue;
241                     unsigned myScore = forwardRewiringSelectionScore(node->op());
242                     if (myScore <= bestScore)
243                         continue;
244                     bestID = id;
245                     bestScore = myScore;
246                 }
247                 
248                 if (!!bestID) {
249                     info = generationInfos.get(bestID);
250                     ASSERT(info.format != DataFormatNone);
251                     found = true;
252                 }
253             }
254             
255             if (!found) {
256                 valueRecoveries[i] = ValueRecovery::constant(jsUndefined());
257                 continue;
258             }
259         }
260         
261         ASSERT(info.format != DataFormatNone);
262         
263         if (info.filled) {
264             if (info.format == DataFormatDouble) {
265                 valueRecoveries[i] = ValueRecovery::inFPR(info.u.fpr);
266                 continue;
267             }
268 #if USE(JSVALUE32_64)
269             if (info.format & DataFormatJS) {
270                 valueRecoveries[i] = ValueRecovery::inPair(info.u.pair.tagGPR, info.u.pair.payloadGPR);
271                 continue;
272             }
273 #endif
274             valueRecoveries[i] = ValueRecovery::inGPR(info.u.gpr, info.format);
275             continue;
276         }
277         
278         valueRecoveries[i] =
279             ValueRecovery::displacedInJSStack(static_cast<VirtualRegister>(info.u.virtualReg), info.format);
280     }
281     
282     // Step 4: Make sure that for locals that coincide with true call frame headers, the exit compiler knows
283     // that those values don't have to be recovered. Signal this by using ValueRecovery::alreadyInJSStack()
284     for (InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; inlineCallFrame; inlineCallFrame = inlineCallFrame->caller.inlineCallFrame) {
285         for (unsigned i = JSStack::CallFrameHeaderSize; i--;)
286             valueRecoveries.setLocal(operandToLocal(inlineCallFrame->stackOffset) - i - 1, ValueRecovery::alreadyInJSStack());
287     }
288 }
289
290 } } // namespace JSC::DFG
291
292 #endif // ENABLE(DFG_JIT)
293