001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.adaptive.controller;
014
015import org.jikesrvm.VM;
016import org.jikesrvm.adaptive.recompilation.CompilerDNA;
017import org.jikesrvm.adaptive.util.AOSLogging;
018import org.jikesrvm.classloader.RVMMethod;
019import org.jikesrvm.classloader.NormalMethod;
020import org.jikesrvm.compilers.common.CompiledMethod;
021import org.jikesrvm.compilers.opt.OptOptions;
022import org.jikesrvm.compilers.opt.driver.CompilationPlan;
023import org.jikesrvm.compilers.opt.driver.InstrumentationPlan;
024import org.jikesrvm.compilers.opt.driver.OptimizationPlanElement;
025import org.jikesrvm.compilers.opt.driver.OptimizationPlanner;
026import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod;
027
028/**
029 * An abstract class providing the interface to the decision making
030 * component of the controller.
031 */
032public abstract class RecompilationStrategy {
033
034  //------  Interface -------
035
036  ControllerPlan considerHotMethod(CompiledMethod cmpMethod, HotMethodEvent hme) {
037    // Default behavior, do nothing.
038    return null;
039  }
040
041  void considerHotCallEdge(CompiledMethod cmpMethod, AINewHotEdgeEvent event) {
042    // Default behavior, do nothing.
043  }
044
045  // Functionality common to all recompilation strategies
046  // (at least for now)
047
048  /**
049   *  Initialize the recompilation strategy.<p>
050   *
051   *  Note: This uses the command line options to set up the
052   *  optimization plans, so this must be run after the command line
053   *  options are available.
054   */
055  void init() {
056    createOptimizationPlans();
057  }
058
059  /**
060   * This helper method creates a ControllerPlan, which contains a
061   * CompilationPlan, for the passed method using the passed optimization
062   * level and instrumentation plan.
063   *
064   * @param method the RVMMethod for the plan
065   * @param optLevel the optimization level to use in the plan
066   * @param instPlan the instrumentation plan to use
067   * @param prevCMID the previous compiled method ID
068   * @param expectedSpeedup  expected speedup from this recompilation
069   * @param expectedCompilationTime expected time for compilation
070   *  and execution of the new method
071   * @param priority a measure of the oveall benefit we expect to see
072   *                 by executing this plan.
073   * @return the compilation plan to be used
074   */
075  ControllerPlan createControllerPlan(RVMMethod method, int optLevel, InstrumentationPlan instPlan, int prevCMID,
076                                         double expectedSpeedup, double expectedCompilationTime, double priority) {
077
078    // Construct the compilation plan (varies depending on strategy)
079    CompilationPlan compPlan = createCompilationPlan((NormalMethod) method, optLevel, instPlan);
080
081    // Create the controller plan
082    return new ControllerPlan(compPlan,
083                                 Controller.controllerClock,
084                                 prevCMID,
085                                 expectedSpeedup,
086                                 expectedCompilationTime,
087                                 priority);
088  }
089
090  /**
091   * Constructs a compilation plan that will compile the given method
092   * with instrumentation.
093   *
094   * @param method The method to be compiled with instrumentation
095   * @param optLevel The opt-level to recompile at
096   * @param instPlan The instrumentation plan
097   * @return a non-{@code null} compilation plan
098   */
099  public CompilationPlan createCompilationPlan(NormalMethod method, int optLevel,
100                                                   InstrumentationPlan instPlan) {
101
102    // Construct a plan from the basic pre-computed opt-levels
103    return new CompilationPlan(method, _optPlans[optLevel], null, _options[optLevel]);
104  }
105
106  /**
107   * Should we consider the hme for recompilation?
108   *
109   * @param hme the HotMethodEvent
110   * @param plan the ControllerPlan for the compiled method (may be {@code null})
111   * @return {@code true/false} value
112   */
113  boolean considerForRecompilation(HotMethodEvent hme, ControllerPlan plan) {
114    RVMMethod method = hme.getMethod();
115    if (plan == null) {
116      // Our caller did not find a matching plan for this compiled method.
117      // Therefore the code was not generated by the AOS recompilation subsystem.
118      if (ControllerMemory.shouldConsiderForInitialRecompilation(method)) {
119        // AOS has not already taken action to address the situation
120        // (or it attempted to take action, and the attempt failed in a way
121        //  that doesn't preclude trying again,
122        //  for example the compilation queue could have been full).
123        return true;
124      } else {
125        // AOS has already taken action to address the situation, and thus
126        // we need to handle this as an old compiled version of a
127        // method still being live on some thread's stack.
128        transferSamplesToNewPlan(hme);
129        return false;
130      }
131    } else {
132      // A matching plan was found.
133      if (plan.getStatus() == ControllerPlan.OUTDATED ||
134          ControllerMemory.planWithStatus(method, ControllerPlan.IN_PROGRESS)) {
135        // (a) The HotMethodEvent actually corresponds to an
136        // old compiled version of the method
137        // that is still live on some thread's stack or
138        // (b) AOS has already initiated a plan that hasn't
139        // completed yet to address the situation.
140        // Therefore don't initiate a new recompilation action.
141        transferSamplesToNewPlan(hme);
142        return false;
143      }
144      // if AOS failed to successfully recompile this method before.
145      // Don't try it again.
146      return !ControllerMemory.planWithStatus(method, ControllerPlan.ABORTED_COMPILATION_ERROR);
147    }
148  }
149
150  private void transferSamplesToNewPlan(HotMethodEvent hme) {
151    AOSLogging.logger.oldVersionStillHot(hme);
152    double oldNumSamples = Controller.methodSamples.getData(hme.getCMID());
153    ControllerPlan activePlan = ControllerMemory.findLatestPlan(hme.getMethod());
154    if (activePlan == null) return; // shouldn't happen.
155    int newCMID = activePlan.getCMID();
156    if (newCMID > 0) {
157      // If we have a valid CMID then transfer the samples.
158      // If the CMID isn't valid, it means the compilation hasn't completed yet and
159      // the samples will be transfered by the compilation thread when it does (so we do nothing).
160      Controller.methodSamples.reset(hme.getCMID());
161      double expectedSpeedup = activePlan.getExpectedSpeedup();
162      double newNumSamples = oldNumSamples / expectedSpeedup;
163      Controller.methodSamples.augmentData(newCMID, newNumSamples);
164    }
165  }
166
167  /**
168   *  This method returns {@code true} if we've already tried to recompile the
169   *  passed method.  It does not guarantee that the compilation was
170   *  successful.
171   *
172   *  @param method the method of interest
173   *  @return whether we've tried to recompile this method
174   */
175  boolean previousRecompilationAttempted(RVMMethod method) {
176    return ControllerMemory.findLatestPlan(method) != null;
177  }
178
179  /**
180   *  @param cmpMethod the compiled method whose previous compiler we want to know
181   *  @return the constant for the previous compiler
182   */
183  int getPreviousCompiler(CompiledMethod cmpMethod) {
184    switch (cmpMethod.getCompilerType()) {
185      case CompiledMethod.TRAP:
186      case CompiledMethod.JNI:
187        return -1; // don't try to optimize these guys!
188      case CompiledMethod.BASELINE: {
189        // Prevent the adaptive system from recompiling certain classes
190        // of baseline compiled methods.
191        if (cmpMethod.getMethod().getDeclaringClass().hasDynamicBridgeAnnotation()) {
192          // The opt compiler does not implement this calling convention.
193          return -1;
194        }
195        if (cmpMethod.getMethod().getDeclaringClass().hasBridgeFromNativeAnnotation()) {
196          // The opt compiler does not implement this calling convention.
197          return -1;
198        }
199        if (cmpMethod.getMethod().hasNoOptCompileAnnotation()) {
200          // Explict declaration that the method should not be opt compiled.
201          return -1;
202        }
203        if (!cmpMethod.getMethod().isInterruptible()) {
204          // A crude filter to identify the subset of core VM methods that
205          // can't be recompiled because we require their code to be non-moving.
206          // We really need to do a better job of this to avoid missing too many opportunities.
207          // NOTE: it doesn't matter whether or not the GC is non-moving here,
208          //       because recompiling effectively moves the code to a new location even if
209          //       GC never moves it again!!!
210          //      (C code may have a return address or other naked pointer into the old instruction array)
211          return -1;
212        }
213        return 0;
214      }
215      case CompiledMethod.OPT:
216        OptCompiledMethod optMeth = (OptCompiledMethod) cmpMethod;
217        return CompilerDNA.getCompilerConstant(optMeth.getOptLevel());
218      default:
219        if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED, "Unknown Compiler");
220        return -1;
221    }
222  }
223
224  /**
225   * @return is the maximum opt level that is valid according to this strategy
226   */
227  int getMaxOptLevel() {
228    return Controller.options.DERIVED_MAX_OPT_LEVEL;
229  }
230
231  private OptimizationPlanElement[][] _optPlans;
232  private OptOptions[] _options;
233
234  /**
235   * Creates the default set of &lt;optimization plan, options&gt; pairs.
236   * Processes optimizing compiler command line options.
237   */
238  void createOptimizationPlans() {
239    OptOptions options = new OptOptions();
240
241    int maxOptLevel = getMaxOptLevel();
242    _options = new OptOptions[maxOptLevel + 1];
243    _optPlans = new OptimizationPlanElement[maxOptLevel + 1][];
244    String[] optCompilerOptions = Controller.getOptCompilerOptions();
245    for (int i = 0; i <= maxOptLevel; i++) {
246      _options[i] = options.dup();
247      _options[i].setOptLevel(i);               // set optimization level specific optimizations
248      processCommandLineOptions(_options[i], i, maxOptLevel, optCompilerOptions);
249      _optPlans[i] = OptimizationPlanner.createOptimizationPlan(_options[i]);
250    }
251  }
252
253  /**
254   * Process the command line arguments and pass the appropriate ones to the
255   * Options
256   * Called by sampling and counters recompilation strategy.
257   *
258   * @param options The options being constructed
259   * @param optLevel The level of the options being constructed
260   * @param maxOptLevel The maximum valid opt level
261   * @param optCompilerOptions The list of command line options
262   */
263  public static void processCommandLineOptions(OptOptions options, int optLevel, int maxOptLevel,
264                                               String[] optCompilerOptions) {
265
266    String prefix = "opt" + optLevel + ":";
267    for (String optCompilerOption : optCompilerOptions) {
268      if (optCompilerOption.startsWith("opt:")) {
269        String option = optCompilerOption.substring(4);
270        if (!options.processAsOption("-X:recomp:", option)) {
271          VM.sysWrite("vm: Unrecognized optimizing compiler command line argument: \"" +
272                      option +
273                      "\" passed in as " +
274                      optCompilerOption +
275                      "\n");
276        }
277      } else if (optCompilerOption.startsWith(prefix)) {
278        String option = optCompilerOption.substring(5);
279        if (!options.processAsOption("-X:recomp:" + prefix, option)) {
280          VM.sysWrite("vm: Unrecognized optimizing compiler command line argument: \"" +
281                      option +
282                      "\" passed in as " +
283                      optCompilerOption +
284                      "\n");
285        }
286      }
287    }
288    // TODO: check for optimization levels that are invalid; that is,
289    // greater than optLevelMax.
290    //
291    for (String optCompilerOption1 : optCompilerOptions) {
292      if (!optCompilerOption1.startsWith("opt")) {
293        // This should never be the case!
294        continue;
295      }
296      if (!optCompilerOption1.startsWith("opt:")) {
297        // must specify optimization level!
298        int endPoint = optCompilerOption1.indexOf(':');
299        if (endPoint == -1) {
300          VM.sysWrite("vm: Unrecognized optimization level in optimizing compiler command line argument: \"" +
301                      optCompilerOption1 +
302                      "\"\n");
303        }
304        String optLevelS;
305        try {
306          optLevelS = optCompilerOption1.substring(3, endPoint);
307        } catch (IndexOutOfBoundsException e) {
308          VM.sysWrite("vm internal error: trying to find opt level has thrown indexOutOfBoundsException\n");
309          e.printStackTrace();
310          continue;
311        }
312        try {
313          Integer optLevelI = Integer.valueOf(optLevelS);
314          int cmdOptLevel = optLevelI;
315          if (cmdOptLevel > maxOptLevel) {
316            VM.sysWrite("vm: Invalid optimization level in optimizing compiler command line argument: \"" +
317                        optCompilerOption1 +
318                        "\"\n" +
319                        "  Specified optimization level " +
320                        cmdOptLevel +
321                        " must be less than " +
322                        maxOptLevel +
323                        "\n");
324          }
325        } catch (NumberFormatException e) {
326          VM.sysWrite("vm: Unrecognized optimization level in optimizing compiler command line argument: \"" +
327                      optCompilerOption1 +
328                      "\"\n");
329        }
330      }
331    }
332  }
333}
334
335
336
337