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. 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 12 namespace v8 { 13 namespace internal { 14 namespace compiler { 15 16 ControlFlowOptimizer::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 28 void 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 46 void 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 54 void 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 63 void 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 71 bool 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 206 bool 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 279