/*
* 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.c1x;
import static com.sun.max.platform.Platform.*;
import static com.sun.max.vm.MaxineVM.*;
import java.io.*;
import java.util.*;
import com.oracle.max.asm.*;
import com.oracle.max.criutils.*;
import com.oracle.max.vm.ext.maxri.*;
import com.sun.c1x.*;
import com.sun.c1x.debug.*;
import com.sun.c1x.graph.*;
import com.sun.c1x.ir.*;
import com.sun.c1x.lir.*;
import com.sun.c1x.observer.*;
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.unsafe.*;
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.deopt.*;
import com.sun.max.vm.compiler.deps.*;
import com.sun.max.vm.compiler.target.*;
import com.sun.max.vm.runtime.*;
import com.sun.max.vm.thread.*;
/**
* Integration of the C1X compiler into Maxine's compilation framework.
*/
public class C1X implements RuntimeCompiler {
/**
* The Maxine specific implementation of the {@linkplain RiRuntime runtime interface} needed by C1X.
*/
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 C1X
* to incorporate runtime specific details when translating bytecode methods.
*/
public final RiXirGenerator xirGenerator;
/**
* The C1X compiler instance configured for the Maxine runtime.
*/
private C1XCompiler compiler;
/**
* Set to true once the C1X options are set (to allow subclasses of this scheme to coexist in the same image).
*/
@HOSTED_ONLY
public static boolean optionsRegistered;
private static final int DEFAULT_OPT_LEVEL = Integer.getInteger("max.c1x.optlevel", 3);
public static final VMIntOption optLevelOption = VMOptions.register(new VMIntOption("-C1X:OptLevel=", DEFAULT_OPT_LEVEL,
"Set the optimization level of C1X.") {
@Override
public boolean parseValue(com.sun.max.unsafe.Pointer optionValue) {
boolean result = super.parseValue(optionValue);
if (result) {
C1XOptions.setOptimizationLevel(getValue());
return true;
}
return false;
}
}, MaxineVM.Phase.STARTING);
/**
* A map from option field names to some text describing the meaning and
* usage of the corresponding C1X option.
*/
private static Map<String, String> helpMap;
public static Map<String, String> getHelpMap() {
if (helpMap == null) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("PrintFilter",
"Filter compiler tracing to methods whose fully qualified name " +
"matches <arg>. If <arg> starts with \"~\", then <arg> (without " +
"the \"~\") is interpreted as a regular expression. Otherwise, " +
"<arg> is interpreted as a simple substring.");
map.put("TraceBytecodeParserLevel",
"Trace frontend bytecode parser at level <n> where 0 means no " +
"tracing, 1 means instruction tracing and 2 means instruction " +
"plus frame state tracing.");
map.put("DetailedAsserts",
"Turn on detailed error checking that has a noticeable performance impact.");
map.put("GenSpecialDivChecks",
"Generate code to check for (Integer.MIN_VALUE / -1) or (Long.MIN_VALUE / -1) " +
"instead of detecting these cases via instruction decoding in a trap handler.");
map.put("UseStackMapTableLiveness",
"Use liveness information derived from StackMapTable class file attribute.");
for (String name : map.keySet()) {
try {
C1XOptions.class.getField(name);
} catch (Exception e) {
throw new InternalError("The name '" + name + "' does not denote a field in " + C1XOptions.class);
}
}
helpMap = Collections.unmodifiableMap(map);
}
return helpMap;
}
@HOSTED_ONLY
public C1X() {
this(new MaxXirGenerator(C1XOptions.PrintXirTemplates), platform().target);
}
@HOSTED_ONLY
public C1X(RiXirGenerator xirGenerator, CiTarget target) {
this.xirGenerator = xirGenerator;
this.target = target;
}
@Override
public void initialize(Phase phase) {
if (isHosted() && !optionsRegistered) {
runtime.initialize();
C1XOptions.setOptimizationLevel(optLevelOption.getValue());
C1XOptions.OptIntrinsify = true;
C1XOptions.StackShadowPages = VmThread.STACK_SHADOW_PAGES;
VMOptions.addFieldOptions("-C1X:", C1XOptions.class, getHelpMap());
VMOptions.addFieldOptions("-ASM:", AsmOptions.class, null);
// Speculative opts (UseAssumptions) are the default in the boot image as they are limited
// to VM classes, which form a closed world.
optionsRegistered = true;
}
if (isHosted() && phase == Phase.HOSTED_COMPILING) {
// Temporary work-around to support the @ACCESSOR annotation.
GraphBuilder.setAccessor(ClassActor.fromJava(Accessor.class));
compiler = new C1XCompiler(runtime, target, xirGenerator, vm().registerConfigs.compilerStub);
compiler.addCompilationObserver(new WordTypeRewriterObserver());
MaxineIntrinsicImplementations.initialize(compiler.intrinsicRegistry);
}
if (phase == Phase.STARTING) {
// Speculative opts are ok provided the compilation broker can handle deopt
C1XOptions.UseAssumptions = vm().compilationBroker.isDeoptSupported() && Deoptimization.UseDeopt;
} else if (phase == Phase.TERMINATING) {
if (C1XOptions.PrintMetrics) {
C1XMetrics.print();
DebugInfo.dumpStats(Log.out);
}
if (C1XOptions.PrintTimers) {
C1XTimers.print();
}
}
}
private static class WordTypeRewriterObserver implements CompilationObserver {
@Override
public void compilationEvent(CompilationEvent event) {
if (event.getLabel() == CompilationEvent.AFTER_PARSING) {
new WordTypeRewriter().apply(event.getCompilation());
} else if (event.getLabel() == CompilationEvent.AFTER_REGISTER_ALLOCATION && ((MethodActor) event.getCompilation().method).isTemplate()) {
processTemplate(event.getCompilation());
}
}
@Override
public void compilationStarted(CompilationEvent event) { }
@Override
public void compilationFinished(CompilationEvent event) { }
}
/**
* T1X templates are compiled in a special way without a return instruction. The control flow just falls through to the
* next template. Therefore, we have to make sure that 1) the template has only one exit point, and 2) that this exit point
* is the last block.
* Condition 1) can be ensured by careful coding of the templates, i.e., having no return statement in the middle of the
* template. If one of the fatal errors below triggers, this condition is violated.
* Condition 2) is guaranteed by this code: we move the return block to the end of the block list.
*
* Note that we currently do not check that templates do not have stubs (which would be at the end of the method).
*/
private static void processTemplate(C1XCompilation compilation) {
List<BlockBegin> code = compilation.hir().linearScanOrder();
BlockBegin returnBlock = null;
for (int i = code.size() - 1; i >= 0; i--) {
BlockBegin block = code.get(i);
LIROpcode lastOp = block.lir().at(block.lir().length() - 1).code;
if (block.numberOfSux() == 0 && lastOp != LIROpcode.Unwind) {
if (returnBlock != null) {
FatalError.unexpected("Template has more than one return instruction");
}
returnBlock = block;
code.remove(i);
}
}
if (returnBlock == null) {
FatalError.unexpected("Template has no return instruction");
}
code.add(returnBlock);
}
public C1XCompiler compiler() {
if (isHosted() && compiler == null) {
FatalError.unexpected("xxxx");
initialize(Phase.HOSTED_COMPILING);
}
return compiler;
}
public TargetMethod compile(final ClassMethodActor method, boolean isDeopt, boolean install, CiStatistics stats) {
CiTargetMethod compiledMethod;
do {
DebugInfoLevel debugInfoLevel = method.isTemplate() ? DebugInfoLevel.REF_MAPS : DebugInfoLevel.FULL;
compiledMethod = compiler().compileMethod(method, -1, stats, debugInfoLevel).targetMethod();
Dependencies deps = Dependencies.validateDependencies(compiledMethod.assumptions());
if (deps != Dependencies.INVALID) {
if (C1XOptions.PrintTimers) {
C1XTimers.INSTALL.start();
}
MaxTargetMethod maxTargetMethod = new MaxTargetMethod(method, compiledMethod, install);
if (C1XOptions.PrintTimers) {
C1XTimers.INSTALL.stop();
}
if (deps != null) {
Dependencies.registerValidatedTarget(deps, maxTargetMethod);
}
TTY.Filter filter = new TTY.Filter(C1XOptions.PrintFilter, method);
try {
printMachineCode(compiledMethod, maxTargetMethod, false);
} finally {
filter.remove();
}
return maxTargetMethod;
}
// Loop back and recompile.
} while(true);
}
void printMachineCode(CiTargetMethod ciTM, MaxTargetMethod maxTM, boolean reentrant) {
if (!C1XOptions.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, target());
cfgPrinter.printMachineCode(runtime.disassemble(ciTM, maxTM), "After code installation");
cfgPrinter.flush();
OutputStream cfgFileStream = CompilationPrinter.globalOut();
if (cfgFileStream != null) {
synchronized (cfgFileStream) {
try {
cfgFileStream.write(cfgPrinterBuffer.toByteArray());
} catch (IOException e) {
TTY.println("WARNING: Error writing CFGPrinter output for %s to disk: %s", maxTM.classMethodActor, e.getMessage());
}
}
}
}
@Override
public Nature nature() {
return Nature.OPT;
}
@Override
public String toString() {
return getClass().getSimpleName();
}
@Override
public boolean matches(String compilerName) {
return compilerName.equals("C1X");
}
}