Implement try/catch in the DFG.
[WebKit-https.git] / Source / JavaScriptCore / dfg / DFGLiveCatchVariablePreservationPhase.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 "DFGLiveCatchVariablePreservationPhase.h"
28
29 #if ENABLE(DFG_JIT)
30
31 #include "DFGBasicBlockInlines.h"
32 #include "DFGGraph.h"
33 #include "DFGInsertionSet.h"
34 #include "DFGPhase.h"
35 #include "FullBytecodeLiveness.h"
36 #include "JSCInlines.h"
37
38 namespace JSC { namespace DFG {
39
40 class FlushLiveCatchVariablesInsertionPhase : public Phase {
41 public:
42     FlushLiveCatchVariablesInsertionPhase(Graph& graph)
43         : Phase(graph, "live catch variable preservation phase")
44     {
45     }
46
47     bool run()
48     {
49         if (!m_graph.m_hasExceptionHandlers)
50             return true;
51
52         DFG_ASSERT(m_graph, nullptr, m_graph.m_form == LoadStore);
53
54         m_currentBlockLiveness.resize(m_graph.block(0)->variablesAtTail.numberOfLocals());
55
56         InsertionSet insertionSet(m_graph);
57         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
58             handleBlock(block, insertionSet);
59             insertionSet.execute(block);
60         }
61
62         return true;
63     }
64
65     bool willCatchException(CodeOrigin origin)
66     {
67         unsigned bytecodeIndexToCheck = origin.bytecodeIndex;
68         m_currentBlockLiveness.clearAll();
69
70         while (1) {
71             InlineCallFrame* inlineCallFrame = origin.inlineCallFrame;
72             CodeBlock* codeBlock = m_graph.baselineCodeBlockFor(inlineCallFrame);
73             if (HandlerInfo* handler = codeBlock->handlerForBytecodeOffset(bytecodeIndexToCheck)) {
74                 unsigned catchBytecodeIndex = handler->target;
75                 m_graph.forAllLocalsLiveInBytecode(CodeOrigin(catchBytecodeIndex, inlineCallFrame), [&] (VirtualRegister operand) {
76                     m_currentBlockLiveness.set(operand.toLocal(), true); 
77                 });
78                 return true;
79             }
80
81             if (!inlineCallFrame)
82                 return false;
83
84             bytecodeIndexToCheck = inlineCallFrame->caller.bytecodeIndex;
85             origin = inlineCallFrame->caller;
86         }
87     }
88
89     void handleBlock(BasicBlock* block, InsertionSet& insertionSet)
90     {
91         // Because precise jump targets ensures that the start of a "try" block is its
92         // own basic block, we will never have two "try" statements in the same DFG
93         // basic block. Therefore, checking the first node in the block is sufficient 
94         // to checking if we're in a try block.
95         if (!willCatchException(block->at(0)->origin.semantic))
96             return;
97
98         Operands<VariableAccessData*> currentBlockAccessData(block->variablesAtTail.numberOfArguments(), block->variablesAtTail.numberOfLocals(), nullptr);
99         HashSet<InlineCallFrame*> seenInlineCallFrames;
100
101         {
102             for (unsigned i = 0; i < block->size(); i++) {
103                 Node* node = block->at(i);
104                 bool isPrimordialSetArgument = node->op() == SetArgument && node->local().isArgument() && node == m_graph.m_arguments[node->local().toArgument()];
105                 InlineCallFrame* inlineCallFrame = node->origin.semantic.inlineCallFrame;
106                 if (inlineCallFrame)
107                     seenInlineCallFrames.add(inlineCallFrame);
108
109                 if (node->op() == SetLocal || (node->op() == SetArgument && !isPrimordialSetArgument)) {
110                     VirtualRegister operand = node->local();
111
112                     int stackOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0;
113                     if ((operand.isLocal() && m_currentBlockLiveness.get(operand.toLocal()))
114                         || (operand.offset() == stackOffset + CallFrame::thisArgumentOffset())) {
115
116                         VariableAccessData* flushAccessData = currentBlockAccessData.operand(operand);
117                         if (!flushAccessData)
118                             flushAccessData = newVariableAccessData(operand);
119
120                         insertionSet.insertNode(i, SpecNone, 
121                             Flush, node->origin, OpInfo(flushAccessData));
122                     }
123                 }
124
125                 if (node->hasVariableAccessData(m_graph))
126                     currentBlockAccessData.operand(node->local()) = node->variableAccessData();
127             }
128         }
129
130         // Flush everything at the end of the block.
131         // FIXME: I think this will only be necessary if we have any successor
132         // blocks who aren't inside this "try" statement. If all our successor's
133         // are in this try statement, they will have Flushes for any live "catch"
134         // variables.
135         {
136             NodeOrigin origin = block->at(block->size() - 1)->origin;
137             auto insertFlushAtEnd = [&] (VirtualRegister operand, bool alwaysFlush) {
138                 if ((operand.isLocal() && m_currentBlockLiveness.get(operand.toLocal())) 
139                     || operand.isArgument()
140                     || alwaysFlush) {
141                     VariableAccessData* accessData = currentBlockAccessData.operand(operand);
142                     if (!accessData)
143                         accessData = newVariableAccessData(operand);
144
145                     currentBlockAccessData.operand(operand) = accessData;
146
147                     insertionSet.insertNode(block->size(), SpecNone, 
148                         Flush, origin, OpInfo(accessData));
149                 }
150             };
151             for (unsigned local = 0; local < block->variablesAtTail.numberOfLocals(); local++)
152                 insertFlushAtEnd(virtualRegisterForLocal(local), false);
153             for (InlineCallFrame* inlineCallFrame : seenInlineCallFrames)
154                 insertFlushAtEnd(VirtualRegister(inlineCallFrame->stackOffset + CallFrame::thisArgumentOffset()), true);
155             insertFlushAtEnd(VirtualRegister(CallFrame::thisArgumentOffset()), true);
156         }
157     }
158
159     VariableAccessData* newVariableAccessData(VirtualRegister operand)
160     {
161         ASSERT(!operand.isConstant());
162         
163         m_graph.m_variableAccessData.append(VariableAccessData(operand));
164         return &m_graph.m_variableAccessData.last();
165     }
166
167     FastBitVector m_currentBlockLiveness;
168 };
169
170 bool performLiveCatchVariablePreservationPhase(Graph& graph)
171 {
172     SamplingRegion samplingRegion("DFG Live Catch Variables Preservation Phase");
173     return runPhase<FlushLiveCatchVariablesInsertionPhase>(graph);
174 }
175
176 } } // namespace JSC::DFG
177
178 #endif // ENABLE(DFG_JIT)