/* * 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.compilers.common; import org.jikesrvm.VM; import org.jikesrvm.VM_SizeConstants; import org.jikesrvm.classloader.VM_Array; import org.jikesrvm.classloader.VM_Method; import org.jikesrvm.classloader.VM_Type; import org.jikesrvm.compilers.baseline.VM_BaselineCompiledMethod; import org.jikesrvm.compilers.opt.VM_OptCompiledMethod; import org.jikesrvm.jni.VM_JNICompiledMethod; import org.jikesrvm.memorymanagers.mminterface.MM_Interface; import org.jikesrvm.runtime.VM_Magic; import org.jikesrvm.runtime.VM_Memory; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.unboxed.LocalAddress; /** * Manage pool of compiled methods. <p> * Original extracted from VM_ClassLoader. <p> */ public class VM_CompiledMethods implements VM_SizeConstants { /** * Create a VM_CompiledMethod appropriate for the given compilerType */ public static synchronized VM_CompiledMethod createCompiledMethod(VM_Method m, int compilerType, boolean forSubArch) { int id = currentCompiledMethodId + 1; if (id == compiledMethods.length) { compiledMethods = growArray(compiledMethods, 2 * compiledMethods.length); } currentCompiledMethodId++; VM_CompiledMethod cm = null; if (compilerType == VM_CompiledMethod.BASELINE) { cm = new VM_BaselineCompiledMethod(id, m, forSubArch); } else if (VM.BuildForOptCompiler && compilerType == VM_CompiledMethod.OPT) { cm = new VM_OptCompiledMethod(id, m, forSubArch); } else if (compilerType == VM_CompiledMethod.JNI) { cm = new VM_JNICompiledMethod(id, m, forSubArch); } else { if (VM.VerifyAssertions) VM._assert(false, "Unexpected compiler type!"); } compiledMethods[id] = cm; return cm; } /** * Create a VM_CompiledMethod for the synthetic hardware trap frame */ public static synchronized VM_CompiledMethod createHardwareTrapCompiledMethod(boolean forSubArch) { int id = currentCompiledMethodId + 1; if (id == compiledMethods.length) { compiledMethods = growArray(compiledMethods, 2 * compiledMethods.length); } currentCompiledMethodId++; VM_CompiledMethod cm = new VM_HardwareTrapCompiledMethod(id, null, forSubArch); compiledMethods[id] = cm; return cm; } // Fetch a previously compiled method. // @Uninterruptible public static VM_CompiledMethod getCompiledMethod(int compiledMethodId) { VM_Magic.isync(); // see potential update from other procs if (VM.VerifyAssertions) { if (!(0 < compiledMethodId && compiledMethodId <= currentCompiledMethodId)) { VM.sysWriteln("WARNING: attempt to get compiled method #", compiledMethodId); return null; } } return compiledMethods[compiledMethodId]; } // Get number of methods compiled so far. // @Uninterruptible public static int numCompiledMethods() { return currentCompiledMethodId + 1; } // Getter method for the debugger, interpreter. // @Uninterruptible public static VM_CompiledMethod[] getCompiledMethods() { return compiledMethods; } /** * Find the method whose machine code contains the specified instruction. * * Assumption: caller has disabled gc (otherwise collector could move * objects without fixing up the raw <code>ip</code> pointer) * * Note: this method is highly inefficient. Normally you should use the * following instead: * * <code> * VM_ClassLoader.getCompiledMethod(VM_Magic.getCompiledMethodID(fp)) * </code> * * @param ip instruction address * * Usage note: <code>ip</code> must point to the instruction *following* the * actual instruction whose method is sought. This allows us to properly * handle the case where the only address we have to work with is a return * address (ie. from a stackframe) or an exception address (ie. from a null * pointer dereference, array bounds check, or divide by zero) on a machine * architecture with variable length instructions. In such situations we'd * have no idea how far to back up the instruction pointer to point to the * "call site" or "exception site". * * @return method (<code>null</code> --> not found) */ @Uninterruptible public static VM_CompiledMethod findMethodForInstruction(LocalAddress ip) { for (int i = 0, n = numCompiledMethods(); i < n; ++i) { VM_CompiledMethod compiledMethod = compiledMethods[i]; if (compiledMethod == null || !compiledMethod.isCompiled()) { continue; // empty slot } if (compiledMethod.containsReturnAddress(ip)) { return compiledMethod; } } return null; } // We keep track of compiled methods that become obsolete because they have // been replaced by another version. These are candidates for GC. But, they // can only be collected once we are certain that they are no longer being // executed. Here, we keep track of them until we know they are no longer // in use. public static void setCompiledMethodObsolete(VM_CompiledMethod compiledMethod) { // Currently, we avoid setting methods of java.lang.Object obsolete. // This is because the TIBs for arrays point to the original version // and are not updated on recompilation. // !!TODO: When replacing a java.lang.Object method, find arrays in JTOC // and update TIB to use newly recompiled method. if (compiledMethod.getMethod().getDeclaringClass().isJavaLangObjectType()) { return; } compiledMethod.setObsolete(); VM_Magic.sync(); scanForObsoleteMethods = true; } // Snip reference to CompiledMethod so that we can reclaim code space. If // the code is currently being executed, stack scanning is responsible for // marking it NOT obsolete. Keep such reference until a future GC. // NOTE: It's expected that this is processed during GC, after scanning // stacks to determine which methods are currently executing. public static void snipObsoleteCompiledMethods() { VM_Magic.isync(); if (!scanForObsoleteMethods) return; scanForObsoleteMethods = false; VM_Magic.sync(); int max = numCompiledMethods(); for (int i = 0; i < max; i++) { VM_CompiledMethod cm = compiledMethods[i]; if (cm != null) { if (cm.isActiveOnStack()) { if (cm.isObsolete()) { // can't get it this time; force us to look again next GC scanForObsoleteMethods = true; VM_Magic.sync(); } cm.clearActiveOnStack(); } else { if (cm.isObsolete()) { // obsolete and not active on a thread stack: it's garbage! compiledMethods[i] = null; } } } } } /** * Report on the space used by compiled code and associated mapping information */ public static void spaceReport() { int[] codeCount = new int[VM_CompiledMethod.NUM_COMPILER_TYPES + 1]; int[] codeBytes = new int[VM_CompiledMethod.NUM_COMPILER_TYPES + 1]; int[] mapBytes = new int[VM_CompiledMethod.NUM_COMPILER_TYPES + 1]; VM_Array codeArray = VM_Type.CodeArrayType.asArray(); for (int i = 0; i < numCompiledMethods(); i++) { VM_CompiledMethod cm = compiledMethods[i]; if (cm == null || !cm.isCompiled()) continue; int ct = cm.getCompilerType(); codeCount[ct]++; int size = codeArray.getInstanceSize(cm.numberOfInstructions()); codeBytes[ct] += VM_Memory.alignUp(size, BYTES_IN_ADDRESS); mapBytes[ct] += cm.size(); } VM.sysWriteln("Compiled code space report\n"); VM.sysWriteln(" Baseline Compiler"); VM.sysWriteln("\tNumber of compiled methods = " + codeCount[VM_CompiledMethod.BASELINE]); VM.sysWriteln("\tTotal size of code (bytes) = " + codeBytes[VM_CompiledMethod.BASELINE]); VM.sysWriteln("\tTotal size of mapping data (bytes) = " + mapBytes[VM_CompiledMethod.BASELINE]); if (codeCount[VM_CompiledMethod.OPT] > 0) { VM.sysWriteln(" Optimizing Compiler"); VM.sysWriteln("\tNumber of compiled methods = " + codeCount[VM_CompiledMethod.OPT]); VM.sysWriteln("\tTotal size of code (bytes) = " + codeBytes[VM_CompiledMethod.OPT]); VM.sysWriteln("\tTotal size of mapping data (bytes) = " + mapBytes[VM_CompiledMethod.OPT]); } if (codeCount[VM_CompiledMethod.JNI] > 0) { VM.sysWriteln(" JNI Stub Compiler (Java->C stubs for native methods)"); VM.sysWriteln("\tNumber of compiled methods = " + codeCount[VM_CompiledMethod.JNI]); VM.sysWriteln("\tTotal size of code (bytes) = " + codeBytes[VM_CompiledMethod.JNI]); VM.sysWriteln("\tTotal size of mapping data (bytes) = " + mapBytes[VM_CompiledMethod.JNI]); } } //----------------// // implementation // //----------------// // Java methods that have been compiled into machine code. // Note that there may be more than one compiled versions of the same method // (ie. at different levels of optimization). // private static VM_CompiledMethod[] compiledMethods = new VM_CompiledMethod[16000]; // Index of most recently allocated slot in compiledMethods[]. // private static int currentCompiledMethodId = 0; // See usage above private static boolean scanForObsoleteMethods = false; // Expand an array. // private static VM_CompiledMethod[] growArray(VM_CompiledMethod[] array, int newLength) { VM_CompiledMethod[] newarray = MM_Interface.newContiguousCompiledMethodArray(newLength); System.arraycopy(array, 0, newarray, 0, array.length); VM_Magic.sync(); return newarray; } }