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}