OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library dart2js.ir_tracer; | |
6 | |
7 import 'dart:async' show EventSink; | |
8 | |
9 import 'cps_ir_nodes.dart' as cps_ir hide Function; | |
10 import '../tracer.dart'; | |
11 | |
12 /** | |
13 * If true, show LetCont expressions in output. | |
14 */ | |
15 const bool IR_TRACE_LET_CONT = false; | |
16 | |
17 class IRTracer extends TracerUtil implements cps_ir.Visitor { | |
18 EventSink<String> output; | |
19 | |
20 IRTracer(this.output); | |
21 | |
22 visit(cps_ir.Node node) => node.accept(this); | |
23 | |
24 void traceGraph(String name, cps_ir.FunctionDefinition graph) { | |
25 tag("cfg", () { | |
26 printProperty("name", name); | |
27 visitFunctionDefinition(graph); | |
28 }); | |
29 } | |
30 | |
31 // Temporary field used during tree walk | |
32 Names names; | |
33 | |
34 visitFunctionDefinition(cps_ir.FunctionDefinition f) { | |
35 names = new Names(); | |
36 BlockCollector builder = new BlockCollector(names); | |
37 builder.visit(f); | |
38 | |
39 printNode(builder.entry); | |
40 for (Block block in builder.cont2block.values) { | |
41 printNode(block); | |
42 } | |
43 names = null; | |
44 } | |
45 | |
46 int countUses(cps_ir.Definition definition) { | |
47 int count = 0; | |
48 cps_ir.Reference ref = definition.firstRef; | |
49 while (ref != null) { | |
50 ++count; | |
51 ref = ref.next; | |
52 } | |
53 return count; | |
54 } | |
55 | |
56 printNode(Block block) { | |
57 tag("block", () { | |
58 printProperty("name", block.name); | |
59 printProperty("from_bci", -1); | |
60 printProperty("to_bci", -1); | |
61 printProperty("predecessors", block.pred.map((n) => n.name)); | |
62 printProperty("successors", block.succ.map((n) => n.name)); | |
63 printEmptyProperty("xhandlers"); | |
64 printEmptyProperty("flags"); | |
65 tag("states", () { | |
66 tag("locals", () { | |
67 printProperty("size", 0); | |
68 printProperty("method", "None"); | |
69 }); | |
70 }); | |
71 tag("HIR", () { | |
72 for (cps_ir.Parameter param in block.parameters) { | |
73 String name = names.name(param); | |
74 printStmt(name, "Parameter $name [useCount=${countUses(param)}]"); | |
75 } | |
76 visit(block.body); | |
77 }); | |
78 }); | |
79 } | |
80 | |
81 void printStmt(String resultVar, String contents) { | |
82 int bci = 0; | |
83 int uses = 0; | |
84 addIndent(); | |
85 add("$bci $uses $resultVar $contents <|@\n"); | |
86 } | |
87 | |
88 visitLetPrim(cps_ir.LetPrim node) { | |
89 String id = names.name(node.primitive); | |
90 printStmt(id, "LetPrim $id = ${formatPrimitive(node.primitive)}"); | |
91 visit(node.body); | |
92 } | |
93 | |
94 visitLetCont(cps_ir.LetCont node) { | |
95 if (IR_TRACE_LET_CONT) { | |
96 String dummy = names.name(node); | |
97 String id = names.name(node.continuation); | |
98 printStmt(dummy, "LetCont $id = <$id>"); | |
99 } | |
100 visit(node.body); | |
101 } | |
102 | |
103 visitInvokeStatic(cps_ir.InvokeStatic node) { | |
104 String dummy = names.name(node); | |
105 String callName = node.selector.name; | |
106 String args = node.arguments.map(formatReference).join(', '); | |
107 String kont = formatReference(node.continuation); | |
108 printStmt(dummy, "InvokeStatic $callName ($args) $kont"); | |
109 } | |
110 | |
111 visitInvokeMethod(cps_ir.InvokeMethod node) { | |
112 String dummy = names.name(node); | |
113 String receiver = formatReference(node.receiver); | |
114 String callName = node.selector.name; | |
115 String args = node.arguments.map(formatReference).join(', '); | |
116 String kont = formatReference(node.continuation); | |
117 printStmt(dummy, | |
118 "InvokeMethod $receiver $callName ($args) $kont"); | |
119 } | |
120 | |
121 visitInvokeSuperMethod(cps_ir.InvokeSuperMethod node) { | |
122 String dummy = names.name(node); | |
123 String callName = node.selector.name; | |
124 String args = node.arguments.map(formatReference).join(', '); | |
125 String kont = formatReference(node.continuation); | |
126 printStmt(dummy, | |
127 "InvokeSuperMethod $callName ($args) $kont"); | |
128 } | |
129 | |
130 visitInvokeConstructor(cps_ir.InvokeConstructor node) { | |
131 String dummy = names.name(node); | |
132 String callName; | |
133 if (node.target.name.isEmpty) { | |
134 callName = '${node.type}'; | |
135 } else { | |
136 callName = '${node.type}.${node.target.name}'; | |
137 } | |
138 String args = node.arguments.map(formatReference).join(', '); | |
139 String kont = formatReference(node.continuation); | |
140 printStmt(dummy, "InvokeConstructor $callName ($args) $kont"); | |
141 } | |
142 | |
143 visitConcatenateStrings(cps_ir.ConcatenateStrings node) { | |
144 String dummy = names.name(node); | |
145 String args = node.arguments.map(formatReference).join(', '); | |
146 String kont = formatReference(node.continuation); | |
147 printStmt(dummy, "ConcatenateStrings ($args) $kont"); | |
148 } | |
149 | |
150 visitLiteralList(cps_ir.LiteralList node) { | |
151 String dummy = names.name(node); | |
152 String values = node.values.map(formatReference).join(', '); | |
153 printStmt(dummy, "LiteralList ($values)"); | |
154 } | |
155 | |
156 visitLiteralMap(cps_ir.LiteralMap node) { | |
157 String dummy = names.name(node); | |
158 List<String> entries = new List<String>(); | |
159 for (cps_ir.LiteralMapEntry entry in node.entries) { | |
160 String key = formatReference(entry.key); | |
161 String value = formatReference(entry.value); | |
162 entries.add("$key: $value"); | |
163 } | |
164 printStmt(dummy, "LiteralMap (${entries.join(', ')})"); | |
165 } | |
166 | |
167 visitTypeOperator(cps_ir.TypeOperator node) { | |
168 String dummy = names.name(node); | |
169 String operator = node.isTypeTest ? 'is' : 'as'; | |
170 List<String> entries = new List<String>(); | |
171 String receiver = formatReference(node.receiver); | |
172 printStmt(dummy, "TypeOperator ($operator $receiver ${node.type})"); | |
173 } | |
174 | |
175 visitInvokeContinuation(cps_ir.InvokeContinuation node) { | |
176 String dummy = names.name(node); | |
177 String kont = formatReference(node.continuation); | |
178 String args = node.arguments.map(formatReference).join(', '); | |
179 printStmt(dummy, "InvokeContinuation $kont ($args)"); | |
180 } | |
181 | |
182 visitBranch(cps_ir.Branch node) { | |
183 String dummy = names.name(node); | |
184 String condition = visit(node.condition); | |
185 String trueCont = formatReference(node.trueContinuation); | |
186 String falseCont = formatReference(node.falseContinuation); | |
187 printStmt(dummy, "Branch $condition ($trueCont, $falseCont)"); | |
188 } | |
189 | |
190 visitSetClosureVariable(cps_ir.SetClosureVariable node) { | |
191 String dummy = names.name(node); | |
192 String variable = node.variable.name; | |
193 String value = formatReference(node.value); | |
194 printStmt(dummy, 'SetClosureVariable $variable = $value'); | |
195 visit(node.body); | |
196 } | |
197 | |
198 visitDeclareFunction(cps_ir.DeclareFunction node) { | |
199 String dummy = names.name(node); | |
200 String variable = node.variable.name; | |
201 printStmt(dummy, 'DeclareFunction $variable'); | |
202 visit(node.body); | |
203 } | |
204 | |
205 String formatReference(cps_ir.Reference ref) { | |
206 cps_ir.Definition target = ref.definition; | |
207 if (target is cps_ir.Continuation && target.isReturnContinuation) { | |
208 return "return"; // Do not generate a name for the return continuation | |
209 } else { | |
210 return names.name(ref.definition); | |
211 } | |
212 } | |
213 | |
214 String formatPrimitive(cps_ir.Primitive p) => visit(p); | |
215 | |
216 visitConstant(cps_ir.Constant node) { | |
217 return "Constant ${node.expression.value}"; | |
218 } | |
219 | |
220 visitParameter(cps_ir.Parameter node) { | |
221 return "Parameter ${names.name(node)}"; | |
222 } | |
223 | |
224 visitContinuation(cps_ir.Continuation node) { | |
225 return "Continuation ${names.name(node)}"; | |
226 } | |
227 | |
228 visitIsTrue(cps_ir.IsTrue node) { | |
229 return "IsTrue(${names.name(node.value.definition)})"; | |
230 } | |
231 | |
232 visitThis(cps_ir.This node) { | |
233 return "This"; | |
234 } | |
235 | |
236 visitReifyTypeVar(cps_ir.ReifyTypeVar node) { | |
237 return "ReifyTypeVar ${node.typeVariable.name}"; | |
238 } | |
239 | |
240 visitCreateFunction(cps_ir.CreateFunction node) { | |
241 return "CreateFunction ${node.definition.element.name}"; | |
242 } | |
243 | |
244 visitGetClosureVariable(cps_ir.GetClosureVariable node) { | |
245 String variable = node.variable.name; | |
246 return 'GetClosureVariable $variable'; | |
247 } | |
248 | |
249 | |
250 visitCondition(cps_ir.Condition c) {} | |
251 visitExpression(cps_ir.Expression e) {} | |
252 visitPrimitive(cps_ir.Primitive p) {} | |
253 visitDefinition(cps_ir.Definition d) {} | |
254 visitNode(cps_ir.Node n) {} | |
255 } | |
256 | |
257 /** | |
258 * Invents (and remembers) names for Continuations, Parameters, etc. | |
259 * The names must match the conventions used by IR Hydra, e.g. | |
260 * Continuations and Functions must have names of form B### since they | |
261 * are visualized as basic blocks. | |
262 */ | |
263 class Names { | |
264 final Map<Object, String> names = {}; | |
265 final Map<String, int> counters = { | |
266 'r': 0, | |
267 'B': 0, | |
268 'v': 0, | |
269 'x': 0 | |
270 }; | |
271 | |
272 String prefix(x) { | |
273 if (x is cps_ir.Parameter) return 'r'; | |
274 if (x is cps_ir.Continuation || x is cps_ir.FunctionDefinition) return 'B'; | |
275 if (x is cps_ir.Primitive) return 'v'; | |
276 return 'x'; | |
277 } | |
278 | |
279 String name(x) { | |
280 String nam = names[x]; | |
281 if (nam == null) { | |
282 String pref = prefix(x); | |
283 int id = counters[pref]++; | |
284 nam = names[x] = '${pref}${id}'; | |
285 } | |
286 return nam; | |
287 } | |
288 } | |
289 | |
290 /** | |
291 * A vertex in the graph visualization, used in place of basic blocks. | |
292 */ | |
293 class Block { | |
294 String name; | |
295 final List<cps_ir.Parameter> parameters; | |
296 final cps_ir.Expression body; | |
297 final List<Block> succ = <Block>[]; | |
298 final List<Block> pred = <Block>[]; | |
299 | |
300 Block(this.name, this.parameters, this.body); | |
301 | |
302 void addEdgeTo(Block successor) { | |
303 succ.add(successor); | |
304 successor.pred.add(this); | |
305 } | |
306 } | |
307 | |
308 class BlockCollector extends cps_ir.Visitor { | |
309 Block entry; | |
310 final Map<cps_ir.Continuation, Block> cont2block = | |
311 <cps_ir.Continuation, Block>{}; | |
312 Block current_block; | |
313 | |
314 Names names; | |
315 BlockCollector(this.names); | |
316 | |
317 Block getBlock(cps_ir.Continuation c) { | |
318 Block block = cont2block[c]; | |
319 if (block == null) { | |
320 block = new Block(names.name(c), c.parameters, c.body); | |
321 cont2block[c] = block; | |
322 } | |
323 return block; | |
324 } | |
325 | |
326 visitFunctionDefinition(cps_ir.FunctionDefinition f) { | |
327 entry = current_block = new Block(names.name(f), [], f.body); | |
328 visit(f.body); | |
329 } | |
330 | |
331 visitLetPrim(cps_ir.LetPrim exp) { | |
332 visit(exp.body); | |
333 } | |
334 | |
335 visitLetCont(cps_ir.LetCont exp) { | |
336 visit(exp.continuation); | |
337 visit(exp.body); | |
338 } | |
339 | |
340 void addEdgeToContinuation(cps_ir.Reference continuation) { | |
341 cps_ir.Definition target = continuation.definition; | |
342 if (target is cps_ir.Continuation && !target.isReturnContinuation) { | |
343 current_block.addEdgeTo(getBlock(target)); | |
344 } | |
345 } | |
346 | |
347 visitInvokeStatic(cps_ir.InvokeStatic exp) { | |
348 addEdgeToContinuation(exp.continuation); | |
349 } | |
350 | |
351 visitInvokeMethod(cps_ir.InvokeMethod exp) { | |
352 addEdgeToContinuation(exp.continuation); | |
353 } | |
354 | |
355 visitInvokeConstructor(cps_ir.InvokeConstructor exp) { | |
356 addEdgeToContinuation(exp.continuation); | |
357 } | |
358 | |
359 visitConcatenateStrings(cps_ir.ConcatenateStrings exp) { | |
360 addEdgeToContinuation(exp.continuation); | |
361 } | |
362 | |
363 visitInvokeContinuation(cps_ir.InvokeContinuation exp) { | |
364 addEdgeToContinuation(exp.continuation); | |
365 } | |
366 | |
367 visitSetClosureVariable(cps_ir.SetClosureVariable exp) { | |
368 visit(exp.body); | |
369 } | |
370 | |
371 visitDeclareFunction(cps_ir.DeclareFunction exp) { | |
372 visit(exp.body); | |
373 } | |
374 | |
375 visitBranch(cps_ir.Branch exp) { | |
376 cps_ir.Continuation trueTarget = exp.trueContinuation.definition; | |
377 if (!trueTarget.isReturnContinuation) { | |
378 current_block.addEdgeTo(getBlock(trueTarget)); | |
379 } | |
380 cps_ir.Continuation falseTarget = exp.falseContinuation.definition; | |
381 if (!falseTarget.isReturnContinuation) { | |
382 current_block.addEdgeTo(getBlock(falseTarget)); | |
383 } | |
384 } | |
385 | |
386 visitContinuation(cps_ir.Continuation c) { | |
387 var old_node = current_block; | |
388 current_block = getBlock(c); | |
389 visit(c.body); | |
390 current_block = old_node; | |
391 } | |
392 } | |
OLD | NEW |