We should support CreateThis in the FTL
[WebKit-https.git] / Source / JavaScriptCore / dfg / DFGVarargsForwardingPhase.cpp
1 /*
2  * Copyright (C) 2015-2018 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 "ClonedArguments.h"
32 #include "DFGArgumentsUtilities.h"
33 #include "DFGClobberize.h"
34 #include "DFGForAllKills.h"
35 #include "DFGGraph.h"
36 #include "DFGPhase.h"
37 #include "JSCInlines.h"
38 #include <wtf/ListDump.h>
39
40 namespace JSC { namespace DFG {
41
42 namespace {
43
44 namespace DFGVarargsForwardingPhaseInternal {
45 static const bool verbose = false;
46 }
47
48 class VarargsForwardingPhase : public Phase {
49 public:
50     VarargsForwardingPhase(Graph& graph)
51         : Phase(graph, "varargs forwarding")
52     {
53     }
54     
55     bool run()
56     {
57         DFG_ASSERT(m_graph, nullptr, m_graph.m_form != SSA);
58         
59         if (DFGVarargsForwardingPhaseInternal::verbose) {
60             dataLog("Graph before varargs forwarding:\n");
61             m_graph.dump();
62         }
63         
64         m_changed = false;
65         for (BasicBlock* block : m_graph.blocksInNaturalOrder())
66             handleBlock(block);
67         return m_changed;
68     }
69
70 private:
71     void handleBlock(BasicBlock* block)
72     {
73         for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
74             Node* node = block->at(nodeIndex);
75             switch (node->op()) {
76             case CreateDirectArguments:
77             case CreateClonedArguments:
78                 handleCandidate(block, nodeIndex);
79                 break;
80             default:
81                 break;
82             }
83         }
84     }
85     
86     void handleCandidate(BasicBlock* block, unsigned candidateNodeIndex)
87     {
88         // We expect calls into this function to be rare. So, this is written in a simple O(n) manner.
89         
90         Node* candidate = block->at(candidateNodeIndex);
91         if (DFGVarargsForwardingPhaseInternal::verbose)
92             dataLog("Handling candidate ", candidate, "\n");
93         
94         // We eliminate GetButterfly over CreateClonedArguments if the butterfly is only
95         // used by a GetByOffset  that loads the CreateClonedArguments's length. We also
96         // eliminate it if the GetButterfly node is totally unused.
97         Vector<Node*, 1> candidateButterflies; 
98
99         // Find the index of the last node in this block to use the candidate, and look for escaping
100         // sites.
101         unsigned lastUserIndex = candidateNodeIndex;
102         Vector<VirtualRegister, 2> relevantLocals; // This is a set. We expect it to be a small set.
103         for (unsigned nodeIndex = candidateNodeIndex + 1; nodeIndex < block->size(); ++nodeIndex) {
104             Node* node = block->at(nodeIndex);
105
106             auto defaultEscape = [&] {
107                 if (m_graph.uses(node, candidate)) {
108                     if (DFGVarargsForwardingPhaseInternal::verbose)
109                         dataLog("    Escape at ", node, "\n");
110                     return true;
111                 }
112                 return false;
113             };
114
115             bool validGetByOffset = false;
116             switch (node->op()) {
117             case MovHint:
118                 if (node->child1() != candidate)
119                     break;
120                 lastUserIndex = nodeIndex;
121                 if (!relevantLocals.contains(node->unlinkedLocal()))
122                     relevantLocals.append(node->unlinkedLocal());
123                 break;
124                 
125             case CheckVarargs:
126             case Check: {
127                 bool sawEscape = false;
128                 m_graph.doToChildren(
129                     node,
130                     [&] (Edge edge) {
131                         if (edge == candidate)
132                             lastUserIndex = nodeIndex;
133                         
134                         if (edge.willNotHaveCheck())
135                             return;
136                         
137                         if (alreadyChecked(edge.useKind(), SpecObject))
138                             return;
139                         
140                         sawEscape = true;
141                     });
142                 if (sawEscape) {
143                     if (DFGVarargsForwardingPhaseInternal::verbose)
144                         dataLog("    Escape at ", node, "\n");
145                     return;
146                 }
147                 break;
148             }
149                 
150             case LoadVarargs:
151                 if (m_graph.uses(node, candidate))
152                     lastUserIndex = nodeIndex;
153                 break;
154                 
155             case CallVarargs:
156             case ConstructVarargs:
157             case TailCallVarargs:
158             case TailCallVarargsInlinedCaller:
159                 if (node->child1() == candidate || node->child2() == candidate) {
160                     if (DFGVarargsForwardingPhaseInternal::verbose)
161                         dataLog("    Escape at ", node, "\n");
162                     return;
163                 }
164                 if (node->child2() == candidate)
165                     lastUserIndex = nodeIndex;
166                 break;
167                 
168             case SetLocal:
169                 if (node->child1() == candidate && node->variableAccessData()->isLoadedFrom()) {
170                     if (DFGVarargsForwardingPhaseInternal::verbose)
171                         dataLog("    Escape at ", node, "\n");
172                     return;
173                 }
174                 break;
175
176             case GetArrayLength: {
177                 if (node->arrayMode().type() == Array::DirectArguments && node->child1() == candidate && node->child1()->op() == CreateDirectArguments) {
178                     lastUserIndex = nodeIndex;
179                     break;
180                 }
181                 if (defaultEscape())
182                     return;
183                 break;
184             }
185             
186             case GetButterfly: {
187                 if (node->child1() == candidate && candidate->op() == CreateClonedArguments) {
188                     lastUserIndex = nodeIndex;
189                     candidateButterflies.append(node);
190                     break;
191                 }
192                 if (defaultEscape())
193                     return;
194                 break;
195             }
196                 
197             case FilterGetByIdStatus:
198             case FilterPutByIdStatus:
199             case FilterCallLinkStatus:
200             case FilterInByIdStatus:
201                 break;
202
203             case GetByOffset: {
204                 if (node->child1()->op() == GetButterfly
205                     && candidateButterflies.contains(node->child1().node())
206                     && node->child2() == candidate
207                     && node->storageAccessData().offset == clonedArgumentsLengthPropertyOffset) {
208                     ASSERT(node->child1()->child1() == candidate);
209                     ASSERT(isOutOfLineOffset(clonedArgumentsLengthPropertyOffset));
210                     // We're good to go. This is getting the length of the arguments.
211                     lastUserIndex = nodeIndex;
212                     validGetByOffset = true;
213                     break;
214                 }
215                 if (defaultEscape())
216                     return;
217                 break;
218             }
219
220             default:
221                 if (defaultEscape())
222                     return;
223                 break;
224             }
225
226             if (!validGetByOffset) {
227                 for (Node* butterfly : candidateButterflies) {
228                     if (m_graph.uses(node, butterfly)) {
229                         if (DFGVarargsForwardingPhaseInternal::verbose)
230                             dataLog("    Butterfly escaped at ", node, "\n");
231                         return;
232                     }
233                 }
234             }
235
236             forAllKilledOperands(
237                 m_graph, node, block->tryAt(nodeIndex + 1),
238                 [&] (VirtualRegister reg) {
239                     if (DFGVarargsForwardingPhaseInternal::verbose)
240                         dataLog("    Killing ", reg, " while we are interested in ", listDump(relevantLocals), "\n");
241                     for (unsigned i = 0; i < relevantLocals.size(); ++i) {
242                         if (relevantLocals[i] == reg) {
243                             relevantLocals[i--] = relevantLocals.last();
244                             relevantLocals.removeLast();
245                             lastUserIndex = nodeIndex;
246                         }
247                     }
248                 });
249         }
250         if (DFGVarargsForwardingPhaseInternal::verbose)
251             dataLog("Selected lastUserIndex = ", lastUserIndex, ", ", block->at(lastUserIndex), "\n");
252         
253         // We're still in business. Determine if between the candidate and the last user there is any
254         // effect that could interfere with sinking.
255         for (unsigned nodeIndex = candidateNodeIndex + 1; nodeIndex <= lastUserIndex; ++nodeIndex) {
256             Node* node = block->at(nodeIndex);
257             
258             // We have our own custom switch to detect some interferences that clobberize() wouldn't know
259             // about, and also some of the common ones, too. In particular, clobberize() doesn't know
260             // that Flush, MovHint, ZombieHint, and KillStack are bad because it's not worried about
261             // what gets read on OSR exit.
262             switch (node->op()) {
263             case MovHint:
264             case ZombieHint:
265             case KillStack:
266                 if (argumentsInvolveStackSlot(candidate, node->unlinkedLocal())) {
267                     if (DFGVarargsForwardingPhaseInternal::verbose)
268                         dataLog("    Interference at ", node, "\n");
269                     return;
270                 }
271                 break;
272                 
273             case PutStack:
274                 if (argumentsInvolveStackSlot(candidate, node->stackAccessData()->local)) {
275                     if (DFGVarargsForwardingPhaseInternal::verbose)
276                         dataLog("    Interference at ", node, "\n");
277                     return;
278                 }
279                 break;
280                 
281             case SetLocal:
282             case Flush:
283                 if (argumentsInvolveStackSlot(candidate, node->local())) {
284                     if (DFGVarargsForwardingPhaseInternal::verbose)
285                         dataLog("    Interference at ", node, "\n");
286                     return;
287                 }
288                 break;
289                 
290             default: {
291                 bool doesInterfere = false;
292                 clobberize(
293                     m_graph, node, NoOpClobberize(),
294                     [&] (AbstractHeap heap) {
295                         if (heap.kind() != Stack) {
296                             ASSERT(!heap.overlaps(Stack));
297                             return;
298                         }
299                         ASSERT(!heap.payload().isTop());
300                         VirtualRegister reg(heap.payload().value32());
301                         if (argumentsInvolveStackSlot(candidate, reg))
302                             doesInterfere = true;
303                     },
304                     NoOpClobberize());
305                 if (doesInterfere) {
306                     if (DFGVarargsForwardingPhaseInternal::verbose)
307                         dataLog("    Interference at ", node, "\n");
308                     return;
309                 }
310             } }
311         }
312         
313         // We can make this work.
314         if (DFGVarargsForwardingPhaseInternal::verbose)
315             dataLog("    Will do forwarding!\n");
316         m_changed = true;
317         
318         // Transform the program.
319         switch (candidate->op()) {
320         case CreateDirectArguments:
321             candidate->setOpAndDefaultFlags(PhantomDirectArguments);
322             break;
323
324         case CreateClonedArguments:
325             candidate->setOpAndDefaultFlags(PhantomClonedArguments);
326             break;
327             
328         default:
329             DFG_CRASH(m_graph, candidate, "bad node type");
330             break;
331         }
332
333         InsertionSet insertionSet(m_graph);
334         for (unsigned nodeIndex = candidateNodeIndex + 1; nodeIndex <= lastUserIndex; ++nodeIndex) {
335             Node* node = block->at(nodeIndex);
336             switch (node->op()) {
337             case Check:
338             case CheckVarargs:
339             case MovHint:
340             case PutHint:
341                 // We don't need to change anything with these.
342                 break;
343                 
344             case LoadVarargs:
345                 if (node->child1() != candidate)
346                     break;
347                 node->setOpAndDefaultFlags(ForwardVarargs);
348                 break;
349                 
350             case CallVarargs:
351                 if (node->child3() != candidate)
352                     break;
353                 node->setOpAndDefaultFlags(CallForwardVarargs);
354                 break;
355                 
356             case ConstructVarargs:
357                 if (node->child3() != candidate)
358                     break;
359                 node->setOpAndDefaultFlags(ConstructForwardVarargs);
360                 break;
361
362             case TailCallVarargs:
363                 if (node->child3() != candidate)
364                     break;
365                 node->setOpAndDefaultFlags(TailCallForwardVarargs);
366                 break;
367
368             case TailCallVarargsInlinedCaller:
369                 if (node->child3() != candidate)
370                     break;
371                 node->setOpAndDefaultFlags(TailCallForwardVarargsInlinedCaller);
372                 break;
373
374             case SetLocal:
375                 // This is super odd. We don't have to do anything here, since in DFG IR, the phantom
376                 // arguments nodes do produce a JSValue. Also, we know that if this SetLocal referenecs a
377                 // candidate then the SetLocal - along with all of its references - will die off pretty
378                 // soon, since it has no real users. DCE will surely kill it. If we make it to SSA, then
379                 // SSA conversion will kill it.
380                 break;
381
382             case GetButterfly: {
383                 if (node->child1().node() == candidate) {
384                     ASSERT(candidateButterflies.contains(node));
385                     node->child1() = Edge();
386                     node->remove(m_graph);
387                 }
388                 break;
389             }
390
391             case FilterGetByIdStatus:
392             case FilterPutByIdStatus:
393             case FilterCallLinkStatus:
394             case FilterInByIdStatus:
395                 if (node->child1().node() == candidate)
396                     node->remove(m_graph);
397                 break;
398
399             case GetByOffset: {
400                 if (node->child2() == candidate) {
401                     ASSERT(candidateButterflies.contains(node->child1().node())); // It's no longer a GetButterfly node, but it should've been a candidate butterfly.
402                     ASSERT(node->storageAccessData().offset == clonedArgumentsLengthPropertyOffset);
403                     node->convertToIdentityOn(
404                         emitCodeToGetArgumentsArrayLength(insertionSet, candidate, nodeIndex, node->origin));
405                 }
406                 break;
407             }
408
409             case GetArrayLength:
410                 if (node->arrayMode().type() == Array::DirectArguments && node->child1() == candidate) {
411                     node->convertToIdentityOn(
412                         emitCodeToGetArgumentsArrayLength(insertionSet, candidate, nodeIndex, node->origin));
413                 }
414                 break;
415
416             default:
417                 if (ASSERT_DISABLED)
418                     break;
419                 m_graph.doToChildren(
420                     node,
421                     [&] (Edge edge) {
422                         DFG_ASSERT(m_graph, node, edge != candidate);
423                     });
424                 break;
425             }
426         }
427
428         insertionSet.execute(block);
429     }
430     
431     bool m_changed;
432 };
433
434 } // anonymous namespace
435
436 bool performVarargsForwarding(Graph& graph)
437 {
438     return runPhase<VarargsForwardingPhase>(graph);
439 }
440
441 } } // namespace JSC::DFG
442
443 #endif // ENABLE(DFG_JIT)
444