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 "DFGMaximalFlushInsertionPhase.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 "JSCInlines.h"
36
37namespace JSC { namespace DFG {
38
39class MaximalFlushInsertionPhase : public Phase {
40public:
41 MaximalFlushInsertionPhase(Graph& graph)
42 : Phase(graph, "maximal flush insertion phase")
43 {
44 }
45
46 bool run()
47 {
48 DFG_ASSERT(m_graph, nullptr, m_graph.m_form == LoadStore);
49
50 InsertionSet insertionSet(m_graph);
51 for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
52 treatRegularBlock(block, insertionSet);
53 insertionSet.execute(block);
54 }
55
56 for (BasicBlock* entrypoint : m_graph.m_roots) {
57 treatRootBlock(entrypoint, insertionSet);
58 insertionSet.execute(entrypoint);
59 }
60
61 return true;
62 }
63
64 void treatRegularBlock(BasicBlock* block, InsertionSet& insertionSet)
65 {
66 Operands<VariableAccessData*> currentBlockAccessData(block->variablesAtTail.numberOfArguments(), block->variablesAtTail.numberOfLocals(), nullptr);
67 // Insert a Flush before every SetLocal to properly pattern the graph such that
68 // any range between SetLocal and Flush has access to the local on the stack.
69 {
70 for (unsigned i = 0; i < block->size(); i++) {
71 Node* node = block->at(i);
72 bool isPrimordialSetArgument = false;
73 if (node->op() == SetArgumentDefinitely && node->local().isArgument()) {
74 auto iter = m_graph.m_rootToArguments.find(block);
75 if (iter != m_graph.m_rootToArguments.end())
76 isPrimordialSetArgument = node == iter->value[node->local().toArgument()];
77 }
78
79 if (node->op() == SetLocal || (node->op() == SetArgumentDefinitely && !isPrimordialSetArgument) || node->op() == SetArgumentMaybe) {
80 VirtualRegister operand = node->local();
81 VariableAccessData* flushAccessData = currentBlockAccessData.operand(operand);
82 if (!flushAccessData)
83 flushAccessData = newVariableAccessData(operand);
84
85 insertionSet.insertNode(i, SpecNone,
86 Flush, node->origin, OpInfo(flushAccessData));
87 }
88
89 if (node->accessesStack(m_graph))
90 currentBlockAccessData.operand(node->local()) = node->variableAccessData();
91 }
92 }
93
94 // Flush everything at the end of the block.
95 {
96 NodeOrigin origin = block->at(block->size() - 1)->origin;
97 auto insertFlushAtEnd = [&] (VirtualRegister operand) {
98 VariableAccessData* accessData = currentBlockAccessData.operand(operand);
99 if (!accessData)
100 accessData = newVariableAccessData(operand);
101
102 currentBlockAccessData.operand(operand) = accessData;
103
104 insertionSet.insertNode(block->size(), SpecNone,
105 Flush, origin, OpInfo(accessData));
106 };
107
108 for (unsigned i = 0; i < block->variablesAtTail.numberOfLocals(); i++)
109 insertFlushAtEnd(virtualRegisterForLocal(i));
110 for (unsigned i = 0; i < block->variablesAtTail.numberOfArguments(); i++)
111 insertFlushAtEnd(virtualRegisterForArgument(i));
112 }
113 }
114
115 void treatRootBlock(BasicBlock* block, InsertionSet& insertionSet)
116 {
117 Operands<VariableAccessData*> initialAccessData(block->variablesAtTail.numberOfArguments(), block->variablesAtTail.numberOfLocals(), nullptr);
118 Operands<Node*> initialAccessNodes(block->variablesAtTail.numberOfArguments(), block->variablesAtTail.numberOfLocals(), nullptr);
119 for (auto* node : *block) {
120 if (!node->accessesStack(m_graph))
121 continue;
122
123 VirtualRegister operand = node->local();
124 if (initialAccessData.operand(operand))
125 continue;
126
127 DFG_ASSERT(m_graph, node, node->op() != SetLocal); // We should have inserted a Flush before this!
128 initialAccessData.operand(operand) = node->variableAccessData();
129 initialAccessNodes.operand(operand) = node;
130 }
131
132 // We want every Flush to be able to reach backwards to
133 // a SetLocal. Doing this in the root block achieves this goal.
134 NodeOrigin origin = block->at(0)->origin;
135 Node* undefined = insertionSet.insertConstant(0, origin, jsUndefined());
136
137 for (unsigned i = 0; i < block->variablesAtTail.numberOfLocals(); i++) {
138 VirtualRegister operand = virtualRegisterForLocal(i);
139 DFG_ASSERT(m_graph, nullptr, initialAccessNodes.operand(operand)->op() == Flush); // We should have inserted a Flush before any SetLocal/SetArgumentDefinitely/SetArgumentMaybe for the local that we are analyzing now.
140 VariableAccessData* accessData = initialAccessData.operand(operand);
141 DFG_ASSERT(m_graph, nullptr, accessData);
142 insertionSet.insertNode(0, SpecNone,
143 SetLocal, origin, OpInfo(accessData), Edge(undefined));
144 accessData->mergeShouldNeverUnbox(true); // We don't know if we can exit here.
145 }
146 }
147
148
149 VariableAccessData* newVariableAccessData(VirtualRegister operand)
150 {
151 ASSERT(!operand.isConstant());
152
153 m_graph.m_variableAccessData.append(VariableAccessData(operand));
154 return &m_graph.m_variableAccessData.last();
155 }
156};
157
158bool performMaximalFlushInsertion(Graph& graph)
159{
160 return runPhase<MaximalFlushInsertionPhase>(graph);
161}
162
163} } // namespace JSC::DFG
164
165#endif // ENABLE(DFG_JIT)
166