/*
* 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.graal;
import static com.sun.max.platform.Platform.*;
import static com.sun.max.vm.MaxineVM.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;
import com.oracle.max.asm.*;
import com.oracle.max.cri.intrinsics.*;
import com.oracle.max.cri.intrinsics.IntrinsicImpl.Registry;
import com.oracle.max.criutils.*;
import com.oracle.max.graal.compiler.*;
import com.oracle.max.graal.compiler.debug.*;
import com.oracle.max.graal.compiler.phases.*;
import com.oracle.max.graal.compiler.phases.PhasePlan.PhasePosition;
import com.oracle.max.graal.graph.*;
import com.oracle.max.graal.graph.NodeClass.CalcOffset;
import com.oracle.max.graal.nodes.*;
import com.oracle.max.graal.snippets.*;
import com.oracle.max.vm.ext.graal.stubs.*;
import com.oracle.max.vm.ext.maxri.*;
import com.sun.cri.ci.CiCompiler.DebugInfoLevel;
import com.sun.cri.ci.*;
import com.sun.cri.ri.*;
import com.sun.cri.xir.*;
import com.sun.max.annotate.*;
import com.sun.max.platform.*;
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.compiler.deps.*;
import com.sun.max.vm.compiler.target.*;
import com.sun.max.vm.hosted.*;
import com.sun.max.vm.thread.*;
import com.sun.max.vm.type.*;
/**
* Integration of the Graal compiler into Maxine's compilation framework.
*/
public class Graal implements RuntimeCompiler {
/**
* The Maxine specific implementation of the {@linkplain RiRuntime runtime interface} needed by Graal.
*/
public final MaxRuntime runtime = MaxRuntime.runtime();
/**
* The {@linkplain CiTarget target} environment derived from a Maxine {@linkplain Platform platform} description.
*/
public final CiTarget target;
/**
* The Maxine specific implementation of the {@linkplain RiXirGenerator interface} used by Graal
* to incorporate runtime specific details when translating bytecode methods.
*/
public final RiXirGenerator xirGenerator;
/**
* The Graal compiler instance configured for the Maxine runtime.
*/
private GraalCompiler compiler;
private PhasePlan plan;
private static final int DEFAULT_OPT_LEVEL = 3;
@HOSTED_ONLY
public Graal() {
this(new MaxXirGenerator(GraalOptions.PrintXirTemplates), platform().target);
}
@HOSTED_ONLY
public Graal(RiXirGenerator xirGenerator, CiTarget target) {
this.xirGenerator = xirGenerator;
this.target = target;
}
@HOSTED_ONLY
public static boolean optionsRegistered;
@HOSTED_ONLY
public static Map<RiMethod, StructuredGraph> cache = new ConcurrentHashMap<RiMethod, StructuredGraph>();
@Override
public void initialize(Phase phase) {
// TODO(ls) implementation of RiType.fields required to enable escape analysis
GraalOptions.EscapeAnalysis = false;
if (isHosted() && !optionsRegistered) {
runtime.initialize();
GraalOptions.StackShadowPages = VmThread.STACK_SHADOW_PAGES;
VMOptions.addFieldOptions("-G:", GraalOptions.class, null);
VMOptions.addFieldOptions("-ASM:", AsmOptions.class, null);
optionsRegistered = true;
}
if (isHosted() && phase == Phase.HOSTED_COMPILING) {
Registry intrinsicRegistry = new IntrinsicImpl.Registry();
GraalMaxineIntrinsicImplementations.initialize(intrinsicRegistry);
for (Map.Entry<String, IntrinsicImpl> e : intrinsicRegistry) {
GraalIntrinsicImpl impl = (GraalIntrinsicImpl) e.getValue();
if (impl.createMethod != null) {
CompiledPrototype.registerImageInvocationStub(MethodActor.fromJava(impl.createMethod));
}
}
runtime.setIntrinsicRegistry(intrinsicRegistry);
GraalContext context = new GraalContext("Virtual Machine Compiler");
compiler = new GraalCompiler(context, runtime, target, xirGenerator, vm().registerConfigs.compilerStub);
plan = new PhasePlan();
plan.addPhase(PhasePosition.AFTER_PARSING, new FoldPhase(runtime));
plan.addPhase(PhasePosition.AFTER_PARSING, new MaxineIntrinsicsPhase());
plan.addPhase(PhasePosition.AFTER_PARSING, new MustInlinePhase(runtime, cache, null));
// Run forced inlining again because high-level optimizations (such as replacing a final field read by a constant) can open up new opportunities.
plan.addPhase(PhasePosition.HIGH_LEVEL, new FoldPhase(runtime));
plan.addPhase(PhasePosition.HIGH_LEVEL, new MaxineIntrinsicsPhase());
plan.addPhase(PhasePosition.HIGH_LEVEL, new MustInlinePhase(runtime, cache, null));
plan.addPhase(PhasePosition.HIGH_LEVEL, new IntrinsificationPhase(runtime));
plan.addPhase(PhasePosition.HIGH_LEVEL, new WordTypeRewriterPhase());
GraalIntrinsics.installIntrinsics(runtime, target, plan);
NativeStubGraphBuilder.initialize();
// Ensure all the Node classes used by Maxine have their NodeClass instances in the image.
for (ClassActor classActor : ClassRegistry.allBootImageClasses()) {
if (Node.class.isAssignableFrom(classActor.toJava())) {
Class<?> nodeClass = classActor.toJava();
if (!Modifier.isAbstract(nodeClass.getModifiers())) {
NodeClass.get(nodeClass);
}
}
}
}
if (isHosted() && phase == Phase.SERIALIZING_IMAGE) {
// Don't put boot time observers into the boot image
compiler.context.observable.clear();
NodeClass.rescanAllFieldOffsets(new CalcOffset() {
@Override
public long getOffset(Field field) {
return FieldActor.fromJava(field).offset();
}
});
}
if (phase == Phase.STARTING) {
// This makes sure the relevant observers are (re)initialized based on the runtime options
compiler.context.reset();
} else if (phase == Phase.TERMINATING) {
if (GraalOptions.Meter) {
compiler.context.metrics.print();
DebugInfo.dumpStats(Log.out);
}
if (GraalOptions.Time) {
compiler.context.timers.print();
}
}
}
public GraalCompiler compiler() {
if (isHosted() && compiler == null) {
initialize(Phase.HOSTED_COMPILING);
}
return compiler;
}
public final TargetMethod compile(final ClassMethodActor method, boolean isDeopt, boolean install, CiStatistics stats) {
CiTargetMethod compiledMethod;
do {
if (method.compilee().isNative()) {
compiledMethod = compileNativeMethod(method);
} else {
compiledMethod = compiler().compileMethod(method, -1, stats, DebugInfoLevel.FULL, plan);
}
Dependencies deps = Dependencies.validateDependencies(compiledMethod.assumptions());
if (deps != Dependencies.INVALID) {
if (GraalOptions.Time) {
compiler().context.timers.startScope("Install Target Method");
}
MaxTargetMethod maxTargetMethod = new MaxTargetMethod(method, compiledMethod, install);
if (GraalOptions.Time) {
compiler().context.timers.endScope();
}
if (deps != null) {
Dependencies.registerValidatedTarget(deps, maxTargetMethod);
}
TTY.Filter filter = new TTY.Filter(GraalOptions.PrintFilter, method);
try {
printMachineCode(compiledMethod, maxTargetMethod, false);
} finally {
filter.remove();
}
return maxTargetMethod;
}
// Loop back and recompile.
} while(true);
}
protected CiTargetMethod compileNativeMethod(final ClassMethodActor method) {
NativeStubGraphBuilder nativeStubCompiler = new NativeStubGraphBuilder(method);
TTY.Filter filter = new TTY.Filter(GraalOptions.PrintFilter, method);
try {
return compiler.compileMethod(method, nativeStubCompiler.build(), plan);
} finally {
filter.remove();
}
}
void printMachineCode(CiTargetMethod ciTM, MaxTargetMethod maxTM, boolean reentrant) {
if (!GraalOptions.PrintCFGToFile || reentrant || TTY.isSuppressed()) {
return;
}
if (!isHosted() && !isRunning()) {
// Cannot write to file system at runtime until the VM is in the RUNNING phase
return;
}
ByteArrayOutputStream cfgPrinterBuffer = new ByteArrayOutputStream();
CFGPrinter cfgPrinter = new CFGPrinter(cfgPrinterBuffer, null, target(), runtime);
cfgPrinter.printMachineCode(runtime.disassemble(ciTM, maxTM), "After code installation");
cfgPrinter.flush();
OutputStream stream = CompilationPrinter.globalOut();
if (stream != null) {
synchronized (stream) {
try {
stream.write(cfgPrinter.buffer.toByteArray());
stream.flush();
} catch (IOException e) {
TTY.println("WARNING: Error writing CFGPrinter output: %s", e);
}
}
}
}
@Override
public Nature nature() {
return Nature.OPT;
}
@Override
public String toString() {
return getClass().getSimpleName();
}
@Override
public boolean matches(String compilerName) {
return compilerName.equals("Graal");
}
}