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.compilers.opt.driver;
014
015import org.jikesrvm.VM;
016import org.jikesrvm.classloader.NormalMethod;
017import org.jikesrvm.compilers.common.CompiledMethod;
018import org.jikesrvm.compilers.opt.MagicNotImplementedException;
019import org.jikesrvm.compilers.opt.OptOptions;
020import org.jikesrvm.compilers.opt.OptimizingCompilerException;
021import org.jikesrvm.compilers.opt.ir.IR;
022import org.jikesrvm.compilers.opt.specialization.InvokeeThreadLocalContext;
023import org.jikesrvm.compilers.opt.specialization.SpecializationDatabase;
024import org.jikesrvm.runtime.Callbacks;
025
026/**
027 * <p> The main driver of the Compiler.
028 * <p> External drivers are responsible for providing the policies; the
029 * role of this class is simply to take a CompilationPlan
030 * and execute it.
031 *
032 * <p> Currently, this class is invoked from four clients:
033 * <ul>
034 *  <li> (1) Command line: ExecuteOptCode
035 *  <li> (2) BootImageWriting: BootImageCompiler.compile (optimizing version)
036 *  <li> (3) RuntimeCompiler: RuntimeCompiler.compile (optimizing version)
037 *  <li> (4) AOS: Compilation threads execute controller plans by invoking
038 *      the opt compiler.
039 * </ul>
040 *
041 * <p> Clients are responsible for ensuring that:
042 *  <ul>
043 *  <li> (1) the VM has been initialized
044 *  <li> (2) Compiler.init has been called before the first opt compilation
045 *  </ul>
046 *
047 * <p> This class is not meant to be instantiated.
048 */
049public final class OptimizingCompiler implements Callbacks.StartupMonitor {
050
051  ////////////////////////////////////////////
052  // Initialization
053  ////////////////////////////////////////////
054  /**
055   * Prepare compiler for use.
056   * @param options options to use for compilations during initialization
057   */
058  public static void init(OptOptions options) {
059    try {
060      if (!(VM.writingBootImage || VM.runningTool || VM.runningVM)) {
061        // Caller failed to ensure that the VM was initialized.
062        throw new OptimizingCompilerException("VM not initialized", true);
063      }
064      // Make a local copy so that some options can be forced off just for the
065      // duration of this initialization step.
066      options = options.dup();
067      options.ESCAPE_SIMPLE_IPA = false;
068
069      initializeStatics();
070
071      // want to be notified when VM boot is done and ready to start application
072      Callbacks.addStartupMonitor(new OptimizingCompiler());
073      isInitialized = true;
074    } catch (OptimizingCompilerException e) {
075      // failures during initialization can't be ignored
076      e.isFatal = true;
077      throw e;
078    } catch (Throwable e) {
079      OptimizingCompilerException oe = new OptimizingCompilerException("Compiler",
080          "untrapped failure during init, " +
081          " Converting to OptimizingCompilerException");
082      oe.initCause(e);
083      throw oe;
084    }
085  }
086
087  /*
088   * callback when application is about to start.
089   */
090  @Override
091  public void notifyStartup() {
092    if (VM.TraceOnStackReplacement) {
093      VM.sysWriteln("Compiler got notified of app ready to begin");
094    }
095    setAppStarted();
096  }
097
098  /**
099   * indicate when the application has started
100   */
101  private static boolean appStarted = false;
102
103  public static synchronized boolean getAppStarted() {
104    return appStarted;
105  }
106
107  public static synchronized void setAppStarted() {
108    appStarted = true;
109  }
110
111  /**
112   * Set up option used while compiling the boot image
113   * @param options the options to set
114   */
115  public static void setBootOptions(OptOptions options) {
116    // Only do guarded inlining if we can use code patches.
117    // Early speculation with method test/class test can result in
118    // bad code that we can't recover from later.
119    options.INLINE_GUARDED = options.guardWithCodePatch();
120
121    // Compute summaries of bootimage methods if we haven't encountered them yet.
122    // Does not handle unimplemented magics very well; disable until
123    // we can get a chance to either implement them on IA32 or fix the
124    // analysis to not be so brittle.
125    // options.SIMPLE_ESCAPE_IPA = true;
126  }
127
128  /**
129   * Call the static init functions for the Compiler subsystems
130   */
131  private static void initializeStatics() {
132    InvokeeThreadLocalContext.init();
133  }
134
135  /**
136   * Prevent instantiation by clients
137   */
138  private OptimizingCompiler() {
139  }
140
141  /**
142   * Has the optimizing compiler been initialized?
143   */
144  private static boolean isInitialized = false;
145
146  /**
147   * @return whether the the optimizing compiler has been initialized
148   */
149  public static boolean isInitialized() {
150    return isInitialized;
151  }
152
153  /**
154   * Reset the optimizing compiler
155   */
156  static void reset() {
157    isInitialized = false;
158  }
159
160  ////////////////////////////////////////////
161  // Public interface for compiling a method
162  ////////////////////////////////////////////
163  /**
164   * Invoke the opt compiler to execute a compilation plan.
165   *
166   * @param cp the compilation plan to be executed
167   * @return the CompiledMethod object that is the result of compilation
168   */
169  public static CompiledMethod compile(CompilationPlan cp) {
170    NormalMethod method = cp.method;
171    OptOptions options = cp.options;
172    checkSupported(method, options);
173    try {
174      printMethodMessage(method, options);
175      IR ir = cp.execute();
176      // if doing analysis only, don't try to return an object
177      if (cp.analyzeOnly || cp.irGeneration) {
178        return null;
179      }
180      // now that we're done compiling, give the specialization
181      // system a chance to eagerly compile any specialized version
182      // that are pending.  TODO: use lazy compilation with specialization.
183      SpecializationDatabase.doDeferredSpecializations();
184      ir.compiledMethod.compileComplete(ir.MIRInfo.machinecode);
185      return ir.compiledMethod;
186    } catch (OptimizingCompilerException e) {
187      throw e;
188    } catch (Throwable e) {
189      fail(e, method);
190      return null;
191    }
192  }
193
194  /**
195   * Debugging aid.
196   * @param what a string message to print
197   */
198  public static void report(String what) {
199    VM.sysWrite(what + '\n');
200  }
201
202  /**
203   * Debugging aid.
204   * @param what a string message to print
205   * @param time a timestamp to print
206   */
207  public static void report(String what, long time) {
208    VM.sysWrite(what);
209    if (what.length() < 8) {
210      VM.sysWrite('\t');
211    }
212    if (what.length() < 16) {
213      VM.sysWrite('\t');
214    }
215    VM.sysWrite('\t' + time + " ms");
216  }
217
218  /**
219   * Debugging aid to be called before printing the IR
220   * @param what a string message to print
221   * @param method the method being compiled
222   */
223  public static void header(String what, NormalMethod method) {
224    System.out.println("********* START OF:  " + what + "   FOR " + method);
225  }
226
227  /**
228   * Debugging aid to be called after printing the IR
229   * @param what a string message to print
230   * @param method the method being compiled
231   */
232  public static void bottom(String what, NormalMethod method) {
233    System.out.println("*********   END OF:  " + what + "   FOR " + method);
234  }
235
236  /**
237   * Prints the IR along with a message.
238   * @param ir the IR to print
239   * @param message the message to print
240   */
241  public static void printInstructions(IR ir, String message) {
242    header(message, ir.method);
243    ir.printInstructions();
244    bottom(message, ir.method);
245  }
246
247  /**
248   * Prints a message of a method name.
249   * @param method the method to print
250   * @param options the print options for the optimizing compiler
251   */
252  private static void printMethodMessage(NormalMethod method, OptOptions options) {
253    if (options.PRINT_METHOD || options.PRINT_INLINE_REPORT) {
254      VM.sysWrite("-methodOpt " +
255                  method.getDeclaringClass() +
256                  ' ' +
257                  method.getName() +
258                  ' ' +
259                  method.getDescriptor() +
260                  " \n");
261    }
262  }
263
264  /**
265   * Abort a compilation with an error.
266   * @param e The exception thrown by a compiler phase
267   * @param method The method being compiled
268   */
269  private static void fail(Throwable e, NormalMethod method) {
270    OptimizingCompilerException optExn =
271        new OptimizingCompilerException("Compiler", "failure during compilation of", method.toString());
272    if (e instanceof OutOfMemoryError) {
273      VM.sysWriteln("Compiler ran out of memory during compilation of ", method.toString());
274      optExn.isFatal = false;
275    } else {
276      VM.sysWriteln("Compiler failure during compilation of ", method.toString());
277      e.printStackTrace();
278    }
279    throw optExn;
280  }
281
282  /**
283   * Checks whether opt compilation of a particular method is supported.
284   * If not, throws a non-fatal run-time exception.
285   *
286   * @param method the method to check
287   * @param options options for printing
288   */
289  private static void checkSupported(NormalMethod method, OptOptions options) {
290    if (method.getDeclaringClass().hasDynamicBridgeAnnotation()) {
291      String msg = "Dynamic Bridge register save protocol not implemented";
292      throw MagicNotImplementedException.EXPECTED(msg);
293    }
294    if (method.getDeclaringClass().hasBridgeFromNativeAnnotation()) {
295      String msg = "Native Bridge prologue not implemented";
296      throw MagicNotImplementedException.EXPECTED(msg);
297    }
298    if (method.hasNoOptCompileAnnotation()) {
299      String msg = "Method throws NoOptCompilePragma";
300      throw MagicNotImplementedException.EXPECTED(msg);
301    }
302    if (options.hasDRIVER_EXCLUDE()) {
303      String name = method.getDeclaringClass().toString() + "." + method.getName();
304      if (options.fuzzyMatchDRIVER_EXCLUDE(name)) {
305        if (!method.getDeclaringClass().hasSaveVolatileAnnotation()) {
306          throw new OptimizingCompilerException("method excluded", false);
307        }
308      }
309    }
310  }
311}