blob: e6548a27c312901177d143990fef488a3e7b7bad [file] [log] [blame]
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.graalvm.compiler.printer;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
import org.graalvm.compiler.bytecode.BytecodeDisassembler;
import org.graalvm.compiler.debug.GraalDebugConfig.Options;
import org.graalvm.compiler.graph.Graph;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.NodeMap;
import org.graalvm.compiler.graph.Position;
import org.graalvm.compiler.nodeinfo.Verbosity;
import org.graalvm.compiler.nodes.AbstractMergeNode;
import org.graalvm.compiler.nodes.BeginNode;
import org.graalvm.compiler.nodes.ConstantNode;
import org.graalvm.compiler.nodes.EndNode;
import org.graalvm.compiler.nodes.ParameterNode;
import org.graalvm.compiler.nodes.PhiNode;
import org.graalvm.compiler.nodes.StateSplit;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
import org.graalvm.compiler.nodes.cfg.Block;
import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
import org.graalvm.compiler.phases.schedule.SchedulePhase;
import jdk.vm.ci.meta.ResolvedJavaMethod;
/**
* Generates a representation of {@link Graph Graphs} that can be visualized and inspected with the
* <a href="http://kenai.com/projects/igv">Ideal Graph Visualizer</a>.
*/
public class IdealGraphPrinter extends BasicIdealGraphPrinter implements GraphPrinter {
private final boolean tryToSchedule;
private final SnippetReflectionProvider snippetReflection;
/**
* Creates a new {@link IdealGraphPrinter} that writes to the specified output stream.
*
* @param tryToSchedule If false, no scheduling is done, which avoids exceptions for
* non-schedulable graphs.
*/
public IdealGraphPrinter(OutputStream stream, boolean tryToSchedule, SnippetReflectionProvider snippetReflection) {
super(stream);
this.begin();
this.tryToSchedule = tryToSchedule;
this.snippetReflection = snippetReflection;
}
@Override
public SnippetReflectionProvider getSnippetReflectionProvider() {
return snippetReflection;
}
/**
* Starts a new group of graphs with the given name, short name and method byte code index (BCI)
* as properties.
*/
@Override
public void beginGroup(String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) {
beginGroup();
beginProperties();
printProperty("name", name);
if (properties != null) {
for (Entry<Object, Object> entry : properties.entrySet()) {
printProperty(entry.getKey().toString(), entry.getValue().toString());
}
}
endProperties();
beginMethod(name, shortName, bci);
if (method != null && method.getCode() != null) {
printBytecodes(new BytecodeDisassembler(false).disassemble(method));
}
endMethod();
}
/**
* Prints an entire {@link Graph} with the specified title, optionally using short names for
* nodes.
*/
@Override
public void print(Graph graph, String title, Map<Object, Object> properties) {
beginGraph(title);
Set<Node> noBlockNodes = Node.newSet();
ScheduleResult schedule = null;
if (graph instanceof StructuredGraph) {
StructuredGraph structuredGraph = (StructuredGraph) graph;
schedule = structuredGraph.getLastSchedule();
if (schedule == null && tryToSchedule) {
if (Options.PrintIdealGraphSchedule.getValue()) {
try {
SchedulePhase schedulePhase = new SchedulePhase();
schedulePhase.apply(structuredGraph);
schedule = structuredGraph.getLastSchedule();
} catch (Throwable t) {
}
}
}
}
ControlFlowGraph cfg = schedule == null ? null : schedule.getCFG();
if (properties != null) {
beginProperties();
for (Entry<Object, Object> entry : properties.entrySet()) {
printProperty(entry.getKey().toString(), entry.getValue().toString());
}
endProperties();
}
beginNodes();
List<Edge> edges = printNodes(graph, cfg == null ? null : cfg.getNodeToBlock(), noBlockNodes);
endNodes();
beginEdges();
for (Edge edge : edges) {
printEdge(edge);
}
endEdges();
if (cfg != null && cfg.getBlocks() != null) {
beginControlFlow();
for (Block block : cfg.getBlocks()) {
printBlock(graph, block, cfg.getNodeToBlock());
}
printNoBlock(noBlockNodes);
endControlFlow();
}
endGraph();
flush();
}
private List<Edge> printNodes(Graph graph, NodeMap<Block> nodeToBlock, Set<Node> noBlockNodes) {
ArrayList<Edge> edges = new ArrayList<>();
NodeMap<Set<Entry<String, Integer>>> colors = graph.createNodeMap();
NodeMap<Set<Entry<String, String>>> colorsToString = graph.createNodeMap();
NodeMap<Set<String>> bits = graph.createNodeMap();
for (Node node : graph.getNodes()) {
beginNode(node.toString(Verbosity.Id));
beginProperties();
printProperty("idx", node.toString(Verbosity.Id));
Map<Object, Object> props = node.getDebugProperties();
if (!props.containsKey("name") || props.get("name").toString().trim().length() == 0) {
String name = node.toString(Verbosity.Name);
printProperty("name", name);
}
printProperty("class", node.getClass().getSimpleName());
Block block = nodeToBlock == null || nodeToBlock.isNew(node) ? null : nodeToBlock.get(node);
if (block != null) {
printProperty("block", Integer.toString(block.getId()));
// if (!(node instanceof PhiNode || node instanceof FrameState || node instanceof
// ParameterNode) && !block.nodes().contains(node)) {
// printProperty("notInOwnBlock", "true");
// }
} else {
printProperty("block", "noBlock");
noBlockNodes.add(node);
}
Set<Entry<String, Integer>> nodeColors = colors.get(node);
if (nodeColors != null) {
for (Entry<String, Integer> color : nodeColors) {
String name = color.getKey();
Integer value = color.getValue();
printProperty(name, Integer.toString(value));
}
}
Set<Entry<String, String>> nodeColorStrings = colorsToString.get(node);
if (nodeColorStrings != null) {
for (Entry<String, String> color : nodeColorStrings) {
String name = color.getKey();
String value = color.getValue();
printProperty(name, value);
}
}
Set<String> nodeBits = bits.get(node);
if (nodeBits != null) {
for (String bit : nodeBits) {
printProperty(bit, "true");
}
}
if (node instanceof BeginNode) {
printProperty("shortName", "B");
} else if (node.getClass() == EndNode.class) {
printProperty("shortName", "E");
} else if (node instanceof ConstantNode) {
ConstantNode cn = (ConstantNode) node;
updateStringPropertiesForConstant(props, cn);
}
if (node.predecessor() != null) {
printProperty("hasPredecessor", "true");
}
for (Entry<Object, Object> entry : props.entrySet()) {
String key = entry.getKey().toString();
Object value = entry.getValue();
String valueString;
if (value == null) {
valueString = "null";
} else {
Class<?> type = value.getClass();
if (type.isArray()) {
if (!type.getComponentType().isPrimitive()) {
valueString = Arrays.toString((Object[]) value);
} else if (type.getComponentType() == Integer.TYPE) {
valueString = Arrays.toString((int[]) value);
} else if (type.getComponentType() == Double.TYPE) {
valueString = Arrays.toString((double[]) value);
} else {
valueString = toString();
}
} else {
valueString = value.toString();
}
}
printProperty(key, valueString);
}
endProperties();
endNode();
// successors
int fromIndex = 0;
for (Position position : node.successorPositions()) {
Node successor = position.get(node);
if (successor != null) {
edges.add(new Edge(node.toString(Verbosity.Id), fromIndex, successor.toString(Verbosity.Id), 0, position.getName()));
}
fromIndex++;
}
// inputs
int toIndex = 1;
for (Position position : node.inputPositions()) {
Node input = position.get(node);
if (input != null) {
edges.add(new Edge(input.toString(Verbosity.Id), input.successors().count(), node.toString(Verbosity.Id), toIndex, position.getName()));
}
toIndex++;
}
}
return edges;
}
private void printBlock(Graph graph, Block block, NodeMap<Block> nodeToBlock) {
beginBlock(Integer.toString(block.getId()));
beginSuccessors();
for (Block sux : block.getSuccessors()) {
if (sux != null) {
printSuccessor(Integer.toString(sux.getId()));
}
}
endSuccessors();
beginBlockNodes();
Set<Node> nodes = Node.newSet();
if (nodeToBlock != null) {
for (Node n : graph.getNodes()) {
Block blk = nodeToBlock.isNew(n) ? null : nodeToBlock.get(n);
if (blk == block) {
nodes.add(n);
}
}
}
if (nodes.size() > 0) {
// if this is the first block: add all locals to this block
if (block.getBeginNode() == ((StructuredGraph) graph).start()) {
for (Node node : graph.getNodes()) {
if (node instanceof ParameterNode) {
nodes.add(node);
}
}
}
Set<Node> snapshot = Node.newSet(nodes);
// add all framestates and phis to their blocks
for (Node node : snapshot) {
if (node instanceof StateSplit && ((StateSplit) node).stateAfter() != null) {
nodes.add(((StateSplit) node).stateAfter());
}
if (node instanceof AbstractMergeNode) {
for (PhiNode phi : ((AbstractMergeNode) node).phis()) {
nodes.add(phi);
}
}
}
for (Node node : nodes) {
printBlockNode(node.toString(Verbosity.Id));
}
}
endBlockNodes();
endBlock();
}
private void printNoBlock(Set<Node> noBlockNodes) {
if (!noBlockNodes.isEmpty()) {
beginBlock("noBlock");
beginBlockNodes();
for (Node node : noBlockNodes) {
printBlockNode(node.toString(Verbosity.Id));
}
endBlockNodes();
endBlock();
}
}
}