BinaryGraphPrinter.java revision 13017:134219a5b0ec
1/*
2 * Copyright (c) 2011, 2014, 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 */
23package org.graalvm.compiler.printer;
24
25import static org.graalvm.compiler.graph.Edges.Type.Inputs;
26import static org.graalvm.compiler.graph.Edges.Type.Successors;
27
28import java.io.IOException;
29import java.nio.ByteBuffer;
30import java.nio.channels.WritableByteChannel;
31import java.nio.charset.Charset;
32import java.util.Arrays;
33import java.util.HashMap;
34import java.util.LinkedHashMap;
35import java.util.LinkedList;
36import java.util.List;
37import java.util.Map;
38import java.util.Map.Entry;
39
40import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
41import org.graalvm.compiler.core.common.cfg.BlockMap;
42import org.graalvm.compiler.debug.Debug;
43import org.graalvm.compiler.debug.GraalDebugConfig.Options;
44import org.graalvm.compiler.graph.CachedGraph;
45import org.graalvm.compiler.graph.Edges;
46import org.graalvm.compiler.graph.Graph;
47import org.graalvm.compiler.graph.InputEdges;
48import org.graalvm.compiler.graph.Node;
49import org.graalvm.compiler.graph.NodeClass;
50import org.graalvm.compiler.graph.NodeList;
51import org.graalvm.compiler.graph.NodeMap;
52import org.graalvm.compiler.nodes.AbstractBeginNode;
53import org.graalvm.compiler.nodes.AbstractEndNode;
54import org.graalvm.compiler.nodes.AbstractMergeNode;
55import org.graalvm.compiler.nodes.ConstantNode;
56import org.graalvm.compiler.nodes.ControlSinkNode;
57import org.graalvm.compiler.nodes.ControlSplitNode;
58import org.graalvm.compiler.nodes.FixedNode;
59import org.graalvm.compiler.nodes.PhiNode;
60import org.graalvm.compiler.nodes.ProxyNode;
61import org.graalvm.compiler.nodes.StructuredGraph;
62import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
63import org.graalvm.compiler.nodes.VirtualState;
64import org.graalvm.compiler.nodes.cfg.Block;
65import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
66import org.graalvm.compiler.phases.schedule.SchedulePhase;
67
68import jdk.vm.ci.meta.JavaType;
69import jdk.vm.ci.meta.ResolvedJavaField;
70import jdk.vm.ci.meta.ResolvedJavaMethod;
71import jdk.vm.ci.meta.Signature;
72
73public class BinaryGraphPrinter implements GraphPrinter {
74
75    private static final int CONSTANT_POOL_MAX_SIZE = 8000;
76
77    private static final int BEGIN_GROUP = 0x00;
78    private static final int BEGIN_GRAPH = 0x01;
79    private static final int CLOSE_GROUP = 0x02;
80
81    private static final int POOL_NEW = 0x00;
82    private static final int POOL_STRING = 0x01;
83    private static final int POOL_ENUM = 0x02;
84    private static final int POOL_CLASS = 0x03;
85    private static final int POOL_METHOD = 0x04;
86    private static final int POOL_NULL = 0x05;
87    private static final int POOL_NODE_CLASS = 0x06;
88    private static final int POOL_FIELD = 0x07;
89    private static final int POOL_SIGNATURE = 0x08;
90
91    private static final int PROPERTY_POOL = 0x00;
92    private static final int PROPERTY_INT = 0x01;
93    private static final int PROPERTY_LONG = 0x02;
94    private static final int PROPERTY_DOUBLE = 0x03;
95    private static final int PROPERTY_FLOAT = 0x04;
96    private static final int PROPERTY_TRUE = 0x05;
97    private static final int PROPERTY_FALSE = 0x06;
98    private static final int PROPERTY_ARRAY = 0x07;
99    private static final int PROPERTY_SUBGRAPH = 0x08;
100
101    private static final int KLASS = 0x00;
102    private static final int ENUM_KLASS = 0x01;
103
104    static final int CURRENT_MAJOR_VERSION = 1;
105    static final int CURRENT_MINOR_VERSION = 0;
106
107    static final byte[] MAGIC_BYTES = {'B', 'I', 'G', 'V'};
108
109    private void writeVersion() throws IOException {
110        writeBytesRaw(MAGIC_BYTES);
111        writeByte(CURRENT_MAJOR_VERSION);
112        writeByte(CURRENT_MINOR_VERSION);
113    }
114
115    private static final class ConstantPool extends LinkedHashMap<Object, Character> {
116
117        private final LinkedList<Character> availableIds;
118        private char nextId;
119        private static final long serialVersionUID = -2676889957907285681L;
120
121        ConstantPool() {
122            super(50, 0.65f);
123            availableIds = new LinkedList<>();
124        }
125
126        @Override
127        protected boolean removeEldestEntry(java.util.Map.Entry<Object, Character> eldest) {
128            if (size() > CONSTANT_POOL_MAX_SIZE) {
129                availableIds.addFirst(eldest.getValue());
130                return true;
131            }
132            return false;
133        }
134
135        private Character nextAvailableId() {
136            if (!availableIds.isEmpty()) {
137                return availableIds.removeFirst();
138            }
139            return nextId++;
140        }
141
142        public char add(Object obj) {
143            Character id = nextAvailableId();
144            put(obj, id);
145            return id;
146        }
147    }
148
149    private final ConstantPool constantPool;
150    private final ByteBuffer buffer;
151    private final WritableByteChannel channel;
152    private SnippetReflectionProvider snippetReflection;
153
154    private static final Charset utf8 = Charset.forName("UTF-8");
155
156    public BinaryGraphPrinter(WritableByteChannel channel) throws IOException {
157        constantPool = new ConstantPool();
158        buffer = ByteBuffer.allocateDirect(256 * 1024);
159        this.channel = channel;
160        writeVersion();
161    }
162
163    @Override
164    public void setSnippetReflectionProvider(SnippetReflectionProvider snippetReflection) {
165        this.snippetReflection = snippetReflection;
166    }
167
168    @Override
169    public SnippetReflectionProvider getSnippetReflectionProvider() {
170        return snippetReflection;
171    }
172
173    @Override
174    public void print(Graph graph, String title, Map<Object, Object> properties) throws IOException {
175        writeByte(BEGIN_GRAPH);
176        writePoolObject(title);
177        writeGraph(graph, properties);
178        flush();
179    }
180
181    private void writeGraph(Graph graph, Map<Object, Object> properties) throws IOException {
182        ScheduleResult scheduleResult = null;
183        if (graph instanceof StructuredGraph) {
184
185            StructuredGraph structuredGraph = (StructuredGraph) graph;
186            scheduleResult = structuredGraph.getLastSchedule();
187            if (scheduleResult == null) {
188
189                // Also provide a schedule when an error occurs
190                if (Options.PrintGraphWithSchedule.getValue(graph.getOptions()) || Debug.contextLookup(Throwable.class) != null) {
191                    try {
192                        SchedulePhase schedule = new SchedulePhase(graph.getOptions());
193                        schedule.apply(structuredGraph);
194                        scheduleResult = structuredGraph.getLastSchedule();
195                    } catch (Throwable t) {
196                    }
197                }
198
199            }
200        }
201        ControlFlowGraph cfg = scheduleResult == null ? Debug.contextLookup(ControlFlowGraph.class) : scheduleResult.getCFG();
202        BlockMap<List<Node>> blockToNodes = scheduleResult == null ? null : scheduleResult.getBlockToNodesMap();
203        NodeMap<Block> nodeToBlocks = scheduleResult == null ? null : scheduleResult.getNodeToBlockMap();
204        List<Block> blocks = cfg == null ? null : Arrays.asList(cfg.getBlocks());
205        writeProperties(properties);
206        writeNodes(graph, nodeToBlocks, cfg);
207        writeBlocks(blocks, blockToNodes);
208    }
209
210    private void flush() throws IOException {
211        buffer.flip();
212        /*
213         * Try not to let interrupted threads aborting the write. There's still a race here but an
214         * interrupt that's been pending for a long time shouldn't stop this writing.
215         */
216        boolean interrupted = Thread.interrupted();
217        try {
218            channel.write(buffer);
219        } finally {
220            if (interrupted) {
221                Thread.currentThread().interrupt();
222            }
223        }
224        buffer.compact();
225    }
226
227    private void ensureAvailable(int i) throws IOException {
228        assert buffer.capacity() >= i : "Can not make " + i + " bytes available, buffer is too small";
229        while (buffer.remaining() < i) {
230            flush();
231        }
232    }
233
234    private void writeByte(int b) throws IOException {
235        ensureAvailable(1);
236        buffer.put((byte) b);
237    }
238
239    private void writeInt(int b) throws IOException {
240        ensureAvailable(4);
241        buffer.putInt(b);
242    }
243
244    private void writeLong(long b) throws IOException {
245        ensureAvailable(8);
246        buffer.putLong(b);
247    }
248
249    private void writeDouble(double b) throws IOException {
250        ensureAvailable(8);
251        buffer.putDouble(b);
252    }
253
254    private void writeFloat(float b) throws IOException {
255        ensureAvailable(4);
256        buffer.putFloat(b);
257    }
258
259    private void writeShort(char b) throws IOException {
260        ensureAvailable(2);
261        buffer.putChar(b);
262    }
263
264    private void writeString(String str) throws IOException {
265        byte[] bytes = str.getBytes(utf8);
266        writeBytes(bytes);
267    }
268
269    private void writeBytes(byte[] b) throws IOException {
270        if (b == null) {
271            writeInt(-1);
272        } else {
273            writeInt(b.length);
274            writeBytesRaw(b);
275        }
276    }
277
278    private void writeBytesRaw(byte[] b) throws IOException {
279        int bytesWritten = 0;
280        while (bytesWritten < b.length) {
281            int toWrite = Math.min(b.length - bytesWritten, buffer.capacity());
282            ensureAvailable(toWrite);
283            buffer.put(b, bytesWritten, toWrite);
284            bytesWritten += toWrite;
285        }
286    }
287
288    private void writeInts(int[] b) throws IOException {
289        if (b == null) {
290            writeInt(-1);
291        } else {
292            writeInt(b.length);
293            int sizeInBytes = b.length * 4;
294            ensureAvailable(sizeInBytes);
295            buffer.asIntBuffer().put(b);
296            buffer.position(buffer.position() + sizeInBytes);
297        }
298    }
299
300    private void writeDoubles(double[] b) throws IOException {
301        if (b == null) {
302            writeInt(-1);
303        } else {
304            writeInt(b.length);
305            int sizeInBytes = b.length * 8;
306            ensureAvailable(sizeInBytes);
307            buffer.asDoubleBuffer().put(b);
308            buffer.position(buffer.position() + sizeInBytes);
309        }
310    }
311
312    private void writePoolObject(Object object) throws IOException {
313        if (object == null) {
314            writeByte(POOL_NULL);
315            return;
316        }
317        Character id = constantPool.get(object);
318        if (id == null) {
319            addPoolEntry(object);
320        } else {
321            if (object instanceof Enum<?>) {
322                writeByte(POOL_ENUM);
323            } else if (object instanceof Class<?> || object instanceof JavaType) {
324                writeByte(POOL_CLASS);
325            } else if (object instanceof NodeClass) {
326                writeByte(POOL_NODE_CLASS);
327            } else if (object instanceof ResolvedJavaMethod) {
328                writeByte(POOL_METHOD);
329            } else if (object instanceof ResolvedJavaField) {
330                writeByte(POOL_FIELD);
331            } else if (object instanceof Signature) {
332                writeByte(POOL_SIGNATURE);
333            } else {
334                writeByte(POOL_STRING);
335            }
336            writeShort(id.charValue());
337        }
338    }
339
340    private static String getClassName(Class<?> klass) {
341        if (!klass.isArray()) {
342            return klass.getName();
343        }
344        return getClassName(klass.getComponentType()) + "[]";
345    }
346
347    private void addPoolEntry(Object object) throws IOException {
348        char index = constantPool.add(object);
349        writeByte(POOL_NEW);
350        writeShort(index);
351        if (object instanceof Class<?>) {
352            Class<?> klass = (Class<?>) object;
353            writeByte(POOL_CLASS);
354            writeString(getClassName(klass));
355            if (klass.isEnum()) {
356                writeByte(ENUM_KLASS);
357                Object[] enumConstants = klass.getEnumConstants();
358                writeInt(enumConstants.length);
359                for (Object o : enumConstants) {
360                    writePoolObject(((Enum<?>) o).name());
361                }
362            } else {
363                writeByte(KLASS);
364            }
365        } else if (object instanceof Enum<?>) {
366            writeByte(POOL_ENUM);
367            writePoolObject(object.getClass());
368            writeInt(((Enum<?>) object).ordinal());
369        } else if (object instanceof JavaType) {
370            JavaType type = (JavaType) object;
371            writeByte(POOL_CLASS);
372            writeString(type.toJavaName());
373            writeByte(KLASS);
374        } else if (object instanceof NodeClass) {
375            NodeClass<?> nodeClass = (NodeClass<?>) object;
376            writeByte(POOL_NODE_CLASS);
377            writeString(nodeClass.getJavaClass().getSimpleName());
378            writeString(nodeClass.getNameTemplate());
379            writeEdgesInfo(nodeClass, Inputs);
380            writeEdgesInfo(nodeClass, Successors);
381        } else if (object instanceof ResolvedJavaMethod) {
382            writeByte(POOL_METHOD);
383            ResolvedJavaMethod method = ((ResolvedJavaMethod) object);
384            writePoolObject(method.getDeclaringClass());
385            writePoolObject(method.getName());
386            writePoolObject(method.getSignature());
387            writeInt(method.getModifiers());
388            writeBytes(method.getCode());
389        } else if (object instanceof ResolvedJavaField) {
390            writeByte(POOL_FIELD);
391            ResolvedJavaField field = ((ResolvedJavaField) object);
392            writePoolObject(field.getDeclaringClass());
393            writePoolObject(field.getName());
394            writePoolObject(field.getType().getName());
395            writeInt(field.getModifiers());
396        } else if (object instanceof Signature) {
397            writeByte(POOL_SIGNATURE);
398            Signature signature = ((Signature) object);
399            int args = signature.getParameterCount(false);
400            writeShort((char) args);
401            for (int i = 0; i < args; i++) {
402                writePoolObject(signature.getParameterType(i, null).getName());
403            }
404            writePoolObject(signature.getReturnType(null).getName());
405        } else {
406            writeByte(POOL_STRING);
407            writeString(object.toString());
408        }
409    }
410
411    private void writeEdgesInfo(NodeClass<?> nodeClass, Edges.Type type) throws IOException {
412        Edges edges = nodeClass.getEdges(type);
413        writeShort((char) edges.getCount());
414        for (int i = 0; i < edges.getCount(); i++) {
415            writeByte(i < edges.getDirectCount() ? 0 : 1);
416            writePoolObject(edges.getName(i));
417            if (type == Inputs) {
418                writePoolObject(((InputEdges) edges).getInputType(i));
419            }
420        }
421    }
422
423    private void writePropertyObject(Object obj) throws IOException {
424        if (obj instanceof Integer) {
425            writeByte(PROPERTY_INT);
426            writeInt(((Integer) obj).intValue());
427        } else if (obj instanceof Long) {
428            writeByte(PROPERTY_LONG);
429            writeLong(((Long) obj).longValue());
430        } else if (obj instanceof Double) {
431            writeByte(PROPERTY_DOUBLE);
432            writeDouble(((Double) obj).doubleValue());
433        } else if (obj instanceof Float) {
434            writeByte(PROPERTY_FLOAT);
435            writeFloat(((Float) obj).floatValue());
436        } else if (obj instanceof Boolean) {
437            if (((Boolean) obj).booleanValue()) {
438                writeByte(PROPERTY_TRUE);
439            } else {
440                writeByte(PROPERTY_FALSE);
441            }
442        } else if (obj instanceof Graph) {
443            writeByte(PROPERTY_SUBGRAPH);
444            writeGraph((Graph) obj, null);
445        } else if (obj instanceof CachedGraph) {
446            writeByte(PROPERTY_SUBGRAPH);
447            writeGraph(((CachedGraph<?>) obj).getReadonlyCopy(), null);
448        } else if (obj != null && obj.getClass().isArray()) {
449            Class<?> componentType = obj.getClass().getComponentType();
450            if (componentType.isPrimitive()) {
451                if (componentType == Double.TYPE) {
452                    writeByte(PROPERTY_ARRAY);
453                    writeByte(PROPERTY_DOUBLE);
454                    writeDoubles((double[]) obj);
455                } else if (componentType == Integer.TYPE) {
456                    writeByte(PROPERTY_ARRAY);
457                    writeByte(PROPERTY_INT);
458                    writeInts((int[]) obj);
459                } else {
460                    writeByte(PROPERTY_POOL);
461                    writePoolObject(obj);
462                }
463            } else {
464                writeByte(PROPERTY_ARRAY);
465                writeByte(PROPERTY_POOL);
466                Object[] array = (Object[]) obj;
467                writeInt(array.length);
468                for (Object o : array) {
469                    writePoolObject(o);
470                }
471            }
472        } else {
473            writeByte(PROPERTY_POOL);
474            writePoolObject(obj);
475        }
476    }
477
478    @SuppressWarnings("deprecation")
479    private static int getNodeId(Node node) {
480        return node.getId();
481    }
482
483    private Object getBlockForNode(Node node, NodeMap<Block> nodeToBlocks) {
484        if (nodeToBlocks.isNew(node)) {
485            return "NEW (not in schedule)";
486        } else {
487            Block block = nodeToBlocks.get(node);
488            if (block != null) {
489                return block.getId();
490            } else if (node instanceof PhiNode) {
491                return getBlockForNode(((PhiNode) node).merge(), nodeToBlocks);
492            }
493        }
494        return null;
495    }
496
497    private void writeNodes(Graph graph, NodeMap<Block> nodeToBlocks, ControlFlowGraph cfg) throws IOException {
498        Map<Object, Object> props = new HashMap<>();
499
500        writeInt(graph.getNodeCount());
501
502        for (Node node : graph.getNodes()) {
503            NodeClass<?> nodeClass = node.getNodeClass();
504            node.getDebugProperties(props);
505            if (cfg != null && Options.PrintGraphProbabilities.getValue(graph.getOptions()) && node instanceof FixedNode) {
506                try {
507                    props.put("probability", cfg.blockFor(node).probability());
508                } catch (Throwable t) {
509                    props.put("probability", 0.0);
510                    props.put("probability-exception", t);
511                }
512            }
513
514            try {
515                props.put("NodeCost-Size", node.estimatedNodeSize());
516                props.put("NodeCost-Cycles", node.estimatedNodeCycles());
517            } catch (Throwable t) {
518                props.put("node-cost-exception", t.getMessage());
519            }
520
521            if (nodeToBlocks != null) {
522                Object block = getBlockForNode(node, nodeToBlocks);
523                if (block != null) {
524                    props.put("node-to-block", block);
525                }
526            }
527
528            if (node instanceof ControlSinkNode) {
529                props.put("category", "controlSink");
530            } else if (node instanceof ControlSplitNode) {
531                props.put("category", "controlSplit");
532            } else if (node instanceof AbstractMergeNode) {
533                props.put("category", "merge");
534            } else if (node instanceof AbstractBeginNode) {
535                props.put("category", "begin");
536            } else if (node instanceof AbstractEndNode) {
537                props.put("category", "end");
538            } else if (node instanceof FixedNode) {
539                props.put("category", "fixed");
540            } else if (node instanceof VirtualState) {
541                props.put("category", "state");
542            } else if (node instanceof PhiNode) {
543                props.put("category", "phi");
544            } else if (node instanceof ProxyNode) {
545                props.put("category", "proxy");
546            } else {
547                if (node instanceof ConstantNode) {
548                    ConstantNode cn = (ConstantNode) node;
549                    updateStringPropertiesForConstant(props, cn);
550                }
551                props.put("category", "floating");
552            }
553
554            writeInt(getNodeId(node));
555            writePoolObject(nodeClass);
556            writeByte(node.predecessor() == null ? 0 : 1);
557            writeProperties(props);
558            writeEdges(node, Inputs);
559            writeEdges(node, Successors);
560
561            props.clear();
562        }
563    }
564
565    private void writeProperties(Map<Object, Object> props) throws IOException {
566        if (props == null) {
567            writeShort((char) 0);
568            return;
569        }
570        // properties
571        writeShort((char) props.size());
572        for (Entry<Object, Object> entry : props.entrySet()) {
573            String key = entry.getKey().toString();
574            writePoolObject(key);
575            writePropertyObject(entry.getValue());
576        }
577    }
578
579    private void writeEdges(Node node, Edges.Type type) throws IOException {
580        NodeClass<?> nodeClass = node.getNodeClass();
581        Edges edges = nodeClass.getEdges(type);
582        final long[] curOffsets = edges.getOffsets();
583        for (int i = 0; i < edges.getDirectCount(); i++) {
584            writeNodeRef(Edges.getNode(node, curOffsets, i));
585        }
586        for (int i = edges.getDirectCount(); i < edges.getCount(); i++) {
587            NodeList<Node> list = Edges.getNodeList(node, curOffsets, i);
588            if (list == null) {
589                writeShort((char) 0);
590            } else {
591                int listSize = list.count();
592                assert listSize == ((char) listSize);
593                writeShort((char) listSize);
594                for (Node edge : list) {
595                    writeNodeRef(edge);
596                }
597            }
598        }
599    }
600
601    private void writeNodeRef(Node edge) throws IOException {
602        if (edge != null) {
603            writeInt(getNodeId(edge));
604        } else {
605            writeInt(-1);
606        }
607    }
608
609    private void writeBlocks(List<Block> blocks, BlockMap<List<Node>> blockToNodes) throws IOException {
610        if (blocks != null && blockToNodes != null) {
611            for (Block block : blocks) {
612                List<Node> nodes = blockToNodes.get(block);
613                if (nodes == null) {
614                    writeInt(0);
615                    return;
616                }
617            }
618            writeInt(blocks.size());
619            for (Block block : blocks) {
620                List<Node> nodes = blockToNodes.get(block);
621                List<Node> extraNodes = new LinkedList<>();
622                writeInt(block.getId());
623                for (Node node : nodes) {
624                    if (node instanceof AbstractMergeNode) {
625                        AbstractMergeNode merge = (AbstractMergeNode) node;
626                        for (PhiNode phi : merge.phis()) {
627                            if (!nodes.contains(phi)) {
628                                extraNodes.add(phi);
629                            }
630                        }
631                    }
632                }
633                writeInt(nodes.size() + extraNodes.size());
634                for (Node node : nodes) {
635                    writeInt(getNodeId(node));
636                }
637                for (Node node : extraNodes) {
638                    writeInt(getNodeId(node));
639                }
640                writeInt(block.getSuccessors().length);
641                for (Block sux : block.getSuccessors()) {
642                    writeInt(sux.getId());
643                }
644            }
645        } else {
646            writeInt(0);
647        }
648    }
649
650    @Override
651    public void beginGroup(String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) throws IOException {
652        writeByte(BEGIN_GROUP);
653        writePoolObject(name);
654        writePoolObject(shortName);
655        writePoolObject(method);
656        writeInt(bci);
657        writeProperties(properties);
658    }
659
660    @Override
661    public void endGroup() throws IOException {
662        writeByte(CLOSE_GROUP);
663    }
664
665    @Override
666    public void close() {
667        try {
668            flush();
669            channel.close();
670        } catch (IOException ex) {
671            throw new Error(ex);
672        }
673    }
674}
675