/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.compilers.opt;
import org.jikesrvm.VM;
import org.jikesrvm.VM_Callbacks;
import org.jikesrvm.classloader.VM_Atom;
import org.jikesrvm.classloader.VM_BootstrapClassLoader;
import org.jikesrvm.classloader.VM_Class;
import org.jikesrvm.classloader.VM_Method;
import org.jikesrvm.classloader.VM_NormalMethod;
import org.jikesrvm.classloader.VM_TypeReference;
import org.jikesrvm.compilers.common.VM_CompiledMethod;
import org.jikesrvm.compilers.opt.ir.OPT_IR;
//TODO - Deal with Subarch
/**
* <p> The main driver of the OPT_Compiler.
* <p> External drivers are responsible for providing the policies; the
* role of this class is simply to take a OPT_CompilationPlan
* and execute it.
*
* Currently, this class is invoked from four clients:
* <ul>
* <li> (1) Command line: ExecuteOptCode
* <li> (2) BootImageWriting: VM_BootImageCompiler.compile (optimizing version)
* <li> (3) RuntimeCompiler: VM_RuntimeCompiler.compile (optimizing version)
* <li> (4) AOS: Compilation threads execute controller plans by invoking
* the opt compiler.
* </ul>
*
* <p> Clients are responsible for ensuring that:
* <ul>
* <li> (1) the VM has been initialized
* <li> (2) OPT_Compiler.init has been called before the first opt compilation
* </ul>
*
* <p> This class is not meant to be instantiated.
*/
public final class OPT_Compiler implements VM_Callbacks.StartupMonitor {
////////////////////////////////////////////
// Initialization
////////////////////////////////////////////
/**
* Prepare compiler for use.
* @param options options to use for compilations during initialization
*/
public static void init(OPT_Options options) {
try {
if (!(VM.writingBootImage || VM.runningTool || VM.runningVM)) {
// Caller failed to ensure that the VM was initialized.
throw new OPT_OptimizingCompilerException("VM not initialized", true);
}
// Make a local copy so that some options can be forced off just for the
// duration of this initialization step.
options = options.dup();
options.SIMPLE_ESCAPE_IPA = false;
initializeStatics();
if (VM.runningVM) {
// Make sure that VM_OptSaveVolatile.java is opt
// compiled (to get special prologues/epilogues)
// TODO: This could be phased out as the new DynamicBridge
// magic comes on line.
loadSpecialClass("Lorg/jikesrvm/compilers/opt/VM_OptSaveVolatile;", options);
}
// want to be notified when VM boot is done and ready to start application
VM_Callbacks.addStartupMonitor(new OPT_Compiler());
isInitialized = true;
} catch (OPT_OptimizingCompilerException e) {
// failures during initialization can't be ignored
e.isFatal = true;
throw e;
} catch (Throwable e) {
VM.sysWriteln(e.toString());
throw new OPT_OptimizingCompilerException("OPT_Compiler",
"untrapped failure during init, " +
" Converting to OPT_OptimizingCompilerException");
}
}
/*
* callback when application is about to start.
*/
public void notifyStartup() {
if (VM.TraceOnStackReplacement) {
VM.sysWriteln("OPT_Compiler got notified of app ready to begin");
}
setAppStarted();
}
/**
* indicate when the application has started
*/
private static boolean appStarted = false;
public static synchronized boolean getAppStarted() { return appStarted; }
public static synchronized void setAppStarted() { appStarted = true; }
/**
* Set up option used while compiling the boot image
* @param options the options to set
*/
public static void setBootOptions(OPT_Options options) {
// Only do guarded inlining if we can use code patches.
// Early speculation with method test/class test can result in
// bad code that we can't recover from later.
options.GUARDED_INLINE = options.guardWithCodePatch();
// Compute summaries of bootimage methods if we haven't encountered them yet.
// Does not handle unimplemented magics very well; disable until
// we can get a chance to either implement them on IA32 or fix the
// analysis to not be so brittle.
// options.SIMPLE_ESCAPE_IPA = true;
}
/**
* Load a class which must be compiled by the opt compiler in a special way
* @param klassName the class to load
* @param options compiler options for compiling the class
*/
private static void loadSpecialClass(String klassName, OPT_Options options) {
VM_TypeReference tRef =
VM_TypeReference.findOrCreate(VM_BootstrapClassLoader.getBootstrapClassLoader(),
VM_Atom.findOrCreateAsciiAtom(klassName));
VM_Class klass = (VM_Class) tRef.peekType();
for (VM_Method meth : klass.getDeclaredMethods()) {
if (meth.isClassInitializer()) {
continue;
}
if (!meth.isCompiled(false) || meth.getCurrentCompiledMethod(false).getCompilerType() != VM_CompiledMethod.OPT) {
OPT_CompilationPlan cp =
new OPT_CompilationPlan((VM_NormalMethod) meth,
OPT_OptimizationPlanner.createOptimizationPlan(options),
null,
options);
meth.replaceCompiledMethod(compile(cp), false);
}
}
}
public static void preloadSpecialClass(OPT_Options options) {
String klassName = "L" + options.PRELOAD_CLASS + ";";
if (options.PRELOAD_AS_BOOT) {
setBootOptions(options);
// Make a local copy so that some options can be altered to mimic options
// during boot build
options = options.dup();
}
try {
loadSpecialClass(klassName, options);
} catch (Throwable e) {
e.printStackTrace();
VM.sysWrite("Ignoring failure of preloadSpecialClass of " + klassName + "\n");
}
}
/**
* Call the static init functions for the OPT_Compiler subsystems
*/
private static void initializeStatics() {
OPT_InvokeeThreadLocalContext.init();
}
/**
* Prevent instantiation by clients
*/
private OPT_Compiler() {
}
/**
* Has the optimizing compiler been initialized?
*/
private static boolean isInitialized = false;
/**
* Has the optimizing compiler been initialized?
*/
public static boolean isInitialized() {
return isInitialized;
}
/**
* Reset the optimizing compiler
*/
static void reset() {
isInitialized = false;
}
////////////////////////////////////////////
// Public interface for compiling a method
////////////////////////////////////////////
/**
* Invoke the opt compiler to execute a compilation plan.
*
* @param cp the compilation plan to be executed
* @return the VM_CompiledMethod object that is the result of compilation
*/
public static VM_CompiledMethod compile(OPT_CompilationPlan cp) {
VM_NormalMethod method = cp.method;
OPT_Options options = cp.options;
checkSupported(method, options);
try {
printMethodMessage(method, options);
OPT_IR ir = cp.execute();
// if doing analysis only, don't try to return an object
if (cp.analyzeOnly || cp.irGeneration) {
return null;
}
// now that we're done compiling, give the specialization
// system a chance to eagerly compile any specialized version
// that are pending. TODO: use lazy compilation with specialization.
OPT_SpecializationDatabase.doDeferredSpecializations();
ir.compiledMethod.compileComplete(ir.MIRInfo.machinecode);
return ir.compiledMethod;
} catch (OPT_OptimizingCompilerException e) {
throw e;
} catch (Throwable e) {
fail(e, method);
return null;
}
}
/**
* Debugging aid.
* @param what a string message to print
*/
public static void report(String what) {
VM.sysWrite(what + '\n');
}
/**
* Debugging aid.
* @param what a string message to print
* @param time a timestamp to print
*/
public static void report(String what, long time) {
VM.sysWrite(what);
if (what.length() < 8) {
VM.sysWrite('\t');
}
if (what.length() < 16) {
VM.sysWrite('\t');
}
VM.sysWrite('\t' + time + " ms");
}
/**
* Debugging aid to be called before printing the IR
* @param what a string message to print
* @param method the method being compiled
*/
public static void header(String what, VM_NormalMethod method) {
System.out.println("********* START OF: " + what + " FOR " + method);
}
/**
* Debugging aid to be called after printing the IR
* @param what a string message to print
* @param method the method being compiled
*/
public static void bottom(String what, VM_NormalMethod method) {
System.out.println("********* END OF: " + what + " FOR " + method);
}
/**
* Print the IR along with a message
* @param ir
* @param message
*/
public static void printInstructions(OPT_IR ir, String message) {
header(message, ir.method);
ir.printInstructions();
bottom(message, ir.method);
}
/**
* Print a message of a method name
* @param method
* @param options
*/
private static void printMethodMessage(VM_NormalMethod method, OPT_Options options) {
if (options.PRINT_METHOD || options.PRINT_INLINE_REPORT) {
VM.sysWrite("-methodOpt " +
method.getDeclaringClass() +
' ' +
method.getName() +
' ' +
method.getDescriptor() +
" \n");
}
}
/**
* Abort a compilation with an error.
* @param e The exception thrown by a compiler phase
* @param method The method being compiled
*/
private static void fail(Throwable e, VM_NormalMethod method) {
OPT_OptimizingCompilerException optExn =
new OPT_OptimizingCompilerException("OPT_Compiler", "failure during compilation of", method.toString());
if (e instanceof OutOfMemoryError) {
VM.sysWriteln("OPT_Compiler ran out of memory during compilation of ", method.toString());
optExn.isFatal = false;
} else {
VM.sysWriteln("OPT_Compiler failure during compilation of ", method.toString());
e.printStackTrace();
}
throw optExn;
}
/**
* Check whether opt compilation of a particular method is supported.
* If not, throw a non-fatal run-time exception.
*/
private static void checkSupported(VM_NormalMethod method, OPT_Options options) {
if (method.getDeclaringClass().hasDynamicBridgeAnnotation()) {
String msg = "Dynamic Bridge register save protocol not implemented";
throw OPT_MagicNotImplementedException.EXPECTED(msg);
}
if (method.getDeclaringClass().hasBridgeFromNativeAnnotation()) {
String msg = "Native Bridge prologue not implemented";
throw OPT_MagicNotImplementedException.EXPECTED(msg);
}
if (method.hasNoOptCompileAnnotation()) {
String msg = "Method throws NoOptCompilePragma";
throw OPT_MagicNotImplementedException.EXPECTED(msg);
}
if (options.hasEXCLUDE()) {
String name = method.getDeclaringClass().toString() + "." + method.getName();
if (options.fuzzyMatchEXCLUDE(name)) {
if (!method.getDeclaringClass().hasSaveVolatileAnnotation()) {
throw new OPT_OptimizingCompilerException("method excluded", false);
}
}
}
}
}