Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1113)

Side by Side Diff: src/compiler/loop-variable-optimizer.cc

Issue 2164263003: [turbofan] Induction variable bound analysis. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Rebase Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/compiler/loop-variable-optimizer.h ('k') | src/compiler/opcodes.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « src/compiler/loop-variable-optimizer.h ('k') | src/compiler/opcodes.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698