--- /dev/null 2017-01-22 10:16:57.869617664 -0800 +++ new/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java 2017-02-15 17:08:37.486835573 -0800 @@ -0,0 +1,651 @@ +/* + * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.printer; + +import static org.graalvm.compiler.graph.Edges.Type.Inputs; +import static org.graalvm.compiler.graph.Edges.Type.Successors; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.WritableByteChannel; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.compiler.core.common.cfg.BlockMap; +import org.graalvm.compiler.debug.Debug; +import org.graalvm.compiler.debug.GraalDebugConfig.Options; +import org.graalvm.compiler.graph.CachedGraph; +import org.graalvm.compiler.graph.Edges; +import org.graalvm.compiler.graph.Graph; +import org.graalvm.compiler.graph.InputEdges; +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.graph.NodeClass; +import org.graalvm.compiler.graph.NodeList; +import org.graalvm.compiler.graph.NodeMap; +import org.graalvm.compiler.nodes.AbstractBeginNode; +import org.graalvm.compiler.nodes.AbstractEndNode; +import org.graalvm.compiler.nodes.AbstractMergeNode; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.ControlSinkNode; +import org.graalvm.compiler.nodes.ControlSplitNode; +import org.graalvm.compiler.nodes.FixedNode; +import org.graalvm.compiler.nodes.PhiNode; +import org.graalvm.compiler.nodes.ProxyNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult; +import org.graalvm.compiler.nodes.VirtualState; +import org.graalvm.compiler.nodes.cfg.Block; +import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; +import org.graalvm.compiler.phases.schedule.SchedulePhase; + +import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.ResolvedJavaField; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.Signature; + +public class BinaryGraphPrinter implements GraphPrinter { + + private static final int CONSTANT_POOL_MAX_SIZE = 8000; + + private static final int BEGIN_GROUP = 0x00; + private static final int BEGIN_GRAPH = 0x01; + private static final int CLOSE_GROUP = 0x02; + + private static final int POOL_NEW = 0x00; + private static final int POOL_STRING = 0x01; + private static final int POOL_ENUM = 0x02; + private static final int POOL_CLASS = 0x03; + private static final int POOL_METHOD = 0x04; + private static final int POOL_NULL = 0x05; + private static final int POOL_NODE_CLASS = 0x06; + private static final int POOL_FIELD = 0x07; + private static final int POOL_SIGNATURE = 0x08; + + private static final int PROPERTY_POOL = 0x00; + private static final int PROPERTY_INT = 0x01; + private static final int PROPERTY_LONG = 0x02; + private static final int PROPERTY_DOUBLE = 0x03; + private static final int PROPERTY_FLOAT = 0x04; + private static final int PROPERTY_TRUE = 0x05; + private static final int PROPERTY_FALSE = 0x06; + private static final int PROPERTY_ARRAY = 0x07; + private static final int PROPERTY_SUBGRAPH = 0x08; + + private static final int KLASS = 0x00; + private static final int ENUM_KLASS = 0x01; + + static final int CURRENT_MAJOR_VERSION = 1; + static final int CURRENT_MINOR_VERSION = 0; + + static final byte[] MAGIC_BYTES = {'B', 'I', 'G', 'V'}; + + private void writeVersion() throws IOException { + writeBytesRaw(MAGIC_BYTES); + writeByte(CURRENT_MAJOR_VERSION); + writeByte(CURRENT_MINOR_VERSION); + } + + private static final class ConstantPool extends LinkedHashMap { + + private final LinkedList availableIds; + private char nextId; + private static final long serialVersionUID = -2676889957907285681L; + + ConstantPool() { + super(50, 0.65f); + availableIds = new LinkedList<>(); + } + + @Override + protected boolean removeEldestEntry(java.util.Map.Entry eldest) { + if (size() > CONSTANT_POOL_MAX_SIZE) { + availableIds.addFirst(eldest.getValue()); + return true; + } + return false; + } + + private Character nextAvailableId() { + if (!availableIds.isEmpty()) { + return availableIds.removeFirst(); + } + return nextId++; + } + + public char add(Object obj) { + Character id = nextAvailableId(); + put(obj, id); + return id; + } + } + + private final ConstantPool constantPool; + private final ByteBuffer buffer; + private final WritableByteChannel channel; + private final SnippetReflectionProvider snippetReflection; + + private static final Charset utf8 = Charset.forName("UTF-8"); + + public BinaryGraphPrinter(WritableByteChannel channel, SnippetReflectionProvider snippetReflection) throws IOException { + constantPool = new ConstantPool(); + buffer = ByteBuffer.allocateDirect(256 * 1024); + this.snippetReflection = snippetReflection; + this.channel = channel; + writeVersion(); + } + + @Override + public SnippetReflectionProvider getSnippetReflectionProvider() { + return snippetReflection; + } + + @Override + public void print(Graph graph, String title, Map properties) throws IOException { + writeByte(BEGIN_GRAPH); + writePoolObject(title); + writeGraph(graph, properties); + flush(); + } + + private void writeGraph(Graph graph, Map properties) throws IOException { + ScheduleResult scheduleResult = null; + if (graph instanceof StructuredGraph) { + + StructuredGraph structuredGraph = (StructuredGraph) graph; + scheduleResult = structuredGraph.getLastSchedule(); + if (scheduleResult == null) { + + // Also provide a schedule when an error occurs + if (Options.PrintIdealGraphSchedule.getValue() || Debug.contextLookup(Throwable.class) != null) { + try { + SchedulePhase schedule = new SchedulePhase(); + schedule.apply(structuredGraph); + scheduleResult = structuredGraph.getLastSchedule(); + } catch (Throwable t) { + } + } + + } + } + ControlFlowGraph cfg = scheduleResult == null ? Debug.contextLookup(ControlFlowGraph.class) : scheduleResult.getCFG(); + BlockMap> blockToNodes = scheduleResult == null ? null : scheduleResult.getBlockToNodesMap(); + NodeMap nodeToBlocks = scheduleResult == null ? null : scheduleResult.getNodeToBlockMap(); + List blocks = cfg == null ? null : Arrays.asList(cfg.getBlocks()); + writeProperties(properties); + writeNodes(graph, nodeToBlocks, cfg); + writeBlocks(blocks, blockToNodes); + } + + private void flush() throws IOException { + buffer.flip(); + channel.write(buffer); + buffer.compact(); + } + + private void ensureAvailable(int i) throws IOException { + assert buffer.capacity() >= i : "Can not make " + i + " bytes available, buffer is too small"; + while (buffer.remaining() < i) { + flush(); + } + } + + private void writeByte(int b) throws IOException { + ensureAvailable(1); + buffer.put((byte) b); + } + + private void writeInt(int b) throws IOException { + ensureAvailable(4); + buffer.putInt(b); + } + + private void writeLong(long b) throws IOException { + ensureAvailable(8); + buffer.putLong(b); + } + + private void writeDouble(double b) throws IOException { + ensureAvailable(8); + buffer.putDouble(b); + } + + private void writeFloat(float b) throws IOException { + ensureAvailable(4); + buffer.putFloat(b); + } + + private void writeShort(char b) throws IOException { + ensureAvailable(2); + buffer.putChar(b); + } + + private void writeString(String str) throws IOException { + byte[] bytes = str.getBytes(utf8); + writeBytes(bytes); + } + + private void writeBytes(byte[] b) throws IOException { + if (b == null) { + writeInt(-1); + } else { + writeInt(b.length); + writeBytesRaw(b); + } + } + + private void writeBytesRaw(byte[] b) throws IOException { + int bytesWritten = 0; + while (bytesWritten < b.length) { + int toWrite = Math.min(b.length - bytesWritten, buffer.capacity()); + ensureAvailable(toWrite); + buffer.put(b, bytesWritten, toWrite); + bytesWritten += toWrite; + } + } + + private void writeInts(int[] b) throws IOException { + if (b == null) { + writeInt(-1); + } else { + writeInt(b.length); + int sizeInBytes = b.length * 4; + ensureAvailable(sizeInBytes); + buffer.asIntBuffer().put(b); + buffer.position(buffer.position() + sizeInBytes); + } + } + + private void writeDoubles(double[] b) throws IOException { + if (b == null) { + writeInt(-1); + } else { + writeInt(b.length); + int sizeInBytes = b.length * 8; + ensureAvailable(sizeInBytes); + buffer.asDoubleBuffer().put(b); + buffer.position(buffer.position() + sizeInBytes); + } + } + + private void writePoolObject(Object object) throws IOException { + if (object == null) { + writeByte(POOL_NULL); + return; + } + Character id = constantPool.get(object); + if (id == null) { + addPoolEntry(object); + } else { + if (object instanceof Enum) { + writeByte(POOL_ENUM); + } else if (object instanceof Class || object instanceof JavaType) { + writeByte(POOL_CLASS); + } else if (object instanceof NodeClass) { + writeByte(POOL_NODE_CLASS); + } else if (object instanceof ResolvedJavaMethod) { + writeByte(POOL_METHOD); + } else if (object instanceof ResolvedJavaField) { + writeByte(POOL_FIELD); + } else if (object instanceof Signature) { + writeByte(POOL_SIGNATURE); + } else { + writeByte(POOL_STRING); + } + writeShort(id.charValue()); + } + } + + private static String getClassName(Class klass) { + if (!klass.isArray()) { + return klass.getName(); + } + return getClassName(klass.getComponentType()) + "[]"; + } + + private void addPoolEntry(Object object) throws IOException { + char index = constantPool.add(object); + writeByte(POOL_NEW); + writeShort(index); + if (object instanceof Class) { + Class klass = (Class) object; + writeByte(POOL_CLASS); + writeString(getClassName(klass)); + if (klass.isEnum()) { + writeByte(ENUM_KLASS); + Object[] enumConstants = klass.getEnumConstants(); + writeInt(enumConstants.length); + for (Object o : enumConstants) { + writePoolObject(((Enum) o).name()); + } + } else { + writeByte(KLASS); + } + } else if (object instanceof Enum) { + writeByte(POOL_ENUM); + writePoolObject(object.getClass()); + writeInt(((Enum) object).ordinal()); + } else if (object instanceof JavaType) { + JavaType type = (JavaType) object; + writeByte(POOL_CLASS); + writeString(type.toJavaName()); + writeByte(KLASS); + } else if (object instanceof NodeClass) { + NodeClass nodeClass = (NodeClass) object; + writeByte(POOL_NODE_CLASS); + writeString(nodeClass.getJavaClass().getSimpleName()); + writeString(nodeClass.getNameTemplate()); + writeEdgesInfo(nodeClass, Inputs); + writeEdgesInfo(nodeClass, Successors); + } else if (object instanceof ResolvedJavaMethod) { + writeByte(POOL_METHOD); + ResolvedJavaMethod method = ((ResolvedJavaMethod) object); + writePoolObject(method.getDeclaringClass()); + writePoolObject(method.getName()); + writePoolObject(method.getSignature()); + writeInt(method.getModifiers()); + writeBytes(method.getCode()); + } else if (object instanceof ResolvedJavaField) { + writeByte(POOL_FIELD); + ResolvedJavaField field = ((ResolvedJavaField) object); + writePoolObject(field.getDeclaringClass()); + writePoolObject(field.getName()); + writePoolObject(field.getType().getName()); + writeInt(field.getModifiers()); + } else if (object instanceof Signature) { + writeByte(POOL_SIGNATURE); + Signature signature = ((Signature) object); + int args = signature.getParameterCount(false); + writeShort((char) args); + for (int i = 0; i < args; i++) { + writePoolObject(signature.getParameterType(i, null).getName()); + } + writePoolObject(signature.getReturnType(null).getName()); + } else { + writeByte(POOL_STRING); + writeString(object.toString()); + } + } + + private void writeEdgesInfo(NodeClass nodeClass, Edges.Type type) throws IOException { + Edges edges = nodeClass.getEdges(type); + writeShort((char) edges.getCount()); + for (int i = 0; i < edges.getCount(); i++) { + writeByte(i < edges.getDirectCount() ? 0 : 1); + writePoolObject(edges.getName(i)); + if (type == Inputs) { + writePoolObject(((InputEdges) edges).getInputType(i)); + } + } + } + + private void writePropertyObject(Object obj) throws IOException { + if (obj instanceof Integer) { + writeByte(PROPERTY_INT); + writeInt(((Integer) obj).intValue()); + } else if (obj instanceof Long) { + writeByte(PROPERTY_LONG); + writeLong(((Long) obj).longValue()); + } else if (obj instanceof Double) { + writeByte(PROPERTY_DOUBLE); + writeDouble(((Double) obj).doubleValue()); + } else if (obj instanceof Float) { + writeByte(PROPERTY_FLOAT); + writeFloat(((Float) obj).floatValue()); + } else if (obj instanceof Boolean) { + if (((Boolean) obj).booleanValue()) { + writeByte(PROPERTY_TRUE); + } else { + writeByte(PROPERTY_FALSE); + } + } else if (obj instanceof Graph) { + writeByte(PROPERTY_SUBGRAPH); + writeGraph((Graph) obj, null); + } else if (obj instanceof CachedGraph) { + writeByte(PROPERTY_SUBGRAPH); + writeGraph(((CachedGraph) obj).getReadonlyCopy(), null); + } else if (obj != null && obj.getClass().isArray()) { + Class componentType = obj.getClass().getComponentType(); + if (componentType.isPrimitive()) { + if (componentType == Double.TYPE) { + writeByte(PROPERTY_ARRAY); + writeByte(PROPERTY_DOUBLE); + writeDoubles((double[]) obj); + } else if (componentType == Integer.TYPE) { + writeByte(PROPERTY_ARRAY); + writeByte(PROPERTY_INT); + writeInts((int[]) obj); + } else { + writeByte(PROPERTY_POOL); + writePoolObject(obj); + } + } else { + writeByte(PROPERTY_ARRAY); + writeByte(PROPERTY_POOL); + Object[] array = (Object[]) obj; + writeInt(array.length); + for (Object o : array) { + writePoolObject(o); + } + } + } else { + writeByte(PROPERTY_POOL); + writePoolObject(obj); + } + } + + @SuppressWarnings("deprecation") + private static int getNodeId(Node node) { + return node.getId(); + } + + private Object getBlockForNode(Node node, NodeMap nodeToBlocks) { + if (nodeToBlocks.isNew(node)) { + return "NEW (not in schedule)"; + } else { + Block block = nodeToBlocks.get(node); + if (block != null) { + return block.getId(); + } else if (node instanceof PhiNode) { + return getBlockForNode(((PhiNode) node).merge(), nodeToBlocks); + } + } + return null; + } + + private void writeNodes(Graph graph, NodeMap nodeToBlocks, ControlFlowGraph cfg) throws IOException { + Map props = new HashMap<>(); + + writeInt(graph.getNodeCount()); + + for (Node node : graph.getNodes()) { + NodeClass nodeClass = node.getNodeClass(); + node.getDebugProperties(props); + if (cfg != null && Options.PrintGraphProbabilities.getValue() && node instanceof FixedNode) { + try { + props.put("probability", cfg.blockFor(node).probability()); + } catch (Throwable t) { + props.put("probability", 0.0); + props.put("probability-exception", t); + } + } + if (nodeToBlocks != null) { + Object block = getBlockForNode(node, nodeToBlocks); + if (block != null) { + props.put("node-to-block", block); + } + } + + if (node instanceof ControlSinkNode) { + props.put("category", "controlSink"); + } else if (node instanceof ControlSplitNode) { + props.put("category", "controlSplit"); + } else if (node instanceof AbstractMergeNode) { + props.put("category", "merge"); + } else if (node instanceof AbstractBeginNode) { + props.put("category", "begin"); + } else if (node instanceof AbstractEndNode) { + props.put("category", "end"); + } else if (node instanceof FixedNode) { + props.put("category", "fixed"); + } else if (node instanceof VirtualState) { + props.put("category", "state"); + } else if (node instanceof PhiNode) { + props.put("category", "phi"); + } else if (node instanceof ProxyNode) { + props.put("category", "proxy"); + } else { + if (node instanceof ConstantNode) { + ConstantNode cn = (ConstantNode) node; + updateStringPropertiesForConstant(props, cn); + } + props.put("category", "floating"); + } + + writeInt(getNodeId(node)); + writePoolObject(nodeClass); + writeByte(node.predecessor() == null ? 0 : 1); + writeProperties(props); + writeEdges(node, Inputs); + writeEdges(node, Successors); + + props.clear(); + } + } + + private void writeProperties(Map props) throws IOException { + if (props == null) { + writeShort((char) 0); + return; + } + // properties + writeShort((char) props.size()); + for (Entry entry : props.entrySet()) { + String key = entry.getKey().toString(); + writePoolObject(key); + writePropertyObject(entry.getValue()); + } + } + + private void writeEdges(Node node, Edges.Type type) throws IOException { + NodeClass nodeClass = node.getNodeClass(); + Edges edges = nodeClass.getEdges(type); + final long[] curOffsets = edges.getOffsets(); + for (int i = 0; i < edges.getDirectCount(); i++) { + writeNodeRef(Edges.getNode(node, curOffsets, i)); + } + for (int i = edges.getDirectCount(); i < edges.getCount(); i++) { + NodeList list = Edges.getNodeList(node, curOffsets, i); + if (list == null) { + writeShort((char) 0); + } else { + int listSize = list.count(); + assert listSize == ((char) listSize); + writeShort((char) listSize); + for (Node edge : list) { + writeNodeRef(edge); + } + } + } + } + + private void writeNodeRef(Node edge) throws IOException { + if (edge != null) { + writeInt(getNodeId(edge)); + } else { + writeInt(-1); + } + } + + private void writeBlocks(List blocks, BlockMap> blockToNodes) throws IOException { + if (blocks != null && blockToNodes != null) { + for (Block block : blocks) { + List nodes = blockToNodes.get(block); + if (nodes == null) { + writeInt(0); + return; + } + } + writeInt(blocks.size()); + for (Block block : blocks) { + List nodes = blockToNodes.get(block); + List extraNodes = new LinkedList<>(); + writeInt(block.getId()); + for (Node node : nodes) { + if (node instanceof AbstractMergeNode) { + AbstractMergeNode merge = (AbstractMergeNode) node; + for (PhiNode phi : merge.phis()) { + if (!nodes.contains(phi)) { + extraNodes.add(phi); + } + } + } + } + writeInt(nodes.size() + extraNodes.size()); + for (Node node : nodes) { + writeInt(getNodeId(node)); + } + for (Node node : extraNodes) { + writeInt(getNodeId(node)); + } + writeInt(block.getSuccessors().length); + for (Block sux : block.getSuccessors()) { + writeInt(sux.getId()); + } + } + } else { + writeInt(0); + } + } + + @Override + public void beginGroup(String name, String shortName, ResolvedJavaMethod method, int bci, Map properties) throws IOException { + writeByte(BEGIN_GROUP); + writePoolObject(name); + writePoolObject(shortName); + writePoolObject(method); + writeInt(bci); + writeProperties(properties); + } + + @Override + public void endGroup() throws IOException { + writeByte(CLOSE_GROUP); + } + + @Override + public void close() { + try { + flush(); + channel.close(); + } catch (IOException ex) { + throw new Error(ex); + } + } +}