/*
* Copyright (c) 2007, 2012, 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 com.sun.max.vm.compiler.target;
import static com.sun.max.platform.Platform.*;
import static com.sun.max.vm.MaxineVM.*;
import static com.sun.max.vm.compiler.target.Safepoints.*;
import java.io.*;
import java.util.*;
import com.oracle.max.asm.target.amd64.*;
import com.sun.cri.ci.*;
import com.sun.cri.ci.CiTargetMethod.Call;
import com.sun.cri.ci.CiTargetMethod.CodeAnnotation;
import com.sun.cri.ci.CiTargetMethod.DataPatch;
import com.sun.cri.ci.CiTargetMethod.Safepoint;
import com.sun.max.annotate.*;
import com.sun.max.lang.*;
import com.sun.max.memory.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.classfile.*;
import com.sun.max.vm.code.*;
import com.sun.max.vm.code.CodeManager.Lifespan;
import com.sun.max.vm.compiler.*;
import com.sun.max.vm.compiler.RuntimeCompiler.Nature;
import com.sun.max.vm.compiler.deopt.*;
import com.sun.max.vm.compiler.deopt.Deoptimization.Continuation;
import com.sun.max.vm.compiler.deopt.Deoptimization.Info;
import com.sun.max.vm.compiler.target.TargetBundleLayout.ArrayField;
import com.sun.max.vm.jni.*;
import com.sun.max.vm.profile.*;
import com.sun.max.vm.runtime.*;
import com.sun.max.vm.stack.*;
import com.sun.max.vm.thread.*;
import com.sun.max.vm.ti.*;
/**
* Represents machine code produced and managed by the VM. The machine code
* is either produced by a compiler or manually assembled. Examples of the
* latter are {@linkplain Stub stubs} and {@linkplain Adapter adapters}.
*/
public abstract class TargetMethod extends MemoryRegion {
/**
* Implemented by a client wanting to do something to a target method.
*/
public interface Closure {
/**
* Processes a given target method.
*
* @param targetMethod the class to process
* @return {@code false} if target method processing (e.g. iteration) by the caller should be stopped
*/
boolean doTargetMethod(TargetMethod targetMethod);
}
/**
* Call back for use with {@link TargetMethod#forEachCodePos(CodePosClosure, CodePointer)}.
*/
public interface CodePosClosure {
/**
* Processes a given bytecode position.
*
* @param method the method actor on which this functionality is to be evaluated.
* @param bci the index in the method's bytecode.
* @return true if the caller should continue to the next code position (if any), false if it should terminate now
*/
boolean doCodePos(ClassMethodActor method, int bci);
}
/**
* The (bytecode) method from which this target method was compiled.
* This will be {@code null} iff this target method is a {@link Stub} or
* and {@link Adapter}.
*/
@INSPECTED
public final ClassMethodActor classMethodActor;
protected Safepoints safepoints = Safepoints.NO_SAFEPOINTS;
/**
* @see #directCallees()
*/
protected Object[] directCallees = NO_DIRECT_CALLEES;
/**
* @see #scalarLiterals()
*/
@INSPECTED
protected byte[] scalarLiterals;
/**
* @see #referenceLiterals()
*/
@INSPECTED(deepCopied = false)
protected Object[] referenceLiterals;
/**
* Pointer to a byte array, stored in the code cache allocation, that contains
* the target code for the compilation. When the code has been evicted (i.e.
* not survived a code cache eviction cycle), this value is set to the sentinel
* value {@link #WIPED_CODE}.
* <p>
* <strong>Note:</strong> The Inspector compares this field to the (fixed) sentinel
* pointer for its test that determines whether the code has been evicted.
* @see #code()
*/
@INSPECTED
private byte[] code;
/**
* @see #codeStart()
*/
@INSPECTED
private Pointer codeStart = Pointer.zero();
/**
* This field is used to implement {@linkplain CodeEviction code eviction}. It serves the following roles
* during different phase of code eviction:
* <ul>
* <li>As a mark field during code eviction, to identify survivors. The mark is set iff the value of this field is {@link Address#allOnes()}.</li>
* <li>To record the old value of {@link #start()} prior the target method being relocated.</li>
* </ul>
*/
@INSPECTED
protected Address oldStart = Address.zero();
/**
* If non-null, then this method has been invalidated.
* Set only by deoptimization operation at safepoint.
*
* @see #invalidated()
*/
private InvalidationMarker invalidated;
/**
* The frame size (in bytes) of an activation of this target method. This does not
* include the space occupied by a return address (if the arch uses one).
*/
private int frameSize = -1;
/**
* The offset of the code restoring the saved registers and returning to the caller.
* When unwinding the stack to an exception handler in the caller of such a method, control must be returned
* to this address (after the return address has been patched with the address of the exception handler).
* A value of {@code -1} means this is not a callee-save target method.
*/
private int registerRestoreEpilogueOffset = -1;
public TargetMethod(String description, CallEntryPoint callEntryPoint) {
assert this instanceof Stub || this instanceof Adapter;
this.classMethodActor = null;
this.callEntryPoint = callEntryPoint;
setRegionName(description);
}
public TargetMethod(ClassMethodActor classMethodActor, CallEntryPoint callEntryPoint) {
assert classMethodActor != null;
assert !(this instanceof Stub);
assert !(this instanceof Adapter);
this.classMethodActor = classMethodActor;
this.callEntryPoint = callEntryPoint;
setRegionName(classMethodActor.name.toString());
}
public int registerRestoreEpilogueOffset() {
return registerRestoreEpilogueOffset;
}
protected void setRegisterRestoreEpilogueOffset(int x) {
registerRestoreEpilogueOffset = x;
}
public final ClassMethodActor classMethodActor() {
return classMethodActor;
}
/**
* Gets the code attribute from which this target method was compiled. This may differ from
* the code attribute of {@link #classMethodActor} in the case where it was rewritten.
*/
public CodeAttribute codeAttribute() {
return classMethodActor == null ? null : classMethodActor.codeAttribute();
}
/**
* Notify this method that it survived an {@linkplain CodeEviction eviction cycle}.
*/
public void survivedEviction() {
// empty
}
/**
* @return true if this method has been {@linkplain CodeEviction evicted} (i.e., its
* machine code is no longer present in the code cache).
*/
public boolean wasEvicted() {
return false;
}
/**
* @return the number of survived {@linkplain CodeEviction eviction cycles}.
*/
public int timesRelocated() {
return 0;
}
/**
* Marks this method as invalidated.
*
* @return true if this was the first attempt to invalidate the method. If not, then the invalidation marker for
* this method was not updated.
* @see #invalidated()
*/
public boolean invalidate(InvalidationMarker marker) {
assert marker != null;
assert isHosted() || VmThread.current().isVmOperationThread();
if (invalidated == null) {
invalidated = marker;
return true;
}
return false;
}
/**
* Determines if this method has been invalidated.
* Invalidated target methods are never used when linking an unlinked
* call site or when resolving a dispatch table entry (e.g. vtable or itable).
* In addition, threads currently executing an invalidated method are required
* to apply deoptimization as soon as the execution context re-enters
* (via returning or stack unwinding during exception throwing) the method.
*
* @return a non-null {@link InvalidationMarker} object iff this method has been invalidated
*/
public InvalidationMarker invalidated() {
return invalidated;
}
/**
* Iterates over the bytecode locations for the inlining chain rooted at a given instruction pointer.
*
* @param cpc a closure called for each bytecode location in the inlining chain rooted at {@code ip} (inner most
* callee first)
* @param ip a pointer to an instruction within this method
* @return the number of bytecode locations iterated over (i.e. the number of times
* {@link CodePosClosure#doCodePos(ClassMethodActor, int)} was called
*/
public int forEachCodePos(CodePosClosure cpc, CodePointer ip) {
return 0;
}
/**
* Provides access to live frame values.
*/
public static class FrameAccess {
/**
* Layout of the callee save area.
*/
public final CiCalleeSaveLayout csl;
/**
* Callee save area.
*/
public final Pointer csa;
/**
* Stack pointer.
*/
public final Pointer sp;
/**
* Frame pointer.
*/
public final Pointer fp;
/**
* Stack pointer.
*/
public Pointer callerSP;
/**
* Frame pointer.
*/
public Pointer callerFP;
public FrameAccess(CiCalleeSaveLayout csl, Pointer csa, Pointer sp, Pointer fp, Pointer callerSP, Pointer callerFP) {
this.csl = csl;
this.csa = csa;
this.sp = sp;
this.fp = fp;
this.callerSP = callerSP;
this.callerFP = callerFP;
}
/**
* For use when walking stacks in callee/caller order.
* @param callerSP
* @param callerFP
*/
public void setCallerInfo(Pointer callerSP, Pointer callerFP) {
this.callerSP = callerSP;
this.callerFP = callerFP;
}
}
/**
* Gets the debug info available for a given safepoint. If {@code fa != null}, then the {@linkplain CiFrame#values values} in the
* returned object are {@link CiConstant}s wrapping values from the thread's stack denoted by {@code fa}. Otherwise,
* they are {@link CiStackSlot}, {@link CiRegister} and {@link CiConstant} values describing how to extract values
* from a live frame.
* <p>
* Note that the returned object is only suitable for deoptimization if an
* optimizing compiler produced this target method. Otherwise, the frame info
* in the returned object may include slots that are not actually live as
* the given safepoint.
*
* @param safepointIndex an index of a safepoint within this method
* @param fa access to a live frame (may be {@code null})
* @return the debug info at the denoted safepoint of {@code null} if none available
*/
public CiDebugInfo debugInfoAt(int safepointIndex, FrameAccess fa) {
return null;
}
/**
* Gets an array containing the direct callees of this method.
* The array can contain instances of {@link ClassMethodActor} and {@link TargetMethod} side by side.
* In case a callee is an actual method, it is represented by a {@link ClassMethodActor}.
* In case it is a stub or the adapter, a {@link TargetMethod} is used.
*
* @return entities referenced by direct call instructions
* @see Safepoints#nextDirectCall(int)
*/
public final Object[] directCallees() {
return directCallees;
}
/**
* Helper method to turn an entry from a directCallees array into a {@link TargetMethod}.
*/
public static TargetMethod directCalleeFrom(Object callee) {
if (callee == null) {
return null;
}
return callee instanceof TargetMethod ? (TargetMethod) callee : ((ClassMethodActor) callee).currentTargetMethod();
}
/**
* Gets the call entry point to be used for a direct call from this target method. By default, the
* {@linkplain #callEntryPoint call entry point} of this target method will be used.
*
* @param safepointIndex the index of the call in {@link #safepoints() safepoints}
*/
protected CallEntryPoint callEntryPointForDirectCall(int safepointIndex) {
return callEntryPoint;
}
/**
* @return non-object data referenced by the machine code
*/
public final byte[] scalarLiterals() {
return scalarLiterals;
}
public final int numberOfScalarLiteralBytes() {
return (scalarLiterals == null) ? 0 : scalarLiterals.length;
}
/**
* @return object references referenced by the machine code
*/
public final Object[] referenceLiterals() {
return referenceLiterals;
}
public final int numberOfReferenceLiterals() {
return (referenceLiterals == null) ? 0 : referenceLiterals.length;
}
/**
* Gets the byte array containing the target-specific machine code of this target method.
*
* Note: if this array is assigned to a variable outside of its target method, care should be taken that no
* safepoints occur between the assignment and uses of the variable. At safepoints, code eviction (see {@link CodeEviction}) might
* take place, potentially invalidating the address of the array, so that client code accessing the variable
* would end up working on invalid addresses.
*/
@INLINE
public final byte[] code() {
return code;
}
public final int codeLength() {
return (code == null) ? 0 : code.length;
}
/**
* Gets the address of the first instruction in this target method's {@linkplain #code() compiled code array}
* in the form of an eviction-safe {@link CodePointer}.
*/
@INLINE
public final CodePointer codeStart() {
return CodePointer.from(codeStart);
}
/**
* Gets the address of a particular instruction in this target method's {@linkplain #code() compiled code array}
* in the form of an eviction-safe {@link CodePointer}.
*
* @param pos the code position. A value of 0 implies the first instruction. This must be a value in the range {@code [0 .. codeLength())}.
*/
@INLINE
public final CodePointer codeAt(int pos) {
assert pos >= 0 && pos < codeLength();
return CodePointer.from(codeStart.plus(pos));
}
@INLINE
public final Address oldStart() {
return oldStart;
}
public final void setOldStart(Address ocs) {
oldStart = ocs;
}
@HOSTED_ONLY
public final void setCodeStart(Pointer codeStart) {
this.codeStart = codeStart;
}
/**
* Gets the size (in bytes) of the stack frame used for the local variables in
* the method represented by this object. The stack pointer is decremented
* by this amount when entering a method and is correspondingly incremented
* by this amount when exiting a method.
*/
public final int frameSize() {
assert frameSize != -1 : "frame size not yet initialized";
return frameSize;
}
/**
* The entry point used for <i>standard</i> calls in this target method to JVM compiled/interpreted code.
* Non-standard calls are those to external native code and calls to the runtime inserted by the
* compiler. The former type of calls directly use the native address supplied by the {@linkplain DynamicLinker linker}
* and the latter always uses {@link CallEntryPoint#OPTIMIZED_ENTRY_POINT}.
*/
@INSPECTED
public final CallEntryPoint callEntryPoint;
public static final Object[] NO_DIRECT_CALLEES = {};
/**
* Assigns the arrays co-located in a {@linkplain CodeRegion code region} containing the machine code and related data.
*
* @param code the code
* @param codeStart the address of the first element of {@code code}
* @param scalarLiterals the scalar data referenced from {@code code}
* @param referenceLiterals the reference data referenced from {@code code}
*/
public final void setCodeArrays(byte[] code, Pointer codeStart, byte[] scalarLiterals, Object[] referenceLiterals) {
this.scalarLiterals = scalarLiterals;
this.referenceLiterals = referenceLiterals;
this.code = code;
this.codeStart = codeStart;
}
protected final void setSafepoints(Safepoints safepoints, Object[] directCallees) {
this.safepoints = safepoints;
this.directCallees = directCallees;
assert safepoints.numberOfDirectCalls() == directCallees.length : safepoints.numberOfDirectCalls() + " != " + directCallees.length;
}
protected final void setFrameSize(int frameSize) {
assert frameSize != -1 : "invalid frame size!";
this.frameSize = frameSize;
}
/**
* Gets this method's life span, i.e., a value indicating how long the machine code is expected to live.
* For baseline methods, this is short - they might be evicted once the code cache meets contention.
* Class initialisers are run only once, they have a one-shot life span.
* Optimised methods, adapters, and stubs are expected to live for a long time.
*/
public abstract Lifespan lifespan();
protected void initCodeBuffer(CiTargetMethod ciTargetMethod, boolean install) {
// Create the arrays for the scalar and the object reference literals
Literals literals = new Literals(ciTargetMethod.dataReferences);
// Allocate and set the code and data buffer
final TargetBundleLayout targetBundleLayout = new TargetBundleLayout(literals.scalars.length, literals.objects.length, ciTargetMethod.targetCodeSize());
if (install) {
Code.allocate(targetBundleLayout, this);
} else {
Code.allocateInHeap(targetBundleLayout, this);
}
if (literals.scalars.length != 0 && literals.scalarsAlignment != 0) {
Pointer scalars = targetBundleLayout.firstElementPointer(start, ArrayField.scalarLiterals);
Address alignedScalars = scalars.alignUp(literals.scalarsAlignment);
if (!scalars.equals(alignedScalars)) {
assert alignedScalars.greaterThan(scalars);
literals.relocateScalars(alignedScalars.minus(scalars).toInt());
}
}
setData(literals.scalars, literals.objects, ciTargetMethod.targetCode());
// Patch relative instructions in the code buffer
assert lifespan() == Lifespan.LONG : "code may move: must protect direct code pointers";
patchInstructions(targetBundleLayout, ciTargetMethod, literals);
}
/**
* Used to serialize a list of {@link DataPatch}es to data structures
* that are co-located with the code that accesses the data.
*/
static class Literals {
/**
* Map from scalar data indexes to the position of the scalar data in {@link #scalars}.
*/
final int[] scalarsMap;
/**
* Serialized scalar literals.
*/
final byte[] scalars;
/**
* The object literals.
*/
final IdentityHashMap<Object, Integer> objectPool;
final Object[] objects;
/**
* The largest alignment requirement of any data in {@link #scalars}. This will be
* 0 if there are no alignment requirements.
*/
final int scalarsAlignment;
/**
* Gets the index in {@link TargetMethod#scalarLiterals} of the scalar data at
* index {@code index} in {@link CiTargetMethod#dataReferences}.
*/
int scalarPos(int dataIndex) {
return scalarsMap[dataIndex];
}
/**
* Relocates the scalar literals by shifting the contents of {@link #scalars} by {@code delta}
* to the right. This assumes that at least {@code delta} padding was put into the end of {@link #scalars}.
*/
void relocateScalars(int delta) {
assert delta > 0;
for (int i = 0; i < scalarsMap.length; i++) {
if (scalarsMap[i] >= 0) {
scalarsMap[i] += delta;
}
}
for (int i = scalars.length - 1; i >= delta; --i) {
scalars[i] = scalars[i - delta];
}
}
public Literals(List<DataPatch> dataReferences) {
objectPool = new IdentityHashMap<Object, Integer>(dataReferences.size());
ArrayList<Object> objectsBuffer = new ArrayList<Object>(dataReferences.size());
ByteArrayOutputStream scalarsBuffer = new ByteArrayOutputStream();
Endianness endianness = platform().endianness();
scalarsMap = new int[dataReferences.size()];
int dataIndex = 0;
int scalarsAlignment = 0;
// Data patches need to be sorted in decreasing order of their alignment requirements
for (DataPatch site : dataReferences) {
if (site.alignment != 0) {
Collections.sort(dataReferences, new Comparator<DataPatch>() {
@Override
public int compare(DataPatch o1, DataPatch o2) {
return o2.alignment - o1.alignment;
}
});
break;
}
}
for (DataPatch site : dataReferences) {
final CiConstant data = site.constant;
if (!data.kind.isObject()) {
if (site.alignment != 0) {
while ((scalarsBuffer.size() & (site.alignment - 1)) != 0) {
scalarsBuffer.write(0);
}
if (site.alignment > scalarsAlignment) {
scalarsAlignment = site.alignment;
}
}
scalarsMap[dataIndex] = scalarsBuffer.size();
} else {
assert site.alignment == 0 : "Alignment for object literals not supported";
scalarsMap[dataIndex] = -1;
}
try {
switch (data.kind) {
case Double:
endianness.writeLong(scalarsBuffer, Double.doubleToRawLongBits(data.asDouble()));
break;
case Float:
endianness.writeInt(scalarsBuffer, Float.floatToRawIntBits(data.asFloat()));
break;
case Int:
endianness.writeInt(scalarsBuffer, data.asInt());
break;
case Long:
endianness.writeLong(scalarsBuffer, data.asLong());
break;
case Object: {
Object object = data.asObject();
assert object != null;
if (!objectPool.containsKey(object)) {
objectPool.put(object, objectsBuffer.size());
objectsBuffer.add(object);
}
break;
}
default:
throw new IllegalArgumentException("Unknown constant type!");
}
} catch (IOException e) {
throw (InternalError) new InternalError("Error serializing " + data).initCause(e);
}
dataIndex++;
}
final int guaranteedAlignment = Word.size();
int padding = scalarsAlignment - guaranteedAlignment;
while (padding > 0) {
scalarsBuffer.write(0);
padding--;
}
scalars = scalarsBuffer.toByteArray();
this.scalarsAlignment = scalarsAlignment;
objects = objectsBuffer.toArray();
}
}
private void patchInstructions(TargetBundleLayout targetBundleLayout, CiTargetMethod ciTargetMethod, Literals literals) {
Offset codeStart = targetBundleLayout.cellOffset(TargetBundleLayout.ArrayField.code);
Offset scalarDiff = Offset.zero();
if (this.scalarLiterals != null) {
Offset scalarStart = targetBundleLayout.cellOffset(TargetBundleLayout.ArrayField.scalarLiterals);
scalarDiff = scalarStart.minus(codeStart).asOffset();
}
Offset referenceDiff = Offset.zero();
if (this.referenceLiterals() != null) {
Offset referenceStart = targetBundleLayout.cellOffset(TargetBundleLayout.ArrayField.referenceLiterals);
referenceDiff = referenceStart.minus(codeStart).asOffset();
}
int dataIndex = 0;
for (DataPatch site : ciTargetMethod.dataReferences) {
switch (site.constant.kind) {
case Double: // fall through
case Float: // fall through
case Int: // fall through
case Long: {
assert site.alignment == 0 || targetBundleLayout.firstElementPointer(start, ArrayField.scalarLiterals).plus(literals.scalarPos(dataIndex)).isAligned(site.alignment) : "patching to a scalar address that is not aligned";
patchRelativeInstruction(site.pcOffset, scalarDiff.plus(literals.scalarPos(dataIndex) - site.pcOffset).toInt());
break;
}
case Object: {
int index = literals.objectPool.get(site.constant.asObject());
patchRelativeInstruction(site.pcOffset, referenceDiff.plus(index * Word.size() - site.pcOffset).toInt());
break;
}
default:
throw new IllegalArgumentException("Unknown constant type!");
}
dataIndex++;
}
}
private void patchRelativeInstruction(int codePos, int displacement) {
if (platform().isa == ISA.AMD64) {
X86InstructionDecoder.patchRelativeInstruction(code(), codePos, displacement);
} else {
throw FatalError.unimplemented();
}
}
protected void initFrameLayout(CiTargetMethod ciTargetMethod) {
this.setFrameSize(ciTargetMethod.frameSize());
this.setRegisterRestoreEpilogueOffset(ciTargetMethod.registerRestoreEpilogueOffset());
}
protected CiDebugInfo[] initSafepoints(CiTargetMethod ciTargetMethod) {
Adapter adapter = null;
int adapterCount = 0;
AdapterGenerator generator = AdapterGenerator.forCallee(this);
if (generator != null) {
adapter = generator.make(classMethodActor);
if (adapter != null) {
adapterCount = 1;
}
}
int total = ciTargetMethod.safepoints.size() + adapterCount;
int directCalls = 0;
for (Safepoint safepoint : ciTargetMethod.safepoints) {
if (safepoint instanceof Call && ((Call) safepoint).direct) {
directCalls++;
}
}
int index = 0;
int[] safepoints = new int[total];
Object[] directCallees = new Object[directCalls + adapterCount];
CiDebugInfo[] debugInfos = new CiDebugInfo[total];
int dcIndex = 0;
if (adapter != null) {
directCallees[index] = adapter;
int callPos = adapter.callOffsetInPrologue();
int safepointPos = safepointPosForCall(callPos, adapter.callSizeInPrologue());
safepoints[index] = Safepoints.make(safepointPos, callPos, DIRECT_CALL);
dcIndex++;
index++;
}
for (Safepoint safepoint : ciTargetMethod.safepoints) {
int encodedSafepoint;
if (safepoint instanceof Call) {
Call call = (Call) safepoint;
int causePos = call.pcOffset;
int safepointPos = safepointPosForCall(call.pcOffset, call.size);
if (call.direct) {
directCallees[dcIndex++] = CallTarget.directCallee(call.target);
if (CallTarget.isTemplateCall(call.target)) {
encodedSafepoint = Safepoints.make(safepointPos, causePos, DIRECT_CALL, TEMPLATE_CALL);
} else {
encodedSafepoint = Safepoints.make(safepointPos, causePos, DIRECT_CALL);
}
} else {
if (CallTarget.isTemplateCall(call.target)) {
encodedSafepoint = Safepoints.make(safepointPos, causePos, INDIRECT_CALL, TEMPLATE_CALL);
} else if (CallTarget.isSymbol(call.target)) {
encodedSafepoint = Safepoints.make(safepointPos, causePos, INDIRECT_CALL, NATIVE_CALL);
// ClassMethodActor caller = (ClassMethodActor) call.debugInfo.codePos.method;
// (ds) cannot rely on debug info at call since Graal uses templates to produce
// native method stubs and the debug info will be in the context of the template
// source code, not the native method.
ClassMethodActor caller = classMethodActor;
assert caller.isNative();
caller.nativeFunction.setCallSite(this, safepointPos);
} else {
encodedSafepoint = Safepoints.make(safepointPos, causePos, INDIRECT_CALL);
}
}
} else {
int safepointPos = safepoint.pcOffset;
encodedSafepoint = Safepoints.make(safepointPos);
}
debugInfos[index] = safepoint.debugInfo;
safepoints[index] = encodedSafepoint;
index++;
}
setSafepoints(new Safepoints(safepoints), directCallees);
return debugInfos;
}
/**
* Completes the definition of this target method as the result of compilation.
*
* @param scalarLiterals a byte array encoding the scalar data accessed by this target via code relative offsets
* @param objectLiterals an object array encoding the object references accessed by this target via code relative
* offsets
* @param codeBuffer the buffer containing the compiled code. The compiled code is in the first {@code this.code.length} bytes of {@code codeBuffer}.
*/
protected final void setData(byte[] scalarLiterals, Object[] objectLiterals, byte[] codeBuffer) {
assert !codeStart.isZero() : "Must call setCodeArrays() first";
// Copy scalar literals
if (scalarLiterals != null && scalarLiterals.length > 0) {
assert scalarLiterals.length != 0;
System.arraycopy(scalarLiterals, 0, this.scalarLiterals, 0, this.scalarLiterals.length);
}
// Copy object literals
if (objectLiterals != null && objectLiterals.length > 0) {
System.arraycopy(objectLiterals, 0, this.referenceLiterals, 0, this.referenceLiterals.length);
}
// now copy the code
System.arraycopy(codeBuffer, 0, this.code, 0, this.code.length);
}
public final ClassMethodActor callSiteToCallee(CodePointer callSite) {
final int callPos = callSite.minus(codeStart).toInt();
int dcIndex = 0;
for (int i = 0; i < safepoints.size(); i++) {
if (safepoints.isSetAt(DIRECT_CALL, i)) {
if (safepoints.causePosAt(i) == callPos && directCallees[dcIndex] instanceof ClassMethodActor) {
return (ClassMethodActor) directCallees[dcIndex];
}
dcIndex++;
}
}
final boolean lockDisabledSafepoints = Log.lock();
Log.print("Could not find callee in ");
Log.print(this);
Log.print(" for call site: ");
Log.print(callSite.toHexString());
Log.print(" [");
Log.print(codeStart());
Log.print(" + ");
Log.print(callPos);
Log.println("]");
dcIndex = 0;
for (int i = 0; i < safepoints.size(); i++) {
if (safepoints.isSetAt(DIRECT_CALL, i)) {
if (safepoints.causePosAt(i) == callPos) {
Log.print("* ");
} else {
Log.print(" ");
}
Log.print("safepoint # ");
Log.print(i);
Log.print(", direct call # ");
Log.print(dcIndex);
Log.print(" ");
Log.println(directCallees[dcIndex]);
dcIndex++;
}
}
Log.unlock(lockDisabledSafepoints);
FatalError.breakpoint();
throw FatalError.unexpected("could not find callee for call site: " + callSite.toHexString());
}
/**
* Returns an absolute address in the machine code of this method, corresponding to the kind of entry point
* passed as parameter.
*
* Note that this method returns an absolute code address. Its usage should occur soon after the invocation of
* this method, without any safepoints in between. At safepoints, code eviction (see {@link CodeEviction}) might
* take place, potentially invalidating absolute code addresses.
*
* @param callEntryPoint the kind of {@link CallEntryPoint} for which the address is to be returned
* @return the entry address ({@link CodePointer}) in this method according to the {@code callEntryPoint}
*/
public CodePointer getEntryPoint(CallEntryPoint callEntryPoint) {
return callEntryPoint.in(this);
}
/**
* Resets a direct call site, make it point to the static trampoline again.
*
* @param safepointIndex the index of the call in {@link #safepoints() safepoints}
* @param dcIndex the index of the call in {@link #directCallees()}
*
* @return {@code true} if the call site was not already pointing to the static trampoline
*/
public final boolean resetDirectCall(int safepointIndex, int dcIndex) {
Object callee = directCallees[dcIndex];
assert !(callee instanceof Adapter);
final int offset = getCallEntryOffset(callee, safepointIndex);
final int callPos = safepoints.causePosAt(safepointIndex);
CodePointer trampoline = vm().stubs.staticTrampoline().codeAt(offset);
return !patchCallSite(callPos, trampoline).equals(trampoline);
}
/**
* Patches the entry point(s) of this target method with direct jump(s) to the
* corresponding entry points of {@code tm}.
* <p>
* <b>This operation can only be performed when at a global safepoint as the patching
* is not guaranteed to be atomic.</b>
*
* @param tm the target of the jump instruction(s) to be patched in
*/
public void redirectTo(TargetMethod tm) {
throw FatalError.unexpected("Cannot patch entry points of " + getClass().getSimpleName() + " " + this);
}
/**
* Links all the calls from this target method to other methods for which the exact method actor is known. Linking a
* call means patching the operand of a call instruction that specifies the address of the target code to call. In
* the case of a callee for which there is no target code available (i.e. it has not yet been compiled or it has
* been evicted from the code cache), the address of a static trampoline is patched
* into the call instruction.
*
* @return true if target code was available for all the direct callees
*/
public final boolean linkDirectCalls() {
boolean linkedAll = true;
if (directCallees.length != 0) {
int dcIndex = 0;
for (int safepointIndex = safepoints.nextDirectCall(0); safepointIndex >= 0; safepointIndex = safepoints.nextDirectCall(safepointIndex + 1)) {
Object currentDirectCallee = directCallees[dcIndex];
final int offset = getCallEntryOffset(currentDirectCallee, safepointIndex);
if (currentDirectCallee == null) {
// template call
assert classMethodActor.isTemplate();
} else if (MaxineVM.isHosted()) {
final TargetMethod callee = getTargetMethod(currentDirectCallee);
if (callee == null) {
if (classMethodActor.isTemplate()) {
assert currentDirectCallee == classMethodActor : "unlinkable call in a template must be a template call";
// leave call site unpatched
} else {
linkedAll = false;
patchStaticTrampoline(safepointIndex, offset);
}
} else {
int callPos = safepoints.causePosAt(safepointIndex);
fixupCallSite(callPos, callee.codeAt(offset));
}
} else {
final TargetMethod callee = getTargetMethod(currentDirectCallee);
if (callee == null || (!Code.bootCodeRegion().contains(callee.codeStart) && !(callee instanceof Adapter))) {
linkedAll = false;
patchStaticTrampoline(safepointIndex, offset);
} else {
int callPos = safepoints.causePosAt(safepointIndex);
fixupCallSite(callPos, callee.codeAt(offset));
}
}
dcIndex++;
}
}
return linkedAll;
}
private void patchStaticTrampoline(final int safepointIndex, final int offset) {
final int callPos = safepoints.causePosAt(safepointIndex);
final CodePointer callSite = codeAt(callPos);
if (!isPatchableCallSite(callSite)) {
FatalError.unexpected(classMethodActor + ": call site calling static trampoline must be patchable: 0x" + callSite.toHexString() +
" [0x" + codeStart.toHexString() + "+" + callPos + "]");
}
fixupCallSite(callPos, vm().stubs.staticTrampoline().codeAt(offset));
}
public final TargetMethod getTargetMethod(Object o) {
TargetMethod result = null;
if (o instanceof ClassMethodActor) {
result = ((ClassMethodActor) o).currentTargetMethod();
} else if (o instanceof TargetMethod) {
result = (TargetMethod) o;
}
return result;
}
private int getCallEntryOffset(Object callee, int safepointIndex) {
if (callee instanceof Adapter) {
return 0;
}
final CallEntryPoint callEntryPoint = callEntryPointForDirectCall(safepointIndex);
return callEntryPoint.offsetInCallee();
}
public final boolean isCalleeSaved() {
return registerRestoreEpilogueOffset >= 0;
}
/**
* Gets the code annotations (if any) associated with this target method.
*
* @return {@code null} if there are no code annotations
*/
public CodeAnnotation[] annotations() {
return null;
}
/**
* @see Safepoints
*/
public final Safepoints safepoints() {
return safepoints;
}
/**
* Gets the target code position for a machine code instruction address.
*
* @param ip
* an instruction pointer that may denote an instruction in this target method
* @return the start position of the bytecode instruction that is implemented at the instruction pointer or
* -1 if {@code ip} denotes an instruction that does not correlate to any bytecode. This will
* be the case when {@code ip} is in the adapter frame stub code, prologue or epilogue.
*/
public int posFor(CodePointer ip) {
final int pos = (int) (ip.toLong() - codeStart.toLong());
if (pos >= 0 && pos <= code.length) {
return pos;
}
return -1;
}
/**
* Gets a mapping from bytecode positions to target code positions. The bytecode positions are in terms of
* the bytecode for this target method's {@link #classMethodActor}.
* A non-zero value
* {@code val} at index {@code i} in the array encodes that there is a bytecode instruction whose opcode is at index
* {@code i} in the bytecode array and whose target code position is {@code val}. Unless {@code i} is equal to the
* length of the bytecode array in which case {@code val} denotes the target code position one byte past the
* last target code byte emitted for the last bytecode instruction.
*
* @return {@code null} if there is no such mapping available
*/
public int[] bciToPosMap() {
return null;
}
public int findSafepointIndex(CodePointer ip) {
final int pos = posFor(ip);
if (safepoints == null || pos < 0 || pos > code.length) {
return -1;
}
return findSafepointIndex(pos);
}
public int findSafepointIndex(int pos) {
return safepoints.indexOf(pos);
}
@Override
public final String toString() {
return (classMethodActor == null) ? regionName() : classMethodActor.format("%H.%n(%p)");
}
public String name() {
return regionName();
}
/**
* Analyzes the target method that this compiler produced to build a call graph. This method gathers the
* methods called directly or indirectly by this target method as well as the methods it inlined.
*
* @param directCalls the set of direct calls to which this method should append
* @param virtualCalls the set of virtual calls to which this method should append
* @param interfaceCalls the set of interface calls to which this method should append
* @param inlinedMethods the set of inlined methods to which this method should append
*/
@HOSTED_ONLY
public abstract void gatherCalls(Set<MethodActor> directCalls, Set<MethodActor> virtualCalls, Set<MethodActor> interfaceCalls, Set<MethodActor> inlinedMethods);
/**
* Modifies the call site at the specified offset to use the new specified entry point.
* The modification must tolerate the execution of the target method by concurrently running threads.
*
* @param callOffset offset to a call site relative to the start of the code of this target method
* @param callEntryPoint entry point the call site should call after patching
* @return the entry point of the call prior to patching
*/
public abstract CodePointer patchCallSite(int callOffset, CodePointer callEntryPoint);
/**
* Fixup a call site in the method. This differs from the above in that the call site is updated before
* any thread can see it. Thus there isn't any concurrency between modifying the call site and threads
* trying to run it.
*
* @param callOffset offset to a call site relative to the start of the code of this target method
* @param callEntryPoint entry point the call site should call after fixup
* @return the entry point of the call prior to patching
*/
public abstract CodePointer fixupCallSite(int callOffset, CodePointer callEntryPoint);
/**
* Indicates whether a call site can be patched safely when multiple threads may execute this target method concurrently.
* @param callSite offset to a call site relative to the start of the code of this target method.
* @return true if mt-safe patching is possible on the specified call site.
*/
public abstract boolean isPatchableCallSite(CodePointer callSite);
/**
* Prepares the reference map for the current frame (and potentially for registers stored in a callee frame).
*
* @param current the current stack frame
* @param callee the callee stack frame (ignoring any interposing {@linkplain Adapter adapter} frame)
* @param preparer the reference map preparer which receives the reference map
*/
public abstract void prepareReferenceMap(StackFrameCursor current, StackFrameCursor callee, FrameReferenceMapVisitor preparer);
/**
* Attempts to catch an exception thrown by this method or a callee method. If a handler exists,
* then the stack is unwound and execution is resumed at the handler.
* <p>
* In the case that is an {@link #invalidated() invalidated} method, the same unwinding
* occurs but executed is redirected to an appropriate deoptimization stub.
* The value of {@code current.ip()} is saved in the {@linkplain Deoptimization#DEOPT_RETURN_ADDRESS_OFFSET
* rescue} slot. This is required so that the frame state associated with the call site is used when
* deoptimizing. This frame state matches the state on entry to the handler
* except that the operand stack is cleared (the exception object is explicitly retrieved and pushed by
* the handler).
*
* @param current the current stack frame
* @param callee the callee stack frame (ignoring any interposing {@linkplain Adapter adapter} frame)
* @param throwable the exception thrown
*/
public abstract void catchException(StackFrameCursor current, StackFrameCursor callee, Throwable throwable);
/**
* Similar to {@link #catchException} but simply checks if there is a handler for {@code exception}
* and returns its code address if so, otherwise {@link CodePointer#zero}.
* @param throwAddress the throw address
*/
public abstract CodePointer throwAddressToCatchAddress(CodePointer throwAddress, Throwable throwable);
public static class CatchExceptionInfo {
public CodePointer codePointer;
public int bci;
}
/**
* Similar to {@link #catchException} save that the stack is not unwound, and just the info on the catch
* location is returned.
* @param current
* @param throwable
* @param info instance in which to store the info
* @return {@code true} iff the exception is handled by this method
*/
public boolean catchExceptionInfo(StackFrameCursor current, Throwable throwable, CatchExceptionInfo info) {
return false;
}
/**
* Accepts a visitor for this stack frame. As this only ever happens in Inspector contexts, this method is
* annotated with {@link HOSTED_ONLY}.
*
* @param current the current stack frame
* @param visitor the visitor which will visit the frame
* @return {@code true} if the visitor indicates the stack walk should continue
*/
@HOSTED_ONLY
public abstract boolean acceptStackFrameVisitor(StackFrameCursor current, StackFrameVisitor visitor);
/**
* Advances the stack frame cursor from this frame to the next frame.
* @param current the current stack frame cursor
*/
public abstract void advance(StackFrameCursor current);
/**
* Gets a pointer to the memory word holding the return address in a frame of this target method.
*
* @param frame an activation frame for this target method
*/
public abstract Pointer returnAddressPointer(StackFrameCursor frame);
/**
* Finalize reference maps if necessary.
*/
public void finalizeReferenceMaps() {
}
/**
* Determines if this a {@link Nature#BASELINE} target method.
*/
public boolean isBaseline() {
return false;
}
/**
* Determines if this method has been instrumented by a {@link VMTIHandler tooling interface}.
*/
public boolean isInstrumented() {
return false;
}
/**
* Creates a deoptimized frame for this method. This can only be called if {@link #isBaseline()}
* returns {@code true} for this object.
*
* @param info details of current deoptimization
* @param frame debug info from which the slots of the deoptimized are initialized
* @param callee used to notify callee of the execution state the deoptimized frame when it is returned to
* @param exception if non-null, this is an in-flight exception that must be handled by the deoptimized frame
* @param reexecute specifies if the instruction at {@code frame.bci} is to be re-executed (ignored if {@code exception != null})
* @return object for notifying the deoptimized frame's caller of continuation state
*/
public Continuation createDeoptimizedFrame(Info info, CiFrame frame, Continuation callee, Throwable exception, boolean reexecute) {
throw FatalError.unexpected("Cannot create deoptimized frame for " + getClass().getSimpleName() + " " + this);
}
/**
* Determines if this method has been compiled under the invariant that the
* register state upon entry to a local exception handler for an implicit
* exception is the same as at the implicit exception point.
*/
public boolean preserveRegistersForLocalExceptionHandler() {
return true;
}
/**
* Gets the profile data gathered during execution of this method.
*
* @return {@code null} if this method has no profiling info
*/
public MethodProfile profile() {
return null;
}
/**
* Determines whether this method has a type profile.
*
* @return {@code true} if there is a type profile associated with this method.
*/
public boolean hasTypeProfile() {
return profile() != null && profile().rawInfo() != null;
}
/**
* Determines whether the entry count of this method is within the threshold
* denoted by {@link MethodInstrumentation#PROTECTION_PERCENTAGE}.
*/
public boolean withinInvocationThreshold() {
return profile() != null && profile().protectedEntryCount();
}
/**
* Gets the stub type of this target method.
*
* @return {@code null} if this is not {@linkplain Stub stub}
*/
public Stub.Type stubType() {
return null;
}
/**
* Invalidate this method's machine code and literals by letting them reference "wiped" sentinels.
*/
public void wipe() {
code = WIPED_CODE;
scalarLiterals = WIPED_SCALAR_LITERALS;
referenceLiterals = WIPED_REFERENCE_LITERALS;
}
public boolean isWiped() {
return code == WIPED_CODE;
}
/**
* Determines if this is a stub of a given type.
*/
public final boolean is(Stub.Type type) {
return stubType() == type;
}
/**
* Sentinel value for the {@link #code} field denoting that it is no longer valid.
* <p>
* <strong>Note:</strong> The Inspector compares the value in the code field against this
* sentinel's fixed location to determine whether the method's code has been evicted.
* That requires that the sentinel's value not change, so it should always be in an
* unmanaged heap (presumed to be in the boot heap automatically).
*/
@INSPECTED
private static final byte[] WIPED_CODE = {};
/**
* Sentinel value for the {@link #scalarLiterals} field denoting that it is no longer valid.
*/
private static final byte[] WIPED_SCALAR_LITERALS = {};
/**
* Sentinel value for the {@link #referenceLiterals} field denoting that it is no longer valid.
*/
private static final Object[] WIPED_REFERENCE_LITERALS = {};
/**
* Marks this target method for preservation during {@linkplain CodeEviction code eviction}.
*/
public final void mark() {
oldStart = Address.allOnes().asAddress();
}
public final void unmark() {
oldStart = Address.zero();
}
/**
* Determines if this method was marked to survive {@linkplain CodeEviction code eviction}.
*/
public final boolean isMarked() {
return oldStart == Address.allOnes().asAddress();
}
/**
* Determines if this method is protected from eviction.
*/
public boolean isProtected() {
return false;
}
/**
* Protect this method from eviction.
* This is not supported for all kinds of target methods.
*/
public void protect() {
// intentionally empty
}
/**
* Gets the layout of the CSA in this target method.
*
* @return {@code null} if this is not a callee-saved target method
*/
public CiCalleeSaveLayout calleeSaveLayout() {
if (classMethodActor != null) {
return vm().registerConfigs.getRegisterConfig(classMethodActor).csl;
}
return null;
}
public abstract VMFrameLayout frameLayout();
}