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 |