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 "DFGBlockSet.h"
33#include "DFGGraph.h"
34#include "DFGInsertionSet.h"
35#include "DFGPhase.h"
36#include "FullBytecodeLiveness.h"
37#include "JSCInlines.h"
38
39namespace JSC { namespace DFG {
40
41class LiveCatchVariablePreservationPhase : public Phase {
42public:
43 LiveCatchVariablePreservationPhase(Graph& graph)
44 : Phase(graph, "live catch variable preservation phase")
45 {
46 }
47
48 bool run()
49 {
50 DFG_ASSERT(m_graph, nullptr, m_graph.m_form == LoadStore);
51
52 if (!m_graph.m_hasExceptionHandlers)
53 return false;
54
55 InsertionSet insertionSet(m_graph);
56 if (m_graph.m_hasExceptionHandlers) {
57 for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
58 handleBlockForTryCatch(block, insertionSet);
59 insertionSet.execute(block);
60 }
61 }
62
63 return true;
64 }
65
66 bool isValidFlushLocation(BasicBlock* startingBlock, unsigned index, VirtualRegister operand)
67 {
68 // This code is not meant to be fast. We just use it for assertions. If we got liveness wrong,
69 // this function would return false for a Flush that we insert.
70 Vector<BasicBlock*, 4> worklist;
71 BlockSet seen;
72
73 auto addPredecessors = [&] (BasicBlock* block) {
74 for (BasicBlock* predecessor : block->predecessors) {
75 bool isNewEntry = seen.add(predecessor);
76 if (isNewEntry)
77 worklist.append(predecessor);
78 }
79 };
80
81 auto flushIsDefinitelyInvalid = [&] (BasicBlock* block, unsigned index) {
82 bool allGood = false;
83 for (unsigned i = index; i--; ) {
84 if (block->at(i)->accessesStack(m_graph) && block->at(i)->local() == operand) {
85 allGood = true;
86 break;
87 }
88 }
89
90 if (allGood)
91 return false;
92
93 if (block->predecessors.isEmpty()) {
94 // This is a root block. We proved we reached here, therefore we can't Flush, as
95 // it'll make this local live at the start of a root block, which is invalid IR.
96 return true;
97 }
98
99 addPredecessors(block);
100 return false;
101 };
102
103 if (flushIsDefinitelyInvalid(startingBlock, index))
104 return false;
105
106 while (!worklist.isEmpty()) {
107 BasicBlock* block = worklist.takeLast();
108 if (flushIsDefinitelyInvalid(block, block->size()))
109 return false;
110 }
111 return true;
112 }
113
114
115 void handleBlockForTryCatch(BasicBlock* block, InsertionSet& insertionSet)
116 {
117 HandlerInfo* currentExceptionHandler = nullptr;
118 FastBitVector liveAtCatchHead;
119 liveAtCatchHead.resize(m_graph.block(0)->variablesAtTail.numberOfLocals());
120
121 HandlerInfo* cachedHandlerResult;
122 CodeOrigin cachedCodeOrigin;
123 auto catchHandler = [&] (CodeOrigin origin) -> HandlerInfo* {
124 ASSERT(origin);
125 if (origin == cachedCodeOrigin)
126 return cachedHandlerResult;
127
128 unsigned bytecodeIndexToCheck = origin.bytecodeIndex();
129
130 cachedCodeOrigin = origin;
131
132 while (1) {
133 InlineCallFrame* inlineCallFrame = origin.inlineCallFrame();
134 CodeBlock* codeBlock = m_graph.baselineCodeBlockFor(inlineCallFrame);
135 if (HandlerInfo* handler = codeBlock->handlerForBytecodeOffset(bytecodeIndexToCheck)) {
136 liveAtCatchHead.clearAll();
137
138 unsigned catchBytecodeIndex = handler->target;
139 m_graph.forAllLocalsLiveInBytecode(CodeOrigin(catchBytecodeIndex, inlineCallFrame), [&] (VirtualRegister operand) {
140 liveAtCatchHead[operand.toLocal()] = true;
141 });
142
143 cachedHandlerResult = handler;
144 break;
145 }
146
147 if (!inlineCallFrame) {
148 cachedHandlerResult = nullptr;
149 break;
150 }
151
152 bytecodeIndexToCheck = inlineCallFrame->directCaller.bytecodeIndex();
153 origin = inlineCallFrame->directCaller;
154 }
155
156 return cachedHandlerResult;
157 };
158
159 Operands<VariableAccessData*> currentBlockAccessData(block->variablesAtTail.numberOfArguments(), block->variablesAtTail.numberOfLocals(), nullptr);
160
161 auto flushEverything = [&] (NodeOrigin origin, unsigned index) {
162 RELEASE_ASSERT(currentExceptionHandler);
163 auto flush = [&] (VirtualRegister operand) {
164 if ((operand.isLocal() && liveAtCatchHead[operand.toLocal()]) || operand.isArgument()) {
165
166 ASSERT(isValidFlushLocation(block, index, operand));
167
168 VariableAccessData* accessData = currentBlockAccessData.operand(operand);
169 if (!accessData)
170 accessData = newVariableAccessData(operand);
171
172 currentBlockAccessData.operand(operand) = accessData;
173
174 insertionSet.insertNode(index, SpecNone,
175 Flush, origin, OpInfo(accessData));
176 }
177 };
178
179 for (unsigned local = 0; local < block->variablesAtTail.numberOfLocals(); local++)
180 flush(virtualRegisterForLocal(local));
181 flush(VirtualRegister(CallFrame::thisArgumentOffset()));
182 };
183
184 for (unsigned nodeIndex = 0; nodeIndex < block->size(); nodeIndex++) {
185 Node* node = block->at(nodeIndex);
186
187 {
188 HandlerInfo* newHandler = catchHandler(node->origin.semantic);
189 if (newHandler != currentExceptionHandler && currentExceptionHandler)
190 flushEverything(node->origin, nodeIndex);
191 currentExceptionHandler = newHandler;
192 }
193
194 if (currentExceptionHandler && (node->op() == SetLocal || node->op() == SetArgumentDefinitely || node->op() == SetArgumentMaybe)) {
195 VirtualRegister operand = node->local();
196 if ((operand.isLocal() && liveAtCatchHead[operand.toLocal()]) || operand.isArgument()) {
197
198 ASSERT(isValidFlushLocation(block, nodeIndex, operand));
199
200 VariableAccessData* variableAccessData = currentBlockAccessData.operand(operand);
201 if (!variableAccessData)
202 variableAccessData = newVariableAccessData(operand);
203
204 insertionSet.insertNode(nodeIndex, SpecNone,
205 Flush, node->origin, OpInfo(variableAccessData));
206 }
207 }
208
209 if (node->accessesStack(m_graph))
210 currentBlockAccessData.operand(node->local()) = node->variableAccessData();
211 }
212
213 if (currentExceptionHandler) {
214 NodeOrigin origin = block->at(block->size() - 1)->origin;
215 flushEverything(origin, block->size());
216 }
217 }
218
219 VariableAccessData* newVariableAccessData(VirtualRegister operand)
220 {
221 ASSERT(!operand.isConstant());
222
223 m_graph.m_variableAccessData.append(VariableAccessData(operand));
224 return &m_graph.m_variableAccessData.last();
225 }
226};
227
228bool performLiveCatchVariablePreservationPhase(Graph& graph)
229{
230 return runPhase<LiveCatchVariablePreservationPhase>(graph);
231}
232
233} } // namespace JSC::DFG
234
235#endif // ENABLE(DFG_JIT)
236