/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.osr;
import static org.jikesrvm.classloader.ClassLoaderConstants.ArrayTypeCode;
import static org.jikesrvm.classloader.ClassLoaderConstants.BooleanTypeCode;
import static org.jikesrvm.classloader.ClassLoaderConstants.ByteTypeCode;
import static org.jikesrvm.classloader.ClassLoaderConstants.CharTypeCode;
import static org.jikesrvm.classloader.ClassLoaderConstants.ClassTypeCode;
import static org.jikesrvm.classloader.ClassLoaderConstants.DoubleTypeCode;
import static org.jikesrvm.classloader.ClassLoaderConstants.FloatTypeCode;
import static org.jikesrvm.classloader.ClassLoaderConstants.IntTypeCode;
import static org.jikesrvm.classloader.ClassLoaderConstants.LongTypeCode;
import static org.jikesrvm.classloader.ClassLoaderConstants.ShortTypeCode;
import static org.jikesrvm.compilers.opt.runtimesupport.OptGCMap.FIRST_GCMAP_REG;
import static org.jikesrvm.osr.OSRConstants.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import org.jikesrvm.VM;
import org.jikesrvm.compilers.opt.inlining.CallSiteTree;
import org.jikesrvm.compilers.opt.ir.Instruction;
import org.jikesrvm.compilers.opt.mir2mc.MachineCodeOffsets;
import org.vmmagic.pragma.Inline;
import org.vmmagic.unboxed.Offset;
/**
* EncodedOSRMap provides the similar function as GC map
* in OptMachineCodeMap.
* <p>
* In OptCompiledMethod, an instance of this class will represent
* all OSR map info for that method.
*/
public final class EncodedOSRMap {
/** osr info entries */
private final long[] mapEntries;
/** the last entry index. */
private final int lastEntry;
/** the OSR map */
private final int[] osrMaps;
/** map used when there are no OSR instructions */
private static final EncodedOSRMap emptyMap = new EncodedOSRMap();
@Inline
public static boolean registerIsSet(int map, int regnum) {
int bitpos = getRegBitPosition(regnum);
return (map & (NEXT_BIT >>> bitpos)) > 0;
}
/**
* Marks a register as a reference type.
*
* @param map the map
* @param regnum the register's number
* @return the updated map
*/
private static int setRegister(int map, int regnum) {
int bitpos = getRegBitPosition(regnum);
map |= (NEXT_BIT >>> bitpos);
return map;
}
@Inline
private static int getRegBitPosition(int regnum) {
return regnum - FIRST_GCMAP_REG + 1;
}
/** Constructor to build empty map */
private EncodedOSRMap() {
this.mapEntries = null;
this.osrMaps = null;
this.lastEntry = -1;
}
/**
* @param varMap the variable map to use for building
* the EncodedOSRMap
* @param mcOffsets the machine code offsets for the
* instructions
*/
private EncodedOSRMap(VariableMap varMap, MachineCodeOffsets mcOffsets) {
int entries = varMap.getNumberOfElements();
this.lastEntry = entries - 1;
if (VM.VerifyAssertions) VM._assert(entries > 0);
this.mapEntries = new long[entries];
ArrayList<Integer> tempOsrMaps = new ArrayList<Integer>();
translateMap(tempOsrMaps, varMap.list, mcOffsets);
this.osrMaps = new int[tempOsrMaps.size()];
for (int i = 0; i < tempOsrMaps.size(); i++) {
this.osrMaps[i] = tempOsrMaps.get(i);
}
//if (VM.TraceOnStackReplacement) {
// printMap();
//}
}
/**
* Encodes the given variable map as OSRMap.
*
* @param varMap the variable map to encode
* @param mcOffsets machine code offsets for the instructions
* @return the canonical empty map if the map
* is empty, an encoded osr map otherwise
*/
public static EncodedOSRMap makeMap(VariableMap varMap, MachineCodeOffsets mcOffsets) {
if (varMap.getNumberOfElements() > 0) {
return new EncodedOSRMap(varMap, mcOffsets);
} else {
return emptyMap;
}
}
/**
* Translates a list of OSR_MapElement to encoding.
* <p>
* we can not trust the osrlist is in the increasing order of
* machine code offset. Sort it first.
*
* @param tempOsrMaps an empty list that will hold temporary
* OSR map information
* @param osrlist information about instructions and variables
* @param mcOffsets machine code offsets for the
* instructions
*/
private void translateMap(ArrayList<Integer> tempOsrMaps,
LinkedList<VariableMapElement> osrlist, final MachineCodeOffsets mcOffsets) {
/* sort the list, use the mc offset of the index instruction
* as the key.
*/
int n = osrlist.size();
VariableMapElement[] osrarray = new VariableMapElement[n];
for (int i = 0; i < n; i++) {
osrarray[i] = osrlist.get(i);
}
/* ideally, the osrList should be in sorted order by MC offset,
* but I got once it is not in the order. To work correctly,
* sort it first.
*
* TODO: figure out why LiveAnalysis does not give correct order?
*/
if (n > 1) {
Arrays.sort(osrarray,
new Comparator<VariableMapElement>() {
@Override
public int compare(VariableMapElement a, VariableMapElement b) {
return mcOffsets.getMachineCodeOffset(a.osr) -
mcOffsets.getMachineCodeOffset(b.osr);
}
});
}
CallSiteTree inliningTree = new CallSiteTree();
for (int i = 0; i < n; i++) {
Instruction instr = osrarray[i].osr;
// add lining element, move sanity later
if (instr.position() != null) {
inliningTree.addLocation(instr.position());
}
}
for (int i = 0; i < n; i++) {
VariableMapElement elm = osrarray[i];
Instruction instr = elm.osr;
int iei = inliningTree.find(instr.position()).encodedOffset;
setIEIndex(i, iei);
// get osr map
LinkedList<MethodVariables> mVarList = elm.mvars;
int osrMapIndex = generateOsrMaps(tempOsrMaps, mVarList);
// use this offset, and adjust on extractState
int mcOffset = mcOffsets.getMachineCodeOffset(instr);
setMCOffset(i, mcOffset);
setOSRMapIndex(i, osrMapIndex);
setBCIndex(i, instr.getBytecodeIndex());
}
}
/**
* Generate value in the Osr map,
* return the index of the first integer in the map.
* <p>
* An OSR Map has following structure:
* <pre>
* | regmap || mid, mpc, (n1, n2) ... ||
* || mid, mpc, (n1, n2) ... ||
* </pre>
* Regmap indicates the value of which register is a reference,
* the execution state extractor can convert the value to an
* object to avoid confusing GC.
* The MSB of regmap indicates next mid is valid.
* <p>
* The MSB of mid indicates if the next mid item will be
* available.
* <p>
* The MSB of mpc indicates if the next is a valid pair
*
* @param tempOsrMaps temporary OSR map information. This method will
* fill this data structure.
* @param mVarList information about variables
* @return the index of the first integer in the map
*/
private int generateOsrMaps(ArrayList<Integer> tempOsrMaps, LinkedList<MethodVariables> mVarList) {
int regmap = (!mVarList.isEmpty()) ? NEXT_BIT : 0;
tempOsrMaps.add(regmap);
int mapIndex = tempOsrMaps.size() - 1;
// from inner to outer
for (int i = 0, m = mVarList.size(); i < m; i++) {
MethodVariables mVar = mVarList.get(i);
_generateMapForOneMethodVariable(tempOsrMaps, mapIndex, mVar, (i == (m - 1)));
}
return mapIndex;
}
/**
* Generate value in the Osr map
* @param tempOsrMaps the maps under construction
* @param regMapIndex used to patch the register map
* @param mVar the method variables
* @param lastMid whether this is the last method in the inlined chain
*/
private void _generateMapForOneMethodVariable(ArrayList<Integer> tempOsrMaps, int regMapIndex, MethodVariables mVar, boolean lastMid) {
// Is this the last method in the inlined chain?
int mid = lastMid ? mVar.methId : (mVar.methId | NEXT_BIT);
tempOsrMaps.add(mid);
LinkedList<LocalRegPair> tupleList = mVar.tupleList;
int m = tupleList.size();
// Is this method has variables?
int bci = (m == 0) ? mVar.bcIndex : (mVar.bcIndex | NEXT_BIT);
tempOsrMaps.add(bci);
// append each element
for (int j = 0; j < m; j++) {
LocalRegPair tuple = tupleList.get(j);
boolean isLast = (j == m - 1);
processTuple(tempOsrMaps, tuple, isLast);
// mark the reg ref map
if (((tuple.typeCode == ClassTypeCode) || (tuple.typeCode == ArrayTypeCode)) && (tuple.valueType == PHYREG)) {
tempOsrMaps.set(regMapIndex, setRegister(tempOsrMaps.get(regMapIndex), tuple.value.toInt()));
}
}
}
/**
* Process a 32-bit tuple.
* @param tempOsrMaps the temporary osr maps
* @param tuple mapping of the local to register
* @param isLast whether to set {@link OSRConstants#NEXT_BIT}
*/
private void processTuple(ArrayList<Integer> tempOsrMaps, LocalRegPair tuple, boolean isLast) {
int first = (tuple.num << NUM_SHIFT) & NUM_MASK;
if (!isLast) {
first |= NEXT_BIT;
}
first |= (tuple.kind ? 1 : 0) << KIND_SHIFT;
first |= (tuple.valueType << VTYPE_SHIFT);
switch (tuple.typeCode) {
case BooleanTypeCode:
case ByteTypeCode:
case CharTypeCode:
case ShortTypeCode:
case IntTypeCode:
first |= (INT << TCODE_SHIFT);
break;
case FloatTypeCode:
first |= (FLOAT << TCODE_SHIFT);
break;
case DoubleTypeCode:
first |= (DOUBLE << TCODE_SHIFT);
break;
case LongTypeCode:
if (VM.BuildFor32Addr || (tuple.valueType == LCONST)) {
// split in two integer parts for OSR map
// process the first half part,
// it is not the last.
first |= NEXT_BIT;
first |= (HIGH_64BIT << TCODE_SHIFT);
// add first word
tempOsrMaps.add(first);
// add the second word
if (VM.BuildFor64Addr) {
tempOsrMaps.add(tuple.value.rshl(32).toInt());
} else {
tempOsrMaps.add(tuple.value.toInt());
tuple = tuple._otherHalf;
}
// process the second half part,
// it may be the last, and it is not the first half.
first = (tuple.num << NUM_SHIFT) & NUM_MASK;
if (!isLast) first |= NEXT_BIT;
first |= (tuple.kind ? 1 : 0) << KIND_SHIFT;
first |= (tuple.valueType << VTYPE_SHIFT);
}
first |= (LONG << TCODE_SHIFT);
break;
case ReturnAddressTypeCode:
if (false) {
VM.sysWrite("returnaddress type for ");
if (tuple.kind == LOCAL) {
VM.sysWrite("L" + tuple.num);
} else {
VM.sysWrite("S" + tuple.num);
}
VM.sysWriteln();
}
first |= (RET_ADDR << TCODE_SHIFT);
break;
case WordTypeCode:
if (VM.BuildFor64Addr && (tuple.valueType == ICONST)) { //KV:TODO
//split in two integer parts for OSR map
// process the first half part,
// it is not the last. */
first |= NEXT_BIT;
first |= (HIGH_64BIT << TCODE_SHIFT);
// add first word
tempOsrMaps.add(first);
// add the second word
tempOsrMaps.add(tuple.value.rshl(32).toInt());
// process the second half part,
// it may be the last, and it is not the first half.
first = (tuple.num << NUM_SHIFT) & NUM_MASK;
if (!isLast) first |= NEXT_BIT;
first |= (tuple.kind ? 1 : 0) << KIND_SHIFT;
first |= (tuple.valueType << VTYPE_SHIFT);
}
first |= (WORD << TCODE_SHIFT);
break;
case ClassTypeCode:
case ArrayTypeCode:
first |= (REF << TCODE_SHIFT);
break;
}
// add first word
tempOsrMaps.add(first);
// add the second word
tempOsrMaps.add(tuple.value.toInt());
}
////////////////////////////////////
// INTERFACE
///////////////////////////////////
/**
* @param mcOffset the machine instruction offset
* @return whether there's an OSR map exist for
* the machine instruction offset
*/
public boolean hasOSRMap(Offset mcOffset) {
int entry = findOSREntry(mcOffset);
return (entry != NO_OSR_ENTRY);
}
/**
* Get bytecode index for a given instruction offset in bytes.
* <p>
* NOTE: It is the caller's reponsibility to make sure there are OSR
* entry exist for a machine instruction offset.
*
* @param mcOffset the instruction offset in bytes
* @return the bytecode index
*/
public int getBytecodeIndexForMCOffset(Offset mcOffset) {
int entry = findOSREntry(mcOffset);
return getBCIndex(entry);
}
/* TODO!
* get inline encoding index for the machine instruction offset
*/
public int getInlineEncodingForMCOffset(Offset mcOffset) {
return -1;
}
/**
* Gets register's reference map for the machine instruction offset
*
* @param mcOffset the instruction offset in bytes
* @return the desired OSR map
*/
public int getRegisterMapForMCOffset(Offset mcOffset) {
int entry = findOSREntry(mcOffset);
int mapIndex = getOSRMapIndex(entry);
return osrMaps[mapIndex];
}
/**
* given a MC offset, return an iterator over the
* elements of this map.
* <p>
* NOTE: the map index is gotten from 'findOSRMapIndex'.
* This has to be changed....
*
* @param mcOffset the instruction offset in bytes
* @return an iterator
*/
public OSRMapIterator getOsrMapIteratorForMCOffset(Offset mcOffset) {
int entry = findOSREntry(mcOffset);
int mapIndex = getOSRMapIndex(entry);
return new OSRMapIterator(osrMaps, mapIndex);
}
/////////////////////////////////
// private functions
////////////////////////////////
/**
* Do a binary search, find the entry for the machine code offset.
*
* @param mcOffset the instruction offset in bytes
* @return {@link OSRConstants#NO_OSR_ENTRY} if no entry was found, the
* entry otherwise
*/
private int findOSREntry(Offset mcOffset) {
int l = 0;
int r = lastEntry;
while (l <= r) {
int m = (l + r) >> 1;
Offset offset = Offset.fromIntSignExtend(getMCOffset(m));
if (offset.EQ(mcOffset)) {
return m;
} else if (offset.sLT(mcOffset)) {
l = m + 1;
} else {
r = m - 1;
}
}
/* this is the place should not be reached, dump OSR content */
if (VM.TraceOnStackReplacement) {
VM.sysWriteln("cannot find map entry for ", mcOffset);
this.printMap();
}
if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
return NO_OSR_ENTRY;
}
private int getMCOffset(int entry) {
return (int) ((mapEntries[entry] & OFFSET_MASK) >>> OFFSET_SHIFT);
}
private int getOSRMapIndex(int entry) {
return (int) ((mapEntries[entry] & OSRI_MASK) >>> OSRI_SHIFT);
}
private int getBCIndex(int entry) {
return (int) ((mapEntries[entry] & BCI_MASK) >>> BCI_SHIFT);
}
@SuppressWarnings("unused")
// Here for completeness (RJG ??)
private int getIEIndex(int entry) {
return (int) ((mapEntries[entry] & IEI_MASK) >>> IEI_SHIFT);
}
private void setMCOffset(int entry, int offset) {
mapEntries[entry] = (mapEntries[entry] & ~OFFSET_MASK) | (((long) offset) << OFFSET_SHIFT);
}
private void setOSRMapIndex(int entry, int index) {
mapEntries[entry] = (mapEntries[entry] & ~OSRI_MASK) | (((long) index) << OSRI_SHIFT);
}
private void setBCIndex(int entry, int index) {
mapEntries[entry] = (mapEntries[entry] & ~BCI_MASK) | (((long) index) << BCI_SHIFT);
}
private void setIEIndex(int entry, int index) {
mapEntries[entry] = (mapEntries[entry] & ~IEI_MASK) | (((long) index) << IEI_SHIFT);
}
/**
* print the encoded map for debugging.
*/
public void printMap() {
if (lastEntry > 0) {
VM.sysWriteln("On-stack-replacement maps:");
}
for (int i = 0; i <= lastEntry; i++) {
VM.sysWrite("Entry " + i + " : ");
int mapIndex = getOSRMapIndex(i);
VM.sysWrite(" mapIndex " + mapIndex + ", ");
int mcOffset = getMCOffset(i);
VM.sysWrite(" mc " + mcOffset + ", ");
int bcIndex = getBCIndex(i);
VM.sysWriteln("bc " + bcIndex);
/*
for (int j=0; j<osrMaps.length; j++) {
VM.sysWriteHex(osrMaps[j]);VM.sysWrite(" ");
}
VM.sysWriteln();
*/
// register map
int regmap = osrMaps[mapIndex] & ~NEXT_BIT;
VM.sysWrite("regmap: " + Integer.toBinaryString(regmap));
OSRMapIterator iterator = new OSRMapIterator(osrMaps, mapIndex);
while (iterator.hasMore()) {
VM.sysWrite("(" + iterator.getValueType() + "," + iterator.getValue() + ")");
iterator.moveToNext();
}
VM.sysWriteln();
}
}
public int[] getMCIndexes() {
int[] indexes = new int[mapEntries.length];
for (int i = 0, n = mapEntries.length; i < n; i++) {
indexes[i] = getMCOffset(i);
}
return indexes;
}
}