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
39namespace JSC { namespace DFG {
40
41void VariableEventStream::logEvent(const VariableEvent& event)
42{
43 dataLogF("seq#%u:", static_cast<unsigned>(size()));
44 event.dump(WTF::dataFile());
45 dataLogF(" ");
46}
47
48namespace {
49
50struct 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
94static 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
117template<VariableEventStream::ReconstructionStyle style>
118unsigned 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
277unsigned 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
284unsigned 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