blob: 103ecae03cd55dbda7f75be8f913491d8038dd72 [file] [log] [blame]
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library ssa.tracer;
import 'dart:async' show EventSink;
import 'ssa.dart';
import '../js_backend/js_backend.dart';
import '../dart2jslib.dart';
import '../tracer.dart';
/**
* Outputs SSA code in a format readable by Hydra IR.
* Tracing is disabled by default, see ../tracer.dart for how
* to enable it.
*/
class HTracer extends HGraphVisitor with TracerUtil {
Compiler compiler;
JavaScriptItemCompilationContext context;
final EventSink<String> output;
HTracer(this.output, this.compiler, this.context);
void traceGraph(String name, HGraph graph) {
DEBUG_MODE = true;
tag("cfg", () {
printProperty("name", name);
visitDominatorTree(graph);
});
}
void addPredecessors(HBasicBlock block) {
if (block.predecessors.isEmpty) {
printEmptyProperty("predecessors");
} else {
addIndent();
add("predecessors");
for (HBasicBlock predecessor in block.predecessors) {
add(' "B${predecessor.id}"');
}
add("\n");
}
}
void addSuccessors(HBasicBlock block) {
if (block.successors.isEmpty) {
printEmptyProperty("successors");
} else {
addIndent();
add("successors");
for (HBasicBlock successor in block.successors) {
add(' "B${successor.id}"');
}
add("\n");
}
}
void addInstructions(HInstructionStringifier stringifier,
HInstructionList list) {
for (HInstruction instruction = list.first;
instruction != null;
instruction = instruction.next) {
int bci = 0;
int uses = instruction.usedBy.length;
String changes = instruction.sideEffects.hasSideEffects() ? '!' : ' ';
String depends = instruction.sideEffects.dependsOnSomething() ? '?' : '';
addIndent();
String temporaryId = stringifier.temporaryId(instruction);
String instructionString = stringifier.visit(instruction);
add("$bci $uses $temporaryId $instructionString $changes $depends <|@\n");
}
}
void visitBasicBlock(HBasicBlock block) {
HInstructionStringifier stringifier =
new HInstructionStringifier(context, block, compiler);
assert(block.id != null);
tag("block", () {
printProperty("name", "B${block.id}");
printProperty("from_bci", -1);
printProperty("to_bci", -1);
addPredecessors(block);
addSuccessors(block);
printEmptyProperty("xhandlers");
printEmptyProperty("flags");
if (block.dominator != null) {
printProperty("dominator", "B${block.dominator.id}");
}
tag("states", () {
tag("locals", () {
printProperty("size", 0);
printProperty("method", "None");
block.forEachPhi((phi) {
String phiId = stringifier.temporaryId(phi);
StringBuffer inputIds = new StringBuffer();
for (int i = 0; i < phi.inputs.length; i++) {
inputIds.write(stringifier.temporaryId(phi.inputs[i]));
inputIds.write(" ");
}
println("${phi.id} $phiId [ $inputIds]");
});
});
});
tag("HIR", () {
addInstructions(stringifier, block.phis);
addInstructions(stringifier, block);
});
});
}
}
class HInstructionStringifier implements HVisitor<String> {
final Compiler compiler;
final JavaScriptItemCompilationContext context;
final HBasicBlock currentBlock;
HInstructionStringifier(this.context, this.currentBlock, this.compiler);
visit(HInstruction node) => '${node.accept(this)} ${node.instructionType}';
String temporaryId(HInstruction instruction) {
String prefix;
if (instruction.isNull()) {
prefix = 'u';
} else if (instruction.isConflicting()) {
prefix = 'c';
} else if (instruction.isExtendableArray(compiler)) {
prefix = 'e';
} else if (instruction.isFixedArray(compiler)) {
prefix = 'f';
} else if (instruction.isMutableArray(compiler)) {
prefix = 'm';
} else if (instruction.isReadableArray(compiler)) {
prefix = 'a';
} else if (instruction.isString(compiler)) {
prefix = 's';
} else if (instruction.isIndexablePrimitive(compiler)) {
prefix = 'r';
} else if (instruction.isBoolean(compiler)) {
prefix = 'b';
} else if (instruction.isInteger(compiler)) {
prefix = 'i';
} else if (instruction.isDouble(compiler)) {
prefix = 'd';
} else if (instruction.isNumber(compiler)) {
prefix = 'n';
} else if (instruction.instructionType.containsAll(compiler.world)) {
prefix = 'v';
} else {
prefix = 'U';
}
return "$prefix${instruction.id}";
}
String visitBoolify(HBoolify node) {
return "Boolify: ${temporaryId(node.inputs[0])}";
}
String handleInvokeBinary(HInvokeBinary node, String op) {
String left = temporaryId(node.left);
String right= temporaryId(node.right);
return '$left $op $right';
}
String visitAdd(HAdd node) => handleInvokeBinary(node, '+');
String visitBitAnd(HBitAnd node) => handleInvokeBinary(node, '&');
String visitBitNot(HBitNot node) {
String operand = temporaryId(node.operand);
return "~$operand";
}
String visitBitOr(HBitOr node) => handleInvokeBinary(node, '|');
String visitBitXor(HBitXor node) => handleInvokeBinary(node, '^');
String visitBoundsCheck(HBoundsCheck node) {
String lengthId = temporaryId(node.length);
String indexId = temporaryId(node.index);
return "Bounds check: length = $lengthId, index = $indexId";
}
String visitBreak(HBreak node) {
HBasicBlock target = currentBlock.successors[0];
if (node.label != null) {
return "Break ${node.label.labelName}: (B${target.id})";
}
return "Break: (B${target.id})";
}
String visitConstant(HConstant constant) => "Constant ${constant.constant}";
String visitContinue(HContinue node) {
HBasicBlock target = currentBlock.successors[0];
if (node.label != null) {
return "Continue ${node.label.labelName}: (B${target.id})";
}
return "Continue: (B${target.id})";
}
String visitDivide(HDivide node) => handleInvokeBinary(node, '/');
String visitExit(HExit node) => "exit";
String visitFieldGet(HFieldGet node) {
if (node.isNullCheck) {
return 'null check on ${temporaryId(node.receiver)}';
}
String fieldName = node.element.name;
return 'field get ${temporaryId(node.receiver)}.$fieldName';
}
String visitFieldSet(HFieldSet node) {
String valueId = temporaryId(node.value);
String fieldName = node.element.name;
return 'field set ${temporaryId(node.receiver)}.$fieldName to $valueId';
}
String visitReadModifyWrite(HReadModifyWrite node) {
String fieldName = node.element.name;
String receiverId = temporaryId(node.receiver);
String op = node.jsOp;
if (node.isAssignOp) {
String valueId = temporaryId(node.value);
return 'field-update $receiverId.$fieldName $op= $valueId';
} else if (node.isPreOp) {
return 'field-update $op$receiverId.$fieldName';
} else {
return 'field-update $receiverId.$fieldName$op';
}
}
String visitLocalGet(HLocalGet node) {
String localName = node.variable.name;
return 'local get ${temporaryId(node.local)}.$localName';
}
String visitLocalSet(HLocalSet node) {
String valueId = temporaryId(node.value);
String localName = node.variable.name;
return 'local set ${temporaryId(node.local)}.$localName to $valueId';
}
String visitGoto(HGoto node) {
HBasicBlock target = currentBlock.successors[0];
return "Goto: (B${target.id})";
}
String visitGreater(HGreater node) => handleInvokeBinary(node, '>');
String visitGreaterEqual(HGreaterEqual node) {
return handleInvokeBinary(node, '>=');
}
String visitIdentity(HIdentity node) => handleInvokeBinary(node, '===');
String visitIf(HIf node) {
HBasicBlock thenBlock = currentBlock.successors[0];
HBasicBlock elseBlock = currentBlock.successors[1];
String conditionId = temporaryId(node.inputs[0]);
return "If ($conditionId): (B${thenBlock.id}) else (B${elseBlock.id})";
}
String visitGenericInvoke(String invokeType, String functionName,
List<HInstruction> arguments) {
StringBuffer argumentsString = new StringBuffer();
for (int i = 0; i < arguments.length; i++) {
if (i != 0) argumentsString.write(", ");
argumentsString.write(temporaryId(arguments[i]));
}
return "$invokeType: $functionName($argumentsString)";
}
String visitIndex(HIndex node) {
String receiver = temporaryId(node.receiver);
String index = temporaryId(node.index);
return "Index: $receiver[$index]";
}
String visitIndexAssign(HIndexAssign node) {
String receiver = temporaryId(node.receiver);
String index = temporaryId(node.index);
String value = temporaryId(node.value);
return "IndexAssign: $receiver[$index] = $value";
}
String visitInterceptor(HInterceptor node) {
String value = temporaryId(node.inputs[0]);
if (node.interceptedClasses != null) {
JavaScriptBackend backend = compiler.backend;
String cls = backend.namer.getInterceptorSuffix(node.interceptedClasses);
return "Intercept ($cls): $value";
}
return "Intercept: $value";
}
String visitInvokeClosure(HInvokeClosure node)
=> visitInvokeDynamic(node, "closure");
String visitInvokeDynamic(HInvokeDynamic invoke, String kind) {
String receiver = temporaryId(invoke.receiver);
String name = invoke.selector.name;
String target = "($kind) $receiver.$name";
int offset = HInvoke.ARGUMENTS_OFFSET;
List arguments = invoke.inputs.sublist(offset);
return visitGenericInvoke("Invoke", target, arguments) +
"(${invoke.selector.mask})";
}
String visitInvokeDynamicMethod(HInvokeDynamicMethod node)
=> visitInvokeDynamic(node, "method");
String visitInvokeDynamicGetter(HInvokeDynamicGetter node)
=> visitInvokeDynamic(node, "get");
String visitInvokeDynamicSetter(HInvokeDynamicSetter node)
=> visitInvokeDynamic(node, "set");
String visitInvokeStatic(HInvokeStatic invoke) {
String target = invoke.element.name;
return visitGenericInvoke("Invoke", target, invoke.inputs);
}
String visitInvokeSuper(HInvokeSuper invoke) {
String target = invoke.element.name;
return visitGenericInvoke("Invoke super", target, invoke.inputs);
}
String visitInvokeConstructorBody(HInvokeConstructorBody invoke) {
String target = invoke.element.name;
return visitGenericInvoke("Invoke constructor body", target, invoke.inputs);
}
String visitForeign(HForeign foreign) {
return visitGenericInvoke("Foreign", "${foreign.codeTemplate.ast}", foreign.inputs);
}
String visitForeignNew(HForeignNew node) {
return visitGenericInvoke("New",
"${node.element.name}",
node.inputs);
}
String visitLess(HLess node) => handleInvokeBinary(node, '<');
String visitLessEqual(HLessEqual node) => handleInvokeBinary(node, '<=');
String visitLiteralList(HLiteralList node) {
StringBuffer elementsString = new StringBuffer();
for (int i = 0; i < node.inputs.length; i++) {
if (i != 0) elementsString.write(", ");
elementsString.write(temporaryId(node.inputs[i]));
}
return "Literal list: [$elementsString]";
}
String visitLoopBranch(HLoopBranch branch) {
HBasicBlock bodyBlock = currentBlock.successors[0];
HBasicBlock exitBlock = currentBlock.successors[1];
String conditionId = temporaryId(branch.inputs[0]);
return "While ($conditionId): (B${bodyBlock.id}) then (B${exitBlock.id})";
}
String visitMultiply(HMultiply node) => handleInvokeBinary(node, '*');
String visitNegate(HNegate node) {
String operand = temporaryId(node.operand);
return "-$operand";
}
String visitNot(HNot node) => "Not: ${temporaryId(node.inputs[0])}";
String visitParameterValue(HParameterValue node) {
return "p${node.sourceElement.name}";
}
String visitLocalValue(HLocalValue node) {
return "l${node.sourceElement.name}";
}
String visitPhi(HPhi phi) {
StringBuffer buffer = new StringBuffer();
buffer.write("Phi(");
for (int i = 0; i < phi.inputs.length; i++) {
if (i > 0) buffer.write(", ");
buffer.write(temporaryId(phi.inputs[i]));
}
buffer.write(")");
return buffer.toString();
}
String visitReturn(HReturn node) => "Return ${temporaryId(node.inputs[0])}";
String visitShiftLeft(HShiftLeft node) => handleInvokeBinary(node, '<<');
String visitShiftRight(HShiftRight node) => handleInvokeBinary(node, '>>');
String visitStatic(HStatic node)
=> "Static ${node.element.name}";
String visitLazyStatic(HLazyStatic node)
=> "LazyStatic ${node.element.name}";
String visitOneShotInterceptor(HOneShotInterceptor node)
=> visitInvokeDynamic(node, "one shot interceptor");
String visitStaticStore(HStaticStore node) {
String lhs = node.element.name;
return "Static $lhs = ${temporaryId(node.inputs[0])}";
}
String visitStringConcat(HStringConcat node) {
var leftId = temporaryId(node.left);
var rightId = temporaryId(node.right);
return "StringConcat: $leftId + $rightId";
}
String visitStringify(HStringify node) {
return "Stringify ${temporaryId(node.inputs[0])}";
}
String visitSubtract(HSubtract node) => handleInvokeBinary(node, '-');
String visitSwitch(HSwitch node) {
StringBuffer buf = new StringBuffer();
buf.write("Switch: (");
buf.write(temporaryId(node.inputs[0]));
buf.write(") ");
for (int i = 1; i < node.inputs.length; i++) {
buf.write(temporaryId(node.inputs[i]));
buf.write(": B");
buf.write(node.block.successors[i - 1].id);
buf.write(", ");
}
buf.write("default: B");
buf.write(node.defaultTarget.id);
return buf.toString();
}
String visitThis(HThis node) => "this";
String visitThrow(HThrow node) => "Throw ${temporaryId(node.inputs[0])}";
String visitThrowExpression(HThrowExpression node) {
return "ThrowExpression ${temporaryId(node.inputs[0])}";
}
String visitTruncatingDivide(HTruncatingDivide node) {
return handleInvokeBinary(node, '~/');
}
String visitExitTry(HExitTry node) {
return "Exit try";
}
String visitTry(HTry node) {
List<HBasicBlock> successors = currentBlock.successors;
String tryBlock = 'B${successors[0].id}';
String catchBlock = 'none';
if (node.catchBlock != null) {
catchBlock = 'B${successors[1].id}';
}
String finallyBlock = 'none';
if (node.finallyBlock != null) {
finallyBlock = 'B${node.finallyBlock.id}';
}
return "Try: $tryBlock, Catch: $catchBlock, Finally: $finallyBlock, "
"Join: B${successors.last.id}";
}
String visitIs(HIs node) {
String type = node.typeExpression.toString();
return "TypeTest: ${temporaryId(node.expression)} is $type";
}
String visitIsViaInterceptor(HIsViaInterceptor node) {
String type = node.typeExpression.toString();
return "TypeTest: ${temporaryId(node.inputs[0])} is $type";
}
String visitTypeConversion(HTypeConversion node) {
assert(node.inputs.length <= 2);
String otherInput = (node.inputs.length == 2)
? temporaryId(node.inputs[1])
: '';
return "TypeConversion: ${temporaryId(node.checkedInput)} to "
"${node.instructionType} $otherInput";
}
String visitTypeKnown(HTypeKnown node) {
assert(node.inputs.length <= 2);
String result =
"TypeKnown: ${temporaryId(node.checkedInput)} is ${node.knownType}";
if (node.witness != null) {
result += " witnessed by ${temporaryId(node.witness)}";
}
return result;
}
String visitRangeConversion(HRangeConversion node) {
return "RangeConversion: ${node.checkedInput}";
}
String visitReadTypeVariable(HReadTypeVariable node) {
return "ReadTypeVariable: ${node.dartType} ${node.hasReceiver}";
}
String visitFunctionType(HFunctionType node) {
return "FunctionType: ${node.dartType}";
}
String visitVoidType(HVoidType node) {
return "VoidType";
}
String visitInterfaceType(HInterfaceType node) {
return "InterfaceType: ${node.dartType}";
}
String visitDynamicType(HDynamicType node) {
return "DynamicType";
}
String visitAwait(HAwait node) {
return "await ${temporaryId(node.inputs[0])}";
}
String visitYield(HYield node) {
return "yield${node.hasStar ? "*" : ""} ${temporaryId(node.inputs[0])}";
}
}