comparison graal/GraalCompiler/src/com/sun/c1x/debug/CFGPrinter.java @ 2509:16b9a8b5ad39

Renamings Runtime=>GraalRuntime and Compiler=>GraalCompiler
author Thomas Wuerthinger <thomas@wuerthinger.net>
date Wed, 27 Apr 2011 11:50:44 +0200
parents graal/Compiler/src/com/sun/c1x/debug/CFGPrinter.java@9ec15d6914ca
children a384fac3fd34
comparison
equal deleted inserted replaced
2508:fea94949e0a2 2509:16b9a8b5ad39
1 /*
2 * Copyright (c) 2009, 2011, 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 com.sun.c1x.debug;
24
25 import java.io.*;
26 import java.util.*;
27
28 import com.sun.c1x.*;
29 import com.sun.c1x.alloc.*;
30 import com.sun.c1x.alloc.Interval.*;
31 import com.sun.c1x.graph.*;
32 import com.sun.c1x.ir.*;
33 import com.sun.c1x.lir.*;
34 import com.sun.c1x.lir.LIRInstruction.*;
35 import com.sun.c1x.util.*;
36 import com.sun.c1x.value.*;
37 import com.sun.cri.ci.*;
38 import com.sun.cri.ci.CiAddress.*;
39 import com.sun.cri.ri.*;
40
41 /**
42 * Utility for printing the control flow graph of a method being compiled by C1X at various compilation phases.
43 * The output format matches that produced by HotSpot so that it can then be fed to the
44 * <a href="https://c1visualizer.dev.java.net/">C1 Visualizer</a>.
45 *
46 * @author Doug Simon
47 */
48 public class CFGPrinter {
49 private static final String COLUMN_END = " <|@";
50 private static final String HOVER_START = "<@";
51 private static final String HOVER_SEP = "|@";
52 private static final String HOVER_END = ">@";
53
54 private static OutputStream cfgFileStream;
55
56 /**
57 * Gets the output stream on the file "output.cfg" in the current working directory.
58 * This stream is first opened if necessary.
59 *
60 * @return the output stream to "output.cfg" or {@code null} if there was an error opening this file for writing
61 */
62 public static synchronized OutputStream cfgFileStream() {
63 if (cfgFileStream == null) {
64 File cfgFile = new File("output.cfg");
65 try {
66 cfgFileStream = new FileOutputStream(cfgFile);
67 } catch (FileNotFoundException e) {
68 TTY.println("WARNING: Could not open " + cfgFile.getAbsolutePath());
69 }
70 }
71 return cfgFileStream;
72 }
73
74 private final LogStream out;
75 private final CiTarget target;
76
77 /**
78 * Creates a control flow graph printer.
79 *
80 * @param os where the output generated via this printer shown be written
81 * @param target the target architecture description
82 */
83 public CFGPrinter(OutputStream os, CiTarget target) {
84 out = new LogStream(os);
85 this.target = target;
86 }
87
88 /**
89 * Flushes all buffered output to the stream passed to {@link #CFGPrinter(OutputStream, CiTarget)}.
90 */
91 public void flush() {
92 out.flush();
93 }
94
95 private void begin(String string) {
96 out.println("begin_" + string);
97 out.adjustIndentation(2);
98 }
99
100 private void end(String string) {
101 out.adjustIndentation(-2);
102 out.println("end_" + string);
103 }
104
105 /**
106 * Prints a compilation timestamp for a given method.
107 *
108 * @param method the method for which a timestamp will be printed
109 */
110 public void printCompilation(RiMethod method) {
111 begin("compilation");
112 out.print("name \" ").print(CiUtil.format("%H::%n", method, true)).println('"');
113 out.print("method \"").print(CiUtil.format("%f %r %H.%n(%p)", method, true)).println('"');
114 out.print("date ").println(System.currentTimeMillis());
115 end("compilation");
116 }
117
118 /**
119 * Print the details of a given control flow graph block.
120 *
121 * @param block the block to print
122 * @param successors the successor blocks of {@code block}
123 * @param handlers the exception handler blocks of {@code block}
124 * @param printHIR if {@code true} the HIR for each instruction in the block will be printed
125 * @param printLIR if {@code true} the LIR for each instruction in the block will be printed
126 */
127 void printBlock(BlockBegin block, List<BlockBegin> successors, Iterable<BlockBegin> handlers, boolean printHIR, boolean printLIR) {
128 begin("block");
129
130 out.print("name \"B").print(block.blockID).println('"');
131 out.print("from_bci ").println(block.bci());
132 out.print("to_bci ").println(block.end() == null ? -1 : block.end().bci());
133
134 out.print("predecessors ");
135 for (BlockBegin pred : block.predecessors()) {
136 out.print("\"B").print(pred.blockID).print("\" ");
137 }
138 out.println();
139
140 out.print("successors ");
141 for (BlockBegin succ : successors) {
142 out.print("\"B").print(succ.blockID).print("\" ");
143 }
144 out.println();
145
146 out.print("xhandlers");
147 for (BlockBegin handler : handlers) {
148 out.print("\"B").print(handler.blockID).print("\" ");
149 }
150 out.println();
151
152 out.print("flags ");
153 if (block.isStandardEntry()) {
154 out.print("\"std\" ");
155 }
156 if (block.isOsrEntry()) {
157 out.print("\"osr\" ");
158 }
159 if (block.isExceptionEntry()) {
160 out.print("\"ex\" ");
161 }
162 if (block.isSubroutineEntry()) {
163 out.print("\"sr\" ");
164 }
165 if (block.isBackwardBranchTarget()) {
166 out.print("\"bb\" ");
167 }
168 if (block.isParserLoopHeader()) {
169 out.print("\"plh\" ");
170 }
171 if (block.isCriticalEdgeSplit()) {
172 out.print("\"ces\" ");
173 }
174 if (block.isLinearScanLoopHeader()) {
175 out.print("\"llh\" ");
176 }
177 if (block.isLinearScanLoopEnd()) {
178 out.print("\"lle\" ");
179 }
180 out.println();
181
182 if (block.dominator() != null) {
183 out.print("dominator \"B").print(block.dominator().blockID).println('"');
184 }
185 if (block.loopIndex() != -1) {
186 out.print("loop_index ").println(block.loopIndex());
187 out.print("loop_depth ").println(block.loopDepth());
188 }
189
190 if (printHIR) {
191 printState(block);
192 printHIR(block);
193 }
194
195 if (printLIR) {
196 printLIR(block);
197 }
198
199 end("block");
200 }
201
202 /**
203 * Prints the JVM frame state upon entry to a given block.
204 *
205 * @param block the block for which the frame state is to be printed
206 */
207 private void printState(BlockBegin block) {
208 begin("states");
209
210 FrameState state = block.stateBefore();
211
212 do {
213 int stackSize = state.stackSize();
214 if (stackSize > 0) {
215 begin("stack");
216 out.print("size ").println(stackSize);
217 out.print("method \"").print(CiUtil.toLocation(state.scope().method, state.bci)).println('"');
218
219 int i = 0;
220 while (i < stackSize) {
221 Value value = state.stackAt(i);
222 out.disableIndentation();
223 out.print(block.stateString(i, value));
224 printOperand(value);
225 out.println();
226 out.enableIndentation();
227 if (value == null) {
228 i++;
229 } else {
230 i += value.kind.sizeInSlots();
231 }
232 }
233 end("stack");
234 }
235
236 if (state.locksSize() > 0) {
237 begin("locks");
238 out.print("size ").println(state.locksSize());
239 out.print("method \"").print(CiUtil.toLocation(state.scope().method, state.bci)).println('"');
240
241 for (int i = 0; i < state.locksSize(); ++i) {
242 Value value = state.lockAt(i);
243 out.disableIndentation();
244 out.print(block.stateString(i, value));
245 printOperand(value);
246 out.println();
247 out.enableIndentation();
248 }
249 end("locks");
250 }
251
252 begin("locals");
253 out.print("size ").println(state.localsSize());
254 out.print("method \"").print(CiUtil.toLocation(state.scope().method, state.bci)).println('"');
255 int i = 0;
256 while (i < state.localsSize()) {
257 Value value = state.localAt(i);
258 if (value != null) {
259 out.disableIndentation();
260 out.print(block.stateString(i, value));
261 printOperand(value);
262 out.println();
263 out.enableIndentation();
264 // also ignore illegal HiWords
265 i += value.isIllegal() ? 1 : value.kind.sizeInSlots();
266 } else {
267 i++;
268 }
269 }
270 state = state.callerState();
271 end("locals");
272 } while (state != null);
273
274 end("states");
275 }
276
277 /**
278 * Formats a given {@linkplain FrameState JVM frame state} as a multi line string.
279 */
280 private String stateToString(FrameState state, CFGOperandFormatter operandFmt) {
281 if (state == null) {
282 return null;
283 }
284
285 StringBuilder buf = new StringBuilder();
286
287 do {
288 buf.append(CiUtil.toLocation(state.scope().method, state.bci));
289 buf.append('\n');
290 if (state.stackSize() > 0) {
291 int i = 0;
292 buf.append("stack: ");
293 while (i < state.stackSize()) {
294 if (i == 0) {
295 buf.append(' ');
296 }
297 Value value = state.stackAt(i);
298 buf.append(stateValueToString(value, operandFmt)).append(' ');
299 i++;
300 }
301 buf.append("\n");
302 }
303
304 if (state.locksSize() > 0) {
305 buf.append("locks: ");
306 for (int i = 0; i < state.locksSize(); ++i) {
307 if (i == 0) {
308 buf.append(' ');
309 }
310 Value value = state.lockAt(i);
311 buf.append(stateValueToString(value, operandFmt)).append(' ');
312 }
313 buf.append("\n");
314 }
315
316 buf.append("locals: ");
317 int i = 0;
318 while (i < state.localsSize()) {
319 if (i == 0) {
320 buf.append(' ');
321 }
322 Value value = state.localAt(i);
323 buf.append(stateValueToString(value, operandFmt)).append(' ');
324 i++;
325 }
326 buf.append("\n");
327 state = state.callerState();
328 } while (state != null);
329 return buf.toString();
330 }
331
332 private String stateValueToString(Value value, OperandFormatter operandFmt) {
333 if (operandFmt == null) {
334 return Util.valueString(value);
335 }
336 if (value == null) {
337 return "-";
338 }
339 return operandFmt.format(value.operand());
340 }
341
342 private String stateValueToString(CiValue value, OperandFormatter operandFmt) {
343 if (value == null) {
344 return "-";
345 }
346 return operandFmt.format(value);
347 }
348
349 /**
350 * Formats a given {@linkplain FrameState JVM frame state} as a multi line string.
351 */
352 private String debugInfoToString(CiDebugInfo info, CFGOperandFormatter operandFmt) {
353 if (info == null) {
354 return null;
355 }
356 StringBuilder sb = new StringBuilder();
357
358
359 if (info.hasRegisterRefMap()) {
360 sb.append("reg-ref-map:");
361 C1XCompilation compilation = C1XCompilation.compilation();
362 CiArchitecture arch = compilation == null ? null : compilation.target.arch;
363 for (int reg = info.registerRefMap.nextSetBit(0); reg >= 0; reg = info.registerRefMap.nextSetBit(reg + 1)) {
364 sb.append(' ').append(arch == null ? "reg" + reg : arch.registers[reg]);
365 }
366 sb.append("\n");
367 }
368 if (info.hasStackRefMap()) {
369 sb.append("frame-ref-map:");
370 CiBitMap bm = info.frameRefMap;
371 for (int i = bm.nextSetBit(0); i >= 0; i = bm.nextSetBit(i + 1)) {
372 sb.append(' ').append(CiStackSlot.get(CiKind.Object, i));
373 }
374 sb.append("\n");
375 }
376 if (info.codePos != null) {
377 sb.append(stateToString(info.codePos, operandFmt));
378 }
379 return sb.toString();
380 }
381
382 /**
383 * Formats a given {@linkplain FrameState JVM frame state} as a multi line string.
384 */
385 private String stateToString(CiCodePos codePos, CFGOperandFormatter operandFmt) {
386 if (codePos == null) {
387 return null;
388 }
389
390 StringBuilder buf = new StringBuilder();
391
392 do {
393 buf.append(CiUtil.toLocation(codePos.method, codePos.bci));
394 buf.append('\n');
395 if (codePos instanceof CiFrame) {
396 CiFrame frame = (CiFrame) codePos;
397 if (frame.numStack > 0) {
398 int i = 0;
399 buf.append("stack: ");
400 while (i < frame.numStack) {
401 if (i == 0) {
402 buf.append(' ');
403 }
404 CiValue value = frame.getStackValue(i);
405 buf.append(stateValueToString(value, operandFmt)).append(' ');
406 i++;
407 }
408 buf.append("\n");
409 }
410
411 if (frame.numLocks > 0) {
412 buf.append("locks: ");
413 for (int i = 0; i < frame.numLocks; ++i) {
414 if (i == 0) {
415 buf.append(' ');
416 }
417 CiValue value = frame.getLockValue(i);
418 buf.append(stateValueToString(value, operandFmt)).append(' ');
419 }
420 buf.append("\n");
421 }
422
423 buf.append("locals: ");
424 int i = 0;
425 while (i < frame.numLocals) {
426 if (i == 0) {
427 buf.append(' ');
428 }
429 CiValue value = frame.getLocalValue(i);
430 buf.append(stateValueToString(value, operandFmt)).append(' ');
431 i++;
432 }
433 buf.append("\n");
434 }
435 codePos = codePos.caller;
436 } while (codePos != null);
437 return buf.toString();
438 }
439
440 /**
441 * Prints the HIR for each instruction in a given block.
442 *
443 * @param block
444 */
445 private void printHIR(BlockBegin block) {
446 begin("IR");
447 out.println("HIR");
448 out.disableIndentation();
449 for (Instruction i = block.next(); i != null; i = i.next()) {
450 printInstructionHIR(i);
451 }
452 out.enableIndentation();
453 end("IR");
454 }
455
456 /**
457 * Formats LIR operands as expected by the C1 Visualizer.
458 */
459 public static class CFGOperandFormatter extends OperandFormatter {
460 /**
461 * The textual delimiters used for an operand depend on the context in which it is being
462 * printed. When printed as part of a frame state or as the result operand in a HIR node listing,
463 * it is enclosed in double-quotes (i.e. {@code "}'s).
464 */
465 public final boolean asStateOrHIROperandResult;
466
467 public CFGOperandFormatter(boolean asStateOrHIROperandResult) {
468 this.asStateOrHIROperandResult = asStateOrHIROperandResult;
469 }
470
471 @Override
472 public String format(CiValue operand) {
473 if (operand.isLegal()) {
474 String op;
475 if (operand.isVariableOrRegister() || operand.isStackSlot()) {
476 op = operand.name();
477 } else if (operand.isConstant()) {
478 CiConstant constant = (CiConstant) operand;
479 op = operand.kind.javaName + ":" + operand.kind.format(constant.boxedValue());
480 } else if (operand.isAddress()) {
481 CiAddress address = (CiAddress) operand;
482 op = "Base:" + format(address.base);
483 if (!address.index.isIllegal()) {
484 op += " Index:" + format(address.index);
485 }
486 if (address.scale != Scale.Times1) {
487 op += " * " + address.scale.value;
488 }
489 op += " Disp:" + address.displacement;
490 } else {
491 assert operand.isIllegal();
492 op = "-";
493 }
494 if (operand.kind != CiKind.Illegal) {
495 op += "|" + operand.kind.typeChar;
496 }
497 if (asStateOrHIROperandResult) {
498 op = " \"" + op.replace('"', '\'') + "\" ";
499 }
500 return op;
501 }
502 return "";
503 }
504 }
505
506 /**
507 * Prints the LIR for each instruction in a given block.
508 *
509 * @param block the block to print
510 */
511 private void printLIR(BlockBegin block) {
512 LIRList lir = block.lir();
513 if (lir != null) {
514 begin("IR");
515 out.println("LIR");
516 for (int i = 0; i < lir.length(); i++) {
517 LIRInstruction inst = lir.at(i);
518 out.printf("nr %4d ", inst.id).print(COLUMN_END);
519
520 if (inst.info != null) {
521 int level = out.indentationLevel();
522 out.adjustIndentation(-level);
523 String state;
524 if (inst.info.debugInfo != null) {
525 // Use register-allocator output if available
526 state = debugInfoToString(inst.info.debugInfo, new CFGOperandFormatter(false));
527 } else {
528 state = stateToString(inst.info.state, new CFGOperandFormatter(false));
529 }
530 if (state != null) {
531 out.print(" st ").print(HOVER_START).print("st").print(HOVER_SEP).print(state).print(HOVER_END).print(COLUMN_END);
532 }
533 out.adjustIndentation(level);
534 }
535
536 out.print(" instruction ").print(inst.toString(new CFGOperandFormatter(false))).print(COLUMN_END);
537 out.println(COLUMN_END);
538 }
539 end("IR");
540 }
541 }
542
543 private void printOperand(Value i) {
544 if (i != null && i.operand().isLegal()) {
545 out.print(new CFGOperandFormatter(true).format(i.operand()));
546 }
547 }
548
549 /**
550 * Prints the HIR for a given instruction.
551 *
552 * @param i the instruction for which HIR will be printed
553 */
554 private void printInstructionHIR(Instruction i) {
555 out.print("bci ").print(i.bci()).println(COLUMN_END);
556 if (i.operand().isLegal()) {
557 out.print("result ").print(new CFGOperandFormatter(false).format(i.operand())).println(COLUMN_END);
558 }
559 out.print("tid ").print(i).println(COLUMN_END);
560
561 String state = stateToString(i.stateBefore(), null);
562 if (state != null) {
563 out.print("st ").print(HOVER_START).print("st").print(HOVER_SEP).print(state).print(HOVER_END).println(COLUMN_END);
564 }
565
566 out.print("instruction ");
567 i.print(out);
568 out.print(COLUMN_END).print(' ').println(COLUMN_END);
569 }
570
571 /**
572 * Prints the control flow graph denoted by a given block map.
573 *
574 * @param blockMap a data structure describing the blocks in a method and how they are connected
575 * @param codeSize the bytecode size of the method from which {@code blockMap} was produced
576 * @param label a label describing the compilation phase that produced the control flow graph
577 * @param printHIR if {@code true} the HIR for each instruction in the block will be printed
578 * @param printLIR if {@code true} the LIR for each instruction in the block will be printed
579 */
580 public void printCFG(RiMethod method, BlockMap blockMap, int codeSize, String label, boolean printHIR, boolean printLIR) {
581 begin("cfg");
582 out.print("name \"").print(label).println('"');
583 for (int bci = 0; bci < codeSize; ++bci) {
584 BlockBegin block = blockMap.get(bci);
585 if (block != null) {
586 printBlock(block, Arrays.asList(blockMap.getSuccessors(block)), blockMap.getHandlers(block), printHIR, printLIR);
587 }
588 }
589 end("cfg");
590 }
591
592 /**
593 * Prints the control flow graph rooted at a given block.
594 *
595 * @param startBlock the entry block of the control flow graph to be printed
596 * @param label a label describing the compilation phase that produced the control flow graph
597 * @param printHIR if {@code true} the HIR for each instruction in the block will be printed
598 * @param printLIR if {@code true} the LIR for each instruction in the block will be printed
599 */
600 public void printCFG(BlockBegin startBlock, String label, final boolean printHIR, final boolean printLIR) {
601 begin("cfg");
602 out.print("name \"").print(label).println('"');
603 startBlock.iteratePreOrder(new BlockClosure() {
604 public void apply(BlockBegin block) {
605 List<BlockBegin> successors = block.end() != null ? block.end().successors() : new ArrayList<BlockBegin>(0);
606 printBlock(block, successors, block.exceptionHandlerBlocks(), printHIR, printLIR);
607 }
608 });
609 end("cfg");
610 }
611
612 public void printIntervals(LinearScan allocator, Interval[] intervals, String name) {
613 begin("intervals");
614 out.println(String.format("name \"%s\"", name));
615
616 for (Interval interval : intervals) {
617 if (interval != null) {
618 printInterval(allocator, interval);
619 }
620 }
621
622 end("intervals");
623 }
624
625 private void printInterval(LinearScan allocator, Interval interval) {
626 out.printf("%d %s ", interval.operandNumber, (interval.operand.isRegister() ? "fixed" : interval.kind().name()));
627 if (interval.operand.isRegister()) {
628 out.printf("\"[%s|%c]\"", interval.operand.name(), interval.operand.kind.typeChar);
629 } else {
630 if (interval.location() != null) {
631 out.printf("\"[%s|%c]\"", interval.location().name(), interval.location().kind.typeChar);
632 }
633 }
634
635 Interval hint = interval.locationHint(false, allocator);
636 out.printf("%d %d ", interval.splitParent().operandNumber, hint != null ? hint.operandNumber : -1);
637
638 // print ranges
639 Range cur = interval.first();
640 while (cur != Range.EndMarker) {
641 out.printf("[%d, %d[", cur.from, cur.to);
642 cur = cur.next;
643 assert cur != null : "range list not closed with range sentinel";
644 }
645
646 // print use positions
647 int prev = 0;
648 UsePosList usePosList = interval.usePosList();
649 for (int i = usePosList.size() - 1; i >= 0; --i) {
650 assert prev < usePosList.usePos(i) : "use positions not sorted";
651 out.printf("%d %s ", usePosList.usePos(i), usePosList.registerPriority(i));
652 prev = usePosList.usePos(i);
653 }
654
655 out.printf(" \"%s\"", interval.spillState());
656 out.println();
657 }
658
659 public void printMachineCode(String code, String label) {
660 if (code.length() == 0) {
661 return;
662 }
663 if (label != null) {
664 begin("cfg");
665 out.print("name \"").print(label).println('"');
666 end("cfg");
667 }
668 begin("nmethod");
669 out.print(code);
670 out.println(" <|@");
671 end("nmethod");
672 }
673 }