]> git.saurik.com Git - apple/javascriptcore.git/blob - dfg/DFGVarargsForwardingPhase.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / dfg / DFGVarargsForwardingPhase.cpp
1 /*
2 * Copyright (C) 2015 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 "DFGVarargsForwardingPhase.h"
28
29 #if ENABLE(DFG_JIT)
30
31 #include "DFGArgumentsUtilities.h"
32 #include "DFGClobberize.h"
33 #include "DFGForAllKills.h"
34 #include "DFGGraph.h"
35 #include "DFGPhase.h"
36 #include "JSCInlines.h"
37 #include <wtf/ListDump.h>
38
39 namespace JSC { namespace DFG {
40
41 namespace {
42
43 bool verbose = false;
44
45 class VarargsForwardingPhase : public Phase {
46 public:
47 VarargsForwardingPhase(Graph& graph)
48 : Phase(graph, "varargs forwarding")
49 {
50 }
51
52 bool run()
53 {
54 DFG_ASSERT(m_graph, nullptr, m_graph.m_form != SSA);
55
56 if (verbose) {
57 dataLog("Graph before varargs forwarding:\n");
58 m_graph.dump();
59 }
60
61 m_changed = false;
62 for (BasicBlock* block : m_graph.blocksInNaturalOrder())
63 handleBlock(block);
64 return m_changed;
65 }
66
67 private:
68 void handleBlock(BasicBlock* block)
69 {
70 for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
71 Node* node = block->at(nodeIndex);
72 switch (node->op()) {
73 case CreateDirectArguments:
74 case CreateClonedArguments:
75 handleCandidate(block, nodeIndex);
76 break;
77 default:
78 break;
79 }
80 }
81 }
82
83 void handleCandidate(BasicBlock* block, unsigned candidateNodeIndex)
84 {
85 // We expect calls into this function to be rare. So, this is written in a simple O(n) manner.
86
87 Node* candidate = block->at(candidateNodeIndex);
88 if (verbose)
89 dataLog("Handling candidate ", candidate, "\n");
90
91 // Find the index of the last node in this block to use the candidate, and look for escaping
92 // sites.
93 unsigned lastUserIndex = candidateNodeIndex;
94 Vector<VirtualRegister, 2> relevantLocals; // This is a set. We expect it to be a small set.
95 for (unsigned nodeIndex = candidateNodeIndex + 1; nodeIndex < block->size(); ++nodeIndex) {
96 Node* node = block->at(nodeIndex);
97
98 switch (node->op()) {
99 case MovHint:
100 if (node->child1() != candidate)
101 break;
102 lastUserIndex = nodeIndex;
103 if (!relevantLocals.contains(node->unlinkedLocal()))
104 relevantLocals.append(node->unlinkedLocal());
105 break;
106
107 case Check: {
108 bool sawEscape = false;
109 m_graph.doToChildren(
110 node,
111 [&] (Edge edge) {
112 if (edge == candidate)
113 lastUserIndex = nodeIndex;
114
115 if (edge.willNotHaveCheck())
116 return;
117
118 if (alreadyChecked(edge.useKind(), SpecObject))
119 return;
120
121 sawEscape = true;
122 });
123 if (sawEscape) {
124 if (verbose)
125 dataLog(" Escape at ", node, "\n");
126 return;
127 }
128 break;
129 }
130
131 case LoadVarargs:
132 if (m_graph.uses(node, candidate))
133 lastUserIndex = nodeIndex;
134 break;
135
136 case CallVarargs:
137 case ConstructVarargs:
138 if (node->child1() == candidate || node->child3() == candidate) {
139 if (verbose)
140 dataLog(" Escape at ", node, "\n");
141 return;
142 }
143 if (node->child2() == candidate)
144 lastUserIndex = nodeIndex;
145 break;
146
147 case SetLocal:
148 if (node->child1() == candidate && node->variableAccessData()->isLoadedFrom()) {
149 if (verbose)
150 dataLog(" Escape at ", node, "\n");
151 return;
152 }
153 break;
154
155 default:
156 if (m_graph.uses(node, candidate)) {
157 if (verbose)
158 dataLog(" Escape at ", node, "\n");
159 return;
160 }
161 }
162
163 forAllKilledOperands(
164 m_graph, node, block->tryAt(nodeIndex + 1),
165 [&] (VirtualRegister reg) {
166 if (verbose)
167 dataLog(" Killing ", reg, " while we are interested in ", listDump(relevantLocals), "\n");
168 for (unsigned i = 0; i < relevantLocals.size(); ++i) {
169 if (relevantLocals[i] == reg) {
170 relevantLocals[i--] = relevantLocals.last();
171 relevantLocals.removeLast();
172 lastUserIndex = nodeIndex;
173 }
174 }
175 });
176 }
177 if (verbose)
178 dataLog("Selected lastUserIndex = ", lastUserIndex, ", ", block->at(lastUserIndex), "\n");
179
180 // We're still in business. Determine if between the candidate and the last user there is any
181 // effect that could interfere with sinking.
182 for (unsigned nodeIndex = candidateNodeIndex + 1; nodeIndex <= lastUserIndex; ++nodeIndex) {
183 Node* node = block->at(nodeIndex);
184
185 // We have our own custom switch to detect some interferences that clobberize() wouldn't know
186 // about, and also some of the common ones, too. In particular, clobberize() doesn't know
187 // that Flush, MovHint, ZombieHint, and KillStack are bad because it's not worried about
188 // what gets read on OSR exit.
189 switch (node->op()) {
190 case MovHint:
191 case ZombieHint:
192 case KillStack:
193 if (argumentsInvolveStackSlot(candidate, node->unlinkedLocal())) {
194 if (verbose)
195 dataLog(" Interference at ", node, "\n");
196 return;
197 }
198 break;
199
200 case PutStack:
201 if (argumentsInvolveStackSlot(candidate, node->stackAccessData()->local)) {
202 if (verbose)
203 dataLog(" Interference at ", node, "\n");
204 return;
205 }
206 break;
207
208 case SetLocal:
209 case Flush:
210 if (argumentsInvolveStackSlot(candidate, node->local())) {
211 if (verbose)
212 dataLog(" Interference at ", node, "\n");
213 return;
214 }
215 break;
216
217 default: {
218 bool doesInterfere = false;
219 clobberize(
220 m_graph, node, NoOpClobberize(),
221 [&] (AbstractHeap heap) {
222 if (heap.kind() != Stack) {
223 ASSERT(!heap.overlaps(Stack));
224 return;
225 }
226 ASSERT(!heap.payload().isTop());
227 VirtualRegister reg(heap.payload().value32());
228 if (argumentsInvolveStackSlot(candidate, reg))
229 doesInterfere = true;
230 },
231 NoOpClobberize());
232 if (doesInterfere) {
233 if (verbose)
234 dataLog(" Interference at ", node, "\n");
235 return;
236 }
237 } }
238 }
239
240 // We can make this work.
241 if (verbose)
242 dataLog(" Will do forwarding!\n");
243 m_changed = true;
244
245 // Transform the program.
246 switch (candidate->op()) {
247 case CreateDirectArguments:
248 candidate->setOpAndDefaultFlags(PhantomDirectArguments);
249 break;
250
251 case CreateClonedArguments:
252 candidate->setOpAndDefaultFlags(PhantomClonedArguments);
253 break;
254
255 default:
256 DFG_CRASH(m_graph, candidate, "bad node type");
257 break;
258 }
259 for (unsigned nodeIndex = candidateNodeIndex + 1; nodeIndex <= lastUserIndex; ++nodeIndex) {
260 Node* node = block->at(nodeIndex);
261 switch (node->op()) {
262 case Check:
263 case MovHint:
264 case PutHint:
265 // We don't need to change anything with these.
266 break;
267
268 case LoadVarargs:
269 if (node->child1() != candidate)
270 break;
271 node->setOpAndDefaultFlags(ForwardVarargs);
272 break;
273
274 case CallVarargs:
275 if (node->child2() != candidate)
276 break;
277 node->setOpAndDefaultFlags(CallForwardVarargs);
278 break;
279
280 case ConstructVarargs:
281 if (node->child2() != candidate)
282 break;
283 node->setOpAndDefaultFlags(ConstructForwardVarargs);
284 break;
285
286 case SetLocal:
287 // This is super odd. We don't have to do anything here, since in DFG IR, the phantom
288 // arguments nodes do produce a JSValue. Also, we know that if this SetLocal referenecs a
289 // candidate then the SetLocal - along with all of its references - will die off pretty
290 // soon, since it has no real users. DCE will surely kill it. If we make it to SSA, then
291 // SSA conversion will kill it.
292 break;
293
294 default:
295 if (ASSERT_DISABLED)
296 break;
297 m_graph.doToChildren(
298 node,
299 [&] (Edge edge) {
300 DFG_ASSERT(m_graph, node, edge != candidate);
301 });
302 break;
303 }
304 }
305 }
306
307 bool m_changed;
308 };
309
310 } // anonymous namespace
311
312 bool performVarargsForwarding(Graph& graph)
313 {
314 SamplingRegion samplingRegion("DFG Varargs Forwarding Phase");
315 return runPhase<VarargsForwardingPhase>(graph);
316 }
317
318 } } // namespace JSC::DFG
319
320 #endif // ENABLE(DFG_JIT)
321