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