1 | /* |
2 | * Copyright (C) 2012-2017 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 "InlineCallFrame.h" |
35 | #include "JSCInlines.h" |
36 | #include <wtf/DataLog.h> |
37 | #include <wtf/HashMap.h> |
38 | |
39 | namespace JSC { namespace DFG { |
40 | |
41 | void VariableEventStream::logEvent(const VariableEvent& event) |
42 | { |
43 | dataLogF("seq#%u:" , static_cast<unsigned>(size())); |
44 | event.dump(WTF::dataFile()); |
45 | dataLogF(" " ); |
46 | } |
47 | |
48 | namespace { |
49 | |
50 | struct MinifiedGenerationInfo { |
51 | bool filled; // true -> in gpr/fpr/pair, false -> spilled |
52 | bool alive; |
53 | VariableRepresentation u; |
54 | DataFormat format; |
55 | |
56 | MinifiedGenerationInfo() |
57 | : filled(false) |
58 | , alive(false) |
59 | , format(DataFormatNone) |
60 | { |
61 | } |
62 | |
63 | void update(const VariableEvent& event) |
64 | { |
65 | switch (event.kind()) { |
66 | case BirthToFill: |
67 | case Fill: |
68 | filled = true; |
69 | alive = true; |
70 | break; |
71 | case BirthToSpill: |
72 | case Spill: |
73 | filled = false; |
74 | alive = true; |
75 | break; |
76 | case Birth: |
77 | alive = true; |
78 | return; |
79 | case Death: |
80 | format = DataFormatNone; |
81 | alive = false; |
82 | return; |
83 | default: |
84 | return; |
85 | } |
86 | |
87 | u = event.variableRepresentation(); |
88 | format = event.dataFormat(); |
89 | } |
90 | }; |
91 | |
92 | } // namespace |
93 | |
94 | static bool tryToSetConstantRecovery(ValueRecovery& recovery, MinifiedNode* node) |
95 | { |
96 | if (!node) |
97 | return false; |
98 | |
99 | if (node->hasConstant()) { |
100 | recovery = ValueRecovery::constant(node->constant()); |
101 | return true; |
102 | } |
103 | |
104 | if (node->isPhantomDirectArguments()) { |
105 | recovery = ValueRecovery::directArgumentsThatWereNotCreated(node->id()); |
106 | return true; |
107 | } |
108 | |
109 | if (node->isPhantomClonedArguments()) { |
110 | recovery = ValueRecovery::clonedArgumentsThatWereNotCreated(node->id()); |
111 | return true; |
112 | } |
113 | |
114 | return false; |
115 | } |
116 | |
117 | template<VariableEventStream::ReconstructionStyle style> |
118 | unsigned VariableEventStream::reconstruct( |
119 | CodeBlock* codeBlock, CodeOrigin codeOrigin, MinifiedGraph& graph, |
120 | unsigned index, Operands<ValueRecovery>& valueRecoveries, Vector<UndefinedOperandSpan>* undefinedOperandSpans) const |
121 | { |
122 | ASSERT(codeBlock->jitType() == JITType::DFGJIT); |
123 | CodeBlock* baselineCodeBlock = codeBlock->baselineVersion(); |
124 | |
125 | unsigned numVariables; |
126 | static const unsigned invalidIndex = std::numeric_limits<unsigned>::max(); |
127 | unsigned firstUndefined = invalidIndex; |
128 | bool firstUndefinedIsArgument = false; |
129 | |
130 | auto flushUndefinedOperandSpan = [&] (unsigned i) { |
131 | if (firstUndefined == invalidIndex) |
132 | return; |
133 | int firstOffset = valueRecoveries.virtualRegisterForIndex(firstUndefined).offset(); |
134 | int lastOffset = valueRecoveries.virtualRegisterForIndex(i - 1).offset(); |
135 | int minOffset = std::min(firstOffset, lastOffset); |
136 | undefinedOperandSpans->append({ firstUndefined, minOffset, i - firstUndefined }); |
137 | firstUndefined = invalidIndex; |
138 | }; |
139 | auto recordUndefinedOperand = [&] (unsigned i) { |
140 | // We want to separate the span of arguments from the span of locals even if they have adjacent operands indexes. |
141 | if (firstUndefined != invalidIndex && firstUndefinedIsArgument != valueRecoveries.isArgument(i)) |
142 | flushUndefinedOperandSpan(i); |
143 | |
144 | if (firstUndefined == invalidIndex) { |
145 | firstUndefined = i; |
146 | firstUndefinedIsArgument = valueRecoveries.isArgument(i); |
147 | } |
148 | }; |
149 | |
150 | auto* inlineCallFrame = codeOrigin.inlineCallFrame(); |
151 | if (inlineCallFrame) |
152 | numVariables = baselineCodeBlockForInlineCallFrame(inlineCallFrame)->numCalleeLocals() + VirtualRegister(inlineCallFrame->stackOffset).toLocal() + 1; |
153 | else |
154 | numVariables = baselineCodeBlock->numCalleeLocals(); |
155 | |
156 | // Crazy special case: if we're at index == 0 then this must be an argument check |
157 | // failure, in which case all variables are already set up. The recoveries should |
158 | // reflect this. |
159 | if (!index) { |
160 | valueRecoveries = Operands<ValueRecovery>(codeBlock->numParameters(), numVariables); |
161 | for (size_t i = 0; i < valueRecoveries.size(); ++i) { |
162 | valueRecoveries[i] = ValueRecovery::displacedInJSStack( |
163 | VirtualRegister(valueRecoveries.operandForIndex(i)), DataFormatJS); |
164 | } |
165 | return numVariables; |
166 | } |
167 | |
168 | // Step 1: Find the last checkpoint, and figure out the number of virtual registers as we go. |
169 | unsigned startIndex = index - 1; |
170 | while (at(startIndex).kind() != Reset) |
171 | startIndex--; |
172 | |
173 | // Step 2: Create a mock-up of the DFG's state and execute the events. |
174 | Operands<ValueSource> operandSources(codeBlock->numParameters(), numVariables); |
175 | for (unsigned i = operandSources.size(); i--;) |
176 | operandSources[i] = ValueSource(SourceIsDead); |
177 | HashMap<MinifiedID, MinifiedGenerationInfo> generationInfos; |
178 | for (unsigned i = startIndex; i < index; ++i) { |
179 | const VariableEvent& event = at(i); |
180 | switch (event.kind()) { |
181 | case Reset: |
182 | // nothing to do. |
183 | break; |
184 | case BirthToFill: |
185 | case BirthToSpill: |
186 | case Birth: { |
187 | MinifiedGenerationInfo info; |
188 | info.update(event); |
189 | generationInfos.add(event.id(), info); |
190 | break; |
191 | } |
192 | case Fill: |
193 | case Spill: |
194 | case Death: { |
195 | HashMap<MinifiedID, MinifiedGenerationInfo>::iterator iter = generationInfos.find(event.id()); |
196 | ASSERT(iter != generationInfos.end()); |
197 | iter->value.update(event); |
198 | break; |
199 | } |
200 | case MovHintEvent: |
201 | if (operandSources.hasOperand(event.bytecodeRegister())) |
202 | operandSources.setOperand(event.bytecodeRegister(), ValueSource(event.id())); |
203 | break; |
204 | case SetLocalEvent: |
205 | if (operandSources.hasOperand(event.bytecodeRegister())) |
206 | operandSources.setOperand(event.bytecodeRegister(), ValueSource::forDataFormat(event.machineRegister(), event.dataFormat())); |
207 | break; |
208 | default: |
209 | RELEASE_ASSERT_NOT_REACHED(); |
210 | break; |
211 | } |
212 | } |
213 | |
214 | // Step 3: Compute value recoveries! |
215 | valueRecoveries = Operands<ValueRecovery>(codeBlock->numParameters(), numVariables); |
216 | for (unsigned i = 0; i < operandSources.size(); ++i) { |
217 | ValueSource& source = operandSources[i]; |
218 | if (source.isTriviallyRecoverable()) { |
219 | valueRecoveries[i] = source.valueRecovery(); |
220 | if (style == ReconstructionStyle::Separated) { |
221 | if (valueRecoveries[i].isConstant() && valueRecoveries[i].constant() == jsUndefined()) |
222 | recordUndefinedOperand(i); |
223 | else |
224 | flushUndefinedOperandSpan(i); |
225 | } |
226 | continue; |
227 | } |
228 | |
229 | ASSERT(source.kind() == HaveNode); |
230 | MinifiedNode* node = graph.at(source.id()); |
231 | MinifiedGenerationInfo info = generationInfos.get(source.id()); |
232 | if (!info.alive) { |
233 | valueRecoveries[i] = ValueRecovery::constant(jsUndefined()); |
234 | if (style == ReconstructionStyle::Separated) |
235 | recordUndefinedOperand(i); |
236 | continue; |
237 | } |
238 | |
239 | if (tryToSetConstantRecovery(valueRecoveries[i], node)) { |
240 | if (style == ReconstructionStyle::Separated) { |
241 | if (node->hasConstant() && node->constant() == jsUndefined()) |
242 | recordUndefinedOperand(i); |
243 | else |
244 | flushUndefinedOperandSpan(i); |
245 | } |
246 | continue; |
247 | } |
248 | |
249 | ASSERT(info.format != DataFormatNone); |
250 | if (style == ReconstructionStyle::Separated) |
251 | flushUndefinedOperandSpan(i); |
252 | |
253 | if (info.filled) { |
254 | if (info.format == DataFormatDouble) { |
255 | valueRecoveries[i] = ValueRecovery::inFPR(info.u.fpr, DataFormatDouble); |
256 | continue; |
257 | } |
258 | #if USE(JSVALUE32_64) |
259 | if (info.format & DataFormatJS) { |
260 | valueRecoveries[i] = ValueRecovery::inPair(info.u.pair.tagGPR, info.u.pair.payloadGPR); |
261 | continue; |
262 | } |
263 | #endif |
264 | valueRecoveries[i] = ValueRecovery::inGPR(info.u.gpr, info.format); |
265 | continue; |
266 | } |
267 | |
268 | valueRecoveries[i] = |
269 | ValueRecovery::displacedInJSStack(static_cast<VirtualRegister>(info.u.virtualReg), info.format); |
270 | } |
271 | if (style == ReconstructionStyle::Separated) |
272 | flushUndefinedOperandSpan(operandSources.size()); |
273 | |
274 | return numVariables; |
275 | } |
276 | |
277 | unsigned VariableEventStream::reconstruct( |
278 | CodeBlock* codeBlock, CodeOrigin codeOrigin, MinifiedGraph& graph, |
279 | unsigned index, Operands<ValueRecovery>& valueRecoveries) const |
280 | { |
281 | return reconstruct<ReconstructionStyle::Combined>(codeBlock, codeOrigin, graph, index, valueRecoveries, nullptr); |
282 | } |
283 | |
284 | unsigned VariableEventStream::reconstruct( |
285 | CodeBlock* codeBlock, CodeOrigin codeOrigin, MinifiedGraph& graph, |
286 | unsigned index, Operands<ValueRecovery>& valueRecoveries, Vector<UndefinedOperandSpan>* undefinedOperandSpans) const |
287 | { |
288 | return reconstruct<ReconstructionStyle::Separated>(codeBlock, codeOrigin, graph, index, valueRecoveries, undefinedOperandSpans); |
289 | } |
290 | |
291 | } } // namespace JSC::DFG |
292 | |
293 | #endif // ENABLE(DFG_JIT) |
294 | |
295 | |