| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, 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 ssa.tracer; | |
| 6 | |
| 7 import 'dart:async' show EventSink; | |
| 8 | |
| 9 import 'ssa.dart'; | |
| 10 import '../js_backend/js_backend.dart'; | |
| 11 import '../dart2jslib.dart'; | |
| 12 import '../tracer.dart'; | |
| 13 | |
| 14 /** | |
| 15 * Outputs SSA code in a format readable by Hydra IR. | |
| 16 * Tracing is disabled by default, see ../tracer.dart for how | |
| 17 * to enable it. | |
| 18 */ | |
| 19 class HTracer extends HGraphVisitor with TracerUtil { | |
| 20 Compiler compiler; | |
| 21 JavaScriptItemCompilationContext context; | |
| 22 final EventSink<String> output; | |
| 23 | |
| 24 HTracer(this.output, this.compiler, this.context); | |
| 25 | |
| 26 void traceGraph(String name, HGraph graph) { | |
| 27 DEBUG_MODE = true; | |
| 28 tag("cfg", () { | |
| 29 printProperty("name", name); | |
| 30 visitDominatorTree(graph); | |
| 31 }); | |
| 32 } | |
| 33 | |
| 34 void addPredecessors(HBasicBlock block) { | |
| 35 if (block.predecessors.isEmpty) { | |
| 36 printEmptyProperty("predecessors"); | |
| 37 } else { | |
| 38 addIndent(); | |
| 39 add("predecessors"); | |
| 40 for (HBasicBlock predecessor in block.predecessors) { | |
| 41 add(' "B${predecessor.id}"'); | |
| 42 } | |
| 43 add("\n"); | |
| 44 } | |
| 45 } | |
| 46 | |
| 47 void addSuccessors(HBasicBlock block) { | |
| 48 if (block.successors.isEmpty) { | |
| 49 printEmptyProperty("successors"); | |
| 50 } else { | |
| 51 addIndent(); | |
| 52 add("successors"); | |
| 53 for (HBasicBlock successor in block.successors) { | |
| 54 add(' "B${successor.id}"'); | |
| 55 } | |
| 56 add("\n"); | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 void addInstructions(HInstructionStringifier stringifier, | |
| 61 HInstructionList list) { | |
| 62 for (HInstruction instruction = list.first; | |
| 63 instruction != null; | |
| 64 instruction = instruction.next) { | |
| 65 int bci = 0; | |
| 66 int uses = instruction.usedBy.length; | |
| 67 String changes = instruction.sideEffects.hasSideEffects() ? '!' : ' '; | |
| 68 String depends = instruction.sideEffects.dependsOnSomething() ? '?' : ''; | |
| 69 addIndent(); | |
| 70 String temporaryId = stringifier.temporaryId(instruction); | |
| 71 String instructionString = stringifier.visit(instruction); | |
| 72 add("$bci $uses $temporaryId $instructionString $changes $depends <|@\n"); | |
| 73 } | |
| 74 } | |
| 75 | |
| 76 void visitBasicBlock(HBasicBlock block) { | |
| 77 HInstructionStringifier stringifier = | |
| 78 new HInstructionStringifier(context, block, compiler); | |
| 79 assert(block.id != null); | |
| 80 tag("block", () { | |
| 81 printProperty("name", "B${block.id}"); | |
| 82 printProperty("from_bci", -1); | |
| 83 printProperty("to_bci", -1); | |
| 84 addPredecessors(block); | |
| 85 addSuccessors(block); | |
| 86 printEmptyProperty("xhandlers"); | |
| 87 printEmptyProperty("flags"); | |
| 88 if (block.dominator != null) { | |
| 89 printProperty("dominator", "B${block.dominator.id}"); | |
| 90 } | |
| 91 tag("states", () { | |
| 92 tag("locals", () { | |
| 93 printProperty("size", 0); | |
| 94 printProperty("method", "None"); | |
| 95 block.forEachPhi((phi) { | |
| 96 String phiId = stringifier.temporaryId(phi); | |
| 97 StringBuffer inputIds = new StringBuffer(); | |
| 98 for (int i = 0; i < phi.inputs.length; i++) { | |
| 99 inputIds.write(stringifier.temporaryId(phi.inputs[i])); | |
| 100 inputIds.write(" "); | |
| 101 } | |
| 102 println("${phi.id} $phiId [ $inputIds]"); | |
| 103 }); | |
| 104 }); | |
| 105 }); | |
| 106 tag("HIR", () { | |
| 107 addInstructions(stringifier, block.phis); | |
| 108 addInstructions(stringifier, block); | |
| 109 }); | |
| 110 }); | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 class HInstructionStringifier implements HVisitor<String> { | |
| 115 final Compiler compiler; | |
| 116 final JavaScriptItemCompilationContext context; | |
| 117 final HBasicBlock currentBlock; | |
| 118 | |
| 119 HInstructionStringifier(this.context, this.currentBlock, this.compiler); | |
| 120 | |
| 121 visit(HInstruction node) => '${node.accept(this)} ${node.instructionType}'; | |
| 122 | |
| 123 String temporaryId(HInstruction instruction) { | |
| 124 String prefix; | |
| 125 if (instruction.isNull()) { | |
| 126 prefix = 'u'; | |
| 127 } else if (instruction.isConflicting()) { | |
| 128 prefix = 'c'; | |
| 129 } else if (instruction.isExtendableArray(compiler)) { | |
| 130 prefix = 'e'; | |
| 131 } else if (instruction.isFixedArray(compiler)) { | |
| 132 prefix = 'f'; | |
| 133 } else if (instruction.isMutableArray(compiler)) { | |
| 134 prefix = 'm'; | |
| 135 } else if (instruction.isReadableArray(compiler)) { | |
| 136 prefix = 'a'; | |
| 137 } else if (instruction.isString(compiler)) { | |
| 138 prefix = 's'; | |
| 139 } else if (instruction.isIndexablePrimitive(compiler)) { | |
| 140 prefix = 'r'; | |
| 141 } else if (instruction.isBoolean(compiler)) { | |
| 142 prefix = 'b'; | |
| 143 } else if (instruction.isInteger(compiler)) { | |
| 144 prefix = 'i'; | |
| 145 } else if (instruction.isDouble(compiler)) { | |
| 146 prefix = 'd'; | |
| 147 } else if (instruction.isNumber(compiler)) { | |
| 148 prefix = 'n'; | |
| 149 } else if (instruction.instructionType.containsAll(compiler.world)) { | |
| 150 prefix = 'v'; | |
| 151 } else { | |
| 152 prefix = 'U'; | |
| 153 } | |
| 154 return "$prefix${instruction.id}"; | |
| 155 } | |
| 156 | |
| 157 String visitBoolify(HBoolify node) { | |
| 158 return "Boolify: ${temporaryId(node.inputs[0])}"; | |
| 159 } | |
| 160 | |
| 161 String handleInvokeBinary(HInvokeBinary node, String op) { | |
| 162 String left = temporaryId(node.left); | |
| 163 String right= temporaryId(node.right); | |
| 164 return '$left $op $right'; | |
| 165 } | |
| 166 | |
| 167 String visitAdd(HAdd node) => handleInvokeBinary(node, '+'); | |
| 168 | |
| 169 String visitBitAnd(HBitAnd node) => handleInvokeBinary(node, '&'); | |
| 170 | |
| 171 String visitBitNot(HBitNot node) { | |
| 172 String operand = temporaryId(node.operand); | |
| 173 return "~$operand"; | |
| 174 } | |
| 175 | |
| 176 String visitBitOr(HBitOr node) => handleInvokeBinary(node, '|'); | |
| 177 | |
| 178 String visitBitXor(HBitXor node) => handleInvokeBinary(node, '^'); | |
| 179 | |
| 180 String visitBoundsCheck(HBoundsCheck node) { | |
| 181 String lengthId = temporaryId(node.length); | |
| 182 String indexId = temporaryId(node.index); | |
| 183 return "Bounds check: length = $lengthId, index = $indexId"; | |
| 184 } | |
| 185 | |
| 186 String visitBreak(HBreak node) { | |
| 187 HBasicBlock target = currentBlock.successors[0]; | |
| 188 if (node.label != null) { | |
| 189 return "Break ${node.label.labelName}: (B${target.id})"; | |
| 190 } | |
| 191 return "Break: (B${target.id})"; | |
| 192 } | |
| 193 | |
| 194 String visitConstant(HConstant constant) => "Constant ${constant.constant}"; | |
| 195 | |
| 196 String visitContinue(HContinue node) { | |
| 197 HBasicBlock target = currentBlock.successors[0]; | |
| 198 if (node.label != null) { | |
| 199 return "Continue ${node.label.labelName}: (B${target.id})"; | |
| 200 } | |
| 201 return "Continue: (B${target.id})"; | |
| 202 } | |
| 203 | |
| 204 String visitDivide(HDivide node) => handleInvokeBinary(node, '/'); | |
| 205 | |
| 206 String visitExit(HExit node) => "exit"; | |
| 207 | |
| 208 String visitFieldGet(HFieldGet node) { | |
| 209 if (node.isNullCheck) { | |
| 210 return 'null check on ${temporaryId(node.receiver)}'; | |
| 211 } | |
| 212 String fieldName = node.element.name; | |
| 213 return 'field get ${temporaryId(node.receiver)}.$fieldName'; | |
| 214 } | |
| 215 | |
| 216 String visitFieldSet(HFieldSet node) { | |
| 217 String valueId = temporaryId(node.value); | |
| 218 String fieldName = node.element.name; | |
| 219 return 'field set ${temporaryId(node.receiver)}.$fieldName to $valueId'; | |
| 220 } | |
| 221 | |
| 222 String visitReadModifyWrite(HReadModifyWrite node) { | |
| 223 String fieldName = node.element.name; | |
| 224 String receiverId = temporaryId(node.receiver); | |
| 225 String op = node.jsOp; | |
| 226 if (node.isAssignOp) { | |
| 227 String valueId = temporaryId(node.value); | |
| 228 return 'field-update $receiverId.$fieldName $op= $valueId'; | |
| 229 } else if (node.isPreOp) { | |
| 230 return 'field-update $op$receiverId.$fieldName'; | |
| 231 } else { | |
| 232 return 'field-update $receiverId.$fieldName$op'; | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 String visitLocalGet(HLocalGet node) { | |
| 237 String localName = node.variable.name; | |
| 238 return 'local get ${temporaryId(node.local)}.$localName'; | |
| 239 } | |
| 240 | |
| 241 String visitLocalSet(HLocalSet node) { | |
| 242 String valueId = temporaryId(node.value); | |
| 243 String localName = node.variable.name; | |
| 244 return 'local set ${temporaryId(node.local)}.$localName to $valueId'; | |
| 245 } | |
| 246 | |
| 247 String visitGoto(HGoto node) { | |
| 248 HBasicBlock target = currentBlock.successors[0]; | |
| 249 return "Goto: (B${target.id})"; | |
| 250 } | |
| 251 | |
| 252 String visitGreater(HGreater node) => handleInvokeBinary(node, '>'); | |
| 253 String visitGreaterEqual(HGreaterEqual node) { | |
| 254 return handleInvokeBinary(node, '>='); | |
| 255 } | |
| 256 String visitIdentity(HIdentity node) => handleInvokeBinary(node, '==='); | |
| 257 | |
| 258 String visitIf(HIf node) { | |
| 259 HBasicBlock thenBlock = currentBlock.successors[0]; | |
| 260 HBasicBlock elseBlock = currentBlock.successors[1]; | |
| 261 String conditionId = temporaryId(node.inputs[0]); | |
| 262 return "If ($conditionId): (B${thenBlock.id}) else (B${elseBlock.id})"; | |
| 263 } | |
| 264 | |
| 265 String visitGenericInvoke(String invokeType, String functionName, | |
| 266 List<HInstruction> arguments) { | |
| 267 StringBuffer argumentsString = new StringBuffer(); | |
| 268 for (int i = 0; i < arguments.length; i++) { | |
| 269 if (i != 0) argumentsString.write(", "); | |
| 270 argumentsString.write(temporaryId(arguments[i])); | |
| 271 } | |
| 272 return "$invokeType: $functionName($argumentsString)"; | |
| 273 } | |
| 274 | |
| 275 String visitIndex(HIndex node) { | |
| 276 String receiver = temporaryId(node.receiver); | |
| 277 String index = temporaryId(node.index); | |
| 278 return "Index: $receiver[$index]"; | |
| 279 } | |
| 280 | |
| 281 String visitIndexAssign(HIndexAssign node) { | |
| 282 String receiver = temporaryId(node.receiver); | |
| 283 String index = temporaryId(node.index); | |
| 284 String value = temporaryId(node.value); | |
| 285 return "IndexAssign: $receiver[$index] = $value"; | |
| 286 } | |
| 287 | |
| 288 String visitInterceptor(HInterceptor node) { | |
| 289 String value = temporaryId(node.inputs[0]); | |
| 290 if (node.interceptedClasses != null) { | |
| 291 JavaScriptBackend backend = compiler.backend; | |
| 292 String cls = backend.namer.getInterceptorSuffix(node.interceptedClasses); | |
| 293 return "Intercept ($cls): $value"; | |
| 294 } | |
| 295 return "Intercept: $value"; | |
| 296 } | |
| 297 | |
| 298 String visitInvokeClosure(HInvokeClosure node) | |
| 299 => visitInvokeDynamic(node, "closure"); | |
| 300 | |
| 301 String visitInvokeDynamic(HInvokeDynamic invoke, String kind) { | |
| 302 String receiver = temporaryId(invoke.receiver); | |
| 303 String name = invoke.selector.name; | |
| 304 String target = "($kind) $receiver.$name"; | |
| 305 int offset = HInvoke.ARGUMENTS_OFFSET; | |
| 306 List arguments = invoke.inputs.sublist(offset); | |
| 307 return visitGenericInvoke("Invoke", target, arguments) + | |
| 308 "(${invoke.selector.mask})"; | |
| 309 } | |
| 310 | |
| 311 String visitInvokeDynamicMethod(HInvokeDynamicMethod node) | |
| 312 => visitInvokeDynamic(node, "method"); | |
| 313 String visitInvokeDynamicGetter(HInvokeDynamicGetter node) | |
| 314 => visitInvokeDynamic(node, "get"); | |
| 315 String visitInvokeDynamicSetter(HInvokeDynamicSetter node) | |
| 316 => visitInvokeDynamic(node, "set"); | |
| 317 | |
| 318 String visitInvokeStatic(HInvokeStatic invoke) { | |
| 319 String target = invoke.element.name; | |
| 320 return visitGenericInvoke("Invoke", target, invoke.inputs); | |
| 321 } | |
| 322 | |
| 323 String visitInvokeSuper(HInvokeSuper invoke) { | |
| 324 String target = invoke.element.name; | |
| 325 return visitGenericInvoke("Invoke super", target, invoke.inputs); | |
| 326 } | |
| 327 | |
| 328 String visitInvokeConstructorBody(HInvokeConstructorBody invoke) { | |
| 329 String target = invoke.element.name; | |
| 330 return visitGenericInvoke("Invoke constructor body", target, invoke.inputs); | |
| 331 } | |
| 332 | |
| 333 String visitForeign(HForeign foreign) { | |
| 334 return visitGenericInvoke("Foreign", "${foreign.codeTemplate.ast}", foreign.
inputs); | |
| 335 } | |
| 336 | |
| 337 String visitForeignNew(HForeignNew node) { | |
| 338 return visitGenericInvoke("New", | |
| 339 "${node.element.name}", | |
| 340 node.inputs); | |
| 341 } | |
| 342 | |
| 343 String visitLess(HLess node) => handleInvokeBinary(node, '<'); | |
| 344 String visitLessEqual(HLessEqual node) => handleInvokeBinary(node, '<='); | |
| 345 | |
| 346 String visitLiteralList(HLiteralList node) { | |
| 347 StringBuffer elementsString = new StringBuffer(); | |
| 348 for (int i = 0; i < node.inputs.length; i++) { | |
| 349 if (i != 0) elementsString.write(", "); | |
| 350 elementsString.write(temporaryId(node.inputs[i])); | |
| 351 } | |
| 352 return "Literal list: [$elementsString]"; | |
| 353 } | |
| 354 | |
| 355 String visitLoopBranch(HLoopBranch branch) { | |
| 356 HBasicBlock bodyBlock = currentBlock.successors[0]; | |
| 357 HBasicBlock exitBlock = currentBlock.successors[1]; | |
| 358 String conditionId = temporaryId(branch.inputs[0]); | |
| 359 return "While ($conditionId): (B${bodyBlock.id}) then (B${exitBlock.id})"; | |
| 360 } | |
| 361 | |
| 362 String visitMultiply(HMultiply node) => handleInvokeBinary(node, '*'); | |
| 363 | |
| 364 String visitNegate(HNegate node) { | |
| 365 String operand = temporaryId(node.operand); | |
| 366 return "-$operand"; | |
| 367 } | |
| 368 | |
| 369 String visitNot(HNot node) => "Not: ${temporaryId(node.inputs[0])}"; | |
| 370 | |
| 371 String visitParameterValue(HParameterValue node) { | |
| 372 return "p${node.sourceElement.name}"; | |
| 373 } | |
| 374 | |
| 375 String visitLocalValue(HLocalValue node) { | |
| 376 return "l${node.sourceElement.name}"; | |
| 377 } | |
| 378 | |
| 379 String visitPhi(HPhi phi) { | |
| 380 StringBuffer buffer = new StringBuffer(); | |
| 381 buffer.write("Phi("); | |
| 382 for (int i = 0; i < phi.inputs.length; i++) { | |
| 383 if (i > 0) buffer.write(", "); | |
| 384 buffer.write(temporaryId(phi.inputs[i])); | |
| 385 } | |
| 386 buffer.write(")"); | |
| 387 return buffer.toString(); | |
| 388 } | |
| 389 | |
| 390 String visitReturn(HReturn node) => "Return ${temporaryId(node.inputs[0])}"; | |
| 391 | |
| 392 String visitShiftLeft(HShiftLeft node) => handleInvokeBinary(node, '<<'); | |
| 393 String visitShiftRight(HShiftRight node) => handleInvokeBinary(node, '>>'); | |
| 394 | |
| 395 String visitStatic(HStatic node) | |
| 396 => "Static ${node.element.name}"; | |
| 397 | |
| 398 String visitLazyStatic(HLazyStatic node) | |
| 399 => "LazyStatic ${node.element.name}"; | |
| 400 | |
| 401 String visitOneShotInterceptor(HOneShotInterceptor node) | |
| 402 => visitInvokeDynamic(node, "one shot interceptor"); | |
| 403 | |
| 404 String visitStaticStore(HStaticStore node) { | |
| 405 String lhs = node.element.name; | |
| 406 return "Static $lhs = ${temporaryId(node.inputs[0])}"; | |
| 407 } | |
| 408 | |
| 409 String visitStringConcat(HStringConcat node) { | |
| 410 var leftId = temporaryId(node.left); | |
| 411 var rightId = temporaryId(node.right); | |
| 412 return "StringConcat: $leftId + $rightId"; | |
| 413 } | |
| 414 | |
| 415 String visitStringify(HStringify node) { | |
| 416 return "Stringify ${temporaryId(node.inputs[0])}"; | |
| 417 } | |
| 418 | |
| 419 String visitSubtract(HSubtract node) => handleInvokeBinary(node, '-'); | |
| 420 | |
| 421 String visitSwitch(HSwitch node) { | |
| 422 StringBuffer buf = new StringBuffer(); | |
| 423 buf.write("Switch: ("); | |
| 424 buf.write(temporaryId(node.inputs[0])); | |
| 425 buf.write(") "); | |
| 426 for (int i = 1; i < node.inputs.length; i++) { | |
| 427 buf.write(temporaryId(node.inputs[i])); | |
| 428 buf.write(": B"); | |
| 429 buf.write(node.block.successors[i - 1].id); | |
| 430 buf.write(", "); | |
| 431 } | |
| 432 buf.write("default: B"); | |
| 433 buf.write(node.defaultTarget.id); | |
| 434 return buf.toString(); | |
| 435 } | |
| 436 | |
| 437 String visitThis(HThis node) => "this"; | |
| 438 | |
| 439 String visitThrow(HThrow node) => "Throw ${temporaryId(node.inputs[0])}"; | |
| 440 | |
| 441 String visitThrowExpression(HThrowExpression node) { | |
| 442 return "ThrowExpression ${temporaryId(node.inputs[0])}"; | |
| 443 } | |
| 444 | |
| 445 String visitTruncatingDivide(HTruncatingDivide node) { | |
| 446 return handleInvokeBinary(node, '~/'); | |
| 447 } | |
| 448 | |
| 449 String visitExitTry(HExitTry node) { | |
| 450 return "Exit try"; | |
| 451 } | |
| 452 | |
| 453 String visitTry(HTry node) { | |
| 454 List<HBasicBlock> successors = currentBlock.successors; | |
| 455 String tryBlock = 'B${successors[0].id}'; | |
| 456 String catchBlock = 'none'; | |
| 457 if (node.catchBlock != null) { | |
| 458 catchBlock = 'B${successors[1].id}'; | |
| 459 } | |
| 460 | |
| 461 String finallyBlock = 'none'; | |
| 462 if (node.finallyBlock != null) { | |
| 463 finallyBlock = 'B${node.finallyBlock.id}'; | |
| 464 } | |
| 465 | |
| 466 return "Try: $tryBlock, Catch: $catchBlock, Finally: $finallyBlock, " | |
| 467 "Join: B${successors.last.id}"; | |
| 468 } | |
| 469 | |
| 470 String visitIs(HIs node) { | |
| 471 String type = node.typeExpression.toString(); | |
| 472 return "TypeTest: ${temporaryId(node.expression)} is $type"; | |
| 473 } | |
| 474 | |
| 475 String visitIsViaInterceptor(HIsViaInterceptor node) { | |
| 476 String type = node.typeExpression.toString(); | |
| 477 return "TypeTest: ${temporaryId(node.inputs[0])} is $type"; | |
| 478 } | |
| 479 | |
| 480 String visitTypeConversion(HTypeConversion node) { | |
| 481 assert(node.inputs.length <= 2); | |
| 482 String otherInput = (node.inputs.length == 2) | |
| 483 ? temporaryId(node.inputs[1]) | |
| 484 : ''; | |
| 485 return "TypeConversion: ${temporaryId(node.checkedInput)} to " | |
| 486 "${node.instructionType} $otherInput"; | |
| 487 } | |
| 488 | |
| 489 String visitTypeKnown(HTypeKnown node) { | |
| 490 assert(node.inputs.length <= 2); | |
| 491 String result = | |
| 492 "TypeKnown: ${temporaryId(node.checkedInput)} is ${node.knownType}"; | |
| 493 if (node.witness != null) { | |
| 494 result += " witnessed by ${temporaryId(node.witness)}"; | |
| 495 } | |
| 496 return result; | |
| 497 } | |
| 498 | |
| 499 String visitRangeConversion(HRangeConversion node) { | |
| 500 return "RangeConversion: ${node.checkedInput}"; | |
| 501 } | |
| 502 | |
| 503 String visitReadTypeVariable(HReadTypeVariable node) { | |
| 504 return "ReadTypeVariable: ${node.dartType} ${node.hasReceiver}"; | |
| 505 } | |
| 506 | |
| 507 String visitFunctionType(HFunctionType node) { | |
| 508 return "FunctionType: ${node.dartType}"; | |
| 509 } | |
| 510 | |
| 511 String visitVoidType(HVoidType node) { | |
| 512 return "VoidType"; | |
| 513 } | |
| 514 | |
| 515 String visitInterfaceType(HInterfaceType node) { | |
| 516 return "InterfaceType: ${node.dartType}"; | |
| 517 } | |
| 518 | |
| 519 String visitDynamicType(HDynamicType node) { | |
| 520 return "DynamicType"; | |
| 521 } | |
| 522 } | |
| OLD | NEW |