Index: src/compiler/control-flow-optimizer.cc |
diff --git a/src/compiler/control-flow-optimizer.cc b/src/compiler/control-flow-optimizer.cc |
index 1a2b4cdfd8cb29524be1c29f9aa185da0be102bb..4a19339746bf5db086c1a565ec17ffffd2c126d8 100644 |
--- a/src/compiler/control-flow-optimizer.cc |
+++ b/src/compiler/control-flow-optimizer.cc |
@@ -54,13 +54,161 @@ void ControlFlowOptimizer::VisitNode(Node* node) { |
void ControlFlowOptimizer::VisitBranch(Node* node) { |
DCHECK_EQ(IrOpcode::kBranch, node->opcode()); |
+ if (TryBuildSwitch(node)) return; |
+ if (TryCloneBranch(node)) return; |
+ VisitNode(node); |
+} |
+ |
+ |
+bool ControlFlowOptimizer::TryCloneBranch(Node* node) { |
+ DCHECK_EQ(IrOpcode::kBranch, node->opcode()); |
+ |
+ // This optimization is a special case of (super)block cloning. It takes an |
+ // input graph as shown below and clones the Branch node for every predecessor |
+ // to the Merge, essentially removing the Merge completely. This avoids |
+ // materializing the bit for the Phi and may offer potential for further |
+ // branch folding optimizations (i.e. because one or more inputs to the Phi is |
+ // a constant). Note that there may be more Phi nodes hanging off the Merge, |
+ // but we can only a certain subset of them currently (actually only Phi and |
+ // EffectPhi nodes whose uses have either the IfTrue or IfFalse as control |
+ // input). |
+ |
+ // Control1 ... ControlN |
+ // ^ ^ |
+ // | | Cond1 ... CondN |
+ // +----+ +----+ ^ ^ |
+ // | | | | |
+ // | | +----+ | |
+ // Merge<--+ | +------------+ |
+ // ^ \|/ |
+ // | Phi |
+ // | | |
+ // Branch----+ |
+ // ^ |
+ // | |
+ // +-----+-----+ |
+ // | | |
+ // IfTrue IfFalse |
+ // ^ ^ |
+ // | | |
+ |
+ // The resulting graph (modulo the Phi and EffectPhi nodes) looks like this: |
+ |
+ // Control1 Cond1 ... ControlN CondN |
+ // ^ ^ ^ ^ |
+ // \ / \ / |
+ // Branch ... Branch |
+ // ^ ^ |
+ // | | |
+ // +---+---+ +---+----+ |
+ // | | | | |
+ // IfTrue IfFalse ... IfTrue IfFalse |
+ // ^ ^ ^ ^ |
+ // | | | | |
+ // +--+ +-------------+ | |
+ // | | +--------------+ +--+ |
+ // | | | | |
+ // Merge Merge |
+ // ^ ^ |
+ // | | |
+ |
+ Node* branch = node; |
+ Node* cond = NodeProperties::GetValueInput(branch, 0); |
+ if (!cond->OwnedBy(branch) || cond->opcode() != IrOpcode::kPhi) return false; |
+ Node* merge = NodeProperties::GetControlInput(branch); |
+ if (merge->opcode() != IrOpcode::kMerge || |
+ NodeProperties::GetControlInput(cond) != merge) { |
+ return false; |
+ } |
+ // Grab the IfTrue/IfFalse projections of the Branch. |
+ Node* control_projections[2]; |
+ NodeProperties::CollectControlProjections(branch, control_projections, |
+ arraysize(control_projections)); |
+ Node* if_true = control_projections[0]; |
+ Node* if_false = control_projections[1]; |
+ DCHECK_EQ(IrOpcode::kIfTrue, if_true->opcode()); |
+ DCHECK_EQ(IrOpcode::kIfFalse, if_false->opcode()); |
+ // Check/collect other Phi/EffectPhi nodes hanging off the Merge. |
+ NodeVector phis(zone()); |
+ for (Node* const use : merge->uses()) { |
+ if (use == branch || use == cond) continue; |
+ // We cannot currently deal with non-Phi/EffectPhi nodes hanging off the |
+ // Merge. Ideally, we would just clone the nodes (and everything that |
+ // depends on it to some distant join point), but that requires knowledge |
+ // about dominance/post-dominance. |
+ if (!NodeProperties::IsPhi(use)) return false; |
+ for (Edge edge : use->use_edges()) { |
+ // Right now we can only handle Phi/EffectPhi nodes whose uses are |
+ // directly control-dependend on either the IfTrue or the IfFalse |
+ // successor, because we know exactly how to update those uses. |
+ // TODO(turbofan): Generalize this to all Phi/EffectPhi nodes using |
+ // dominance/post-dominance on the sea of nodes. |
+ if (edge.from()->op()->ControlInputCount() != 1) return false; |
+ Node* control = NodeProperties::GetControlInput(edge.from()); |
+ if (NodeProperties::IsPhi(edge.from())) { |
+ control = NodeProperties::GetControlInput(control, edge.index()); |
+ } |
+ if (control != if_true && control != if_false) return false; |
+ } |
+ phis.push_back(use); |
+ } |
+ BranchHint const hint = BranchHintOf(branch->op()); |
+ int const input_count = merge->op()->ControlInputCount(); |
+ DCHECK_LE(1, input_count); |
+ Node** const inputs = zone()->NewArray<Node*>(2 * input_count); |
+ Node** const merge_true_inputs = &inputs[0]; |
+ Node** const merge_false_inputs = &inputs[input_count]; |
+ for (int index = 0; index < input_count; ++index) { |
+ Node* cond1 = NodeProperties::GetValueInput(cond, index); |
+ Node* control1 = NodeProperties::GetControlInput(merge, index); |
+ Node* branch1 = graph()->NewNode(common()->Branch(hint), cond1, control1); |
+ merge_true_inputs[index] = graph()->NewNode(common()->IfTrue(), branch1); |
+ merge_false_inputs[index] = graph()->NewNode(common()->IfFalse(), branch1); |
+ Enqueue(branch1); |
+ } |
+ Node* const merge_true = graph()->NewNode(common()->Merge(input_count), |
+ input_count, merge_true_inputs); |
+ Node* const merge_false = graph()->NewNode(common()->Merge(input_count), |
+ input_count, merge_false_inputs); |
+ for (Node* const phi : phis) { |
+ for (int index = 0; index < input_count; ++index) { |
+ inputs[index] = phi->InputAt(index); |
+ } |
+ inputs[input_count] = merge_true; |
+ Node* phi_true = graph()->NewNode(phi->op(), input_count + 1, inputs); |
+ inputs[input_count] = merge_false; |
+ Node* phi_false = graph()->NewNode(phi->op(), input_count + 1, inputs); |
+ for (Edge edge : phi->use_edges()) { |
+ Node* control = NodeProperties::GetControlInput(edge.from()); |
+ if (NodeProperties::IsPhi(edge.from())) { |
+ control = NodeProperties::GetControlInput(control, edge.index()); |
+ } |
+ DCHECK(control == if_true || control == if_false); |
+ edge.UpdateTo((control == if_true) ? phi_true : phi_false); |
+ } |
+ phi->Kill(); |
+ } |
+ // Fix up IfTrue and IfFalse and kill all dead nodes. |
+ if_false->ReplaceUses(merge_false); |
+ if_true->ReplaceUses(merge_true); |
+ if_false->Kill(); |
+ if_true->Kill(); |
+ branch->Kill(); |
+ cond->Kill(); |
+ merge->Kill(); |
+ return true; |
+} |
+ |
+ |
+bool ControlFlowOptimizer::TryBuildSwitch(Node* node) { |
+ DCHECK_EQ(IrOpcode::kBranch, node->opcode()); |
Node* branch = node; |
Node* cond = NodeProperties::GetValueInput(branch, 0); |
- if (cond->opcode() != IrOpcode::kWord32Equal) return VisitNode(node); |
+ if (cond->opcode() != IrOpcode::kWord32Equal) return false; |
Int32BinopMatcher m(cond); |
Node* index = m.left().node(); |
- if (!m.right().HasValue()) return VisitNode(node); |
+ if (!m.right().HasValue()) return false; |
int32_t value = m.right().Value(); |
ZoneSet<int32_t> values(zone()); |
values.insert(value); |
@@ -122,6 +270,7 @@ void ControlFlowOptimizer::VisitBranch(Node* node) { |
Enqueue(if_false); |
branch->RemoveAllInputs(); |
} |
+ return true; |
} |