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