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