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