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