/*
* Copyright (c) 2009, 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 java.lang.reflect.*;
import java.util.*;
import com.sun.max.annotate.*;
import com.sun.max.lang.*;
import com.sun.max.vm.runtime.*;
/**
* A set of safepoints sorted by their {@linkplain #posAt(int) positions}. The information for each safepoint
* is encoded in an {@code int} as shown below.
* <pre>
*
* 0 25 26 27 28 29 30 31
* +--------------------------------------------+--------+--+--+--+--+
* | position | | | | | |
* +--------------------------------------------+--------+--+--+--+--+
* ^ ^ ^ ^ ^
* | | | | |
* | | | | +---- NATIVE_CALL
* | | | +------- INDIRECT_CALL
* | | +---------- DIRECT_CALL
* | +------------- TEMPLATE_CALL
* +------------------ cause position offset
* </pre>
*
* The width of the 'position' field supports a code array of up to 32Mb.
*/
public final class Safepoints {
/**
* Attributes of a safepoint.
*/
public static class Attr {
public final String name;
// Bit field info.
private final int mask;
@HOSTED_ONLY
Attr(String name, int bit) {
this.name = name;
this.mask = 1 << bit;
}
/**
* Determines if this attribute value is set for a given safepoint.
*
* @param safepoint the safepoint to query
*/
public boolean isSet(int safepoint) {
return (safepoint & mask) != 0;
}
@Override
public String toString() {
return name;
}
}
/**
* Attribute value denoting a direct call. A direct call is a call instruction with
* an operand encoding the offset from the call instruction to the target of the call.
*/
public static final Attr DIRECT_CALL = new Attr("DIRECT_CALL", 29);
/**
* Attribute value denoting an indirect call. An indirect call is a call instruction
* with a register or memory operand specifying the target address of the call.
*/
public static final Attr INDIRECT_CALL = new Attr("INDIRECT_CALL", 30);
/**
* Attribute value denoting a native function call. A safepoint with this attribute
* set will also have the {@link #INDIRECT_CALL} attribute set.
*/
public static final Attr NATIVE_CALL = new Attr("NATIVE_CALL", 31);
/**
* Attribute value denoting a template call. A safepoint with this attribute
* set will also have either the {@link #DIRECT_CALL} or {@link #INDIRECT_CALL} attribute set.
*/
public static final Attr TEMPLATE_CALL = new Attr("TEMPLATE_CALL", 28);
/**
* Complete set of declared safepoint attributes.
*/
public static final Attr[] ALL_ATTRS;
static {
ArrayList<Attr> attrs = new ArrayList<Attr>();
int mask = 0;
for (final Field field : Safepoints.class.getDeclaredFields()) {
if (field.getType() == Attr.class && Modifier.isStatic(field.getModifiers())) {
try {
Attr attr = (Attr) field.get(null);
assert !attr.isSet(mask) : attr + " has the same mask as another attribute";
mask |= attr.mask;
attrs.add(attr);
} catch (Exception e) {
throw FatalError.unexpected("Could not read value of " + field, e);
}
}
}
ALL_ATTRS = attrs.toArray(new Attr[attrs.size()]);
}
/**
* Mask for extracting position.
*/
public static final int POS_MASK = (1 << 25) - 1;
private static final int CAUSE_OFFSET_MASK = ((1 << 28) - 1) & ~POS_MASK;
private static final int CAUSE_OFFSET_SHIFT = 25;
private static final int MAX_CAUSE_OFFSET = 7;
/**
* Mask for extracting attributes.
*/
public static final int ATTRS_MASK = -1 & ~(POS_MASK | CAUSE_OFFSET_MASK);
private final int[] safepoints;
public static final Safepoints NO_SAFEPOINTS = new Safepoints();
/**
* Creates an object encapsulating a set of safepoints.
*/
public Safepoints(int... safepoints) {
this.safepoints = safepoints;
assert validateSafepoints(safepoints);
}
private boolean validateSafepoints(int[] safepoints) {
for (int i = 0; i < safepoints.length; i++) {
int safepoint = safepoints[i];
boolean isCall = isCall(safepoint);
assert !NATIVE_CALL.isSet(safepoint) || INDIRECT_CALL.isSet(safepoint) : "a native call must be an indirect call";
assert !TEMPLATE_CALL.isSet(safepoint) || isCall : "a template call must be a direct or indirect call";
assert causePos(safepoint) == pos(safepoint) || isCall : "cause position can only be different from safepoint position for a call";
if (i != 0) {
int pos = pos(safepoint);
int prev = safepoints[i - 1];
int prevPos = pos(prev);
assert prevPos < pos : "safepoint positions are not sorted: " + prevPos + " >= " + pos;
}
}
return true;
}
/**
* Gets the position denoted by a given index.
*
* @param index an index within the safepoint positions array wrapped by this object
*/
public int posAt(int index) {
return pos(safepoints[index]);
}
/**
* Gets the position of a safepoint.
*
* @param safepoint an encoded safepoint
*/
public static int pos(int safepoint) {
return safepoint & POS_MASK;
}
/**
* Gets the position of the instruction that is the reason for safepoint at a given index.
* On some platforms such as x86, the safepoint for a call is recorded at the position of
* the instruction following the call (i.e. the return address). However, the cause for
* the safepoint is the call instruction itself. Apart from safepoints for calls,
* {@code posAt(i) == causePosAt(i)}.
*
* @param index the index of a safepoint
*/
public int causePosAt(int index) {
return causePos(safepoints[index]);
}
/**
* Reads the cause position from an encoded safepoint.
*
* @see #causePosAt(int)
* @param safepoint an encoded safepoint
*/
public static int causePos(int safepoint) {
int pos = pos(safepoint);
int causeOffset = (safepoint & CAUSE_OFFSET_MASK) >> CAUSE_OFFSET_SHIFT;
return pos - causeOffset;
}
/**
* Determines if a given safepoint is a call.
*
* @param safepoint the safepoint to test
*/
public static boolean isCall(int safepoint) {
return INDIRECT_CALL.isSet(safepoint) || DIRECT_CALL.isSet(safepoint);
}
/**
* Gets the safepoint at a given index.
*
* @param index the index of the safepoint to retrieve
*/
public int safepointAt(int index) {
return safepoints[index];
}
/**
* Gets the number of safepoints.
*/
public int size() {
return safepoints.length;
}
/**
* Determines if a given attribute is set for a given safepoint.
*
* @param a the attribute to test
* @param index the index denoting the safepoint to query
*/
public boolean isSetAt(Attr a, int index) {
return a.isSet(safepoints[index]);
}
/**
* Finds the safepoint corresponding to a given code position.
*
* @param pos the position to search for
* @return the index of the safepoint whose {@linkplain #posAt(int) position} is equal to {@code pos} or -1 if no
* such safepoint exists
*/
public int indexOf(int pos) {
// Use binary search since safepoints are sorted by position
int left = 0;
int right = safepoints.length;
while (right > left) {
final int middle = left + ((right - left) >> 1);
int p = posAt(middle);
if (p > pos) {
right = middle;
} else if (p == pos) {
return middle;
} else {
left = middle + 1;
}
}
return -1;
}
/**
* Returns the index of the first direct call safepoint at or after a specified start index.
* If no such safepoint exists then -1 is returned.
* <p>
* To iterate over the direct calls, use the following loop:
*
* <pre>
* for (int i = safepoints.nextDirectCall(0); i >= 0; i = safepoints.nextDirectCall(i + 1)) {
* // operate on safepoint i here
* }
* </pre>
*
* @param i the index to start searching from (inclusive)
* @return the index of the first direct call safepoint between {@code [i .. length())} or -1 if there is no direct call safepoint in this range
* @throws IndexOutOfBoundsException if the specified index is negative.
*/
public int nextDirectCall(int i) {
while (i < safepoints.length) {
if (isSetAt(DIRECT_CALL, i)) {
return i;
}
i++;
}
return -1;
}
public int numberOfDirectCalls() {
int n = 0;
for (int i = 0; i < safepoints.length; i++) {
if (isSetAt(DIRECT_CALL, i)) {
n++;
}
}
return n;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(safepoints.length * 15);
sb.append("{");
for (int i = 0; i < safepoints.length; ++i) {
if (sb.length() != 1) {
sb.append(", ");
}
int safepoint = safepoints[i];
sb.append(i);
decodeSafepoint(safepoint, sb);
}
return sb.append("}").toString();
}
private static void decodeSafepoint(int safepoint, StringBuilder sb) {
sb.append(" -> ").append(pos(safepoint));
int attrs = safepoint & ATTRS_MASK;
if (attrs != 0) {
for (Attr a : Safepoints.ALL_ATTRS) {
if (a.isSet(attrs)) {
sb.append(" | ").append(a.name);
}
}
}
}
@HOSTED_ONLY
public static String inspectSafepoint(int safepoint) {
StringBuilder sb = new StringBuilder();
decodeSafepoint(safepoint, sb);
return sb.toString();
}
/**
* Gets the safepoint position associated with a call.
*
* @param callPos position of the call instruction
* @param callSize size of the call instruction
*/
public static int safepointPosForCall(int callPos, int callSize) {
if (platform().isa == ISA.AMD64) {
return callPos + callSize;
} else {
throw FatalError.unimplemented();
}
}
/**
* Encodes a safepoint with no attributes.
*
* @param safepointPos the position of the safepoint (also its {@linkplain #causePosAt(int) cause} position)
*/
public static int make(int safepointPos) {
return make(safepointPos, safepointPos, 0);
}
/**
* Encodes a safepoint with a single attribute.
*
* @param safepointPos the position of the safepoint
* @param causePos the {@linkplain #causePosAt(int) cause} position of the safepoint
* @param a an attribute of the safepoint
*/
public static int make(int safepointPos, int causePos, Attr a) {
return make(safepointPos, causePos, a.mask);
}
/**
* Encodes a safepoint with two attributes.
*
* @param safepointPos the position of the safepoint
* @param causePos the {@linkplain #causePosAt(int) cause} position of the safepoint
* @param a1 an attribute of the safepoint
* @param a2 an attribute of the safepoint
*/
public static int make(int safepointPos, int causePos, Attr a1, Attr a2) {
assert (a1.mask & a2.mask) == 0;
return make(safepointPos, causePos, a1.mask | a2.mask);
}
/**
* Encodes a safepoint.
*
* @param safepointPos the position of the safepoint
* @param causePos the {@linkplain #causePosAt(int) cause} position of the safepoint
* @param attrs mask of the safepoint's attributes
*/
public static int make(int safepointPos, int causePos, int attrs) {
assert pos(safepointPos) == safepointPos : "safepoint position out of range";
assert (attrs & ATTRS_MASK) == attrs;
int causeOffset = safepointPos - causePos;
assert causeOffset >= 0 && causeOffset <= MAX_CAUSE_OFFSET : "cause position out of range";
return safepointPos | (causeOffset << CAUSE_OFFSET_SHIFT) | attrs;
}
}