v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
loop-variable-optimizer.cc
Go to the documentation of this file.
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
6
10#include "src/compiler/node.h"
13#include "src/zone/zone.h"
14
15namespace v8 {
16namespace internal {
17namespace compiler {
18
19// Macro for outputting trace information from representation inference.
20#define TRACE(...) \
21 do { \
22 if (v8_flags.trace_turbo_loop) PrintF(__VA_ARGS__); \
23 } while (false)
24
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
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)
48 : node->op()->ControlInputCount();
49 for (int i = 0; i < inputs_end; 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()) {
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
79 if (v8_flags.trace_turbo_loop) {
80 StdoutStream{} << "New upper bound for " << phi()->id() << " (loop "
82 << "): " << *bound << std::endl;
83 }
84 upper_bounds_.push_back(Bound(bound, kind));
85}
86
89 if (v8_flags.trace_turbo_loop) {
90 StdoutStream{} << "New lower bound for " << phi()->id() << " (loop "
92 << "): " << *bound;
93 }
94 lower_bounds_.push_back(Bound(bound, kind));
95}
96
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
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
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
150 // Conservatively take the limits from the loop entry here.
152}
153
154void 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::kNumberLessThan:
162 case IrOpcode::kSpeculativeNumberLessThan:
163 AddCmpToLimits(&limits, cond, InductionVariable::kStrict, polarity);
164 break;
165 case IrOpcode::kJSGreaterThan:
166 AddCmpToLimits(&limits, cond, InductionVariable::kNonStrict, !polarity);
167 break;
168 case IrOpcode::kJSLessThanOrEqual:
169 case IrOpcode::kNumberLessThanOrEqual:
170 case IrOpcode::kSpeculativeNumberLessThanOrEqual:
171 AddCmpToLimits(&limits, cond, InductionVariable::kNonStrict, polarity);
172 break;
173 case IrOpcode::kJSGreaterThanOrEqual:
174 AddCmpToLimits(&limits, cond, InductionVariable::kStrict, !polarity);
175 break;
176 default:
177 break;
178 }
179 limits_.Set(node, limits);
180}
181
184 bool polarity) {
185 Node* left = node->InputAt(0);
186 Node* right = node->InputAt(1);
187 if (FindInductionVariable(left) || FindInductionVariable(right)) {
188 if (polarity) {
189 limits->PushFront(Constraint{left, kind, right}, zone());
190 } else {
194 limits->PushFront(Constraint{right, kind, left}, zone());
195 }
196 }
197}
198
199void LoopVariableOptimizer::VisitStart(Node* node) { limits_.Set(node, {}); }
200
204
206 DCHECK_EQ(1, node->op()->ControlInputCount());
208}
209
213
215 Node* node) {
216 auto var = induction_vars_.find(node->id());
217 if (var != induction_vars_.end()) {
218 return var->second;
219 }
220 return nullptr;
221}
222
224 DCHECK_EQ(2, phi->op()->ValueInputCount());
226 DCHECK_EQ(IrOpcode::kLoop, loop->opcode());
227 Node* initial = phi->InputAt(0);
228 Node* arith = phi->InputAt(1);
230 if (arith->opcode() == IrOpcode::kJSAdd ||
231 arith->opcode() == IrOpcode::kNumberAdd ||
232 arith->opcode() == IrOpcode::kSpeculativeNumberAdd ||
233 arith->opcode() == IrOpcode::kSpeculativeAdditiveSafeIntegerAdd ||
234 arith->opcode() == IrOpcode::kSpeculativeAdditiveSafeIntegerSubtract ||
235 arith->opcode() == IrOpcode::kSpeculativeSmallIntegerAdd) {
237 } else if (arith->opcode() == IrOpcode::kJSSubtract ||
238 arith->opcode() == IrOpcode::kNumberSubtract ||
239 arith->opcode() == IrOpcode::kSpeculativeNumberSubtract ||
240 arith->opcode() == IrOpcode::kSpeculativeSmallIntegerSubtract) {
242 } else {
243 return nullptr;
244 }
245
246 // We allow a few additional conversions on the lhs of the arithmetic
247 // operation. This needs to be kept in sync with the corresponding code in
248 // {Typer::Visitor::InductionVariablePhiTypeIsPrefixedPoint}.
249 // TODO(jarin) Support both sides.
250 Node* input = arith->InputAt(0);
251 if (input->opcode() == IrOpcode::kSpeculativeToNumber ||
252 input->opcode() == IrOpcode::kJSToNumber ||
253 input->opcode() == IrOpcode::kJSToNumberConvertBigInt) {
254 input = input->InputAt(0);
255 }
256 if (input != phi) return nullptr;
257
258 Node* effect_phi = nullptr;
259 for (Node* use : loop->uses()) {
260 if (use->opcode() == IrOpcode::kEffectPhi) {
261 DCHECK_NULL(effect_phi);
262 effect_phi = use;
263 }
264 }
265 if (!effect_phi) return nullptr;
266
267 Node* incr = arith->InputAt(1);
268 return zone()->New<InductionVariable>(phi, effect_phi, arith, incr, initial,
269 zone(), arithmeticType);
270}
271
273 if (loop->op()->ControlInputCount() != 2) return;
274 TRACE("Loop variables for loop %i:", loop->id());
275 for (Edge edge : loop->use_edges()) {
277 edge.from()->opcode() == IrOpcode::kPhi) {
278 Node* phi = edge.from();
279 InductionVariable* induction_var = TryGetInductionVariable(phi);
280 if (induction_var) {
281 induction_vars_[phi->id()] = induction_var;
282 TRACE(" %i", induction_var->phi()->id());
283 }
284 }
285 }
286 TRACE("\n");
287}
288
290 for (auto entry : induction_vars_) {
291 // It only make sense to analyze the induction variables if
292 // there is a bound.
293 InductionVariable* induction_var = entry.second;
295 PhiRepresentationOf(induction_var->phi()->op()));
296 if (induction_var->upper_bounds().empty() &&
297 induction_var->lower_bounds().empty()) {
298 continue;
299 }
300 // Insert the increment value to the value inputs.
301 induction_var->phi()->InsertInput(graph()->zone(),
302 induction_var->phi()->InputCount() - 1,
303 induction_var->increment());
304 // Insert the bound inputs to the value inputs.
305 for (auto bound : induction_var->lower_bounds()) {
306 induction_var->phi()->InsertInput(
307 graph()->zone(), induction_var->phi()->InputCount() - 1, bound.bound);
308 }
309 for (auto bound : induction_var->upper_bounds()) {
310 induction_var->phi()->InsertInput(
311 graph()->zone(), induction_var->phi()->InputCount() - 1, bound.bound);
312 }
314 induction_var->phi(),
315 common()->InductionVariablePhi(induction_var->phi()->InputCount() - 1));
316 }
317}
318
320 for (auto entry : induction_vars_) {
321 InductionVariable* induction_var = entry.second;
322 if (induction_var->phi()->opcode() == IrOpcode::kInductionVariablePhi) {
323 // Turn the induction variable phi back to normal phi.
324 int value_count = 2;
325 Node* control = NodeProperties::GetControlInput(induction_var->phi());
326 DCHECK_EQ(value_count, control->op()->ControlInputCount());
327 induction_var->phi()->TrimInputCount(value_count + 1);
328 induction_var->phi()->ReplaceInput(value_count, control);
330 induction_var->phi(),
331 common()->Phi(MachineRepresentation::kTagged, value_count));
332
333 // If the backedge is not a subtype of the phi's type, we insert a sigma
334 // to get the typing right.
335 Node* backedge_value = induction_var->phi()->InputAt(1);
336 Type backedge_type = NodeProperties::GetType(backedge_value);
337 Type phi_type = NodeProperties::GetType(induction_var->phi());
338 if (!backedge_type.Is(phi_type)) {
339 Node* loop = NodeProperties::GetControlInput(induction_var->phi());
340 Node* backedge_control = loop->InputAt(1);
341 Node* backedge_effect =
342 NodeProperties::GetEffectInput(induction_var->effect_phi(), 1);
343 Node* rename =
344 graph()->NewNode(common()->TypeGuard(phi_type), backedge_value,
345 backedge_effect, backedge_control);
346 induction_var->effect_phi()->ReplaceInput(1, rename);
347 induction_var->phi()->ReplaceInput(1, rename);
348 }
349 }
350 }
351}
352
353#undef TRACE
354
355} // namespace compiler
356} // namespace internal
357} // namespace v8
Builtins::Kind kind
Definition builtins.cc:40
T * New(Args &&... args)
Definition zone.h:114
void ResetToCommonAncestor(FunctionalList other)
void AddLowerBound(Node *bound, ConstraintKind kind)
void AddUpperBound(Node *bound, ConstraintKind kind)
const InductionVariable * FindInductionVariable(Node *node)
void AddCmpToLimits(VariableLimits *limits, Node *node, InductionVariable::ConstraintKind kind, bool polarity)
ZoneMap< int, InductionVariable * > induction_vars_
LoopVariableOptimizer(TFGraph *graph, CommonOperatorBuilder *common, Zone *zone)
bool Set(Node *node, T const &data)
V8_INLINE void Set(Node *node, State state)
Definition node-marker.h:69
V8_INLINE State Get(const Node *node)
Definition node-marker.h:65
static void ChangeOp(Node *node, const Operator *new_op)
static Type GetType(const Node *node)
static Node * GetEffectInput(Node *node, int index=0)
static Node * GetControlInput(Node *node, int index=0)
constexpr IrOpcode::Value opcode() const
Definition node.h:52
void TrimInputCount(int new_input_count)
Definition node.cc:262
const Operator * op() const
Definition node.h:50
int InputCount() const
Definition node.h:59
void ReplaceInput(int index, Node *new_to)
Definition node.h:76
Node * InputAt(int index) const
Definition node.h:70
NodeId id() const
Definition node.h:57
void InsertInput(Zone *zone, int index, Node *new_to)
Definition node.cc:203
Node * NewNode(const Operator *op, int input_count, Node *const *inputs, bool incomplete=false)
bool Is(Type that) const
Zone * zone_
int start
#define TRACE(...)
MachineRepresentation PhiRepresentationOf(const Operator *const op)
V8_EXPORT_PRIVATE FlagValues v8_flags
other heap size generate builtins concurrently on separate threads in mksnapshot track concurrent recompilation artificial compilation delay in ms max number of threads that concurrent Turbofan can use(0 for unbounded)") DEFINE_BOOL( stress_concurrent_inlining
#define DCHECK_NULL(val)
Definition logging.h:491
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
TFGraph * graph_