--- /dev/null 2016-05-31 09:42:47.975716356 -0700 +++ new/src/jdk.vm.compiler/share/classes/org.graalvm.compiler.code/src/org/graalvm/compiler/code/CompilationResult.java 2016-12-07 13:47:41.678707857 -0800 @@ -0,0 +1,716 @@ +/* + * 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 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. + */ +public class CompilationResult { + + /** + * 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. + */ + 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 {@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). + */ + public final int low; + + /** + * The high value in the key range (inclusive). + */ + public final int high; + + /** + * 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 infopoints = new ArrayList<>(); + private final List sourceMapping = new ArrayList<>(); + private final List dataPatches = new ArrayList<>(); + private final List exceptionHandlers = new ArrayList<>(); + private final List marks = new ArrayList<>(); + + private int totalFrameSize = -1; + private int maxInterpreterFrameSize = -1; + + private StackSlot customStackArea = null; + + private final String name; + + /** + * The buffer containing the emitted machine code. + */ + private byte[] targetCode; + + /** + * The leading number of bytes in {@link #targetCode} containing the emitted machine code. + */ + private int targetCodeSize; + + private ArrayList annotations; + + private Assumption[] assumptions; + + /** + * 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. + */ + private ResolvedJavaField[] fields; + + private int bytecodeSize; + + private boolean hasUnsafeAccess; + + private boolean isImmutablePIC; + + public CompilationResult() { + this(null, false); + } + + public CompilationResult(String name) { + this(name, false); + } + + public CompilationResult(boolean isImmutablePIC) { + this(null, isImmutablePIC); + } + + public CompilationResult(String name, boolean isImmutablePIC) { + this.name = name; + this.isImmutablePIC = isImmutablePIC; + } + + @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.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; + } + + /** + * @return the entryBCI + */ + public int getEntryBCI() { + return entryBCI; + } + + /** + * @param entryBCI the entryBCI to set + */ + public void setEntryBCI(int entryBCI) { + checkOpen(); + this.entryBCI = entryBCI; + } + + /** + * Sets the assumptions made during compilation. + */ + public void setAssumptions(Assumption[] assumptions) { + checkOpen(); + this.assumptions = assumptions; + } + + /** + * 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. + * + * @param rootMethod the root method of the compilation + * @param inlinedMethods the methods inlined during compilation + */ + public void setMethods(ResolvedJavaMethod rootMethod, Collection 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. + * + * @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. + * + * @param accessedFields the collected set of fields accessed during compilation + */ + public void setFields(Collection accessedFields) { + assert 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. + * + * @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. + * + * @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. + * + * @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. + * + * @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 + * {@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 + * {@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. + * + * @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. + * + * @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. + * + * @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. + * + * @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. + * + * @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 {@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. + * + * @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. + * + * @return the first stack slot of the custom stack area + */ + public StackSlot getCustomStackArea() { + return customStackArea; + } + + /** + * @see #getCustomStackArea() + * @param slot + */ + public void setCustomStackAreaOffset(StackSlot slot) { + checkOpen(); + customStackArea = slot; + } + + /** + * @return the machine code generated for this method + */ + public byte[] getTargetCode() { + return targetCode; + } + + /** + * @return the size of the machine code generated for this method + */ + public int getTargetCodeSize() { + return targetCodeSize; + } + + /** + * @return the code annotations or {@code null} if there are none + */ + public List 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); + } + + /** + * @return the list of infopoints, sorted by {@link Site#pcOffset} + */ + public List getInfopoints() { + if (infopoints.isEmpty()) { + return emptyList(); + } + return unmodifiableList(infopoints); + } + + /** + * @return the list of data references + */ + public List getDataPatches() { + if (dataPatches.isEmpty()) { + return emptyList(); + } + return unmodifiableList(dataPatches); + } + + /** + * @return the list of exception handlers + */ + public List getExceptionHandlers() { + if (exceptionHandlers.isEmpty()) { + return emptyList(); + } + return unmodifiableList(exceptionHandlers); + } + + /** + * @return the list of marks + */ + public List getMarks() { + if (marks.isEmpty()) { + return emptyList(); + } + return unmodifiableList(marks); + } + + /** + * @return the list of {@link SourceMapping}s + */ + public List getSourceMappings() { + if (sourceMapping.isEmpty()) { + return emptyList(); + } + return unmodifiableList(sourceMapping); + } + + public String getName() { + return name; + } + + 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 + * {@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. + */ + public void close() { + if (closed) { + throw new IllegalStateException("Cannot re-close compilation result " + this); + } + dataSection.close(); + closed = true; + } +}