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