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 
  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 }