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 jdk.vm.ci.hotspot; 24 25 import static jdk.vm.ci.common.InitTimer.timer; 26 27 import java.io.IOException; 28 import java.io.OutputStream; 29 import java.io.PrintStream; 30 import java.util.Collections; 31 import java.util.HashMap; 32 import java.util.List; 33 import java.util.Map; 34 import java.util.Objects; 35 import java.util.ServiceLoader; 36 import java.util.TreeMap; 37 38 import jdk.internal.misc.VM; 39 import jdk.vm.ci.code.Architecture; 40 import jdk.vm.ci.code.CompilationRequestResult; 41 import jdk.vm.ci.code.CompiledCode; 42 import jdk.vm.ci.code.InstalledCode; 43 import jdk.vm.ci.common.InitTimer; 44 import jdk.vm.ci.common.JVMCIError; 45 import jdk.vm.ci.hotspot.HotSpotJVMCICompilerFactory.CompilationLevel; 46 import jdk.vm.ci.meta.JavaKind; 47 import jdk.vm.ci.meta.JavaType; 48 import jdk.vm.ci.meta.ResolvedJavaType; 49 import jdk.vm.ci.runtime.JVMCI; 50 import jdk.vm.ci.runtime.JVMCIBackend; 51 import jdk.vm.ci.runtime.JVMCICompiler; 52 import jdk.vm.ci.runtime.JVMCICompilerFactory; 53 import jdk.vm.ci.services.JVMCIServiceLocator; 54 55 /** 56 * HotSpot implementation of a JVMCI runtime. 57 * 58 * The initialization of this class is very fragile since it's initialized both through 59 * {@link JVMCI#initialize()} or through calling {@link HotSpotJVMCIRuntime#runtime()} and 60 * {@link HotSpotJVMCIRuntime#runtime()} is also called by {@link JVMCI#initialize()}. So this class 61 * can't have a static initializer and any required initialization must be done as part of 62 * {@link #runtime()}. This allows the initialization to funnel back through 63 * {@link JVMCI#initialize()} without deadlocking. 64 */ 65 public final class HotSpotJVMCIRuntime implements HotSpotJVMCIRuntimeProvider { 66 67 @SuppressWarnings("try") 68 static class DelayedInit { 69 private static final HotSpotJVMCIRuntime instance; 70 71 static { 72 try (InitTimer t = timer("HotSpotJVMCIRuntime.<init>")) { 73 instance = new HotSpotJVMCIRuntime(); 74 } 75 } 76 } 77 78 /** 79 * Gets the singleton {@link HotSpotJVMCIRuntime} object. 80 */ 81 public static HotSpotJVMCIRuntime runtime() { 82 JVMCI.initialize(); 83 return DelayedInit.instance; 84 } 85 86 /** 87 * A list of all supported JVMCI options. 88 */ 89 public enum Option { 90 // @formatter:off 91 Compiler(String.class, null, "Selects the system compiler. This must match the getCompilerName() value returned " + 92 "by a jdk.vm.ci.runtime.JVMCICompilerFactory provider. " + 93 "An empty string or the value \"null\" selects a compiler " + 94 "that will raise an exception upon receiving a compilation request."), 95 // Note: The following one is not used (see InitTimer.ENABLED). It is added here 96 // so that -XX:+JVMCIPrintProperties shows the option. 97 InitTimer(Boolean.class, false, "Specifies if initialization timing is enabled."), 98 PrintConfig(Boolean.class, false, "Prints VM configuration available via JVMCI."), 99 TraceMethodDataFilter(String.class, null, 100 "Enables tracing of profiling info when read by JVMCI.", 101 "Empty value: trace all methods", 102 "Non-empty value: trace methods whose fully qualified name contains the value."), 103 UseProfilingInformation(Boolean.class, true, ""); 104 // @formatter:on 105 106 /** 107 * The prefix for system properties that are JVMCI options. 108 */ 109 private static final String JVMCI_OPTION_PROPERTY_PREFIX = "jvmci."; 110 111 /** 112 * Marker for uninitialized flags. 113 */ 114 private static final String UNINITIALIZED = "UNINITIALIZED"; 115 116 private final Class<?> type; 117 private Object value; 118 private final Object defaultValue; 119 private boolean isDefault; 120 private final String[] helpLines; 121 122 Option(Class<?> type, Object defaultValue, String... helpLines) { 123 assert Character.isUpperCase(name().charAt(0)) : "Option name must start with upper-case letter: " + name(); 124 this.type = type; 125 this.value = UNINITIALIZED; 126 this.defaultValue = defaultValue; 127 this.helpLines = helpLines; 128 } 129 130 @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "sentinel must be String since it's a static final in an enum") 131 private Object getValue() { 132 if (value == UNINITIALIZED) { 133 String propertyValue = VM.getSavedProperty(getPropertyName()); 134 if (propertyValue == null) { 135 this.value = defaultValue; 136 this.isDefault = true; 137 } else { 138 if (type == Boolean.class) { 139 this.value = Boolean.parseBoolean(propertyValue); 140 } else if (type == String.class) { 141 this.value = propertyValue; 142 } else { 143 throw new JVMCIError("Unexpected option type " + type); 144 } 145 this.isDefault = false; 146 } 147 // Saved properties should not be interned - let's be sure 148 assert value != UNINITIALIZED; 149 } 150 return value; 151 } 152 153 /** 154 * Gets the name of system property from which this option gets its value. 155 */ 156 public String getPropertyName() { 157 return JVMCI_OPTION_PROPERTY_PREFIX + name(); 158 } 159 160 /** 161 * Returns the option's value as boolean. 162 * 163 * @return option's value 164 */ 165 public boolean getBoolean() { 166 return (boolean) getValue(); 167 } 168 169 /** 170 * Returns the option's value as String. 171 * 172 * @return option's value 173 */ 174 public String getString() { 175 return (String) getValue(); 176 } 177 178 private static final int PROPERTY_LINE_WIDTH = 80; 179 private static final int PROPERTY_HELP_INDENT = 10; 180 181 /** 182 * Prints a description of the properties used to configure shared JVMCI code. 183 * 184 * @param out stream to print to 185 */ 186 public static void printProperties(PrintStream out) { 187 out.println("[JVMCI properties]"); 188 Option[] values = values(); 189 for (Option option : values) { 190 Object value = option.getValue(); 191 if (value instanceof String) { 192 value = '"' + String.valueOf(value) + '"'; 193 } 194 195 String name = option.getPropertyName(); 196 String assign = option.isDefault ? "=" : ":="; 197 String typeName = option.type.getSimpleName(); 198 String linePrefix = String.format("%s %s %s ", name, assign, value); 199 int typeStartPos = PROPERTY_LINE_WIDTH - typeName.length(); 200 int linePad = typeStartPos - linePrefix.length(); 201 if (linePad > 0) { 202 out.printf("%s%-" + linePad + "s[%s]%n", linePrefix, "", typeName); 203 } else { 204 out.printf("%s[%s]%n", linePrefix, typeName); 205 } 206 for (String line : option.helpLines) { 207 out.printf("%" + PROPERTY_HELP_INDENT + "s%s%n", "", line); 208 } 209 } 210 } 211 } 212 213 public static HotSpotJVMCIBackendFactory findFactory(String architecture) { 214 for (HotSpotJVMCIBackendFactory factory : ServiceLoader.load(HotSpotJVMCIBackendFactory.class, ClassLoader.getSystemClassLoader())) { 215 if (factory.getArchitecture().equalsIgnoreCase(architecture)) { 216 return factory; 217 } 218 } 219 220 throw new JVMCIError("No JVMCI runtime available for the %s architecture", architecture); 221 } 222 223 /** 224 * Gets the kind of a word value on the {@linkplain #getHostJVMCIBackend() host} backend. 225 */ 226 public static JavaKind getHostWordKind() { 227 return runtime().getHostJVMCIBackend().getCodeCache().getTarget().wordJavaKind; 228 } 229 230 protected final CompilerToVM compilerToVm; 231 232 protected final HotSpotVMConfigStore configStore; 233 protected final HotSpotVMConfig config; 234 private final JVMCIBackend hostBackend; 235 236 private final JVMCICompilerFactory compilerFactory; 237 private final HotSpotJVMCICompilerFactory hsCompilerFactory; 238 private volatile JVMCICompiler compiler; 239 protected final HotSpotJVMCIMetaAccessContext metaAccessContext; 240 241 /** 242 * Stores the result of {@link HotSpotJVMCICompilerFactory#getCompilationLevelAdjustment} so 243 * that it can be read from the VM. 244 */ 245 @SuppressWarnings("unused") private final int compilationLevelAdjustment; 246 247 private final Map<Class<? extends Architecture>, JVMCIBackend> backends = new HashMap<>(); 248 249 private volatile List<HotSpotVMEventListener> vmEventListeners; 250 251 private Iterable<HotSpotVMEventListener> getVmEventListeners() { 252 if (vmEventListeners == null) { 253 synchronized (this) { 254 if (vmEventListeners == null) { 255 vmEventListeners = JVMCIServiceLocator.getProviders(HotSpotVMEventListener.class); 256 } 257 } 258 } 259 return vmEventListeners; 260 } 261 262 /** 263 * Stores the result of {@link HotSpotJVMCICompilerFactory#getTrivialPrefixes()} so that it can 264 * be read from the VM. 265 */ 266 @SuppressWarnings("unused") private final String[] trivialPrefixes; 267 268 @SuppressWarnings("try") 269 private HotSpotJVMCIRuntime() { 270 compilerToVm = new CompilerToVM(); 271 272 try (InitTimer t = timer("HotSpotVMConfig<init>")) { 273 configStore = new HotSpotVMConfigStore(compilerToVm); 274 config = new HotSpotVMConfig(configStore); 275 } 276 277 String hostArchitecture = config.getHostArchitectureName(); 278 279 HotSpotJVMCIBackendFactory factory; 280 try (InitTimer t = timer("find factory:", hostArchitecture)) { 281 factory = findFactory(hostArchitecture); 282 } 283 284 try (InitTimer t = timer("create JVMCI backend:", hostArchitecture)) { 285 hostBackend = registerBackend(factory.createJVMCIBackend(this, null)); 286 } 287 288 metaAccessContext = new HotSpotJVMCIMetaAccessContext(); 289 290 compilerFactory = HotSpotJVMCICompilerConfig.getCompilerFactory(); 291 if (compilerFactory instanceof HotSpotJVMCICompilerFactory) { 292 hsCompilerFactory = (HotSpotJVMCICompilerFactory) compilerFactory; 293 trivialPrefixes = hsCompilerFactory.getTrivialPrefixes(); 294 switch (hsCompilerFactory.getCompilationLevelAdjustment()) { 295 case None: 296 compilationLevelAdjustment = config.compLevelAdjustmentNone; 297 break; 298 case ByHolder: 299 compilationLevelAdjustment = config.compLevelAdjustmentByHolder; 300 break; 301 case ByFullSignature: 302 compilationLevelAdjustment = config.compLevelAdjustmentByFullSignature; 303 break; 304 default: 305 compilationLevelAdjustment = config.compLevelAdjustmentNone; 306 break; 307 } 308 } else { 309 hsCompilerFactory = null; 310 trivialPrefixes = null; 311 compilationLevelAdjustment = config.compLevelAdjustmentNone; 312 } 313 314 if (config.getFlag("JVMCIPrintProperties", Boolean.class)) { 315 PrintStream out = new PrintStream(getLogStream()); 316 Option.printProperties(out); 317 compilerFactory.printProperties(out); 318 System.exit(0); 319 } 320 321 if (Option.PrintConfig.getBoolean()) { 322 printConfig(configStore, compilerToVm); 323 } 324 } 325 326 private JVMCIBackend registerBackend(JVMCIBackend backend) { 327 Class<? extends Architecture> arch = backend.getCodeCache().getTarget().arch.getClass(); 328 JVMCIBackend oldValue = backends.put(arch, backend); 329 assert oldValue == null : "cannot overwrite existing backend for architecture " + arch.getSimpleName(); 330 return backend; 331 } 332 333 public ResolvedJavaType fromClass(Class<?> javaClass) { 334 return metaAccessContext.fromClass(javaClass); 335 } 336 337 public HotSpotVMConfigStore getConfigStore() { 338 return configStore; 339 } 340 341 public HotSpotVMConfig getConfig() { 342 return config; 343 } 344 345 public CompilerToVM getCompilerToVM() { 346 return compilerToVm; 347 } 348 349 public JVMCICompiler getCompiler() { 350 if (compiler == null) { 351 synchronized (this) { 352 if (compiler == null) { 353 compiler = compilerFactory.createCompiler(this); 354 } 355 } 356 } 357 return compiler; 358 } 359 360 public JavaType lookupType(String name, HotSpotResolvedObjectType accessingType, boolean resolve) { 361 Objects.requireNonNull(accessingType, "cannot resolve type without an accessing class"); 362 // If the name represents a primitive type we can short-circuit the lookup. 363 if (name.length() == 1) { 364 JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0)); 365 return fromClass(kind.toJavaClass()); 366 } 367 368 // Resolve non-primitive types in the VM. 369 HotSpotResolvedObjectTypeImpl hsAccessingType = (HotSpotResolvedObjectTypeImpl) accessingType; 370 try { 371 final HotSpotResolvedObjectTypeImpl klass = compilerToVm.lookupType(name, hsAccessingType.mirror(), resolve); 372 373 if (klass == null) { 374 assert resolve == false; 375 return HotSpotUnresolvedJavaType.create(this, name); 376 } 377 return klass; 378 } catch (ClassNotFoundException e) { 379 throw (NoClassDefFoundError) new NoClassDefFoundError().initCause(e); 380 } 381 } 382 383 public JVMCIBackend getHostJVMCIBackend() { 384 return hostBackend; 385 } 386 387 public <T extends Architecture> JVMCIBackend getJVMCIBackend(Class<T> arch) { 388 assert arch != Architecture.class; 389 return backends.get(arch); 390 } 391 392 public Map<Class<? extends Architecture>, JVMCIBackend> getJVMCIBackends() { 393 return Collections.unmodifiableMap(backends); 394 } 395 396 /** 397 * Called from the VM. 398 */ 399 @SuppressWarnings({"unused"}) 400 private int adjustCompilationLevel(Class<?> declaringClass, String name, String signature, boolean isOsr, int level) { 401 CompilationLevel curLevel; 402 if (level == config.compilationLevelNone) { 403 curLevel = CompilationLevel.None; 404 } else if (level == config.compilationLevelSimple) { 405 curLevel = CompilationLevel.Simple; 406 } else if (level == config.compilationLevelLimitedProfile) { 407 curLevel = CompilationLevel.LimitedProfile; 408 } else if (level == config.compilationLevelFullProfile) { 409 curLevel = CompilationLevel.FullProfile; 410 } else if (level == config.compilationLevelFullOptimization) { 411 curLevel = CompilationLevel.FullOptimization; 412 } else { 413 throw JVMCIError.shouldNotReachHere(); 414 } 415 416 switch (hsCompilerFactory.adjustCompilationLevel(declaringClass, name, signature, isOsr, curLevel)) { 417 case None: 418 return config.compilationLevelNone; 419 case Simple: 420 return config.compilationLevelSimple; 421 case LimitedProfile: 422 return config.compilationLevelLimitedProfile; 423 case FullProfile: 424 return config.compilationLevelFullProfile; 425 case FullOptimization: 426 return config.compilationLevelFullOptimization; 427 default: 428 return level; 429 } 430 } 431 432 /** 433 * Called from the VM. 434 */ 435 @SuppressWarnings({"unused"}) 436 private HotSpotCompilationRequestResult compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long jvmciEnv, int id) { 437 CompilationRequestResult result = getCompiler().compileMethod(new HotSpotCompilationRequest(method, entryBCI, jvmciEnv, id)); 438 assert result != null : "compileMethod must always return something"; 439 HotSpotCompilationRequestResult hsResult; 440 if (result instanceof HotSpotCompilationRequestResult) { 441 hsResult = (HotSpotCompilationRequestResult) result; 442 } else { 443 Object failure = result.getFailure(); 444 if (failure != null) { 445 boolean retry = false; // Be conservative with unknown compiler 446 hsResult = HotSpotCompilationRequestResult.failure(failure.toString(), retry); 447 } else { 448 int inlinedBytecodes = -1; 449 hsResult = HotSpotCompilationRequestResult.success(inlinedBytecodes); 450 } 451 } 452 453 return hsResult; 454 } 455 456 /** 457 * Shuts down the runtime. 458 * 459 * Called from the VM. 460 */ 461 @SuppressWarnings({"unused"}) 462 private void shutdown() throws Exception { 463 for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) { 464 vmEventListener.notifyShutdown(); 465 } 466 } 467 468 /** 469 * Notify on completion of a bootstrap. 470 * 471 * Called from the VM. 472 */ 473 @SuppressWarnings({"unused"}) 474 private void bootstrapFinished() throws Exception { 475 for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) { 476 vmEventListener.notifyBootstrapFinished(); 477 } 478 } 479 480 /** 481 * Notify on successful install into the CodeCache. 482 * 483 * @param hotSpotCodeCacheProvider 484 * @param installedCode 485 * @param compiledCode 486 */ 487 void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompiledCode compiledCode) { 488 for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) { 489 vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compiledCode); 490 } 491 } 492 493 @SuppressFBWarnings(value = "DM_DEFAULT_ENCODING", justification = "no localization here please!") 494 private static void printConfigLine(CompilerToVM vm, String format, Object... args) { 495 String line = String.format(format, args); 496 byte[] lineBytes = line.getBytes(); 497 vm.writeDebugOutput(lineBytes, 0, lineBytes.length); 498 vm.flushDebugOutput(); 499 } 500 501 private static void printConfig(HotSpotVMConfigStore store, CompilerToVM vm) { 502 TreeMap<String, VMField> fields = new TreeMap<>(store.getFields()); 503 for (VMField field : fields.values()) { 504 if (!field.isStatic()) { 505 printConfigLine(vm, "[vmconfig:instance field] %s %s {offset=%d[0x%x]}%n", field.type, field.name, field.offset, field.offset); 506 } else { 507 String value = field.value == null ? "null" : field.value instanceof Boolean ? field.value.toString() : String.format("%d[0x%x]", field.value, field.value); 508 printConfigLine(vm, "[vmconfig:static field] %s %s = %s {address=0x%x}%n", field.type, field.name, value, field.address); 509 } 510 } 511 TreeMap<String, VMFlag> flags = new TreeMap<>(store.getFlags()); 512 for (VMFlag flag : flags.values()) { 513 printConfigLine(vm, "[vmconfig:flag] %s %s = %s%n", flag.type, flag.name, flag.value); 514 } 515 TreeMap<String, Long> addresses = new TreeMap<>(store.getAddresses()); 516 for (Map.Entry<String, Long> e : addresses.entrySet()) { 517 printConfigLine(vm, "[vmconfig:address] %s = %d[0x%x]%n", e.getKey(), e.getValue(), e.getValue()); 518 } 519 TreeMap<String, Long> constants = new TreeMap<>(store.getConstants()); 520 for (Map.Entry<String, Long> e : constants.entrySet()) { 521 printConfigLine(vm, "[vmconfig:constant] %s = %d[0x%x]%n", e.getKey(), e.getValue(), e.getValue()); 522 } 523 for (VMIntrinsicMethod e : store.getIntrinsics()) { 524 printConfigLine(vm, "[vmconfig:intrinsic] %d = %s.%s %s%n", e.id, e.declaringClass, e.name, e.descriptor); 525 } 526 } 527 528 public OutputStream getLogStream() { 529 return new OutputStream() { 530 531 @Override 532 public void write(byte[] b, int off, int len) throws IOException { 533 if (b == null) { 534 throw new NullPointerException(); 535 } else if (off < 0 || off > b.length || len < 0 || (off + len) > b.length || (off + len) < 0) { 536 throw new IndexOutOfBoundsException(); 537 } else if (len == 0) { 538 return; 539 } 540 compilerToVm.writeDebugOutput(b, off, len); 541 } 542 543 @Override 544 public void write(int b) throws IOException { 545 write(new byte[]{(byte) b}, 0, 1); 546 } 547 548 @Override 549 public void flush() throws IOException { 550 compilerToVm.flushDebugOutput(); 551 } 552 }; 553 } 554 555 /** 556 * Collects the current values of all JVMCI benchmark counters, summed up over all threads. 557 */ 558 public long[] collectCounters() { 559 return compilerToVm.collectCounters(); 560 } 561 }