blob: 3fc3bcefaca43f67292ca90827362139117f02e5 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/compiler/control-flow-optimizer.h"
6
7#include "src/compiler/common-operator.h"
8#include "src/compiler/graph.h"
9#include "src/compiler/node-matchers.h"
10#include "src/compiler/node-properties.h"
11
12namespace v8 {
13namespace internal {
14namespace compiler {
15
16ControlFlowOptimizer::ControlFlowOptimizer(Graph* graph,
17 CommonOperatorBuilder* common,
18 MachineOperatorBuilder* machine,
19 Zone* zone)
20 : graph_(graph),
21 common_(common),
22 machine_(machine),
23 queue_(zone),
24 queued_(graph, 2),
25 zone_(zone) {}
26
27
28void ControlFlowOptimizer::Optimize() {
29 Enqueue(graph()->start());
30 while (!queue_.empty()) {
31 Node* node = queue_.front();
32 queue_.pop();
33 if (node->IsDead()) continue;
34 switch (node->opcode()) {
35 case IrOpcode::kBranch:
36 VisitBranch(node);
37 break;
38 default:
39 VisitNode(node);
40 break;
41 }
42 }
43}
44
45
46void ControlFlowOptimizer::Enqueue(Node* node) {
47 DCHECK_NOT_NULL(node);
48 if (node->IsDead() || queued_.Get(node)) return;
49 queued_.Set(node, true);
50 queue_.push(node);
51}
52
53
54void ControlFlowOptimizer::VisitNode(Node* node) {
55 for (Edge edge : node->use_edges()) {
56 if (NodeProperties::IsControlEdge(edge)) {
57 Enqueue(edge.from());
58 }
59 }
60}
61
62
63void ControlFlowOptimizer::VisitBranch(Node* node) {
64 DCHECK_EQ(IrOpcode::kBranch, node->opcode());
65 if (TryBuildSwitch(node)) return;
66 if (TryCloneBranch(node)) return;
67 VisitNode(node);
68}
69
70
71bool ControlFlowOptimizer::TryCloneBranch(Node* node) {
72 DCHECK_EQ(IrOpcode::kBranch, node->opcode());
73
74 // This optimization is a special case of (super)block cloning. It takes an
75 // input graph as shown below and clones the Branch node for every predecessor
76 // to the Merge, essentially removing the Merge completely. This avoids
77 // materializing the bit for the Phi and may offer potential for further
78 // branch folding optimizations (i.e. because one or more inputs to the Phi is
79 // a constant). Note that there may be more Phi nodes hanging off the Merge,
80 // but we can only a certain subset of them currently (actually only Phi and
81 // EffectPhi nodes whose uses have either the IfTrue or IfFalse as control
82 // input).
83
84 // Control1 ... ControlN
85 // ^ ^
86 // | | Cond1 ... CondN
87 // +----+ +----+ ^ ^
88 // | | | |
89 // | | +----+ |
90 // Merge<--+ | +------------+
91 // ^ \|/
92 // | Phi
93 // | |
94 // Branch----+
95 // ^
96 // |
97 // +-----+-----+
98 // | |
99 // IfTrue IfFalse
100 // ^ ^
101 // | |
102
103 // The resulting graph (modulo the Phi and EffectPhi nodes) looks like this:
104
105 // Control1 Cond1 ... ControlN CondN
106 // ^ ^ ^ ^
107 // \ / \ /
108 // Branch ... Branch
109 // ^ ^
110 // | |
111 // +---+---+ +---+----+
112 // | | | |
113 // IfTrue IfFalse ... IfTrue IfFalse
114 // ^ ^ ^ ^
115 // | | | |
116 // +--+ +-------------+ |
117 // | | +--------------+ +--+
118 // | | | |
119 // Merge Merge
120 // ^ ^
121 // | |
122
123 Node* branch = node;
124 Node* cond = NodeProperties::GetValueInput(branch, 0);
125 if (!cond->OwnedBy(branch) || cond->opcode() != IrOpcode::kPhi) return false;
126 Node* merge = NodeProperties::GetControlInput(branch);
127 if (merge->opcode() != IrOpcode::kMerge ||
128 NodeProperties::GetControlInput(cond) != merge) {
129 return false;
130 }
131 // Grab the IfTrue/IfFalse projections of the Branch.
132 BranchMatcher matcher(branch);
133 // Check/collect other Phi/EffectPhi nodes hanging off the Merge.
134 NodeVector phis(zone());
135 for (Node* const use : merge->uses()) {
136 if (use == branch || use == cond) continue;
137 // We cannot currently deal with non-Phi/EffectPhi nodes hanging off the
138 // Merge. Ideally, we would just clone the nodes (and everything that
139 // depends on it to some distant join point), but that requires knowledge
140 // about dominance/post-dominance.
141 if (!NodeProperties::IsPhi(use)) return false;
142 for (Edge edge : use->use_edges()) {
143 // Right now we can only handle Phi/EffectPhi nodes whose uses are
144 // directly control-dependend on either the IfTrue or the IfFalse
145 // successor, because we know exactly how to update those uses.
146 // TODO(turbofan): Generalize this to all Phi/EffectPhi nodes using
147 // dominance/post-dominance on the sea of nodes.
148 if (edge.from()->op()->ControlInputCount() != 1) return false;
149 Node* control = NodeProperties::GetControlInput(edge.from());
150 if (NodeProperties::IsPhi(edge.from())) {
151 control = NodeProperties::GetControlInput(control, edge.index());
152 }
153 if (control != matcher.IfTrue() && control != matcher.IfFalse())
154 return false;
155 }
156 phis.push_back(use);
157 }
158 BranchHint const hint = BranchHintOf(branch->op());
159 int const input_count = merge->op()->ControlInputCount();
160 DCHECK_LE(1, input_count);
161 Node** const inputs = zone()->NewArray<Node*>(2 * input_count);
162 Node** const merge_true_inputs = &inputs[0];
163 Node** const merge_false_inputs = &inputs[input_count];
164 for (int index = 0; index < input_count; ++index) {
165 Node* cond1 = NodeProperties::GetValueInput(cond, index);
166 Node* control1 = NodeProperties::GetControlInput(merge, index);
167 Node* branch1 = graph()->NewNode(common()->Branch(hint), cond1, control1);
168 merge_true_inputs[index] = graph()->NewNode(common()->IfTrue(), branch1);
169 merge_false_inputs[index] = graph()->NewNode(common()->IfFalse(), branch1);
170 Enqueue(branch1);
171 }
172 Node* const merge_true = graph()->NewNode(common()->Merge(input_count),
173 input_count, merge_true_inputs);
174 Node* const merge_false = graph()->NewNode(common()->Merge(input_count),
175 input_count, merge_false_inputs);
176 for (Node* const phi : phis) {
177 for (int index = 0; index < input_count; ++index) {
178 inputs[index] = phi->InputAt(index);
179 }
180 inputs[input_count] = merge_true;
181 Node* phi_true = graph()->NewNode(phi->op(), input_count + 1, inputs);
182 inputs[input_count] = merge_false;
183 Node* phi_false = graph()->NewNode(phi->op(), input_count + 1, inputs);
184 for (Edge edge : phi->use_edges()) {
185 Node* control = NodeProperties::GetControlInput(edge.from());
186 if (NodeProperties::IsPhi(edge.from())) {
187 control = NodeProperties::GetControlInput(control, edge.index());
188 }
189 DCHECK(control == matcher.IfTrue() || control == matcher.IfFalse());
190 edge.UpdateTo((control == matcher.IfTrue()) ? phi_true : phi_false);
191 }
192 phi->Kill();
193 }
194 // Fix up IfTrue and IfFalse and kill all dead nodes.
195 matcher.IfFalse()->ReplaceUses(merge_false);
196 matcher.IfTrue()->ReplaceUses(merge_true);
197 matcher.IfFalse()->Kill();
198 matcher.IfTrue()->Kill();
199 branch->Kill();
200 cond->Kill();
201 merge->Kill();
202 return true;
203}
204
205
206bool ControlFlowOptimizer::TryBuildSwitch(Node* node) {
207 DCHECK_EQ(IrOpcode::kBranch, node->opcode());
208
209 Node* branch = node;
210 if (BranchHintOf(branch->op()) != BranchHint::kNone) return false;
211 Node* cond = NodeProperties::GetValueInput(branch, 0);
212 if (cond->opcode() != IrOpcode::kWord32Equal) return false;
213 Int32BinopMatcher m(cond);
214 Node* index = m.left().node();
215 if (!m.right().HasValue()) return false;
216 int32_t value = m.right().Value();
217 ZoneSet<int32_t> values(zone());
218 values.insert(value);
219
220 Node* if_false;
221 Node* if_true;
222 while (true) {
223 BranchMatcher matcher(branch);
224 DCHECK(matcher.Matched());
225
226 if_true = matcher.IfTrue();
227 if_false = matcher.IfFalse();
228
229 auto it = if_false->uses().begin();
230 if (it == if_false->uses().end()) break;
231 Node* branch1 = *it++;
232 if (branch1->opcode() != IrOpcode::kBranch) break;
233 if (BranchHintOf(branch1->op()) != BranchHint::kNone) break;
234 if (it != if_false->uses().end()) break;
235 Node* cond1 = branch1->InputAt(0);
236 if (cond1->opcode() != IrOpcode::kWord32Equal) break;
237 Int32BinopMatcher m1(cond1);
238 if (m1.left().node() != index) break;
239 if (!m1.right().HasValue()) break;
240 int32_t value1 = m1.right().Value();
241 if (values.find(value1) != values.end()) break;
242 DCHECK_NE(value, value1);
243
244 if (branch != node) {
245 branch->NullAllInputs();
246 if_true->ReplaceInput(0, node);
247 }
248 NodeProperties::ChangeOp(if_true, common()->IfValue(value));
249 if_false->NullAllInputs();
250 Enqueue(if_true);
251
252 branch = branch1;
253 value = value1;
254 values.insert(value);
255 }
256
257 DCHECK_EQ(IrOpcode::kBranch, node->opcode());
258 DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
259 if (branch == node) {
260 DCHECK_EQ(1u, values.size());
261 return false;
262 }
263 DCHECK_LT(1u, values.size());
264 node->ReplaceInput(0, index);
265 NodeProperties::ChangeOp(node, common()->Switch(values.size() + 1));
266 if_true->ReplaceInput(0, node);
267 NodeProperties::ChangeOp(if_true, common()->IfValue(value));
268 Enqueue(if_true);
269 if_false->ReplaceInput(0, node);
270 NodeProperties::ChangeOp(if_false, common()->IfDefault());
271 Enqueue(if_false);
272 branch->NullAllInputs();
273 return true;
274}
275
276} // namespace compiler
277} // namespace internal
278} // namespace v8