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