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