1 /*
   2  * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 
  25 package org.graalvm.compiler.printer;
  26 
  27 import static java.lang.Character.toLowerCase;
  28 
  29 import java.io.OutputStream;
  30 import java.util.ArrayList;
  31 import java.util.Arrays;
  32 import java.util.BitSet;
  33 import java.util.Iterator;
  34 import java.util.List;
  35 import java.util.Map;
  36 import java.util.TreeMap;
  37 
  38 import jdk.internal.vm.compiler.collections.UnmodifiableMapCursor;
  39 import org.graalvm.compiler.bytecode.Bytecode;
  40 import org.graalvm.compiler.bytecode.BytecodeDisassembler;
  41 import org.graalvm.compiler.core.common.alloc.Trace;
  42 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
  43 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
  44 import org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph;
  45 import org.graalvm.compiler.core.gen.NodeLIRBuilder;
  46 import org.graalvm.compiler.graph.Node;
  47 import org.graalvm.compiler.graph.NodeBitMap;
  48 import org.graalvm.compiler.graph.NodeMap;
  49 import org.graalvm.compiler.graph.Position;
  50 import org.graalvm.compiler.java.BciBlockMapping;
  51 import org.graalvm.compiler.lir.LIR;
  52 import org.graalvm.compiler.lir.LIRInstruction;
  53 import org.graalvm.compiler.lir.alloc.trace.GlobalLivenessInfo;
  54 import org.graalvm.compiler.lir.debug.IntervalDumper;
  55 import org.graalvm.compiler.lir.debug.IntervalDumper.IntervalVisitor;
  56 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
  57 import org.graalvm.compiler.nodeinfo.Verbosity;
  58 import org.graalvm.compiler.nodes.AbstractBeginNode;
  59 import org.graalvm.compiler.nodes.AbstractEndNode;
  60 import org.graalvm.compiler.nodes.AbstractMergeNode;
  61 import org.graalvm.compiler.nodes.FixedNode;
  62 import org.graalvm.compiler.nodes.FixedWithNextNode;
  63 import org.graalvm.compiler.nodes.FrameState;
  64 import org.graalvm.compiler.nodes.PhiNode;
  65 import org.graalvm.compiler.nodes.StateSplit;
  66 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
  67 import org.graalvm.compiler.nodes.ValueNode;
  68 import org.graalvm.compiler.nodes.ValuePhiNode;
  69 import org.graalvm.compiler.nodes.calc.FloatingNode;
  70 import org.graalvm.compiler.nodes.cfg.Block;
  71 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
  72 
  73 import jdk.vm.ci.code.DebugInfo;
  74 import jdk.vm.ci.code.TargetDescription;
  75 import jdk.vm.ci.meta.JavaKind;
  76 import jdk.vm.ci.meta.ResolvedJavaMethod;
  77 import jdk.vm.ci.meta.Value;
  78 
  79 /**
  80  * Utility for printing Graal IR at various compilation phases.
  81  */
  82 class CFGPrinter extends CompilationPrinter {
  83 
  84     protected TargetDescription target;
  85     protected LIR lir;
  86     protected NodeLIRBuilder nodeLirGenerator;
  87     protected ControlFlowGraph cfg;
  88     protected ScheduleResult schedule;
  89     protected ResolvedJavaMethod method;
  90     protected GlobalLivenessInfo livenessInfo;
  91     protected LIRGenerationResult res;
  92 
  93     /**
  94      * Creates a control flow graph printer.
  95      *
  96      * @param out where the output generated via this printer shown be written
  97      */
  98     CFGPrinter(OutputStream out) {
  99         super(out);
 100     }
 101 
 102     /**
 103      * Prints the control flow graph denoted by a given block map.
 104      *
 105      * @param label A label describing the compilation phase that produced the control flow graph.
 106      * @param blockMap A data structure describing the blocks in a method and how they are
 107      *            connected.
 108      */
 109     public void printCFG(String label, BciBlockMapping blockMap) {
 110         begin("cfg");
 111         out.print("name \"").print(label).println('"');
 112         for (BciBlockMapping.BciBlock block : blockMap.getBlocks()) {
 113             begin("block");
 114             printBlock(block);
 115             end("block");
 116         }
 117         end("cfg");
 118     }
 119 
 120     private void printBlock(BciBlockMapping.BciBlock block) {
 121         out.print("name \"B").print(block.getStartBci()).println('"');
 122         out.print("from_bci ").println(block.getStartBci());
 123         out.print("to_bci ").println(block.getEndBci());
 124 
 125         out.println("predecessors ");
 126 
 127         out.print("successors ");
 128         for (BciBlockMapping.BciBlock succ : block.getSuccessors()) {
 129             if (!succ.isExceptionEntry()) {
 130                 out.print("\"B").print(succ.getStartBci()).print("\" ");
 131             }
 132         }
 133         out.println();
 134 
 135         out.print("xhandlers");
 136         for (BciBlockMapping.BciBlock succ : block.getSuccessors()) {
 137             if (succ.isExceptionEntry()) {
 138                 out.print("\"B").print(succ.getStartBci()).print("\" ");
 139             }
 140         }
 141         out.println();
 142 
 143         out.print("flags ");
 144         if (block.isExceptionEntry()) {
 145             out.print("\"ex\" ");
 146         }
 147         if (block.isLoopHeader()) {
 148             out.print("\"plh\" ");
 149         }
 150         out.println();
 151 
 152         out.print("loop_depth ").println(Long.bitCount(block.getLoops()));
 153     }
 154 
 155     private NodeMap<Block> latestScheduling;
 156     private NodeBitMap printedNodes;
 157 
 158     private boolean inFixedSchedule(Node node) {
 159         return lir != null || schedule != null || node.isDeleted() || cfg.getNodeToBlock().get(node) != null;
 160     }
 161 
 162     /**
 163      * Prints the specified list of blocks.
 164      *
 165      * @param label A label describing the compilation phase that produced the control flow graph.
 166      * @param blocks The list of blocks to be printed.
 167      */
 168     public void printCFG(String label, AbstractBlockBase<?>[] blocks, boolean printNodes) {
 169         if (lir == null) {
 170             latestScheduling = new NodeMap<>(cfg.getNodeToBlock());
 171             for (AbstractBlockBase<?> abstractBlock : blocks) {
 172                 if (abstractBlock == null) {
 173                     continue;
 174                 }
 175                 Block block = (Block) abstractBlock;
 176                 Node cur = block.getBeginNode();
 177                 while (true) {
 178                     assert inFixedSchedule(cur) && latestScheduling.get(cur) == block;
 179                     scheduleInputs(cur, block);
 180 
 181                     if (cur == block.getEndNode()) {
 182                         break;
 183                     }
 184                     assert cur.successors().count() == 1;
 185                     cur = cur.successors().first();
 186                 }
 187             }
 188         }
 189 
 190         begin("cfg");
 191         out.print("name \"").print(label).println('"');
 192         for (AbstractBlockBase<?> block : blocks) {
 193             printBlock(block, printNodes);
 194         }
 195         end("cfg");
 196         // NOTE: we do this only because the c1visualizer does not recognize the bytecode block if
 197         // it is proceeding the cfg blocks. Currently we have no direct influence on the emit order.
 198         // As a workaround we dump the bytecode after every cfg.
 199         if (method != null) {
 200             printBytecodes(new BytecodeDisassembler(false).disassemble(method));
 201         }
 202 
 203         latestScheduling = null;
 204     }
 205 
 206     private void scheduleInputs(Node node, Block nodeBlock) {
 207         if (node instanceof ValuePhiNode) {
 208             PhiNode phi = (PhiNode) node;
 209             Block phiBlock = latestScheduling.get(phi.merge());
 210             assert phiBlock != null;
 211             for (Block pred : phiBlock.getPredecessors()) {
 212                 schedule(phi.valueAt((AbstractEndNode) pred.getEndNode()), pred);
 213             }
 214 
 215         } else {
 216             for (Node input : node.inputs()) {
 217                 schedule(input, nodeBlock);
 218             }
 219         }
 220     }
 221 
 222     private void schedule(Node input, Block block) {
 223         if (!inFixedSchedule(input)) {
 224             Block inputBlock = block;
 225             if (latestScheduling.get(input) != null) {
 226                 inputBlock = AbstractControlFlowGraph.commonDominatorTyped(inputBlock, latestScheduling.get(input));
 227             }
 228             if (inputBlock != latestScheduling.get(input)) {
 229                 latestScheduling.set(input, inputBlock);
 230                 scheduleInputs(input, inputBlock);
 231             }
 232         }
 233     }
 234 
 235     private void printBlock(AbstractBlockBase<?> block, boolean printNodes) {
 236         if (block == null) {
 237             return;
 238         }
 239         printBlockProlog(block);
 240         if (printNodes) {
 241             assert block instanceof Block;
 242             printNodes((Block) block);
 243         }
 244         printBlockEpilog(block);
 245     }
 246 
 247     private void printBlockEpilog(AbstractBlockBase<?> block) {
 248         printLIR(block);
 249         end("block");
 250     }
 251 
 252     private void printBlockProlog(AbstractBlockBase<?> block) {
 253         begin("block");
 254 
 255         out.print("name \"").print(blockToString(block)).println('"');
 256         out.println("from_bci -1");
 257         out.println("to_bci -1");
 258 
 259         out.print("predecessors ");
 260         for (AbstractBlockBase<?> pred : block.getPredecessors()) {
 261             out.print("\"").print(blockToString(pred)).print("\" ");
 262         }
 263         out.println();
 264 
 265         out.print("successors ");
 266         for (AbstractBlockBase<?> succ : block.getSuccessors()) {
 267             if (!succ.isExceptionEntry()) {
 268                 out.print("\"").print(blockToString(succ)).print("\" ");
 269             }
 270         }
 271         out.println();
 272 
 273         out.print("xhandlers");
 274         for (AbstractBlockBase<?> succ : block.getSuccessors()) {
 275             if (succ.isExceptionEntry()) {
 276                 out.print("\"").print(blockToString(succ)).print("\" ");
 277             }
 278         }
 279         out.println();
 280 
 281         out.print("flags ");
 282         if (block.isLoopHeader()) {
 283             out.print("\"llh\" ");
 284         }
 285         if (block.isLoopEnd()) {
 286             out.print("\"lle\" ");
 287         }
 288         if (block.isExceptionEntry()) {
 289             out.print("\"ex\" ");
 290         }
 291         out.println();
 292 
 293         if (block.getLoop() != null) {
 294             out.print("loop_index ").println(block.getLoop().getIndex());
 295             out.print("loop_depth ").println(block.getLoop().getDepth());
 296         }
 297 
 298         out.print("probability ").println(Double.doubleToRawLongBits(block.probability()));
 299     }
 300 
 301     private void printNodes(Block block) {
 302         printedNodes = new NodeBitMap(cfg.graph);
 303         begin("IR");
 304         out.println("HIR");
 305         out.disableIndentation();
 306 
 307         if (block.getBeginNode() instanceof AbstractMergeNode) {
 308             // Currently phi functions are not in the schedule, so print them separately here.
 309             for (ValueNode phi : ((AbstractMergeNode) block.getBeginNode()).phis()) {
 310                 printNode(phi, false);
 311             }
 312         }
 313 
 314         Node cur = block.getBeginNode();
 315         while (true) {
 316             printNode(cur, false);
 317 
 318             if (cur == block.getEndNode()) {
 319                 UnmodifiableMapCursor<Node, Block> cursor = latestScheduling.getEntries();
 320                 while (cursor.advance()) {
 321                     if (cursor.getValue() == block && !inFixedSchedule(cursor.getKey()) && !printedNodes.isMarked(cursor.getKey())) {
 322                         printNode(cursor.getKey(), true);
 323                     }
 324                 }
 325                 break;
 326             }
 327             assert cur.successors().count() == 1;
 328             cur = cur.successors().first();
 329         }
 330 
 331         out.enableIndentation();
 332         end("IR");
 333         printedNodes = null;
 334     }
 335 
 336     private void printNode(Node node, boolean unscheduled) {
 337         assert !printedNodes.isMarked(node);
 338         printedNodes.mark(node);
 339 
 340         if (!(node instanceof ValuePhiNode)) {
 341             for (Node input : node.inputs()) {
 342                 if (!inFixedSchedule(input) && !printedNodes.isMarked(input)) {
 343                     printNode(input, true);
 344                 }
 345             }
 346         }
 347 
 348         if (unscheduled) {
 349             assert lir == null && schedule == null : "unscheduled nodes can only be present before LIR generation";
 350             out.print("f ").print(HOVER_START).print("u").print(HOVER_SEP).print("unscheduled").print(HOVER_END).println(COLUMN_END);
 351         } else if (node instanceof FixedWithNextNode) {
 352             out.print("f ").print(HOVER_START).print("#").print(HOVER_SEP).print("fixed with next").print(HOVER_END).println(COLUMN_END);
 353         } else if (node instanceof FixedNode) {
 354             out.print("f ").print(HOVER_START).print("*").print(HOVER_SEP).print("fixed").print(HOVER_END).println(COLUMN_END);
 355         } else if (node instanceof FloatingNode) {
 356             out.print("f ").print(HOVER_START).print("~").print(HOVER_SEP).print("floating").print(HOVER_END).println(COLUMN_END);
 357         }
 358         out.print("tid ").print(nodeToString(node)).println(COLUMN_END);
 359 
 360         if (nodeLirGenerator != null) {
 361             Value operand = nodeLirGenerator.hasOperand(node) ? nodeLirGenerator.operand(node) : null;
 362             if (operand != null) {
 363                 out.print("result ").print(operand.toString()).println(COLUMN_END);
 364             }
 365         }
 366 
 367         if (node instanceof StateSplit) {
 368             StateSplit stateSplit = (StateSplit) node;
 369             if (stateSplit.stateAfter() != null) {
 370                 String state = stateToString(stateSplit.stateAfter());
 371                 out.print("st ").print(HOVER_START).print("st").print(HOVER_SEP).print(state).print(HOVER_END).println(COLUMN_END);
 372             }
 373         }
 374 
 375         Map<Object, Object> props = new TreeMap<>(node.getDebugProperties());
 376         out.print("d ").print(HOVER_START).print("d").print(HOVER_SEP);
 377         out.println("=== Debug Properties ===");
 378         for (Map.Entry<Object, Object> entry : props.entrySet()) {
 379             out.print(entry.getKey().toString()).print(": ").print(entry.getValue() == null ? "[null]" : entry.getValue().toString()).println();
 380         }
 381         out.println("=== Inputs ===");
 382         printNamedNodes(node, node.inputPositions().iterator(), "", "\n", null);
 383         out.println("=== Succesors ===");
 384         printNamedNodes(node, node.successorPositions().iterator(), "", "\n", null);
 385         out.println("=== Usages ===");
 386         if (!node.hasNoUsages()) {
 387             for (Node usage : node.usages()) {
 388                 out.print(nodeToString(usage)).print(" ");
 389             }
 390             out.println();
 391         }
 392         out.println("=== Predecessor ===");
 393         out.print(nodeToString(node.predecessor())).print(" ");
 394         out.print(HOVER_END).println(COLUMN_END);
 395 
 396         out.print("instruction ");
 397         out.print(HOVER_START).print(node.getNodeClass().shortName()).print(HOVER_SEP).print(node.getClass().getName()).print(HOVER_END).print(" ");
 398         printNamedNodes(node, node.inputPositions().iterator(), "", "", "#NDF");
 399         printNamedNodes(node, node.successorPositions().iterator(), "#", "", "#NDF");
 400         for (Map.Entry<Object, Object> entry : props.entrySet()) {
 401             String key = entry.getKey().toString();
 402             if (key.startsWith("data.") && !key.equals("data.stamp")) {
 403                 out.print(key.substring("data.".length())).print(": ").print(entry.getValue() == null ? "[null]" : entry.getValue().toString()).print(" ");
 404             }
 405         }
 406         out.print(COLUMN_END).print(' ').println(COLUMN_END);
 407     }
 408 
 409     private void printNamedNodes(Node node, Iterator<Position> iter, String prefix, String suffix, String hideSuffix) {
 410         int lastIndex = -1;
 411         while (iter.hasNext()) {
 412             Position pos = iter.next();
 413             if (hideSuffix != null && pos.getName().endsWith(hideSuffix)) {
 414                 continue;
 415             }
 416 
 417             if (pos.getIndex() != lastIndex) {
 418                 if (lastIndex != -1) {
 419                     out.print(suffix);
 420                 }
 421                 out.print(prefix).print(pos.getName()).print(": ");
 422                 lastIndex = pos.getIndex();
 423             }
 424             out.print(nodeToString(pos.get(node))).print(" ");
 425         }
 426         if (lastIndex != -1) {
 427             out.print(suffix);
 428         }
 429     }
 430 
 431     private String stateToString(FrameState state) {
 432         StringBuilder buf = new StringBuilder();
 433         FrameState curState = state;
 434         do {
 435             buf.append(Bytecode.toLocation(curState.getCode(), curState.bci)).append('\n');
 436 
 437             if (curState.stackSize() > 0) {
 438                 buf.append("stack: ");
 439                 for (int i = 0; i < curState.stackSize(); i++) {
 440                     buf.append(stateValueToString(curState.stackAt(i))).append(' ');
 441                 }
 442                 buf.append("\n");
 443             }
 444 
 445             buf.append("locals: ");
 446             for (int i = 0; i < curState.localsSize(); i++) {
 447                 buf.append(stateValueToString(curState.localAt(i))).append(' ');
 448             }
 449             buf.append("\n");
 450 
 451             buf.append("locks: ");
 452             for (int i = 0; i < curState.locksSize(); i++) {
 453                 buf.append(stateValueToString(curState.lockAt(i))).append(' ');
 454             }
 455             buf.append("\n");
 456 
 457             curState = curState.outerFrameState();
 458         } while (curState != null);
 459 
 460         return buf.toString();
 461     }
 462 
 463     private String stateValueToString(ValueNode value) {
 464         String result = nodeToString(value);
 465         if (nodeLirGenerator != null && value != null && nodeLirGenerator.hasOperand(value)) {
 466             Value operand = nodeLirGenerator.operand(value);
 467             assert operand != null;
 468             result += ": " + operand;
 469         }
 470         return result;
 471     }
 472 
 473     /**
 474      * Prints the LIR for each instruction in a given block.
 475      *
 476      * @param block the block to print
 477      */
 478     private void printLIR(AbstractBlockBase<?> block) {
 479         if (lir == null) {
 480             return;
 481         }
 482         ArrayList<LIRInstruction> lirInstructions = lir.getLIRforBlock(block);
 483         if (lirInstructions == null) {
 484             return;
 485         }
 486 
 487         begin("IR");
 488         out.println("LIR");
 489 
 490         if (livenessInfo != null) {
 491             int opId = lirInstructions.get(0).id();
 492             printLiveVars(livenessInfo.getBlockIn(block), "in(var)", opId);
 493             printLiveLoc(livenessInfo.getInLocation(block), "in(loc)", opId);
 494         }
 495         for (int i = 0; i < lirInstructions.size(); i++) {
 496             LIRInstruction inst = lirInstructions.get(i);
 497             printLIRInstruction(inst);
 498         }
 499         if (livenessInfo != null) {
 500             int opId = lirInstructions.get(lirInstructions.size() - 1).id();
 501             printLiveVars(livenessInfo.getBlockOut(block), "out(var)", opId);
 502             printLiveLoc(livenessInfo.getOutLocation(block), "out(loc)", opId);
 503         }
 504         end("IR");
 505     }
 506 
 507     private void printLiveVars(int[] live, String lbl, int opId) {
 508         out.printf("nr %4d ", opId).print(COLUMN_END).print(" instruction ");
 509         out.print(lbl).print(" [");
 510         for (int i = 0; i < live.length; i++) {
 511             if (i > 0) {
 512                 out.print(", ");
 513             }
 514             int varNum = live[i];
 515             Value value = varNum >= 0 ? livenessInfo.getVariable(varNum) : Value.ILLEGAL;
 516             out.print(i).print(": ").print(value.toString());
 517         }
 518         out.print(']');
 519         out.print(COLUMN_END);
 520         out.println(COLUMN_END);
 521     }
 522 
 523     private void printLiveLoc(Value[] values, String lbl, int opId) {
 524         if (values != null) {
 525             out.printf("nr %4d ", opId).print(COLUMN_END).print(" instruction ");
 526             out.print(lbl).print(" [");
 527             for (int i = 0; i < values.length; i++) {
 528                 if (i > 0) {
 529                     out.print(", ");
 530                 }
 531                 out.print(i).print(": ").print(values[i].toString());
 532             }
 533             out.print(']');
 534             out.print(COLUMN_END);
 535             out.println(COLUMN_END);
 536         }
 537     }
 538 
 539     private void printLIRInstruction(LIRInstruction inst) {
 540         if (inst == null) {
 541             out.print("nr   -1 ").print(COLUMN_END).print(" instruction ").print("<deleted>").print(COLUMN_END);
 542             out.println(COLUMN_END);
 543         } else {
 544             out.printf("nr %4d ", inst.id()).print(COLUMN_END);
 545 
 546             final StringBuilder stateString = new StringBuilder();
 547             inst.forEachState(state -> {
 548                 if (state.hasDebugInfo()) {
 549                     DebugInfo di = state.debugInfo();
 550                     stateString.append(debugInfoToString(di.getBytecodePosition(), di.getReferenceMap(), state.getLiveBasePointers(), di.getCalleeSaveInfo()));
 551                 } else {
 552                     stateString.append(debugInfoToString(state.topFrame, null, state.getLiveBasePointers(), null));
 553                 }
 554             });
 555             if (stateString.length() > 0) {
 556                 int level = out.indentationLevel();
 557                 out.adjustIndentation(-level);
 558                 out.print(" st ").print(HOVER_START).print("st").print(HOVER_SEP).print(stateString.toString()).print(HOVER_END).print(COLUMN_END);
 559                 out.adjustIndentation(level);
 560             }
 561 
 562             out.print(" instruction ").print(inst.toString(res)).print(COLUMN_END);
 563             out.println(COLUMN_END);
 564         }
 565     }
 566 
 567     private String nodeToString(Node node) {
 568         if (node == null) {
 569             return "-";
 570         }
 571         String prefix;
 572         if (node instanceof AbstractBeginNode && (lir == null && schedule == null)) {
 573             prefix = "B";
 574         } else if (node instanceof ValueNode) {
 575             ValueNode value = (ValueNode) node;
 576             if (value.getStackKind() == JavaKind.Illegal) {
 577                 prefix = "v";
 578             } else {
 579                 prefix = String.valueOf(toLowerCase(value.getStackKind().getTypeChar()));
 580             }
 581         } else {
 582             prefix = "?";
 583         }
 584         return prefix + node.toString(Verbosity.Id);
 585     }
 586 
 587     private String blockToString(AbstractBlockBase<?> block) {
 588         if (lir == null && schedule == null && block instanceof Block) {
 589             // During all the front-end phases, the block schedule is built only for the debug
 590             // output.
 591             // Therefore, the block numbers would be different for every CFG printed -> use the id
 592             // of the first instruction.
 593             return "B" + ((Block) block).getBeginNode().toString(Verbosity.Id);
 594         } else {
 595             // LIR instructions contain references to blocks and these blocks are printed as the
 596             // blockID -> use the blockID.
 597             return "B" + block.getId();
 598         }
 599     }
 600 
 601     IntervalVisitor intervalVisitor = new IntervalVisitor() {
 602 
 603         /**
 604          * @return a formatted description of the operand that the C1Visualizer can handle.
 605          */
 606         String getFormattedOperand(Value operand) {
 607             String s = operand.toString();
 608             int last = s.lastIndexOf('|');
 609             if (last != -1) {
 610                 return s.substring(0, last) + "|" + operand.getPlatformKind().getTypeChar();
 611             }
 612             return s;
 613         }
 614 
 615         @Override
 616         public void visitIntervalStart(Value parentOperand, Value splitOperand, Value location, Value hint, String typeName) {
 617             out.printf("%s %s ", getFormattedOperand(splitOperand), typeName);
 618             if (location != null) {
 619                 out.printf("\"[%s]\"", getFormattedOperand(location));
 620             } else {
 621                 out.printf("\"[%s]\"", getFormattedOperand(splitOperand));
 622             }
 623             out.printf(" %s %s ", getFormattedOperand(parentOperand), hint != null ? getFormattedOperand(hint) : -1);
 624         }
 625 
 626         @Override
 627         public void visitRange(int from, int to) {
 628             out.printf("[%d, %d[", from, to);
 629         }
 630 
 631         @Override
 632         public void visitUsePos(int usePos, Object registerPriority) {
 633             out.printf("%d %s ", usePos, registerPriority);
 634         }
 635 
 636         @Override
 637         public void visitIntervalEnd(Object spillState) {
 638             out.printf(" \"%s\"", spillState);
 639             out.println();
 640         }
 641 
 642     };
 643 
 644     public void printIntervals(String label, IntervalDumper intervals) {
 645         begin("intervals");
 646         out.println(String.format("name \"%s\"", label));
 647 
 648         intervals.visitIntervals(intervalVisitor);
 649 
 650         end("intervals");
 651     }
 652 
 653     public void printSchedule(String message, ScheduleResult theSchedule) {
 654         schedule = theSchedule;
 655         cfg = schedule.getCFG();
 656         printedNodes = new NodeBitMap(cfg.graph);
 657 
 658         begin("cfg");
 659         out.print("name \"").print(message).println('"');
 660         for (Block b : schedule.getCFG().getBlocks()) {
 661             if (schedule.nodesFor(b) != null) {
 662                 printScheduledBlock(b, schedule.nodesFor(b));
 663             }
 664         }
 665         end("cfg");
 666 
 667         schedule = null;
 668         cfg = null;
 669         printedNodes = null;
 670     }
 671 
 672     private void printScheduledBlock(Block block, List<Node> nodesFor) {
 673         printBlockProlog(block);
 674         begin("IR");
 675         out.println("HIR");
 676         out.disableIndentation();
 677 
 678         if (block.getBeginNode() instanceof AbstractMergeNode) {
 679             // Currently phi functions are not in the schedule, so print them separately here.
 680             for (ValueNode phi : ((AbstractMergeNode) block.getBeginNode()).phis()) {
 681                 printNode(phi, false);
 682             }
 683         }
 684 
 685         for (Node n : nodesFor) {
 686             printNode(n, false);
 687         }
 688 
 689         out.enableIndentation();
 690         end("IR");
 691 
 692         printBlockEpilog(block);
 693     }
 694 
 695     public void printTraces(String label, TraceBuilderResult traces) {
 696         begin("cfg");
 697         out.print("name \"").print(label).println('"');
 698 
 699         for (Trace trace : traces.getTraces()) {
 700             printTrace(trace, traces);
 701         }
 702 
 703         end("cfg");
 704     }
 705 
 706     private void printTrace(Trace trace, TraceBuilderResult traceBuilderResult) {
 707         printTraceProlog(trace, traceBuilderResult);
 708         printTraceInstructions(trace, traceBuilderResult);
 709         printTraceEpilog();
 710     }
 711 
 712     private void printTraceProlog(Trace trace, TraceBuilderResult traceBuilderResult) {
 713         begin("block");
 714 
 715         out.print("name \"").print(traceToString(trace)).println('"');
 716         out.println("from_bci -1");
 717         out.println("to_bci -1");
 718 
 719         out.print("predecessors ");
 720         for (Trace pred : getPredecessors(trace, traceBuilderResult)) {
 721             out.print("\"").print(traceToString(pred)).print("\" ");
 722         }
 723         out.println();
 724 
 725         out.print("successors ");
 726         for (Trace succ : getSuccessors(trace, traceBuilderResult)) {
 727             // if (!succ.isExceptionEntry()) {
 728             out.print("\"").print(traceToString(succ)).print("\" ");
 729             // }
 730         }
 731         out.println();
 732 
 733         out.print("xhandlers");
 734         // TODO(je) add support for exception handler
 735         out.println();
 736 
 737         out.print("flags ");
 738         // TODO(je) add support for flags
 739         out.println();
 740         // TODO(je) add support for loop infos
 741     }
 742 
 743     private void printTraceInstructions(Trace trace, TraceBuilderResult traceBuilderResult) {
 744         if (lir == null) {
 745             return;
 746         }
 747         begin("IR");
 748         out.println("LIR");
 749 
 750         for (AbstractBlockBase<?> block : trace.getBlocks()) {
 751             ArrayList<LIRInstruction> lirInstructions = lir.getLIRforBlock(block);
 752             if (lirInstructions == null) {
 753                 continue;
 754             }
 755             printBlockInstruction(block, traceBuilderResult);
 756             for (int i = 0; i < lirInstructions.size(); i++) {
 757                 LIRInstruction inst = lirInstructions.get(i);
 758                 printLIRInstruction(inst);
 759             }
 760         }
 761         end("IR");
 762     }
 763 
 764     private void printBlockInstruction(AbstractBlockBase<?> block, TraceBuilderResult traceBuilderResult) {
 765         out.print("nr ").print(block.toString()).print(COLUMN_END).print(" instruction ");
 766 
 767         if (block.getPredecessorCount() > 0) {
 768             out.print("<- ");
 769             printBlockListWithTrace(Arrays.asList(block.getPredecessors()), traceBuilderResult);
 770             out.print(" ");
 771         }
 772         if (block.getSuccessorCount() > 0) {
 773             out.print("-> ");
 774             printBlockListWithTrace(Arrays.asList(block.getSuccessors()), traceBuilderResult);
 775         }
 776 
 777         out.print(COLUMN_END);
 778         out.println(COLUMN_END);
 779     }
 780 
 781     private void printBlockListWithTrace(List<? extends AbstractBlockBase<?>> blocks, TraceBuilderResult traceBuilderResult) {
 782         Iterator<? extends AbstractBlockBase<?>> it = blocks.iterator();
 783         printBlockWithTrace(it.next(), traceBuilderResult);
 784         while (it.hasNext()) {
 785             out.print(",");
 786             printBlockWithTrace(it.next(), traceBuilderResult);
 787         }
 788     }
 789 
 790     private void printBlockWithTrace(AbstractBlockBase<?> block, TraceBuilderResult traceBuilderResult) {
 791         out.print(block.toString());
 792         out.print("[T").print(traceBuilderResult.getTraceForBlock(block).getId()).print("]");
 793     }
 794 
 795     private void printTraceEpilog() {
 796         end("block");
 797     }
 798 
 799     private static boolean isLoopBackEdge(AbstractBlockBase<?> src, AbstractBlockBase<?> dst) {
 800         return dst.isLoopHeader() && dst.getLoop().equals(src.getLoop());
 801     }
 802 
 803     private static List<Trace> getSuccessors(Trace trace, TraceBuilderResult traceBuilderResult) {
 804         BitSet bs = new BitSet(traceBuilderResult.getTraces().size());
 805         for (AbstractBlockBase<?> block : trace.getBlocks()) {
 806             for (AbstractBlockBase<?> s : block.getSuccessors()) {
 807                 Trace otherTrace = traceBuilderResult.getTraceForBlock(s);
 808                 int otherTraceId = otherTrace.getId();
 809                 if (trace.getId() != otherTraceId || isLoopBackEdge(block, s)) {
 810                     bs.set(otherTraceId);
 811                 }
 812             }
 813         }
 814         List<Trace> succ = new ArrayList<>();
 815         for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
 816             succ.add(traceBuilderResult.getTraces().get(i));
 817         }
 818         return succ;
 819     }
 820 
 821     private static List<Trace> getPredecessors(Trace trace, TraceBuilderResult traceBuilderResult) {
 822         BitSet bs = new BitSet(traceBuilderResult.getTraces().size());
 823         for (AbstractBlockBase<?> block : trace.getBlocks()) {
 824             for (AbstractBlockBase<?> p : block.getPredecessors()) {
 825                 Trace otherTrace = traceBuilderResult.getTraceForBlock(p);
 826                 int otherTraceId = otherTrace.getId();
 827                 if (trace.getId() != otherTraceId || isLoopBackEdge(p, block)) {
 828                     bs.set(traceBuilderResult.getTraceForBlock(p).getId());
 829                 }
 830             }
 831         }
 832         List<Trace> pred = new ArrayList<>();
 833         for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
 834             pred.add(traceBuilderResult.getTraces().get(i));
 835         }
 836         return pred;
 837     }
 838 
 839     private static String traceToString(Trace trace) {
 840         return new StringBuilder("T").append(trace.getId()).toString();
 841     }
 842 
 843 }