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