/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * 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/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.osr; import java.util.LinkedList; import org.jikesrvm.ArchitectureSpecific.VM_OptGCMapIteratorConstants; import org.jikesrvm.VM; import org.jikesrvm.compilers.opt.ir.OPT_CallSiteTree; import org.jikesrvm.compilers.opt.ir.OPT_Instruction; import org.vmmagic.pragma.Inline; import org.vmmagic.unboxed.Offset; /** * OSR_EncodedOSRMap provides the samilar function as GC map * in VM_OptMachineCodeMap. * * In VM_OptCompiledMethod, an instance of this class will represent * all OSR map info for that method. */ public class OSR_EncodedOSRMap implements VM_OptGCMapIteratorConstants, OSR_Constants { /* osr info entries */ private long[] mapEntries; /* the last entry index. */ private int lastEntry; /* the OSR map */ private int[] osrMaps; private int mapSize = 16; private int lastIndex = 0; private static final boolean DEBUG = false; @Inline public static boolean registerIsSet(int map, int regnum) { int bitpos = getRegBitPosition(regnum); return (map & (NEXT_BIT >>> bitpos)) > 0; } /* * mark a register as reference type */ private static int setRegister(int map, int regnum) { int bitpos = getRegBitPosition(regnum); map |= (NEXT_BIT >>> bitpos); return map; } /* * get register bit position */ @Inline private static int getRegBitPosition(int regnum) { return regnum - FIRST_GCMAP_REG + 1; } /* */ public OSR_EncodedOSRMap(OSR_VariableMap varMap) { int entries = varMap.getNumberOfElements(); this.lastEntry = entries - 1; if (entries > 0) { this.mapEntries = new long[entries]; this.osrMaps = new int[mapSize]; translateMap(varMap.list); resizeOsrMaps(); } /* if (VM.TraceOnStackReplacement) { printMap(); } */ } /* * translates a list of OSR_MapElement to encoding, * we can not trust the osrlist is in the increasing order of * machine code offset. Sort it first. */ private void translateMap(LinkedList<OSR_VariableMapElement> osrlist) { /* sort the list, use the mc offset of the index instruction * as the key. */ int n = osrlist.size(); OSR_VariableMapElement[] osrarray = new OSR_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? */ quickSort(osrarray, 0, n - 1); // make inline encoding, OSR maps, OPT_CallSiteTree inliningTree = new OPT_CallSiteTree(); for (int i = 0; i < n; i++) { OPT_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++) { OSR_VariableMapElement elm = osrarray[i]; OPT_Instruction instr = elm.osr; int iei = inliningTree.find(instr.position).encodedOffset; setIEIndex(i, iei); // get osr map LinkedList<OSR_MethodVariables> mVarList = elm.mvars; int osrMapIndex = generateOsrMaps(mVarList); // use this offset, and adjust on extractState int mcOffset = instr.getmcOffset(); setMCOffset(i, mcOffset); setOSRMapIndex(i, osrMapIndex); setBCIndex(i, instr.getBytecodeIndex()); } } // use the mc offset as key, correctly we should use the next // instruction's mc offset as the key, but since there are // in the order, we will use the current instruction's mc offset // as the key. private static void quickSort(OSR_VariableMapElement[] array, int start, int end) { if (start < end) { int pivot = partition(array, start, end); quickSort(array, start, pivot); quickSort(array, pivot + 1, end); } } private static int partition(OSR_VariableMapElement[] array, int start, int end) { int left = start; int right = end; int pivot = start; OSR_VariableMapElement pivot_elm = array[pivot]; int pivot_offset = pivot_elm.osr.getmcOffset(); while (true) { /* Move right while item > pivot */ while (array[right].osr.getmcOffset() > pivot_offset) right--; /* Move left while item < pivot */ while (array[left].osr.getmcOffset() < pivot_offset) left++; if (left < right) { /* swap left and right */ OSR_VariableMapElement temp = array[left]; array[left] = array[right]; array[right] = temp; } else { return right; } } } /* generate value in the Osr map, * return the index of the first integer in the map. * * An OSR Map has following structure: * | regmap || mid, mpc, (n1, n2) ... || * || mid, mpc, (n1, n2) ... || * 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. * * The MSB of mid indicates if the next mid item will be * available. * * The MSB of mpc indicates if the next is a valid pair */ private int generateOsrMaps(LinkedList<OSR_MethodVariables> mVarList) { int regmap = (!mVarList.isEmpty()) ? NEXT_BIT : 0; int mapIndex = addIntToOsrMap(regmap); // from inner to outer for (int i = 0, m = mVarList.size(); i < m; i++) { OSR_MethodVariables mVar = mVarList.get(i); _generateMapForOneMethodVariable(mapIndex, mVar, (i == (m - 1))); } return mapIndex; } /* @param regMapIndex, used to patch the register map * @param mVar, the method variables */ private void _generateMapForOneMethodVariable(int regMapIndex, OSR_MethodVariables mVar, boolean lastMid) { // Is this the last method in the inlined chain? int mid = lastMid ? mVar.methId : (mVar.methId | NEXT_BIT); addIntToOsrMap(mid); LinkedList<OSR_LocalRegPair> tupleList = mVar.tupleList; int m = tupleList.size(); // Is this method has variables? int bci = (m == 0) ? mVar.bcIndex : (mVar.bcIndex | NEXT_BIT); addIntToOsrMap(bci); // append each element for (int j = 0; j < m; j++) { OSR_LocalRegPair tuple = tupleList.get(j); boolean isLast = (j == m - 1); processTuple(tuple, isLast); // mark the reg ref map if (((tuple.typeCode == ClassTypeCode) || (tuple.typeCode == ArrayTypeCode)) && (tuple.valueType == PHYREG)) { osrMaps[regMapIndex] = setRegister(osrMaps[regMapIndex], tuple.value.toInt()); } } } /* * process on 32-bit tuple. * * tuple, maps the local to register, spill * isLast, indicates to set NEXT_BIT */ private void processTuple(OSR_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 addIntToOsrMap(first); // add the second word if (VM.BuildFor64Addr) { addIntToOsrMap(tuple.value.rshl(32).toInt()); } else { addIntToOsrMap(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 (DEBUG) { VM.sysWrite("returnaddress type for "); if (tuple.kind == LOCAL) { VM.sysWrite("L" + tuple.num); } else { VM.sysWrite("S" + tuple.num); } VM.sysWrite("\n"); } 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 addIntToOsrMap(first); // add the second word addIntToOsrMap(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 addIntToOsrMap(first); // add the second word addIntToOsrMap(tuple.value.toInt()); } /* add an int to osrMaps, expand the array if necessary. * return the index of the word. */ private int addIntToOsrMap(int value) { if (lastIndex >= mapSize) { // double the size int oldSize = mapSize; mapSize <<= 1; int[] oldMaps = osrMaps; osrMaps = new int[mapSize]; System.arraycopy(oldMaps, 0, osrMaps, 0, oldSize); } osrMaps[lastIndex++] = value; return lastIndex - 1; } private void resizeOsrMaps() { if (VM.VerifyAssertions) VM._assert(mapSize == osrMaps.length); if (lastIndex < mapSize - 1) { int[] newMaps = new int[lastIndex]; System.arraycopy(osrMaps, 0, newMaps, 0, lastIndex); osrMaps = newMaps; mapSize = lastIndex; } } //////////////////////////////////// // INTERFACE /////////////////////////////////// /* * does the OSR map exist for a machine instruction offset */ public final boolean hasOSRMap(Offset mcOffset) { int entry = findOSREntry(mcOffset); return (entry != NO_OSR_ENTRY); } /* WARNING: * It is the caller's reposibility to make sure there are OSR * entry exist for a machine instruction offset. */ /* * get bytecode index for a given instruction offset in bytes. */ public final int getBytecodeIndexForMCOffset(Offset mcOffset) { int entry = findOSREntry(mcOffset); return getBCIndex(entry); } /* TODO! * get inline encoding index for the machine instruction offset */ public final int getInlineEncodingForMCOffset(Offset mcOffset) { return -1; } /* * get register's reference map for the machine instruction offset */ public final 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. * NOTE: the map index is gotten from 'findOSRMapIndex'. * This has to be changed.... */ public final OSR_MapIterator getOsrMapIteratorForMCOffset(Offset mcOffset) { int entry = findOSREntry(mcOffset); int mapIndex = getOSRMapIndex(entry); return new OSR_MapIterator(osrMaps, mapIndex); } ///////////////////////////////// // private functions //////////////////////////////// /* * Do a binary search, find the entry for the machine code offset. * Return -1 if no entry was found. */ 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.sysWrite("cannot find map entry for ", mcOffset, "\n"); 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.sysWrite("On-stack-replacement maps:\n"); } 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.sysWrite("\n"); */ // register map int regmap = osrMaps[mapIndex] & ~NEXT_BIT; VM.sysWrite("regmap: " + Integer.toBinaryString(regmap)); OSR_MapIterator iterator = new OSR_MapIterator(osrMaps, mapIndex); while (iterator.hasMore()) { VM.sysWrite("(" + iterator.getValueType() + "," + iterator.getValue() + ")"); iterator.moveToNext(); } VM.sysWrite("\n"); } } 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; } }