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 463833 : 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 927672 : induction_vars_(zone) {}
34 :
35 463826 : void LoopVariableOptimizer::Run() {
36 463826 : ZoneQueue<Node*> queue(zone());
37 927665 : queue.push(graph()->start());
38 : NodeMarker<bool> queued(graph(), 2);
39 8348332 : while (!queue.empty()) {
40 7884492 : 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 7884493 : : node->op()->ControlInputCount();
49 29244743 : for (int i = 0; i < inputs_end; i++) {
50 22352408 : if (!reduced_.Get(NodeProperties::GetControlInput(node, i))) {
51 : all_inputs_visited = false;
52 : break;
53 : }
54 : }
55 7884503 : if (!all_inputs_visited) continue;
56 :
57 7388413 : VisitNode(node);
58 7388417 : reduced_.Set(node, true);
59 :
60 : // Queue control outputs.
61 83771565 : for (Edge edge : node->use_edges()) {
62 54553771 : if (NodeProperties::IsControlEdge(edge) &&
63 : edge.from()->op()->ControlOutputCount() > 0) {
64 7620710 : Node* use = edge.from();
65 7704998 : if (use->opcode() == IrOpcode::kLoop &&
66 84288 : edge.index() != kAssumedLoopEntryIndex) {
67 42144 : VisitBackedge(node, use);
68 7578566 : } else if (!queued.Get(use)) {
69 : queue.push(use);
70 7420683 : queued.Set(use, true);
71 : }
72 : }
73 : }
74 : }
75 463843 : }
76 :
77 11875 : void InductionVariable::AddUpperBound(Node* bound,
78 : InductionVariable::ConstraintKind kind) {
79 11875 : 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 23750 : upper_bounds_.push_back(Bound(bound, kind));
85 11875 : }
86 :
87 528 : void InductionVariable::AddLowerBound(Node* bound,
88 : InductionVariable::ConstraintKind kind) {
89 528 : 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 1056 : lower_bounds_.push_back(Bound(bound, kind));
95 528 : }
96 :
97 42144 : void LoopVariableOptimizer::VisitBackedge(Node* from, Node* loop) {
98 42144 : 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 59310 : for (Constraint constraint : limits_.Get(from)) {
103 32285 : if (constraint.left->opcode() == IrOpcode::kPhi &&
104 15119 : NodeProperties::GetControlInput(constraint.left) == loop) {
105 11875 : auto var = induction_vars_.find(constraint.left->id());
106 11875 : if (var != induction_vars_.end()) {
107 11875 : var->second->AddUpperBound(constraint.right, constraint.kind);
108 : }
109 : }
110 19324 : if (constraint.right->opcode() == IrOpcode::kPhi &&
111 2158 : NodeProperties::GetControlInput(constraint.right) == loop) {
112 528 : auto var = induction_vars_.find(constraint.right->id());
113 528 : if (var != induction_vars_.end()) {
114 528 : var->second->AddLowerBound(constraint.left, constraint.kind);
115 : }
116 : }
117 : }
118 : }
119 :
120 7388442 : void LoopVariableOptimizer::VisitNode(Node* node) {
121 7388442 : switch (node->opcode()) {
122 : case IrOpcode::kMerge:
123 263843 : return VisitMerge(node);
124 : case IrOpcode::kLoop:
125 : return VisitLoop(node);
126 : case IrOpcode::kIfFalse:
127 620363 : return VisitIf(node, false);
128 : case IrOpcode::kIfTrue:
129 620364 : 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 263846 : void LoopVariableOptimizer::VisitMerge(Node* node) {
140 : // Merge the limits of all incoming edges.
141 263846 : VariableLimits merged = limits_.Get(node->InputAt(0));
142 1237682 : for (int i = 1; i < node->InputCount(); i++) {
143 486919 : merged.ResetToCommonAncestor(limits_.Get(node->InputAt(i)));
144 : }
145 263845 : limits_.Set(node, merged);
146 263845 : }
147 :
148 0 : void LoopVariableOptimizer::VisitLoop(Node* node) {
149 42144 : DetectInductionVariables(node);
150 : // Conservatively take the limits from the loop entry here.
151 42144 : return TakeConditionsFromFirstControl(node);
152 : }
153 :
154 1240723 : void LoopVariableOptimizer::VisitIf(Node* node, bool polarity) {
155 : Node* branch = node->InputAt(0);
156 : Node* cond = branch->InputAt(0);
157 1240723 : VariableLimits limits = limits_.Get(branch);
158 : // Normalize to less than comparison.
159 1240723 : switch (cond->opcode()) {
160 : case IrOpcode::kJSLessThan:
161 : case IrOpcode::kNumberLessThan:
162 : case IrOpcode::kSpeculativeNumberLessThan:
163 125716 : AddCmpToLimits(&limits, cond, InductionVariable::kStrict, polarity);
164 125718 : break;
165 : case IrOpcode::kJSGreaterThan:
166 24632 : AddCmpToLimits(&limits, cond, InductionVariable::kNonStrict, !polarity);
167 24632 : break;
168 : case IrOpcode::kJSLessThanOrEqual:
169 : case IrOpcode::kNumberLessThanOrEqual:
170 : case IrOpcode::kSpeculativeNumberLessThanOrEqual:
171 5890 : AddCmpToLimits(&limits, cond, InductionVariable::kNonStrict, polarity);
172 5890 : break;
173 : case IrOpcode::kJSGreaterThanOrEqual:
174 2938 : AddCmpToLimits(&limits, cond, InductionVariable::kStrict, !polarity);
175 2938 : break;
176 : default:
177 : break;
178 : }
179 1240725 : limits_.Set(node, limits);
180 1240725 : }
181 :
182 159180 : 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 237270 : if (FindInductionVariable(left) || FindInductionVariable(right)) {
188 82344 : if (polarity) {
189 41173 : limits->PushFront(Constraint{left, kind, right}, zone());
190 : } else {
191 : kind = (kind == InductionVariable::kStrict)
192 : ? InductionVariable::kNonStrict
193 41171 : : InductionVariable::kStrict;
194 41171 : limits->PushFront(Constraint{right, kind, left}, zone());
195 : }
196 : }
197 159179 : }
198 :
199 463824 : void LoopVariableOptimizer::VisitStart(Node* node) { limits_.Set(node, {}); }
200 :
201 0 : void LoopVariableOptimizer::VisitLoopExit(Node* node) {
202 167075 : return TakeConditionsFromFirstControl(node);
203 : }
204 :
205 0 : void LoopVariableOptimizer::VisitOtherControl(Node* node) {
206 : DCHECK_EQ(1, node->op()->ControlInputCount());
207 5210829 : return TakeConditionsFromFirstControl(node);
208 : }
209 :
210 5420036 : void LoopVariableOptimizer::TakeConditionsFromFirstControl(Node* node) {
211 10840095 : limits_.Set(node, limits_.Get(NodeProperties::GetControlInput(node, 0)));
212 5420052 : }
213 :
214 0 : const InductionVariable* LoopVariableOptimizer::FindInductionVariable(
215 : Node* node) {
216 237270 : auto var = induction_vars_.find(node->id());
217 237270 : if (var != induction_vars_.end()) {
218 82341 : return var->second;
219 : }
220 : return nullptr;
221 : }
222 :
223 67758 : InductionVariable* LoopVariableOptimizer::TryGetInductionVariable(Node* phi) {
224 : DCHECK_EQ(2, phi->op()->ValueInputCount());
225 67758 : 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 133537 : if (arith->opcode() == IrOpcode::kJSAdd ||
231 63843 : arith->opcode() == IrOpcode::kNumberAdd ||
232 130937 : arith->opcode() == IrOpcode::kSpeculativeNumberAdd ||
233 : arith->opcode() == IrOpcode::kSpeculativeSafeIntegerAdd) {
234 : arithmeticType = InductionVariable::ArithmeticType::kAddition;
235 97010 : } else if (arith->opcode() == IrOpcode::kJSSubtract ||
236 48190 : arith->opcode() == IrOpcode::kNumberSubtract ||
237 96710 : 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 40724 : if (input->opcode() == IrOpcode::kSpeculativeToNumber ||
247 40724 : input->opcode() == IrOpcode::kJSToNumber ||
248 : input->opcode() == IrOpcode::kJSToNumberConvertBigInt) {
249 : input = input->InputAt(0);
250 : }
251 21069 : if (input != phi) return nullptr;
252 :
253 : Node* effect_phi = nullptr;
254 183394 : for (Node* use : loop->uses()) {
255 165527 : if (use->opcode() == IrOpcode::kEffectPhi) {
256 : DCHECK_NULL(effect_phi);
257 : effect_phi = use;
258 : }
259 : }
260 17867 : if (!effect_phi) return nullptr;
261 :
262 : Node* incr = arith->InputAt(1);
263 : return new (zone()) InductionVariable(phi, effect_phi, arith, incr, initial,
264 17867 : zone(), arithmeticType);
265 : }
266 :
267 42144 : void LoopVariableOptimizer::DetectInductionVariables(Node* loop) {
268 42144 : if (loop->op()->ControlInputCount() != 2) return;
269 42144 : TRACE("Loop variables for loop %i:", loop->id());
270 909658 : for (Edge edge : loop->use_edges()) {
271 867514 : if (NodeProperties::IsControlEdge(edge) &&
272 : edge.from()->opcode() == IrOpcode::kPhi) {
273 : Node* phi = edge.from();
274 67758 : InductionVariable* induction_var = TryGetInductionVariable(phi);
275 67758 : if (induction_var) {
276 17867 : induction_vars_[phi->id()] = induction_var;
277 17867 : TRACE(" %i", induction_var->phi()->id());
278 : }
279 : }
280 : }
281 42144 : TRACE("\n");
282 : }
283 :
284 463815 : void LoopVariableOptimizer::ChangeToInductionVariablePhis() {
285 481688 : 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 23925 : 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 12257 : induction_var->phi()->InsertInput(graph()->zone(),
297 : induction_var->phi()->InputCount() - 1,
298 12257 : induction_var->increment());
299 : // Insert the bound inputs to the value inputs.
300 12785 : for (auto bound : induction_var->lower_bounds()) {
301 528 : induction_var->phi()->InsertInput(
302 528 : graph()->zone(), induction_var->phi()->InputCount() - 1, bound.bound);
303 : }
304 24132 : for (auto bound : induction_var->upper_bounds()) {
305 11875 : induction_var->phi()->InsertInput(
306 11875 : graph()->zone(), induction_var->phi()->InputCount() - 1, bound.bound);
307 : }
308 12257 : NodeProperties::ChangeOp(
309 : induction_var->phi(),
310 12257 : common()->InductionVariablePhi(induction_var->phi()->InputCount() - 1));
311 : }
312 463821 : }
313 :
314 463830 : void LoopVariableOptimizer::ChangeToPhisAndInsertGuards() {
315 481690 : for (auto entry : induction_vars_) {
316 : InductionVariable* induction_var = entry.second;
317 17867 : if (induction_var->phi()->opcode() == IrOpcode::kInductionVariablePhi) {
318 : // Turn the induction variable phi back to normal phi.
319 : int value_count = 2;
320 12257 : Node* control = NodeProperties::GetControlInput(induction_var->phi());
321 : DCHECK_EQ(value_count, control->op()->ControlInputCount());
322 12257 : induction_var->phi()->TrimInputCount(value_count + 1);
323 12257 : induction_var->phi()->ReplaceInput(value_count, control);
324 12257 : NodeProperties::ChangeOp(
325 : induction_var->phi(),
326 12257 : 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 12257 : Type backedge_type = NodeProperties::GetType(backedge_value);
332 : Type phi_type = NodeProperties::GetType(induction_var->phi());
333 12257 : if (!backedge_type.Is(phi_type)) {
334 7727 : Node* loop = NodeProperties::GetControlInput(induction_var->phi());
335 : Node* backedge_control = loop->InputAt(1);
336 : Node* backedge_effect =
337 7727 : NodeProperties::GetEffectInput(induction_var->effect_phi(), 1);
338 : Node* rename =
339 7727 : graph()->NewNode(common()->TypeGuard(phi_type), backedge_value,
340 : backedge_effect, backedge_control);
341 7727 : induction_var->effect_phi()->ReplaceInput(1, rename);
342 7727 : induction_var->phi()->ReplaceInput(1, rename);
343 : }
344 : }
345 : }
346 463823 : }
347 :
348 : #undef TRACE
349 :
350 : } // namespace compiler
351 : } // namespace internal
352 120216 : } // namespace v8
|