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