v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
analyzer-iterator.h
Go to the documentation of this file.
1// Copyright 2023 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#ifndef V8_COMPILER_TURBOSHAFT_ANALYZER_ITERATOR_H_
6#define V8_COMPILER_TURBOSHAFT_ANALYZER_ITERATOR_H_
7
8#include "src/base/logging.h"
14
16
17// AnalyzerIterator provides methods to iterate forward a Graph in a way that is
18// efficient for the SnapshotTable: blocks that are close in the graphs will be
19// visited somewhat consecutively (which means that the SnapshotTable shouldn't
20// have to travel far).
21//
22// To understand why this is important, consider the following graph:
23//
24// B1 <------
25// |\ |
26// | \ |
27// | v |
28// | B27---
29// v
30// B2 <------
31// |\ |
32// | \ |
33// | v |
34// | B26---
35// v
36// B3 <------
37// |\ |
38// | \ |
39// | v |
40// | B25---
41// v
42// ...
43//
44// If we iterate its blocks in increasing ID order, then we'll visit B1, B2,
45// B3... and only afterwards will we visit the Backedges. If said backedges can
46// update the loop headers snapshots, then when visiting B25, we'll decide to
47// revisit starting from B3, and will revisit everything after, then same thing
48// for B26 after which we'll start over from B2 (and thus even revisit B3 and
49// B25), etc, leading to a quadratic (in the number of blocks) analysis.
50//
51// Instead, the visitation order offered by AnalyzerIterator is a BFS in the
52// dominator tree (ie, after visiting a node, AnalyzerIterator visit the nodes
53// it dominates), with an subtlety for loops: when a node dominates multiple
54// nodes, successors that are in the same loop as the current node are visited
55// before nodes that are in outer loops.
56// In the example above, the visitation order would thus be B1, B27, B2, B26,
57// B3, B25.
58//
59// The MarkLoopForRevisit method can be used when visiting a backedge to
60// instruct AnalyzerIterator that the loop to which this backedge belongs should
61// be revisited. All of the blocks of this loop will then be revisited.
62//
63// Implementation details for revisitation of loops:
64//
65// In order to avoid visiting loop exits (= blocks whose dominator is in a loop
66// but which aren't themselves in the loop) multiple times, the stack of Blocks
67// to visit contains pairs of "block, generation". Additionally, we have a
68// global {current_generation_} counter, which is incremented when we revisit a
69// loop. When visiting a block, we record in {visited_} that it has been visited
70// at {current_generation_}. When we pop a block from the stack and its
71// "generation" field is less than what is recorded in {visited_}, then we skip
72// it. On the other hand, if its "generation" field is greater than the one
73// recorded in {visited_}, it means that we've revisited a loop since the last
74// time we visited this block, so we should revisit it as well.
75
77 public:
79 const LoopFinder& loop_finder)
80 : graph_(graph),
81 loop_finder_(loop_finder),
82 visited_(graph.block_count(), kNotVisitedGeneration, phase_zone),
83 stack_(phase_zone) {
84 stack_.push_back({&graph.StartBlock(), kGenerationForFirstVisit});
85 }
86
87 bool HasNext() const {
88 DCHECK_IMPLIES(!stack_.empty(), !IsOutdated(stack_.back()));
89 return !stack_.empty();
90 }
91 const Block* Next();
92 // Schedule the loop pointed to by the current block (as a backedge)
93 // to be revisited on the next iteration.
94 void MarkLoopForRevisit();
95 // Schedule the loop pointed to by the current block (as a backedge) to be
96 // revisited on the next iteration but skip the loop header.
97 void MarkLoopForRevisitSkipHeader();
98
99 private:
100 struct StackNode {
101 const Block* block;
102 uint64_t generation;
103 };
104 static constexpr uint64_t kNotVisitedGeneration = 0;
105 static constexpr uint64_t kGenerationForFirstVisit = 1;
106
107 void PopOutdated();
108 bool IsOutdated(StackNode node) const {
109 return visited_[node.block->index()] >= node.generation;
110 }
111
112 const Graph& graph_;
114
115 uint64_t current_generation_ = kGenerationForFirstVisit;
116
117 // The last block returned by Next.
118 StackNode curr_ = {nullptr, 0};
119
120 // {visited_} maps BlockIndex to the generation they were visited with. If a
121 // Block has been visited with a generation `n`, then we never want to revisit
122 // it with a generation `k` when `k <= n`.
124
125 // The stack of blocks that are left to visit. We maintain the invariant that
126 // the .back() of {stack_} is never out-dated (ie, its generation is always
127 // greater than the generation for its node recorded in {visited_}), so that
128 // "Next" can simply check whether {stack_} is empty or not.
130};
131
132} // namespace v8::internal::compiler::turboshaft
133
134#endif // V8_COMPILER_TURBOSHAFT_ANALYZER_ITERATOR_H_
AnalyzerIterator(Zone *phase_zone, const Graph &graph, const LoopFinder &loop_finder)
HeapObjectSet *const visited_
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define V8_EXPORT_PRIVATE
Definition macros.h:460
TFGraph * graph_