blob: 26541c815739a947954061d603235595a9855405 [file] [log] [blame]
/*
* Copyright (c) 2009, 2016, 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 org.graalvm.compiler.core;
import static org.graalvm.compiler.core.GraalCompilerOptions.EmitLIRRepeatCount;
import static org.graalvm.compiler.core.common.GraalOptions.UseGraalInstrumentation;
import static org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality.Optional;
import java.util.Collection;
import java.util.List;
import org.graalvm.compiler.code.CompilationResult;
import org.graalvm.compiler.core.LIRGenerationPhase.LIRGenerationContext;
import org.graalvm.compiler.core.common.alloc.ComputeBlockOrder;
import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.core.common.util.CompilationAlarm;
import org.graalvm.compiler.core.target.Backend;
import org.graalvm.compiler.debug.Debug;
import org.graalvm.compiler.debug.Debug.Scope;
import org.graalvm.compiler.debug.DebugCloseable;
import org.graalvm.compiler.debug.DebugCounter;
import org.graalvm.compiler.debug.DebugTimer;
import org.graalvm.compiler.debug.internal.method.MethodMetricsRootScopeInfo;
import org.graalvm.compiler.lir.BailoutAndRestartBackendException;
import org.graalvm.compiler.lir.LIR;
import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory;
import org.graalvm.compiler.lir.framemap.FrameMap;
import org.graalvm.compiler.lir.framemap.FrameMapBuilder;
import org.graalvm.compiler.lir.gen.LIRGenerationResult;
import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
import org.graalvm.compiler.lir.phases.AllocationPhase.AllocationContext;
import org.graalvm.compiler.lir.phases.LIRSuites;
import org.graalvm.compiler.lir.phases.PostAllocationOptimizationPhase.PostAllocationOptimizationContext;
import org.graalvm.compiler.lir.phases.PreAllocationOptimizationPhase.PreAllocationOptimizationContext;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
import org.graalvm.compiler.nodes.cfg.Block;
import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
import org.graalvm.compiler.options.OptionValue.OverrideScope;
import org.graalvm.compiler.phases.OptimisticOptimizations;
import org.graalvm.compiler.phases.PhaseSuite;
import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
import org.graalvm.compiler.phases.common.instrumentation.ExtractInstrumentationPhase;
import org.graalvm.compiler.phases.schedule.SchedulePhase;
import org.graalvm.compiler.phases.tiers.HighTierContext;
import org.graalvm.compiler.phases.tiers.LowTierContext;
import org.graalvm.compiler.phases.tiers.MidTierContext;
import org.graalvm.compiler.phases.tiers.Suites;
import org.graalvm.compiler.phases.tiers.TargetProvider;
import org.graalvm.compiler.phases.util.Providers;
import jdk.vm.ci.code.RegisterConfig;
import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.code.site.ConstantReference;
import jdk.vm.ci.code.site.DataPatch;
import jdk.vm.ci.meta.Assumptions;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ProfilingInfo;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.VMConstant;
/**
* Static methods for orchestrating the compilation of a {@linkplain StructuredGraph graph}.
*/
public class GraalCompiler {
private static final DebugTimer CompilerTimer = Debug.timer("GraalCompiler");
private static final DebugTimer FrontEnd = Debug.timer("FrontEnd");
private static final DebugTimer BackEnd = Debug.timer("BackEnd");
private static final DebugTimer EmitLIR = Debug.timer("EmitLIR");
private static final DebugTimer EmitCode = Debug.timer("EmitCode");
private static final LIRGenerationPhase LIR_GENERATION_PHASE = new LIRGenerationPhase();
/**
* Encapsulates all the inputs to a {@linkplain GraalCompiler#compile(Request) compilation}.
*/
public static class Request<T extends CompilationResult> {
public final StructuredGraph graph;
public final ResolvedJavaMethod installedCodeOwner;
public final Providers providers;
public final Backend backend;
public final PhaseSuite<HighTierContext> graphBuilderSuite;
public final OptimisticOptimizations optimisticOpts;
public final ProfilingInfo profilingInfo;
public final Suites suites;
public final LIRSuites lirSuites;
public final T compilationResult;
public final CompilationResultBuilderFactory factory;
/**
* @param graph the graph to be compiled
* @param installedCodeOwner the method the compiled code will be associated with once
* installed. This argument can be null.
* @param providers
* @param backend
* @param graphBuilderSuite
* @param optimisticOpts
* @param profilingInfo
* @param suites
* @param lirSuites
* @param compilationResult
* @param factory
*/
public Request(StructuredGraph graph, ResolvedJavaMethod installedCodeOwner, Providers providers, Backend backend, PhaseSuite<HighTierContext> graphBuilderSuite,
OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, Suites suites, LIRSuites lirSuites, T compilationResult, CompilationResultBuilderFactory factory) {
this.graph = graph;
this.installedCodeOwner = installedCodeOwner;
this.providers = providers;
this.backend = backend;
this.graphBuilderSuite = graphBuilderSuite;
this.optimisticOpts = optimisticOpts;
this.profilingInfo = profilingInfo;
this.suites = suites;
this.lirSuites = lirSuites;
this.compilationResult = compilationResult;
this.factory = factory;
}
/**
* Executes this compilation request.
*
* @return the result of the compilation
*/
public T execute() {
return GraalCompiler.compile(this);
}
}
/**
* Requests compilation of a given graph.
*
* @param graph the graph to be compiled
* @param installedCodeOwner the method the compiled code will be associated with once
* installed. This argument can be null.
* @return the result of the compilation
*/
public static <T extends CompilationResult> T compileGraph(StructuredGraph graph, ResolvedJavaMethod installedCodeOwner, Providers providers, Backend backend,
PhaseSuite<HighTierContext> graphBuilderSuite, OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, Suites suites, LIRSuites lirSuites, T compilationResult,
CompilationResultBuilderFactory factory) {
return compile(new Request<>(graph, installedCodeOwner, providers, backend, graphBuilderSuite, optimisticOpts, profilingInfo, suites, lirSuites, compilationResult, factory));
}
/**
* Services a given compilation request.
*
* @return the result of the compilation
*/
@SuppressWarnings("try")
public static <T extends CompilationResult> T compile(Request<T> r) {
try (Scope s = MethodMetricsRootScopeInfo.createRootScopeIfAbsent(r.installedCodeOwner);
CompilationAlarm alarm = CompilationAlarm.trackCompilationPeriod()) {
assert !r.graph.isFrozen();
try (Scope s0 = Debug.scope("GraalCompiler", r.graph, r.providers.getCodeCache()); DebugCloseable a = CompilerTimer.start()) {
emitFrontEnd(r.providers, r.backend, r.graph, r.graphBuilderSuite, r.optimisticOpts, r.profilingInfo, r.suites);
emitBackEnd(r.graph, null, r.installedCodeOwner, r.backend, r.compilationResult, r.factory, null, r.lirSuites);
} catch (Throwable e) {
throw Debug.handle(e);
}
return r.compilationResult;
}
}
/**
* Builds the graph, optimizes it.
*/
@SuppressWarnings("try")
public static void emitFrontEnd(Providers providers, TargetProvider target, StructuredGraph graph, PhaseSuite<HighTierContext> graphBuilderSuite, OptimisticOptimizations optimisticOpts,
ProfilingInfo profilingInfo, Suites suites) {
try (Scope s = Debug.scope("FrontEnd"); DebugCloseable a = FrontEnd.start()) {
HighTierContext highTierContext = new HighTierContext(providers, graphBuilderSuite, optimisticOpts);
if (graph.start().next() == null) {
graphBuilderSuite.apply(graph, highTierContext);
new DeadCodeEliminationPhase(Optional).apply(graph);
} else {
Debug.dump(Debug.INFO_LOG_LEVEL, graph, "initial state");
}
if (UseGraalInstrumentation.getValue()) {
new ExtractInstrumentationPhase().apply(graph, highTierContext);
}
suites.getHighTier().apply(graph, highTierContext);
graph.maybeCompress();
MidTierContext midTierContext = new MidTierContext(providers, target, optimisticOpts, profilingInfo);
suites.getMidTier().apply(graph, midTierContext);
graph.maybeCompress();
LowTierContext lowTierContext = new LowTierContext(providers, target);
suites.getLowTier().apply(graph, lowTierContext);
Debug.dump(Debug.BASIC_LOG_LEVEL, graph.getLastSchedule(), "Final HIR schedule");
} catch (Throwable e) {
throw Debug.handle(e);
}
}
@SuppressWarnings("try")
public static <T extends CompilationResult> void emitBackEnd(StructuredGraph graph, Object stub, ResolvedJavaMethod installedCodeOwner, Backend backend, T compilationResult,
CompilationResultBuilderFactory factory, RegisterConfig registerConfig, LIRSuites lirSuites) {
try (Scope s = Debug.scope("BackEnd", graph.getLastSchedule()); DebugCloseable a = BackEnd.start()) {
// Repeatedly run the LIR code generation pass to improve statistical profiling results.
for (int i = 0; i < EmitLIRRepeatCount.getValue(); i++) {
SchedulePhase dummySchedule = new SchedulePhase();
dummySchedule.apply(graph);
emitLIR(backend, graph, stub, registerConfig, lirSuites);
}
LIRGenerationResult lirGen = null;
lirGen = emitLIR(backend, graph, stub, registerConfig, lirSuites);
try (Scope s2 = Debug.scope("CodeGen", lirGen, lirGen.getLIR())) {
int bytecodeSize = graph.method() == null ? 0 : graph.getBytecodeSize();
compilationResult.setHasUnsafeAccess(graph.hasUnsafeAccess());
emitCode(backend, graph.getAssumptions(), graph.method(), graph.getMethods(), graph.getFields(), bytecodeSize, lirGen, compilationResult, installedCodeOwner, factory);
} catch (Throwable e) {
throw Debug.handle(e);
}
} catch (Throwable e) {
throw Debug.handle(e);
}
}
@SuppressWarnings("try")
public static LIRGenerationResult emitLIR(Backend backend, StructuredGraph graph, Object stub, RegisterConfig registerConfig, LIRSuites lirSuites) {
OverrideScope overrideScope = null;
LIRSuites lirSuites0 = lirSuites;
while (true) {
try (OverrideScope scope = overrideScope) {
return emitLIR0(backend, graph, stub, registerConfig, lirSuites0);
} catch (BailoutAndRestartBackendException e) {
if (BailoutAndRestartBackendException.Options.LIRUnlockBackendRestart.getValue() && e.shouldRestart()) {
overrideScope = e.getOverrideScope();
lirSuites0 = e.updateLIRSuites(lirSuites);
if (lirSuites0 != null) {
continue;
}
}
/*
* The BailoutAndRestartBackendException is permanent. If restart fails or is
* disabled we throw the bailout.
*/
throw e;
}
}
}
@SuppressWarnings("try")
private static LIRGenerationResult emitLIR0(Backend backend, StructuredGraph graph, Object stub, RegisterConfig registerConfig, LIRSuites lirSuites) {
try (Scope ds = Debug.scope("EmitLIR"); DebugCloseable a = EmitLIR.start()) {
ScheduleResult schedule = graph.getLastSchedule();
Block[] blocks = schedule.getCFG().getBlocks();
Block startBlock = schedule.getCFG().getStartBlock();
assert startBlock != null;
assert startBlock.getPredecessorCount() == 0;
LIR lir = null;
AbstractBlockBase<?>[] codeEmittingOrder = null;
AbstractBlockBase<?>[] linearScanOrder = null;
try (Scope s = Debug.scope("ComputeLinearScanOrder", lir)) {
codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blocks.length, startBlock);
linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blocks.length, startBlock);
lir = new LIR(schedule.getCFG(), linearScanOrder, codeEmittingOrder);
Debug.dump(Debug.INFO_LOG_LEVEL, lir, "After linear scan order");
} catch (Throwable e) {
throw Debug.handle(e);
}
FrameMapBuilder frameMapBuilder = backend.newFrameMapBuilder(registerConfig);
LIRGenerationResult lirGenRes = backend.newLIRGenerationResult(graph.compilationId(), lir, frameMapBuilder, graph, stub);
LIRGeneratorTool lirGen = backend.newLIRGenerator(lirGenRes);
NodeLIRBuilderTool nodeLirGen = backend.newNodeLIRBuilder(graph, lirGen);
// LIR generation
LIRGenerationContext context = new LIRGenerationContext(lirGen, nodeLirGen, graph, schedule);
LIR_GENERATION_PHASE.apply(backend.getTarget(), lirGenRes, context);
try (Scope s = Debug.scope("LIRStages", nodeLirGen, lir)) {
Debug.dump(Debug.BASIC_LOG_LEVEL, lir, "After LIR generation");
LIRGenerationResult result = emitLowLevel(backend.getTarget(), lirGenRes, lirGen, lirSuites, backend.newRegisterAllocationConfig(registerConfig));
Debug.dump(Debug.BASIC_LOG_LEVEL, lir, "Before code generation");
return result;
} catch (Throwable e) {
throw Debug.handle(e);
}
} catch (Throwable e) {
throw Debug.handle(e);
}
}
protected static <T extends CompilationResult> String getCompilationUnitName(StructuredGraph graph, T compilationResult) {
if (compilationResult != null && compilationResult.getName() != null) {
return compilationResult.getName();
}
ResolvedJavaMethod method = graph.method();
if (method == null) {
return "<unknown>";
}
return method.format("%H.%n(%p)");
}
public static LIRGenerationResult emitLowLevel(TargetDescription target, LIRGenerationResult lirGenRes, LIRGeneratorTool lirGen, LIRSuites lirSuites,
RegisterAllocationConfig registerAllocationConfig) {
PreAllocationOptimizationContext preAllocOptContext = new PreAllocationOptimizationContext(lirGen);
lirSuites.getPreAllocationOptimizationStage().apply(target, lirGenRes, preAllocOptContext);
AllocationContext allocContext = new AllocationContext(lirGen.getSpillMoveFactory(), registerAllocationConfig);
lirSuites.getAllocationStage().apply(target, lirGenRes, allocContext);
PostAllocationOptimizationContext postAllocOptContext = new PostAllocationOptimizationContext(lirGen);
lirSuites.getPostAllocationOptimizationStage().apply(target, lirGenRes, postAllocOptContext);
return lirGenRes;
}
@SuppressWarnings("try")
public static void emitCode(Backend backend, Assumptions assumptions, ResolvedJavaMethod rootMethod, Collection<ResolvedJavaMethod> inlinedMethods, Collection<ResolvedJavaField> accessedFields,
int bytecodeSize, LIRGenerationResult lirGenRes,
CompilationResult compilationResult, ResolvedJavaMethod installedCodeOwner, CompilationResultBuilderFactory factory) {
try (DebugCloseable a = EmitCode.start()) {
FrameMap frameMap = lirGenRes.getFrameMap();
CompilationResultBuilder crb = backend.newCompilationResultBuilder(lirGenRes, frameMap, compilationResult, factory);
backend.emitCode(crb, lirGenRes.getLIR(), installedCodeOwner);
if (assumptions != null && !assumptions.isEmpty()) {
compilationResult.setAssumptions(assumptions.toArray());
}
if (rootMethod != null) {
compilationResult.setMethods(rootMethod, inlinedMethods);
compilationResult.setFields(accessedFields);
compilationResult.setBytecodeSize(bytecodeSize);
}
crb.finish();
if (Debug.isCountEnabled()) {
List<DataPatch> ldp = compilationResult.getDataPatches();
JavaKind[] kindValues = JavaKind.values();
DebugCounter[] dms = new DebugCounter[kindValues.length];
for (int i = 0; i < dms.length; i++) {
dms[i] = Debug.counter("DataPatches-%s", kindValues[i]);
}
for (DataPatch dp : ldp) {
JavaKind kind = JavaKind.Illegal;
if (dp.reference instanceof ConstantReference) {
VMConstant constant = ((ConstantReference) dp.reference).getConstant();
if (constant instanceof JavaConstant) {
kind = ((JavaConstant) constant).getJavaKind();
}
}
dms[kind.ordinal()].add(1);
}
Debug.counter("CompilationResults").increment();
Debug.counter("CodeBytesEmitted").add(compilationResult.getTargetCodeSize());
Debug.counter("InfopointsEmitted").add(compilationResult.getInfopoints().size());
Debug.counter("DataPatches").add(ldp.size());
Debug.counter("ExceptionHandlersEmitted").add(compilationResult.getExceptionHandlers().size());
}
Debug.dump(Debug.BASIC_LOG_LEVEL, compilationResult, "After code generation");
}
}
}