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 reduced_(graph->NodeCount(), zone),
33 induction_vars_(zone) {}
34
Run()35 void LoopVariableOptimizer::Run() {
36 ZoneQueue<Node*> queue(zone());
37 queue.push(graph()->start());
38 NodeMarker<bool> queued(graph(), 2);
39 while (!queue.empty()) {
40 Node* node = queue.front();
41 queue.pop();
42 queued.Set(node, false);
43
44 DCHECK(!reduced_.Get(node));
45 bool all_inputs_visited = true;
46 int inputs_end = (node->opcode() == IrOpcode::kLoop)
47 ? kFirstBackedge
48 : node->op()->ControlInputCount();
49 for (int i = 0; i < inputs_end; i++) {
50 if (!reduced_.Get(NodeProperties::GetControlInput(node, i))) {
51 all_inputs_visited = false;
52 break;
53 }
54 }
55 if (!all_inputs_visited) continue;
56
57 VisitNode(node);
58 reduced_.Set(node, true);
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
AddUpperBound(Node * bound,InductionVariable::ConstraintKind kind)77 void InductionVariable::AddUpperBound(Node* bound,
78 InductionVariable::ConstraintKind kind) {
79 if (FLAG_trace_turbo_loop) {
80 StdoutStream{} << "New upper bound for " << phi()->id() << " (loop "
81 << NodeProperties::GetControlInput(phi())->id()
82 << "): " << *bound << std::endl;
83 }
84 upper_bounds_.push_back(Bound(bound, kind));
85 }
86
AddLowerBound(Node * bound,InductionVariable::ConstraintKind kind)87 void InductionVariable::AddLowerBound(Node* bound,
88 InductionVariable::ConstraintKind kind) {
89 if (FLAG_trace_turbo_loop) {
90 StdoutStream{} << "New lower bound for " << phi()->id() << " (loop "
91 << NodeProperties::GetControlInput(phi())->id()
92 << "): " << *bound;
93 }
94 lower_bounds_.push_back(Bound(bound, kind));
95 }
96
VisitBackedge(Node * from,Node * loop)97 void LoopVariableOptimizer::VisitBackedge(Node* from, Node* loop) {
98 if (loop->op()->ControlInputCount() != 2) return;
99
100 // Go through the constraints, and update the induction variables in
101 // this loop if they are involved in the constraint.
102 for (Constraint constraint : limits_.Get(from)) {
103 if (constraint.left->opcode() == IrOpcode::kPhi &&
104 NodeProperties::GetControlInput(constraint.left) == loop) {
105 auto var = induction_vars_.find(constraint.left->id());
106 if (var != induction_vars_.end()) {
107 var->second->AddUpperBound(constraint.right, constraint.kind);
108 }
109 }
110 if (constraint.right->opcode() == IrOpcode::kPhi &&
111 NodeProperties::GetControlInput(constraint.right) == loop) {
112 auto var = induction_vars_.find(constraint.right->id());
113 if (var != induction_vars_.end()) {
114 var->second->AddLowerBound(constraint.left, constraint.kind);
115 }
116 }
117 }
118 }
119
VisitNode(Node * node)120 void LoopVariableOptimizer::VisitNode(Node* node) {
121 switch (node->opcode()) {
122 case IrOpcode::kMerge:
123 return VisitMerge(node);
124 case IrOpcode::kLoop:
125 return VisitLoop(node);
126 case IrOpcode::kIfFalse:
127 return VisitIf(node, false);
128 case IrOpcode::kIfTrue:
129 return VisitIf(node, true);
130 case IrOpcode::kStart:
131 return VisitStart(node);
132 case IrOpcode::kLoopExit:
133 return VisitLoopExit(node);
134 default:
135 return VisitOtherControl(node);
136 }
137 }
138
VisitMerge(Node * node)139 void LoopVariableOptimizer::VisitMerge(Node* node) {
140 // Merge the limits of all incoming edges.
141 VariableLimits merged = limits_.Get(node->InputAt(0));
142 for (int i = 1; i < node->InputCount(); i++) {
143 merged.ResetToCommonAncestor(limits_.Get(node->InputAt(i)));
144 }
145 limits_.Set(node, merged);
146 }
147
VisitLoop(Node * node)148 void LoopVariableOptimizer::VisitLoop(Node* node) {
149 DetectInductionVariables(node);
150 // Conservatively take the limits from the loop entry here.
151 return TakeConditionsFromFirstControl(node);
152 }
153
VisitIf(Node * node,bool polarity)154 void LoopVariableOptimizer::VisitIf(Node* node, bool polarity) {
155 Node* branch = node->InputAt(0);
156 Node* cond = branch->InputAt(0);
157 VariableLimits limits = limits_.Get(branch);
158 // Normalize to less than comparison.
159 switch (cond->opcode()) {
160 case IrOpcode::kJSLessThan:
161 case IrOpcode::kSpeculativeNumberLessThan:
162 AddCmpToLimits(&limits, cond, InductionVariable::kStrict, polarity);
163 break;
164 case IrOpcode::kJSGreaterThan:
165 AddCmpToLimits(&limits, cond, InductionVariable::kNonStrict, !polarity);
166 break;
167 case IrOpcode::kJSLessThanOrEqual:
168 case IrOpcode::kSpeculativeNumberLessThanOrEqual:
169 AddCmpToLimits(&limits, cond, InductionVariable::kNonStrict, polarity);
170 break;
171 case IrOpcode::kJSGreaterThanOrEqual:
172 AddCmpToLimits(&limits, cond, InductionVariable::kStrict, !polarity);
173 break;
174 default:
175 break;
176 }
177 limits_.Set(node, limits);
178 }
179
AddCmpToLimits(VariableLimits * limits,Node * node,InductionVariable::ConstraintKind kind,bool polarity)180 void LoopVariableOptimizer::AddCmpToLimits(
181 VariableLimits* limits, Node* node, InductionVariable::ConstraintKind kind,
182 bool polarity) {
183 Node* left = node->InputAt(0);
184 Node* right = node->InputAt(1);
185 if (FindInductionVariable(left) || FindInductionVariable(right)) {
186 if (polarity) {
187 limits->PushFront(Constraint{left, kind, right}, zone());
188 } else {
189 kind = (kind == InductionVariable::kStrict)
190 ? InductionVariable::kNonStrict
191 : InductionVariable::kStrict;
192 limits->PushFront(Constraint{right, kind, left}, zone());
193 }
194 }
195 }
196
VisitStart(Node * node)197 void LoopVariableOptimizer::VisitStart(Node* node) { limits_.Set(node, {}); }
198
VisitLoopExit(Node * node)199 void LoopVariableOptimizer::VisitLoopExit(Node* node) {
200 return TakeConditionsFromFirstControl(node);
201 }
202
VisitOtherControl(Node * node)203 void LoopVariableOptimizer::VisitOtherControl(Node* node) {
204 DCHECK_EQ(1, node->op()->ControlInputCount());
205 return TakeConditionsFromFirstControl(node);
206 }
207
TakeConditionsFromFirstControl(Node * node)208 void LoopVariableOptimizer::TakeConditionsFromFirstControl(Node* node) {
209 limits_.Set(node, limits_.Get(NodeProperties::GetControlInput(node, 0)));
210 }
211
FindInductionVariable(Node * node)212 const InductionVariable* LoopVariableOptimizer::FindInductionVariable(
213 Node* node) {
214 auto var = induction_vars_.find(node->id());
215 if (var != induction_vars_.end()) {
216 return var->second;
217 }
218 return nullptr;
219 }
220
TryGetInductionVariable(Node * phi)221 InductionVariable* LoopVariableOptimizer::TryGetInductionVariable(Node* phi) {
222 DCHECK_EQ(2, phi->op()->ValueInputCount());
223 Node* loop = NodeProperties::GetControlInput(phi);
224 DCHECK_EQ(IrOpcode::kLoop, loop->opcode());
225 Node* initial = phi->InputAt(0);
226 Node* arith = phi->InputAt(1);
227 InductionVariable::ArithmeticType arithmeticType;
228 if (arith->opcode() == IrOpcode::kJSAdd ||
229 arith->opcode() == IrOpcode::kSpeculativeNumberAdd ||
230 arith->opcode() == IrOpcode::kSpeculativeSafeIntegerAdd) {
231 arithmeticType = InductionVariable::ArithmeticType::kAddition;
232 } else if (arith->opcode() == IrOpcode::kJSSubtract ||
233 arith->opcode() == IrOpcode::kSpeculativeNumberSubtract ||
234 arith->opcode() == IrOpcode::kSpeculativeSafeIntegerSubtract) {
235 arithmeticType = InductionVariable::ArithmeticType::kSubtraction;
236 } else {
237 return nullptr;
238 }
239
240 // TODO(jarin) Support both sides.
241 Node* input = arith->InputAt(0);
242 if (input->opcode() == IrOpcode::kSpeculativeToNumber ||
243 input->opcode() == IrOpcode::kJSToNumber ||
244 input->opcode() == IrOpcode::kJSToNumberConvertBigInt) {
245 input = input->InputAt(0);
246 }
247 if (input != phi) return nullptr;
248
249 Node* effect_phi = nullptr;
250 for (Node* use : loop->uses()) {
251 if (use->opcode() == IrOpcode::kEffectPhi) {
252 DCHECK_NULL(effect_phi);
253 effect_phi = use;
254 }
255 }
256 if (!effect_phi) return nullptr;
257
258 Node* incr = arith->InputAt(1);
259 return new (zone()) InductionVariable(phi, effect_phi, arith, incr, initial,
260 zone(), arithmeticType);
261 }
262
DetectInductionVariables(Node * loop)263 void LoopVariableOptimizer::DetectInductionVariables(Node* loop) {
264 if (loop->op()->ControlInputCount() != 2) return;
265 TRACE("Loop variables for loop %i:", loop->id());
266 for (Edge edge : loop->use_edges()) {
267 if (NodeProperties::IsControlEdge(edge) &&
268 edge.from()->opcode() == IrOpcode::kPhi) {
269 Node* phi = edge.from();
270 InductionVariable* induction_var = TryGetInductionVariable(phi);
271 if (induction_var) {
272 induction_vars_[phi->id()] = induction_var;
273 TRACE(" %i", induction_var->phi()->id());
274 }
275 }
276 }
277 TRACE("\n");
278 }
279
ChangeToInductionVariablePhis()280 void LoopVariableOptimizer::ChangeToInductionVariablePhis() {
281 for (auto entry : induction_vars_) {
282 // It only make sense to analyze the induction variables if
283 // there is a bound.
284 InductionVariable* induction_var = entry.second;
285 DCHECK_EQ(MachineRepresentation::kTagged,
286 PhiRepresentationOf(induction_var->phi()->op()));
287 if (induction_var->upper_bounds().size() == 0 &&
288 induction_var->lower_bounds().size() == 0) {
289 continue;
290 }
291 // Insert the increment value to the value inputs.
292 induction_var->phi()->InsertInput(graph()->zone(),
293 induction_var->phi()->InputCount() - 1,
294 induction_var->increment());
295 // Insert the bound inputs to the value inputs.
296 for (auto bound : induction_var->lower_bounds()) {
297 induction_var->phi()->InsertInput(
298 graph()->zone(), induction_var->phi()->InputCount() - 1, bound.bound);
299 }
300 for (auto bound : induction_var->upper_bounds()) {
301 induction_var->phi()->InsertInput(
302 graph()->zone(), induction_var->phi()->InputCount() - 1, bound.bound);
303 }
304 NodeProperties::ChangeOp(
305 induction_var->phi(),
306 common()->InductionVariablePhi(induction_var->phi()->InputCount() - 1));
307 }
308 }
309
ChangeToPhisAndInsertGuards()310 void LoopVariableOptimizer::ChangeToPhisAndInsertGuards() {
311 for (auto entry : induction_vars_) {
312 InductionVariable* induction_var = entry.second;
313 if (induction_var->phi()->opcode() == IrOpcode::kInductionVariablePhi) {
314 // Turn the induction variable phi back to normal phi.
315 int value_count = 2;
316 Node* control = NodeProperties::GetControlInput(induction_var->phi());
317 DCHECK_EQ(value_count, control->op()->ControlInputCount());
318 induction_var->phi()->TrimInputCount(value_count + 1);
319 induction_var->phi()->ReplaceInput(value_count, control);
320 NodeProperties::ChangeOp(
321 induction_var->phi(),
322 common()->Phi(MachineRepresentation::kTagged, value_count));
323
324 // If the backedge is not a subtype of the phi's type, we insert a sigma
325 // to get the typing right.
326 Node* backedge_value = induction_var->phi()->InputAt(1);
327 Type backedge_type = NodeProperties::GetType(backedge_value);
328 Type phi_type = NodeProperties::GetType(induction_var->phi());
329 if (!backedge_type.Is(phi_type)) {
330 Node* loop = NodeProperties::GetControlInput(induction_var->phi());
331 Node* backedge_control = loop->InputAt(1);
332 Node* backedge_effect =
333 NodeProperties::GetEffectInput(induction_var->effect_phi(), 1);
334 Node* rename =
335 graph()->NewNode(common()->TypeGuard(phi_type), backedge_value,
336 backedge_effect, backedge_control);
337 induction_var->effect_phi()->ReplaceInput(1, rename);
338 induction_var->phi()->ReplaceInput(1, rename);
339 }
340 }
341 }
342 }
343
344 #undef TRACE
345
346 } // namespace compiler
347 } // namespace internal
348 } // namespace v8
349