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/branch-elimination.h" 6 7 #include "src/compiler/js-graph.h" 8 #include "src/compiler/node-properties.h" 9 #include "src/compiler/simplified-operator.h" 10 11 namespace v8 { 12 namespace internal { 13 namespace compiler { 14 15 BranchElimination::BranchElimination(Editor* editor, JSGraph* js_graph, 16 Zone* zone) 17 : AdvancedReducer(editor), 18 node_conditions_(zone, js_graph->graph()->NodeCount()), 19 zone_(zone), 20 dead_(js_graph->graph()->NewNode(js_graph->common()->Dead())) {} 21 22 23 BranchElimination::~BranchElimination() {} 24 25 26 Reduction BranchElimination::Reduce(Node* node) { 27 switch (node->opcode()) { 28 case IrOpcode::kDead: 29 return NoChange(); 30 case IrOpcode::kMerge: 31 return ReduceMerge(node); 32 case IrOpcode::kLoop: 33 return ReduceLoop(node); 34 case IrOpcode::kBranch: 35 return ReduceBranch(node); 36 case IrOpcode::kIfFalse: 37 return ReduceIf(node, false); 38 case IrOpcode::kIfTrue: 39 return ReduceIf(node, true); 40 case IrOpcode::kStart: 41 return ReduceStart(node); 42 default: 43 if (node->op()->ControlOutputCount() > 0) { 44 return ReduceOtherControl(node); 45 } 46 break; 47 } 48 return NoChange(); 49 } 50 51 52 Reduction BranchElimination::ReduceBranch(Node* node) { 53 Node* condition = node->InputAt(0); 54 Node* control_input = NodeProperties::GetControlInput(node, 0); 55 const ControlPathConditions* from_input = node_conditions_.Get(control_input); 56 if (from_input != nullptr) { 57 Maybe<bool> condition_value = from_input->LookupCondition(condition); 58 // If we know the condition we can discard the branch. 59 if (condition_value.IsJust()) { 60 bool known_value = condition_value.FromJust(); 61 for (Node* const use : node->uses()) { 62 switch (use->opcode()) { 63 case IrOpcode::kIfTrue: 64 Replace(use, known_value ? control_input : dead()); 65 break; 66 case IrOpcode::kIfFalse: 67 Replace(use, known_value ? dead() : control_input); 68 break; 69 default: 70 UNREACHABLE(); 71 } 72 } 73 return Replace(dead()); 74 } 75 } 76 return TakeConditionsFromFirstControl(node); 77 } 78 79 80 Reduction BranchElimination::ReduceIf(Node* node, bool is_true_branch) { 81 // Add the condition to the list arriving from the input branch. 82 Node* branch = NodeProperties::GetControlInput(node, 0); 83 const ControlPathConditions* from_branch = node_conditions_.Get(branch); 84 // If we do not know anything about the predecessor, do not propagate just 85 // yet because we will have to recompute anyway once we compute the 86 // predecessor. 87 if (from_branch == nullptr) { 88 DCHECK(node_conditions_.Get(node) == nullptr); 89 return NoChange(); 90 } 91 Node* condition = branch->InputAt(0); 92 return UpdateConditions( 93 node, from_branch->AddCondition(zone_, condition, is_true_branch)); 94 } 95 96 97 Reduction BranchElimination::ReduceLoop(Node* node) { 98 // Here we rely on having only reducible loops: 99 // The loop entry edge always dominates the header, so we can just use 100 // the information from the loop entry edge. 101 return TakeConditionsFromFirstControl(node); 102 } 103 104 105 Reduction BranchElimination::ReduceMerge(Node* node) { 106 // Shortcut for the case when we do not know anything about some 107 // input. 108 for (int i = 0; i < node->InputCount(); i++) { 109 if (node_conditions_.Get(node->InputAt(i)) == nullptr) { 110 DCHECK(node_conditions_.Get(node) == nullptr); 111 return NoChange(); 112 } 113 } 114 115 const ControlPathConditions* first = node_conditions_.Get(node->InputAt(0)); 116 // Make a copy of the first input's conditions and merge with the conditions 117 // from other inputs. 118 ControlPathConditions* conditions = 119 new (zone_->New(sizeof(ControlPathConditions))) 120 ControlPathConditions(*first); 121 for (int i = 1; i < node->InputCount(); i++) { 122 conditions->Merge(*(node_conditions_.Get(node->InputAt(i)))); 123 } 124 125 return UpdateConditions(node, conditions); 126 } 127 128 129 Reduction BranchElimination::ReduceStart(Node* node) { 130 return UpdateConditions(node, ControlPathConditions::Empty(zone_)); 131 } 132 133 134 const BranchElimination::ControlPathConditions* 135 BranchElimination::PathConditionsForControlNodes::Get(Node* node) { 136 if (static_cast<size_t>(node->id()) < info_for_node_.size()) { 137 return info_for_node_[node->id()]; 138 } 139 return nullptr; 140 } 141 142 143 void BranchElimination::PathConditionsForControlNodes::Set( 144 Node* node, const ControlPathConditions* conditions) { 145 size_t index = static_cast<size_t>(node->id()); 146 if (index >= info_for_node_.size()) { 147 info_for_node_.resize(index + 1, nullptr); 148 } 149 info_for_node_[index] = conditions; 150 } 151 152 153 Reduction BranchElimination::ReduceOtherControl(Node* node) { 154 DCHECK_EQ(1, node->op()->ControlInputCount()); 155 return TakeConditionsFromFirstControl(node); 156 } 157 158 159 Reduction BranchElimination::TakeConditionsFromFirstControl(Node* node) { 160 // We just propagate the information from the control input (ideally, 161 // we would only revisit control uses if there is change). 162 const ControlPathConditions* from_input = 163 node_conditions_.Get(NodeProperties::GetControlInput(node, 0)); 164 return UpdateConditions(node, from_input); 165 } 166 167 168 Reduction BranchElimination::UpdateConditions( 169 Node* node, const ControlPathConditions* conditions) { 170 const ControlPathConditions* original = node_conditions_.Get(node); 171 // Only signal that the node has Changed if the condition information has 172 // changed. 173 if (conditions != original) { 174 if (original == nullptr || *conditions != *original) { 175 node_conditions_.Set(node, conditions); 176 return Changed(node); 177 } 178 } 179 return NoChange(); 180 } 181 182 183 // static 184 const BranchElimination::ControlPathConditions* 185 BranchElimination::ControlPathConditions::Empty(Zone* zone) { 186 return new (zone->New(sizeof(ControlPathConditions))) 187 ControlPathConditions(nullptr, 0); 188 } 189 190 191 void BranchElimination::ControlPathConditions::Merge( 192 const ControlPathConditions& other) { 193 // Change the current condition list to a longest common tail 194 // of this condition list and the other list. (The common tail 195 // should correspond to the list from the common dominator.) 196 197 // First, we throw away the prefix of the longer list, so that 198 // we have lists of the same length. 199 size_t other_size = other.condition_count_; 200 BranchCondition* other_condition = other.head_; 201 while (other_size > condition_count_) { 202 other_condition = other_condition->next; 203 other_size--; 204 } 205 while (condition_count_ > other_size) { 206 head_ = head_->next; 207 condition_count_--; 208 } 209 210 // Then we go through both lists in lock-step until we find 211 // the common tail. 212 while (head_ != other_condition) { 213 DCHECK(condition_count_ > 0); 214 condition_count_--; 215 other_condition = other_condition->next; 216 head_ = head_->next; 217 } 218 } 219 220 221 const BranchElimination::ControlPathConditions* 222 BranchElimination::ControlPathConditions::AddCondition(Zone* zone, 223 Node* condition, 224 bool is_true) const { 225 DCHECK(LookupCondition(condition).IsNothing()); 226 227 BranchCondition* new_head = new (zone->New(sizeof(BranchCondition))) 228 BranchCondition(condition, is_true, head_); 229 230 ControlPathConditions* conditions = 231 new (zone->New(sizeof(ControlPathConditions))) 232 ControlPathConditions(new_head, condition_count_ + 1); 233 return conditions; 234 } 235 236 237 Maybe<bool> BranchElimination::ControlPathConditions::LookupCondition( 238 Node* condition) const { 239 for (BranchCondition* current = head_; current != nullptr; 240 current = current->next) { 241 if (current->condition == condition) { 242 return Just<bool>(current->is_true); 243 } 244 } 245 return Nothing<bool>(); 246 } 247 248 249 bool BranchElimination::ControlPathConditions::operator==( 250 const ControlPathConditions& other) const { 251 if (condition_count_ != other.condition_count_) return false; 252 BranchCondition* this_condition = head_; 253 BranchCondition* other_condition = other.head_; 254 while (true) { 255 if (this_condition == other_condition) return true; 256 if (this_condition->condition != other_condition->condition || 257 this_condition->is_true != other_condition->is_true) { 258 return false; 259 } 260 this_condition = this_condition->next; 261 other_condition = other_condition->next; 262 } 263 UNREACHABLE(); 264 return false; 265 } 266 267 } // namespace compiler 268 } // namespace internal 269 } // namespace v8 270