/* * 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.adaptive.controller; import org.jikesrvm.VM; import org.jikesrvm.adaptive.recompilation.CompilerDNA; import org.jikesrvm.adaptive.util.AOSLogging; import org.jikesrvm.classloader.RVMMethod; import org.jikesrvm.classloader.NormalMethod; import org.jikesrvm.compilers.common.CompiledMethod; import org.jikesrvm.compilers.opt.OptOptions; import org.jikesrvm.compilers.opt.driver.CompilationPlan; import org.jikesrvm.compilers.opt.driver.InstrumentationPlan; import org.jikesrvm.compilers.opt.driver.OptimizationPlanElement; import org.jikesrvm.compilers.opt.driver.OptimizationPlanner; import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod; /** * An abstract class providing the interface to the decision making * component of the controller. */ public abstract class RecompilationStrategy { //------ Interface ------- /** * A hot method has been passed to the controller by an organizer */ ControllerPlan considerHotMethod(CompiledMethod cmpMethod, HotMethodEvent hme) { // Default behavior, do nothing. return null; } /** * A hot call edge has been passed to the controller by an organizer */ void considerHotCallEdge(CompiledMethod cmpMethod, 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 RVMMethod 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 */ ControllerPlan createControllerPlan(RVMMethod method, int optLevel, InstrumentationPlan instPlan, int prevCMID, double expectedSpeedup, double expectedCompilationTime, double priority) { // Construct the compilation plan (varies depending on strategy) CompilationPlan compPlan = createCompilationPlan((NormalMethod) method, optLevel, instPlan); // Create the controller plan return new ControllerPlan(compPlan, 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 CompilationPlan createCompilationPlan(NormalMethod method, int optLevel, InstrumentationPlan instPlan) { // Construct a plan from the basic pre-computed opt-levels return new CompilationPlan(method, _optPlans[optLevel], null, _options[optLevel]); } /** * Should we consider the hme for recompilation? * * @param hme the HotMethodEvent * @param plan the ControllerPlan for the compiled method (may be null) * @return true/false value */ boolean considerForRecompilation(HotMethodEvent hme, ControllerPlan plan) { RVMMethod 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 (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() == ControllerPlan.OUTDATED || ControllerMemory.planWithStatus(method, 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 !ControllerMemory.planWithStatus(method, ControllerPlan.ABORTED_COMPILATION_ERROR); } } private void transferSamplesToNewPlan(HotMethodEvent hme) { AOSLogging.logger.oldVersionStillHot(hme); double oldNumSamples = Controller.methodSamples.getData(hme.getCMID()); ControllerPlan activePlan = 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). Controller.methodSamples.reset(hme.getCMID()); double expectedSpeedup = activePlan.getExpectedSpeedup(); double newNumSamples = oldNumSamples / expectedSpeedup; 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(RVMMethod method) { return ControllerMemory.findLatestPlan(method) != null; } /** * This method retrieves the previous compiler constant. */ int getPreviousCompiler(CompiledMethod cmpMethod) { switch (cmpMethod.getCompilerType()) { case CompiledMethod.TRAP: case CompiledMethod.JNI: return -1; // don't try to optimize these guys! case 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 CompiledMethod.OPT: OptCompiledMethod optMeth = (OptCompiledMethod) cmpMethod; return 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 Controller.options.DERIVED_MAX_OPT_LEVEL; } private OptimizationPlanElement[][] _optPlans; private OptOptions[] _options; /** * Create the default set of <optimization plan, options> pairs * Process optimizing compiler command line options. */ void createOptimizationPlans() { OptOptions options = new OptOptions(); int maxOptLevel = getMaxOptLevel(); _options = new OptOptions[maxOptLevel + 1]; _optPlans = new OptimizationPlanElement[maxOptLevel + 1][]; String[] optCompilerOptions = 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] = OptimizationPlanner.createOptimizationPlan(_options[i]); } } /** * Process the command line arguments and pass the appropriate ones to the * 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(OptOptions 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"); } } } } }