1 /*
   2  * Copyright (c) 2009, 2014, 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 jdk.internal.jvmci.code;
  24 
  25 import static java.util.Collections.*;
  26 import static jdk.internal.jvmci.meta.MetaUtil.*;
  27 
  28 import java.util.*;
  29 
  30 import jdk.internal.jvmci.meta.*;
  31 import jdk.internal.jvmci.meta.Assumptions.*;
  32 
  33 /**
  34  * Represents the output from compiling a method, including the compiled machine code, associated
  35  * data and references, relocation information, deoptimization information, etc.
  36  */
  37 public class CompilationResult {
  38 
  39     /**
  40      * Represents a code position with associated additional information.
  41      */
  42     public abstract static class Site {
  43 
  44         /**
  45          * The position (or offset) of this site with respect to the start of the target method.
  46          */
  47         public final int pcOffset;
  48 
  49         public Site(int pos) {
  50             this.pcOffset = pos;
  51         }
  52 
  53         @Override
  54         public final int hashCode() {
  55             throw new UnsupportedOperationException("hashCode");
  56         }
  57 
  58         @Override
  59         public String toString() {
  60             return identityHashCodeString(this);
  61         }
  62 
  63         @Override
  64         public abstract boolean equals(Object obj);
  65     }
  66 
  67     /**
  68      * Represents an infopoint with associated debug info. Note that safepoints are also infopoints.
  69      */
  70     public static class Infopoint extends Site implements Comparable<Infopoint> {
  71 
  72         public final DebugInfo debugInfo;
  73 
  74         public final InfopointReason reason;
  75 
  76         public Infopoint(int pcOffset, DebugInfo debugInfo, InfopointReason reason) {
  77             super(pcOffset);
  78             this.debugInfo = debugInfo;
  79             this.reason = reason;
  80         }
  81 
  82         @Override
  83         public String toString() {
  84             StringBuilder sb = new StringBuilder();
  85             sb.append(pcOffset);
  86             sb.append("[<infopoint>]");
  87             appendDebugInfo(sb, debugInfo);
  88             return sb.toString();
  89         }
  90 
  91         @Override
  92         public int compareTo(Infopoint o) {
  93             if (pcOffset < o.pcOffset) {
  94                 return -1;
  95             } else if (pcOffset > o.pcOffset) {
  96                 return 1;
  97             }
  98             return this.reason.compareTo(o.reason);
  99         }
 100 
 101         @Override
 102         public boolean equals(Object obj) {
 103             if (this == obj) {
 104                 return true;
 105             }
 106             if (obj != null && obj.getClass() == getClass()) {
 107                 Infopoint that = (Infopoint) obj;
 108                 if (this.pcOffset == that.pcOffset && Objects.equals(this.debugInfo, that.debugInfo) && Objects.equals(this.reason, that.reason)) {
 109                     return true;
 110                 }
 111             }
 112             return false;
 113         }
 114     }
 115 
 116     public enum MetaSpaceAccessType {
 117         Move,
 118         Store, // store only works for compressed oops (memory <- 32bit value). Compressed oops is
 119                // not supported using AOT. TODO: Look at HotSpotStoreConstantOp
 120         Compare; // HotSpotCompareMemoryConstantOp, HotSpotCompareConstantOp
 121 
 122         private MetaSpaceAccessType() {
 123         }
 124     }
 125 
 126     /**
 127      * Represents a meta space pointer access in the code.
 128      */
 129     public static final class MetaSpaceAccess extends Infopoint {
 130 
 131         private static final long serialVersionUID = 1701958512608684706L;
 132 
 133         /**
 134          * Metaspace reference.
 135          */
 136         public final Object reference; // Object here is a HotSpotResolvedObjectType or a
 137                                        // HotSpotMetaSpaceConstant
 138 
 139         public final MetaSpaceAccessType type;
 140 
 141         /**
 142          * Instruction size.
 143          */
 144         public final int instructionSize;
 145 
 146         public MetaSpaceAccess(Object reference, int instructionSize, MetaSpaceAccessType type, int pcOffset, DebugInfo debugInfo) {
 147             super(pcOffset, debugInfo, InfopointReason.METASPACE_ACCESS);
 148             this.type = type;
 149             this.reference = reference;
 150             this.instructionSize = instructionSize;
 151         }
 152     }
 153 
 154     /**
 155      * Represents a call in the code.
 156      */
 157     public static final class Call extends Infopoint {
 158 
 159         /**
 160          * The target of the call.
 161          */
 162         public final InvokeTarget target;
 163 
 164         /**
 165          * The size of the call instruction.
 166          */
 167         public final int size;
 168 
 169         /**
 170          * Specifies if this call is direct or indirect. A direct call has an immediate operand
 171          * encoding the absolute or relative (to the call itself) address of the target. An indirect
 172          * call has a register or memory operand specifying the target address of the call.
 173          */
 174         public final boolean direct;
 175 
 176         public Call(InvokeTarget target, int pcOffset, int size, boolean direct, DebugInfo debugInfo) {
 177             super(pcOffset, debugInfo, InfopointReason.CALL);
 178             this.size = size;
 179             this.target = target;
 180             this.direct = direct;
 181         }
 182 
 183         @Override
 184         public boolean equals(Object obj) {
 185             if (this == obj) {
 186                 return true;
 187             }
 188             if (obj instanceof Call && super.equals(obj)) {
 189                 Call that = (Call) obj;
 190                 if (this.size == that.size && this.direct == that.direct && Objects.equals(this.target, that.target)) {
 191                     return true;
 192                 }
 193             }
 194             return false;
 195         }
 196 
 197         @Override
 198         public String toString() {
 199             StringBuilder sb = new StringBuilder();
 200             sb.append(pcOffset);
 201             sb.append('[');
 202             sb.append(target);
 203             sb.append(']');
 204 
 205             if (debugInfo != null) {
 206                 appendDebugInfo(sb, debugInfo);
 207             }
 208 
 209             return sb.toString();
 210         }
 211     }
 212 
 213     /**
 214      * Represents some external data that is referenced by the code.
 215      */
 216     public abstract static class Reference {
 217 
 218         @Override
 219         public abstract int hashCode();
 220 
 221         @Override
 222         public abstract boolean equals(Object obj);
 223     }
 224 
 225     public static final class ConstantReference extends Reference {
 226 
 227         private final VMConstant constant;
 228 
 229         public ConstantReference(VMConstant constant) {
 230             this.constant = constant;
 231         }
 232 
 233         public VMConstant getConstant() {
 234             return constant;
 235         }
 236 
 237         @Override
 238         public String toString() {
 239             return constant.toString();
 240         }
 241 
 242         @Override
 243         public int hashCode() {
 244             return constant.hashCode();
 245         }
 246 
 247         @Override
 248         public boolean equals(Object obj) {
 249             if (this == obj) {
 250                 return true;
 251             }
 252             if (obj instanceof ConstantReference) {
 253                 ConstantReference that = (ConstantReference) obj;
 254                 return Objects.equals(this.constant, that.constant);
 255             }
 256             return false;
 257         }
 258     }
 259 
 260     public static final class DataSectionReference extends Reference {
 261 
 262         private boolean initialized;
 263         private int offset;
 264 
 265         public DataSectionReference() {
 266             // will be set after the data section layout is fixed
 267             offset = 0xDEADDEAD;
 268         }
 269 
 270         public int getOffset() {
 271             assert initialized;
 272 
 273             return offset;
 274         }
 275 
 276         public void setOffset(int offset) {
 277             assert !initialized;
 278             initialized = true;
 279 
 280             this.offset = offset;
 281         }
 282 
 283         @Override
 284         public int hashCode() {
 285             return offset;
 286         }
 287 
 288         @Override
 289         public boolean equals(Object obj) {
 290             if (this == obj) {
 291                 return true;
 292             }
 293             if (obj instanceof DataSectionReference) {
 294                 DataSectionReference that = (DataSectionReference) obj;
 295                 return this.offset == that.offset;
 296             }
 297             return false;
 298         }
 299     }
 300 
 301     /**
 302      * Represents a code site that references some data. The associated data can be either a
 303      * {@link DataSectionReference reference} to the data section, or it may be an inlined
 304      * {@link JavaConstant} that needs to be patched.
 305      */
 306     public static final class DataPatch extends Site {
 307 
 308         public Reference reference;
 309         public Object note;
 310 
 311         public DataPatch(int pcOffset, Reference reference) {
 312             super(pcOffset);
 313             this.reference = reference;
 314             this.note = null;
 315         }
 316 
 317         public DataPatch(int pcOffset, Reference reference, Object note) {
 318             super(pcOffset);
 319             this.reference = reference;
 320             this.note = note;
 321         }
 322 
 323         @Override
 324         public String toString() {
 325             if (note != null) {
 326                 return String.format("%d[<data patch referring to %s>, note: %s]", pcOffset, reference.toString(), note.toString());
 327             } else {
 328                 return String.format("%d[<data patch referring to %s>]", pcOffset, reference.toString());
 329             }
 330         }
 331 
 332         @Override
 333         public boolean equals(Object obj) {
 334             if (this == obj) {
 335                 return true;
 336             }
 337             if (obj instanceof DataPatch) {
 338                 DataPatch that = (DataPatch) obj;
 339                 if (this.pcOffset == that.pcOffset && Objects.equals(this.reference, that.reference) && Objects.equals(this.note, that.note)) {
 340                     return true;
 341                 }
 342             }
 343             return false;
 344         }
 345     }
 346 
 347     /**
 348      * Provides extra information about instructions or data at specific positions in
 349      * {@link CompilationResult#getTargetCode()}. This is optional information that can be used to
 350      * enhance a disassembly of the code.
 351      */
 352     public abstract static class CodeAnnotation {
 353 
 354         public final int position;
 355 
 356         public CodeAnnotation(int position) {
 357             this.position = position;
 358         }
 359 
 360         @Override
 361         public final int hashCode() {
 362             throw new UnsupportedOperationException("hashCode");
 363         }
 364 
 365         @Override
 366         public String toString() {
 367             return identityHashCodeString(this);
 368         }
 369 
 370         @Override
 371         public abstract boolean equals(Object obj);
 372     }
 373 
 374     /**
 375      * A string comment about one or more instructions at a specific position in the code.
 376      */
 377     public static final class CodeComment extends CodeAnnotation {
 378 
 379         public final String value;
 380 
 381         public CodeComment(int position, String comment) {
 382             super(position);
 383             this.value = comment;
 384         }
 385 
 386         @Override
 387         public boolean equals(Object obj) {
 388             if (this == obj) {
 389                 return true;
 390             }
 391             if (obj instanceof CodeComment) {
 392                 CodeComment that = (CodeComment) obj;
 393                 if (this.position == that.position && this.value.equals(that.value)) {
 394                     return true;
 395                 }
 396             }
 397             return false;
 398         }
 399 
 400         @Override
 401         public String toString() {
 402             return getClass().getSimpleName() + "@" + position + ": " + value;
 403         }
 404     }
 405 
 406     /**
 407      * Describes a table of signed offsets embedded in the code. The offsets are relative to the
 408      * starting address of the table. This type of table maybe generated when translating a
 409      * multi-way branch based on a key value from a dense value set (e.g. the {@code tableswitch}
 410      * JVM instruction).
 411      *
 412      * The table is indexed by the contiguous range of integers from {@link #low} to {@link #high}
 413      * inclusive.
 414      */
 415     public static final class JumpTable extends CodeAnnotation {
 416 
 417         /**
 418          * The low value in the key range (inclusive).
 419          */
 420         public final int low;
 421 
 422         /**
 423          * The high value in the key range (inclusive).
 424          */
 425         public final int high;
 426 
 427         /**
 428          * The size (in bytes) of each table entry.
 429          */
 430         public final int entrySize;
 431 
 432         public JumpTable(int position, int low, int high, int entrySize) {
 433             super(position);
 434             this.low = low;
 435             this.high = high;
 436             this.entrySize = entrySize;
 437         }
 438 
 439         @Override
 440         public boolean equals(Object obj) {
 441             if (this == obj) {
 442                 return true;
 443             }
 444             if (obj instanceof JumpTable) {
 445                 JumpTable that = (JumpTable) obj;
 446                 if (this.position == that.position && this.entrySize == that.entrySize && this.low == that.low && this.high == that.high) {
 447                     return true;
 448                 }
 449             }
 450             return false;
 451         }
 452 
 453         @Override
 454         public String toString() {
 455             return getClass().getSimpleName() + "@" + position + ": [" + low + " .. " + high + "]";
 456         }
 457     }
 458 
 459     /**
 460      * Represents exception handler information for a specific code position. It includes the catch
 461      * code position as well as the caught exception type.
 462      */
 463     public static final class ExceptionHandler extends Site {
 464 
 465         public final int handlerPos;
 466 
 467         ExceptionHandler(int pcOffset, int handlerPos) {
 468             super(pcOffset);
 469             this.handlerPos = handlerPos;
 470         }
 471 
 472         @Override
 473         public String toString() {
 474             return String.format("%d[<exception edge to %d>]", pcOffset, handlerPos);
 475         }
 476 
 477         @Override
 478         public boolean equals(Object obj) {
 479             if (this == obj) {
 480                 return true;
 481             }
 482             if (obj instanceof ExceptionHandler) {
 483                 ExceptionHandler that = (ExceptionHandler) obj;
 484                 if (this.pcOffset == that.pcOffset && this.handlerPos == that.handlerPos) {
 485                     return true;
 486                 }
 487             }
 488             return false;
 489         }
 490     }
 491 
 492     /**
 493      * Represents a mark in the machine code that can be used by the runtime for its own purposes. A
 494      * mark can reference other marks.
 495      */
 496     public static final class Mark extends Site {
 497 
 498         public final Object id;
 499 
 500         public Mark(int pcOffset, Object id) {
 501             super(pcOffset);
 502             this.id = id;
 503         }
 504 
 505         @Override
 506         public String toString() {
 507             if (id == null) {
 508                 return String.format("%d[<mar>]", pcOffset);
 509             } else if (id instanceof Integer) {
 510                 return String.format("%d[<mark with id %s>]", pcOffset, Integer.toHexString((Integer) id));
 511             } else {
 512                 return String.format("%d[<mark with id %s>]", pcOffset, id.toString());
 513             }
 514         }
 515 
 516         @Override
 517         public boolean equals(Object obj) {
 518             if (this == obj) {
 519                 return true;
 520             }
 521             if (obj instanceof Mark) {
 522                 Mark that = (Mark) obj;
 523                 if (this.pcOffset == that.pcOffset && Objects.equals(this.id, that.id)) {
 524                     return true;
 525                 }
 526             }
 527             return false;
 528         }
 529     }
 530 
 531     private int id = -1;
 532 
 533     /**
 534      * Specifies whether this compilation is a {@code +ImmutableCode} {@code +GeneratePIC}
 535      * compilation.
 536      */
 537     private final boolean isImmutablePIC;
 538 
 539     private int entryBCI = -1;
 540 
 541     private final DataSection dataSection = new DataSection();
 542 
 543     private final List<Infopoint> infopoints = new ArrayList<>();
 544     private final List<DataPatch> dataPatches = new ArrayList<>();
 545     private final List<ExceptionHandler> exceptionHandlers = new ArrayList<>();
 546     private final List<Mark> marks = new ArrayList<>();
 547 
 548     private int totalFrameSize = -1;
 549     private int customStackAreaOffset = -1;
 550 
 551     private final String name;
 552 
 553     /**
 554      * The buffer containing the emitted machine code.
 555      */
 556     private byte[] targetCode;
 557 
 558     /**
 559      * The leading number of bytes in {@link #targetCode} containing the emitted machine code.
 560      */
 561     private int targetCodeSize;
 562 
 563     private ArrayList<CodeAnnotation> annotations;
 564 
 565     private Assumption[] assumptions;
 566 
 567     /**
 568      * The list of the methods whose bytecodes were used as input to the compilation. If
 569      * {@code null}, then the compilation did not record method dependencies. Otherwise, the first
 570      * element of this array is the root method of the compilation.
 571      */
 572     private ResolvedJavaMethod[] methods;
 573 
 574     private int bytecodeSize;
 575 
 576     private boolean hasUnsafeAccess;
 577 
 578     public CompilationResult() {
 579         this(null);
 580     }
 581 
 582     public CompilationResult(String name) {
 583         this.name = name;
 584         this.isImmutablePIC = false;
 585     }
 586 
 587     public CompilationResult(boolean isImmutablePIC) {
 588         this.name = null;
 589         this.isImmutablePIC = isImmutablePIC;
 590     }
 591 
 592     @Override
 593     public int hashCode() {
 594         // CompilationResult instances should not be used as hash map keys
 595         throw new UnsupportedOperationException("hashCode");
 596     }
 597 
 598     @Override
 599     public String toString() {
 600         if (methods != null) {
 601             return getClass().getName() + "[" + methods[0].format("%H.%n(%p)%r") + "]";
 602         }
 603         return identityHashCodeString(this);
 604     }
 605 
 606     @Override
 607     public boolean equals(Object obj) {
 608         if (this == obj) {
 609             return true;
 610         }
 611         if (obj != null && obj.getClass() == getClass()) {
 612             CompilationResult that = (CompilationResult) obj;
 613             // @formatter:off
 614             if (this.entryBCI == that.entryBCI &&
 615                 this.id == that.id &&
 616                 this.customStackAreaOffset == that.customStackAreaOffset &&
 617                 this.totalFrameSize == that.totalFrameSize &&
 618                 this.targetCodeSize == that.targetCodeSize &&
 619                 Objects.equals(this.name, that.name) &&
 620                 Objects.equals(this.annotations, that.annotations) &&
 621                 Objects.equals(this.dataSection, that.dataSection) &&
 622                 Objects.equals(this.exceptionHandlers, that.exceptionHandlers) &&
 623                 Objects.equals(this.dataPatches, that.dataPatches) &&
 624                 Objects.equals(this.infopoints, that.infopoints) &&
 625                 Objects.equals(this.marks,  that.marks) &&
 626                 Arrays.equals(this.assumptions, that.assumptions) &&
 627                 Arrays.equals(targetCode, that.targetCode)) {
 628                 return true;
 629             }
 630             // @formatter:on
 631         }
 632         return false;
 633     }
 634 
 635     /**
 636      * @return the compile id
 637      */
 638     public int getId() {
 639         return id;
 640     }
 641 
 642     /**
 643      * @param id the compile id to set
 644      */
 645     public void setId(int id) {
 646         this.id = id;
 647     }
 648 
 649     /**
 650      * @return true is this is a {@code +ImmutableCode} {@code +GeneratePIC} compilation, false
 651      *         otherwise.
 652      */
 653     public boolean isImmutablePIC() {
 654         return isImmutablePIC;
 655     }
 656 
 657     /**
 658      * @return the entryBCI
 659      */
 660     public int getEntryBCI() {
 661         return entryBCI;
 662     }
 663 
 664     /**
 665      * @param entryBCI the entryBCI to set
 666      */
 667     public void setEntryBCI(int entryBCI) {
 668         this.entryBCI = entryBCI;
 669     }
 670 
 671     /**
 672      * Sets the assumptions made during compilation.
 673      */
 674     public void setAssumptions(Assumption[] assumptions) {
 675         this.assumptions = assumptions;
 676     }
 677 
 678     /**
 679      * Gets the assumptions made during compilation.
 680      */
 681     public Assumption[] getAssumptions() {
 682         return assumptions;
 683     }
 684 
 685     /**
 686      * Sets the methods whose bytecodes were used as input to the compilation.
 687      *
 688      * @param rootMethod the root method of the compilation
 689      * @param inlinedMethods the methods inlined during compilation
 690      */
 691     public void setMethods(ResolvedJavaMethod rootMethod, Collection<ResolvedJavaMethod> inlinedMethods) {
 692         assert rootMethod != null;
 693         assert inlinedMethods != null;
 694         if (inlinedMethods.contains(rootMethod)) {
 695             methods = inlinedMethods.toArray(new ResolvedJavaMethod[inlinedMethods.size()]);
 696             for (int i = 0; i < methods.length; i++) {
 697                 if (methods[i].equals(rootMethod)) {
 698                     if (i != 0) {
 699                         ResolvedJavaMethod tmp = methods[0];
 700                         methods[0] = methods[i];
 701                         methods[i] = tmp;
 702                     }
 703                     break;
 704                 }
 705             }
 706         } else {
 707             methods = new ResolvedJavaMethod[1 + inlinedMethods.size()];
 708             methods[0] = rootMethod;
 709             int i = 1;
 710             for (ResolvedJavaMethod m : inlinedMethods) {
 711                 methods[i++] = m;
 712             }
 713         }
 714     }
 715 
 716     /**
 717      * Gets the methods whose bytecodes were used as input to the compilation.
 718      *
 719      * @return {@code null} if the compilation did not record method dependencies otherwise the
 720      *         methods whose bytecodes were used as input to the compilation with the first element
 721      *         being the root method of the compilation
 722      */
 723     public ResolvedJavaMethod[] getMethods() {
 724         return methods;
 725     }
 726 
 727     public void setBytecodeSize(int bytecodeSize) {
 728         this.bytecodeSize = bytecodeSize;
 729     }
 730 
 731     public int getBytecodeSize() {
 732         return bytecodeSize;
 733     }
 734 
 735     public DataSection getDataSection() {
 736         return dataSection;
 737     }
 738 
 739     /**
 740      * The total frame size of the method in bytes. This includes the return address pushed onto the
 741      * stack, if any.
 742      *
 743      * @return the frame size
 744      */
 745     public int getTotalFrameSize() {
 746         assert totalFrameSize != -1 : "frame size not yet initialized!";
 747         return totalFrameSize;
 748     }
 749 
 750     /**
 751      * Sets the total frame size in bytes. This includes the return address pushed onto the stack,
 752      * if any.
 753      *
 754      * @param size the size of the frame in bytes
 755      */
 756     public void setTotalFrameSize(int size) {
 757         totalFrameSize = size;
 758     }
 759 
 760     /**
 761      * Sets the machine that has been generated by the compiler.
 762      *
 763      * @param code the machine code generated
 764      * @param size the size of the machine code
 765      */
 766     public void setTargetCode(byte[] code, int size) {
 767         targetCode = code;
 768         targetCodeSize = size;
 769     }
 770 
 771     /**
 772      * Records a data patch in the code section. The data patch can refer to something in the
 773      * {@link DataSectionReference data section} or directly to an {@link ConstantReference inlined
 774      * constant}.
 775      *
 776      * @param codePos The position in the code that needs to be patched.
 777      * @param ref The reference that should be inserted in the code.
 778      */
 779     public void recordDataPatch(int codePos, Reference ref) {
 780         assert codePos >= 0 && ref != null;
 781         dataPatches.add(new DataPatch(codePos, ref));
 782     }
 783 
 784     /**
 785      * Records a data patch in the code section. The data patch can refer to something in the
 786      * {@link DataSectionReference data section} or directly to an {@link ConstantReference inlined
 787      * constant}.
 788      *
 789      * @param codePos The position in the code that needs to be patched.
 790      * @param ref The reference that should be inserted in the code.
 791      * @param note The note attached to data patch for use by post-processing tools
 792      */
 793     public void recordDataPatchWithNote(int codePos, Reference ref, Object note) {
 794         assert codePos >= 0 && ref != null;
 795         dataPatches.add(new DataPatch(codePos, ref, note));
 796     }
 797 
 798     /**
 799      * Records metaspace access.
 800      */
 801     public void recordMetaspaceAccess(Object reference, int instructionSize, MetaSpaceAccessType type, int codePos, DebugInfo debugInfo) {
 802         final MetaSpaceAccess metaspace = new MetaSpaceAccess(reference, instructionSize, type, codePos, debugInfo);
 803         addInfopoint(metaspace);
 804     }
 805 
 806     /**
 807      * Records a call in the code array.
 808      *
 809      * @param codePos the position of the call in the code array
 810      * @param size the size of the call instruction
 811      * @param target the being called
 812      * @param debugInfo the debug info for the call
 813      * @param direct specifies if this is a {@linkplain Call#direct direct} call
 814      */
 815     public void recordCall(int codePos, int size, InvokeTarget target, DebugInfo debugInfo, boolean direct) {
 816         final Call call = new Call(target, codePos, size, direct, debugInfo);
 817         addInfopoint(call);
 818     }
 819 
 820     /**
 821      * Records an exception handler for this method.
 822      *
 823      * @param codePos the position in the code that is covered by the handler
 824      * @param handlerPos the position of the handler
 825      */
 826     public void recordExceptionHandler(int codePos, int handlerPos) {
 827         assert validateExceptionHandlerAdd(codePos, handlerPos) : String.format("Duplicate exception handler for pc 0x%x handlerPos 0x%x", codePos, handlerPos);
 828         exceptionHandlers.add(new ExceptionHandler(codePos, handlerPos));
 829     }
 830 
 831     /**
 832      * Validate if the exception handler for codePos already exists and handlerPos is different.
 833      *
 834      * @param codePos
 835      * @param handlerPos
 836      * @return true if the validation is successful
 837      */
 838     private boolean validateExceptionHandlerAdd(int codePos, int handlerPos) {
 839         ExceptionHandler exHandler = getExceptionHandlerForCodePos(codePos);
 840         return exHandler == null || exHandler.handlerPos == handlerPos;
 841     }
 842 
 843     /**
 844      * Returns the first ExceptionHandler which matches codePos.
 845      *
 846      * @param codePos position to search for
 847      * @return first matching ExceptionHandler
 848      */
 849     private ExceptionHandler getExceptionHandlerForCodePos(int codePos) {
 850         for (ExceptionHandler h : exceptionHandlers) {
 851             if (h.pcOffset == codePos) {
 852                 return h;
 853             }
 854         }
 855         return null;
 856     }
 857 
 858     /**
 859      * Records an infopoint in the code array.
 860      *
 861      * @param codePos the position of the infopoint in the code array
 862      * @param debugInfo the debug info for the infopoint
 863      */
 864     public void recordInfopoint(int codePos, DebugInfo debugInfo, InfopointReason reason) {
 865         addInfopoint(new Infopoint(codePos, debugInfo, reason));
 866     }
 867 
 868     /**
 869      * Records a custom infopoint in the code section.
 870      *
 871      * Compiler implementations can use this method to record non-standard infopoints, which are not
 872      * handled by the dedicated methods like {@link #recordCall}.
 873      *
 874      * @param infopoint the infopoint to record, usually a derived class from {@link Infopoint}
 875      */
 876     public void addInfopoint(Infopoint infopoint) {
 877         // The infopoints list must always be sorted
 878         if (!infopoints.isEmpty()) {
 879             Infopoint previousInfopoint = infopoints.get(infopoints.size() - 1);
 880             if (previousInfopoint.pcOffset > infopoint.pcOffset) {
 881                 // This re-sorting should be very rare
 882                 Collections.sort(infopoints);
 883                 previousInfopoint = infopoints.get(infopoints.size() - 1);
 884             }
 885             if (previousInfopoint.pcOffset == infopoint.pcOffset) {
 886                 if (infopoint.reason.canBeOmitted()) {
 887                     return;
 888                 }
 889                 if (previousInfopoint.reason.canBeOmitted()) {
 890                     Infopoint removed = infopoints.remove(infopoints.size() - 1);
 891                     assert removed == previousInfopoint;
 892                 } else {
 893                     throw new RuntimeException("Infopoints that can not be omited should have distinct PCs");
 894                 }
 895             }
 896         }
 897         infopoints.add(infopoint);
 898     }
 899 
 900     /**
 901      * Records an instruction mark within this method.
 902      *
 903      * @param codePos the position in the code that is covered by the handler
 904      * @param markId the identifier for this mark
 905      */
 906     public Mark recordMark(int codePos, Object markId) {
 907         Mark mark = new Mark(codePos, markId);
 908         marks.add(mark);
 909         return mark;
 910     }
 911 
 912     /**
 913      * Offset in bytes for the custom stack area (relative to sp).
 914      *
 915      * @return the offset in bytes
 916      */
 917     public int getCustomStackAreaOffset() {
 918         return customStackAreaOffset;
 919     }
 920 
 921     /**
 922      * @see #getCustomStackAreaOffset()
 923      * @param offset
 924      */
 925     public void setCustomStackAreaOffset(int offset) {
 926         customStackAreaOffset = offset;
 927     }
 928 
 929     /**
 930      * @return the machine code generated for this method
 931      */
 932     public byte[] getTargetCode() {
 933         return targetCode;
 934     }
 935 
 936     /**
 937      * @return the size of the machine code generated for this method
 938      */
 939     public int getTargetCodeSize() {
 940         return targetCodeSize;
 941     }
 942 
 943     /**
 944      * @return the code annotations or {@code null} if there are none
 945      */
 946     public List<CodeAnnotation> getAnnotations() {
 947         if (annotations == null) {
 948             return Collections.emptyList();
 949         }
 950         return annotations;
 951     }
 952 
 953     public void addAnnotation(CodeAnnotation annotation) {
 954         assert annotation != null;
 955         if (annotations == null) {
 956             annotations = new ArrayList<>();
 957         }
 958         annotations.add(annotation);
 959     }
 960 
 961     private static void appendDebugInfo(StringBuilder sb, DebugInfo info) {
 962         if (info != null) {
 963             ReferenceMap refMap = info.getReferenceMap();
 964             if (refMap != null) {
 965                 sb.append(refMap.toString());
 966                 sb.append(']');
 967             }
 968             RegisterSaveLayout calleeSaveInfo = info.getCalleeSaveInfo();
 969             if (calleeSaveInfo != null) {
 970                 sb.append(" callee-save-info[");
 971                 String sep = "";
 972                 for (Map.Entry<Register, Integer> e : calleeSaveInfo.registersToSlots(true).entrySet()) {
 973                     sb.append(sep).append(e.getKey()).append("->").append(e.getValue());
 974                     sep = ", ";
 975                 }
 976                 sb.append(']');
 977             }
 978             BytecodePosition codePos = info.getBytecodePosition();
 979             if (codePos != null) {
 980                 MetaUtil.appendLocation(sb.append(" "), codePos.getMethod(), codePos.getBCI());
 981                 if (info.hasFrame()) {
 982                     sb.append(" #locals=").append(info.frame().numLocals).append(" #expr=").append(info.frame().numStack);
 983                     if (info.frame().numLocks > 0) {
 984                         sb.append(" #locks=").append(info.frame().numLocks);
 985                     }
 986                 }
 987             }
 988         }
 989     }
 990 
 991     /**
 992      * @return the list of infopoints, sorted by {@link Site#pcOffset}
 993      */
 994     public List<Infopoint> getInfopoints() {
 995         if (infopoints.isEmpty()) {
 996             return emptyList();
 997         }
 998         return unmodifiableList(infopoints);
 999     }
1000 
1001     /**
1002      * @return the list of data references
1003      */
1004     public List<DataPatch> getDataPatches() {
1005         if (dataPatches.isEmpty()) {
1006             return emptyList();
1007         }
1008         return unmodifiableList(dataPatches);
1009     }
1010 
1011     /**
1012      * @return the list of exception handlers
1013      */
1014     public List<ExceptionHandler> getExceptionHandlers() {
1015         if (exceptionHandlers.isEmpty()) {
1016             return emptyList();
1017         }
1018         return unmodifiableList(exceptionHandlers);
1019     }
1020 
1021     /**
1022      * @return the list of marks
1023      */
1024     public List<Mark> getMarks() {
1025         if (marks.isEmpty()) {
1026             return emptyList();
1027         }
1028         return unmodifiableList(marks);
1029     }
1030 
1031     public String getName() {
1032         return name;
1033     }
1034 
1035     public void setHasUnsafeAccess(boolean hasUnsafeAccess) {
1036         this.hasUnsafeAccess = hasUnsafeAccess;
1037     }
1038 
1039     public boolean hasUnsafeAccess() {
1040         return hasUnsafeAccess;
1041     }
1042 
1043     public void reset() {
1044         hasUnsafeAccess = false;
1045         infopoints.clear();
1046         dataPatches.clear();
1047         exceptionHandlers.clear();
1048         marks.clear();
1049         dataSection.clear();
1050         if (annotations != null) {
1051             annotations.clear();
1052         }
1053     }
1054 }