1 /*
   2  * Copyright (c) 2015, 2018, 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  */
  23 
  24 
  25 package org.graalvm.compiler.hotspot;
  26 
  27 import static jdk.vm.ci.common.InitTimer.timer;
  28 import static jdk.vm.ci.services.Services.IS_BUILDING_NATIVE_IMAGE;
  29 import static org.graalvm.compiler.hotspot.HotSpotGraalOptionValues.GRAAL_OPTION_PROPERTY_PREFIX;
  30 
  31 import java.io.PrintStream;
  32 
  33 import org.graalvm.compiler.api.runtime.GraalRuntime;
  34 import org.graalvm.compiler.debug.MethodFilter;
  35 import org.graalvm.compiler.options.Option;
  36 import org.graalvm.compiler.options.OptionKey;
  37 import org.graalvm.compiler.options.OptionType;
  38 import org.graalvm.compiler.options.OptionValues;
  39 import org.graalvm.compiler.options.OptionsParser;
  40 import org.graalvm.compiler.phases.tiers.CompilerConfiguration;
  41 
  42 import jdk.vm.ci.common.InitTimer;
  43 import jdk.vm.ci.hotspot.HotSpotJVMCICompilerFactory;
  44 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
  45 import jdk.vm.ci.hotspot.HotSpotSignature;
  46 import jdk.vm.ci.runtime.JVMCIRuntime;
  47 
  48 public final class HotSpotGraalCompilerFactory extends HotSpotJVMCICompilerFactory {
  49 
  50     private static MethodFilter[] graalCompileOnlyFilter;
  51     private static boolean compileGraalWithC1Only;
  52 
  53     private IsGraalPredicate isGraalPredicate;
  54 
  55     private final HotSpotGraalJVMCIServiceLocator locator;
  56 
  57     HotSpotGraalCompilerFactory(HotSpotGraalJVMCIServiceLocator locator) {
  58         this.locator = locator;
  59     }
  60 
  61     @Override
  62     public String getCompilerName() {
  63         return "graal";
  64     }
  65 
  66     /**
  67      * Initialized when this factory is {@linkplain #onSelection() selected}.
  68      */
  69     private OptionValues options;
  70 
  71     @Override
  72     public void onSelection() {
  73         JVMCIVersionCheck.check(false);
  74         assert options == null : "cannot select " + getClass() + " service more than once";
  75         options = HotSpotGraalOptionValues.defaultOptions();
  76         initializeGraalCompilePolicyFields(options);
  77         isGraalPredicate = compileGraalWithC1Only ? new IsGraalPredicate() : null;
  78         /*
  79          * Exercise this code path early to encourage loading now. This doesn't solve problem of
  80          * deadlock during class loading but seems to eliminate it in practice.
  81          */
  82         adjustCompilationLevelInternal(Object.class, "hashCode", "()I", CompilationLevel.FullOptimization);
  83         adjustCompilationLevelInternal(Object.class, "hashCode", "()I", CompilationLevel.Simple);
  84         if (IS_BUILDING_NATIVE_IMAGE) {
  85             // Triggers initialization of all option descriptors
  86             Options.CompileGraalWithC1Only.getName();
  87         }
  88     }
  89 
  90     private static void initializeGraalCompilePolicyFields(OptionValues options) {
  91         compileGraalWithC1Only = Options.CompileGraalWithC1Only.getValue(options);
  92         String optionValue = Options.GraalCompileOnly.getValue(options);
  93         if (optionValue != null) {
  94             MethodFilter[] filter = MethodFilter.parse(optionValue);
  95             if (filter.length == 0) {
  96                 filter = null;
  97             }
  98             graalCompileOnlyFilter = filter;
  99         }
 100     }
 101 
 102     @Override
 103     public void printProperties(PrintStream out) {
 104         out.println("[Graal properties]");
 105         options.printHelp(OptionsParser.getOptionsLoader(), out, GRAAL_OPTION_PROPERTY_PREFIX);
 106     }
 107 
 108     static class Options {
 109 
 110         // @formatter:off
 111         @Option(help = "In tiered mode compile Graal and JVMCI using optimized first tier code.", type = OptionType.Expert)
 112         public static final OptionKey<Boolean> CompileGraalWithC1Only = new OptionKey<>(true);
 113 
 114         @Option(help = "A filter applied to a method the VM has selected for compilation by Graal. " +
 115                        "A method not matching the filter is redirected to a lower tier compiler. " +
 116                        "The filter format is the same as for the MethodFilter option.", type = OptionType.Expert)
 117         public static final OptionKey<String> GraalCompileOnly = new OptionKey<>(null);
 118         // @formatter:on
 119 
 120     }
 121 
 122     @Override
 123     public HotSpotGraalCompiler createCompiler(JVMCIRuntime runtime) {
 124         CompilerConfigurationFactory factory = CompilerConfigurationFactory.selectFactory(null, options);
 125         if (isGraalPredicate != null) {
 126             isGraalPredicate.onCompilerConfigurationFactorySelection(factory);
 127         }
 128         HotSpotGraalCompiler compiler = createCompiler("VM", runtime, options, factory);
 129         // Only the HotSpotGraalRuntime associated with the compiler created via
 130         // jdk.vm.ci.runtime.JVMCIRuntime.getCompiler() is registered for receiving
 131         // VM events.
 132         locator.onCompilerCreation(compiler);
 133         return compiler;
 134     }
 135 
 136     /**
 137      * Creates a new {@link HotSpotGraalRuntime} object and a new {@link HotSpotGraalCompiler} and
 138      * returns the latter.
 139      *
 140      * @param runtimeNameQualifier a qualifier to be added to the {@linkplain GraalRuntime#getName()
 141      *            name} of the {@linkplain HotSpotGraalCompiler#getGraalRuntime() runtime} created
 142      *            by this method
 143      * @param runtime the JVMCI runtime on which the {@link HotSpotGraalRuntime} is built
 144      * @param compilerConfigurationFactory factory for the {@link CompilerConfiguration}
 145      */
 146     @SuppressWarnings("try")
 147     public static HotSpotGraalCompiler createCompiler(String runtimeNameQualifier, JVMCIRuntime runtime, OptionValues options, CompilerConfigurationFactory compilerConfigurationFactory) {
 148         HotSpotJVMCIRuntime jvmciRuntime = (HotSpotJVMCIRuntime) runtime;
 149         try (InitTimer t = timer("HotSpotGraalRuntime.<init>")) {
 150             HotSpotGraalRuntime graalRuntime = new HotSpotGraalRuntime(runtimeNameQualifier, jvmciRuntime, compilerConfigurationFactory, options);
 151             return new HotSpotGraalCompiler(jvmciRuntime, graalRuntime, graalRuntime.getOptions());
 152         }
 153     }
 154 
 155     @Override
 156     public CompilationLevelAdjustment getCompilationLevelAdjustment() {
 157         if (graalCompileOnlyFilter != null) {
 158             return CompilationLevelAdjustment.ByFullSignature;
 159         }
 160         if (compileGraalWithC1Only) {
 161             // We only decide using the class declaring the method
 162             // so no need to have the method name and signature
 163             // symbols converted to a String.
 164             return CompilationLevelAdjustment.ByHolder;
 165         }
 166         return CompilationLevelAdjustment.None;
 167     }
 168 
 169     @Override
 170     public CompilationLevel adjustCompilationLevel(Object declaringClassObject, String name, String signature, boolean isOsr, CompilationLevel level) {
 171         if (declaringClassObject instanceof String) {
 172             // This must be SVM mode in which case only GraalCompileC1Only matters since Graal and
 173             // JVMCI are already compiled.
 174             return checkGraalCompileOnlyFilter((String) declaringClassObject, name, signature, level);
 175         }
 176         Class<?> declaringClass = (Class<?>) declaringClassObject;
 177         return adjustCompilationLevelInternal(declaringClass, name, signature, level);
 178     }
 179 
 180     static {
 181         // Fail-fast detection for package renaming to guard use of package
 182         // prefixes in adjustCompilationLevelInternal.
 183         assert jdk.vm.ci.services.Services.class.getName().equals("jdk.vm.ci.services.Services");
 184         assert HotSpotGraalCompilerFactory.class.getName().equals("org.graalvm.compiler.hotspot.HotSpotGraalCompilerFactory");
 185     }
 186 
 187     private CompilationLevel adjustCompilationLevelInternal(Class<?> declaringClass, String name, String signature, CompilationLevel level) {
 188         if (compileGraalWithC1Only) {
 189             if (level.ordinal() > CompilationLevel.Simple.ordinal()) {
 190                 if (isGraalPredicate.apply(declaringClass)) {
 191                     return CompilationLevel.Simple;
 192                 }
 193             }
 194         }
 195         return checkGraalCompileOnlyFilter(declaringClass.getName(), name, signature, level);
 196     }
 197 
 198     public static CompilationLevel checkGraalCompileOnlyFilter(String declaringClassName, String name, String signature, CompilationLevel level) {
 199         if (graalCompileOnlyFilter != null) {
 200             if (level == CompilationLevel.FullOptimization) {
 201                 HotSpotSignature sig = null;
 202                 for (MethodFilter filter : graalCompileOnlyFilter) {
 203                     if (filter.hasSignature() && sig == null) {
 204                         sig = new HotSpotSignature(HotSpotJVMCIRuntime.runtime(), signature);
 205                     }
 206                     if (filter.matches(declaringClassName, name, sig)) {
 207                         return level;
 208                     }
 209                 }
 210                 return CompilationLevel.Simple;
 211             }
 212         }
 213         return level;
 214     }
 215 }