FastBitVector should have efficient and easy-to-use vector-vector operations
[WebKit-https.git] / Source / JavaScriptCore / dfg / DFGLiveCatchVariablePreservationPhase.cpp
1 /*
2  * Copyright (C) 2015-2016 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 LiveCatchVariablePreservationPhase : public Phase {
41 public:
42     LiveCatchVariablePreservationPhase(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[operand.toLocal()] = true;
77                 });
78                 return true;
79             }
80
81             if (!inlineCallFrame)
82                 return false;
83
84             bytecodeIndexToCheck = inlineCallFrame->directCaller.bytecodeIndex;
85             origin = inlineCallFrame->directCaller;
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[operand.toLocal()])
114                         || (operand.offset() == stackOffset + CallFrame::thisArgumentOffset())) {
115
116                         VariableAccessData* variableAccessData = currentBlockAccessData.operand(operand);
117                         if (!variableAccessData)
118                             variableAccessData = newVariableAccessData(operand);
119
120                         insertionSet.insertNode(i, SpecNone, 
121                             Flush, node->origin, OpInfo(variableAccessData));
122                     }
123                 }
124
125                 if (node->accessesStack(m_graph))
126                     currentBlockAccessData.operand(node->local()) = node->variableAccessData();
127             }
128         }
129
130         // Insert Flush for everything at the end of the block.
131         {
132             NodeOrigin origin = block->at(block->size() - 1)->origin;
133             auto preserveLivenessAtEndOfBlock = [&] (VirtualRegister operand, bool alwaysInsert) {
134                 if ((operand.isLocal() && m_currentBlockLiveness[operand.toLocal()]) 
135                     || operand.isArgument()
136                     || alwaysInsert) {
137                     VariableAccessData* accessData = currentBlockAccessData.operand(operand);
138                     if (!accessData)
139                         accessData = newVariableAccessData(operand);
140
141                     currentBlockAccessData.operand(operand) = accessData;
142
143                     insertionSet.insertNode(block->size(), SpecNone, 
144                         Flush, origin, OpInfo(accessData));
145                 }
146             };
147             for (unsigned local = 0; local < block->variablesAtTail.numberOfLocals(); local++)
148                 preserveLivenessAtEndOfBlock(virtualRegisterForLocal(local), false);
149             for (InlineCallFrame* inlineCallFrame : seenInlineCallFrames)
150                 preserveLivenessAtEndOfBlock(VirtualRegister(inlineCallFrame->stackOffset + CallFrame::thisArgumentOffset()), true);
151             preserveLivenessAtEndOfBlock(VirtualRegister(CallFrame::thisArgumentOffset()), true);
152         }
153     }
154
155     VariableAccessData* newVariableAccessData(VirtualRegister operand)
156     {
157         ASSERT(!operand.isConstant());
158         
159         m_graph.m_variableAccessData.append(VariableAccessData(operand));
160         return &m_graph.m_variableAccessData.last();
161     }
162
163     FastBitVector m_currentBlockLiveness;
164 };
165
166 bool performLiveCatchVariablePreservationPhase(Graph& graph)
167 {
168     return runPhase<LiveCatchVariablePreservationPhase>(graph);
169 }
170
171 } } // namespace JSC::DFG
172
173 #endif // ENABLE(DFG_JIT)