Line data Source code
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 :
25 463896 : 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 927790 : induction_vars_(zone) {}
34 :
35 463892 : void LoopVariableOptimizer::Run() {
36 463892 : ZoneQueue<Node*> queue(zone());
37 927794 : queue.push(graph()->start());
38 : NodeMarker<bool> queued(graph(), 2);
39 8142925 : while (!queue.empty()) {
40 7679028 : 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 7679019 : : node->op()->ControlInputCount();
49 28786641 : for (int i = 0; i < inputs_end; i++) {
50 22122884 : if (!reduced_.Get(NodeProperties::GetControlInput(node, i))) {
51 : all_inputs_visited = false;
52 : break;
53 : }
54 : }
55 7679025 : if (!all_inputs_visited) continue;
56 :
57 7171380 : VisitNode(node);
58 7171426 : reduced_.Set(node, true);
59 :
60 : // Queue control outputs.
61 80917840 : for (Edge edge : node->use_edges()) {
62 52710187 : if (NodeProperties::IsControlEdge(edge) &&
63 : edge.from()->op()->ControlOutputCount() > 0) {
64 7415743 : Node* use = edge.from();
65 7500473 : if (use->opcode() == IrOpcode::kLoop &&
66 84730 : edge.index() != kAssumedLoopEntryIndex) {
67 42365 : VisitBackedge(node, use);
68 7373378 : } else if (!queued.Get(use)) {
69 : queue.push(use);
70 7215184 : queued.Set(use, true);
71 : }
72 : }
73 : }
74 : }
75 463896 : }
76 :
77 11794 : void InductionVariable::AddUpperBound(Node* bound,
78 : InductionVariable::ConstraintKind kind) {
79 11794 : if (FLAG_trace_turbo_loop) {
80 0 : StdoutStream{} << "New upper bound for " << phi()->id() << " (loop "
81 0 : << NodeProperties::GetControlInput(phi())->id()
82 0 : << "): " << *bound << std::endl;
83 : }
84 23588 : upper_bounds_.push_back(Bound(bound, kind));
85 11794 : }
86 :
87 527 : void InductionVariable::AddLowerBound(Node* bound,
88 : InductionVariable::ConstraintKind kind) {
89 527 : if (FLAG_trace_turbo_loop) {
90 0 : StdoutStream{} << "New lower bound for " << phi()->id() << " (loop "
91 0 : << NodeProperties::GetControlInput(phi())->id()
92 0 : << "): " << *bound;
93 : }
94 1054 : lower_bounds_.push_back(Bound(bound, kind));
95 527 : }
96 :
97 42365 : void LoopVariableOptimizer::VisitBackedge(Node* from, Node* loop) {
98 42365 : 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 59312 : for (Constraint constraint : limits_.Get(from)) {
103 31874 : if (constraint.left->opcode() == IrOpcode::kPhi &&
104 14927 : NodeProperties::GetControlInput(constraint.left) == loop) {
105 11794 : auto var = induction_vars_.find(constraint.left->id());
106 11794 : if (var != induction_vars_.end()) {
107 11794 : var->second->AddUpperBound(constraint.right, constraint.kind);
108 : }
109 : }
110 19055 : if (constraint.right->opcode() == IrOpcode::kPhi &&
111 2108 : NodeProperties::GetControlInput(constraint.right) == loop) {
112 527 : auto var = induction_vars_.find(constraint.right->id());
113 527 : if (var != induction_vars_.end()) {
114 527 : var->second->AddLowerBound(constraint.left, constraint.kind);
115 : }
116 : }
117 : }
118 : }
119 :
120 7171473 : void LoopVariableOptimizer::VisitNode(Node* node) {
121 7171473 : switch (node->opcode()) {
122 : case IrOpcode::kMerge:
123 266374 : return VisitMerge(node);
124 : case IrOpcode::kLoop:
125 : return VisitLoop(node);
126 : case IrOpcode::kIfFalse:
127 623346 : return VisitIf(node, false);
128 : case IrOpcode::kIfTrue:
129 623353 : 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 :
139 266380 : void LoopVariableOptimizer::VisitMerge(Node* node) {
140 : // Merge the limits of all incoming edges.
141 266380 : VariableLimits merged = limits_.Get(node->InputAt(0));
142 1254346 : for (int i = 1; i < node->InputCount(); i++) {
143 493982 : merged.ResetToCommonAncestor(limits_.Get(node->InputAt(i)));
144 : }
145 266381 : limits_.Set(node, merged);
146 266379 : }
147 :
148 0 : void LoopVariableOptimizer::VisitLoop(Node* node) {
149 42364 : DetectInductionVariables(node);
150 : // Conservatively take the limits from the loop entry here.
151 42364 : return TakeConditionsFromFirstControl(node);
152 : }
153 :
154 1246685 : void LoopVariableOptimizer::VisitIf(Node* node, bool polarity) {
155 : Node* branch = node->InputAt(0);
156 : Node* cond = branch->InputAt(0);
157 1246685 : VariableLimits limits = limits_.Get(branch);
158 : // Normalize to less than comparison.
159 1246685 : switch (cond->opcode()) {
160 : case IrOpcode::kJSLessThan:
161 : case IrOpcode::kNumberLessThan:
162 : case IrOpcode::kSpeculativeNumberLessThan:
163 125147 : AddCmpToLimits(&limits, cond, InductionVariable::kStrict, polarity);
164 125151 : break;
165 : case IrOpcode::kJSGreaterThan:
166 25340 : AddCmpToLimits(&limits, cond, InductionVariable::kNonStrict, !polarity);
167 25340 : break;
168 : case IrOpcode::kJSLessThanOrEqual:
169 : case IrOpcode::kNumberLessThanOrEqual:
170 : case IrOpcode::kSpeculativeNumberLessThanOrEqual:
171 5506 : AddCmpToLimits(&limits, cond, InductionVariable::kNonStrict, polarity);
172 5506 : break;
173 : case IrOpcode::kJSGreaterThanOrEqual:
174 2946 : AddCmpToLimits(&limits, cond, InductionVariable::kStrict, !polarity);
175 2946 : break;
176 : default:
177 : break;
178 : }
179 1246689 : limits_.Set(node, limits);
180 1246690 : }
181 :
182 158947 : void LoopVariableOptimizer::AddCmpToLimits(
183 : VariableLimits* limits, Node* node, InductionVariable::ConstraintKind kind,
184 : bool polarity) {
185 : Node* left = node->InputAt(0);
186 : Node* right = node->InputAt(1);
187 237135 : if (FindInductionVariable(left) || FindInductionVariable(right)) {
188 81983 : if (polarity) {
189 40994 : limits->PushFront(Constraint{left, kind, right}, zone());
190 : } else {
191 : kind = (kind == InductionVariable::kStrict)
192 : ? InductionVariable::kNonStrict
193 40989 : : InductionVariable::kStrict;
194 40989 : limits->PushFront(Constraint{right, kind, left}, zone());
195 : }
196 : }
197 158945 : }
198 :
199 463895 : void LoopVariableOptimizer::VisitStart(Node* node) { limits_.Set(node, {}); }
200 :
201 0 : void LoopVariableOptimizer::VisitLoopExit(Node* node) {
202 171895 : return TakeConditionsFromFirstControl(node);
203 : }
204 :
205 0 : void LoopVariableOptimizer::VisitOtherControl(Node* node) {
206 : DCHECK_EQ(1, node->op()->ControlInputCount());
207 4980246 : return TakeConditionsFromFirstControl(node);
208 : }
209 :
210 5194498 : void LoopVariableOptimizer::TakeConditionsFromFirstControl(Node* node) {
211 10389013 : limits_.Set(node, limits_.Get(NodeProperties::GetControlInput(node, 0)));
212 5194506 : }
213 :
214 0 : const InductionVariable* LoopVariableOptimizer::FindInductionVariable(
215 : Node* node) {
216 237135 : auto var = induction_vars_.find(node->id());
217 237135 : if (var != induction_vars_.end()) {
218 81976 : return var->second;
219 : }
220 : return nullptr;
221 : }
222 :
223 67685 : InductionVariable* LoopVariableOptimizer::TryGetInductionVariable(Node* phi) {
224 : DCHECK_EQ(2, phi->op()->ValueInputCount());
225 67685 : Node* loop = NodeProperties::GetControlInput(phi);
226 : DCHECK_EQ(IrOpcode::kLoop, loop->opcode());
227 : Node* initial = phi->InputAt(0);
228 : Node* arith = phi->InputAt(1);
229 : InductionVariable::ArithmeticType arithmeticType;
230 133459 : if (arith->opcode() == IrOpcode::kJSAdd ||
231 63729 : arith->opcode() == IrOpcode::kNumberAdd ||
232 130829 : arith->opcode() == IrOpcode::kSpeculativeNumberAdd ||
233 : arith->opcode() == IrOpcode::kSpeculativeSafeIntegerAdd) {
234 : arithmeticType = InductionVariable::ArithmeticType::kAddition;
235 96908 : } else if (arith->opcode() == IrOpcode::kJSSubtract ||
236 48123 : arith->opcode() == IrOpcode::kNumberSubtract ||
237 96592 : arith->opcode() == IrOpcode::kSpeculativeNumberSubtract ||
238 : arith->opcode() == IrOpcode::kSpeculativeSafeIntegerSubtract) {
239 : arithmeticType = InductionVariable::ArithmeticType::kSubtraction;
240 : } else {
241 : return nullptr;
242 : }
243 :
244 : // TODO(jarin) Support both sides.
245 : Node* input = arith->InputAt(0);
246 40716 : if (input->opcode() == IrOpcode::kSpeculativeToNumber ||
247 40716 : input->opcode() == IrOpcode::kJSToNumber ||
248 : input->opcode() == IrOpcode::kJSToNumberConvertBigInt) {
249 : input = input->InputAt(0);
250 : }
251 21065 : if (input != phi) return nullptr;
252 :
253 : Node* effect_phi = nullptr;
254 182559 : for (Node* use : loop->uses()) {
255 164775 : if (use->opcode() == IrOpcode::kEffectPhi) {
256 : DCHECK_NULL(effect_phi);
257 : effect_phi = use;
258 : }
259 : }
260 17784 : if (!effect_phi) return nullptr;
261 :
262 : Node* incr = arith->InputAt(1);
263 : return new (zone()) InductionVariable(phi, effect_phi, arith, incr, initial,
264 17784 : zone(), arithmeticType);
265 : }
266 :
267 42364 : void LoopVariableOptimizer::DetectInductionVariables(Node* loop) {
268 42364 : if (loop->op()->ControlInputCount() != 2) return;
269 42364 : TRACE("Loop variables for loop %i:", loop->id());
270 919608 : for (Edge edge : loop->use_edges()) {
271 877238 : if (NodeProperties::IsControlEdge(edge) &&
272 : edge.from()->opcode() == IrOpcode::kPhi) {
273 : Node* phi = edge.from();
274 67685 : InductionVariable* induction_var = TryGetInductionVariable(phi);
275 67690 : if (induction_var) {
276 17784 : induction_vars_[phi->id()] = induction_var;
277 17784 : TRACE(" %i", induction_var->phi()->id());
278 : }
279 : }
280 : }
281 42364 : TRACE("\n");
282 : }
283 :
284 463890 : void LoopVariableOptimizer::ChangeToInductionVariablePhis() {
285 481676 : for (auto entry : induction_vars_) {
286 : // It only make sense to analyze the induction variables if
287 : // there is a bound.
288 : InductionVariable* induction_var = entry.second;
289 : DCHECK_EQ(MachineRepresentation::kTagged,
290 : PhiRepresentationOf(induction_var->phi()->op()));
291 23836 : if (induction_var->upper_bounds().size() == 0 &&
292 : induction_var->lower_bounds().size() == 0) {
293 : continue;
294 : }
295 : // Insert the increment value to the value inputs.
296 12179 : induction_var->phi()->InsertInput(graph()->zone(),
297 : induction_var->phi()->InputCount() - 1,
298 12179 : induction_var->increment());
299 : // Insert the bound inputs to the value inputs.
300 12706 : for (auto bound : induction_var->lower_bounds()) {
301 527 : induction_var->phi()->InsertInput(
302 527 : graph()->zone(), induction_var->phi()->InputCount() - 1, bound.bound);
303 : }
304 23973 : for (auto bound : induction_var->upper_bounds()) {
305 11794 : induction_var->phi()->InsertInput(
306 11794 : graph()->zone(), induction_var->phi()->InputCount() - 1, bound.bound);
307 : }
308 12179 : NodeProperties::ChangeOp(
309 : induction_var->phi(),
310 12179 : common()->InductionVariablePhi(induction_var->phi()->InputCount() - 1));
311 : }
312 463892 : }
313 :
314 463896 : void LoopVariableOptimizer::ChangeToPhisAndInsertGuards() {
315 481676 : for (auto entry : induction_vars_) {
316 : InductionVariable* induction_var = entry.second;
317 17784 : if (induction_var->phi()->opcode() == IrOpcode::kInductionVariablePhi) {
318 : // Turn the induction variable phi back to normal phi.
319 : int value_count = 2;
320 12179 : Node* control = NodeProperties::GetControlInput(induction_var->phi());
321 : DCHECK_EQ(value_count, control->op()->ControlInputCount());
322 12179 : induction_var->phi()->TrimInputCount(value_count + 1);
323 12179 : induction_var->phi()->ReplaceInput(value_count, control);
324 12179 : NodeProperties::ChangeOp(
325 : induction_var->phi(),
326 12179 : common()->Phi(MachineRepresentation::kTagged, value_count));
327 :
328 : // If the backedge is not a subtype of the phi's type, we insert a sigma
329 : // to get the typing right.
330 : Node* backedge_value = induction_var->phi()->InputAt(1);
331 12179 : Type backedge_type = NodeProperties::GetType(backedge_value);
332 : Type phi_type = NodeProperties::GetType(induction_var->phi());
333 12179 : if (!backedge_type.Is(phi_type)) {
334 7706 : Node* loop = NodeProperties::GetControlInput(induction_var->phi());
335 : Node* backedge_control = loop->InputAt(1);
336 : Node* backedge_effect =
337 7706 : NodeProperties::GetEffectInput(induction_var->effect_phi(), 1);
338 : Node* rename =
339 7706 : graph()->NewNode(common()->TypeGuard(phi_type), backedge_value,
340 : backedge_effect, backedge_control);
341 7706 : induction_var->effect_phi()->ReplaceInput(1, rename);
342 7706 : induction_var->phi()->ReplaceInput(1, rename);
343 : }
344 : }
345 : }
346 463892 : }
347 :
348 : #undef TRACE
349 :
350 : } // namespace compiler
351 : } // namespace internal
352 122036 : } // namespace v8
|