blob: 09b732b186433d5d88be9fadad0d282e0d6fd80b [file] [log] [blame]
fpizlo@apple.comcaa68812012-08-02 04:32:30 +00001/*
2 * Copyright (C) 2012 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 "DFGStructureCheckHoistingPhase.h"
28
29#if ENABLE(DFG_JIT)
30
31#include "DFGBasicBlock.h"
32#include "DFGGraph.h"
33#include "DFGInsertionSet.h"
34#include "DFGPhase.h"
fpizlo@apple.comc6e5add2012-12-03 01:59:42 +000035#include "DFGVariableAccessDataDump.h"
fpizlo@apple.coma4b4cbe2013-01-12 04:47:03 +000036#include "Operations.h"
fpizlo@apple.comcaa68812012-08-02 04:32:30 +000037#include <wtf/HashMap.h>
38
39namespace JSC { namespace DFG {
40
41enum CheckBallot { VoteOther, VoteStructureCheck };
42
43class StructureCheckHoistingPhase : public Phase {
44public:
45 StructureCheckHoistingPhase(Graph& graph)
46 : Phase(graph, "structure check hoisting")
47 {
48 }
49
50 bool run()
51 {
52 for (unsigned i = m_graph.m_variableAccessData.size(); i--;) {
53 VariableAccessData* variable = &m_graph.m_variableAccessData[i];
54 if (!variable->isRoot())
55 continue;
56 variable->clearVotes();
57 }
58
59 // Identify the set of variables that are always subject to the same structure
60 // checks. For now, only consider monomorphic structure checks (one structure).
61
62 for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
63 BasicBlock* block = m_graph.m_blocks[blockIndex].get();
64 if (!block)
65 continue;
66 for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
67 NodeIndex nodeIndex = block->at(indexInBlock);
68 Node& node = m_graph[nodeIndex];
69 if (!node.shouldGenerate())
70 continue;
71 switch (node.op()) {
fpizlo@apple.com6e0a9ed2012-09-16 02:36:22 +000072 case CheckStructure:
73 case StructureTransitionWatchpoint: {
fpizlo@apple.comcaa68812012-08-02 04:32:30 +000074 Node& child = m_graph[node.child1()];
75 if (child.op() != GetLocal)
76 break;
77 VariableAccessData* variable = child.variableAccessData();
78 variable->vote(VoteStructureCheck);
79 if (variable->isCaptured() || variable->structureCheckHoistingFailed())
80 break;
81 if (!isCellSpeculation(variable->prediction()))
82 break;
83 noticeStructureCheck(variable, node.structureSet());
84 break;
85 }
86
87 case ForwardCheckStructure:
fpizlo@apple.comeb3323d2012-08-20 06:11:24 +000088 case ForwardStructureTransitionWatchpoint:
fpizlo@apple.comcaa68812012-08-02 04:32:30 +000089 // We currently rely on the fact that we're the only ones who would
90 // insert this node.
91 ASSERT_NOT_REACHED();
92 break;
93
94 case GetByOffset:
95 case PutByOffset:
96 case PutStructure:
fpizlo@apple.comcaa68812012-08-02 04:32:30 +000097 case AllocatePropertyStorage:
98 case ReallocatePropertyStorage:
fpizlo@apple.comd8dd0532012-09-13 04:18:52 +000099 case GetButterfly:
fpizlo@apple.comf24804c2012-08-15 02:48:35 +0000100 case GetByVal:
101 case PutByVal:
102 case PutByValAlias:
103 case GetArrayLength:
fpizlo@apple.com04c19742012-08-26 22:35:26 +0000104 case CheckArray:
105 case GetIndexedPropertyStorage:
fpizlo@apple.comf24804c2012-08-15 02:48:35 +0000106 case Phantom:
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000107 // Don't count these uses.
108 break;
109
fpizlo@apple.comf10d0722012-12-03 09:21:22 +0000110 case ArrayifyToStructure:
111 case Arrayify:
112 if (node.arrayMode().conversion() == Array::RageConvert) {
113 // Rage conversion changes structures. We should avoid tying to do
114 // any kind of hoisting when rage conversion is in play.
115 Node& child = m_graph[node.child1()];
116 if (child.op() != GetLocal)
117 break;
118 VariableAccessData* variable = child.variableAccessData();
119 variable->vote(VoteOther);
120 if (variable->isCaptured() || variable->structureCheckHoistingFailed())
121 break;
122 if (!isCellSpeculation(variable->prediction()))
123 break;
124 noticeStructureCheck(variable, 0);
125 }
126 break;
127
fpizlo@apple.com6e0a9ed2012-09-16 02:36:22 +0000128 case SetLocal: {
129 // Find all uses of the source of the SetLocal. If any of them are a
130 // kind of CheckStructure, then we should notice them to ensure that
131 // we're not hoisting a check that would contravene checks that are
132 // already being performed.
133 VariableAccessData* variable = node.variableAccessData();
134 if (variable->isCaptured() || variable->structureCheckHoistingFailed())
135 break;
136 if (!isCellSpeculation(variable->prediction()))
137 break;
138 NodeIndex source = node.child1().index();
139 for (unsigned subIndexInBlock = 0; subIndexInBlock < block->size(); ++subIndexInBlock) {
140 NodeIndex subNodeIndex = block->at(subIndexInBlock);
141 Node& subNode = m_graph[subNodeIndex];
142 if (!subNode.shouldGenerate())
143 continue;
144 switch (subNode.op()) {
fpizlo@apple.com37108892012-09-28 20:38:40 +0000145 case CheckStructure: {
fpizlo@apple.com6e0a9ed2012-09-16 02:36:22 +0000146 if (subNode.child1().index() != source)
147 break;
148
149 noticeStructureCheck(variable, subNode.structureSet());
150 break;
151 }
fpizlo@apple.com37108892012-09-28 20:38:40 +0000152 case StructureTransitionWatchpoint: {
153 if (subNode.child1().index() != source)
154 break;
155
156 noticeStructureCheck(variable, subNode.structure());
157 break;
158 }
fpizlo@apple.com6e0a9ed2012-09-16 02:36:22 +0000159 default:
160 break;
161 }
162 }
163
164 m_graph.vote(node, VoteOther);
165 break;
166 }
oliver@apple.comc909f5f2012-10-18 23:37:40 +0000167 case GarbageValue:
168 break;
fpizlo@apple.com6e0a9ed2012-09-16 02:36:22 +0000169
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000170 default:
171 m_graph.vote(node, VoteOther);
172 break;
173 }
174 }
175 }
176
177 // Disable structure hoisting on variables that appear to mostly be used in
178 // contexts where it doesn't make sense.
179
180 for (unsigned i = m_graph.m_variableAccessData.size(); i--;) {
181 VariableAccessData* variable = &m_graph.m_variableAccessData[i];
182 if (!variable->isRoot())
183 continue;
184 if (variable->voteRatio() >= Options::structureCheckVoteRatioForHoisting())
185 continue;
186 HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable);
187 if (iter == m_map.end())
188 continue;
189#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
fpizlo@apple.comc6e5add2012-12-03 01:59:42 +0000190 dataLog(
191 "Zeroing the structure to hoist for ", VariableAccessDataDump(m_graph, variable),
192 " because the ratio is ", variable->voteRatio(), ".\n");
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000193#endif
benjamin@webkit.orgee554052012-10-07 23:12:07 +0000194 iter->value.m_structure = 0;
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000195 }
fpizlo@apple.com8e116772012-08-28 00:40:43 +0000196
197 // Disable structure check hoisting for variables that cross the OSR entry that
198 // we're currently taking, and where the value currently does not have the
199 // structure we want.
200
201 for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
202 BasicBlock* block = m_graph.m_blocks[blockIndex].get();
203 if (!block)
204 continue;
205 ASSERT(block->isReachable);
206 if (!block->isOSRTarget)
207 continue;
208 if (block->bytecodeBegin != m_graph.m_osrEntryBytecodeIndex)
209 continue;
210 for (size_t i = 0; i < m_graph.m_mustHandleValues.size(); ++i) {
211 int operand = m_graph.m_mustHandleValues.operandForIndex(i);
212 NodeIndex nodeIndex = block->variablesAtHead.operand(operand);
213 if (nodeIndex == NoNode)
214 continue;
215 VariableAccessData* variable = m_graph[nodeIndex].variableAccessData();
216 HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable);
217 if (iter == m_map.end())
218 continue;
benjamin@webkit.orgee554052012-10-07 23:12:07 +0000219 if (!iter->value.m_structure)
fpizlo@apple.com8e116772012-08-28 00:40:43 +0000220 continue;
221 JSValue value = m_graph.m_mustHandleValues[i];
222 if (!value || !value.isCell()) {
223#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
fpizlo@apple.comc6e5add2012-12-03 01:59:42 +0000224 dataLog(
225 "Zeroing the structure to hoist for ", VariableAccessDataDump(m_graph, variable),
fpizlo@apple.comd2e00512012-12-07 22:55:58 +0000226 " because the OSR entry value is not a cell: ", value, ".\n");
fpizlo@apple.com8e116772012-08-28 00:40:43 +0000227#endif
benjamin@webkit.orgee554052012-10-07 23:12:07 +0000228 iter->value.m_structure = 0;
fpizlo@apple.com8e116772012-08-28 00:40:43 +0000229 continue;
230 }
benjamin@webkit.orgee554052012-10-07 23:12:07 +0000231 if (value.asCell()->structure() != iter->value.m_structure) {
fpizlo@apple.com8e116772012-08-28 00:40:43 +0000232#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
fpizlo@apple.comc6e5add2012-12-03 01:59:42 +0000233 dataLog(
234 "Zeroing the structure to hoist for ", VariableAccessDataDump(m_graph, variable),
235 " because the OSR entry value has structure ",
236 RawPointer(value.asCell()->structure()), " and we wanted ",
237 RawPointer(iter->value.m_structure), ".\n");
fpizlo@apple.com8e116772012-08-28 00:40:43 +0000238#endif
benjamin@webkit.orgee554052012-10-07 23:12:07 +0000239 iter->value.m_structure = 0;
fpizlo@apple.com8e116772012-08-28 00:40:43 +0000240 continue;
241 }
242 }
243 }
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000244
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000245 bool changed = false;
246
247#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
248 for (HashMap<VariableAccessData*, CheckData>::iterator it = m_map.begin();
249 it != m_map.end(); ++it) {
benjamin@webkit.orgee554052012-10-07 23:12:07 +0000250 if (!it->value.m_structure) {
fpizlo@apple.comc6e5add2012-12-03 01:59:42 +0000251 dataLog(
252 "Not hoisting checks for ", VariableAccessDataDump(m_graph, it->key),
253 " because of heuristics.\n");
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000254 continue;
255 }
fpizlo@apple.comc6e5add2012-12-03 01:59:42 +0000256 dataLog("Hoisting checks for ", VariableAccessDataDump(m_graph, it->key), "\n");
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000257 }
258#endif // DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
fpizlo@apple.com6a23c422012-08-18 00:48:38 +0000259
fpizlo@apple.comf1ee41f2012-10-14 20:22:17 +0000260 // Place CheckStructure's at SetLocal sites.
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000261
fpizlo@apple.comf45e88b2013-01-20 19:29:50 +0000262 InsertionSet insertionSet(m_graph);
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000263 for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
264 BasicBlock* block = m_graph.m_blocks[blockIndex].get();
265 if (!block)
266 continue;
267 for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
268 NodeIndex nodeIndex = block->at(indexInBlock);
269 Node& node = m_graph[nodeIndex];
fpizlo@apple.com8ab9c432012-08-03 21:41:05 +0000270 // Be careful not to use 'node' after appending to the graph. In those switch
271 // cases where we need to append, we first carefully extract everything we need
272 // from the node, before doing any appending.
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000273 if (!node.shouldGenerate())
274 continue;
275 switch (node.op()) {
276 case SetArgument: {
277 ASSERT(!blockIndex);
278 // Insert a GetLocal and a CheckStructure immediately following this
279 // SetArgument, if the variable was a candidate for structure hoisting.
280 // If the basic block previously only had the SetArgument as its
281 // variable-at-tail, then replace it with this GetLocal.
282 VariableAccessData* variable = node.variableAccessData();
283 HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable);
284 if (iter == m_map.end())
285 break;
benjamin@webkit.orgee554052012-10-07 23:12:07 +0000286 if (!iter->value.m_structure)
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000287 break;
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000288
fpizlo@apple.com8ab9c432012-08-03 21:41:05 +0000289 CodeOrigin codeOrigin = node.codeOrigin;
290
fpizlo@apple.comf45e88b2013-01-20 19:29:50 +0000291 NodeIndex getLocalIndex = insertionSet.insertNode(
292 indexInBlock + 1, DontRefChildren, DontRefNode, variable->prediction(),
293 GetLocal, codeOrigin, OpInfo(variable), nodeIndex);
294 insertionSet.insertNode(
295 indexInBlock + 1, RefChildren, DontRefNode, SpecNone, CheckStructure,
296 codeOrigin, OpInfo(m_graph.addStructureSet(iter->value.m_structure)),
297 getLocalIndex);
298
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000299 if (block->variablesAtTail.operand(variable->local()) == nodeIndex)
300 block->variablesAtTail.operand(variable->local()) = getLocalIndex;
301
fpizlo@apple.com00673b32012-08-16 23:17:24 +0000302 m_graph.substituteGetLocal(*block, indexInBlock, variable, getLocalIndex);
303
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000304 changed = true;
305 break;
306 }
307
308 case SetLocal: {
309 VariableAccessData* variable = node.variableAccessData();
310 HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable);
311 if (iter == m_map.end())
312 break;
benjamin@webkit.orgee554052012-10-07 23:12:07 +0000313 if (!iter->value.m_structure)
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000314 break;
fpizlo@apple.com8ab9c432012-08-03 21:41:05 +0000315
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000316 // First insert a dead SetLocal to tell OSR that the child's value should
317 // be dropped into this bytecode variable if the CheckStructure decides
318 // to exit.
fpizlo@apple.com8ab9c432012-08-03 21:41:05 +0000319
320 CodeOrigin codeOrigin = node.codeOrigin;
321 NodeIndex child1 = node.child1().index();
322
fpizlo@apple.comf45e88b2013-01-20 19:29:50 +0000323 insertionSet.insertNode(
324 indexInBlock, DontRefChildren, DontRefNode, SpecNone, SetLocal, codeOrigin,
325 OpInfo(variable), child1);
326
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000327 // Use a ForwardCheckStructure to indicate that we should exit to the
328 // next bytecode instruction rather than reexecuting the current one.
fpizlo@apple.comf45e88b2013-01-20 19:29:50 +0000329 insertionSet.insertNode(
330 indexInBlock, RefChildren, DontRefNode, SpecNone, ForwardCheckStructure,
331 codeOrigin, OpInfo(m_graph.addStructureSet(iter->value.m_structure)), child1);
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000332 changed = true;
333 break;
334 }
335
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000336 default:
337 break;
338 }
339 }
fpizlo@apple.comf45e88b2013-01-20 19:29:50 +0000340 insertionSet.execute(block);
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000341 }
342
343 return changed;
344 }
345
346private:
347 void noticeStructureCheck(VariableAccessData* variable, Structure* structure)
348 {
349 HashMap<VariableAccessData*, CheckData>::AddResult result =
fpizlo@apple.comf1ee41f2012-10-14 20:22:17 +0000350 m_map.add(variable, CheckData(structure));
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000351 if (result.isNewEntry)
352 return;
benjamin@webkit.orgee554052012-10-07 23:12:07 +0000353 if (result.iterator->value.m_structure == structure)
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000354 return;
benjamin@webkit.orgee554052012-10-07 23:12:07 +0000355 result.iterator->value.m_structure = 0;
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000356 }
357
358 void noticeStructureCheck(VariableAccessData* variable, const StructureSet& set)
359 {
360 if (set.size() != 1) {
361 noticeStructureCheck(variable, 0);
362 return;
363 }
364 noticeStructureCheck(variable, set.singletonStructure());
365 }
366
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000367 struct CheckData {
368 Structure* m_structure;
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000369
370 CheckData()
371 : m_structure(0)
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000372 {
373 }
374
fpizlo@apple.comf1ee41f2012-10-14 20:22:17 +0000375 CheckData(Structure* structure)
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000376 : m_structure(structure)
fpizlo@apple.comcaa68812012-08-02 04:32:30 +0000377 {
378 }
379 };
380
381 HashMap<VariableAccessData*, CheckData> m_map;
382};
383
384bool performStructureCheckHoisting(Graph& graph)
385{
386 SamplingRegion samplingRegion("DFG Structure Check Hoisting Phase");
387 return runPhase<StructureCheckHoistingPhase>(graph);
388}
389
390} } // namespace JSC::DFG
391
392#endif // ENABLE(DFG_JIT)
393
394