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.compilers.common;
014    
015    import org.jikesrvm.ArchitectureSpecific;
016    import org.jikesrvm.ArchitectureSpecific.JNICompiler;
017    import org.jikesrvm.VM;
018    import org.jikesrvm.Callbacks;
019    import org.jikesrvm.Constants;
020    import org.jikesrvm.adaptive.controller.Controller;
021    import org.jikesrvm.adaptive.controller.ControllerMemory;
022    import org.jikesrvm.adaptive.controller.ControllerPlan;
023    import org.jikesrvm.adaptive.recompilation.InvocationCounts;
024    import org.jikesrvm.adaptive.recompilation.BulkCompile;
025    import org.jikesrvm.adaptive.recompilation.instrumentation.AOSInstrumentationPlan;
026    import org.jikesrvm.adaptive.util.AOSGenerator;
027    import org.jikesrvm.adaptive.util.AOSLogging;
028    import org.jikesrvm.adaptive.util.CompilerAdviceAttribute;
029    import org.jikesrvm.classloader.NativeMethod;
030    import org.jikesrvm.classloader.NormalMethod;
031    import org.jikesrvm.classloader.RVMType;
032    import org.jikesrvm.classloader.TypeReference;
033    import org.jikesrvm.compilers.baseline.BaselineCompiler;
034    import org.jikesrvm.compilers.opt.MagicNotImplementedException;
035    import org.jikesrvm.compilers.opt.OptimizingCompilerException;
036    import org.jikesrvm.compilers.opt.OptOptions;
037    import org.jikesrvm.compilers.opt.driver.CompilationPlan;
038    import org.jikesrvm.compilers.opt.driver.OptimizationPlanElement;
039    import org.jikesrvm.compilers.opt.driver.OptimizationPlanner;
040    import org.jikesrvm.compilers.opt.driver.OptimizingCompiler;
041    import org.jikesrvm.runtime.Time;
042    import org.jikesrvm.scheduler.RVMThread;
043    
044    /**
045     * Harness to select which compiler to dynamically
046     * compile a method in first invocation.<p>
047     * <p>
048     * A place to put code common to all runtime compilers.
049     * This includes instrumentation code to get equivalent data for
050     * each of the runtime compilers.
051     * <p>
052     * We collect the following data for each compiler
053     * <ol>
054     * <li>
055     *   total number of methods complied by the compiler
056     * <li>
057     *   total compilation time in milliseconds.
058     * <li>
059     *   total number of bytes of bytecodes compiled by the compiler
060     *   (under the assumption that there is no padding in the bytecode
061     *   array and thus RVMMethod.getBytecodes().length is the number bytes
062     *   of bytecode for a method)
063     * <li>
064     *   total number of machine code instructions generated by the compiler
065     *   (under the assumption that there is no (excessive) padding in the
066     *   machine code array and thus CompiledMethod.numberOfInsturctions()
067     *   is a close enough approximation of the number of machinecodes generated)
068     * </ol>
069     *   Note that even if 3. & 4. are inflated due to padding, the numbers will
070     *   still be an accurate measure of the space costs of the compile-only
071     *   approach.
072     */
073    public class RuntimeCompiler implements Constants, Callbacks.ExitMonitor {
074    
075      // Use these to encode the compiler for record()
076      public static final byte JNI_COMPILER = 0;
077      public static final byte BASELINE_COMPILER = 1;
078      public static final byte OPT_COMPILER = 2;
079    
080      // Data accumulators
081      private static final String[] name = {"JNI\t", "Base\t", "Opt\t"};   // Output names
082      private static int[] totalMethods = {0, 0, 0};
083      private static double[] totalCompTime = {0, 0, 0};
084      private static int[] totalBCLength = {0, 0, 0};
085      private static int[] totalMCLength = {0, 0, 0};
086    
087      // running sum of the natural logs of the rates,
088      //  used for geometric mean, the product of rates is too big for doubles
089      //  so we use the principle of logs to help us
090      // We compute  e ** ((log a + log b + ... + log n) / n )
091      private static double[] totalLogOfRates = {0, 0, 0};
092    
093      // We can't record values until Math.log is loaded, so we miss the first few
094      private static int[] totalLogValueMethods = {0, 0, 0};
095    
096      private static String[] earlyOptArgs = new String[0];
097    
098      /** is the opt compiler usable? This will be the case after booting. */
099      protected static boolean compilerEnabled;
100    
101      // FIXME Make opt compiler reentrant and update documentation accordingly.
102    
103      /**
104       * is opt compiler currently in use? This flag is used to detect/avoid
105       * recursive opt compilation (ie when opt compilation causes a method to be
106       * compiled). We also make all public entrypoints static synchronized methods
107       * because the opt compiler is not reentrant. There are two cases here:
108       * <ol>
109       *   <li>recursive opt compilation by the same thread (always bad)
110       *   <li>parallel opt compilation (currently bad because opt compiler
111       *     is not reentrant, future ok)
112       * </ol>
113       * <p>
114       * NOTE: The associated code can be quite subtle, so please be absolutely sure
115       * you know what you're doing before modifying it!!!
116       */
117      protected static boolean compilationInProgress;
118    
119      // Cache objects needed to cons up compilation plans
120      // TODO: cutting link to opt compiler by declaring type as object.
121      public static final Object /* Options */ options = VM.BuildForAdaptiveSystem ? new OptOptions() : null;
122      public static Object /* OptimizationPlanElement[] */ optimizationPlan;
123    
124      /**
125       * To be called when the VM is about to exit.
126       * @param value the exit value
127       */
128      @Override
129      public void notifyExit(int value) {
130        report(false);
131      }
132    
133      /**
134       * This method records the time and sizes (bytecode and machine code) for
135       * a compilation.
136       * @param compiler the compiler used
137       * @param method the resulting RVMMethod
138       * @param compiledMethod the resulting compiled method
139       */
140      public static void record(byte compiler, NormalMethod method, CompiledMethod compiledMethod) {
141    
142        recordCompilation(compiler,
143                          method.getBytecodeLength(),
144                          compiledMethod.numberOfInstructions(),
145                          compiledMethod.getCompilationTime());
146    
147        if (VM.BuildForAdaptiveSystem) {
148          if (AOSLogging.logger.booted()) {
149            AOSLogging.logger.recordUpdatedCompilationRates(compiler,
150                                                        method,
151                                                        method.getBytecodeLength(),
152                                                        totalBCLength[compiler],
153                                                        compiledMethod.numberOfInstructions(),
154                                                        totalMCLength[compiler],
155                                                        compiledMethod.getCompilationTime(),
156                                                        totalCompTime[compiler],
157                                                        totalLogOfRates[compiler],
158                                                        totalLogValueMethods[compiler],
159                                                        totalMethods[compiler]);
160          }
161        }
162      }
163    
164      /**
165       * This method records the time and sizes (bytecode and machine code) for
166       * a compilation
167       * @param compiler the compiler used
168       * @param method the resulting RVMMethod
169       * @param compiledMethod the resulting compiled method
170       */
171      public static void record(byte compiler, NativeMethod method, CompiledMethod compiledMethod) {
172    
173        recordCompilation(compiler, 0, // don't have any bytecode info, its native
174                          compiledMethod.numberOfInstructions(), compiledMethod.getCompilationTime());
175      }
176    
177      /**
178       * This method does the actual recording
179       * @param compiler the compiler used
180       * @param BCLength the number of bytecodes in method source
181       * @param MCLength the length of the generated machine code
182       * @param compTime the compilation time in ms
183       */
184      private static void recordCompilation(byte compiler, int BCLength, int MCLength, double compTime) {
185    
186        totalMethods[compiler]++;
187        totalMCLength[compiler] += MCLength;
188        totalCompTime[compiler] += compTime;
189    
190        // Comp rate not useful for JNI compiler because there is no bytecode!
191        if (compiler != JNI_COMPILER) {
192          totalBCLength[compiler] += BCLength;
193          double rate = BCLength / compTime;
194    
195          // need to be fully booted before calling log
196          if (VM.fullyBooted) {
197            // we want the geometric mean, but the product of rates is too big
198            //  for doubles, so we use the principle of logs to help us
199            // We compute  e ** ((log a + log b + ... + log n) / n )
200            totalLogOfRates[compiler] += Math.log(rate);
201            totalLogValueMethods[compiler]++;
202          }
203        }
204      }
205    
206      /**
207       * This method produces a summary report of compilation activities
208       * @param explain Explains the metrics used in the report
209       */
210      public static void report(boolean explain) {
211        VM.sysWrite("\n\t\tCompilation Subsystem Report\n");
212        VM.sysWrite("Comp\t#Meths\tTime\tbcb/ms\tmcb/bcb\tMCKB\tBCKB\n");
213        for (int i = 0; i <= name.length - 1; i++) {
214          if (totalMethods[i] > 0) {
215            VM.sysWrite(name[i]);
216            // Number of methods
217            VM.sysWrite(totalMethods[i]);
218            VM.sysWrite("\t");
219            // Compilation time
220            VM.sysWrite(totalCompTime[i]);
221            VM.sysWrite("\t");
222    
223            if (i == JNI_COMPILER) {
224              VM.sysWrite("NA");
225            } else {
226              // Bytecode bytes per millisecond,
227              //  use unweighted geomean
228              VM.sysWrite(Math.exp(totalLogOfRates[i] / totalLogValueMethods[i]), 2);
229            }
230            VM.sysWrite("\t");
231            // Ratio of machine code bytes to bytecode bytes
232            if (i != JNI_COMPILER) {
233              VM.sysWrite((double) (totalMCLength[i] << ArchitectureSpecific.RegisterConstants.LG_INSTRUCTION_WIDTH) /
234                          (double) totalBCLength[i], 2);
235            } else {
236              VM.sysWrite("NA");
237            }
238            VM.sysWrite("\t");
239            // Generated machine code Kbytes
240            VM.sysWrite((double) (totalMCLength[i] << ArchitectureSpecific.RegisterConstants.LG_INSTRUCTION_WIDTH) /
241                        1024, 1);
242            VM.sysWrite("\t");
243            // Compiled bytecode Kbytes
244            if (i != JNI_COMPILER) {
245              VM.sysWrite((double) totalBCLength[i] / 1024, 1);
246            } else {
247              VM.sysWrite("NA");
248            }
249            VM.sysWrite("\n");
250          }
251        }
252        if (explain) {
253          // Generate an explanation of the metrics reported
254          VM.sysWrite("\t\t\tExplanation of Metrics\n");
255          VM.sysWrite("#Meths:\t\tTotal number of methods compiled by the compiler\n");
256          VM.sysWrite("Time:\t\tTotal compilation time in milliseconds\n");
257          VM.sysWrite("bcb/ms:\t\tNumber of bytecode bytes complied per millisecond\n");
258          VM.sysWrite("mcb/bcb:\tRatio of machine code bytes to bytecode bytes\n");
259          VM.sysWrite("MCKB:\t\tTotal number of machine code bytes generated in kilobytes\n");
260          VM.sysWrite("BCKB:\t\tTotal number of bytecode bytes compiled in kilobytes\n");
261        }
262    
263        BaselineCompiler.generateBaselineCompilerSubsystemReport(explain);
264    
265        if (VM.BuildForAdaptiveSystem) {
266          // Get the opt's report
267          RVMType theType = TypeReference.OptimizationPlanner.peekType();
268          if (theType != null && theType.asClass().isInitialized()) {
269            OptimizationPlanner.generateOptimizingCompilerSubsystemReport(explain);
270          } else {
271            VM.sysWrite("\n\tNot generating Optimizing Compiler SubSystem Report because \n");
272            VM.sysWrite("\tthe opt compiler was never invoked.\n\n");
273          }
274        }
275      }
276    
277      /**
278       * Return the current estimate of basline-compiler rate, in bcb/msec
279       */
280      public static double getBaselineRate() {
281        return Math.exp(totalLogOfRates[BASELINE_COMPILER] / totalLogValueMethods[BASELINE_COMPILER]);
282      }
283    
284      /**
285       * This method will compile the passed method using the baseline compiler.
286       * @param method the method to compile
287       */
288      public static CompiledMethod baselineCompile(NormalMethod method) {
289        Callbacks.notifyMethodCompile(method, CompiledMethod.BASELINE);
290        long start = 0;
291        CompiledMethod cm = null;
292        try {
293          if (VM.MeasureCompilation || VM.BuildForAdaptiveSystem) {
294            start = Time.nanoTime();
295          }
296    
297          cm = BaselineCompiler.compile(method);
298        } finally {
299          if (VM.MeasureCompilation || VM.BuildForAdaptiveSystem) {
300            long end = Time.nanoTime();
301            if (cm != null) {
302              double compileTime = Time.nanosToMillis(end - start);
303              cm.setCompilationTime(compileTime);
304              record(BASELINE_COMPILER, method, cm);
305            }
306          }
307        }
308    
309    
310        return cm;
311      }
312    
313      /**
314       * Process command line argument destined for the opt compiler
315       */
316      public static void processOptCommandLineArg(String prefix, String arg) {
317        if (VM.BuildForAdaptiveSystem) {
318          if (compilerEnabled) {
319            if (((OptOptions) options).processAsOption(prefix, arg)) {
320              // update the optimization plan to reflect the new command line argument
321              optimizationPlan = OptimizationPlanner.createOptimizationPlan((OptOptions) options);
322            } else {
323              VM.sysWrite("Unrecognized opt compiler argument \"" + arg + "\"");
324              VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
325            }
326          } else {
327            String[] tmp = new String[earlyOptArgs.length + 2];
328            for (int i = 0; i < earlyOptArgs.length; i++) {
329              tmp[i] = earlyOptArgs[i];
330            }
331            earlyOptArgs = tmp;
332            earlyOptArgs[earlyOptArgs.length - 2] = prefix;
333            earlyOptArgs[earlyOptArgs.length - 1] = arg;
334          }
335        } else {
336          if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
337        }
338      }
339    
340      /**
341       * Attempt to compile the passed method with the Compiler.
342       * Don't handle OptimizingCompilerExceptions
343       *   (leave it up to caller to decide what to do)<p>
344       * Precondition: compilationInProgress "lock" has been acquired
345       * @param method the method to compile
346       * @param plan the plan to use for compiling the method
347       */
348      private static CompiledMethod optCompile(NormalMethod method, CompilationPlan plan)
349          throws OptimizingCompilerException {
350        if (VM.BuildForOptCompiler) {
351          if (VM.VerifyAssertions) {
352            VM._assert(compilationInProgress, "Failed to acquire compilationInProgress \"lock\"");
353          }
354    
355          Callbacks.notifyMethodCompile(method, CompiledMethod.OPT);
356          long start = 0;
357          CompiledMethod cm = null;
358          try {
359            if (VM.MeasureCompilation || VM.BuildForAdaptiveSystem) {
360              start = Time.nanoTime();
361            }
362            cm = OptimizingCompiler.compile(plan);
363          } finally {
364            if (VM.MeasureCompilation || VM.BuildForAdaptiveSystem) {
365              long end = Time.nanoTime();
366              if (cm != null) {
367                double compileTime = Time.nanosToMillis(end - start);
368                cm.setCompilationTime(compileTime);
369                record(OPT_COMPILER, method, cm);
370              }
371            }
372          }
373    
374          return cm;
375        } else {
376          if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
377          return null;
378        }
379      }
380    
381      // These methods are safe to invoke from RuntimeCompiler.compile
382    
383      /**
384       * This method tries to compile the passed method with the Compiler,
385       * using the default compilation plan.  If
386       * this fails we will use the quicker compiler (baseline for now)
387       * The following is carefully crafted to avoid (infinte) recursive opt
388       * compilation for all combinations of bootimages & lazy/eager compilation.
389       * Be absolutely sure you know what you're doing before changing it !!!
390       * @param method the method to compile
391       */
392      public static synchronized CompiledMethod optCompileWithFallBack(NormalMethod method) {
393        if (VM.BuildForOptCompiler) {
394          if (compilationInProgress) {
395            return fallback(method);
396          } else {
397            try {
398              compilationInProgress = true;
399              CompilationPlan plan =
400                  new CompilationPlan(method,
401                                          (OptimizationPlanElement[]) optimizationPlan,
402                                          null,
403                                          (OptOptions) options);
404              return optCompileWithFallBackInternal(method, plan);
405            } finally {
406              compilationInProgress = false;
407            }
408          }
409        } else {
410          if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
411          return null;
412        }
413      }
414    
415      /**
416       * This method tries to compile the passed method with the Compiler
417       * with the passed compilation plan.  If
418       * this fails we will use the quicker compiler (baseline for now)
419       * The following is carefully crafted to avoid (infinite) recursive opt
420       * compilation for all combinations of bootimages & lazy/eager compilation.
421       * Be absolutely sure you know what you're doing before changing it !!!
422       * @param method the method to compile
423       * @param plan the compilation plan to use for the compile
424       */
425      public static synchronized CompiledMethod optCompileWithFallBack(NormalMethod method,
426                                                                          CompilationPlan plan) {
427        if (VM.BuildForOptCompiler) {
428          if (compilationInProgress) {
429            return fallback(method);
430          } else {
431            try {
432              compilationInProgress = true;
433              return optCompileWithFallBackInternal(method, plan);
434            } finally {
435              compilationInProgress = false;
436            }
437          }
438        } else {
439          if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
440          return null;
441        }
442      }
443    
444      /**
445       * This real method that performs the opt compilation.
446       * @param method the method to compile
447       * @param plan the compilation plan to use
448       */
449      private static CompiledMethod optCompileWithFallBackInternal(NormalMethod method, CompilationPlan plan) {
450        if (VM.BuildForOptCompiler) {
451          if (method.hasNoOptCompileAnnotation()) return fallback(method);
452          try {
453            return optCompile(method, plan);
454          } catch (OptimizingCompilerException e) {
455            String msg =
456                "RuntimeCompiler: can't optimize \"" +
457                method +
458                "\" (error was: " +
459                e +
460                "): reverting to baseline compiler\n";
461            if (e.isFatal && VM.ErrorsFatal) {
462              e.printStackTrace();
463              VM.sysFail(msg);
464            } else {
465              boolean printMsg = true;
466              if (e instanceof MagicNotImplementedException) {
467                printMsg = !((MagicNotImplementedException) e).isExpected;
468              }
469              if (printMsg) VM.sysWrite(msg);
470            }
471            return fallback(method);
472          }
473        } else {
474          if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
475          return null;
476        }
477      }
478    
479      /* recompile the specialized method with Compiler. */
480      public static CompiledMethod recompileWithOptOnStackSpecialization(CompilationPlan plan) {
481        if (VM.BuildForOptCompiler) {
482          if (VM.VerifyAssertions) { VM._assert(plan.method.isForOsrSpecialization());}
483          if (compilationInProgress) {
484            return null;
485          }
486    
487          try {
488            compilationInProgress = true;
489    
490            // the compiler will check if isForOsrSpecialization of the method
491            CompiledMethod cm = optCompile(plan.method, plan);
492    
493            // we do not replace the compiledMethod of original method,
494            // because it is temporary method
495            return cm;
496          } catch (OptimizingCompilerException e) {
497            e.printStackTrace();
498            String msg =
499                "Optimizing compiler " +
500                "(via recompileWithOptOnStackSpecialization): " +
501                "can't optimize \"" +
502                plan
503                    .method +
504                            "\" (error was: " +
505                            e +
506                            ")\n";
507    
508            if (e.isFatal && VM.ErrorsFatal) {
509              VM.sysFail(msg);
510            } else {
511              VM.sysWrite(msg);
512            }
513            return null;
514          } finally {
515            compilationInProgress = false;
516          }
517        } else {
518          if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
519          return null;
520        }
521      }
522    
523      /**
524       * This method tries to compile the passed method with the Compiler.
525       * It will install the new compiled method in the VM, if successful.
526       * <p>
527       * NOTE: the recompile method should never be invoked via
528       *      RuntimeCompiler.compile;
529       *   it does not have sufficient guards against recursive recompilation.
530       * @param plan the compilation plan to use
531       * @return the CMID of the new method if successful, -1 if the
532       *    recompilation failed.
533       *
534       **/
535      public static synchronized int recompileWithOpt(CompilationPlan plan) {
536        if (VM.BuildForOptCompiler) {
537          if (compilationInProgress) {
538            return -1;
539          } else {
540            try {
541              compilationInProgress = true;
542              CompiledMethod cm = optCompile(plan.method, plan);
543              try {
544                plan.method.replaceCompiledMethod(cm);
545              } catch (Throwable e) {
546                String msg = "Failure in RVMMethod.replaceCompiledMethod (via recompileWithOpt): while replacing \"" + plan
547                    .method + "\" (error was: " + e + ")\n";
548                if (VM.ErrorsFatal) {
549                  e.printStackTrace();
550                  VM.sysFail(msg);
551                } else {
552                  VM.sysWrite(msg);
553                }
554                return -1;
555              }
556              return cm.getId();
557            } catch (OptimizingCompilerException e) {
558              String msg = "Optimizing compiler (via recompileWithOpt): can't optimize \"" + plan
559                  .method + "\" (error was: " + e + ")\n";
560              if (e.isFatal && VM.ErrorsFatal) {
561                e.printStackTrace();
562                VM.sysFail(msg);
563              } else {
564                // VM.sysWrite(msg);
565              }
566              return -1;
567            } finally {
568              compilationInProgress = false;
569            }
570          }
571        } else {
572          if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
573          return -1;
574        }
575      }
576    
577      /**
578       * A wrapper method for those callers who don't want to make
579       * optimization plans
580       * @param method the method to recompile
581       */
582      public static int recompileWithOpt(NormalMethod method) {
583        if (VM.BuildForOptCompiler) {
584          CompilationPlan plan =
585              new CompilationPlan(method,
586                                      (OptimizationPlanElement[]) optimizationPlan,
587                                      null,
588                                      (OptOptions) options);
589          return recompileWithOpt(plan);
590        } else {
591          if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
592          return -1;
593        }
594      }
595    
596      /**
597       * This method uses the default compiler (baseline) to compile a method
598       * It is typically called when a more aggressive compilation fails.
599       * This method is safe to invoke from RuntimeCompiler.compile.
600       */
601      protected static CompiledMethod fallback(NormalMethod method) {
602        // call the inherited method "baselineCompile"
603        return baselineCompile(method);
604      }
605    
606      public static void boot() {
607        if (VM.MeasureCompilation) {
608          Callbacks.addExitMonitor(new RuntimeCompiler());
609        }
610        if (VM.BuildForAdaptiveSystem) {
611          optimizationPlan = OptimizationPlanner.createOptimizationPlan((OptOptions) options);
612          if (VM.MeasureCompilationPhases) {
613            OptimizationPlanner.initializeMeasureCompilation();
614          }
615    
616          OptimizingCompiler.init((OptOptions) options);
617    
618          BulkCompile.init();
619          // when we reach here the OPT compiler is enabled.
620          compilerEnabled = true;
621    
622          for (int i = 0; i < earlyOptArgs.length; i += 2) {
623            processOptCommandLineArg(earlyOptArgs[i], earlyOptArgs[i + 1]);
624          }
625        }
626      }
627    
628      public static void processCommandLineArg(String prefix, String arg) {
629        if (VM.BuildForAdaptiveSystem) {
630          if (Controller.options != null && Controller.options.optIRC()) {
631            processOptCommandLineArg(prefix, arg);
632          } else {
633            BaselineCompiler.processCommandLineArg(prefix, arg);
634          }
635        } else {
636          BaselineCompiler.processCommandLineArg(prefix, arg);
637        }
638      }
639    
640      /**
641       * Compile a Java method when it is first invoked.
642       * @param method the method to compile
643       * @return its compiled method.
644       */
645      public static CompiledMethod compile(NormalMethod method) {
646        if (VM.BuildForAdaptiveSystem) {
647          CompiledMethod cm;
648          if (!Controller.enabled) {
649            // System still early in boot process; compile with baseline compiler
650            cm = baselineCompile(method);
651            ControllerMemory.incrementNumBase();
652          } else {
653            if (Controller.options.optIRC()) {
654              if (// will only run once: don't bother optimizing
655                  method.isClassInitializer() ||
656                  // exception in progress. can't use opt compiler:
657                  // it uses exceptions and runtime doesn't support
658                  // multiple pending (undelivered) exceptions [--DL]
659                  RVMThread.getCurrentThread().getExceptionRegisters().inuse) {
660                // compile with baseline compiler
661                cm = baselineCompile(method);
662                ControllerMemory.incrementNumBase();
663              } else { // compile with opt compiler
664                AOSInstrumentationPlan instrumentationPlan =
665                    new AOSInstrumentationPlan(Controller.options, method);
666                CompilationPlan compPlan =
667                    new CompilationPlan(method,
668                                            (OptimizationPlanElement[]) optimizationPlan,
669                                            instrumentationPlan,
670                                            (OptOptions) options);
671                cm = optCompileWithFallBack(method, compPlan);
672              }
673            } else {
674              if ((Controller.options.BACKGROUND_RECOMPILATION && !Controller.options.ENABLE_PRECOMPILE)) {
675                // must be an initial compilation: compile with baseline compiler
676                // or if recompilation with OSR.
677                cm = baselineCompile(method);
678                ControllerMemory.incrementNumBase();
679              } else {
680                if (CompilerAdviceAttribute.hasAdvice()) {
681                  CompilerAdviceAttribute attr = CompilerAdviceAttribute.getCompilerAdviceInfo(method);
682                  if (attr.getCompiler() != CompiledMethod.OPT) {
683                    cm = fallback(method);
684                    AOSLogging.logger.recordCompileTime(cm, 0.0);
685                    return cm;
686                  }
687                  int newCMID = -2;
688                  CompilationPlan compPlan;
689                  if (Controller.options.counters()) {
690                    // for invocation counter, we only use one optimization level
691                    compPlan = InvocationCounts.createCompilationPlan(method);
692                  } else {
693                    // for now there is not two options for sampling, so
694                    // we don't have to use: if (Controller.options.sampling())
695                    compPlan = Controller.recompilationStrategy.createCompilationPlan(method, attr.getOptLevel(), null);
696                  }
697                  AOSLogging.logger.recompilationStarted(compPlan);
698                  newCMID = recompileWithOpt(compPlan);
699                  cm = newCMID == -1 ? null : CompiledMethods.getCompiledMethod(newCMID);
700                  if (newCMID == -1) {
701                    AOSLogging.logger.recompilationAborted(compPlan);
702                  } else if (newCMID > 0) {
703                    AOSLogging.logger.recompilationCompleted(compPlan);
704                  }
705                  if (cm == null) { // if recompilation is aborted
706                    cm = baselineCompile(method);
707                    ControllerMemory.incrementNumBase();
708                  }
709                } else {
710                  // check to see if there is a compilation plan for this method.
711                  ControllerPlan plan = ControllerMemory.findLatestPlan(method);
712                  if (plan == null || plan.getStatus() != ControllerPlan.IN_PROGRESS) {
713                    // initial compilation or some other funny state: compile with baseline compiler
714                    cm = baselineCompile(method);
715                    ControllerMemory.incrementNumBase();
716                  } else {
717                    cm = plan.doRecompile();
718                    if (cm == null) {
719                      // opt compilation aborted for some reason.
720                      cm = baselineCompile(method);
721                    }
722                  }
723                }
724              }
725            }
726          }
727          if ((Controller.options.ENABLE_ADVICE_GENERATION) &&
728              (cm.getCompilerType() == CompiledMethod.BASELINE) &&
729              Controller
730                  .enabled) {
731            AOSGenerator.baseCompilationCompleted(cm);
732          }
733          AOSLogging.logger.recordCompileTime(cm, 0.0);
734          return cm;
735        } else {
736          return baselineCompile(method);
737        }
738      }
739    
740      /**
741       * Compile the stub for a native method when it is first invoked.
742       * @param method the method to compile
743       * @return its compiled method.
744       */
745      public static CompiledMethod compile(NativeMethod method) {
746        Callbacks.notifyMethodCompile(method, CompiledMethod.JNI);
747        long start = 0;
748        CompiledMethod cm = null;
749        try {
750          if (VM.MeasureCompilation || VM.BuildForAdaptiveSystem) {
751            start = Time.nanoTime();
752          }
753    
754          cm = JNICompiler.compile(method);
755          if (VM.verboseJNI) {
756            VM.sysWriteln("[Dynamic-linking native method " +
757                          method.getDeclaringClass() +
758                          "." +
759                          method.getName() +
760                          " " +
761                          method.getDescriptor());
762          }
763        } finally {
764          if (VM.MeasureCompilation || VM.BuildForAdaptiveSystem) {
765            long end = Time.nanoTime();
766            if (cm != null) {
767              double compileTime = Time.nanosToMillis(end - start);
768              cm.setCompilationTime(compileTime);
769              record(JNI_COMPILER, method, cm);
770            }
771          }
772        }
773    
774        return cm;
775      }
776    
777      /**
778       * returns the string version of compiler number, using the naming scheme
779       * in this file
780       * @param compiler the compiler of interest
781       * @return the string version of compiler number
782       */
783      public static String getCompilerName(byte compiler) {
784        return name[compiler];
785      }
786    
787    }