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