001/* 002 * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.printer; 024 025import static jdk.internal.jvmci.code.ValueUtil.*; 026 027import java.io.*; 028import java.util.*; 029 030import jdk.internal.jvmci.code.*; 031import jdk.internal.jvmci.meta.*; 032 033import com.oracle.graal.compiler.common.cfg.*; 034import com.oracle.graal.compiler.gen.*; 035import com.oracle.graal.graph.*; 036import com.oracle.graal.java.*; 037import com.oracle.graal.lir.*; 038import com.oracle.graal.lir.alloc.lsra.*; 039import com.oracle.graal.lir.alloc.lsra.Interval.UsePosList; 040import com.oracle.graal.lir.stackslotalloc.*; 041import com.oracle.graal.nodeinfo.*; 042import com.oracle.graal.nodes.*; 043import com.oracle.graal.nodes.calc.*; 044import com.oracle.graal.nodes.cfg.*; 045import com.oracle.graal.phases.schedule.*; 046 047/** 048 * Utility for printing Graal IR at various compilation phases. 049 */ 050class CFGPrinter extends CompilationPrinter { 051 052 protected TargetDescription target; 053 protected LIR lir; 054 protected NodeLIRBuilder nodeLirGenerator; 055 protected ControlFlowGraph cfg; 056 protected SchedulePhase schedule; 057 protected ResolvedJavaMethod method; 058 059 /** 060 * Creates a control flow graph printer. 061 * 062 * @param out where the output generated via this printer shown be written 063 */ 064 public CFGPrinter(OutputStream out) { 065 super(out); 066 } 067 068 /** 069 * Prints the control flow graph denoted by a given block map. 070 * 071 * @param label A label describing the compilation phase that produced the control flow graph. 072 * @param blockMap A data structure describing the blocks in a method and how they are 073 * connected. 074 */ 075 public void printCFG(String label, BciBlockMapping blockMap) { 076 begin("cfg"); 077 out.print("name \"").print(label).println('"'); 078 for (BciBlockMapping.BciBlock block : blockMap.getBlocks()) { 079 begin("block"); 080 printBlock(block); 081 end("block"); 082 } 083 end("cfg"); 084 } 085 086 private void printBlock(BciBlockMapping.BciBlock block) { 087 out.print("name \"B").print(block.startBci).println('"'); 088 out.print("from_bci ").println(block.startBci); 089 out.print("to_bci ").println(block.endBci); 090 091 out.println("predecessors "); 092 093 out.print("successors "); 094 for (BciBlockMapping.BciBlock succ : block.getSuccessors()) { 095 if (!succ.isExceptionEntry) { 096 out.print("\"B").print(succ.startBci).print("\" "); 097 } 098 } 099 out.println(); 100 101 out.print("xhandlers"); 102 for (BciBlockMapping.BciBlock succ : block.getSuccessors()) { 103 if (succ.isExceptionEntry) { 104 out.print("\"B").print(succ.startBci).print("\" "); 105 } 106 } 107 out.println(); 108 109 out.print("flags "); 110 if (block.isExceptionEntry) { 111 out.print("\"ex\" "); 112 } 113 if (block.isLoopHeader) { 114 out.print("\"plh\" "); 115 } 116 out.println(); 117 118 out.print("loop_depth ").println(Long.bitCount(block.loops)); 119 } 120 121 private NodeMap<Block> latestScheduling; 122 private NodeBitMap printedNodes; 123 124 private boolean inFixedSchedule(Node node) { 125 return lir != null || schedule != null || node.isDeleted() || cfg.getNodeToBlock().get(node) != null; 126 } 127 128 /** 129 * Prints the specified list of blocks. 130 * 131 * @param label A label describing the compilation phase that produced the control flow graph. 132 * @param blocks The list of blocks to be printed. 133 */ 134 public void printCFG(String label, List<? extends AbstractBlockBase<?>> blocks, boolean printNodes) { 135 if (lir == null) { 136 latestScheduling = new NodeMap<>(cfg.getNodeToBlock()); 137 for (AbstractBlockBase<?> abstractBlock : blocks) { 138 Block block = (Block) abstractBlock; 139 Node cur = block.getBeginNode(); 140 while (true) { 141 assert inFixedSchedule(cur) && latestScheduling.get(cur) == block; 142 scheduleInputs(cur, block); 143 144 if (cur == block.getEndNode()) { 145 break; 146 } 147 assert cur.successors().count() == 1; 148 cur = cur.successors().first(); 149 } 150 } 151 } 152 153 begin("cfg"); 154 out.print("name \"").print(label).println('"'); 155 for (AbstractBlockBase<?> block : blocks) { 156 printBlock(block, printNodes); 157 } 158 end("cfg"); 159 // NOTE: we do this only because the c1visualizer does not recognize the bytecode block if 160 // it is proceeding the cfg blocks. Currently we have no direct influence on the emit order. 161 // As a workaround we dump the bytecode after every cfg. 162 if (method != null) { 163 printBytecodes(new BytecodeDisassembler(false).disassemble(method)); 164 } 165 166 latestScheduling = null; 167 } 168 169 private void scheduleInputs(Node node, Block nodeBlock) { 170 if (node instanceof ValuePhiNode) { 171 PhiNode phi = (PhiNode) node; 172 assert nodeBlock.getBeginNode() == phi.merge(); 173 for (Block pred : nodeBlock.getPredecessors()) { 174 schedule(phi.valueAt((AbstractEndNode) pred.getEndNode()), pred); 175 } 176 177 } else { 178 for (Node input : node.inputs()) { 179 schedule(input, nodeBlock); 180 } 181 } 182 } 183 184 private void schedule(Node input, Block block) { 185 if (!inFixedSchedule(input)) { 186 Block inputBlock = block; 187 if (latestScheduling.get(input) != null) { 188 inputBlock = AbstractControlFlowGraph.commonDominatorTyped(inputBlock, latestScheduling.get(input)); 189 } 190 if (inputBlock != latestScheduling.get(input)) { 191 latestScheduling.set(input, inputBlock); 192 scheduleInputs(input, inputBlock); 193 } 194 } 195 } 196 197 private void printBlock(AbstractBlockBase<?> block, boolean printNodes) { 198 printBlockProlog(block); 199 if (printNodes) { 200 assert block instanceof Block; 201 printNodes((Block) block); 202 } 203 printBlockEpilog(block); 204 } 205 206 private void printBlockEpilog(AbstractBlockBase<?> block) { 207 printLIR(block); 208 end("block"); 209 } 210 211 private void printBlockProlog(AbstractBlockBase<?> block) { 212 begin("block"); 213 214 out.print("name \"").print(blockToString(block)).println('"'); 215 out.println("from_bci -1"); 216 out.println("to_bci -1"); 217 218 out.print("predecessors "); 219 for (AbstractBlockBase<?> pred : block.getPredecessors()) { 220 out.print("\"").print(blockToString(pred)).print("\" "); 221 } 222 out.println(); 223 224 out.print("successors "); 225 for (AbstractBlockBase<?> succ : block.getSuccessors()) { 226 if (!succ.isExceptionEntry()) { 227 out.print("\"").print(blockToString(succ)).print("\" "); 228 } 229 } 230 out.println(); 231 232 out.print("xhandlers"); 233 for (AbstractBlockBase<?> succ : block.getSuccessors()) { 234 if (succ.isExceptionEntry()) { 235 out.print("\"").print(blockToString(succ)).print("\" "); 236 } 237 } 238 out.println(); 239 240 out.print("flags "); 241 if (block.isLoopHeader()) { 242 out.print("\"llh\" "); 243 } 244 if (block.isLoopEnd()) { 245 out.print("\"lle\" "); 246 } 247 if (block.isExceptionEntry()) { 248 out.print("\"ex\" "); 249 } 250 out.println(); 251 252 if (block.getLoop() != null) { 253 out.print("loop_index ").println(block.getLoop().getIndex()); 254 out.print("loop_depth ").println(block.getLoop().getDepth()); 255 } 256 } 257 258 private void printNodes(Block block) { 259 printedNodes = new NodeBitMap(cfg.graph); 260 begin("IR"); 261 out.println("HIR"); 262 out.disableIndentation(); 263 264 if (block.getBeginNode() instanceof AbstractMergeNode) { 265 // Currently phi functions are not in the schedule, so print them separately here. 266 for (ValueNode phi : ((AbstractMergeNode) block.getBeginNode()).phis()) { 267 printNode(phi, false); 268 } 269 } 270 271 Node cur = block.getBeginNode(); 272 while (true) { 273 printNode(cur, false); 274 275 if (cur == block.getEndNode()) { 276 for (Map.Entry<Node, Block> entry : latestScheduling.entries()) { 277 if (entry.getValue() == block && !inFixedSchedule(entry.getKey()) && !printedNodes.isMarked(entry.getKey())) { 278 printNode(entry.getKey(), true); 279 } 280 } 281 break; 282 } 283 assert cur.successors().count() == 1; 284 cur = cur.successors().first(); 285 } 286 287 out.enableIndentation(); 288 end("IR"); 289 printedNodes = null; 290 } 291 292 private void printNode(Node node, boolean unscheduled) { 293 assert !printedNodes.isMarked(node); 294 printedNodes.mark(node); 295 296 if (!(node instanceof ValuePhiNode)) { 297 for (Node input : node.inputs()) { 298 if (!inFixedSchedule(input) && !printedNodes.isMarked(input)) { 299 printNode(input, true); 300 } 301 } 302 } 303 304 if (unscheduled) { 305 assert lir == null && schedule == null : "unscheduled nodes can only be present before LIR generation"; 306 out.print("f ").print(HOVER_START).print("u").print(HOVER_SEP).print("unscheduled").print(HOVER_END).println(COLUMN_END); 307 } else if (node instanceof FixedWithNextNode) { 308 out.print("f ").print(HOVER_START).print("#").print(HOVER_SEP).print("fixed with next").print(HOVER_END).println(COLUMN_END); 309 } else if (node instanceof FixedNode) { 310 out.print("f ").print(HOVER_START).print("*").print(HOVER_SEP).print("fixed").print(HOVER_END).println(COLUMN_END); 311 } else if (node instanceof FloatingNode) { 312 out.print("f ").print(HOVER_START).print("~").print(HOVER_SEP).print("floating").print(HOVER_END).println(COLUMN_END); 313 } 314 out.print("tid ").print(nodeToString(node)).println(COLUMN_END); 315 316 if (nodeLirGenerator != null) { 317 Value operand = nodeLirGenerator.hasOperand(node) ? nodeLirGenerator.operand(node) : null; 318 if (operand != null) { 319 out.print("result ").print(operand.toString()).println(COLUMN_END); 320 } 321 } 322 323 if (node instanceof StateSplit) { 324 StateSplit stateSplit = (StateSplit) node; 325 if (stateSplit.stateAfter() != null) { 326 String state = stateToString(stateSplit.stateAfter()); 327 out.print("st ").print(HOVER_START).print("st").print(HOVER_SEP).print(state).print(HOVER_END).println(COLUMN_END); 328 } 329 } 330 331 Map<Object, Object> props = new TreeMap<>(node.getDebugProperties()); 332 out.print("d ").print(HOVER_START).print("d").print(HOVER_SEP); 333 out.println("=== Debug Properties ==="); 334 for (Map.Entry<Object, Object> entry : props.entrySet()) { 335 out.print(entry.getKey().toString()).print(": ").print(entry.getValue() == null ? "[null]" : entry.getValue().toString()).println(); 336 } 337 out.println("=== Inputs ==="); 338 printNamedNodes(node, node.inputs().iterator(), "", "\n", null); 339 out.println("=== Succesors ==="); 340 printNamedNodes(node, node.successors().iterator(), "", "\n", null); 341 out.println("=== Usages ==="); 342 if (!node.hasNoUsages()) { 343 for (Node usage : node.usages()) { 344 out.print(nodeToString(usage)).print(" "); 345 } 346 out.println(); 347 } 348 out.println("=== Predecessor ==="); 349 out.print(nodeToString(node.predecessor())).print(" "); 350 out.print(HOVER_END).println(COLUMN_END); 351 352 out.print("instruction "); 353 out.print(HOVER_START).print(node.getNodeClass().shortName()).print(HOVER_SEP).print(node.getClass().getName()).print(HOVER_END).print(" "); 354 printNamedNodes(node, node.inputs().iterator(), "", "", "#NDF"); 355 printNamedNodes(node, node.successors().iterator(), "#", "", "#NDF"); 356 for (Map.Entry<Object, Object> entry : props.entrySet()) { 357 String key = entry.getKey().toString(); 358 if (key.startsWith("data.") && !key.equals("data.stamp")) { 359 out.print(key.substring("data.".length())).print(": ").print(entry.getValue() == null ? "[null]" : entry.getValue().toString()).print(" "); 360 } 361 } 362 out.print(COLUMN_END).print(' ').println(COLUMN_END); 363 } 364 365 private void printNamedNodes(Node node, NodePosIterator iter, String prefix, String suffix, String hideSuffix) { 366 int lastIndex = -1; 367 while (iter.hasNext()) { 368 Position pos = iter.nextPosition(); 369 if (hideSuffix != null && pos.getName().endsWith(hideSuffix)) { 370 continue; 371 } 372 373 if (pos.getIndex() != lastIndex) { 374 if (lastIndex != -1) { 375 out.print(suffix); 376 } 377 out.print(prefix).print(pos.getName()).print(": "); 378 lastIndex = pos.getIndex(); 379 } 380 out.print(nodeToString(pos.get(node))).print(" "); 381 } 382 if (lastIndex != -1) { 383 out.print(suffix); 384 } 385 } 386 387 private String stateToString(FrameState state) { 388 StringBuilder buf = new StringBuilder(); 389 FrameState curState = state; 390 do { 391 buf.append(MetaUtil.toLocation(curState.method(), curState.bci)).append('\n'); 392 393 if (curState.stackSize() > 0) { 394 buf.append("stack: "); 395 for (int i = 0; i < curState.stackSize(); i++) { 396 buf.append(stateValueToString(curState.stackAt(i))).append(' '); 397 } 398 buf.append("\n"); 399 } 400 401 buf.append("locals: "); 402 for (int i = 0; i < curState.localsSize(); i++) { 403 buf.append(stateValueToString(curState.localAt(i))).append(' '); 404 } 405 buf.append("\n"); 406 407 buf.append("locks: "); 408 for (int i = 0; i < curState.locksSize(); i++) { 409 buf.append(stateValueToString(curState.lockAt(i))).append(' '); 410 } 411 buf.append("\n"); 412 413 curState = curState.outerFrameState(); 414 } while (curState != null); 415 416 return buf.toString(); 417 } 418 419 private String stateValueToString(ValueNode value) { 420 String result = nodeToString(value); 421 if (nodeLirGenerator != null && value != null && nodeLirGenerator.hasOperand(value)) { 422 Value operand = nodeLirGenerator.operand(value); 423 assert operand != null; 424 result += ": " + operand; 425 } 426 return result; 427 } 428 429 /** 430 * Prints the LIR for each instruction in a given block. 431 * 432 * @param block the block to print 433 */ 434 private void printLIR(AbstractBlockBase<?> block) { 435 if (lir == null) { 436 return; 437 } 438 List<LIRInstruction> lirInstructions = lir.getLIRforBlock(block); 439 if (lirInstructions == null) { 440 return; 441 } 442 443 begin("IR"); 444 out.println("LIR"); 445 446 for (int i = 0; i < lirInstructions.size(); i++) { 447 LIRInstruction inst = lirInstructions.get(i); 448 if (inst == null) { 449 out.print("nr -1 ").print(COLUMN_END).print(" instruction ").print("<deleted>").print(COLUMN_END); 450 out.println(COLUMN_END); 451 } else { 452 out.printf("nr %4d ", inst.id()).print(COLUMN_END); 453 454 final StringBuilder stateString = new StringBuilder(); 455 inst.forEachState(state -> { 456 if (state.hasDebugInfo()) { 457 DebugInfo di = state.debugInfo(); 458 stateString.append(debugInfoToString(di.getBytecodePosition(), di.getReferenceMap(), state.getLiveBasePointers(), di.getCalleeSaveInfo())); 459 } else { 460 stateString.append(debugInfoToString(state.topFrame, null, state.getLiveBasePointers(), null)); 461 } 462 }); 463 if (stateString.length() > 0) { 464 int level = out.indentationLevel(); 465 out.adjustIndentation(-level); 466 out.print(" st ").print(HOVER_START).print("st").print(HOVER_SEP).print(stateString.toString()).print(HOVER_END).print(COLUMN_END); 467 out.adjustIndentation(level); 468 } 469 470 out.print(" instruction ").print(inst.toString()).print(COLUMN_END); 471 out.println(COLUMN_END); 472 } 473 } 474 end("IR"); 475 } 476 477 private String nodeToString(Node node) { 478 if (node == null) { 479 return "-"; 480 } 481 String prefix; 482 if (node instanceof AbstractBeginNode && (lir == null && schedule == null)) { 483 prefix = "B"; 484 } else if (node instanceof ValueNode) { 485 ValueNode value = (ValueNode) node; 486 if (value.getKind() == Kind.Illegal) { 487 prefix = "v"; 488 } else { 489 prefix = String.valueOf(value.getKind().getTypeChar()); 490 } 491 } else { 492 prefix = "?"; 493 } 494 return prefix + node.toString(Verbosity.Id); 495 } 496 497 private String blockToString(AbstractBlockBase<?> block) { 498 if (lir == null && schedule == null && block instanceof Block) { 499 // During all the front-end phases, the block schedule is built only for the debug 500 // output. 501 // Therefore, the block numbers would be different for every CFG printed -> use the id 502 // of the first instruction. 503 return "B" + ((Block) block).getBeginNode().toString(Verbosity.Id); 504 } else { 505 // LIR instructions contain references to blocks and these blocks are printed as the 506 // blockID -> use the blockID. 507 return "B" + block.getId(); 508 } 509 } 510 511 public void printIntervals(String label, Interval[] intervals) { 512 begin("intervals"); 513 out.println(String.format("name \"%s\"", label)); 514 515 for (Interval interval : intervals) { 516 if (interval != null) { 517 printInterval(interval); 518 } 519 } 520 521 end("intervals"); 522 } 523 524 private void printInterval(Interval interval) { 525 out.printf("%s %s ", interval.operand, (isRegister(interval.operand) ? "fixed" : interval.kind().getPlatformKind())); 526 if (isRegister(interval.operand)) { 527 out.printf("\"[%s|%c]\"", interval.operand, interval.operand.getKind().getTypeChar()); 528 } else { 529 if (interval.location() != null) { 530 out.printf("\"[%s|%c]\"", interval.location(), interval.location().getKind().getTypeChar()); 531 } 532 } 533 534 Interval hint = interval.locationHint(false); 535 out.printf("%s %s ", interval.splitParent().operand, hint != null ? hint.operand : -1); 536 537 // print ranges 538 Range cur = interval.first(); 539 while (cur != Range.EndMarker) { 540 out.printf("[%d, %d[", cur.from, cur.to); 541 cur = cur.next; 542 assert cur != null : "range list not closed with range sentinel"; 543 } 544 545 // print use positions 546 int prev = -1; 547 UsePosList usePosList = interval.usePosList(); 548 for (int i = usePosList.size() - 1; i >= 0; --i) { 549 assert prev < usePosList.usePos(i) : "use positions not sorted"; 550 out.printf("%d %s ", usePosList.usePos(i), usePosList.registerPriority(i)); 551 prev = usePosList.usePos(i); 552 } 553 554 out.printf(" \"%s\"", interval.spillState()); 555 out.println(); 556 } 557 558 public void printStackIntervals(String label, StackInterval[] intervals) { 559 begin("intervals"); 560 out.println(String.format("name \"%s\"", label)); 561 562 for (StackInterval interval : intervals) { 563 if (interval != null) { 564 printStackInterval(interval); 565 } 566 } 567 568 end("intervals"); 569 } 570 571 private void printStackInterval(StackInterval interval) { 572 out.printf("%s %s ", interval.getOperand(), interval.isFixed() ? "fixed" : interval.kind().getPlatformKind()); 573 if (interval.location() != null) { 574 out.printf("\"[%s|%c]\"", interval.location(), interval.location().getKind().getTypeChar()); 575 } else { 576 out.printf("\"[%s|%c]\"", interval.getOperand(), interval.getOperand().getKind().getTypeChar()); 577 } 578 579 StackInterval hint = interval.locationHint(); 580 out.printf("%s %s ", interval.getOperand(), hint != null ? hint.getOperand() : -1); 581 582 out.printf("[%d, %d[", interval.from(), interval.to()); 583 584 // print spill state 585 out.printf(" \"NOT_SUPPORTED\""); 586 out.println(); 587 } 588 589 public void printSchedule(String message, SchedulePhase theSchedule) { 590 schedule = theSchedule; 591 cfg = schedule.getCFG(); 592 printedNodes = new NodeBitMap(cfg.graph); 593 594 begin("cfg"); 595 out.print("name \"").print(message).println('"'); 596 for (Block b : schedule.getCFG().getBlocks()) { 597 if (schedule.nodesFor(b) != null) { 598 printScheduledBlock(b, schedule.nodesFor(b)); 599 } 600 } 601 end("cfg"); 602 603 schedule = null; 604 cfg = null; 605 printedNodes = null; 606 } 607 608 private void printScheduledBlock(Block block, List<Node> nodesFor) { 609 printBlockProlog(block); 610 begin("IR"); 611 out.println("HIR"); 612 out.disableIndentation(); 613 614 if (block.getBeginNode() instanceof AbstractMergeNode) { 615 // Currently phi functions are not in the schedule, so print them separately here. 616 for (ValueNode phi : ((AbstractMergeNode) block.getBeginNode()).phis()) { 617 printNode(phi, false); 618 } 619 } 620 621 for (Node n : nodesFor) { 622 printNode(n, false); 623 } 624 625 out.enableIndentation(); 626 end("IR"); 627 628 printBlockEpilog(block); 629 } 630}