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