OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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/loop-variable-optimizer.h" |
| 6 |
| 7 #include "src/compiler/common-operator.h" |
| 8 #include "src/compiler/graph.h" |
| 9 #include "src/compiler/node-marker.h" |
| 10 #include "src/compiler/node-properties.h" |
| 11 #include "src/compiler/node.h" |
| 12 #include "src/zone-containers.h" |
| 13 #include "src/zone.h" |
| 14 |
| 15 namespace v8 { |
| 16 namespace internal { |
| 17 namespace compiler { |
| 18 |
| 19 // Macro for outputting trace information from representation inference. |
| 20 #define TRACE(...) \ |
| 21 do { \ |
| 22 if (FLAG_trace_turbo_loop) PrintF(__VA_ARGS__); \ |
| 23 } while (false) |
| 24 |
| 25 LoopVariableOptimizer::LoopVariableOptimizer(Graph* graph, |
| 26 CommonOperatorBuilder* common, |
| 27 Zone* zone) |
| 28 : graph_(graph), |
| 29 common_(common), |
| 30 zone_(zone), |
| 31 limits_(zone), |
| 32 induction_vars_(zone) {} |
| 33 |
| 34 void LoopVariableOptimizer::Run() { |
| 35 ZoneQueue<Node*> queue(zone()); |
| 36 queue.push(graph()->start()); |
| 37 NodeMarker<bool> queued(graph(), 2); |
| 38 while (!queue.empty()) { |
| 39 Node* node = queue.front(); |
| 40 queue.pop(); |
| 41 queued.Set(node, false); |
| 42 |
| 43 DCHECK(limits_.find(node->id()) == limits_.end()); |
| 44 bool all_inputs_visited = true; |
| 45 int inputs_end = (node->opcode() == IrOpcode::kLoop) |
| 46 ? kFirstBackedge |
| 47 : node->op()->ControlInputCount(); |
| 48 for (int i = 0; i < inputs_end; i++) { |
| 49 auto input = limits_.find(NodeProperties::GetControlInput(node, i)->id()); |
| 50 if (input == limits_.end()) { |
| 51 all_inputs_visited = false; |
| 52 break; |
| 53 } |
| 54 } |
| 55 if (!all_inputs_visited) continue; |
| 56 |
| 57 VisitNode(node); |
| 58 DCHECK(limits_.find(node->id()) != limits_.end()); |
| 59 |
| 60 // Queue control outputs. |
| 61 for (Edge edge : node->use_edges()) { |
| 62 if (NodeProperties::IsControlEdge(edge) && |
| 63 edge.from()->op()->ControlOutputCount() > 0) { |
| 64 Node* use = edge.from(); |
| 65 if (use->opcode() == IrOpcode::kLoop && |
| 66 edge.index() != kAssumedLoopEntryIndex) { |
| 67 VisitBackedge(node, use); |
| 68 } else if (!queued.Get(use)) { |
| 69 queue.push(use); |
| 70 queued.Set(use, true); |
| 71 } |
| 72 } |
| 73 } |
| 74 } |
| 75 } |
| 76 |
| 77 class LoopVariableOptimizer::Constraint : public ZoneObject { |
| 78 public: |
| 79 InductionVariable::ConstraintKind kind() const { return kind_; } |
| 80 Node* left() const { return left_; } |
| 81 Node* right() const { return right_; } |
| 82 |
| 83 const Constraint* next() const { return next_; } |
| 84 |
| 85 Constraint(Node* left, InductionVariable::ConstraintKind kind, Node* right, |
| 86 const Constraint* next) |
| 87 : left_(left), right_(right), kind_(kind), next_(next) {} |
| 88 |
| 89 private: |
| 90 Node* left_; |
| 91 Node* right_; |
| 92 InductionVariable::ConstraintKind kind_; |
| 93 const Constraint* next_; |
| 94 }; |
| 95 |
| 96 class LoopVariableOptimizer::VariableLimits : public ZoneObject { |
| 97 public: |
| 98 static VariableLimits* Empty(Zone* zone) { |
| 99 return new (zone) VariableLimits(); |
| 100 } |
| 101 |
| 102 VariableLimits* Copy(Zone* zone) const { |
| 103 return new (zone) VariableLimits(this); |
| 104 } |
| 105 |
| 106 void Add(Node* left, InductionVariable::ConstraintKind kind, Node* right, |
| 107 Zone* zone) { |
| 108 head_ = new (zone) Constraint(left, kind, right, head_); |
| 109 limit_count_++; |
| 110 } |
| 111 |
| 112 void Merge(const VariableLimits* other) { |
| 113 // Change the current condition list to a longest common tail |
| 114 // of this condition list and the other list. (The common tail |
| 115 // should correspond to the list from the common dominator.) |
| 116 |
| 117 // First, we throw away the prefix of the longer list, so that |
| 118 // we have lists of the same length. |
| 119 size_t other_size = other->limit_count_; |
| 120 const Constraint* other_limit = other->head_; |
| 121 while (other_size > limit_count_) { |
| 122 other_limit = other_limit->next(); |
| 123 other_size--; |
| 124 } |
| 125 while (limit_count_ > other_size) { |
| 126 head_ = head_->next(); |
| 127 limit_count_--; |
| 128 } |
| 129 |
| 130 // Then we go through both lists in lock-step until we find |
| 131 // the common tail. |
| 132 while (head_ != other_limit) { |
| 133 DCHECK(limit_count_ > 0); |
| 134 limit_count_--; |
| 135 other_limit = other_limit->next(); |
| 136 head_ = head_->next(); |
| 137 } |
| 138 } |
| 139 |
| 140 const Constraint* head() const { return head_; } |
| 141 |
| 142 private: |
| 143 VariableLimits() {} |
| 144 explicit VariableLimits(const VariableLimits* other) |
| 145 : head_(other->head_), limit_count_(other->limit_count_) {} |
| 146 |
| 147 const Constraint* head_ = nullptr; |
| 148 size_t limit_count_ = 0; |
| 149 }; |
| 150 |
| 151 void InductionVariable::AddUpperBound(Node* bound, |
| 152 InductionVariable::ConstraintKind kind, |
| 153 Zone* graph_zone) { |
| 154 if (FLAG_trace_turbo_loop) { |
| 155 OFStream os(stdout); |
| 156 os << "New upper bound for " << phi()->id() << " (loop " |
| 157 << NodeProperties::GetControlInput(phi())->id() << "): " << *bound |
| 158 << std::endl; |
| 159 } |
| 160 upper_bounds_.push_back(Bound(bound, kind)); |
| 161 } |
| 162 |
| 163 void InductionVariable::AddLowerBound(Node* bound, |
| 164 InductionVariable::ConstraintKind kind, |
| 165 Zone* graph_zone) { |
| 166 if (FLAG_trace_turbo_loop) { |
| 167 OFStream os(stdout); |
| 168 os << "New lower bound for " << phi()->id() << " (loop " |
| 169 << NodeProperties::GetControlInput(phi())->id() << "): " << *bound; |
| 170 } |
| 171 lower_bounds_.push_back(Bound(bound, kind)); |
| 172 } |
| 173 |
| 174 void LoopVariableOptimizer::VisitBackedge(Node* from, Node* loop) { |
| 175 if (loop->op()->ControlInputCount() != 2) return; |
| 176 |
| 177 // Go through the constraints, and update the induction variables in |
| 178 // this loop if they are involved in the constraint. |
| 179 const VariableLimits* limits = limits_[from->id()]; |
| 180 for (const Constraint* constraint = limits->head(); constraint != nullptr; |
| 181 constraint = constraint->next()) { |
| 182 if (constraint->left()->opcode() == IrOpcode::kPhi && |
| 183 NodeProperties::GetControlInput(constraint->left()) == loop) { |
| 184 auto var = induction_vars_.find(constraint->left()->id()); |
| 185 if (var != induction_vars_.end()) { |
| 186 var->second->AddUpperBound(constraint->right(), constraint->kind(), |
| 187 graph()->zone()); |
| 188 } |
| 189 } |
| 190 if (constraint->right()->opcode() == IrOpcode::kPhi && |
| 191 NodeProperties::GetControlInput(constraint->right()) == loop) { |
| 192 auto var = induction_vars_.find(constraint->right()->id()); |
| 193 if (var != induction_vars_.end()) { |
| 194 var->second->AddUpperBound(constraint->left(), constraint->kind(), |
| 195 graph()->zone()); |
| 196 } |
| 197 } |
| 198 } |
| 199 } |
| 200 |
| 201 void LoopVariableOptimizer::VisitNode(Node* node) { |
| 202 switch (node->opcode()) { |
| 203 case IrOpcode::kMerge: |
| 204 return VisitMerge(node); |
| 205 case IrOpcode::kLoop: |
| 206 return VisitLoop(node); |
| 207 case IrOpcode::kIfFalse: |
| 208 return VisitIf(node, false); |
| 209 case IrOpcode::kIfTrue: |
| 210 return VisitIf(node, true); |
| 211 case IrOpcode::kStart: |
| 212 return VisitStart(node); |
| 213 case IrOpcode::kLoopExit: |
| 214 return VisitLoopExit(node); |
| 215 default: |
| 216 return VisitOtherControl(node); |
| 217 } |
| 218 } |
| 219 |
| 220 void LoopVariableOptimizer::VisitMerge(Node* node) { |
| 221 // Merge the limits of all incoming edges. |
| 222 VariableLimits* merged = limits_[node->InputAt(0)->id()]->Copy(zone()); |
| 223 for (int i = 1; i < node->InputCount(); i++) { |
| 224 merged->Merge(limits_[node->InputAt(0)->id()]); |
| 225 } |
| 226 limits_[node->id()] = merged; |
| 227 } |
| 228 |
| 229 void LoopVariableOptimizer::VisitLoop(Node* node) { |
| 230 DetectInductionVariables(node); |
| 231 // Conservatively take the limits from the loop entry here. |
| 232 return TakeConditionsFromFirstControl(node); |
| 233 } |
| 234 |
| 235 void LoopVariableOptimizer::VisitIf(Node* node, bool polarity) { |
| 236 Node* branch = node->InputAt(0); |
| 237 Node* cond = branch->InputAt(0); |
| 238 VariableLimits* limits = limits_[branch->id()]->Copy(zone()); |
| 239 // Normalize to less than comparison. |
| 240 switch (cond->opcode()) { |
| 241 case IrOpcode::kJSLessThan: |
| 242 AddCmpToLimits(limits, cond, InductionVariable::kStrict, polarity); |
| 243 break; |
| 244 case IrOpcode::kJSGreaterThan: |
| 245 AddCmpToLimits(limits, cond, InductionVariable::kNonStrict, !polarity); |
| 246 break; |
| 247 case IrOpcode::kJSLessThanOrEqual: |
| 248 AddCmpToLimits(limits, cond, InductionVariable::kNonStrict, polarity); |
| 249 break; |
| 250 case IrOpcode::kJSGreaterThanOrEqual: |
| 251 AddCmpToLimits(limits, cond, InductionVariable::kStrict, !polarity); |
| 252 break; |
| 253 default: |
| 254 break; |
| 255 } |
| 256 limits_[node->id()] = limits; |
| 257 } |
| 258 |
| 259 void LoopVariableOptimizer::AddCmpToLimits( |
| 260 VariableLimits* limits, Node* node, InductionVariable::ConstraintKind kind, |
| 261 bool polarity) { |
| 262 Node* left = node->InputAt(0); |
| 263 Node* right = node->InputAt(1); |
| 264 if (FindInductionVariable(left) || FindInductionVariable(right)) { |
| 265 if (polarity) { |
| 266 limits->Add(left, kind, right, zone()); |
| 267 } else { |
| 268 kind = (kind == InductionVariable::kStrict) |
| 269 ? InductionVariable::kNonStrict |
| 270 : InductionVariable::kStrict; |
| 271 limits->Add(right, kind, left, zone()); |
| 272 } |
| 273 } |
| 274 } |
| 275 |
| 276 void LoopVariableOptimizer::VisitStart(Node* node) { |
| 277 limits_[node->id()] = VariableLimits::Empty(zone()); |
| 278 } |
| 279 |
| 280 void LoopVariableOptimizer::VisitLoopExit(Node* node) { |
| 281 return TakeConditionsFromFirstControl(node); |
| 282 } |
| 283 |
| 284 void LoopVariableOptimizer::VisitOtherControl(Node* node) { |
| 285 DCHECK_EQ(1, node->op()->ControlInputCount()); |
| 286 return TakeConditionsFromFirstControl(node); |
| 287 } |
| 288 |
| 289 void LoopVariableOptimizer::TakeConditionsFromFirstControl(Node* node) { |
| 290 const VariableLimits* limits = |
| 291 limits_[NodeProperties::GetControlInput(node, 0)->id()]; |
| 292 DCHECK_NOT_NULL(limits); |
| 293 limits_[node->id()] = limits; |
| 294 } |
| 295 |
| 296 const InductionVariable* LoopVariableOptimizer::FindInductionVariable( |
| 297 Node* node) { |
| 298 auto var = induction_vars_.find(node->id()); |
| 299 if (var != induction_vars_.end()) { |
| 300 return var->second; |
| 301 } |
| 302 return nullptr; |
| 303 } |
| 304 |
| 305 InductionVariable* LoopVariableOptimizer::TryGetInductionVariable(Node* phi) { |
| 306 DCHECK_EQ(2, phi->op()->ValueInputCount()); |
| 307 DCHECK_EQ(IrOpcode::kLoop, NodeProperties::GetControlInput(phi)->opcode()); |
| 308 Node* initial = phi->InputAt(0); |
| 309 Node* arith = phi->InputAt(1); |
| 310 // TODO(jarin) Support subtraction. |
| 311 if (arith->opcode() != IrOpcode::kJSAdd) { |
| 312 return nullptr; |
| 313 } |
| 314 // TODO(jarin) Support both sides. |
| 315 if (arith->InputAt(0) != phi) { |
| 316 if (arith->InputAt(0)->opcode() != IrOpcode::kJSToNumber || |
| 317 arith->InputAt(0)->InputAt(0) != phi) { |
| 318 return nullptr; |
| 319 } |
| 320 } |
| 321 Node* incr = arith->InputAt(1); |
| 322 return new (zone()) InductionVariable(phi, arith, incr, initial, zone()); |
| 323 } |
| 324 |
| 325 void LoopVariableOptimizer::DetectInductionVariables(Node* loop) { |
| 326 if (loop->op()->ControlInputCount() != 2) return; |
| 327 TRACE("Loop variables for loop %i:", loop->id()); |
| 328 for (Edge edge : loop->use_edges()) { |
| 329 if (NodeProperties::IsControlEdge(edge) && |
| 330 edge.from()->opcode() == IrOpcode::kPhi) { |
| 331 Node* phi = edge.from(); |
| 332 InductionVariable* induction_var = TryGetInductionVariable(phi); |
| 333 if (induction_var) { |
| 334 induction_vars_[phi->id()] = induction_var; |
| 335 TRACE(" %i", induction_var->phi()->id()); |
| 336 } |
| 337 } |
| 338 } |
| 339 TRACE("\n"); |
| 340 } |
| 341 |
| 342 void LoopVariableOptimizer::ChangeToInductionVariablePhis() { |
| 343 for (auto entry : induction_vars_) { |
| 344 // It only make sense to analyze the induction variables if |
| 345 // there is a bound. |
| 346 InductionVariable* induction_var = entry.second; |
| 347 DCHECK_EQ(MachineRepresentation::kTagged, |
| 348 PhiRepresentationOf(induction_var->phi()->op())); |
| 349 if (induction_var->upper_bounds().size() == 0 && |
| 350 induction_var->lower_bounds().size() == 0) { |
| 351 continue; |
| 352 } |
| 353 // Insert the increment value to the value inputs. |
| 354 induction_var->phi()->InsertInput(graph()->zone(), |
| 355 induction_var->phi()->InputCount() - 1, |
| 356 induction_var->increment()); |
| 357 // Insert the bound inputs to the value inputs. |
| 358 for (auto bound : induction_var->lower_bounds()) { |
| 359 induction_var->phi()->InsertInput( |
| 360 graph()->zone(), induction_var->phi()->InputCount() - 1, bound.bound); |
| 361 } |
| 362 for (auto bound : induction_var->upper_bounds()) { |
| 363 induction_var->phi()->InsertInput( |
| 364 graph()->zone(), induction_var->phi()->InputCount() - 1, bound.bound); |
| 365 } |
| 366 NodeProperties::ChangeOp( |
| 367 induction_var->phi(), |
| 368 common()->InductionVariablePhi(induction_var->phi()->InputCount() - 1)); |
| 369 } |
| 370 } |
| 371 |
| 372 void LoopVariableOptimizer::ChangeFromInductionVariablePhis() { |
| 373 for (auto entry : induction_vars_) { |
| 374 InductionVariable* induction_var = entry.second; |
| 375 if (induction_var->phi()->opcode() == IrOpcode::kInductionVariablePhi) { |
| 376 int value_count = 2; |
| 377 Node* control = NodeProperties::GetControlInput(induction_var->phi()); |
| 378 DCHECK_EQ(value_count, control->op()->ControlInputCount()); |
| 379 induction_var->phi()->TrimInputCount(value_count + 1); |
| 380 induction_var->phi()->ReplaceInput(value_count, control); |
| 381 NodeProperties::ChangeOp( |
| 382 induction_var->phi(), |
| 383 common()->Phi(MachineRepresentation::kTagged, value_count)); |
| 384 } |
| 385 } |
| 386 } |
| 387 |
| 388 } // namespace compiler |
| 389 } // namespace internal |
| 390 } // namespace v8 |
OLD | NEW |