/*
* Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.max.vm.ext.maxri;
import java.io.*;
import java.util.*;
import com.oracle.max.asm.*;
import com.sun.cri.ci.*;
import com.sun.max.*;
import com.sun.max.config.*;
import com.sun.max.io.*;
import com.sun.max.lang.*;
import com.sun.max.program.*;
import com.sun.max.program.option.*;
import com.sun.max.test.*;
import com.sun.max.vm.*;
import com.sun.max.vm.MaxineVM.Phase;
import com.sun.max.vm.actor.holder.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.compiler.*;
import com.sun.max.vm.hosted.*;
import com.sun.max.vm.profile.*;
import com.sun.max.vm.reflection.*;
import com.sun.max.vm.type.*;
import com.sun.max.vm.value.*;
/**
* A harness to run a {@linkplain RuntimeCompiler compiler} offline.
*/
public class Compile {
private static final OptionSet options = new OptionSet(false);
private static final Map<String, String> compilerAliases = RuntimeCompiler.aliases;
private static final String compilerAliasNames = compilerAliases.keySet().toString().replaceAll("[\\[\\]]", "");
private static final Option<String> compilerOption = options.newStringOption("c", null,
"The alias of the compiler to use " + compilerAliases.keySet() + " chosen from the following list: " + compilerAliasNames);
private static final Option<Integer> traceOption = options.newIntegerOption("trace", 0,
"Set the tracing level of the Maxine VM and runtime.");
private static final Option<Integer> verboseOption = options.newIntegerOption("verbose", 1,
"Set the verbosity level of the testing framework.");
private static final Option<Boolean> reflectionStubsOption = options.newBooleanOption("reflect", false,
"Generate and compile reflection stubs for the methods.");
private static final Option<Boolean> failFastOption = options.newBooleanOption("fail-fast", false,
"Stop compilation upon the first bailout.");
private static final Option<Boolean> helpOption = options.newBooleanOption("help", false,
"Show help message and exit.");
private static final Option<Boolean> profOption = options.newBooleanOption("prof", true,
"Emit method profiling in baseline compiled methods.");
static void addFieldOptions(String prefix, String optionsClassName) {
Class optionsClass = Classes.forName(optionsClassName);
try {
Map<String, String> m = null;
try {
m = Utils.cast(Classes.getDeclaredMethod(optionsClass, "getHelpMap").invoke(null));
} catch (NoSuchMethodError e) {
}
options.addFieldOptions(optionsClass, prefix, m);
} catch (Throwable e) {
e.printStackTrace();
}
}
static {
// add all the compiler options
addFieldOptions("G", "com.oracle.max.graal.compiler.GraalOptions");
addFieldOptions("C1X", "com.sun.c1x.C1XOptions");
addFieldOptions("T1X", "com.oracle.max.vm.ext.t1x.T1XOptions");
options.addFieldOptions(AsmOptions.class, "ASM", null);
}
private static PrintStream out = System.out;
private static String[] expandArguments(String[] args) throws IOException {
List<String> result = new ArrayList<String>(args.length);
for (String arg : args) {
if (arg.charAt(0) == '@') {
File file = new File(arg.substring(1));
result.addAll(Files.readLines(file));
} else {
result.add(arg);
}
}
return result.toArray(new String[result.size()]);
}
/**
* Check alias (short form of compiler name).
* @param compilerName
* @return full (aliased) class name or {@code compilerName} if not found
*/
private static String getCompilerClassname(String compilerName) {
if (compilerName == null) {
return null;
}
String compilerClassname = compilerAliases.get(compilerName);
return compilerClassname == null ? compilerName : compilerClassname;
}
public static void main(String[] args) throws IOException {
args = VMOption.extractVMArgs(args);
VMConfigurator vmConfigurator = new VMConfigurator(options);
options.parseArguments(args);
options.setValuesAgain();
final String[] arguments = expandArguments(options.getArguments());
if (helpOption.getValue()) {
options.printHelp(System.out, 80);
return;
}
String compilerName = getCompilerClassname(compilerOption.getValue());
if (compilerName == null) {
System.out.println("Must specify compiler to use with the -" + compilerOption + " option");
System.out.println("Valid values are: " + compilerAliasNames + " or fully qualified class name");
return;
}
if (compilerName.contains("T1X")) {
RuntimeCompiler.baselineCompilerOption.setValue(compilerName);
} else {
RuntimeCompiler.optimizingCompilerOption.setValue(compilerName);
}
Trace.on(traceOption.getValue());
if (profOption.getValue()) {
MethodInstrumentation.enable(500);
}
vmConfigurator.create();
// create the prototype
if (verboseOption.getValue() > 0) {
out.print("Initializing Java prototype... ");
}
JavaPrototype.initialize(false);
if (verboseOption.getValue() > 0) {
out.println("done");
}
CompilationBroker cb = MaxineVM.vm().compilationBroker;
final RuntimeCompiler compiler = compilerName.contains("T1X") ? cb.baselineCompiler : cb.optimizingCompiler;
cb.optimizingCompiler.initialize(Phase.HOSTED_COMPILING);
if (cb.optimizingCompiler != cb.baselineCompiler && compiler == cb.baselineCompiler) {
cb.baselineCompiler.initialize(Phase.HOSTED_COMPILING);
}
final Classpath classpath = Classpath.fromSystem();
// final List<MethodActor> methods = new MyMethodFinder().find(arguments, classpath, HostedVMClassLoader.HOSTED_VM_CLASS_LOADER, null);
final List<MethodActor> methods = new MyMethodFinder().find(arguments, classpath, Compile.class.getClassLoader(), null);
final ProgressPrinter progress = new ProgressPrinter(out, methods.size(), verboseOption.getValue(), false);
if (reflectionStubsOption.getValue()) {
addReflectionStubs(methods);
}
doCompile(compiler, methods, progress);
if (verboseOption.getValue() > 0) {
progress.report();
}
compiler.initialize(Phase.TERMINATING);
// Non-zero exit code indicates number of failures
System.exit(progress.failed());
}
protected static void addReflectionStubs(final List<MethodActor> methods) {
ArrayList<MethodActor> stubMethods = new ArrayList<MethodActor>();
for (MethodActor m : methods) {
try {
if (!m.isInstanceInitializer()) {
InvocationStub javaStub = InvocationStub.newMethodStub(m.toJava(), Boxing.JAVA);
InvocationStub valueStub = InvocationStub.newMethodStub(m.toJava(), Boxing.VALUE);
stubMethods.add(ClassRegistry.findMethod(javaStub.getClass(), "invoke", Object.class, Object[].class));
stubMethods.add(ClassRegistry.findMethod(valueStub.getClass(), "invoke", Value[].class));
} else {
InvocationStub javaStub = InvocationStub.newConstructorStub(m.toJavaConstructor(), null, Boxing.JAVA);
InvocationStub valueStub = InvocationStub.newConstructorStub(m.toJavaConstructor(), null, Boxing.VALUE);
stubMethods.add(ClassRegistry.findMethod(javaStub.getClass(), "newInstance", Object[].class));
stubMethods.add(ClassRegistry.findMethod(valueStub.getClass(), "newInstance", Value[].class));
}
} catch (Throwable e) {
out.println("Error adding reflectionstubs for " + m + ": " + e);
}
}
methods.addAll(stubMethods);
}
private static void doCompile(RuntimeCompiler compiler, List<MethodActor> methods, ProgressPrinter progress) {
// compile all the methods and report progress
for (MethodActor methodActor : methods) {
progress.begin(methodActor.toString());
Throwable error = compile(compiler, methodActor, true);
if (error == null) {
progress.pass();
} else {
progress.fail(error.toString());
if (failFastOption.getValue()) {
out.println("");
break;
}
}
}
}
private static Throwable compile(RuntimeCompiler compiler, MethodActor method, boolean printBailout) {
// compile a single method
ClassMethodActor classMethodActor = (ClassMethodActor) method;
Throwable thrown = null;
CiStatistics stats = new CiStatistics();
try {
compiler.compile(classMethodActor, false, true, stats);
} catch (Throwable t) {
thrown = t;
}
if (printBailout && thrown != null) {
out.println("");
out.println(method);
thrown.printStackTrace();
}
return thrown;
}
static class MyMethodFinder extends MethodFinder {
HashSet<PatternMatcher> patterns;
@Override
protected void addClassToProcess(PatternMatcher classNamePattern, String className, List<String> matchingClasses) {
boolean added = patterns.add(classNamePattern);
if (added && verboseOption.getValue() > 0) {
out.print("Classes " + classNamePattern.type + " '" + classNamePattern.pattern + "'... ");
}
super.addClassToProcess(classNamePattern, className, matchingClasses);
}
@Override
public List<MethodActor> find(String[] patterns, Classpath classpath, ClassLoader classLoader, List<Throwable> nonFatalErrors) {
this.patterns = new HashSet<PatternMatcher>();
return super.find(patterns, classpath, classLoader, nonFatalErrors);
}
private static boolean isCompilable(MethodActor method) {
return method instanceof ClassMethodActor && !method.isAbstract() && !method.isIntrinsic();
}
@Override
protected void addMethod(MethodActor method, List<MethodActor> methods) {
if (isCompilable(method)) {
super.addMethod(method, methods);
if ((methods.size() % 1000) == 0 && verboseOption.getValue() >= 1) {
out.print('.');
}
}
}
@Override
protected ClassActor getClassActor(Class< ? > javaClass) {
ClassActor classActor = null;
try {
classActor = ClassActor.fromJava(javaClass);
BootImagePackage bootImagePackage = BootImagePackage.fromClass(javaClass);
if (bootImagePackage != null && bootImagePackage.name().contains(".prototype")) {
return null;
}
} catch (Throwable t) {
// do nothing.
}
return classActor;
}
}
}