1 /* 2 * Copyright (c) 2009, 2016, 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.code; 24 25 import static java.util.Collections.emptyList; 26 import static java.util.Collections.unmodifiableList; 27 import static jdk.vm.ci.meta.MetaUtil.identityHashCodeString; 28 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 import java.util.Collection; 32 import java.util.Collections; 33 import java.util.List; 34 import java.util.Objects; 35 36 import org.graalvm.compiler.graph.NodeSourcePosition; 37 38 import jdk.vm.ci.code.DebugInfo; 39 import jdk.vm.ci.code.StackSlot; 40 import jdk.vm.ci.code.site.Call; 41 import jdk.vm.ci.code.site.ConstantReference; 42 import jdk.vm.ci.code.site.DataPatch; 43 import jdk.vm.ci.code.site.DataSectionReference; 44 import jdk.vm.ci.code.site.ExceptionHandler; 45 import jdk.vm.ci.code.site.Infopoint; 46 import jdk.vm.ci.code.site.InfopointReason; 47 import jdk.vm.ci.code.site.Mark; 48 import jdk.vm.ci.code.site.Reference; 49 import jdk.vm.ci.code.site.Site; 50 import jdk.vm.ci.meta.Assumptions.Assumption; 51 import jdk.vm.ci.meta.InvokeTarget; 52 import jdk.vm.ci.meta.ResolvedJavaField; 53 import jdk.vm.ci.meta.ResolvedJavaMethod; 54 55 /** 56 * Represents the output from compiling a method, including the compiled machine code, associated 57 * data and references, relocation information, deoptimization information, etc. 58 */ 59 public class CompilationResult { 60 61 /** 62 * Provides extra information about instructions or data at specific positions in 63 * {@link CompilationResult#getTargetCode()}. This is optional information that can be used to 64 * enhance a disassembly of the code. 65 */ 66 public abstract static class CodeAnnotation { 67 68 public final int position; 69 70 public CodeAnnotation(int position) { 71 this.position = position; 72 } 73 74 @Override 75 public final int hashCode() { 76 throw new UnsupportedOperationException("hashCode"); 77 } 78 79 @Override 80 public String toString() { 81 return identityHashCodeString(this); 82 } 83 84 @Override 85 public abstract boolean equals(Object obj); 86 } 87 88 /** 89 * A string comment about one or more instructions at a specific position in the code. 90 */ 91 public static final class CodeComment extends CodeAnnotation { 92 93 public final String value; 94 95 public CodeComment(int position, String comment) { 96 super(position); 97 this.value = comment; 98 } 99 100 @Override 101 public boolean equals(Object obj) { 102 if (this == obj) { 103 return true; 104 } 105 if (obj instanceof CodeComment) { 106 CodeComment that = (CodeComment) obj; 107 if (this.position == that.position && this.value.equals(that.value)) { 108 return true; 109 } 110 } 111 return false; 112 } 113 114 @Override 115 public String toString() { 116 return getClass().getSimpleName() + "@" + position + ": " + value; 117 } 118 } 119 120 /** 121 * Describes a table of signed offsets embedded in the code. The offsets are relative to the 122 * starting address of the table. This type of table maybe generated when translating a 123 * multi-way branch based on a key value from a dense value set (e.g. the {@code tableswitch} 124 * JVM instruction). 125 * 126 * The table is indexed by the contiguous range of integers from {@link #low} to {@link #high} 127 * inclusive. 128 */ 129 public static final class JumpTable extends CodeAnnotation { 130 131 /** 132 * The low value in the key range (inclusive). 133 */ 134 public final int low; 135 136 /** 137 * The high value in the key range (inclusive). 138 */ 139 public final int high; 140 141 /** 142 * The size (in bytes) of each table entry. 143 */ 144 public final int entrySize; 145 146 public JumpTable(int position, int low, int high, int entrySize) { 147 super(position); 148 this.low = low; 149 this.high = high; 150 this.entrySize = entrySize; 151 } 152 153 @Override 154 public boolean equals(Object obj) { 155 if (this == obj) { 156 return true; 157 } 158 if (obj instanceof JumpTable) { 159 JumpTable that = (JumpTable) obj; 160 if (this.position == that.position && this.entrySize == that.entrySize && this.low == that.low && this.high == that.high) { 161 return true; 162 } 163 } 164 return false; 165 } 166 167 @Override 168 public String toString() { 169 return getClass().getSimpleName() + "@" + position + ": [" + low + " .. " + high + "]"; 170 } 171 } 172 173 private boolean closed; 174 175 private int entryBCI = -1; 176 177 private final DataSection dataSection = new DataSection(); 178 179 private final List<Infopoint> infopoints = new ArrayList<>(); 180 private final List<SourceMapping> sourceMapping = new ArrayList<>(); 181 private final List<DataPatch> dataPatches = new ArrayList<>(); 182 private final List<ExceptionHandler> exceptionHandlers = new ArrayList<>(); 183 private final List<Mark> marks = new ArrayList<>(); 184 185 private int totalFrameSize = -1; 186 private int maxInterpreterFrameSize = -1; 187 188 private StackSlot customStackArea = null; 189 190 private final String name; 191 192 /** 193 * The buffer containing the emitted machine code. 194 */ 195 private byte[] targetCode; 196 197 /** 198 * The leading number of bytes in {@link #targetCode} containing the emitted machine code. 199 */ 200 private int targetCodeSize; 201 202 private ArrayList<CodeAnnotation> annotations; 203 204 private Assumption[] assumptions; 205 206 /** 207 * The list of the methods whose bytecodes were used as input to the compilation. If 208 * {@code null}, then the compilation did not record method dependencies. Otherwise, the first 209 * element of this array is the root method of the compilation. 210 */ 211 private ResolvedJavaMethod[] methods; 212 213 /** 214 * The list of fields that were accessed from the bytecodes. 215 */ 216 private ResolvedJavaField[] fields; 217 218 private int bytecodeSize; 219 220 private boolean hasUnsafeAccess; 221 222 private boolean isImmutablePIC; 223 224 public CompilationResult() { 225 this(null, false); 226 } 227 228 public CompilationResult(String name) { 229 this(name, false); 230 } 231 232 public CompilationResult(boolean isImmutablePIC) { 233 this(null, isImmutablePIC); 234 } 235 236 public CompilationResult(String name, boolean isImmutablePIC) { 237 this.name = name; 238 this.isImmutablePIC = isImmutablePIC; 239 } 240 241 @Override 242 public int hashCode() { 243 // CompilationResult instances should not be used as hash map keys 244 throw new UnsupportedOperationException("hashCode"); 245 } 246 247 @Override 248 public String toString() { 249 if (methods != null) { 250 return getClass().getName() + "[" + methods[0].format("%H.%n(%p)%r") + "]"; 251 } 252 return identityHashCodeString(this); 253 } 254 255 @Override 256 public boolean equals(Object obj) { 257 if (this == obj) { 258 return true; 259 } 260 if (obj != null && obj.getClass() == getClass()) { 261 CompilationResult that = (CompilationResult) obj; 262 // @formatter:off 263 if (this.entryBCI == that.entryBCI && 264 Objects.equals(this.customStackArea, that.customStackArea) && 265 this.totalFrameSize == that.totalFrameSize && 266 this.targetCodeSize == that.targetCodeSize && 267 Objects.equals(this.name, that.name) && 268 Objects.equals(this.annotations, that.annotations) && 269 Objects.equals(this.dataSection, that.dataSection) && 270 Objects.equals(this.exceptionHandlers, that.exceptionHandlers) && 271 Objects.equals(this.dataPatches, that.dataPatches) && 272 Objects.equals(this.infopoints, that.infopoints) && 273 Objects.equals(this.marks, that.marks) && 274 Arrays.equals(this.assumptions, that.assumptions) && 275 Arrays.equals(targetCode, that.targetCode)) { 276 return true; 277 } 278 // @formatter:on 279 } 280 return false; 281 } 282 283 /** 284 * @return the entryBCI 285 */ 286 public int getEntryBCI() { 287 return entryBCI; 288 } 289 290 /** 291 * @param entryBCI the entryBCI to set 292 */ 293 public void setEntryBCI(int entryBCI) { 294 checkOpen(); 295 this.entryBCI = entryBCI; 296 } 297 298 /** 299 * Sets the assumptions made during compilation. 300 */ 301 public void setAssumptions(Assumption[] assumptions) { 302 checkOpen(); 303 this.assumptions = assumptions; 304 } 305 306 /** 307 * Gets the assumptions made during compilation. 308 * 309 * The caller must not modify the contents of the returned array. 310 */ 311 public Assumption[] getAssumptions() { 312 return assumptions; 313 } 314 315 /** 316 * Sets the methods whose bytecodes were used as input to the compilation. 317 * 318 * @param rootMethod the root method of the compilation 319 * @param inlinedMethods the methods inlined during compilation 320 */ 321 public void setMethods(ResolvedJavaMethod rootMethod, Collection<ResolvedJavaMethod> inlinedMethods) { 322 checkOpen(); 323 assert rootMethod != null; 324 assert inlinedMethods != null; 325 if (inlinedMethods.contains(rootMethod)) { 326 methods = inlinedMethods.toArray(new ResolvedJavaMethod[inlinedMethods.size()]); 327 for (int i = 0; i < methods.length; i++) { 328 if (methods[i].equals(rootMethod)) { 329 if (i != 0) { 330 ResolvedJavaMethod tmp = methods[0]; 331 methods[0] = methods[i]; 332 methods[i] = tmp; 333 } 334 break; 335 } 336 } 337 } else { 338 methods = new ResolvedJavaMethod[1 + inlinedMethods.size()]; 339 methods[0] = rootMethod; 340 int i = 1; 341 for (ResolvedJavaMethod m : inlinedMethods) { 342 methods[i++] = m; 343 } 344 } 345 } 346 347 /** 348 * Gets the methods whose bytecodes were used as input to the compilation. 349 * 350 * The caller must not modify the contents of the returned array. 351 * 352 * @return {@code null} if the compilation did not record method dependencies otherwise the 353 * methods whose bytecodes were used as input to the compilation with the first element 354 * being the root method of the compilation 355 */ 356 public ResolvedJavaMethod[] getMethods() { 357 return methods; 358 } 359 360 /** 361 * Sets the fields that were referenced from the bytecodes that were used as input to the 362 * compilation. 363 * 364 * @param accessedFields the collected set of fields accessed during compilation 365 */ 366 public void setFields(Collection<ResolvedJavaField> accessedFields) { 367 assert accessedFields != null; 368 fields = accessedFields.toArray(new ResolvedJavaField[accessedFields.size()]); 369 } 370 371 /** 372 * Gets the fields that were referenced from bytecodes that were used as input to the 373 * compilation. 374 * 375 * The caller must not modify the contents of the returned array. 376 * 377 * @return {@code null} if the compilation did not record fields dependencies otherwise the 378 * fields that were accessed from bytecodes were used as input to the compilation. 379 */ 380 public ResolvedJavaField[] getFields() { 381 return fields; 382 } 383 384 public void setBytecodeSize(int bytecodeSize) { 385 checkOpen(); 386 this.bytecodeSize = bytecodeSize; 387 } 388 389 public int getBytecodeSize() { 390 return bytecodeSize; 391 } 392 393 public DataSection getDataSection() { 394 return dataSection; 395 } 396 397 /** 398 * The total frame size of the method in bytes. This includes the return address pushed onto the 399 * stack, if any. 400 * 401 * @return the frame size 402 */ 403 public int getTotalFrameSize() { 404 assert totalFrameSize != -1 : "frame size not yet initialized!"; 405 return totalFrameSize; 406 } 407 408 /** 409 * Sets the total frame size in bytes. This includes the return address pushed onto the stack, 410 * if any. 411 * 412 * @param size the size of the frame in bytes 413 */ 414 public void setTotalFrameSize(int size) { 415 checkOpen(); 416 totalFrameSize = size; 417 } 418 419 public int getMaxInterpreterFrameSize() { 420 return maxInterpreterFrameSize; 421 } 422 423 public void setMaxInterpreterFrameSize(int maxInterpreterFrameSize) { 424 checkOpen(); 425 this.maxInterpreterFrameSize = maxInterpreterFrameSize; 426 } 427 428 public boolean isImmutablePIC() { 429 return this.isImmutablePIC; 430 } 431 432 /** 433 * Sets the machine that has been generated by the compiler. 434 * 435 * @param code the machine code generated 436 * @param size the size of the machine code 437 */ 438 public void setTargetCode(byte[] code, int size) { 439 checkOpen(); 440 targetCode = code; 441 targetCodeSize = size; 442 } 443 444 /** 445 * Records a data patch in the code section. The data patch can refer to something in the 446 * {@link DataSectionReference data section} or directly to an {@link ConstantReference inlined 447 * constant}. 448 * 449 * @param codePos the position in the code that needs to be patched 450 * @param ref the reference that should be inserted in the code 451 */ 452 public void recordDataPatch(int codePos, Reference ref) { 453 checkOpen(); 454 assert codePos >= 0 && ref != null; 455 dataPatches.add(new DataPatch(codePos, ref)); 456 } 457 458 /** 459 * Records a data patch in the code section. The data patch can refer to something in the 460 * {@link DataSectionReference data section} or directly to an {@link ConstantReference inlined 461 * constant}. 462 * 463 * @param codePos the position in the code that needs to be patched 464 * @param ref the reference that should be inserted in the code 465 * @param note a note attached to data patch for use by post-processing tools 466 */ 467 public void recordDataPatchWithNote(int codePos, Reference ref, Object note) { 468 assert codePos >= 0 && ref != null; 469 dataPatches.add(new DataPatch(codePos, ref, note)); 470 } 471 472 /** 473 * Records a call in the code array. 474 * 475 * @param codePos the position of the call in the code array 476 * @param size the size of the call instruction 477 * @param target the being called 478 * @param debugInfo the debug info for the call 479 * @param direct specifies if this is a {@linkplain Call#direct direct} call 480 */ 481 public void recordCall(int codePos, int size, InvokeTarget target, DebugInfo debugInfo, boolean direct) { 482 checkOpen(); 483 final Call call = new Call(target, codePos, size, direct, debugInfo); 484 addInfopoint(call); 485 } 486 487 /** 488 * Records an exception handler for this method. 489 * 490 * @param codePos the position in the code that is covered by the handler 491 * @param handlerPos the position of the handler 492 */ 493 public void recordExceptionHandler(int codePos, int handlerPos) { 494 checkOpen(); 495 assert validateExceptionHandlerAdd(codePos, handlerPos) : String.format("Duplicate exception handler for pc 0x%x handlerPos 0x%x", codePos, handlerPos); 496 exceptionHandlers.add(new ExceptionHandler(codePos, handlerPos)); 497 } 498 499 /** 500 * Validate if the exception handler for codePos already exists and handlerPos is different. 501 * 502 * @param codePos 503 * @param handlerPos 504 * @return true if the validation is successful 505 */ 506 private boolean validateExceptionHandlerAdd(int codePos, int handlerPos) { 507 ExceptionHandler exHandler = getExceptionHandlerForCodePos(codePos); 508 return exHandler == null || exHandler.handlerPos == handlerPos; 509 } 510 511 /** 512 * Returns the first ExceptionHandler which matches codePos. 513 * 514 * @param codePos position to search for 515 * @return first matching ExceptionHandler 516 */ 517 private ExceptionHandler getExceptionHandlerForCodePos(int codePos) { 518 for (ExceptionHandler h : exceptionHandlers) { 519 if (h.pcOffset == codePos) { 520 return h; 521 } 522 } 523 return null; 524 } 525 526 /** 527 * Records an infopoint in the code array. 528 * 529 * @param codePos the position of the infopoint in the code array 530 * @param debugInfo the debug info for the infopoint 531 */ 532 public void recordInfopoint(int codePos, DebugInfo debugInfo, InfopointReason reason) { 533 addInfopoint(new Infopoint(codePos, debugInfo, reason)); 534 } 535 536 /** 537 * Records a custom infopoint in the code section. 538 * 539 * Compiler implementations can use this method to record non-standard infopoints, which are not 540 * handled by dedicated methods like {@link #recordCall}. 541 * 542 * @param infopoint the infopoint to record, usually a derived class from {@link Infopoint} 543 */ 544 public void addInfopoint(Infopoint infopoint) { 545 checkOpen(); 546 infopoints.add(infopoint); 547 } 548 549 public void recordSourceMapping(int startOffset, int endOffset, NodeSourcePosition sourcePosition) { 550 checkOpen(); 551 sourceMapping.add(new SourceMapping(startOffset, endOffset, sourcePosition)); 552 } 553 554 /** 555 * Records an instruction mark within this method. 556 * 557 * @param codePos the position in the code that is covered by the handler 558 * @param markId the identifier for this mark 559 */ 560 public Mark recordMark(int codePos, Object markId) { 561 checkOpen(); 562 Mark mark = new Mark(codePos, markId); 563 marks.add(mark); 564 return mark; 565 } 566 567 /** 568 * Start of the custom stack area. 569 * 570 * @return the first stack slot of the custom stack area 571 */ 572 public StackSlot getCustomStackArea() { 573 return customStackArea; 574 } 575 576 /** 577 * @see #getCustomStackArea() 578 * @param slot 579 */ 580 public void setCustomStackAreaOffset(StackSlot slot) { 581 checkOpen(); 582 customStackArea = slot; 583 } 584 585 /** 586 * @return the machine code generated for this method 587 */ 588 public byte[] getTargetCode() { 589 return targetCode; 590 } 591 592 /** 593 * @return the size of the machine code generated for this method 594 */ 595 public int getTargetCodeSize() { 596 return targetCodeSize; 597 } 598 599 /** 600 * @return the code annotations or {@code null} if there are none 601 */ 602 public List<CodeAnnotation> getAnnotations() { 603 if (annotations == null) { 604 return Collections.emptyList(); 605 } 606 return annotations; 607 } 608 609 public void addAnnotation(CodeAnnotation annotation) { 610 checkOpen(); 611 assert annotation != null; 612 if (annotations == null) { 613 annotations = new ArrayList<>(); 614 } 615 annotations.add(annotation); 616 } 617 618 /** 619 * @return the list of infopoints, sorted by {@link Site#pcOffset} 620 */ 621 public List<Infopoint> getInfopoints() { 622 if (infopoints.isEmpty()) { 623 return emptyList(); 624 } 625 return unmodifiableList(infopoints); 626 } 627 628 /** 629 * @return the list of data references 630 */ 631 public List<DataPatch> getDataPatches() { 632 if (dataPatches.isEmpty()) { 633 return emptyList(); 634 } 635 return unmodifiableList(dataPatches); 636 } 637 638 /** 639 * @return the list of exception handlers 640 */ 641 public List<ExceptionHandler> getExceptionHandlers() { 642 if (exceptionHandlers.isEmpty()) { 643 return emptyList(); 644 } 645 return unmodifiableList(exceptionHandlers); 646 } 647 648 /** 649 * @return the list of marks 650 */ 651 public List<Mark> getMarks() { 652 if (marks.isEmpty()) { 653 return emptyList(); 654 } 655 return unmodifiableList(marks); 656 } 657 658 /** 659 * @return the list of {@link SourceMapping}s 660 */ 661 public List<SourceMapping> getSourceMappings() { 662 if (sourceMapping.isEmpty()) { 663 return emptyList(); 664 } 665 return unmodifiableList(sourceMapping); 666 } 667 668 public String getName() { 669 return name; 670 } 671 672 public void setHasUnsafeAccess(boolean hasUnsafeAccess) { 673 checkOpen(); 674 this.hasUnsafeAccess = hasUnsafeAccess; 675 } 676 677 public boolean hasUnsafeAccess() { 678 return hasUnsafeAccess; 679 } 680 681 /** 682 * Clears the information in this object pertaining to generating code. That is, the 683 * {@linkplain #getMarks() marks}, {@linkplain #getInfopoints() infopoints}, 684 * {@linkplain #getExceptionHandlers() exception handlers}, {@linkplain #getDataPatches() data 685 * patches} and {@linkplain #getAnnotations() annotations} recorded in this object are cleared. 686 */ 687 public void resetForEmittingCode() { 688 checkOpen(); 689 infopoints.clear(); 690 sourceMapping.clear(); 691 dataPatches.clear(); 692 exceptionHandlers.clear(); 693 marks.clear(); 694 dataSection.clear(); 695 if (annotations != null) { 696 annotations.clear(); 697 } 698 } 699 700 private void checkOpen() { 701 if (closed) { 702 throw new IllegalStateException(); 703 } 704 } 705 706 /** 707 * Closes this compilation result to future updates. 708 */ 709 public void close() { 710 if (closed) { 711 throw new IllegalStateException("Cannot re-close compilation result " + this); 712 } 713 dataSection.close(); 714 closed = true; 715 } 716 }