1/*
2 * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.compiler.microbenchmarks.lir;
24
25import static org.graalvm.compiler.microbenchmarks.graal.util.GraalUtil.getGraph;
26import static org.graalvm.compiler.microbenchmarks.graal.util.GraalUtil.getMethodFromMethodSpec;
27
28import java.lang.annotation.Annotation;
29import java.lang.annotation.ElementType;
30import java.lang.annotation.Inherited;
31import java.lang.annotation.Retention;
32import java.lang.annotation.RetentionPolicy;
33import java.lang.annotation.Target;
34import java.lang.reflect.Field;
35import java.lang.reflect.Method;
36
37import org.openjdk.jmh.annotations.Level;
38import org.openjdk.jmh.annotations.Param;
39import org.openjdk.jmh.annotations.Scope;
40import org.openjdk.jmh.annotations.Setup;
41import org.openjdk.jmh.annotations.State;
42
43import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
44import org.graalvm.compiler.api.test.Graal;
45import org.graalvm.compiler.code.CompilationResult;
46import org.graalvm.compiler.core.GraalCompiler;
47import org.graalvm.compiler.core.GraalCompiler.Request;
48import org.graalvm.compiler.core.LIRGenerationPhase;
49import org.graalvm.compiler.core.LIRGenerationPhase.LIRGenerationContext;
50import org.graalvm.compiler.core.common.CompilationIdentifier;
51import org.graalvm.compiler.core.common.alloc.ComputeBlockOrder;
52import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
53import org.graalvm.compiler.core.target.Backend;
54import org.graalvm.compiler.debug.Debug;
55import org.graalvm.compiler.debug.DebugEnvironment;
56import org.graalvm.compiler.debug.internal.DebugScope;
57import org.graalvm.compiler.lir.LIR;
58import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory;
59import org.graalvm.compiler.lir.framemap.FrameMapBuilder;
60import org.graalvm.compiler.lir.gen.LIRGenerationResult;
61import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
62import org.graalvm.compiler.lir.phases.AllocationPhase.AllocationContext;
63import org.graalvm.compiler.lir.phases.LIRPhase;
64import org.graalvm.compiler.lir.phases.LIRSuites;
65import org.graalvm.compiler.lir.phases.PostAllocationOptimizationPhase.PostAllocationOptimizationContext;
66import org.graalvm.compiler.lir.phases.PreAllocationOptimizationPhase.PreAllocationOptimizationContext;
67import org.graalvm.compiler.microbenchmarks.graal.util.GraalState;
68import org.graalvm.compiler.microbenchmarks.graal.util.GraalUtil;
69import org.graalvm.compiler.microbenchmarks.graal.util.MethodSpec;
70import org.graalvm.compiler.nodes.StructuredGraph;
71import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
72import org.graalvm.compiler.nodes.cfg.Block;
73import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
74import org.graalvm.compiler.nodes.spi.LoweringProvider;
75import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
76import org.graalvm.compiler.options.DerivedOptionValue;
77import org.graalvm.compiler.phases.OptimisticOptimizations;
78import org.graalvm.compiler.phases.PhaseSuite;
79import org.graalvm.compiler.phases.tiers.HighTierContext;
80import org.graalvm.compiler.phases.tiers.Suites;
81import org.graalvm.compiler.phases.tiers.TargetProvider;
82import org.graalvm.compiler.phases.util.Providers;
83import org.graalvm.compiler.runtime.RuntimeProvider;
84
85import jdk.vm.ci.code.CodeCacheProvider;
86import jdk.vm.ci.code.RegisterConfig;
87import jdk.vm.ci.code.TargetDescription;
88import jdk.vm.ci.meta.ConstantReflectionProvider;
89import jdk.vm.ci.meta.MetaAccessProvider;
90import jdk.vm.ci.meta.ResolvedJavaMethod;
91
92/**
93 * State providing a new copy of a graph for each invocation of a benchmark. Subclasses of this
94 * class are annotated with {@link MethodSpec} to specify the Java method that will be parsed to
95 * obtain the original graph.
96 */
97@State(Scope.Thread)
98public abstract class GraalCompilerState {
99
100    /**
101     * Original graph from which the per-benchmark invocation {@link #graph} is cloned.
102     */
103    private StructuredGraph originalGraph;
104
105    /**
106     * The graph processed by the benchmark.
107     */
108    private StructuredGraph graph;
109    private final Backend backend;
110    private final Providers providers;
111    private final DerivedOptionValue<Suites> suites;
112    private final DerivedOptionValue<LIRSuites> lirSuites;
113
114    /**
115     * We only allow inner classes to subclass this to ensure that the {@link Setup} methods are
116     * executed in the right order.
117     */
118    @SuppressWarnings("try")
119    protected GraalCompilerState() {
120        this.backend = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend();
121        this.providers = backend.getProviders();
122        this.suites = new DerivedOptionValue<>(this::createSuites);
123        this.lirSuites = new DerivedOptionValue<>(this::createLIRSuites);
124
125        // Ensure a debug configuration for this thread is initialized
126        if (Debug.isEnabled() && DebugScope.getConfig() == null) {
127            DebugEnvironment.initialize(System.out);
128        }
129
130    }
131
132    protected boolean useProfilingInfo() {
133        return false;
134    }
135
136    @SuppressWarnings("try")
137    protected void initializeMethod() {
138        GraalState graal = new GraalState();
139        ResolvedJavaMethod method = graal.metaAccess.lookupJavaMethod(getMethod());
140        StructuredGraph structuredGraph = null;
141        try (Debug.Scope s = Debug.scope("GraphState", method)) {
142            structuredGraph = preprocessOriginal(getGraph(graal, method, useProfilingInfo()));
143        } catch (Throwable t) {
144            Debug.handle(t);
145        }
146        this.originalGraph = structuredGraph;
147    }
148
149    protected Method getMethod() {
150        Class<?> c = getClass();
151        if (isMethodSpecAnnotationPresent(c)) {
152            return getMethodFromMethodSpec(c);
153        }
154        return findParamField(this);
155    }
156
157    protected boolean isMethodSpecAnnotationPresent(Class<?> startClass) {
158        Class<?> c = startClass;
159        while (c != null) {
160            if (c.isAnnotationPresent(MethodSpec.class)) {
161                return true;
162            }
163            c = c.getSuperclass();
164        }
165        return false;
166    }
167
168    /**
169     * Declares {@link GraalCompilerState#getMethodFromString(String) method description field}. The
170     * field must be a {@link String} and have a {@link Param} annotation.
171     */
172    @Inherited
173    @Target({ElementType.FIELD})
174    @Retention(RetentionPolicy.RUNTIME)
175    public @interface MethodDescString {
176    }
177
178    private static Method findParamField(Object obj) {
179        Class<?> c = obj.getClass();
180        Class<? extends Annotation> annotationClass = MethodDescString.class;
181        try {
182            for (Field f : c.getFields()) {
183                if (f.isAnnotationPresent(annotationClass)) {
184                    // these checks could be done by an annotation processor
185                    if (!f.getType().equals(String.class)) {
186                        throw new RuntimeException("Found a field annotated with " + annotationClass.getSimpleName() + " in " + c + " which is not a " + String.class.getSimpleName());
187                    }
188                    if (!f.isAnnotationPresent(Param.class)) {
189                        throw new RuntimeException("Found a field annotated with " + annotationClass.getSimpleName() + " in " + c + " which is not annotated with " + Param.class.getSimpleName());
190                    }
191                    String methodName;
192                    methodName = (String) f.get(obj);
193                    assert methodName != null;
194                    return getMethodFromString(methodName);
195                }
196            }
197        } catch (Exception e) {
198            throw new RuntimeException(e);
199        }
200        throw new RuntimeException("Could not find class annotated with " + annotationClass.getSimpleName() + " in hierarchy of " + c);
201    }
202
203    /**
204     * Gets a {@link Method} from a method description string. The format is as follows:
205     *
206     * <pre>
207     * ClassName#MethodName
208     * ClassName#MethodName(ClassName, ClassName, ...)
209     * </pre>
210     *
211     * <code>CodeName</code> is passed to {@link Class#forName(String)}. <br>
212     * <b>Examples:</b>
213     *
214     * <pre>
215     * java.lang.String#equals
216     * java.lang.String#equals(java.lang.Object)
217     * </pre>
218     */
219    protected static Method getMethodFromString(String methodDesc) {
220        try {
221            String[] s0 = methodDesc.split("#", 2);
222            if (s0.length != 2) {
223                throw new RuntimeException("Missing method description? " + methodDesc);
224            }
225            String className = s0[0];
226            Class<?> clazz = Class.forName(className);
227            String[] s1 = s0[1].split("\\(", 2);
228            String name = s1[0];
229            Class<?>[] parameters = null;
230            if (s1.length > 1) {
231                String parametersPart = s1[1];
232                if (parametersPart.charAt(parametersPart.length() - 1) != ')') {
233                    throw new RuntimeException("Missing closing ')'? " + methodDesc);
234                }
235                String[] s2 = parametersPart.substring(0, parametersPart.length() - 1).split(",");
236                parameters = new Class<?>[s2.length];
237                for (int i = 0; i < s2.length; i++) {
238                    parameters[i] = Class.forName(s2[i]);
239                }
240            }
241            return GraalUtil.getMethod(clazz, name, parameters);
242        } catch (ClassNotFoundException e) {
243            throw new RuntimeException(e);
244        }
245    }
246
247    protected StructuredGraph preprocessOriginal(StructuredGraph structuredGraph) {
248        return structuredGraph;
249    }
250
251    protected Suites createSuites() {
252        Suites ret = backend.getSuites().getDefaultSuites().copy();
253        return ret;
254    }
255
256    protected LIRSuites createLIRSuites() {
257        LIRSuites ret = backend.getSuites().getDefaultLIRSuites().copy();
258        return ret;
259    }
260
261    protected Backend getBackend() {
262        return backend;
263    }
264
265    protected Suites getSuites() {
266        return suites.getValue();
267    }
268
269    protected LIRSuites getOriginalLIRSuites() {
270        return lirSuites.getValue();
271    }
272
273    protected Providers getProviders() {
274        return providers;
275    }
276
277    protected SnippetReflectionProvider getSnippetReflection() {
278        return Graal.getRequiredCapability(SnippetReflectionProvider.class);
279    }
280
281    protected TargetDescription getTarget() {
282        return getTargetProvider().getTarget();
283    }
284
285    protected TargetProvider getTargetProvider() {
286        return getBackend();
287    }
288
289    protected CodeCacheProvider getCodeCache() {
290        return getProviders().getCodeCache();
291    }
292
293    protected ConstantReflectionProvider getConstantReflection() {
294        return getProviders().getConstantReflection();
295    }
296
297    protected MetaAccessProvider getMetaAccess() {
298        return getProviders().getMetaAccess();
299    }
300
301    protected LoweringProvider getLowerer() {
302        return getProviders().getLowerer();
303    }
304
305    protected PhaseSuite<HighTierContext> getDefaultGraphBuilderSuite() {
306        // defensive copying
307        return backend.getSuites().getDefaultGraphBuilderSuite().copy();
308    }
309
310    protected LIRSuites getLIRSuites() {
311        return request.lirSuites;
312    }
313
314    private Request<CompilationResult> request;
315    private LIRGenerationResult lirGenRes;
316    private LIRGeneratorTool lirGenTool;
317    private NodeLIRBuilderTool nodeLirGen;
318    private RegisterConfig registerConfig;
319    private ScheduleResult schedule;
320    private AbstractBlockBase<?>[] codeEmittingOrder;
321    private AbstractBlockBase<?>[] linearScanOrder;
322
323    /**
324     * Copies the {@link #originalGraph original graph} and prepares the {@link #request}.
325     *
326     * The {@link Suites} can be changed by overriding {@link #createSuites()}. {@link LIRSuites}
327     * can be changed by overriding {@link #createLIRSuites()}.
328     */
329    protected final void prepareRequest() {
330        assert originalGraph != null : "call initialzeMethod first";
331        CompilationIdentifier compilationId = backend.getCompilationIdentifier(originalGraph.method());
332        graph = originalGraph.copyWithIdentifier(compilationId);
333        assert !graph.isFrozen();
334        ResolvedJavaMethod installedCodeOwner = graph.method();
335        request = new Request<>(graph, installedCodeOwner, getProviders(), getBackend(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL,
336                        graph.getProfilingInfo(), getSuites(), getOriginalLIRSuites(), new CompilationResult(), CompilationResultBuilderFactory.Default);
337    }
338
339    /**
340     * Executes the high-level (FrontEnd) part of the compiler.
341     */
342    protected final void emitFrontEnd() {
343        GraalCompiler.emitFrontEnd(request.providers, request.backend, request.graph, request.graphBuilderSuite, request.optimisticOpts, request.profilingInfo, request.suites);
344        request.graph.freeze();
345    }
346
347    /**
348     * Executes the low-level (BackEnd) part of the compiler.
349     */
350    protected final void emitBackEnd() {
351        emitLIR();
352        emitCode();
353    }
354
355    /**
356     * Generates {@link LIR} and executes the {@link LIR} pipeline.
357     */
358    protected final void emitLIR() {
359        generateLIR();
360        emitLowLevel();
361    }
362
363    /**
364     * Generates the initial {@link LIR}.
365     */
366    protected final void generateLIR() {
367        preLIRGeneration();
368        lirGeneration();
369    }
370
371    /**
372     * Sets up {@link LIR} generation.
373     */
374    protected final void preLIRGeneration() {
375        assert request.graph.isFrozen() : "Graph not frozen.";
376        Object stub = null;
377        schedule = request.graph.getLastSchedule();
378        ControlFlowGraph cfg = deepCopy(schedule.getCFG());
379        Block[] blocks = cfg.getBlocks();
380        Block startBlock = cfg.getStartBlock();
381        assert startBlock != null;
382        assert startBlock.getPredecessorCount() == 0;
383
384        codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blocks.length, startBlock);
385        linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blocks.length, startBlock);
386
387        LIR lir = new LIR(cfg, linearScanOrder, codeEmittingOrder);
388        FrameMapBuilder frameMapBuilder = request.backend.newFrameMapBuilder(registerConfig);
389        lirGenRes = request.backend.newLIRGenerationResult(graph.compilationId(), lir, frameMapBuilder, request.graph, stub);
390        lirGenTool = request.backend.newLIRGenerator(lirGenRes);
391        nodeLirGen = request.backend.newNodeLIRBuilder(request.graph, lirGenTool);
392    }
393
394    private static ControlFlowGraph deepCopy(ControlFlowGraph cfg) {
395        return ControlFlowGraph.compute(cfg.graph, true, true, true, true);
396    }
397
398    /**
399     * Executes the {@link LIRGenerationPhase}.
400     */
401    protected final void lirGeneration() {
402        LIRGenerationContext context = new LIRGenerationContext(lirGenTool, nodeLirGen, request.graph, schedule);
403        new LIRGenerationPhase().apply(request.backend.getTarget(), lirGenRes, context);
404    }
405
406    /**
407     * Executes the low-level compiler stages.
408     */
409    protected final void emitLowLevel() {
410        preAllocationStage();
411        allocationStage();
412        postAllocationStage();
413    }
414
415    /**
416     * Executes a {@link LIRPhase} within a given {@code context}.
417     */
418    protected <C> void applyLIRPhase(LIRPhase<C> phase, C context) {
419        phase.apply(request.backend.getTarget(), lirGenRes, context);
420    }
421
422    /**
423     * Executes the {@link PreAllocationStage}.
424     *
425     * {@link LIRPhase phases} can be changed by overriding {@link #createLIRSuites()}.
426     */
427    protected final void preAllocationStage() {
428        applyLIRPhase(getLIRSuites().getPreAllocationOptimizationStage(), createPreAllocationOptimizationContext());
429    }
430
431    protected PreAllocationOptimizationContext createPreAllocationOptimizationContext() {
432        return new PreAllocationOptimizationContext(lirGenTool);
433    }
434
435    /**
436     * Executes the {@link AllocationStage}.
437     *
438     * {@link LIRPhase phases} can be changed by overriding {@link #createLIRSuites()}.
439     */
440    protected final void allocationStage() {
441        applyLIRPhase(getLIRSuites().getAllocationStage(), createAllocationContext());
442    }
443
444    protected AllocationContext createAllocationContext() {
445        return new AllocationContext(lirGenTool.getSpillMoveFactory(), request.backend.newRegisterAllocationConfig(registerConfig));
446    }
447
448    /**
449     * Executes the {@link PostAllocationStage}.
450     *
451     * {@link LIRPhase phases} can be changed by overriding {@link #createLIRSuites()}.
452     */
453    protected final void postAllocationStage() {
454        applyLIRPhase(getLIRSuites().getPostAllocationOptimizationStage(), createPostAllocationOptimizationContext());
455    }
456
457    protected PostAllocationOptimizationContext createPostAllocationOptimizationContext() {
458        return new PostAllocationOptimizationContext(lirGenTool);
459    }
460
461    /**
462     * Emits the machine code.
463     */
464    protected final void emitCode() {
465        int bytecodeSize = request.graph.method() == null ? 0 : request.graph.getBytecodeSize();
466        request.compilationResult.setHasUnsafeAccess(request.graph.hasUnsafeAccess());
467        GraalCompiler.emitCode(request.backend, request.graph.getAssumptions(), request.graph.method(), request.graph.getMethods(), request.graph.getFields(), bytecodeSize, lirGenRes,
468                        request.compilationResult, request.installedCodeOwner, request.factory);
469    }
470
471    protected StructuredGraph graph() {
472        return graph;
473    }
474
475    protected LIR getLIR() {
476        return lirGenRes.getLIR();
477    }
478
479    public abstract static class Compile extends GraalCompilerState {
480
481        @Setup(Level.Trial)
482        public void init() {
483            initializeMethod();
484        }
485
486        @Setup(Level.Invocation)
487        public void setup() {
488            prepareRequest();
489        }
490
491        public CompilationResult compile() {
492            emitFrontEnd();
493            emitBackEnd();
494            return super.request.compilationResult;
495        }
496
497    }
498
499    public abstract static class FrontEndOnly extends GraalCompilerState {
500
501        @Setup(Level.Trial)
502        public void init() {
503            initializeMethod();
504        }
505
506        @Setup(Level.Invocation)
507        public void setup() {
508            prepareRequest();
509        }
510
511        public StructuredGraph compile() {
512            emitFrontEnd();
513            return super.graph;
514        }
515
516    }
517
518    public abstract static class BackEndOnly extends GraalCompilerState {
519
520        @Setup(Level.Trial)
521        public void init() {
522            initializeMethod();
523        }
524
525        /**
526         * Cannot do this {@link Level#Trial only once} since {@link #emitCode()} closes the
527         * {@link CompilationResult}.
528         */
529        @Setup(Level.Invocation)
530        public void setupGraph() {
531            prepareRequest();
532            emitFrontEnd();
533        }
534
535        public CompilationResult compile() {
536            emitBackEnd();
537            return super.request.compilationResult;
538        }
539    }
540
541    public abstract static class PreAllocationStage extends GraalCompilerState {
542        /**
543         * No need to rebuild the graph for every invocation since it is not altered by the backend.
544         */
545        @Setup(Level.Trial)
546        public void setupGraph() {
547            initializeMethod();
548            prepareRequest();
549            emitFrontEnd();
550        }
551
552        @Setup(Level.Invocation)
553        public void setup() {
554            generateLIR();
555        }
556
557        public LIRGenerationResult compile() {
558            preAllocationStage();
559            return super.lirGenRes;
560        }
561    }
562
563    public abstract static class AllocationStage extends GraalCompilerState {
564        /**
565         * No need to rebuild the graph for every invocation since it is not altered by the backend.
566         */
567        @Setup(Level.Trial)
568        public void setupGraph() {
569            initializeMethod();
570            prepareRequest();
571            emitFrontEnd();
572        }
573
574        @Setup(Level.Invocation)
575        public void setup() {
576            generateLIR();
577            preAllocationStage();
578        }
579
580        public LIRGenerationResult compile() {
581            allocationStage();
582            return super.lirGenRes;
583        }
584    }
585
586    public abstract static class PostAllocationStage extends GraalCompilerState {
587        /**
588         * No need to rebuild the graph for every invocation since it is not altered by the backend.
589         */
590        @Setup(Level.Trial)
591        public void setupGraph() {
592            initializeMethod();
593            prepareRequest();
594            emitFrontEnd();
595        }
596
597        @Setup(Level.Invocation)
598        public void setup() {
599            generateLIR();
600            preAllocationStage();
601            allocationStage();
602        }
603
604        public LIRGenerationResult compile() {
605            postAllocationStage();
606            return super.lirGenRes;
607        }
608    }
609}
610