fourthTier: Landing the initial FTL logic in a single commit to avoid spurious
[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->getJITType() == JITCode::DFGJIT);
114     CodeBlock* baselineCodeBlock = codeBlock->baselineVersion();
115     
116     unsigned numVariables;
117     if (codeOrigin.inlineCallFrame)
118         numVariables = baselineCodeBlockForInlineCallFrame(codeOrigin.inlineCallFrame)->m_numCalleeRegisters + 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             // There are four possibilities:
197             //
198             // Int32ToDouble: We can use this in place of the original node, but
199             //    we'd rather not; so we use it only if it is the only remaining
200             //    live version.
201             //
202             // ValueToInt32: If the only remaining live version of the value is
203             //    ValueToInt32, then we can use it.
204             //
205             // UInt32ToNumber: If the only live version of the value is a UInt32ToNumber
206             //    then the only remaining uses are ones that want a properly formed number
207             //    rather than a UInt32 intermediate.
208             //
209             // DoubleAsInt32: Same as UInt32ToNumber.
210             //
211             // The reverse of the above: This node could be a UInt32ToNumber, but its
212             //    alternative is still alive. This means that the only remaining uses of
213             //    the number would be fine with a UInt32 intermediate.
214             
215             bool found = false;
216             
217             if (node && needsOSRBackwardRewiring(node->op())) {
218                 MinifiedID id = node->child1();
219                 if (tryToSetConstantRecovery(valueRecoveries[i], codeBlock, graph.at(id)))
220                     continue;
221                 info = generationInfos.get(id);
222                 if (info.format != DataFormatNone)
223                     found = true;
224             }
225             
226             if (!found) {
227                 MinifiedID int32ToDoubleID;
228                 MinifiedID valueToInt32ID;
229                 MinifiedID uint32ToNumberID;
230                 MinifiedID doubleAsInt32ID;
231                 
232                 HashMap<MinifiedID, MinifiedGenerationInfo>::iterator iter = generationInfos.begin();
233                 HashMap<MinifiedID, MinifiedGenerationInfo>::iterator end = generationInfos.end();
234                 for (; iter != end; ++iter) {
235                     MinifiedID id = iter->key;
236                     node = graph.at(id);
237                     if (!node)
238                         continue;
239                     if (!node->hasChild1())
240                         continue;
241                     if (node->child1() != source.id())
242                         continue;
243                     if (iter->value.format == DataFormatNone)
244                         continue;
245                     switch (node->op()) {
246                     case Int32ToDouble:
247                     case ForwardInt32ToDouble:
248                         int32ToDoubleID = id;
249                         break;
250                     case ValueToInt32:
251                         valueToInt32ID = id;
252                         break;
253                     case UInt32ToNumber:
254                         uint32ToNumberID = id;
255                         break;
256                     case DoubleAsInt32:
257                         doubleAsInt32ID = id;
258                         break;
259                     default:
260                         ASSERT(!needsOSRForwardRewiring(node->op()));
261                         break;
262                     }
263                 }
264                 
265                 MinifiedID idToUse;
266                 if (!!doubleAsInt32ID)
267                     idToUse = doubleAsInt32ID;
268                 else if (!!int32ToDoubleID)
269                     idToUse = int32ToDoubleID;
270                 else if (!!valueToInt32ID)
271                     idToUse = valueToInt32ID;
272                 else if (!!uint32ToNumberID)
273                     idToUse = uint32ToNumberID;
274                 
275                 if (!!idToUse) {
276                     info = generationInfos.get(idToUse);
277                     ASSERT(info.format != DataFormatNone);
278                     found = true;
279                 }
280             }
281             
282             if (!found) {
283                 valueRecoveries[i] = ValueRecovery::constant(jsUndefined());
284                 continue;
285             }
286         }
287         
288         ASSERT(info.format != DataFormatNone);
289         
290         if (info.filled) {
291             if (info.format == DataFormatDouble) {
292                 valueRecoveries[i] = ValueRecovery::inFPR(info.u.fpr);
293                 continue;
294             }
295 #if USE(JSVALUE32_64)
296             if (info.format & DataFormatJS) {
297                 valueRecoveries[i] = ValueRecovery::inPair(info.u.pair.tagGPR, info.u.pair.payloadGPR);
298                 continue;
299             }
300 #endif
301             valueRecoveries[i] = ValueRecovery::inGPR(info.u.gpr, info.format);
302             continue;
303         }
304         
305         valueRecoveries[i] =
306             ValueRecovery::displacedInJSStack(static_cast<VirtualRegister>(info.u.virtualReg), info.format);
307     }
308     
309     // Step 4: Make sure that for locals that coincide with true call frame headers, the exit compiler knows
310     // that those values don't have to be recovered. Signal this by using ValueRecovery::alreadyInJSStack()
311     for (InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; inlineCallFrame; inlineCallFrame = inlineCallFrame->caller.inlineCallFrame) {
312         for (unsigned i = JSStack::CallFrameHeaderSize; i--;)
313             valueRecoveries.setLocal(inlineCallFrame->stackOffset - i - 1, ValueRecovery::alreadyInJSStack());
314     }
315 }
316
317 } } // namespace JSC::DFG
318
319 #endif // ENABLE(DFG_JIT)
320