1 /*
   2  * Copyright (c) 2015, 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  */
  23 package org.graalvm.compiler.hotspot;
  24 
  25 import static jdk.vm.ci.common.InitTimer.timer;
  26 import static org.graalvm.compiler.options.OptionValue.PROFILE_OPTIONVALUE_PROPERTY_NAME;
  27 
  28 import java.io.File;
  29 import java.io.FileReader;
  30 import java.io.IOException;
  31 import java.io.PrintStream;
  32 import java.util.HashMap;
  33 import java.util.Map;
  34 import java.util.Map.Entry;
  35 import java.util.Properties;
  36 import java.util.ServiceLoader;
  37 
  38 import org.graalvm.compiler.debug.MethodFilter;
  39 import org.graalvm.compiler.options.Option;
  40 import org.graalvm.compiler.options.OptionDescriptors;
  41 import org.graalvm.compiler.options.OptionType;
  42 import org.graalvm.compiler.options.OptionValue;
  43 import org.graalvm.compiler.options.OptionsParser;
  44 import org.graalvm.compiler.phases.tiers.CompilerConfiguration;
  45 
  46 import jdk.vm.ci.common.InitTimer;
  47 import jdk.vm.ci.hotspot.HotSpotJVMCICompilerFactory;
  48 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
  49 import jdk.vm.ci.hotspot.HotSpotSignature;
  50 import jdk.vm.ci.runtime.JVMCIRuntime;
  51 import jdk.vm.ci.services.Services;
  52 
  53 public final class HotSpotGraalCompilerFactory extends HotSpotJVMCICompilerFactory {
  54 
  55     /**
  56      * The name of the system property specifying a file containing extra Graal option settings.
  57      */
  58     private static final String GRAAL_OPTIONS_FILE_PROPERTY_NAME = "graal.options.file";
  59 
  60     /**
  61      * The name of the system property specifying the Graal version.
  62      */
  63     private static final String GRAAL_VERSION_PROPERTY_NAME = "graal.version";
  64 
  65     /**
  66      * The prefix for system properties that correspond to {@link Option} annotated fields. A field
  67      * named {@code MyOption} will have its value set from a system property with the name
  68      * {@code GRAAL_OPTION_PROPERTY_PREFIX + "MyOption"}.
  69      */
  70     public static final String GRAAL_OPTION_PROPERTY_PREFIX = "graal.";
  71 
  72     private static MethodFilter[] graalCompileOnlyFilter;
  73 
  74     /**
  75      * Gets the system property assignment that would set the current value for a given option.
  76      */
  77     public static String asSystemPropertySetting(OptionValue<?> value) {
  78         return GRAAL_OPTION_PROPERTY_PREFIX + value.getName() + "=" + value.getValue();
  79     }
  80 
  81     private final HotSpotGraalJVMCIServiceLocator locator;
  82 
  83     HotSpotGraalCompilerFactory(HotSpotGraalJVMCIServiceLocator locator) {
  84         this.locator = locator;
  85     }
  86 
  87     @Override
  88     public String getCompilerName() {
  89         return "graal";
  90     }
  91 
  92     @Override
  93     public void onSelection() {
  94         initializeOptions();
  95         JVMCIVersionCheck.check(false);
  96     }
  97 
  98     @Override
  99     public void printProperties(PrintStream out) {
 100         ServiceLoader<OptionDescriptors> loader = ServiceLoader.load(OptionDescriptors.class, OptionDescriptors.class.getClassLoader());
 101         out.println("[Graal properties]");
 102         OptionsParser.printFlags(loader, out, allOptionsSettings.keySet(), GRAAL_OPTION_PROPERTY_PREFIX);
 103     }
 104 
 105     static class Options {
 106 
 107         // @formatter:off
 108         @Option(help = "In tiered mode compile Graal and JVMCI using optimized first tier code.", type = OptionType.Expert)
 109         public static final OptionValue<Boolean> CompileGraalWithC1Only = new OptionValue<>(true);
 110 
 111         @Option(help = "Hook into VM-level mechanism for denoting compilations to be performed in first tier.", type = OptionType.Expert)
 112         public static final OptionValue<Boolean> UseTrivialPrefixes = new OptionValue<>(false);
 113 
 114         @Option(help = "A method filter selecting what should be compiled by Graal.  All other requests will be reduced to CompilationLevel.Simple.", type = OptionType.Expert)
 115         public static final OptionValue<String> GraalCompileOnly = new OptionValue<>(null);
 116         // @formatter:on
 117 
 118     }
 119 
 120     private static Map<String, String> allOptionsSettings;
 121 
 122     /**
 123      * Initializes options if they haven't already been initialized.
 124      *
 125      * Initialization means first parsing the options in the file denoted by the
 126      * {@code VM.getSavedProperty(String) saved} system property named
 127      * {@value HotSpotGraalCompilerFactory#GRAAL_OPTIONS_FILE_PROPERTY_NAME} if the file exists
 128      * followed by parsing the options encoded in saved system properties whose names start with
 129      * {@value #GRAAL_OPTION_PROPERTY_PREFIX}. Key/value pairs are parsed from the aforementioned
 130      * file with {@link Properties#load(java.io.Reader)}.
 131      */
 132     @SuppressWarnings("try")
 133     private static synchronized void initializeOptions() {
 134         if (allOptionsSettings == null) {
 135             try (InitTimer t = timer("InitializeOptions")) {
 136                 ServiceLoader<OptionDescriptors> loader = ServiceLoader.load(OptionDescriptors.class, OptionDescriptors.class.getClassLoader());
 137                 Map<String, String> savedProps = Services.getSavedProperties();
 138                 String optionsFile = savedProps.get(GRAAL_OPTIONS_FILE_PROPERTY_NAME);
 139 
 140                 if (optionsFile != null) {
 141                     File graalOptions = new File(optionsFile);
 142                     if (graalOptions.exists()) {
 143                         try (FileReader fr = new FileReader(graalOptions)) {
 144                             Properties props = new Properties();
 145                             props.load(fr);
 146                             Map<String, String> optionSettings = new HashMap<>();
 147                             for (Map.Entry<Object, Object> e : props.entrySet()) {
 148                                 optionSettings.put((String) e.getKey(), (String) e.getValue());
 149                             }
 150                             try {
 151                                 OptionsParser.parseOptions(optionSettings, null, loader);
 152                                 if (allOptionsSettings == null) {
 153                                     allOptionsSettings = new HashMap<>(optionSettings);
 154                                 } else {
 155                                     allOptionsSettings.putAll(optionSettings);
 156                                 }
 157                             } catch (Throwable e) {
 158                                 throw new InternalError("Error parsing an option from " + graalOptions, e);
 159                             }
 160                         } catch (IOException e) {
 161                             throw new InternalError("Error reading " + graalOptions, e);
 162                         }
 163                     }
 164                 }
 165 
 166                 Map<String, String> optionSettings = new HashMap<>();
 167                 for (Entry<String, String> e : savedProps.entrySet()) {
 168                     String name = e.getKey();
 169                     if (name.startsWith(GRAAL_OPTION_PROPERTY_PREFIX)) {
 170                         if (name.equals("graal.PrintFlags") || name.equals("graal.ShowFlags")) {
 171                             System.err.println("The " + name + " option has been removed and will be ignored. Use -XX:+JVMCIPrintProperties instead.");
 172                         } else if (name.equals(GRAAL_OPTIONS_FILE_PROPERTY_NAME) || name.equals(GRAAL_VERSION_PROPERTY_NAME) || name.equals(PROFILE_OPTIONVALUE_PROPERTY_NAME)) {
 173                             // Ignore well known properties that do not denote an option
 174                         } else {
 175                             String value = e.getValue();
 176                             optionSettings.put(name.substring(GRAAL_OPTION_PROPERTY_PREFIX.length()), value);
 177                         }
 178                     }
 179                 }
 180 
 181                 OptionsParser.parseOptions(optionSettings, null, loader);
 182 
 183                 if (allOptionsSettings == null) {
 184                     allOptionsSettings = optionSettings;
 185                 } else {
 186                     allOptionsSettings.putAll(optionSettings);
 187                 }
 188 
 189                 if (Options.GraalCompileOnly.getValue() != null) {
 190                     graalCompileOnlyFilter = MethodFilter.parse(Options.GraalCompileOnly.getValue());
 191                     if (graalCompileOnlyFilter.length == 0) {
 192                         graalCompileOnlyFilter = null;
 193                     }
 194                 }
 195                 if (graalCompileOnlyFilter != null || !Options.UseTrivialPrefixes.getValue()) {
 196                     /*
 197                      * Exercise this code path early to encourage loading now. This doesn't solve
 198                      * problem of deadlock during class loading but seems to eliminate it in
 199                      * practice.
 200                      */
 201                     adjustCompilationLevelInternal(Object.class, "hashCode", "()I", CompilationLevel.FullOptimization);
 202                     adjustCompilationLevelInternal(Object.class, "hashCode", "()I", CompilationLevel.Simple);
 203                 }
 204             }
 205         }
 206     }
 207 
 208     @Override
 209     public HotSpotGraalCompiler createCompiler(JVMCIRuntime runtime) {
 210         HotSpotGraalCompiler compiler = createCompiler(runtime, CompilerConfigurationFactory.selectFactory(null));
 211         // Only the HotSpotGraalRuntime associated with the compiler created via
 212         // jdk.vm.ci.runtime.JVMCIRuntime.getCompiler() is registered for receiving
 213         // VM events.
 214         locator.onCompilerCreation(compiler);
 215         return compiler;
 216     }
 217 
 218     /**
 219      * Creates a new {@link HotSpotGraalRuntime} object and a new {@link HotSpotGraalCompiler} and
 220      * returns the latter.
 221      *
 222      * @param runtime the JVMCI runtime on which the {@link HotSpotGraalRuntime} is built
 223      * @param compilerConfigurationFactory factory for the {@link CompilerConfiguration}
 224      */
 225     @SuppressWarnings("try")
 226     public static HotSpotGraalCompiler createCompiler(JVMCIRuntime runtime, CompilerConfigurationFactory compilerConfigurationFactory) {
 227         HotSpotJVMCIRuntime jvmciRuntime = (HotSpotJVMCIRuntime) runtime;
 228         try (InitTimer t = timer("HotSpotGraalRuntime.<init>")) {
 229             HotSpotGraalRuntime graalRuntime = new HotSpotGraalRuntime(jvmciRuntime, compilerConfigurationFactory);
 230             return new HotSpotGraalCompiler(jvmciRuntime, graalRuntime);
 231         }
 232     }
 233 
 234     @Override
 235     public String[] getTrivialPrefixes() {
 236         if (Options.UseTrivialPrefixes.getValue()) {
 237             if (Options.CompileGraalWithC1Only.getValue()) {
 238                 return new String[]{"jdk/vm/ci", "org/graalvm/compiler", "com/oracle/graal"};
 239             }
 240         }
 241         return null;
 242     }
 243 
 244     @Override
 245     public CompilationLevelAdjustment getCompilationLevelAdjustment() {
 246         if (graalCompileOnlyFilter != null) {
 247             return CompilationLevelAdjustment.ByFullSignature;
 248         }
 249         if (!Options.UseTrivialPrefixes.getValue()) {
 250             if (Options.CompileGraalWithC1Only.getValue()) {
 251                 // We only decide using the class declaring the method
 252                 // so no need to have the method name and signature
 253                 // symbols converted to a String.
 254                 return CompilationLevelAdjustment.ByHolder;
 255             }
 256         }
 257         return CompilationLevelAdjustment.None;
 258     }
 259 
 260     @Override
 261     public CompilationLevel adjustCompilationLevel(Class<?> declaringClass, String name, String signature, boolean isOsr, CompilationLevel level) {
 262         return adjustCompilationLevelInternal(declaringClass, name, signature, level);
 263     }
 264 
 265     /*
 266      * This method is static so it can be exercised during initialization.
 267      */
 268     private static CompilationLevel adjustCompilationLevelInternal(Class<?> declaringClass, String name, String signature, CompilationLevel level) {
 269         if (graalCompileOnlyFilter != null) {
 270             if (level == CompilationLevel.FullOptimization) {
 271                 String declaringClassName = declaringClass.getName();
 272                 HotSpotSignature sig = null;
 273                 for (MethodFilter filter : graalCompileOnlyFilter) {
 274                     if (filter.hasSignature() && sig == null) {
 275                         sig = new HotSpotSignature(HotSpotJVMCIRuntime.runtime(), signature);
 276                     }
 277                     if (filter.matches(declaringClassName, name, sig)) {
 278                         return level;
 279                     }
 280                 }
 281                 return CompilationLevel.Simple;
 282             }
 283         }
 284         if (level.ordinal() > CompilationLevel.Simple.ordinal()) {
 285             String declaringClassName = declaringClass.getName();
 286             if (declaringClassName.startsWith("jdk.vm.ci") || declaringClassName.startsWith("org.graalvm.compiler") || declaringClassName.startsWith("com.oracle.graal")) {
 287                 return CompilationLevel.Simple;
 288             }
 289         }
 290         return level;
 291     }
 292 }