/*
 * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


package org.graalvm.compiler.code;

import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static jdk.vm.ci.meta.MetaUtil.identityHashCodeString;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

import jdk.internal.vm.compiler.collections.EconomicSet;
import org.graalvm.compiler.core.common.CompilationIdentifier;
import org.graalvm.compiler.graph.NodeSourcePosition;

import jdk.vm.ci.code.DebugInfo;
import jdk.vm.ci.code.StackSlot;
import jdk.vm.ci.code.site.Call;
import jdk.vm.ci.code.site.ConstantReference;
import jdk.vm.ci.code.site.DataPatch;
import jdk.vm.ci.code.site.DataSectionReference;
import jdk.vm.ci.code.site.ExceptionHandler;
import jdk.vm.ci.code.site.Infopoint;
import jdk.vm.ci.code.site.InfopointReason;
import jdk.vm.ci.code.site.Mark;
import jdk.vm.ci.code.site.Reference;
import jdk.vm.ci.code.site.Site;
import jdk.vm.ci.meta.Assumptions.Assumption;
import jdk.vm.ci.meta.InvokeTarget;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaMethod;

Represents the output from compiling a method, including the compiled machine code, associated data and references, relocation information, deoptimization information, etc.
/** * Represents the output from compiling a method, including the compiled machine code, associated * data and references, relocation information, deoptimization information, etc. */
public class CompilationResult {
Provides extra information about instructions or data at specific positions in CompilationResult.getTargetCode(). This is optional information that can be used to enhance a disassembly of the code.
/** * Provides extra information about instructions or data at specific positions in * {@link CompilationResult#getTargetCode()}. This is optional information that can be used to * enhance a disassembly of the code. */
public abstract static class CodeAnnotation { public final int position; public CodeAnnotation(int position) { this.position = position; } @Override public final int hashCode() { throw new UnsupportedOperationException("hashCode"); } @Override public String toString() { return identityHashCodeString(this); } @Override public abstract boolean equals(Object obj); }
A string comment about one or more instructions at a specific position in the code.
/** * A string comment about one or more instructions at a specific position in the code. */
public static final class CodeComment extends CodeAnnotation { public final String value; public CodeComment(int position, String comment) { super(position); this.value = comment; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof CodeComment) { CodeComment that = (CodeComment) obj; if (this.position == that.position && this.value.equals(that.value)) { return true; } } return false; } @Override public String toString() { return getClass().getSimpleName() + "@" + position + ": " + value; } }
Describes a table of signed offsets embedded in the code. The offsets are relative to the starting address of the table. This type of table maybe generated when translating a multi-way branch based on a key value from a dense value set (e.g. the tableswitch JVM instruction). The table is indexed by the contiguous range of integers from low to high inclusive.
/** * Describes a table of signed offsets embedded in the code. The offsets are relative to the * starting address of the table. This type of table maybe generated when translating a * multi-way branch based on a key value from a dense value set (e.g. the {@code tableswitch} * JVM instruction). * * The table is indexed by the contiguous range of integers from {@link #low} to {@link #high} * inclusive. */
public static final class JumpTable extends CodeAnnotation {
The low value in the key range (inclusive).
/** * The low value in the key range (inclusive). */
public final int low;
The high value in the key range (inclusive).
/** * The high value in the key range (inclusive). */
public final int high;
The size (in bytes) of each table entry.
/** * The size (in bytes) of each table entry. */
public final int entrySize; public JumpTable(int position, int low, int high, int entrySize) { super(position); this.low = low; this.high = high; this.entrySize = entrySize; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof JumpTable) { JumpTable that = (JumpTable) obj; if (this.position == that.position && this.entrySize == that.entrySize && this.low == that.low && this.high == that.high) { return true; } } return false; } @Override public String toString() { return getClass().getSimpleName() + "@" + position + ": [" + low + " .. " + high + "]"; } } private boolean closed; private int entryBCI = -1; private final DataSection dataSection = new DataSection(); private final List<Infopoint> infopoints = new ArrayList<>(); private final List<SourceMapping> sourceMapping = new ArrayList<>(); private final List<DataPatch> dataPatches = new ArrayList<>(); private final List<ExceptionHandler> exceptionHandlers = new ArrayList<>(); private final List<Mark> marks = new ArrayList<>(); private int totalFrameSize = -1; private int maxInterpreterFrameSize = -1; private StackSlot customStackArea = null; private final String name; private final CompilationIdentifier compilationId;
The buffer containing the emitted machine code.
/** * The buffer containing the emitted machine code. */
private byte[] targetCode;
The leading number of bytes in targetCode containing the emitted machine code.
/** * The leading number of bytes in {@link #targetCode} containing the emitted machine code. */
private int targetCodeSize; private ArrayList<CodeAnnotation> annotations; private Assumption[] assumptions;
The list of the methods whose bytecodes were used as input to the compilation. If null, then the compilation did not record method dependencies. Otherwise, the first element of this array is the root method of the compilation.
/** * The list of the methods whose bytecodes were used as input to the compilation. If * {@code null}, then the compilation did not record method dependencies. Otherwise, the first * element of this array is the root method of the compilation. */
private ResolvedJavaMethod[] methods;
The list of fields that were accessed from the bytecodes.
/** * The list of fields that were accessed from the bytecodes. */
private ResolvedJavaField[] fields; private int bytecodeSize; private boolean hasUnsafeAccess; private boolean isImmutablePIC; public CompilationResult(CompilationIdentifier compilationId) { this(compilationId, compilationId.toString(CompilationIdentifier.Verbosity.NAME), false); } public CompilationResult(CompilationIdentifier compilationId, String name) { this(compilationId, name, false); } public CompilationResult(CompilationIdentifier compilationId, boolean isImmutablePIC) { this(compilationId, null, isImmutablePIC); } public CompilationResult(CompilationIdentifier compilationId, String name, boolean isImmutablePIC) { this.compilationId = compilationId; this.name = name; this.isImmutablePIC = isImmutablePIC; } public CompilationResult(String name) { this(null, name); } @Override public int hashCode() { // CompilationResult instances should not be used as hash map keys throw new UnsupportedOperationException("hashCode"); } @Override public String toString() { if (methods != null) { return getClass().getName() + "[" + methods[0].format("%H.%n(%p)%r") + "]"; } return identityHashCodeString(this); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj != null && obj.getClass() == getClass()) { CompilationResult that = (CompilationResult) obj; // @formatter:off if (this.entryBCI == that.entryBCI && Objects.equals(this.customStackArea, that.customStackArea) && this.totalFrameSize == that.totalFrameSize && this.targetCodeSize == that.targetCodeSize && Objects.equals(this.name, that.name) && Objects.equals(this.compilationId, that.compilationId) && Objects.equals(this.annotations, that.annotations) && Objects.equals(this.dataSection, that.dataSection) && Objects.equals(this.exceptionHandlers, that.exceptionHandlers) && Objects.equals(this.dataPatches, that.dataPatches) && Objects.equals(this.infopoints, that.infopoints) && Objects.equals(this.marks, that.marks) && Arrays.equals(this.assumptions, that.assumptions) && Arrays.equals(targetCode, that.targetCode)) { return true; } // @formatter:on } return false; }
Returns:the entryBCI
/** * @return the entryBCI */
public int getEntryBCI() { return entryBCI; }
Params:
  • entryBCI – the entryBCI to set
/** * @param entryBCI the entryBCI to set */
public void setEntryBCI(int entryBCI) { checkOpen(); this.entryBCI = entryBCI; }
Sets the assumptions made during compilation.
/** * Sets the assumptions made during compilation. */
public void setAssumptions(Assumption[] assumptions) { this.assumptions = assumptions; }
Gets the assumptions made during compilation. The caller must not modify the contents of the returned array.
/** * Gets the assumptions made during compilation. * * The caller must not modify the contents of the returned array. */
public Assumption[] getAssumptions() { return assumptions; }
Sets the methods whose bytecodes were used as input to the compilation.
Params:
  • rootMethod – the root method of the compilation
  • inlinedMethods – the methods inlined during compilation
/** * Sets the methods whose bytecodes were used as input to the compilation. * * @param rootMethod the root method of the compilation * @param inlinedMethods the methods inlined during compilation */
public void setMethods(ResolvedJavaMethod rootMethod, Collection<ResolvedJavaMethod> inlinedMethods) { checkOpen(); assert rootMethod != null; assert inlinedMethods != null; if (inlinedMethods.contains(rootMethod)) { methods = inlinedMethods.toArray(new ResolvedJavaMethod[inlinedMethods.size()]); for (int i = 0; i < methods.length; i++) { if (methods[i].equals(rootMethod)) { if (i != 0) { ResolvedJavaMethod tmp = methods[0]; methods[0] = methods[i]; methods[i] = tmp; } break; } } } else { methods = new ResolvedJavaMethod[1 + inlinedMethods.size()]; methods[0] = rootMethod; int i = 1; for (ResolvedJavaMethod m : inlinedMethods) { methods[i++] = m; } } }
Gets the methods whose bytecodes were used as input to the compilation. The caller must not modify the contents of the returned array.
Returns:null if the compilation did not record method dependencies otherwise the methods whose bytecodes were used as input to the compilation with the first element being the root method of the compilation
/** * Gets the methods whose bytecodes were used as input to the compilation. * * The caller must not modify the contents of the returned array. * * @return {@code null} if the compilation did not record method dependencies otherwise the * methods whose bytecodes were used as input to the compilation with the first element * being the root method of the compilation */
public ResolvedJavaMethod[] getMethods() { return methods; }
Sets the fields that were referenced from the bytecodes that were used as input to the compilation.
Params:
  • accessedFields – the collected set of fields accessed during compilation
/** * Sets the fields that were referenced from the bytecodes that were used as input to the * compilation. * * @param accessedFields the collected set of fields accessed during compilation */
public void setFields(EconomicSet<ResolvedJavaField> accessedFields) { if (accessedFields != null) { fields = accessedFields.toArray(new ResolvedJavaField[accessedFields.size()]); } }
Gets the fields that were referenced from bytecodes that were used as input to the compilation. The caller must not modify the contents of the returned array.
Returns:null if the compilation did not record fields dependencies otherwise the fields that were accessed from bytecodes were used as input to the compilation.
/** * Gets the fields that were referenced from bytecodes that were used as input to the * compilation. * * The caller must not modify the contents of the returned array. * * @return {@code null} if the compilation did not record fields dependencies otherwise the * fields that were accessed from bytecodes were used as input to the compilation. */
public ResolvedJavaField[] getFields() { return fields; } public void setBytecodeSize(int bytecodeSize) { checkOpen(); this.bytecodeSize = bytecodeSize; } public int getBytecodeSize() { return bytecodeSize; } public DataSection getDataSection() { return dataSection; }
The total frame size of the method in bytes. This includes the return address pushed onto the stack, if any.
Returns:the frame size
/** * The total frame size of the method in bytes. This includes the return address pushed onto the * stack, if any. * * @return the frame size */
public int getTotalFrameSize() { assert totalFrameSize != -1 : "frame size not yet initialized!"; return totalFrameSize; }
Sets the total frame size in bytes. This includes the return address pushed onto the stack, if any.
Params:
  • size – the size of the frame in bytes
/** * Sets the total frame size in bytes. This includes the return address pushed onto the stack, * if any. * * @param size the size of the frame in bytes */
public void setTotalFrameSize(int size) { checkOpen(); totalFrameSize = size; } public int getMaxInterpreterFrameSize() { return maxInterpreterFrameSize; } public void setMaxInterpreterFrameSize(int maxInterpreterFrameSize) { checkOpen(); this.maxInterpreterFrameSize = maxInterpreterFrameSize; } public boolean isImmutablePIC() { return this.isImmutablePIC; }
Sets the machine that has been generated by the compiler.
Params:
  • code – the machine code generated
  • size – the size of the machine code
/** * Sets the machine that has been generated by the compiler. * * @param code the machine code generated * @param size the size of the machine code */
public void setTargetCode(byte[] code, int size) { checkOpen(); targetCode = code; targetCodeSize = size; }
Records a data patch in the code section. The data patch can refer to something in the data section or directly to an inlined constant.
Params:
  • codePos – the position in the code that needs to be patched
  • ref – the reference that should be inserted in the code
/** * Records a data patch in the code section. The data patch can refer to something in the * {@link DataSectionReference data section} or directly to an {@link ConstantReference inlined * constant}. * * @param codePos the position in the code that needs to be patched * @param ref the reference that should be inserted in the code */
public void recordDataPatch(int codePos, Reference ref) { checkOpen(); assert codePos >= 0 && ref != null; dataPatches.add(new DataPatch(codePos, ref)); }
Records a data patch in the code section. The data patch can refer to something in the data section or directly to an inlined constant.
Params:
  • codePos – the position in the code that needs to be patched
  • ref – the reference that should be inserted in the code
  • note – a note attached to data patch for use by post-processing tools
/** * Records a data patch in the code section. The data patch can refer to something in the * {@link DataSectionReference data section} or directly to an {@link ConstantReference inlined * constant}. * * @param codePos the position in the code that needs to be patched * @param ref the reference that should be inserted in the code * @param note a note attached to data patch for use by post-processing tools */
public void recordDataPatchWithNote(int codePos, Reference ref, Object note) { assert codePos >= 0 && ref != null; dataPatches.add(new DataPatch(codePos, ref, note)); }
Records a call in the code array.
Params:
  • codePos – the position of the call in the code array
  • size – the size of the call instruction
  • target – the being called
  • debugInfo – the debug info for the call
  • direct – specifies if this is a direct call
/** * Records a call in the code array. * * @param codePos the position of the call in the code array * @param size the size of the call instruction * @param target the being called * @param debugInfo the debug info for the call * @param direct specifies if this is a {@linkplain Call#direct direct} call */
public void recordCall(int codePos, int size, InvokeTarget target, DebugInfo debugInfo, boolean direct) { checkOpen(); final Call call = new Call(target, codePos, size, direct, debugInfo); addInfopoint(call); }
Records an exception handler for this method.
Params:
  • codePos – the position in the code that is covered by the handler
  • handlerPos – the position of the handler
/** * Records an exception handler for this method. * * @param codePos the position in the code that is covered by the handler * @param handlerPos the position of the handler */
public void recordExceptionHandler(int codePos, int handlerPos) { checkOpen(); assert validateExceptionHandlerAdd(codePos, handlerPos) : String.format("Duplicate exception handler for pc 0x%x handlerPos 0x%x", codePos, handlerPos); exceptionHandlers.add(new ExceptionHandler(codePos, handlerPos)); }
Validate if the exception handler for codePos already exists and handlerPos is different.
Params:
  • codePos –
  • handlerPos –
Returns:true if the validation is successful
/** * Validate if the exception handler for codePos already exists and handlerPos is different. * * @param codePos * @param handlerPos * @return true if the validation is successful */
private boolean validateExceptionHandlerAdd(int codePos, int handlerPos) { ExceptionHandler exHandler = getExceptionHandlerForCodePos(codePos); return exHandler == null || exHandler.handlerPos == handlerPos; }
Returns the first ExceptionHandler which matches codePos.
Params:
  • codePos – position to search for
Returns:first matching ExceptionHandler
/** * Returns the first ExceptionHandler which matches codePos. * * @param codePos position to search for * @return first matching ExceptionHandler */
private ExceptionHandler getExceptionHandlerForCodePos(int codePos) { for (ExceptionHandler h : exceptionHandlers) { if (h.pcOffset == codePos) { return h; } } return null; }
Records an infopoint in the code array.
Params:
  • codePos – the position of the infopoint in the code array
  • debugInfo – the debug info for the infopoint
/** * Records an infopoint in the code array. * * @param codePos the position of the infopoint in the code array * @param debugInfo the debug info for the infopoint */
public void recordInfopoint(int codePos, DebugInfo debugInfo, InfopointReason reason) { addInfopoint(new Infopoint(codePos, debugInfo, reason)); }
Records a custom infopoint in the code section. Compiler implementations can use this method to record non-standard infopoints, which are not handled by dedicated methods like recordCall.
Params:
  • infopoint – the infopoint to record, usually a derived class from Infopoint
/** * Records a custom infopoint in the code section. * * Compiler implementations can use this method to record non-standard infopoints, which are not * handled by dedicated methods like {@link #recordCall}. * * @param infopoint the infopoint to record, usually a derived class from {@link Infopoint} */
public void addInfopoint(Infopoint infopoint) { checkOpen(); infopoints.add(infopoint); } public void recordSourceMapping(int startOffset, int endOffset, NodeSourcePosition sourcePosition) { checkOpen(); sourceMapping.add(new SourceMapping(startOffset, endOffset, sourcePosition)); }
Records an instruction mark within this method.
Params:
  • codePos – the position in the code that is covered by the handler
  • markId – the identifier for this mark
/** * Records an instruction mark within this method. * * @param codePos the position in the code that is covered by the handler * @param markId the identifier for this mark */
public Mark recordMark(int codePos, Object markId) { checkOpen(); Mark mark = new Mark(codePos, markId); marks.add(mark); return mark; }
Start of the custom stack area.
Returns:the first stack slot of the custom stack area
/** * Start of the custom stack area. * * @return the first stack slot of the custom stack area */
public StackSlot getCustomStackArea() { return customStackArea; }
Params:
  • slot –
See Also:
  • getCustomStackArea()
/** * @see #getCustomStackArea() * @param slot */
public void setCustomStackAreaOffset(StackSlot slot) { checkOpen(); customStackArea = slot; }
Returns:the machine code generated for this method
/** * @return the machine code generated for this method */
public byte[] getTargetCode() { return targetCode; }
Returns:the size of the machine code generated for this method
/** * @return the size of the machine code generated for this method */
public int getTargetCodeSize() { return targetCodeSize; }
Returns:the code annotations or null if there are none
/** * @return the code annotations or {@code null} if there are none */
public List<CodeAnnotation> getAnnotations() { if (annotations == null) { return Collections.emptyList(); } return annotations; } public void addAnnotation(CodeAnnotation annotation) { checkOpen(); assert annotation != null; if (annotations == null) { annotations = new ArrayList<>(); } annotations.add(annotation); }
Returns:the list of infopoints, sorted by Site.pcOffset
/** * @return the list of infopoints, sorted by {@link Site#pcOffset} */
public List<Infopoint> getInfopoints() { if (infopoints.isEmpty()) { return emptyList(); } return unmodifiableList(infopoints); }
Returns:the list of data references
/** * @return the list of data references */
public List<DataPatch> getDataPatches() { if (dataPatches.isEmpty()) { return emptyList(); } return unmodifiableList(dataPatches); }
Returns:the list of exception handlers
/** * @return the list of exception handlers */
public List<ExceptionHandler> getExceptionHandlers() { if (exceptionHandlers.isEmpty()) { return emptyList(); } return unmodifiableList(exceptionHandlers); }
Returns:the list of marks
/** * @return the list of marks */
public List<Mark> getMarks() { if (marks.isEmpty()) { return emptyList(); } return unmodifiableList(marks); }
Returns:the list of SourceMappings
/** * @return the list of {@link SourceMapping}s */
public List<SourceMapping> getSourceMappings() { if (sourceMapping.isEmpty()) { return emptyList(); } return unmodifiableList(sourceMapping); } public String getName() { return name; } public CompilationIdentifier getCompilationId() { return compilationId; } public void setHasUnsafeAccess(boolean hasUnsafeAccess) { checkOpen(); this.hasUnsafeAccess = hasUnsafeAccess; } public boolean hasUnsafeAccess() { return hasUnsafeAccess; }
Clears the information in this object pertaining to generating code. That is, the marks, infopoints, exception handlers, data patches and annotations recorded in this object are cleared.
/** * Clears the information in this object pertaining to generating code. That is, the * {@linkplain #getMarks() marks}, {@linkplain #getInfopoints() infopoints}, * {@linkplain #getExceptionHandlers() exception handlers}, {@linkplain #getDataPatches() data * patches} and {@linkplain #getAnnotations() annotations} recorded in this object are cleared. */
public void resetForEmittingCode() { checkOpen(); infopoints.clear(); sourceMapping.clear(); dataPatches.clear(); exceptionHandlers.clear(); marks.clear(); dataSection.clear(); if (annotations != null) { annotations.clear(); } } private void checkOpen() { if (closed) { throw new IllegalStateException(); } }
Closes this compilation result to future updates.
/** * Closes this compilation result to future updates. */
public void close() { if (closed) { throw new IllegalStateException("Cannot re-close compilation result " + this); } dataSection.close(); closed = true; } }