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 }