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