1 // 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.
5 #include "src/compiler/control-flow-optimizer.h"
7 #include "src/compiler/js-graph.h"
8 #include "src/compiler/node-matchers.h"
9 #include "src/compiler/node-properties.h"
15 ControlFlowOptimizer::ControlFlowOptimizer(JSGraph* jsgraph, Zone* zone)
18 queued_(jsgraph->graph(), 2),
22 void ControlFlowOptimizer::Optimize() {
23 Enqueue(graph()->start());
24 while (!queue_.empty()) {
25 Node* node = queue_.front();
27 if (node->IsDead()) continue;
28 switch (node->opcode()) {
29 case IrOpcode::kBranch:
40 void ControlFlowOptimizer::Enqueue(Node* node) {
41 DCHECK_NOT_NULL(node);
42 if (node->IsDead() || queued_.Get(node)) return;
43 queued_.Set(node, true);
48 void ControlFlowOptimizer::VisitNode(Node* node) {
49 for (Edge edge : node->use_edges()) {
50 if (NodeProperties::IsControlEdge(edge)) {
57 void ControlFlowOptimizer::VisitBranch(Node* node) {
58 DCHECK_EQ(IrOpcode::kBranch, node->opcode());
59 if (TryBuildSwitch(node)) return;
60 if (TryCloneBranch(node)) return;
65 bool ControlFlowOptimizer::TryCloneBranch(Node* node) {
66 DCHECK_EQ(IrOpcode::kBranch, node->opcode());
68 // This optimization is a special case of (super)block cloning. It takes an
69 // input graph as shown below and clones the Branch node for every predecessor
70 // to the Merge, essentially removing the Merge completely. This avoids
71 // materializing the bit for the Phi and may offer potential for further
72 // branch folding optimizations (i.e. because one or more inputs to the Phi is
73 // a constant). Note that there may be more Phi nodes hanging off the Merge,
74 // but we can only a certain subset of them currently (actually only Phi and
75 // EffectPhi nodes whose uses have either the IfTrue or IfFalse as control
78 // Control1 ... ControlN
80 // | | Cond1 ... CondN
84 // Merge<--+ | +------------+
97 // The resulting graph (modulo the Phi and EffectPhi nodes) looks like this:
99 // Control1 Cond1 ... ControlN CondN
105 // +---+---+ +---+----+
107 // IfTrue IfFalse ... IfTrue IfFalse
110 // +--+ +-------------+ |
111 // | | +--------------+ +--+
118 Node* cond = NodeProperties::GetValueInput(branch, 0);
119 if (!cond->OwnedBy(branch) || cond->opcode() != IrOpcode::kPhi) return false;
120 Node* merge = NodeProperties::GetControlInput(branch);
121 if (merge->opcode() != IrOpcode::kMerge ||
122 NodeProperties::GetControlInput(cond) != merge) {
125 // Grab the IfTrue/IfFalse projections of the Branch.
126 Node* control_projections[2];
127 NodeProperties::CollectControlProjections(branch, control_projections,
128 arraysize(control_projections));
129 Node* if_true = control_projections[0];
130 Node* if_false = control_projections[1];
131 DCHECK_EQ(IrOpcode::kIfTrue, if_true->opcode());
132 DCHECK_EQ(IrOpcode::kIfFalse, if_false->opcode());
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());
153 if (control != if_true && control != if_false) return false;
157 BranchHint const hint = BranchHintOf(branch->op());
158 int const input_count = merge->op()->ControlInputCount();
159 DCHECK_LE(1, input_count);
160 Node** const inputs = zone()->NewArray<Node*>(2 * input_count);
161 Node** const merge_true_inputs = &inputs[0];
162 Node** const merge_false_inputs = &inputs[input_count];
163 for (int index = 0; index < input_count; ++index) {
164 Node* cond1 = NodeProperties::GetValueInput(cond, index);
165 Node* control1 = NodeProperties::GetControlInput(merge, index);
166 Node* branch1 = graph()->NewNode(common()->Branch(hint), cond1, control1);
167 merge_true_inputs[index] = graph()->NewNode(common()->IfTrue(), branch1);
168 merge_false_inputs[index] = graph()->NewNode(common()->IfFalse(), branch1);
171 Node* const merge_true = graph()->NewNode(common()->Merge(input_count),
172 input_count, merge_true_inputs);
173 Node* const merge_false = graph()->NewNode(common()->Merge(input_count),
174 input_count, merge_false_inputs);
175 for (Node* const phi : phis) {
176 for (int index = 0; index < input_count; ++index) {
177 inputs[index] = phi->InputAt(index);
179 inputs[input_count] = merge_true;
180 Node* phi_true = graph()->NewNode(phi->op(), input_count + 1, inputs);
181 inputs[input_count] = merge_false;
182 Node* phi_false = graph()->NewNode(phi->op(), input_count + 1, inputs);
183 for (Edge edge : phi->use_edges()) {
184 Node* control = NodeProperties::GetControlInput(edge.from());
185 if (NodeProperties::IsPhi(edge.from())) {
186 control = NodeProperties::GetControlInput(control, edge.index());
188 DCHECK(control == if_true || control == if_false);
189 edge.UpdateTo((control == if_true) ? phi_true : phi_false);
193 // Fix up IfTrue and IfFalse and kill all dead nodes.
194 if_false->ReplaceUses(merge_false);
195 if_true->ReplaceUses(merge_true);
205 bool ControlFlowOptimizer::TryBuildSwitch(Node* node) {
206 DCHECK_EQ(IrOpcode::kBranch, node->opcode());
209 if (BranchHintOf(branch->op()) != BranchHint::kNone) return false;
210 Node* cond = NodeProperties::GetValueInput(branch, 0);
211 if (cond->opcode() != IrOpcode::kWord32Equal) return false;
212 Int32BinopMatcher m(cond);
213 Node* index = m.left().node();
214 if (!m.right().HasValue()) return false;
215 int32_t value = m.right().Value();
216 ZoneSet<int32_t> values(zone());
217 values.insert(value);
222 Node* control_projections[2];
223 NodeProperties::CollectControlProjections(branch, control_projections, 2);
224 if_true = control_projections[0];
225 if_false = control_projections[1];
226 DCHECK_EQ(IrOpcode::kIfTrue, if_true->opcode());
227 DCHECK_EQ(IrOpcode::kIfFalse, if_false->opcode());
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);
244 if (branch != node) {
245 branch->NullAllInputs();
246 if_true->ReplaceInput(0, node);
248 if_true->set_op(common()->IfValue(value));
249 if_false->NullAllInputs();
254 values.insert(value);
257 DCHECK_EQ(IrOpcode::kBranch, node->opcode());
258 DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
259 DCHECK_EQ(IrOpcode::kIfTrue, if_true->opcode());
260 DCHECK_EQ(IrOpcode::kIfFalse, if_false->opcode());
261 if (branch == node) {
262 DCHECK_EQ(1u, values.size());
265 DCHECK_LT(1u, values.size());
266 node->set_op(common()->Switch(values.size() + 1));
267 node->ReplaceInput(0, index);
268 if_true->set_op(common()->IfValue(value));
269 if_true->ReplaceInput(0, node);
271 if_false->set_op(common()->IfDefault());
272 if_false->ReplaceInput(0, node);
274 branch->NullAllInputs();
279 CommonOperatorBuilder* ControlFlowOptimizer::common() const {
280 return jsgraph()->common();
284 Graph* ControlFlowOptimizer::graph() const { return jsgraph()->graph(); }
287 MachineOperatorBuilder* ControlFlowOptimizer::machine() const {
288 return jsgraph()->machine();
291 } // namespace compiler
292 } // namespace internal