/* * 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.adaptive.controller; import org.jikesrvm.VM; import org.jikesrvm.adaptive.recompilation.VM_CompilerDNA; import org.jikesrvm.adaptive.util.VM_AOSLogging; import org.jikesrvm.classloader.VM_Method; import org.jikesrvm.classloader.VM_NormalMethod; import org.jikesrvm.compilers.common.VM_CompiledMethod; import org.jikesrvm.compilers.opt.OPT_CompilationPlan; import org.jikesrvm.compilers.opt.OPT_InstrumentationPlan; import org.jikesrvm.compilers.opt.OPT_OptimizationPlanElement; import org.jikesrvm.compilers.opt.OPT_OptimizationPlanner; import org.jikesrvm.compilers.opt.OPT_Options; import org.jikesrvm.compilers.opt.VM_OptCompiledMethod; /** * An abstract class providing the interface to the decision making * component of the controller. */ public abstract class VM_RecompilationStrategy { //------ Interface ------- /** * A hot method has been passed to the controller by an organizer */ VM_ControllerPlan considerHotMethod(VM_CompiledMethod cmpMethod, VM_HotMethodEvent hme) { // Default behavior, do nothing. return null; } /** * A hot call edge has been passed to the controller by an organizer */ void considerHotCallEdge(VM_CompiledMethod cmpMethod, VM_AINewHotEdgeEvent event) { // Default behavior, do nothing. } // Functionality common to all recompilation strategies // (at least for now) /** * Initialize the recompilation strategy. * * Note: This uses the command line options to set up the * optimization plans, so this must be run after the command line * options are available. */ void init() { createOptimizationPlans(); } /** * This helper method creates a ControllerPlan, which contains a * CompilationPlan, for the passed method using the passed optimization * level and instrumentation plan. * * @param method the VM_Method for the plan * @param optLevel the optimization level to use in the plan * @param instPlan the instrumentation plan to use * @param prevCMID the previous compiled method ID * @param expectedSpeedup expected speedup from this recompilation * @param priority a measure of the oveall benefit we expect to see * by executing this plan. * @return the compilation plan to be used */ VM_ControllerPlan createControllerPlan(VM_Method method, int optLevel, OPT_InstrumentationPlan instPlan, int prevCMID, double expectedSpeedup, double expectedCompilationTime, double priority) { // Construct the compilation plan (varies depending on strategy) OPT_CompilationPlan compPlan = createCompilationPlan((VM_NormalMethod) method, optLevel, instPlan); // Create the controller plan return new VM_ControllerPlan(compPlan, VM_Controller.controllerClock, prevCMID, expectedSpeedup, expectedCompilationTime, priority); } /** * Construct a compilation plan that will compile the given method * with instrumentation. * * @param method The method to be compiled with instrumentation * @param optLevel The opt-level to recompile at * @param instPlan The instrumentation plan */ public OPT_CompilationPlan createCompilationPlan(VM_NormalMethod method, int optLevel, OPT_InstrumentationPlan instPlan) { // Construct a plan from the basic pre-computed opt-levels return new OPT_CompilationPlan(method, _optPlans[optLevel], null, _options[optLevel]); } /** * Should we consider the hme for recompilation? * * @param hme the VM_HotMethodEvent * @param plan the VM_ControllerPlan for the compiled method (may be null) * @return true/false value */ boolean considerForRecompilation(VM_HotMethodEvent hme, VM_ControllerPlan plan) { VM_Method method = hme.getMethod(); if (plan == null) { // Our caller did not find a matching plan for this compiled method. // Therefore the code was not generated by the AOS recompilation subsystem. if (VM_ControllerMemory.shouldConsiderForInitialRecompilation(method)) { // AOS has not already taken action to address the situation // (or it attempted to take action, and the attempt failed in a way // that doesn't preclude trying again, // for example the compilation queue could have been full). return true; } else { // AOS has already taken action to address the situation, and thus // we need to handle this as an old compiled version of a // method still being live on some thread's stack. transferSamplesToNewPlan(hme); return false; } } else { // A matching plan was found. if (plan.getStatus() == VM_ControllerPlan.OUTDATED || VM_ControllerMemory.planWithStatus(method, VM_ControllerPlan.IN_PROGRESS)) { // (a) The HotMethodEvent actually corresponds to an // old compiled version of the method // that is still live on some thread's stack or // (b) AOS has already initiated a plan that hasn't // completed yet to address the situation. // Therefore don't initiate a new recompilation action. transferSamplesToNewPlan(hme); return false; } // if AOS failed to successfully recompile this method before. // Don't try it again. return !VM_ControllerMemory.planWithStatus(method, VM_ControllerPlan.ABORTED_COMPILATION_ERROR); } } private void transferSamplesToNewPlan(VM_HotMethodEvent hme) { VM_AOSLogging.oldVersionStillHot(hme); double oldNumSamples = VM_Controller.methodSamples.getData(hme.getCMID()); VM_ControllerPlan activePlan = VM_ControllerMemory.findLatestPlan(hme.getMethod()); if (activePlan == null) return; // shouldn't happen. int newCMID = activePlan.getCMID(); if (newCMID > 0) { // If we have a valid CMID then transfer the samples. // If the CMID isn't valid, it means the compilation hasn't completed yet and // the samples will be transfered by the compilation thread when it does (so we do nothing). VM_Controller.methodSamples.reset(hme.getCMID()); double expectedSpeedup = activePlan.getExpectedSpeedup(); double newNumSamples = oldNumSamples / expectedSpeedup; VM_Controller.methodSamples.augmentData(newCMID, newNumSamples); } } /** * This method returns true if we've already tried to recompile the * passed method. It does not guarantee that the compilation was * successful. * * @param method the method of interest * @return whether we've tried to recompile this method */ boolean previousRecompilationAttempted(VM_Method method) { return VM_ControllerMemory.findLatestPlan(method) != null; } /** * This method retrieves the previous compiler constant. */ int getPreviousCompiler(VM_CompiledMethod cmpMethod) { switch (cmpMethod.getCompilerType()) { case VM_CompiledMethod.TRAP: case VM_CompiledMethod.JNI: return -1; // don't try to optimize these guys! case VM_CompiledMethod.BASELINE: { // Prevent the adaptive system from recompiling certain classes // of baseline compiled methods. if (cmpMethod.getMethod().getDeclaringClass().hasDynamicBridgeAnnotation()) { // The opt compiler does not implement this calling convention. return -1; } if (cmpMethod.getMethod().getDeclaringClass().hasBridgeFromNativeAnnotation()) { // The opt compiler does not implement this calling convention. return -1; } if (cmpMethod.getMethod().hasNoOptCompileAnnotation()) { // Explict declaration that the method should not be opt compiled. return -1; } if (!cmpMethod.getMethod().isInterruptible()) { // A crude filter to identify the subset of core VM methods that // can't be recompiled because we require their code to be non-moving. // We really need to do a better job of this to avoid missing too many opportunities. // NOTE: it doesn't matter whether or not the GC is non-moving here, // because recompiling effectively moves the code to a new location even if // GC never moves it again!!! // (C code may have a return address or other naked pointer into the old instruction array) return -1; } return 0; } case VM_CompiledMethod.OPT: VM_OptCompiledMethod optMeth = (VM_OptCompiledMethod) cmpMethod; return VM_CompilerDNA.getCompilerConstant(optMeth.getOptLevel()); default: if (VM.VerifyAssertions) VM._assert(false, "Unknown Compiler"); return -1; } } /** * What is the maximum opt level that is vallid according to this strategy? */ int getMaxOptLevel() { return VM_Controller.options.DERIVED_MAX_OPT_LEVEL; } private OPT_OptimizationPlanElement[][] _optPlans; private OPT_Options[] _options; /** * Create the default set of <optimization plan, options> pairs * Process optimizing compiler command line options. */ void createOptimizationPlans() { OPT_Options options = new OPT_Options(); int maxOptLevel = getMaxOptLevel(); _options = new OPT_Options[maxOptLevel + 1]; _optPlans = new OPT_OptimizationPlanElement[maxOptLevel + 1][]; String[] optCompilerOptions = VM_Controller.getOptCompilerOptions(); for (int i = 0; i <= maxOptLevel; i++) { _options[i] = options.dup(); _options[i].setOptLevel(i); // set optimization level specific optimiations processCommandLineOptions(_options[i], i, maxOptLevel, optCompilerOptions); _optPlans[i] = OPT_OptimizationPlanner.createOptimizationPlan(_options[i]); if (_options[i].PRELOAD_CLASS != null) { VM.sysWrite("PRELOAD_CLASS should be specified with -X:irc not -X:recomp\n"); VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG); } } } /** * Process the command line arguments and pass the appropriate ones to the * OPT_Options * Called by sampling and counters recompilation strategy. * * @param options The options being constructed * @param optLevel The level of the options being constructed * @param maxOptLevel The maximum valid opt level * @param optCompilerOptions The list of command line options */ public static void processCommandLineOptions(OPT_Options options, int optLevel, int maxOptLevel, String[] optCompilerOptions) { String prefix = "opt" + optLevel + ":"; for (String optCompilerOption : optCompilerOptions) { if (optCompilerOption.startsWith("opt:")) { String option = optCompilerOption.substring(4); if (!options.processAsOption("-X:recomp:", option)) { VM.sysWrite("vm: Unrecognized optimizing compiler command line argument: \"" + option + "\" passed in as " + optCompilerOption + "\n"); } } else if (optCompilerOption.startsWith(prefix)) { String option = optCompilerOption.substring(5); if (!options.processAsOption("-X:recomp:" + prefix, option)) { VM.sysWrite("vm: Unrecognized optimizing compiler command line argument: \"" + option + "\" passed in as " + optCompilerOption + "\n"); } } } // TODO: check for optimization levels that are invalid; that is, // greater than optLevelMax. // for (String optCompilerOption1 : optCompilerOptions) { if (!optCompilerOption1.startsWith("opt")) { // This should never be the case! continue; } if (!optCompilerOption1.startsWith("opt:")) { // must specify optimization level! int endPoint = optCompilerOption1.indexOf(":"); if (endPoint == -1) { VM.sysWrite("vm: Unrecognized optimization level in optimizing compiler command line argument: \"" + optCompilerOption1 + "\"\n"); } String optLevelS; try { optLevelS = optCompilerOption1.substring(3, endPoint); } catch (IndexOutOfBoundsException e) { VM.sysWrite("vm internal error: trying to find opt level has thrown indexOutOfBoundsException\n"); e.printStackTrace(); continue; } try { Integer optLevelI = Integer.valueOf(optLevelS); int cmdOptLevel = optLevelI; if (cmdOptLevel > maxOptLevel) { VM.sysWrite("vm: Invalid optimization level in optimizing compiler command line argument: \"" + optCompilerOption1 + "\"\n" + " Specified optimization level " + cmdOptLevel + " must be less than " + maxOptLevel + "\n"); } } catch (NumberFormatException e) { VM.sysWrite("vm: Unrecognized optimization level in optimizing compiler command line argument: \"" + optCompilerOption1 + "\"\n"); } } } } }