/* * 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.compilers.opt.runtimesupport; import static org.jikesrvm.compilers.opt.driver.OptConstants.INSTRUMENTATION_BCI; import static org.jikesrvm.compilers.opt.driver.OptConstants.UNKNOWN_BCI; import static org.jikesrvm.compilers.opt.ir.Operators.IR_PROLOGUE_opcode; import java.util.ArrayList; import org.jikesrvm.VM; import org.jikesrvm.adaptive.database.callgraph.CallSite; import org.jikesrvm.architecture.ArchConstants; import org.jikesrvm.classloader.MemberReference; import org.jikesrvm.classloader.NormalMethod; import org.jikesrvm.classloader.RVMArray; import org.jikesrvm.classloader.RVMMethod; import org.jikesrvm.classloader.TypeReference; import org.jikesrvm.compilers.opt.OptimizingCompilerException; import org.jikesrvm.compilers.opt.inlining.CallSiteTree; import org.jikesrvm.compilers.opt.ir.GCIRMap; import org.jikesrvm.compilers.opt.ir.GCIRMapElement; import org.jikesrvm.compilers.opt.ir.IR; import org.jikesrvm.compilers.opt.ir.Instruction; import org.jikesrvm.compilers.opt.ir.operand.MethodOperand; import org.jikesrvm.compilers.opt.mir2mc.MachineCodeOffsets; import org.vmmagic.pragma.Inline; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.unboxed.Offset; /** * A class that encapsulates mapping information about generated machine code. * Since there will be an instance of this class with every OptCompiledMethod, * we attempt to pack the data into a reasonably small number of bits. * * <p> The supported functions are: * <ul> * <li> (1) Map from a machine code offset to a GC map (register & stack map). * <li> (2) Map from machinecode offset to <method, bcIndex> pair. * Used for: * <ul> * <li> dynamic linking * <li> lazy compilation * <li> adaptive system profiling * </ul> * <li> (3) Map from a machine code offset to a tree of <method, bcIndex> pairs * that encodes the inlining sequence. * Used for: * <ul> * <li> IPA * <li> stack inspection (print stack trace, * security manager, etc). * <li> general debugging support. * <li> adaptive system profiling * </ul> *</ul> *<p> * Note: This file contains two types of methods * <ul> * <li>1) methods called during compilation to create the maps * <li>2) methods called at GC time (no allocation allowed!) * </ul> */ public final class OptMachineCodeMap { private OptMachineCodeMap(int[] _MCInformation, int[] _gcMaps, int[] _inlineEncoding) { MCInformation = _MCInformation; gcMaps = _gcMaps; inlineEncoding = _inlineEncoding; } /** * Creates the map, called during compilation * @param ir the ir object for this method * @param machineCodeSize the number of machine code instructions generated. * @return the created map */ static OptMachineCodeMap create(IR ir, int machineCodeSize) { /** Dump maps as methods are compiled */ final boolean DUMP_MAPS = ir.options.PRINT_GC_MAPS && (!ir.options.hasMETHOD_TO_PRINT() || (ir.options.hasMETHOD_TO_PRINT() && ir.options.fuzzyMatchMETHOD_TO_PRINT(ir.method.toString())) ); /** Dump stats on map size as maps are compiled */ final boolean DUMP_MAP_SIZES = false; if (DUMP_MAPS) { VM.sysWriteln("Creating final machine code map for " + ir.method); } // create all machine code maps MachineCodeOffsets mcOffsets = ir.MIRInfo.mcOffsets; final OptMachineCodeMap map = generateMCInformation(ir.MIRInfo.gcIRMap, DUMP_MAPS, mcOffsets); if (DUMP_MAP_SIZES) { map.recordStats(ir.method, map.size(), machineCodeSize << ArchConstants.getLogInstructionWidth(), DUMP_MAP_SIZES); } if (DUMP_MAPS) { VM.sysWriteln("Final Machine code information:"); map.dumpMCInformation(DUMP_MAPS); for (Instruction i = ir.firstInstructionInCodeOrder(); i != null; i = i.nextInstructionInCodeOrder()) { VM.sysWriteln(mcOffsets.getMachineCodeOffset(i) + "\t" + i); } } return map; } /** * Get the bytecode index for a machine instruction offset. * * @param MCOffset the machine code offset of interest * @return -1 if unknown. */ @Uninterruptible public int getBytecodeIndexForMCOffset(Offset MCOffset) { int entry = findMCEntry(MCOffset); if (entry == -1) { return -1; } return getBytecodeIndex(entry); } /** * Get the RVMMethod for a machine instruction offset. * This method is the source method that the instruction came from. * * @param MCOffset the machine code offset of interest * @return {@code null} if unknown */ @Uninterruptible public NormalMethod getMethodForMCOffset(Offset MCOffset) { int entry = findMCEntry(MCOffset); if (entry == -1) { return null; } int iei = getInlineEncodingIndex(entry); if (iei == -1) { return null; } int mid = OptEncodedCallSiteTree.getMethodID(iei, inlineEncoding); return (NormalMethod) MemberReference.getMethodRef(mid).getResolvedMember(); } /** * Return the inlining encoding index for the machine instruction offset. * * @param MCOffset the machine code offset of interest * @return -1 if unknown. */ @Uninterruptible public int getInlineEncodingForMCOffset(Offset MCOffset) { int entry = findMCEntry(MCOffset); if (entry == -1) { return -1; } return getInlineEncodingIndex(entry); } /** * This method searches for the GC map corresponding to the * passed machine code offset. * If no map is present, an error has occurred and OptGCMap.ERROR * is returned. * * @param MCOffset the machine code offset to look for * @return the GC map index or OptGCMap.ERROR */ @Uninterruptible public int findGCMapIndex(Offset MCOffset) { int entry = findMCEntry(MCOffset); if (entry == -1) return OptGCMap.ERROR; return getGCMapIndex(entry); } /** * @return an arraylist of CallSite objects representing all non-inlined * callsites in the method. Returns null if there are no such callsites. */ public ArrayList<CallSite> getNonInlinedCallSites() { ArrayList<CallSite> ans = null; if (MCInformation == null) return ans; for (int entry = 0; entry < MCInformation.length;) { int callInfo = getCallInfo(entry); if (callInfo == IS_UNGUARDED_CALL) { int bcIndex = getBytecodeIndex(entry); if (bcIndex != -1) { int iei = getInlineEncodingIndex(entry); if (iei != -1) { int mid = OptEncodedCallSiteTree.getMethodID(iei, inlineEncoding); RVMMethod caller = MemberReference.getMemberRef(mid).asMethodReference().peekResolvedMethod(); if (caller != null) { if (ans == null) ans = new ArrayList<CallSite>(); ans.add(new CallSite(caller, bcIndex)); } } } } entry = nextEntry(entry); } return ans; } /** * This method searches the machine code maps and determines if * the given call edge is definitely inlined into the method. * NOTE: This current implementation may return false even if the * edge actually was inlined. This happens when no GC point occurs within * the inlined body. This is less than ideal; we need to fix this at some point. * @param caller caller RVMMethod * @param bcIndex bytecode index of the caller method * @param callee callee RVMMethod * @return {@code true} if the call edge is <em>definitely</em> inlined in this compiled method. */ public boolean hasInlinedEdge(RVMMethod caller, int bcIndex, RVMMethod callee) { if (MCInformation == null) return false; if (inlineEncoding == null) return false; return OptEncodedCallSiteTree.edgePresent(caller.getId(), bcIndex, callee.getId(), inlineEncoding); } @Uninterruptible public int gcMapInformation(int index) { return OptGCMap.gcMapInformation(index, gcMaps); } @Uninterruptible public boolean registerIsSet(int entry, int registerNumber) { return OptGCMap.registerIsSet(entry, registerNumber, gcMaps); } /** * @param currentIndex index for current location * @return the next (relative) location or -1 for no more locations */ @Uninterruptible public int nextLocation(int currentIndex) { return OptGCMap.nextLocation(currentIndex, gcMaps); } /////////////////////////////////////// // Implementation /////////////////////////////////////// /** * Does a binary search of the machine code maps to find the index * in MCInformation where the entry for the argument machine code * offset starts. Will return -1 if the entry doesn't exist. * * @param MCOffset the machine code offset of interest * @return -1 if no entry exists, the index of the matching entry otherwise */ @Uninterruptible private int findMCEntry(Offset MCOffset) { // Given a machine code instruction MCOffset, find the corresponding entry if (MCInformation == null) return -1; if (MCInformation.length == 0) return -1; int left = 0; int right = MCInformation.length - 1; while (left <= right) { int middle = (left + right) >> 1; // take the average while ((MCInformation[middle] & START_OF_ENTRY) != START_OF_ENTRY) { // if necessary, step backwards to beginning of entry. middle--; } Offset offset = Offset.fromIntSignExtend(getMCOffset(middle)); if (MCOffset.EQ(offset)) { return middle; } else if (MCOffset.sGT(offset)) { // middle is too small, shift interval to the right left = middle + 1; if (left >= MCInformation.length) return -1; while ((MCInformation[left] & START_OF_ENTRY) != START_OF_ENTRY) { // if necessary, step forward to find next entry, but not passed end // Need to do this to avoid finding middle again left++; if (left >= MCInformation.length) { return -1; } } } else { // middle is too small, shift interval to the left right = middle - 1; // Note no need to adjust as, we won't chance finding middle again } } return -1; } private int nextEntry(int entry) { if (isBigEntry(entry)) return entry + SIZEOF_BIG_ENTRY; if (isHugeEntry(entry)) return entry + SIZEOF_HUGE_ENTRY; return entry + SIZEOF_ENTRY; } //////////////////////////////////////////// // Create the map (at compile time) //////////////////////////////////////////// /** * This method walks the IR map, and for each entry it creates * the machine code mapping information for the entry. * It is called during the compilation of the method, not at GC time. * @param irMap the irmap to translate from * @param DUMP_MAPS dump while we work * @param mcOffsets machine code offset information * @return the machine code map */ private static OptMachineCodeMap generateMCInformation(GCIRMap irMap, boolean DUMP_MAPS, MachineCodeOffsets mcOffsets) { CallSiteTree inliningMap = new CallSiteTree(); int numEntries = 0; // (1) Count how many entries we are going to have and // construct and encode the inlining information for those entries. for (GCIRMapElement irMapElem : irMap) { numEntries++; Instruction instr = irMapElem.getInstruction(); if (instr.position() == null && instr.getBytecodeIndex() != INSTRUMENTATION_BCI) { if ((VM.BuildForIA32 && org.jikesrvm.compilers.opt.ir.ia32.MIR_Call.conforms(instr) && org.jikesrvm.compilers.opt.ir.ia32.MIR_Call.hasMethod(instr)) || (VM.BuildForPowerPC && org.jikesrvm.compilers.opt.ir.ppc.MIR_Call.conforms(instr) && org.jikesrvm.compilers.opt.ir.ppc.MIR_Call.hasMethod(instr))) { throw new OptimizingCompilerException("position required for all call instructions " + instr); } } else { inliningMap.addLocation(instr.position()); } } if (numEntries == 0) return emptyMachineCodeMap; // if no entries, then we are done. int[] inlineEncoding = OptEncodedCallSiteTree.getEncoding(inliningMap); // (2) Encode the primary machine code mapping information and the GCMaps. OptGCMap gcMapBuilder = new OptGCMap(); int[] tmpMC = new int[numEntries * SIZEOF_HUGE_ENTRY]; int lastMCInfoEntry = 0; for (GCIRMapElement irMapElem : irMap) { Instruction instr = irMapElem.getInstruction(); if (DUMP_MAPS) VM.sysWrite("IR Map for " + instr + "\n\t" + irMapElem); // retrieve the machine code offset (in bytes) from the instruction, ensureCorrectMapConstruction(mcOffsets, instr); int mco = mcOffsets.getMachineCodeOffset(instr); if (mco < 0) { VM.sysWrite("Negative machine code MCOffset found:" + mco); Instruction i = irMapElem.getInstruction(); int machineCodeOffsetForI = mcOffsets.getMachineCodeOffset(i); VM.sysWriteln(i.getBytecodeIndex() + ", " + i + ", " + machineCodeOffsetForI); throw new OptimizingCompilerException("Negative machine code MCOffset found"); } // create GC map and get GCI int gci = gcMapBuilder.generateGCMapEntry(irMapElem); // get bci information int bci = instr.getBytecodeIndex(); if (bci < 0) { if ((bci == UNKNOWN_BCI) && ((VM.BuildForIA32 && org.jikesrvm.compilers.opt.ir.ia32.MIR_Call.conforms(instr) && org.jikesrvm.compilers.opt.ir.ia32.MIR_Call.hasMethod(instr)) || (VM.BuildForPowerPC && org.jikesrvm.compilers.opt.ir.ppc.MIR_Call.conforms(instr) && org.jikesrvm.compilers.opt.ir.ppc.MIR_Call.hasMethod(instr)))) { throw new OptimizingCompilerException("valid bytecode index required for all calls " + instr); } bci = -1; } // get index into inline encoding int iei = -1; if (instr.position() != null) { iei = inliningMap.find(instr.position()).encodedOffset; } // set the call info int cm = 0; if ((VM.BuildForIA32 && org.jikesrvm.compilers.opt.ir.ia32.MIR_Call.conforms(instr)) || (VM.BuildForPowerPC && org.jikesrvm.compilers.opt.ir.ppc.MIR_Call.conforms(instr))) { MethodOperand mo; if (VM.BuildForIA32) { mo = org.jikesrvm.compilers.opt.ir.ia32.MIR_Call.getMethod(instr); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); mo = org.jikesrvm.compilers.opt.ir.ppc.MIR_Call.getMethod(instr); } if (mo != null && mo.isGuardedInlineOffBranch()) { cm = IS_GUARDED_CALL; } else { cm = IS_UNGUARDED_CALL; } } // Encode this entry into MCInformation if (bci < INVALID_BCI && iei < INVALID_IEI && gci < INVALID_GCI && mco < (OFFSET_MASK >>> OFFSET_SHIFT)) { // use a small entry if (bci == -1) bci = INVALID_BCI; if (iei == -1) iei = INVALID_IEI; if (gci == -1) gci = INVALID_GCI; if (VM.VerifyAssertions) { VM._assert((cm & (CALL_MASK >>> CALL_SHIFT)) == cm); VM._assert((bci & (BCI_MASK >>> BCI_SHIFT)) == bci); VM._assert((iei & (IEI_MASK >>> IEI_SHIFT)) == iei); VM._assert((gci & (GCI_MASK >>> GCI_SHIFT)) == gci); VM._assert((mco & (OFFSET_MASK >>> OFFSET_SHIFT)) == mco); } int t = START_OF_ENTRY; t |= (cm << CALL_SHIFT); t |= (bci << BCI_SHIFT); t |= (iei << IEI_SHIFT); t |= (gci << GCI_SHIFT); t |= (mco << OFFSET_SHIFT); tmpMC[lastMCInfoEntry++] = t; } else if (bci < BIG_INVALID_BCI && iei < BIG_INVALID_IEI && gci < BIG_INVALID_GCI && mco < (BIG_OFFSET_MASK >>> BIG_OFFSET_SHIFT)) { // use a big entry if (bci == -1) bci = BIG_INVALID_BCI; if (iei == -1) iei = BIG_INVALID_IEI; if (gci == -1) gci = BIG_INVALID_GCI; if (VM.VerifyAssertions) { VM._assert((cm & (BIG_CALL_MASK >>> BIG_CALL_SHIFT)) == cm); VM._assert((bci & (BIG_BCI_MASK >>> BIG_BCI_SHIFT)) == bci); VM._assert((iei & (BIG_IEI_MASK >>> BIG_IEI_SHIFT)) == iei); VM._assert((gci & (BIG_GCI_MASK >>> BIG_GCI_SHIFT)) == gci); VM._assert((mco & (BIG_OFFSET_MASK >>> BIG_OFFSET_SHIFT)) == mco); } int startIdx = lastMCInfoEntry; tmpMC[startIdx] = START_OF_BIG_ENTRY; tmpMC[startIdx + BIG_CALL_IDX_ADJ] |= (cm << BIG_CALL_SHIFT); tmpMC[startIdx + BIG_BCI_IDX_ADJ] |= (bci << BIG_BCI_SHIFT); tmpMC[startIdx + BIG_OFFSET_IDX_ADJ] |= (mco << BIG_OFFSET_SHIFT); tmpMC[startIdx + BIG_GCI_IDX_ADJ] |= (gci << BIG_GCI_SHIFT); tmpMC[startIdx + BIG_IEI_IDX_ADJ] |= (iei << BIG_IEI_SHIFT); lastMCInfoEntry += SIZEOF_BIG_ENTRY; } else { // use a huge entry if (bci == -1) bci = HUGE_INVALID_BCI; if (iei == -1) iei = HUGE_INVALID_IEI; if (gci == -1) gci = HUGE_INVALID_GCI; if (VM.VerifyAssertions) { VM._assert((cm & (HUGE_CALL_MASK >>> HUGE_CALL_SHIFT)) == cm); VM._assert((bci & (HUGE_BCI_MASK >>> HUGE_BCI_SHIFT)) == bci); VM._assert((iei & (HUGE_IEI_MASK >>> HUGE_IEI_SHIFT)) == iei); VM._assert((gci & (HUGE_GCI_MASK >>> HUGE_GCI_SHIFT)) == gci); VM._assert((mco & (HUGE_OFFSET_MASK >>> HUGE_OFFSET_SHIFT)) == mco); } int startIdx = lastMCInfoEntry; tmpMC[startIdx] = START_OF_HUGE_ENTRY; tmpMC[startIdx + HUGE_CALL_IDX_ADJ] |= (cm << HUGE_CALL_SHIFT); tmpMC[startIdx + HUGE_BCI_IDX_ADJ] |= (bci << HUGE_BCI_SHIFT); tmpMC[startIdx + HUGE_OFFSET_IDX_ADJ] |= (mco << HUGE_OFFSET_SHIFT); tmpMC[startIdx + HUGE_GCI_IDX_ADJ] |= (gci << HUGE_GCI_SHIFT); tmpMC[startIdx + HUGE_IEI_IDX_ADJ] |= (iei << HUGE_IEI_SHIFT); lastMCInfoEntry += SIZEOF_HUGE_ENTRY; } } int[] mcInformation = new int[lastMCInfoEntry]; System.arraycopy(tmpMC, 0, mcInformation, 0, mcInformation.length); int[] gcMaps = gcMapBuilder.finish(); return new OptMachineCodeMap(mcInformation, gcMaps, inlineEncoding); } /** * Ensures correct map construction by either correcting oddities or failing * immediately in case of errors. * * @param mcOffsets machine code offset information * @param instr the instruction to be processed */ private static void ensureCorrectMapConstruction( MachineCodeOffsets mcOffsets, Instruction instr) { if (mcOffsets.lacksMachineCodeOffset(instr)) { // In non-interruptible code, we may encounter an IR_PROLOGUE instruction // without a machine code offset. This can happen in the following way: // - GC maps are built. The prologue instruction is present at this stage. // - After register allocation (and after all GC Maps have been updated), // the prologue and epilogue will be created. Because the method is // not interruptible, no stack overflow check will be inserted and the // prologue instruction will be removed. // - Machine code offsets are set by the Assembler. The instruction does not // get an offset because it is no longer present in the IR. // - The machine code maps are created from the GC maps via this class // which runs into the instruction with the machine code offset. // This is merely an oddity and not a problem because runtime services // will not query the machine code maps for non-interruptible code. // Therefore, it is justified to add a special case for this. if (instr.getOpcode() == IR_PROLOGUE_opcode) { mcOffsets.fabricateMachineCodeOffsetForPrologueInstruction(instr); } else { // Unknown case, most likely an error. throw new OptimizingCompilerException("Found instruction without valid machine code offset during " + "generation of machine code information: " + instr); } } } //////////////////////////////////////////// // Accessors // NB: The accessors take an entry number, which is defined to // be the index of word I of the MCInformation entry //////////////////////////////////////////// /** * Returns the MCOffset for the entry passed * @param entry the index of the start of the entry * @return the MCOffset for this entry */ @Uninterruptible private int getMCOffset(int entry) { if (isBigEntry(entry)) { int t = MCInformation[entry + BIG_OFFSET_IDX_ADJ]; return (t & BIG_OFFSET_MASK) >>> BIG_OFFSET_SHIFT; } else if (isHugeEntry(entry)) { int t = MCInformation[entry + HUGE_OFFSET_IDX_ADJ]; return (t & HUGE_OFFSET_MASK) >>> HUGE_OFFSET_SHIFT; } else { int t = MCInformation[entry]; return (t & OFFSET_MASK) >>> OFFSET_SHIFT; } } /** * Returns the GC map index for the entry passed * @param entry the index of the start of the entry * @return the GC map entry index for this entry (or -1 if none) */ @Uninterruptible private int getGCMapIndex(int entry) { if (isBigEntry(entry)) { int t = MCInformation[entry + BIG_GCI_IDX_ADJ]; int gci = (t & BIG_GCI_MASK) >>> BIG_GCI_SHIFT; if (gci == BIG_INVALID_GCI) return -1; return gci; } else if (isHugeEntry(entry)) { int t = MCInformation[entry + HUGE_GCI_IDX_ADJ]; int gci = (t & HUGE_GCI_MASK) >>> HUGE_GCI_SHIFT; if (gci == HUGE_INVALID_GCI) return -1; return gci; } else { int t = MCInformation[entry]; int gci = (t & GCI_MASK) >>> GCI_SHIFT; if (gci == INVALID_GCI) return -1; return gci; } } /** * Returns the Bytecode index for the entry passed * @param entry the index of the start of the entry * @return the bytecode index for this entry (-1 if unknown) */ @Uninterruptible private int getBytecodeIndex(int entry) { if (isBigEntry(entry)) { int t = MCInformation[entry + BIG_BCI_IDX_ADJ]; int bci = (t & BIG_BCI_MASK) >>> BIG_BCI_SHIFT; if (bci == BIG_INVALID_BCI) return -1; return bci; } else if (isHugeEntry(entry)) { int t = MCInformation[entry + HUGE_BCI_IDX_ADJ]; int bci = (t & HUGE_BCI_MASK) >>> HUGE_BCI_SHIFT; if (bci == HUGE_INVALID_BCI) return -1; return bci; } else { int t = MCInformation[entry]; int bci = (t & BCI_MASK) >>> BCI_SHIFT; if (bci == INVALID_BCI) return -1; return bci; } } /** * Returns the inline encoding index for the entry passed. * @param entry the index of the start of the entry * @return the inline encoding index for this entry (-1 if unknown) */ @Uninterruptible private int getInlineEncodingIndex(int entry) { if (isBigEntry(entry)) { int t = MCInformation[entry + BIG_IEI_IDX_ADJ]; int iei = (t & BIG_IEI_MASK) >>> BIG_IEI_SHIFT; if (iei == BIG_INVALID_IEI) return -1; return iei; } else if (isHugeEntry(entry)) { int t = MCInformation[entry + HUGE_IEI_IDX_ADJ]; int iei = (t & HUGE_IEI_MASK) >>> HUGE_IEI_SHIFT; if (iei == HUGE_INVALID_IEI) return -1; return iei; } else { int t = MCInformation[entry]; int iei = (t & IEI_MASK) >>> IEI_SHIFT; if (iei == INVALID_IEI) return -1; return iei; } } /** * Returns the call info for the entry passed. * @param entry the index of the start of the entry * @return the call info for this entry */ @Uninterruptible private int getCallInfo(int entry) { if (isBigEntry(entry)) { int t = MCInformation[entry + BIG_CALL_IDX_ADJ]; return (t & BIG_CALL_MASK) >>> BIG_CALL_SHIFT; } else if (isHugeEntry(entry)) { int t = MCInformation[entry + HUGE_CALL_IDX_ADJ]; return (t & HUGE_CALL_MASK) >>> HUGE_CALL_SHIFT; } else { int t = MCInformation[entry]; return (t & CALL_MASK) >>> CALL_SHIFT; } } /** * @param entry the entry's index * @return whether the the entry is a big entry */ @Uninterruptible @Inline private boolean isBigEntry(int entry) { if (VM.VerifyAssertions) { VM._assert((MCInformation[entry] & START_OF_ENTRY) == START_OF_ENTRY); } return (MCInformation[entry] & START_BITS) == START_OF_BIG_ENTRY; } /** * @param entry the entry's index * @return whether the the entry is a huge entry */ @Uninterruptible @Inline private boolean isHugeEntry(int entry) { if (VM.VerifyAssertions) { VM._assert((MCInformation[entry] & START_OF_ENTRY) == START_OF_ENTRY); } return (MCInformation[entry] & START_BITS) == START_OF_HUGE_ENTRY; } //////////////////////////////////////////// // Debugging //////////////////////////////////////////// public void dumpMCInformation(boolean DUMP_MAPS) { if (DUMP_MAPS) { VM.sysWriteln(" Dumping the MCInformation"); if (MCInformation == null) return; for (int idx = 0; idx < MCInformation.length;) { printMCInformationEntry(idx, DUMP_MAPS); idx = nextEntry(idx); } } } private void printMCInformationEntry(int entry, boolean DUMP_MAPS) { if (DUMP_MAPS) { String sep = "\tMC: "; if (isBigEntry(entry)) sep = "B\tMC: "; if (isHugeEntry(entry)) sep = "H\tMC: "; int mcOffset = getMCOffset(entry); VM.sysWrite(entry + sep + mcOffset + " (" + Integer.toHexString(mcOffset) + ")"); int bci = getBytecodeIndex(entry); if (bci != -1) { VM.sysWriteln(); VM.sysWrite("\tBCI: " + bci); } int iei = getInlineEncodingIndex(entry); if (iei != -1) { VM.sysWriteln(); VM.sysWrite("\tIEI: " + iei); } boolean first = true; while (iei >= 0) { int mid = OptEncodedCallSiteTree.getMethodID(iei, inlineEncoding); RVMMethod meth = MemberReference.getMethodRef(mid).getResolvedMember(); if (first) { first = false; VM.sysWriteln(); VM.sysWrite("\tIn method " + meth + " at bytecode " + bci); } else { VM.sysWriteln(); VM.sysWrite("\tInlined into " + meth + " at bytecode " + bci); } if (iei > 0) { bci = OptEncodedCallSiteTree.getByteCodeOffset(iei, inlineEncoding); } iei = OptEncodedCallSiteTree.getParent(iei, inlineEncoding); } if (getGCMapIndex(entry) != OptGCMap.NO_MAP_ENTRY) { VM.sysWriteln(); VM.sysWrite("\tGC Map Idx: " + getGCMapIndex(entry) + " "); OptGCMap.dumpMap(getGCMapIndex(entry), gcMaps); } else { VM.sysWriteln(); VM.sysWrite("\tno GC map"); } VM.sysWriteln(); } } private void recordStats(RVMMethod method, int mapSize, int machineCodeSize, boolean DUMP_MAP_SIZES) { if (DUMP_MAP_SIZES) { double mapMCPercent = (double) mapSize / machineCodeSize; VM.sysWrite(method); VM.sysWrite(" map is " + (int) (mapMCPercent * 100) + "% (" + mapSize + "/" + machineCodeSize + ") of MC.\n"); totalMCSize += machineCodeSize; totalMapSize += mapSize; double MCPct = (double) totalMapSize / totalMCSize; VM.sysWriteln(" Cumulative maps are now " + (int) (MCPct * 100) + "% (" + totalMapSize + "/" + totalMCSize + ") of MC."); } } /** * @return total bytes of machine code maps */ int size() { int size = TYPE.peekType().asClass().getInstanceSize(); if (MCInformation != null) size += RVMArray.IntArray.getInstanceSize(MCInformation.length); if (inlineEncoding != null) size += RVMArray.IntArray.getInstanceSize(inlineEncoding.length); if (gcMaps != null) size += RVMArray.IntArray.getInstanceSize(gcMaps.length); return size; } //////////////////////////////////////////// // // Encoding constants and backing data. // //////////////////////////////////////////// // An entry contains the following data: // o: a machine code offset (in bytes) // g: an index into the GC maps array // b: bytecode index of the instruction // i: index into the inline encoding. // c: bits to encode one of three possibilites // (a) the instruction is not a call // (b) the instruction is a "normal" call // (c) the instruction is a call in the off-branch // of a guarded inline. // U indicates an unused bit; its value is undefined. // // We support three entry formats as defined below // private static final int START_OF_ENTRY = 0x80000000; private static final int START_OF_BIG_ENTRY = 0xc0000000; private static final int START_OF_HUGE_ENTRY = 0xe0000000; private static final int START_BITS = 0xe0000000; // A small entry is 1 int used as follows: // 10cc bbbb bbii iiii iggg ggoo oooo oooo private static final int CALL_MASK = 0x30000000; private static final int CALL_SHIFT = 28; private static final int BCI_MASK = 0x0fc00000; private static final int BCI_SHIFT = 22; private static final int IEI_MASK = 0x003f8000; private static final int IEI_SHIFT = 15; private static final int GCI_MASK = 0x00007c00; private static final int GCI_SHIFT = 10; private static final int OFFSET_MASK = 0x000003ff; private static final int OFFSET_SHIFT = 0; private static final int INVALID_GCI = (GCI_MASK >>> GCI_SHIFT); private static final int INVALID_BCI = (BCI_MASK >>> BCI_SHIFT); private static final int INVALID_IEI = (IEI_MASK >>> IEI_SHIFT); private static final int SIZEOF_ENTRY = 1; // A big entry is 2 ints used as follows: // 110c cbbb bbbb bbbb biii iiii iiii iiii // 0ggg gggg gggg ggoo oooo oooo oooo oooo private static final int BIG_CALL_MASK = 0x18000000; private static final int BIG_CALL_SHIFT = 27; private static final int BIG_CALL_IDX_ADJ = 0; private static final int BIG_BCI_MASK = 0x07ff8000; private static final int BIG_BCI_SHIFT = 15; private static final int BIG_BCI_IDX_ADJ = 0; private static final int BIG_IEI_MASK = 0x00007fff; private static final int BIG_IEI_SHIFT = 0; private static final int BIG_IEI_IDX_ADJ = 0; private static final int BIG_GCI_MASK = 0x7ffc0000; private static final int BIG_GCI_SHIFT = 18; private static final int BIG_GCI_IDX_ADJ = 1; private static final int BIG_OFFSET_MASK = 0x0003ffff; private static final int BIG_OFFSET_SHIFT = 0; private static final int BIG_OFFSET_IDX_ADJ = 1; private static final int BIG_INVALID_GCI = (BIG_GCI_MASK >>> BIG_GCI_SHIFT); private static final int BIG_INVALID_BCI = (BIG_BCI_MASK >>> BIG_BCI_SHIFT); private static final int BIG_INVALID_IEI = (BIG_IEI_MASK >>> BIG_IEI_SHIFT); private static final int SIZEOF_BIG_ENTRY = 2; // A huge entry is 4 ints used as follows: // 111c cUUU UUUU UUUU bbbb bbbb bbbb bbbb // 0iii iiii iiii iiii iiii iiii iiii iiii // 0ggg gggg gggg gggg gggg gggg gggg gggg // 0ooo oooo oooo oooo oooo oooo oooo oooo private static final int HUGE_CALL_MASK = 0x18000000; private static final int HUGE_CALL_SHIFT = 27; private static final int HUGE_CALL_IDX_ADJ = 0; private static final int HUGE_BCI_MASK = 0x0000ffff; private static final int HUGE_BCI_SHIFT = 0; private static final int HUGE_BCI_IDX_ADJ = 0; private static final int HUGE_IEI_MASK = 0x7fffffff; private static final int HUGE_IEI_SHIFT = 0; private static final int HUGE_IEI_IDX_ADJ = 1; private static final int HUGE_GCI_MASK = 0x7fffffff; private static final int HUGE_GCI_SHIFT = 0; private static final int HUGE_GCI_IDX_ADJ = 2; private static final int HUGE_OFFSET_MASK = 0x7fffffff; private static final int HUGE_OFFSET_SHIFT = 0; private static final int HUGE_OFFSET_IDX_ADJ = 3; private static final int HUGE_INVALID_GCI = (HUGE_GCI_MASK >>> HUGE_GCI_SHIFT); private static final int HUGE_INVALID_BCI = (HUGE_BCI_MASK >>> HUGE_BCI_SHIFT); private static final int HUGE_INVALID_IEI = (HUGE_IEI_MASK >>> HUGE_IEI_SHIFT); private static final int SIZEOF_HUGE_ENTRY = 4; // bit patterns for cc portion of machine code map */ private static final int IS_UNGUARDED_CALL = 0x1; private static final int IS_GUARDED_CALL = 0x3; /** * Hold entries as defined by the constants above. */ private final int[] MCInformation; /** * array of GC maps as defined by OptGCMap */ private final int[] gcMaps; /** * encoded data as defined by OptEncodedCallSiteTree. */ public final int[] inlineEncoding; /** * Running totals for the size of machine code and maps */ private static int totalMCSize = 0; private static int totalMapSize = 0; /** * A machine code map when no information is present */ private static final OptMachineCodeMap emptyMachineCodeMap = new OptMachineCodeMap(null, null, null); private static final TypeReference TYPE = TypeReference.findOrCreate(OptMachineCodeMap.class); }