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 }