| 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 |