001/*
002 * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.printer;
024
025import static com.oracle.graal.compiler.common.GraalOptions.*;
026import static com.oracle.graal.graph.Edges.Type.*;
027
028import java.io.*;
029import java.nio.*;
030import java.nio.channels.*;
031import java.util.*;
032import java.util.Map.Entry;
033
034import com.oracle.graal.debug.*;
035import jdk.internal.jvmci.meta.*;
036
037import com.oracle.graal.compiler.common.cfg.*;
038import com.oracle.graal.graph.*;
039import com.oracle.graal.nodes.*;
040import com.oracle.graal.nodes.cfg.*;
041import com.oracle.graal.phases.schedule.*;
042
043public class BinaryGraphPrinter implements GraphPrinter {
044
045    private static final int CONSTANT_POOL_MAX_SIZE = 8000;
046
047    private static final int BEGIN_GROUP = 0x00;
048    private static final int BEGIN_GRAPH = 0x01;
049    private static final int CLOSE_GROUP = 0x02;
050
051    private static final int POOL_NEW = 0x00;
052    private static final int POOL_STRING = 0x01;
053    private static final int POOL_ENUM = 0x02;
054    private static final int POOL_CLASS = 0x03;
055    private static final int POOL_METHOD = 0x04;
056    private static final int POOL_NULL = 0x05;
057    private static final int POOL_NODE_CLASS = 0x06;
058    private static final int POOL_FIELD = 0x07;
059    private static final int POOL_SIGNATURE = 0x08;
060
061    private static final int PROPERTY_POOL = 0x00;
062    private static final int PROPERTY_INT = 0x01;
063    private static final int PROPERTY_LONG = 0x02;
064    private static final int PROPERTY_DOUBLE = 0x03;
065    private static final int PROPERTY_FLOAT = 0x04;
066    private static final int PROPERTY_TRUE = 0x05;
067    private static final int PROPERTY_FALSE = 0x06;
068    private static final int PROPERTY_ARRAY = 0x07;
069    private static final int PROPERTY_SUBGRAPH = 0x08;
070
071    private static final int KLASS = 0x00;
072    private static final int ENUM_KLASS = 0x01;
073
074    private static final class ConstantPool extends LinkedHashMap<Object, Character> {
075
076        private final LinkedList<Character> availableIds;
077        private char nextId;
078        private static final long serialVersionUID = -2676889957907285681L;
079
080        public ConstantPool() {
081            super(50, 0.65f);
082            availableIds = new LinkedList<>();
083        }
084
085        @Override
086        protected boolean removeEldestEntry(java.util.Map.Entry<Object, Character> eldest) {
087            if (size() > CONSTANT_POOL_MAX_SIZE) {
088                availableIds.addFirst(eldest.getValue());
089                return true;
090            }
091            return false;
092        }
093
094        private Character nextAvailableId() {
095            if (!availableIds.isEmpty()) {
096                return availableIds.removeFirst();
097            }
098            return nextId++;
099        }
100
101        public char add(Object obj) {
102            Character id = nextAvailableId();
103            put(obj, id);
104            return id;
105        }
106    }
107
108    private final ConstantPool constantPool;
109    private final ByteBuffer buffer;
110    private final WritableByteChannel channel;
111
112    public BinaryGraphPrinter(WritableByteChannel channel) {
113        constantPool = new ConstantPool();
114        buffer = ByteBuffer.allocateDirect(256 * 1024);
115        this.channel = channel;
116    }
117
118    public void print(Graph graph, String title, SchedulePhase predefinedSchedule) throws IOException {
119        writeByte(BEGIN_GRAPH);
120        writePoolObject(title);
121        writeGraph(graph, predefinedSchedule);
122        flush();
123    }
124
125    private void writeGraph(Graph graph) throws IOException {
126        writeGraph(graph, null);
127    }
128
129    private void writeGraph(Graph graph, SchedulePhase predefinedSchedule) throws IOException {
130        SchedulePhase schedule = predefinedSchedule;
131        if (schedule == null) {
132            // Also provide a schedule when an error occurs
133            if (PrintIdealGraphSchedule.getValue() || Debug.contextLookup(Throwable.class) != null) {
134                try {
135                    schedule = new SchedulePhase();
136                    schedule.apply((StructuredGraph) graph);
137                } catch (Throwable t) {
138                }
139            }
140        }
141        ControlFlowGraph cfg = schedule == null ? null : schedule.getCFG();
142        BlockMap<List<Node>> blockToNodes = schedule == null ? null : schedule.getBlockToNodesMap();
143        NodeMap<Block> nodeToBlocks = schedule == null ? null : schedule.getNodeToBlockMap();
144        List<Block> blocks = cfg == null ? null : cfg.getBlocks();
145        writeNodes(graph, nodeToBlocks, cfg);
146        writeBlocks(blocks, blockToNodes);
147    }
148
149    private void flush() throws IOException {
150        buffer.flip();
151        channel.write(buffer);
152        buffer.compact();
153    }
154
155    private void ensureAvailable(int i) throws IOException {
156        assert buffer.capacity() >= i : "Can not make " + i + " bytes available, buffer is too small";
157        while (buffer.remaining() < i) {
158            flush();
159        }
160    }
161
162    private void writeByte(int b) throws IOException {
163        ensureAvailable(1);
164        buffer.put((byte) b);
165    }
166
167    private void writeInt(int b) throws IOException {
168        ensureAvailable(4);
169        buffer.putInt(b);
170    }
171
172    private void writeLong(long b) throws IOException {
173        ensureAvailable(8);
174        buffer.putLong(b);
175    }
176
177    private void writeDouble(double b) throws IOException {
178        ensureAvailable(8);
179        buffer.putDouble(b);
180    }
181
182    private void writeFloat(float b) throws IOException {
183        ensureAvailable(4);
184        buffer.putFloat(b);
185    }
186
187    private void writeShort(char b) throws IOException {
188        ensureAvailable(2);
189        buffer.putChar(b);
190    }
191
192    private void writeString(String str) throws IOException {
193        writeInt(str.length());
194        int sizeInBytes = str.length() * 2;
195        ensureAvailable(sizeInBytes);
196        buffer.asCharBuffer().put(str);
197        buffer.position(buffer.position() + sizeInBytes);
198    }
199
200    private void writeBytes(byte[] b) throws IOException {
201        if (b == null) {
202            writeInt(-1);
203        } else {
204            writeInt(b.length);
205            ensureAvailable(b.length);
206            buffer.put(b);
207        }
208    }
209
210    private void writeInts(int[] b) throws IOException {
211        if (b == null) {
212            writeInt(-1);
213        } else {
214            writeInt(b.length);
215            int sizeInBytes = b.length * 4;
216            ensureAvailable(sizeInBytes);
217            buffer.asIntBuffer().put(b);
218            buffer.position(buffer.position() + sizeInBytes);
219        }
220    }
221
222    private void writeDoubles(double[] b) throws IOException {
223        if (b == null) {
224            writeInt(-1);
225        } else {
226            writeInt(b.length);
227            int sizeInBytes = b.length * 8;
228            ensureAvailable(sizeInBytes);
229            buffer.asDoubleBuffer().put(b);
230            buffer.position(buffer.position() + sizeInBytes);
231        }
232    }
233
234    private void writePoolObject(Object object) throws IOException {
235        if (object == null) {
236            writeByte(POOL_NULL);
237            return;
238        }
239        Character id = constantPool.get(object);
240        if (id == null) {
241            addPoolEntry(object);
242        } else {
243            if (object instanceof Enum<?>) {
244                writeByte(POOL_ENUM);
245            } else if (object instanceof Class<?> || object instanceof JavaType) {
246                writeByte(POOL_CLASS);
247            } else if (object instanceof NodeClass) {
248                writeByte(POOL_NODE_CLASS);
249            } else if (object instanceof ResolvedJavaMethod) {
250                writeByte(POOL_METHOD);
251            } else if (object instanceof ResolvedJavaField) {
252                writeByte(POOL_FIELD);
253            } else if (object instanceof Signature) {
254                writeByte(POOL_SIGNATURE);
255            } else {
256                writeByte(POOL_STRING);
257            }
258            writeShort(id.charValue());
259        }
260    }
261
262    private static String getClassName(Class<?> klass) {
263        if (!klass.isArray()) {
264            return klass.getName();
265        }
266        return getClassName(klass.getComponentType()) + "[]";
267    }
268
269    private void addPoolEntry(Object object) throws IOException {
270        char index = constantPool.add(object);
271        writeByte(POOL_NEW);
272        writeShort(index);
273        if (object instanceof Class<?>) {
274            Class<?> klass = (Class<?>) object;
275            writeByte(POOL_CLASS);
276            writeString(getClassName(klass));
277            if (klass.isEnum()) {
278                writeByte(ENUM_KLASS);
279                Object[] enumConstants = klass.getEnumConstants();
280                writeInt(enumConstants.length);
281                for (Object o : enumConstants) {
282                    writePoolObject(((Enum<?>) o).name());
283                }
284            } else {
285                writeByte(KLASS);
286            }
287        } else if (object instanceof Enum<?>) {
288            writeByte(POOL_ENUM);
289            writePoolObject(object.getClass());
290            writeInt(((Enum<?>) object).ordinal());
291        } else if (object instanceof JavaType) {
292            JavaType type = (JavaType) object;
293            writeByte(POOL_CLASS);
294            writeString(type.toJavaName());
295            writeByte(KLASS);
296        } else if (object instanceof NodeClass) {
297            NodeClass<?> nodeClass = (NodeClass<?>) object;
298            writeByte(POOL_NODE_CLASS);
299            writeString(nodeClass.getJavaClass().getSimpleName());
300            writeString(nodeClass.getNameTemplate());
301            writeEdgesInfo(nodeClass, Inputs);
302            writeEdgesInfo(nodeClass, Successors);
303        } else if (object instanceof ResolvedJavaMethod) {
304            writeByte(POOL_METHOD);
305            ResolvedJavaMethod method = ((ResolvedJavaMethod) object);
306            writePoolObject(method.getDeclaringClass());
307            writePoolObject(method.getName());
308            writePoolObject(method.getSignature());
309            writeInt(method.getModifiers());
310            writeBytes(method.getCode());
311        } else if (object instanceof ResolvedJavaField) {
312            writeByte(POOL_FIELD);
313            ResolvedJavaField field = ((ResolvedJavaField) object);
314            writePoolObject(field.getDeclaringClass());
315            writePoolObject(field.getName());
316            writePoolObject(field.getType().getName());
317            writeInt(field.getModifiers());
318        } else if (object instanceof Signature) {
319            writeByte(POOL_SIGNATURE);
320            Signature signature = ((Signature) object);
321            int args = signature.getParameterCount(false);
322            writeShort((char) args);
323            for (int i = 0; i < args; i++) {
324                writePoolObject(signature.getParameterType(i, null).getName());
325            }
326            writePoolObject(signature.getReturnType(null).getName());
327        } else {
328            writeByte(POOL_STRING);
329            writeString(object.toString());
330        }
331    }
332
333    private void writeEdgesInfo(NodeClass<?> nodeClass, Edges.Type type) throws IOException {
334        Edges edges = nodeClass.getEdges(type);
335        writeShort((char) edges.getCount());
336        for (int i = 0; i < edges.getCount(); i++) {
337            writeByte(i < edges.getDirectCount() ? 0 : 1);
338            writePoolObject(edges.getName(i));
339            if (type == Inputs) {
340                writePoolObject(((InputEdges) edges).getInputType(i));
341            }
342        }
343    }
344
345    private void writePropertyObject(Object obj) throws IOException {
346        if (obj instanceof Integer) {
347            writeByte(PROPERTY_INT);
348            writeInt(((Integer) obj).intValue());
349        } else if (obj instanceof Long) {
350            writeByte(PROPERTY_LONG);
351            writeLong(((Long) obj).longValue());
352        } else if (obj instanceof Double) {
353            writeByte(PROPERTY_DOUBLE);
354            writeDouble(((Double) obj).doubleValue());
355        } else if (obj instanceof Float) {
356            writeByte(PROPERTY_FLOAT);
357            writeFloat(((Float) obj).floatValue());
358        } else if (obj instanceof Boolean) {
359            if (((Boolean) obj).booleanValue()) {
360                writeByte(PROPERTY_TRUE);
361            } else {
362                writeByte(PROPERTY_FALSE);
363            }
364        } else if (obj instanceof Graph) {
365            writeByte(PROPERTY_SUBGRAPH);
366            writeGraph((Graph) obj);
367        } else if (obj instanceof CachedGraph) {
368            writeByte(PROPERTY_SUBGRAPH);
369            writeGraph(((CachedGraph<?>) obj).getReadonlyCopy());
370        } else if (obj != null && obj.getClass().isArray()) {
371            Class<?> componentType = obj.getClass().getComponentType();
372            if (componentType.isPrimitive()) {
373                if (componentType == Double.TYPE) {
374                    writeByte(PROPERTY_ARRAY);
375                    writeByte(PROPERTY_DOUBLE);
376                    writeDoubles((double[]) obj);
377                } else if (componentType == Integer.TYPE) {
378                    writeByte(PROPERTY_ARRAY);
379                    writeByte(PROPERTY_INT);
380                    writeInts((int[]) obj);
381                } else {
382                    writeByte(PROPERTY_POOL);
383                    writePoolObject(obj);
384                }
385            } else {
386                writeByte(PROPERTY_ARRAY);
387                writeByte(PROPERTY_POOL);
388                Object[] array = (Object[]) obj;
389                writeInt(array.length);
390                for (Object o : array) {
391                    writePoolObject(o);
392                }
393            }
394        } else {
395            writeByte(PROPERTY_POOL);
396            writePoolObject(obj);
397        }
398    }
399
400    @SuppressWarnings("deprecation")
401    private static int getNodeId(Node node) {
402        return node.getId();
403    }
404
405    private void writeNodes(Graph graph, NodeMap<Block> nodeToBlocks, ControlFlowGraph cfg) throws IOException {
406        Map<Object, Object> props = new HashMap<>();
407
408        writeInt(graph.getNodeCount());
409
410        for (Node node : graph.getNodes()) {
411            NodeClass<?> nodeClass = node.getNodeClass();
412            node.getDebugProperties(props);
413            if (cfg != null && PrintGraphProbabilities.getValue() && node instanceof FixedNode) {
414                try {
415                    props.put("probability", cfg.blockFor(node).probability());
416                } catch (Throwable t) {
417                    props.put("probability", t);
418                }
419            }
420            if (nodeToBlocks != null) {
421                if (nodeToBlocks.isNew(node)) {
422                    props.put("node-to-block", "NEW (not in schedule)");
423                } else {
424                    Block block = nodeToBlocks.get(node);
425                    if (block != null) {
426                        props.put("node-to-block", block.getId());
427                    }
428                }
429            }
430
431            if (node instanceof ControlSinkNode) {
432                props.put("category", "controlSink");
433            } else if (node instanceof ControlSplitNode) {
434                props.put("category", "controlSplit");
435            } else if (node instanceof AbstractMergeNode) {
436                props.put("category", "merge");
437            } else if (node instanceof AbstractBeginNode) {
438                props.put("category", "begin");
439            } else if (node instanceof AbstractEndNode) {
440                props.put("category", "end");
441            } else if (node instanceof FixedNode) {
442                props.put("category", "fixed");
443            } else if (node instanceof VirtualState) {
444                props.put("category", "state");
445            } else if (node instanceof PhiNode) {
446                props.put("category", "phi");
447            } else if (node instanceof ProxyNode) {
448                props.put("category", "proxy");
449            } else {
450                props.put("category", "floating");
451            }
452
453            writeInt(getNodeId(node));
454            writePoolObject(nodeClass);
455            writeByte(node.predecessor() == null ? 0 : 1);
456            // properties
457            writeShort((char) props.size());
458            for (Entry<Object, Object> entry : props.entrySet()) {
459                String key = entry.getKey().toString();
460                writePoolObject(key);
461                writePropertyObject(entry.getValue());
462            }
463            writeEdges(node, Inputs);
464            writeEdges(node, Successors);
465
466            props.clear();
467        }
468    }
469
470    private void writeEdges(Node node, Edges.Type type) throws IOException {
471        NodeClass<?> nodeClass = node.getNodeClass();
472        Edges edges = nodeClass.getEdges(type);
473        final long[] curOffsets = edges.getOffsets();
474        for (int i = 0; i < edges.getDirectCount(); i++) {
475            writeNodeRef(Edges.getNode(node, curOffsets, i));
476        }
477        for (int i = edges.getDirectCount(); i < edges.getCount(); i++) {
478            NodeList<Node> list = Edges.getNodeList(node, curOffsets, i);
479            if (list == null) {
480                writeShort((char) 0);
481            } else {
482                int listSize = list.count();
483                assert listSize == ((char) listSize);
484                writeShort((char) listSize);
485                for (Node edge : list) {
486                    writeNodeRef(edge);
487                }
488            }
489        }
490    }
491
492    private void writeNodeRef(Node edge) throws IOException {
493        if (edge != null) {
494            writeInt(getNodeId(edge));
495        } else {
496            writeInt(-1);
497        }
498    }
499
500    private void writeBlocks(List<Block> blocks, BlockMap<List<Node>> blockToNodes) throws IOException {
501        if (blocks != null && blockToNodes != null) {
502            for (Block block : blocks) {
503                List<Node> nodes = blockToNodes.get(block);
504                if (nodes == null) {
505                    writeInt(0);
506                    return;
507                }
508            }
509            writeInt(blocks.size());
510            for (Block block : blocks) {
511                List<Node> nodes = blockToNodes.get(block);
512                writeInt(block.getId());
513                writeInt(nodes.size());
514                for (Node node : nodes) {
515                    writeInt(getNodeId(node));
516                }
517                writeInt(block.getSuccessors().size());
518                for (Block sux : block.getSuccessors()) {
519                    writeInt(sux.getId());
520                }
521            }
522        } else {
523            writeInt(0);
524        }
525    }
526
527    public void beginGroup(String name, String shortName, ResolvedJavaMethod method, int bci) throws IOException {
528        writeByte(BEGIN_GROUP);
529        writePoolObject(name);
530        writePoolObject(shortName);
531        writePoolObject(method);
532        writeInt(bci);
533    }
534
535    public void endGroup() throws IOException {
536        writeByte(CLOSE_GROUP);
537    }
538
539    @Override
540    public void close() {
541        try {
542            flush();
543            channel.close();
544        } catch (IOException ex) {
545            throw new Error(ex);
546        }
547    }
548}