package org.graalvm.compiler.printer;
import static java.lang.Character.toLowerCase;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import jdk.internal.vm.compiler.collections.UnmodifiableMapCursor;
import org.graalvm.compiler.bytecode.Bytecode;
import org.graalvm.compiler.bytecode.BytecodeDisassembler;
import org.graalvm.compiler.core.common.alloc.Trace;
import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph;
import org.graalvm.compiler.core.gen.NodeLIRBuilder;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.NodeBitMap;
import org.graalvm.compiler.graph.NodeMap;
import org.graalvm.compiler.graph.Position;
import org.graalvm.compiler.java.BciBlockMapping;
import org.graalvm.compiler.lir.LIR;
import org.graalvm.compiler.lir.LIRInstruction;
import org.graalvm.compiler.lir.debug.IntervalDumper;
import org.graalvm.compiler.lir.debug.IntervalDumper.IntervalVisitor;
import org.graalvm.compiler.lir.gen.LIRGenerationResult;
import org.graalvm.compiler.nodeinfo.Verbosity;
import org.graalvm.compiler.nodes.AbstractBeginNode;
import org.graalvm.compiler.nodes.AbstractEndNode;
import org.graalvm.compiler.nodes.AbstractMergeNode;
import org.graalvm.compiler.nodes.FixedNode;
import org.graalvm.compiler.nodes.FixedWithNextNode;
import org.graalvm.compiler.nodes.FrameState;
import org.graalvm.compiler.nodes.PhiNode;
import org.graalvm.compiler.nodes.StateSplit;
import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.ValuePhiNode;
import org.graalvm.compiler.nodes.calc.FloatingNode;
import org.graalvm.compiler.nodes.cfg.Block;
import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
import jdk.vm.ci.code.DebugInfo;
import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.Value;
class CFGPrinter extends CompilationPrinter {
protected TargetDescription target;
protected LIR lir;
protected NodeLIRBuilder nodeLirGenerator;
protected ControlFlowGraph cfg;
protected ScheduleResult schedule;
protected ResolvedJavaMethod method;
protected LIRGenerationResult res;
CFGPrinter(OutputStream out) {
super(out);
}
public void printCFG(String label, BciBlockMapping blockMap) {
begin("cfg");
out.print("name \"").print(label).println('"');
for (BciBlockMapping.BciBlock block : blockMap.getBlocks()) {
begin("block");
printBlock(block);
end("block");
}
end("cfg");
}
private void printBlock(BciBlockMapping.BciBlock block) {
out.print("name \"B").print(block.getStartBci()).println('"');
out.print("from_bci ").println(block.getStartBci());
out.print("to_bci ").println(block.getEndBci());
out.println("predecessors ");
out.print("successors ");
for (BciBlockMapping.BciBlock succ : block.getSuccessors()) {
if (!succ.isExceptionEntry()) {
out.print("\"B").print(succ.getStartBci()).print("\" ");
}
}
out.println();
out.print("xhandlers");
for (BciBlockMapping.BciBlock succ : block.getSuccessors()) {
if (succ.isExceptionEntry()) {
out.print("\"B").print(succ.getStartBci()).print("\" ");
}
}
out.println();
out.print("flags ");
if (block.isExceptionEntry()) {
out.print("\"ex\" ");
}
if (block.isLoopHeader()) {
out.print("\"plh\" ");
}
out.println();
out.print("loop_depth ").println(Long.bitCount(block.getLoops()));
}
private NodeMap<Block> latestScheduling;
private NodeBitMap printedNodes;
private boolean inFixedSchedule(Node node) {
return lir != null || schedule != null || node.isDeleted() || cfg.getNodeToBlock().get(node) != null;
}
public void printCFG(String label, AbstractBlockBase<?>[] blocks, boolean printNodes) {
if (lir == null) {
latestScheduling = new NodeMap<>(cfg.getNodeToBlock());
for (AbstractBlockBase<?> abstractBlock : blocks) {
if (abstractBlock == null) {
continue;
}
Block block = (Block) abstractBlock;
Node cur = block.getBeginNode();
while (true) {
assert inFixedSchedule(cur) && latestScheduling.get(cur) == block;
scheduleInputs(cur, block);
if (cur == block.getEndNode()) {
break;
}
assert cur.successors().count() == 1;
cur = cur.successors().first();
}
}
}
begin("cfg");
out.print("name \"").print(label).println('"');
for (AbstractBlockBase<?> block : blocks) {
printBlock(block, printNodes);
}
end("cfg");
if (method != null) {
printBytecodes(new BytecodeDisassembler(false).disassemble(method));
}
latestScheduling = null;
}
private void scheduleInputs(Node node, Block nodeBlock) {
if (node instanceof ValuePhiNode) {
PhiNode phi = (PhiNode) node;
Block phiBlock = latestScheduling.get(phi.merge());
assert phiBlock != null;
for (Block pred : phiBlock.getPredecessors()) {
schedule(phi.valueAt((AbstractEndNode) pred.getEndNode()), pred);
}
} else {
for (Node input : node.inputs()) {
schedule(input, nodeBlock);
}
}
}
private void schedule(Node input, Block block) {
if (!inFixedSchedule(input)) {
Block inputBlock = block;
if (latestScheduling.get(input) != null) {
inputBlock = AbstractControlFlowGraph.commonDominatorTyped(inputBlock, latestScheduling.get(input));
}
if (inputBlock != latestScheduling.get(input)) {
latestScheduling.set(input, inputBlock);
scheduleInputs(input, inputBlock);
}
}
}
private void printBlock(AbstractBlockBase<?> block, boolean printNodes) {
if (block == null) {
return;
}
printBlockProlog(block);
if (printNodes) {
assert block instanceof Block;
printNodes((Block) block);
}
printBlockEpilog(block);
}
private void printBlockEpilog(AbstractBlockBase<?> block) {
printLIR(block);
end("block");
}
private void printBlockProlog(AbstractBlockBase<?> block) {
begin("block");
out.print("name \"").print(blockToString(block)).println('"');
out.println("from_bci -1");
out.println("to_bci -1");
out.print("predecessors ");
for (AbstractBlockBase<?> pred : block.getPredecessors()) {
out.print("\"").print(blockToString(pred)).print("\" ");
}
out.println();
out.print("successors ");
for (AbstractBlockBase<?> succ : block.getSuccessors()) {
if (!succ.isExceptionEntry()) {
out.print("\"").print(blockToString(succ)).print("\" ");
}
}
out.println();
out.print("xhandlers");
for (AbstractBlockBase<?> succ : block.getSuccessors()) {
if (succ.isExceptionEntry()) {
out.print("\"").print(blockToString(succ)).print("\" ");
}
}
out.println();
out.print("flags ");
if (block.isLoopHeader()) {
out.print("\"llh\" ");
}
if (block.isLoopEnd()) {
out.print("\"lle\" ");
}
if (block.isExceptionEntry()) {
out.print("\"ex\" ");
}
out.println();
if (block.getLoop() != null) {
out.print("loop_index ").println(block.getLoop().getIndex());
out.print("loop_depth ").println(block.getLoop().getDepth());
}
out.print("probability ").println(Double.doubleToRawLongBits(block.getRelativeFrequency()));
}
private void printNodes(Block block) {
printedNodes = new NodeBitMap(cfg.graph);
begin("IR");
out.println("HIR");
out.disableIndentation();
if (block.getBeginNode() instanceof AbstractMergeNode) {
for (ValueNode phi : ((AbstractMergeNode) block.getBeginNode()).phis()) {
printNode(phi, false);
}
}
Node cur = block.getBeginNode();
while (true) {
printNode(cur, false);
if (cur == block.getEndNode()) {
UnmodifiableMapCursor<Node, Block> cursor = latestScheduling.getEntries();
while (cursor.advance()) {
if (cursor.getValue() == block && !inFixedSchedule(cursor.getKey()) && !printedNodes.isMarked(cursor.getKey())) {
printNode(cursor.getKey(), true);
}
}
break;
}
assert cur.successors().count() == 1;
cur = cur.successors().first();
}
out.enableIndentation();
end("IR");
printedNodes = null;
}
private void printNode(Node node, boolean unscheduled) {
assert !printedNodes.isMarked(node);
printedNodes.mark(node);
if (!(node instanceof ValuePhiNode)) {
for (Node input : node.inputs()) {
if (!inFixedSchedule(input) && !printedNodes.isMarked(input)) {
printNode(input, true);
}
}
}
if (unscheduled) {
assert lir == null && schedule == null : "unscheduled nodes can only be present before LIR generation";
out.print("f ").print(HOVER_START).print("u").print(HOVER_SEP).print("unscheduled").print(HOVER_END).println(COLUMN_END);
} else if (node instanceof FixedWithNextNode) {
out.print("f ").print(HOVER_START).print("#").print(HOVER_SEP).print("fixed with next").print(HOVER_END).println(COLUMN_END);
} else if (node instanceof FixedNode) {
out.print("f ").print(HOVER_START).print("*").print(HOVER_SEP).print("fixed").print(HOVER_END).println(COLUMN_END);
} else if (node instanceof FloatingNode) {
out.print("f ").print(HOVER_START).print("~").print(HOVER_SEP).print("floating").print(HOVER_END).println(COLUMN_END);
}
out.print("tid ").print(nodeToString(node)).println(COLUMN_END);
if (nodeLirGenerator != null) {
Value operand = nodeLirGenerator.hasOperand(node) ? nodeLirGenerator.operand(node) : null;
if (operand != null) {
out.print("result ").print(operand.toString()).println(COLUMN_END);
}
}
if (node instanceof StateSplit) {
StateSplit stateSplit = (StateSplit) node;
if (stateSplit.stateAfter() != null) {
String state = stateToString(stateSplit.stateAfter());
out.print("st ").print(HOVER_START).print("st").print(HOVER_SEP).print(state).print(HOVER_END).println(COLUMN_END);
}
}
Map<Object, Object> props = new TreeMap<>(node.getDebugProperties());
out.print("d ").print(HOVER_START).print("d").print(HOVER_SEP);
out.println("=== Debug Properties ===");
for (Map.Entry<Object, Object> entry : props.entrySet()) {
out.print(entry.getKey().toString()).print(": ").print(entry.getValue() == null ? "[null]" : entry.getValue().toString()).println();
}
out.println("=== Inputs ===");
printNamedNodes(node, node.inputPositions().iterator(), "", "\n", null);
out.println("=== Succesors ===");
printNamedNodes(node, node.successorPositions().iterator(), "", "\n", null);
out.println("=== Usages ===");
if (!node.hasNoUsages()) {
for (Node usage : node.usages()) {
out.print(nodeToString(usage)).print(" ");
}
out.println();
}
out.println("=== Predecessor ===");
out.print(nodeToString(node.predecessor())).print(" ");
out.print(HOVER_END).println(COLUMN_END);
out.print("instruction ");
out.print(HOVER_START).print(node.getNodeClass().shortName()).print(HOVER_SEP).print(node.getClass().getName()).print(HOVER_END).print(" ");
printNamedNodes(node, node.inputPositions().iterator(), "", "", "#NDF");
printNamedNodes(node, node.successorPositions().iterator(), "#", "", "#NDF");
for (Map.Entry<Object, Object> entry : props.entrySet()) {
String key = entry.getKey().toString();
if (key.startsWith("data.") && !key.equals("data.stamp")) {
out.print(key.substring("data.".length())).print(": ").print(entry.getValue() == null ? "[null]" : entry.getValue().toString()).print(" ");
}
}
out.print(COLUMN_END).print(' ').println(COLUMN_END);
}
private void printNamedNodes(Node node, Iterator<Position> iter, String prefix, String suffix, String hideSuffix) {
int lastIndex = -1;
while (iter.hasNext()) {
Position pos = iter.next();
if (hideSuffix != null && pos.getName().endsWith(hideSuffix)) {
continue;
}
if (pos.getIndex() != lastIndex) {
if (lastIndex != -1) {
out.print(suffix);
}
out.print(prefix).print(pos.getName()).print(": ");
lastIndex = pos.getIndex();
}
out.print(nodeToString(pos.get(node))).print(" ");
}
if (lastIndex != -1) {
out.print(suffix);
}
}
private String stateToString(FrameState state) {
StringBuilder buf = new StringBuilder();
FrameState curState = state;
do {
buf.append(Bytecode.toLocation(curState.getCode(), curState.bci)).append('\n');
if (curState.stackSize() > 0) {
buf.append("stack: ");
for (int i = 0; i < curState.stackSize(); i++) {
buf.append(stateValueToString(curState.stackAt(i))).append(' ');
}
buf.append("\n");
}
buf.append("locals: ");
for (int i = 0; i < curState.localsSize(); i++) {
buf.append(stateValueToString(curState.localAt(i))).append(' ');
}
buf.append("\n");
buf.append("locks: ");
for (int i = 0; i < curState.locksSize(); i++) {
buf.append(stateValueToString(curState.lockAt(i))).append(' ');
}
buf.append("\n");
curState = curState.outerFrameState();
} while (curState != null);
return buf.toString();
}
private String stateValueToString(ValueNode value) {
String result = nodeToString(value);
if (nodeLirGenerator != null && value != null && nodeLirGenerator.hasOperand(value)) {
Value operand = nodeLirGenerator.operand(value);
assert operand != null;
result += ": " + operand;
}
return result;
}
private void printLIR(AbstractBlockBase<?> block) {
if (lir == null) {
return;
}
ArrayList<LIRInstruction> lirInstructions = lir.getLIRforBlock(block);
if (lirInstructions == null) {
return;
}
begin("IR");
out.println("LIR");
for (int i = 0; i < lirInstructions.size(); i++) {
LIRInstruction inst = lirInstructions.get(i);
printLIRInstruction(inst);
}
end("IR");
}
private void printLIRInstruction(LIRInstruction inst) {
if (inst == null) {
out.print("nr -1 ").print(COLUMN_END).print(" instruction ").print("<deleted>").print(COLUMN_END);
out.println(COLUMN_END);
} else {
out.printf("nr %4d ", inst.id()).print(COLUMN_END);
final StringBuilder stateString = new StringBuilder();
inst.forEachState(state -> {
if (state.hasDebugInfo()) {
DebugInfo di = state.debugInfo();
stateString.append(debugInfoToString(di.getBytecodePosition(), di.getReferenceMap(), state.getLiveBasePointers(), di.getCalleeSaveInfo()));
} else {
stateString.append(debugInfoToString(state.topFrame, null, state.getLiveBasePointers(), null));
}
});
if (stateString.length() > 0) {
int level = out.indentationLevel();
out.adjustIndentation(-level);
out.print(" st ").print(HOVER_START).print("st").print(HOVER_SEP).print(stateString.toString()).print(HOVER_END).print(COLUMN_END);
out.adjustIndentation(level);
}
out.print(" instruction ").print(inst.toString(res)).print(COLUMN_END);
out.println(COLUMN_END);
}
}
private String nodeToString(Node node) {
if (node == null) {
return "-";
}
String prefix;
if (node instanceof AbstractBeginNode && (lir == null && schedule == null)) {
prefix = "B";
} else if (node instanceof ValueNode) {
ValueNode value = (ValueNode) node;
if (value.getStackKind() == JavaKind.Illegal) {
prefix = "v";
} else {
prefix = String.valueOf(toLowerCase(value.getStackKind().getTypeChar()));
}
} else {
prefix = "?";
}
return prefix + node.toString(Verbosity.Id);
}
private String blockToString(AbstractBlockBase<?> block) {
if (lir == null && schedule == null && block instanceof Block) {
return "B" + ((Block) block).getBeginNode().toString(Verbosity.Id);
} else {
return "B" + block.getId();
}
}
IntervalVisitor intervalVisitor = new IntervalVisitor() {
String getFormattedOperand(Value operand) {
String s = operand.toString();
int last = s.lastIndexOf('|');
if (last != -1) {
return s.substring(0, last) + "|" + operand.getPlatformKind().getTypeChar();
}
return s;
}
@Override
public void visitIntervalStart(Value parentOperand, Value splitOperand, Value location, Value hint, String typeName) {
out.printf("%s %s ", getFormattedOperand(splitOperand), typeName);
if (location != null) {
out.printf("\"[%s]\"", getFormattedOperand(location));
} else {
out.printf("\"[%s]\"", getFormattedOperand(splitOperand));
}
out.printf(" %s %s ", getFormattedOperand(parentOperand), hint != null ? getFormattedOperand(hint) : -1);
}
@Override
public void visitRange(int from, int to) {
out.printf("[%d, %d[", from, to);
}
@Override
public void visitUsePos(int usePos, Object registerPriority) {
out.printf("%d %s ", usePos, registerPriority);
}
@Override
public void visitIntervalEnd(Object spillState) {
out.printf(" \"%s\"", spillState);
out.println();
}
};
public void printIntervals(String label, IntervalDumper intervals) {
begin("intervals");
out.println(String.format("name \"%s\"", label));
intervals.visitIntervals(intervalVisitor);
end("intervals");
}
public void printSchedule(String message, ScheduleResult theSchedule) {
schedule = theSchedule;
cfg = schedule.getCFG();
printedNodes = new NodeBitMap(cfg.graph);
begin("cfg");
out.print("name \"").print(message).println('"');
for (Block b : schedule.getCFG().getBlocks()) {
if (schedule.nodesFor(b) != null) {
printScheduledBlock(b, schedule.nodesFor(b));
}
}
end("cfg");
schedule = null;
cfg = null;
printedNodes = null;
}
private void printScheduledBlock(Block block, List<Node> nodesFor) {
printBlockProlog(block);
begin("IR");
out.println("HIR");
out.disableIndentation();
if (block.getBeginNode() instanceof AbstractMergeNode) {
for (ValueNode phi : ((AbstractMergeNode) block.getBeginNode()).phis()) {
printNode(phi, false);
}
}
for (Node n : nodesFor) {
printNode(n, false);
}
out.enableIndentation();
end("IR");
printBlockEpilog(block);
}
public void printTraces(String label, TraceBuilderResult traces) {
begin("cfg");
out.print("name \"").print(label).println('"');
for (Trace trace : traces.getTraces()) {
printTrace(trace, traces);
}
end("cfg");
}
private void printTrace(Trace trace, TraceBuilderResult traceBuilderResult) {
printTraceProlog(trace, traceBuilderResult);
printTraceInstructions(trace, traceBuilderResult);
printTraceEpilog();
}
private void printTraceProlog(Trace trace, TraceBuilderResult traceBuilderResult) {
begin("block");
out.print("name \"").print(traceToString(trace)).println('"');
out.println("from_bci -1");
out.println("to_bci -1");
out.print("predecessors ");
for (Trace pred : getPredecessors(trace, traceBuilderResult)) {
out.print("\"").print(traceToString(pred)).print("\" ");
}
out.println();
out.print("successors ");
for (Trace succ : getSuccessors(trace, traceBuilderResult)) {
out.print("\"").print(traceToString(succ)).print("\" ");
}
out.println();
out.print("xhandlers");
out.println();
out.print("flags ");
out.println();
}
private void printTraceInstructions(Trace trace, TraceBuilderResult traceBuilderResult) {
if (lir == null) {
return;
}
begin("IR");
out.println("LIR");
for (AbstractBlockBase<?> block : trace.getBlocks()) {
ArrayList<LIRInstruction> lirInstructions = lir.getLIRforBlock(block);
if (lirInstructions == null) {
continue;
}
printBlockInstruction(block, traceBuilderResult);
for (int i = 0; i < lirInstructions.size(); i++) {
LIRInstruction inst = lirInstructions.get(i);
printLIRInstruction(inst);
}
}
end("IR");
}
private void printBlockInstruction(AbstractBlockBase<?> block, TraceBuilderResult traceBuilderResult) {
out.print("nr ").print(block.toString()).print(COLUMN_END).print(" instruction ");
if (block.getPredecessorCount() > 0) {
out.print("<- ");
printBlockListWithTrace(Arrays.asList(block.getPredecessors()), traceBuilderResult);
out.print(" ");
}
if (block.getSuccessorCount() > 0) {
out.print("-> ");
printBlockListWithTrace(Arrays.asList(block.getSuccessors()), traceBuilderResult);
}
out.print(COLUMN_END);
out.println(COLUMN_END);
}
private void printBlockListWithTrace(List<? extends AbstractBlockBase<?>> blocks, TraceBuilderResult traceBuilderResult) {
Iterator<? extends AbstractBlockBase<?>> it = blocks.iterator();
printBlockWithTrace(it.next(), traceBuilderResult);
while (it.hasNext()) {
out.print(",");
printBlockWithTrace(it.next(), traceBuilderResult);
}
}
private void printBlockWithTrace(AbstractBlockBase<?> block, TraceBuilderResult traceBuilderResult) {
out.print(block.toString());
out.print("[T").print(traceBuilderResult.getTraceForBlock(block).getId()).print("]");
}
private void printTraceEpilog() {
end("block");
}
private static boolean isLoopBackEdge(AbstractBlockBase<?> src, AbstractBlockBase<?> dst) {
return dst.isLoopHeader() && dst.getLoop().equals(src.getLoop());
}
private static List<Trace> getSuccessors(Trace trace, TraceBuilderResult traceBuilderResult) {
BitSet bs = new BitSet(traceBuilderResult.getTraces().size());
for (AbstractBlockBase<?> block : trace.getBlocks()) {
for (AbstractBlockBase<?> s : block.getSuccessors()) {
Trace otherTrace = traceBuilderResult.getTraceForBlock(s);
int otherTraceId = otherTrace.getId();
if (trace.getId() != otherTraceId || isLoopBackEdge(block, s)) {
bs.set(otherTraceId);
}
}
}
List<Trace> succ = new ArrayList<>();
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
succ.add(traceBuilderResult.getTraces().get(i));
}
return succ;
}
private static List<Trace> getPredecessors(Trace trace, TraceBuilderResult traceBuilderResult) {
BitSet bs = new BitSet(traceBuilderResult.getTraces().size());
for (AbstractBlockBase<?> block : trace.getBlocks()) {
for (AbstractBlockBase<?> p : block.getPredecessors()) {
Trace otherTrace = traceBuilderResult.getTraceForBlock(p);
int otherTraceId = otherTrace.getId();
if (trace.getId() != otherTraceId || isLoopBackEdge(p, block)) {
bs.set(traceBuilderResult.getTraceForBlock(p).getId());
}
}
}
List<Trace> pred = new ArrayList<>();
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
pred.add(traceBuilderResult.getTraces().get(i));
}
return pred;
}
private static String traceToString(Trace trace) {
return new StringBuilder("T").append(trace.getId()).toString();
}
}