--- /dev/null 2017-01-22 10:16:57.869617664 -0800 +++ new/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/lir/GraalCompilerState.java 2017-02-15 17:06:19.111780186 -0800 @@ -0,0 +1,609 @@ +/* + * Copyright (c) 2016, 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.microbenchmarks.lir; + +import static org.graalvm.compiler.microbenchmarks.graal.util.GraalUtil.getGraph; +import static org.graalvm.compiler.microbenchmarks.graal.util.GraalUtil.getMethodFromMethodSpec; + +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.compiler.api.test.Graal; +import org.graalvm.compiler.code.CompilationResult; +import org.graalvm.compiler.core.GraalCompiler; +import org.graalvm.compiler.core.GraalCompiler.Request; +import org.graalvm.compiler.core.LIRGenerationPhase; +import org.graalvm.compiler.core.LIRGenerationPhase.LIRGenerationContext; +import org.graalvm.compiler.core.common.CompilationIdentifier; +import org.graalvm.compiler.core.common.alloc.ComputeBlockOrder; +import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; +import org.graalvm.compiler.core.target.Backend; +import org.graalvm.compiler.debug.Debug; +import org.graalvm.compiler.debug.DebugEnvironment; +import org.graalvm.compiler.debug.internal.DebugScope; +import org.graalvm.compiler.lir.LIR; +import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory; +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.LIRPhase; +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.microbenchmarks.graal.util.GraalState; +import org.graalvm.compiler.microbenchmarks.graal.util.GraalUtil; +import org.graalvm.compiler.microbenchmarks.graal.util.MethodSpec; +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.cfg.ControlFlowGraph; +import org.graalvm.compiler.nodes.spi.LoweringProvider; +import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; +import org.graalvm.compiler.options.DerivedOptionValue; +import org.graalvm.compiler.phases.OptimisticOptimizations; +import org.graalvm.compiler.phases.PhaseSuite; +import org.graalvm.compiler.phases.tiers.HighTierContext; +import org.graalvm.compiler.phases.tiers.Suites; +import org.graalvm.compiler.phases.tiers.TargetProvider; +import org.graalvm.compiler.phases.util.Providers; +import org.graalvm.compiler.runtime.RuntimeProvider; + +import jdk.vm.ci.code.CodeCacheProvider; +import jdk.vm.ci.code.RegisterConfig; +import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.meta.ConstantReflectionProvider; +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaMethod; + +/** + * State providing a new copy of a graph for each invocation of a benchmark. Subclasses of this + * class are annotated with {@link MethodSpec} to specify the Java method that will be parsed to + * obtain the original graph. + */ +@State(Scope.Thread) +public abstract class GraalCompilerState { + + /** + * Original graph from which the per-benchmark invocation {@link #graph} is cloned. + */ + private StructuredGraph originalGraph; + + /** + * The graph processed by the benchmark. + */ + private StructuredGraph graph; + private final Backend backend; + private final Providers providers; + private final DerivedOptionValue suites; + private final DerivedOptionValue lirSuites; + + /** + * We only allow inner classes to subclass this to ensure that the {@link Setup} methods are + * executed in the right order. + */ + @SuppressWarnings("try") + protected GraalCompilerState() { + this.backend = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend(); + this.providers = backend.getProviders(); + this.suites = new DerivedOptionValue<>(this::createSuites); + this.lirSuites = new DerivedOptionValue<>(this::createLIRSuites); + + // Ensure a debug configuration for this thread is initialized + if (Debug.isEnabled() && DebugScope.getConfig() == null) { + DebugEnvironment.initialize(System.out); + } + + } + + protected boolean useProfilingInfo() { + return false; + } + + @SuppressWarnings("try") + protected void initializeMethod() { + GraalState graal = new GraalState(); + ResolvedJavaMethod method = graal.metaAccess.lookupJavaMethod(getMethod()); + StructuredGraph structuredGraph = null; + try (Debug.Scope s = Debug.scope("GraphState", method)) { + structuredGraph = preprocessOriginal(getGraph(graal, method, useProfilingInfo())); + } catch (Throwable t) { + Debug.handle(t); + } + this.originalGraph = structuredGraph; + } + + protected Method getMethod() { + Class c = getClass(); + if (isMethodSpecAnnotationPresent(c)) { + return getMethodFromMethodSpec(c); + } + return findParamField(this); + } + + protected boolean isMethodSpecAnnotationPresent(Class startClass) { + Class c = startClass; + while (c != null) { + if (c.isAnnotationPresent(MethodSpec.class)) { + return true; + } + c = c.getSuperclass(); + } + return false; + } + + /** + * Declares {@link GraalCompilerState#getMethodFromString(String) method description field}. The + * field must be a {@link String} and have a {@link Param} annotation. + */ + @Inherited + @Target({ElementType.FIELD}) + @Retention(RetentionPolicy.RUNTIME) + public @interface MethodDescString { + } + + private static Method findParamField(Object obj) { + Class c = obj.getClass(); + Class annotationClass = MethodDescString.class; + try { + for (Field f : c.getFields()) { + if (f.isAnnotationPresent(annotationClass)) { + // these checks could be done by an annotation processor + if (!f.getType().equals(String.class)) { + throw new RuntimeException("Found a field annotated with " + annotationClass.getSimpleName() + " in " + c + " which is not a " + String.class.getSimpleName()); + } + if (!f.isAnnotationPresent(Param.class)) { + throw new RuntimeException("Found a field annotated with " + annotationClass.getSimpleName() + " in " + c + " which is not annotated with " + Param.class.getSimpleName()); + } + String methodName; + methodName = (String) f.get(obj); + assert methodName != null; + return getMethodFromString(methodName); + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + throw new RuntimeException("Could not find class annotated with " + annotationClass.getSimpleName() + " in hierarchy of " + c); + } + + /** + * Gets a {@link Method} from a method description string. The format is as follows: + * + *
+     * ClassName#MethodName
+     * ClassName#MethodName(ClassName, ClassName, ...)
+     * 
+ * + * CodeName is passed to {@link Class#forName(String)}.
+ * Examples: + * + *
+     * java.lang.String#equals
+     * java.lang.String#equals(java.lang.Object)
+     * 
+ */ + protected static Method getMethodFromString(String methodDesc) { + try { + String[] s0 = methodDesc.split("#", 2); + if (s0.length != 2) { + throw new RuntimeException("Missing method description? " + methodDesc); + } + String className = s0[0]; + Class clazz = Class.forName(className); + String[] s1 = s0[1].split("\\(", 2); + String name = s1[0]; + Class[] parameters = null; + if (s1.length > 1) { + String parametersPart = s1[1]; + if (parametersPart.charAt(parametersPart.length() - 1) != ')') { + throw new RuntimeException("Missing closing ')'? " + methodDesc); + } + String[] s2 = parametersPart.substring(0, parametersPart.length() - 1).split(","); + parameters = new Class[s2.length]; + for (int i = 0; i < s2.length; i++) { + parameters[i] = Class.forName(s2[i]); + } + } + return GraalUtil.getMethod(clazz, name, parameters); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + protected StructuredGraph preprocessOriginal(StructuredGraph structuredGraph) { + return structuredGraph; + } + + protected Suites createSuites() { + Suites ret = backend.getSuites().getDefaultSuites().copy(); + return ret; + } + + protected LIRSuites createLIRSuites() { + LIRSuites ret = backend.getSuites().getDefaultLIRSuites().copy(); + return ret; + } + + protected Backend getBackend() { + return backend; + } + + protected Suites getSuites() { + return suites.getValue(); + } + + protected LIRSuites getOriginalLIRSuites() { + return lirSuites.getValue(); + } + + protected Providers getProviders() { + return providers; + } + + protected SnippetReflectionProvider getSnippetReflection() { + return Graal.getRequiredCapability(SnippetReflectionProvider.class); + } + + protected TargetDescription getTarget() { + return getTargetProvider().getTarget(); + } + + protected TargetProvider getTargetProvider() { + return getBackend(); + } + + protected CodeCacheProvider getCodeCache() { + return getProviders().getCodeCache(); + } + + protected ConstantReflectionProvider getConstantReflection() { + return getProviders().getConstantReflection(); + } + + protected MetaAccessProvider getMetaAccess() { + return getProviders().getMetaAccess(); + } + + protected LoweringProvider getLowerer() { + return getProviders().getLowerer(); + } + + protected PhaseSuite getDefaultGraphBuilderSuite() { + // defensive copying + return backend.getSuites().getDefaultGraphBuilderSuite().copy(); + } + + protected LIRSuites getLIRSuites() { + return request.lirSuites; + } + + private Request request; + private LIRGenerationResult lirGenRes; + private LIRGeneratorTool lirGenTool; + private NodeLIRBuilderTool nodeLirGen; + private RegisterConfig registerConfig; + private ScheduleResult schedule; + private AbstractBlockBase[] codeEmittingOrder; + private AbstractBlockBase[] linearScanOrder; + + /** + * Copies the {@link #originalGraph original graph} and prepares the {@link #request}. + * + * The {@link Suites} can be changed by overriding {@link #createSuites()}. {@link LIRSuites} + * can be changed by overriding {@link #createLIRSuites()}. + */ + protected final void prepareRequest() { + assert originalGraph != null : "call initialzeMethod first"; + CompilationIdentifier compilationId = backend.getCompilationIdentifier(originalGraph.method()); + graph = originalGraph.copyWithIdentifier(compilationId); + assert !graph.isFrozen(); + ResolvedJavaMethod installedCodeOwner = graph.method(); + request = new Request<>(graph, installedCodeOwner, getProviders(), getBackend(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL, + graph.getProfilingInfo(), getSuites(), getOriginalLIRSuites(), new CompilationResult(), CompilationResultBuilderFactory.Default); + } + + /** + * Executes the high-level (FrontEnd) part of the compiler. + */ + protected final void emitFrontEnd() { + GraalCompiler.emitFrontEnd(request.providers, request.backend, request.graph, request.graphBuilderSuite, request.optimisticOpts, request.profilingInfo, request.suites); + request.graph.freeze(); + } + + /** + * Executes the low-level (BackEnd) part of the compiler. + */ + protected final void emitBackEnd() { + emitLIR(); + emitCode(); + } + + /** + * Generates {@link LIR} and executes the {@link LIR} pipeline. + */ + protected final void emitLIR() { + generateLIR(); + emitLowLevel(); + } + + /** + * Generates the initial {@link LIR}. + */ + protected final void generateLIR() { + preLIRGeneration(); + lirGeneration(); + } + + /** + * Sets up {@link LIR} generation. + */ + protected final void preLIRGeneration() { + assert request.graph.isFrozen() : "Graph not frozen."; + Object stub = null; + schedule = request.graph.getLastSchedule(); + ControlFlowGraph cfg = deepCopy(schedule.getCFG()); + Block[] blocks = cfg.getBlocks(); + Block startBlock = cfg.getStartBlock(); + assert startBlock != null; + assert startBlock.getPredecessorCount() == 0; + + codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blocks.length, startBlock); + linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blocks.length, startBlock); + + LIR lir = new LIR(cfg, linearScanOrder, codeEmittingOrder); + FrameMapBuilder frameMapBuilder = request.backend.newFrameMapBuilder(registerConfig); + lirGenRes = request.backend.newLIRGenerationResult(graph.compilationId(), lir, frameMapBuilder, request.graph, stub); + lirGenTool = request.backend.newLIRGenerator(lirGenRes); + nodeLirGen = request.backend.newNodeLIRBuilder(request.graph, lirGenTool); + } + + private static ControlFlowGraph deepCopy(ControlFlowGraph cfg) { + return ControlFlowGraph.compute(cfg.graph, true, true, true, true); + } + + /** + * Executes the {@link LIRGenerationPhase}. + */ + protected final void lirGeneration() { + LIRGenerationContext context = new LIRGenerationContext(lirGenTool, nodeLirGen, request.graph, schedule); + new LIRGenerationPhase().apply(request.backend.getTarget(), lirGenRes, context); + } + + /** + * Executes the low-level compiler stages. + */ + protected final void emitLowLevel() { + preAllocationStage(); + allocationStage(); + postAllocationStage(); + } + + /** + * Executes a {@link LIRPhase} within a given {@code context}. + */ + protected void applyLIRPhase(LIRPhase phase, C context) { + phase.apply(request.backend.getTarget(), lirGenRes, context); + } + + /** + * Executes the {@link PreAllocationStage}. + * + * {@link LIRPhase phases} can be changed by overriding {@link #createLIRSuites()}. + */ + protected final void preAllocationStage() { + applyLIRPhase(getLIRSuites().getPreAllocationOptimizationStage(), createPreAllocationOptimizationContext()); + } + + protected PreAllocationOptimizationContext createPreAllocationOptimizationContext() { + return new PreAllocationOptimizationContext(lirGenTool); + } + + /** + * Executes the {@link AllocationStage}. + * + * {@link LIRPhase phases} can be changed by overriding {@link #createLIRSuites()}. + */ + protected final void allocationStage() { + applyLIRPhase(getLIRSuites().getAllocationStage(), createAllocationContext()); + } + + protected AllocationContext createAllocationContext() { + return new AllocationContext(lirGenTool.getSpillMoveFactory(), request.backend.newRegisterAllocationConfig(registerConfig)); + } + + /** + * Executes the {@link PostAllocationStage}. + * + * {@link LIRPhase phases} can be changed by overriding {@link #createLIRSuites()}. + */ + protected final void postAllocationStage() { + applyLIRPhase(getLIRSuites().getPostAllocationOptimizationStage(), createPostAllocationOptimizationContext()); + } + + protected PostAllocationOptimizationContext createPostAllocationOptimizationContext() { + return new PostAllocationOptimizationContext(lirGenTool); + } + + /** + * Emits the machine code. + */ + protected final void emitCode() { + int bytecodeSize = request.graph.method() == null ? 0 : request.graph.getBytecodeSize(); + request.compilationResult.setHasUnsafeAccess(request.graph.hasUnsafeAccess()); + GraalCompiler.emitCode(request.backend, request.graph.getAssumptions(), request.graph.method(), request.graph.getMethods(), request.graph.getFields(), bytecodeSize, lirGenRes, + request.compilationResult, request.installedCodeOwner, request.factory); + } + + protected StructuredGraph graph() { + return graph; + } + + protected LIR getLIR() { + return lirGenRes.getLIR(); + } + + public abstract static class Compile extends GraalCompilerState { + + @Setup(Level.Trial) + public void init() { + initializeMethod(); + } + + @Setup(Level.Invocation) + public void setup() { + prepareRequest(); + } + + public CompilationResult compile() { + emitFrontEnd(); + emitBackEnd(); + return super.request.compilationResult; + } + + } + + public abstract static class FrontEndOnly extends GraalCompilerState { + + @Setup(Level.Trial) + public void init() { + initializeMethod(); + } + + @Setup(Level.Invocation) + public void setup() { + prepareRequest(); + } + + public StructuredGraph compile() { + emitFrontEnd(); + return super.graph; + } + + } + + public abstract static class BackEndOnly extends GraalCompilerState { + + @Setup(Level.Trial) + public void init() { + initializeMethod(); + } + + /** + * Cannot do this {@link Level#Trial only once} since {@link #emitCode()} closes the + * {@link CompilationResult}. + */ + @Setup(Level.Invocation) + public void setupGraph() { + prepareRequest(); + emitFrontEnd(); + } + + public CompilationResult compile() { + emitBackEnd(); + return super.request.compilationResult; + } + } + + public abstract static class PreAllocationStage extends GraalCompilerState { + /** + * No need to rebuild the graph for every invocation since it is not altered by the backend. + */ + @Setup(Level.Trial) + public void setupGraph() { + initializeMethod(); + prepareRequest(); + emitFrontEnd(); + } + + @Setup(Level.Invocation) + public void setup() { + generateLIR(); + } + + public LIRGenerationResult compile() { + preAllocationStage(); + return super.lirGenRes; + } + } + + public abstract static class AllocationStage extends GraalCompilerState { + /** + * No need to rebuild the graph for every invocation since it is not altered by the backend. + */ + @Setup(Level.Trial) + public void setupGraph() { + initializeMethod(); + prepareRequest(); + emitFrontEnd(); + } + + @Setup(Level.Invocation) + public void setup() { + generateLIR(); + preAllocationStage(); + } + + public LIRGenerationResult compile() { + allocationStage(); + return super.lirGenRes; + } + } + + public abstract static class PostAllocationStage extends GraalCompilerState { + /** + * No need to rebuild the graph for every invocation since it is not altered by the backend. + */ + @Setup(Level.Trial) + public void setupGraph() { + initializeMethod(); + prepareRequest(); + emitFrontEnd(); + } + + @Setup(Level.Invocation) + public void setup() { + generateLIR(); + preAllocationStage(); + allocationStage(); + } + + public LIRGenerationResult compile() { + postAllocationStage(); + return super.lirGenRes; + } + } +}