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