OLD | NEW |
1 // Copyright 2015 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/compiler/control-flow-optimizer.h" | 5 #include "src/compiler/control-flow-optimizer.h" |
6 | 6 |
7 #include "src/compiler/js-graph.h" | 7 #include "src/compiler/js-graph.h" |
8 #include "src/compiler/node-matchers.h" | 8 #include "src/compiler/node-matchers.h" |
9 #include "src/compiler/node-properties.h" | 9 #include "src/compiler/node-properties.h" |
10 | 10 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
47 | 47 |
48 void ControlFlowOptimizer::VisitNode(Node* node) { | 48 void ControlFlowOptimizer::VisitNode(Node* node) { |
49 for (Node* use : node->uses()) { | 49 for (Node* use : node->uses()) { |
50 if (NodeProperties::IsControl(use)) Enqueue(use); | 50 if (NodeProperties::IsControl(use)) Enqueue(use); |
51 } | 51 } |
52 } | 52 } |
53 | 53 |
54 | 54 |
55 void ControlFlowOptimizer::VisitBranch(Node* node) { | 55 void ControlFlowOptimizer::VisitBranch(Node* node) { |
56 DCHECK_EQ(IrOpcode::kBranch, node->opcode()); | 56 DCHECK_EQ(IrOpcode::kBranch, node->opcode()); |
| 57 if (TryBuildSwitch(node)) return; |
| 58 if (TryCloneBranch(node)) return; |
| 59 VisitNode(node); |
| 60 } |
| 61 |
| 62 |
| 63 bool ControlFlowOptimizer::TryCloneBranch(Node* node) { |
| 64 DCHECK_EQ(IrOpcode::kBranch, node->opcode()); |
| 65 |
| 66 // This optimization is a special case of (super)block cloning. It takes an |
| 67 // input graph as shown below and clones the Branch node for every predecessor |
| 68 // to the Merge, essentially removing the Merge completely. This avoids |
| 69 // materializing the bit for the Phi and may offer potential for further |
| 70 // branch folding optimizations (i.e. because one or more inputs to the Phi is |
| 71 // a constant). Note that there may be more Phi nodes hanging off the Merge, |
| 72 // but we can only a certain subset of them currently (actually only Phi and |
| 73 // EffectPhi nodes whose uses have either the IfTrue or IfFalse as control |
| 74 // input). |
| 75 |
| 76 // Control1 ... ControlN |
| 77 // ^ ^ |
| 78 // | | Cond1 ... CondN |
| 79 // +----+ +----+ ^ ^ |
| 80 // | | | | |
| 81 // | | +----+ | |
| 82 // Merge<--+ | +------------+ |
| 83 // ^ \|/ |
| 84 // | Phi |
| 85 // | | |
| 86 // Branch----+ |
| 87 // ^ |
| 88 // | |
| 89 // +-----+-----+ |
| 90 // | | |
| 91 // IfTrue IfFalse |
| 92 // ^ ^ |
| 93 // | | |
| 94 |
| 95 // The resulting graph (modulo the Phi and EffectPhi nodes) looks like this: |
| 96 |
| 97 // Control1 Cond1 ... ControlN CondN |
| 98 // ^ ^ ^ ^ |
| 99 // \ / \ / |
| 100 // Branch ... Branch |
| 101 // ^ ^ |
| 102 // | | |
| 103 // +---+---+ +---+----+ |
| 104 // | | | | |
| 105 // IfTrue IfFalse ... IfTrue IfFalse |
| 106 // ^ ^ ^ ^ |
| 107 // | | | | |
| 108 // +--+ +-------------+ | |
| 109 // | | +--------------+ +--+ |
| 110 // | | | | |
| 111 // Merge Merge |
| 112 // ^ ^ |
| 113 // | | |
| 114 |
| 115 Node* branch = node; |
| 116 Node* cond = NodeProperties::GetValueInput(branch, 0); |
| 117 if (!cond->OwnedBy(branch) || cond->opcode() != IrOpcode::kPhi) return false; |
| 118 Node* merge = NodeProperties::GetControlInput(branch); |
| 119 if (merge->opcode() != IrOpcode::kMerge || |
| 120 NodeProperties::GetControlInput(cond) != merge) { |
| 121 return false; |
| 122 } |
| 123 // Grab the IfTrue/IfFalse projections of the Branch. |
| 124 Node* control_projections[2]; |
| 125 NodeProperties::CollectControlProjections(branch, control_projections, |
| 126 arraysize(control_projections)); |
| 127 Node* if_true = control_projections[0]; |
| 128 Node* if_false = control_projections[1]; |
| 129 DCHECK_EQ(IrOpcode::kIfTrue, if_true->opcode()); |
| 130 DCHECK_EQ(IrOpcode::kIfFalse, if_false->opcode()); |
| 131 // Check/collect other Phi/EffectPhi nodes hanging off the Merge. |
| 132 NodeVector phis(zone()); |
| 133 for (Node* const use : merge->uses()) { |
| 134 if (use == branch || use == cond) continue; |
| 135 // We cannot currently deal with non-Phi/EffectPhi nodes hanging off the |
| 136 // Merge. Ideally, we would just clone the nodes (and everything that |
| 137 // depends on it to some distant join point), but that requires knowledge |
| 138 // about dominance/post-dominance. |
| 139 if (!NodeProperties::IsPhi(use)) return false; |
| 140 for (Edge edge : use->use_edges()) { |
| 141 // Right now we can only handle Phi/EffectPhi nodes whose uses are |
| 142 // directly control-dependend on either the IfTrue or the IfFalse |
| 143 // successor, because we know exactly how to update those uses. |
| 144 // TODO(turbofan): Generalize this to all Phi/EffectPhi nodes using |
| 145 // dominance/post-dominance on the sea of nodes. |
| 146 if (edge.from()->op()->ControlInputCount() != 1) return false; |
| 147 Node* control = NodeProperties::GetControlInput(edge.from()); |
| 148 if (NodeProperties::IsPhi(edge.from())) { |
| 149 control = NodeProperties::GetControlInput(control, edge.index()); |
| 150 } |
| 151 if (control != if_true && control != if_false) return false; |
| 152 } |
| 153 phis.push_back(use); |
| 154 } |
| 155 BranchHint const hint = BranchHintOf(branch->op()); |
| 156 int const input_count = merge->op()->ControlInputCount(); |
| 157 DCHECK_LE(1, input_count); |
| 158 Node** const inputs = zone()->NewArray<Node*>(2 * input_count); |
| 159 Node** const merge_true_inputs = &inputs[0]; |
| 160 Node** const merge_false_inputs = &inputs[input_count]; |
| 161 for (int index = 0; index < input_count; ++index) { |
| 162 Node* cond1 = NodeProperties::GetValueInput(cond, index); |
| 163 Node* control1 = NodeProperties::GetControlInput(merge, index); |
| 164 Node* branch1 = graph()->NewNode(common()->Branch(hint), cond1, control1); |
| 165 merge_true_inputs[index] = graph()->NewNode(common()->IfTrue(), branch1); |
| 166 merge_false_inputs[index] = graph()->NewNode(common()->IfFalse(), branch1); |
| 167 Enqueue(branch1); |
| 168 } |
| 169 Node* const merge_true = graph()->NewNode(common()->Merge(input_count), |
| 170 input_count, merge_true_inputs); |
| 171 Node* const merge_false = graph()->NewNode(common()->Merge(input_count), |
| 172 input_count, merge_false_inputs); |
| 173 for (Node* const phi : phis) { |
| 174 for (int index = 0; index < input_count; ++index) { |
| 175 inputs[index] = phi->InputAt(index); |
| 176 } |
| 177 inputs[input_count] = merge_true; |
| 178 Node* phi_true = graph()->NewNode(phi->op(), input_count + 1, inputs); |
| 179 inputs[input_count] = merge_false; |
| 180 Node* phi_false = graph()->NewNode(phi->op(), input_count + 1, inputs); |
| 181 for (Edge edge : phi->use_edges()) { |
| 182 Node* control = NodeProperties::GetControlInput(edge.from()); |
| 183 if (NodeProperties::IsPhi(edge.from())) { |
| 184 control = NodeProperties::GetControlInput(control, edge.index()); |
| 185 } |
| 186 DCHECK(control == if_true || control == if_false); |
| 187 edge.UpdateTo((control == if_true) ? phi_true : phi_false); |
| 188 } |
| 189 phi->Kill(); |
| 190 } |
| 191 // Fix up IfTrue and IfFalse and kill all dead nodes. |
| 192 if_false->ReplaceUses(merge_false); |
| 193 if_true->ReplaceUses(merge_true); |
| 194 if_false->Kill(); |
| 195 if_true->Kill(); |
| 196 branch->Kill(); |
| 197 cond->Kill(); |
| 198 merge->Kill(); |
| 199 return true; |
| 200 } |
| 201 |
| 202 |
| 203 bool ControlFlowOptimizer::TryBuildSwitch(Node* node) { |
| 204 DCHECK_EQ(IrOpcode::kBranch, node->opcode()); |
57 | 205 |
58 Node* branch = node; | 206 Node* branch = node; |
59 Node* cond = NodeProperties::GetValueInput(branch, 0); | 207 Node* cond = NodeProperties::GetValueInput(branch, 0); |
60 if (cond->opcode() != IrOpcode::kWord32Equal) return VisitNode(node); | 208 if (cond->opcode() != IrOpcode::kWord32Equal) return false; |
61 Int32BinopMatcher m(cond); | 209 Int32BinopMatcher m(cond); |
62 Node* index = m.left().node(); | 210 Node* index = m.left().node(); |
63 if (!m.right().HasValue()) return VisitNode(node); | 211 if (!m.right().HasValue()) return false; |
64 int32_t value = m.right().Value(); | 212 int32_t value = m.right().Value(); |
65 ZoneSet<int32_t> values(zone()); | 213 ZoneSet<int32_t> values(zone()); |
66 values.insert(value); | 214 values.insert(value); |
67 | 215 |
68 Node* if_false; | 216 Node* if_false; |
69 Node* if_true; | 217 Node* if_true; |
70 while (true) { | 218 while (true) { |
71 Node* control_projections[2]; | 219 Node* control_projections[2]; |
72 NodeProperties::CollectControlProjections(branch, control_projections, 2); | 220 NodeProperties::CollectControlProjections(branch, control_projections, 2); |
73 if_true = control_projections[0]; | 221 if_true = control_projections[0]; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
115 node->set_op(common()->Switch(values.size() + 1)); | 263 node->set_op(common()->Switch(values.size() + 1)); |
116 node->ReplaceInput(0, index); | 264 node->ReplaceInput(0, index); |
117 if_true->set_op(common()->IfValue(value)); | 265 if_true->set_op(common()->IfValue(value)); |
118 if_true->ReplaceInput(0, node); | 266 if_true->ReplaceInput(0, node); |
119 Enqueue(if_true); | 267 Enqueue(if_true); |
120 if_false->set_op(common()->IfDefault()); | 268 if_false->set_op(common()->IfDefault()); |
121 if_false->ReplaceInput(0, node); | 269 if_false->ReplaceInput(0, node); |
122 Enqueue(if_false); | 270 Enqueue(if_false); |
123 branch->RemoveAllInputs(); | 271 branch->RemoveAllInputs(); |
124 } | 272 } |
| 273 return true; |
125 } | 274 } |
126 | 275 |
127 | 276 |
128 CommonOperatorBuilder* ControlFlowOptimizer::common() const { | 277 CommonOperatorBuilder* ControlFlowOptimizer::common() const { |
129 return jsgraph()->common(); | 278 return jsgraph()->common(); |
130 } | 279 } |
131 | 280 |
132 | 281 |
133 Graph* ControlFlowOptimizer::graph() const { return jsgraph()->graph(); } | 282 Graph* ControlFlowOptimizer::graph() const { return jsgraph()->graph(); } |
134 | 283 |
135 | 284 |
136 MachineOperatorBuilder* ControlFlowOptimizer::machine() const { | 285 MachineOperatorBuilder* ControlFlowOptimizer::machine() const { |
137 return jsgraph()->machine(); | 286 return jsgraph()->machine(); |
138 } | 287 } |
139 | 288 |
140 } // namespace compiler | 289 } // namespace compiler |
141 } // namespace internal | 290 } // namespace internal |
142 } // namespace v8 | 291 } // namespace v8 |
OLD | NEW |