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