OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 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_CONTROL_EQUIVALENCE_H_ | |
6 #define V8_COMPILER_CONTROL_EQUIVALENCE_H_ | |
7 | |
8 #include "src/v8.h" | |
9 | |
10 #include "src/compiler/graph.h" | |
11 #include "src/compiler/node.h" | |
12 #include "src/compiler/node-properties.h" | |
13 #include "src/zone-containers.h" | |
14 | |
15 namespace v8 { | |
16 namespace internal { | |
17 namespace compiler { | |
18 | |
19 // Determines control dependence equivalence classes for control nodes. Any two | |
20 // nodes having the same set of control dependences land in one class. These | |
21 // classes can in turn be used to: | |
22 // - Build a program structure tree (PST) for controls in the graph. | |
23 // - Determine single-entry single-exit (SESE) regions within the graph. | |
24 // | |
25 // Note that this implementation actually uses cycle equivalence to establish | |
26 // class numbers. Any two nodes are cycle equivalent if they occur in the same | |
27 // set of cycles. It can be shown that control dependence equivalence reduces | |
28 // down cycle equivalence for strongly connected control flow graphs. | |
Jarin
2014/12/01 20:56:24
Did not quite understand the last sentence, perhap
Michael Starzinger
2014/12/02 12:20:34
Done.
| |
29 // | |
30 // The algorithm is based on research by Johnson, Pearson & Pingali which also | |
Jarin
2014/12/01 20:56:24
Could you give a more complete citation? (At least
Michael Starzinger
2014/12/02 12:20:34
Done.
| |
31 // contains proofs for the aforementioned equivalence. | |
32 class ControlEquivalence : public ZoneObject { | |
33 public: | |
34 ControlEquivalence(Zone* zone, Graph* graph) | |
35 : zone_(zone), | |
36 graph_(graph), | |
37 dfs_number_(0), | |
38 class_number_(1), | |
39 node_data_(graph->NodeCount(), EmptyData(), zone) {} | |
40 | |
41 // Run the main algorithm starting from the {exit} control node. This causes | |
42 // the following iterations over control edges of the graph: | |
43 // 1) A breath-first backwards traversal to determine the set of nodes that | |
Jarin
2014/12/01 20:56:24
typo: breadth
Michael Starzinger
2014/12/02 12:20:34
Done.
| |
44 // participate in the next step. Takes O(E) time and O(N) space. | |
45 // 2) An undirected depth-first backwards traversal that determines class | |
46 // numbers for all participating nodes. Takes O(E) time and O(N) space. | |
47 void Run(Node* exit) { | |
48 if (GetClass(exit) != kInvalidClass) return; | |
49 DetermineParticipation(exit); | |
50 RunUndirectedDFS(exit); | |
51 } | |
52 | |
53 // Retrieves a previously computed class number. | |
54 size_t ClassOf(Node* node) { | |
55 DCHECK(GetClass(node) != kInvalidClass); | |
56 return GetClass(node); | |
57 } | |
58 | |
59 private: | |
60 static const size_t kInvalidClass = static_cast<size_t>(-1); | |
61 typedef enum { kInputDirection, kUseDirection } DFSDirection; | |
62 | |
63 struct Bracket { | |
64 DFSDirection direction; // Direction in which this bracket was added. | |
65 size_t recent_class; // Cached class when bracket was topmost. | |
66 size_t recent_size; // Cached set-size when bracket was topmost. | |
67 Node* from; // Node that this bracket originates from. | |
68 Node* to; // Node that this bracket points to. | |
69 }; | |
70 | |
71 // The set of brackets for each node during the DFS walk. | |
72 typedef ZoneLinkedList<Bracket> BracketList; | |
73 | |
74 struct DFSStackEntry { | |
75 DFSDirection direction; // Direction currently used in DFS walk. | |
76 Node::Inputs::iterator input; // Iterator used for the "input" direction. | |
77 Node::Uses::iterator use; // Iterator used for the "use" direction. | |
78 Node* parent_node; // Parent node of entry during DFS walk. | |
79 Node* node; // Node that this stack entry belongs to. | |
80 }; | |
81 | |
82 // The stack is used during the undirected DFS walk. | |
83 typedef ZoneStack<DFSStackEntry> DFSStack; | |
84 | |
85 struct NodeData { | |
86 size_t class_number; // Equivalence class number assigned to node. | |
87 size_t dfs_number; // Pre-order DFS number assigned to node. | |
88 bool on_stack; // Indicates node is on DFS stack during walk. | |
89 bool participates; // Indicates node participates in DFS walk. | |
90 BracketList blist; // List of brackets per node. | |
91 }; | |
92 | |
93 // The per-node data computed during the DFS walk. | |
94 typedef ZoneVector<NodeData> Data; | |
95 | |
96 void VisitPre(Node* node) { | |
97 Trace("CEQ: Pre-visit of #%d:%s\n", node->id(), node->op()->mnemonic()); | |
98 | |
99 // Dispense a new pre-order number. | |
100 SetNumber(node, NewDFSNumber()); | |
101 Trace(" Assigned DFS number is %d\n", GetNumber(node)); | |
102 } | |
103 | |
104 void VisitMid(Node* node, DFSDirection direction) { | |
105 Trace("CEQ: Mid-visit of #%d:%s\n", node->id(), node->op()->mnemonic()); | |
106 BracketList& blist = GetBracketList(node); | |
107 | |
108 // Remove brackets pointing to this node. | |
109 BracketListDelete(blist, node, direction); | |
110 | |
111 // Potentially introduce artificial dependency from start to end. | |
112 if (blist.empty()) { | |
113 DCHECK_EQ(graph_->start(), node); | |
114 DCHECK_EQ(kInputDirection, direction); | |
115 VisitBackedge(graph_->start(), graph_->end(), kInputDirection); | |
116 } | |
117 | |
118 // Potentially start a new equivalence class. | |
119 BracketListTrace(blist); | |
120 Bracket* recent = &blist.back(); | |
121 if (recent->recent_size != blist.size()) { | |
122 recent->recent_size = blist.size(); | |
123 recent->recent_class = NewClassNumber(); | |
124 } | |
125 | |
126 // Assign equivalence class to node. | |
127 SetClass(node, recent->recent_class); | |
Jarin
2014/12/01 20:56:24
I am quite confused here when trying to match the
Michael Starzinger
2014/12/02 12:20:34
Acknowledged.
As discussed offline: The capping b
| |
128 Trace(" Assigned class number is %d\n", GetClass(node)); | |
129 } | |
130 | |
131 void VisitPost(Node* node, Node* parent_node, DFSDirection direction) { | |
132 Trace("CEQ: Post-visit of #%d:%s\n", node->id(), node->op()->mnemonic()); | |
133 BracketList& blist = GetBracketList(node); | |
134 | |
135 // Remove brackets pointing to this node. | |
136 BracketListDelete(blist, node, direction); | |
137 | |
138 // Propagate bracket list up the DFS tree. | |
139 if (parent_node != NULL) { | |
140 BracketList& parent_blist = GetBracketList(parent_node); | |
141 parent_blist.splice(parent_blist.end(), blist); | |
142 } | |
143 } | |
144 | |
145 void VisitBackedge(Node* from, Node* to, DFSDirection direction) { | |
146 Trace("CEQ: Backedge from #%d:%s to #%d:%s\n", from->id(), | |
147 from->op()->mnemonic(), to->id(), to->op()->mnemonic()); | |
148 | |
149 // Push backedge onto the bracket list. | |
150 Bracket bracket = {direction, kInvalidClass, 0, from, to}; | |
151 GetBracketList(from).push_back(bracket); | |
152 } | |
153 | |
154 void RunUndirectedDFS(Node* exit) { | |
155 ZoneStack<DFSStackEntry> stack(zone_); | |
156 DFSPush(stack, exit, NULL, kInputDirection); | |
157 VisitPre(exit); | |
158 | |
159 while (!stack.empty()) { // Undirected depth-first backwards traversal. | |
160 DFSStackEntry& entry = stack.top(); | |
161 Node* node = entry.node; | |
162 | |
163 if (entry.direction == kInputDirection) { | |
164 if (entry.input != node->inputs().end()) { | |
165 Node::Edge edge = entry.input.edge(); | |
166 Node* input = *entry.input; | |
167 ++(entry.input); | |
168 if (NodeProperties::IsControlEdge(edge) && | |
169 NodeProperties::IsControl(input)) { | |
170 // Visit next control input. | |
171 if (!GetData(input)->participates) continue; | |
172 if (GetData(input)->on_stack) { | |
173 // Found backedge if input is on stack. | |
174 if (input != entry.parent_node) { | |
175 VisitBackedge(node, input, kInputDirection); | |
176 } | |
177 } else { | |
178 // Push input onto stack. | |
179 DFSPush(stack, input, node, kInputDirection); | |
180 VisitPre(input); | |
181 } | |
182 } | |
183 continue; | |
184 } | |
185 if (entry.use != node->uses().end()) { | |
186 // Switch direction to uses. | |
187 entry.direction = kUseDirection; | |
188 VisitMid(node, kInputDirection); | |
189 continue; | |
190 } | |
191 } | |
192 | |
193 if (entry.direction == kUseDirection) { | |
194 if (entry.use != node->uses().end()) { | |
195 Node::Edge edge = entry.use.edge(); | |
196 Node* use = *(entry.use); | |
197 ++(entry.use); | |
198 if (NodeProperties::IsControlEdge(edge) && | |
199 NodeProperties::IsControl(use)) { | |
200 // Visit next control use. | |
201 if (!GetData(use)->participates) continue; | |
202 if (GetData(use)->on_stack) { | |
203 // Found backedge if use is on stack. | |
204 if (use != entry.parent_node) { | |
205 VisitBackedge(node, use, kUseDirection); | |
206 } | |
207 } else { | |
208 // Push input onto stack. | |
209 DFSPush(stack, use, node, kUseDirection); | |
210 VisitPre(use); | |
211 } | |
212 } | |
213 continue; | |
214 } | |
215 if (entry.input != node->inputs().end()) { | |
216 // Switch direction to inputs. | |
217 entry.direction = kInputDirection; | |
218 VisitMid(node, kUseDirection); | |
219 continue; | |
220 } | |
221 } | |
222 | |
223 // Pop node from stack when done with all inputs and uses. | |
224 DCHECK(entry.input == node->inputs().end()); | |
225 DCHECK(entry.use == node->uses().end()); | |
226 DFSPop(stack, node); | |
227 VisitPost(node, entry.parent_node, entry.direction); | |
228 } | |
229 } | |
230 | |
231 void DetermineParticipationEnqueue(ZoneQueue<Node*>& queue, Node* node) { | |
232 if (!GetData(node)->participates) { | |
233 GetData(node)->participates = true; | |
234 queue.push(node); | |
235 } | |
236 } | |
237 | |
238 void DetermineParticipation(Node* exit) { | |
239 ZoneQueue<Node*> queue(zone_); | |
240 DetermineParticipationEnqueue(queue, exit); | |
241 while (!queue.empty()) { // Breadth-first backwards traversal. | |
242 Node* node = queue.front(); | |
243 queue.pop(); | |
244 int max = NodeProperties::PastControlIndex(node); | |
245 for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) { | |
246 DetermineParticipationEnqueue(queue, node->InputAt(i)); | |
247 } | |
248 } | |
249 } | |
250 | |
251 private: | |
252 NodeData* GetData(Node* node) { return &node_data_[node->id()]; } | |
253 int NewClassNumber() { return class_number_++; } | |
254 int NewDFSNumber() { return dfs_number_++; } | |
255 | |
256 // Template used to initialize per-node data. | |
257 NodeData EmptyData() { | |
258 return {kInvalidClass, 0, false, false, BracketList(zone_)}; | |
259 } | |
260 | |
261 // Accessors for the DFS number stored within the per-node data. | |
262 size_t GetNumber(Node* node) { return GetData(node)->dfs_number; } | |
263 void SetNumber(Node* node, size_t number) { | |
264 GetData(node)->dfs_number = number; | |
265 } | |
266 | |
267 // Accessors for the equivalence class stored within the per-node data. | |
268 size_t GetClass(Node* node) { return GetData(node)->class_number; } | |
269 void SetClass(Node* node, size_t number) { | |
270 GetData(node)->class_number = number; | |
271 } | |
272 | |
273 // Accessors for the bracket list stored within the per-node data. | |
274 BracketList& GetBracketList(Node* node) { return GetData(node)->blist; } | |
275 void SetBracketList(Node* node, BracketList& list) { | |
276 GetData(node)->blist = list; | |
277 } | |
278 | |
279 // Mutates the DFS stack by pushing an entry. | |
280 void DFSPush(DFSStack& stack, Node* node, Node* from, DFSDirection dir) { | |
281 DCHECK(GetData(node)->participates); | |
282 GetData(node)->on_stack = true; | |
283 Node::Inputs::iterator input = node->inputs().begin(); | |
284 Node::Uses::iterator use = node->uses().begin(); | |
285 stack.push({dir, input, use, from, node}); | |
286 } | |
287 | |
288 // Mutates the DFS stack by popping an entry. | |
289 void DFSPop(DFSStack& stack, Node* node) { | |
290 DCHECK_EQ(stack.top().node, node); | |
291 GetData(node)->on_stack = false; | |
292 GetData(node)->participates = false; | |
293 stack.pop(); | |
294 } | |
295 | |
296 // TODO(mstarzinger): Optimize this to avoid linear search. | |
297 void BracketListDelete(BracketList& blist, Node* to, DFSDirection direction) { | |
298 for (BracketList::iterator i = blist.begin(); i != blist.end(); /*nop*/) { | |
299 if (i->to == to && i->direction != direction) { | |
300 Trace(" BList erased: {%d->%d}\n", i->from->id(), i->to->id()); | |
301 i = blist.erase(i); | |
302 } else { | |
303 ++i; | |
304 } | |
305 } | |
306 } | |
307 | |
308 void BracketListTrace(BracketList& blist) { | |
309 if (FLAG_trace_turbo_scheduler) { | |
310 Trace(" BList: "); | |
311 for (Bracket bracket : blist) { | |
312 Trace("{%d->%d} ", bracket.from->id(), bracket.to->id()); | |
313 } | |
314 Trace("\n"); | |
315 } | |
316 } | |
317 | |
318 void Trace(const char* msg, ...) { | |
319 if (FLAG_trace_turbo_scheduler) { | |
320 va_list arguments; | |
321 va_start(arguments, msg); | |
322 base::OS::VPrint(msg, arguments); | |
323 va_end(arguments); | |
324 } | |
325 } | |
326 | |
327 Zone* zone_; | |
328 Graph* graph_; | |
329 int dfs_number_; // Generates new DFS pre-order numbers on demand. | |
330 int class_number_; // Generates new equivalence class numbers on demand. | |
331 Data node_data_; // Per-node data stored as a side-table. | |
332 }; | |
333 | |
334 } // namespace compiler | |
335 } // namespace internal | |
336 } // namespace v8 | |
337 | |
338 #endif // V8_COMPILER_CONTROL_EQUIVALENCE_H_ | |
OLD | NEW |