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.ArrayList; 31 import java.util.Collections; 32 import java.util.HashMap; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Objects; 36 import java.util.ServiceLoader; 37 import java.util.TreeMap; 38 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.services.HotSpotJVMCICompilerFactory; 47 import jdk.vm.ci.hotspot.services.HotSpotJVMCICompilerFactory.CompilationLevel; 48 import jdk.vm.ci.hotspot.services.HotSpotVMEventListener; 49 import jdk.vm.ci.meta.JavaKind; 50 import jdk.vm.ci.meta.JavaType; 51 import jdk.vm.ci.meta.ResolvedJavaType; 52 import jdk.vm.ci.runtime.JVMCI; 53 import jdk.vm.ci.runtime.JVMCIBackend; 54 import jdk.vm.ci.runtime.JVMCICompiler; 55 import jdk.vm.ci.runtime.services.JVMCICompilerFactory; 56 import jdk.vm.ci.services.Services; 57 58 /** 59 * HotSpot implementation of a JVMCI runtime. 60 * 61 * The initialization of this class is very fragile since it's initialized both through 62 * {@link JVMCI#initialize()} or through calling {@link HotSpotJVMCIRuntime#runtime()} and 63 * {@link HotSpotJVMCIRuntime#runtime()} is also called by {@link JVMCI#initialize()}. So this class 64 * can't have a static initializer and any required initialization must be done as part of 65 * {@link #runtime()}. This allows the initialization to funnel back through 66 * {@link JVMCI#initialize()} without deadlocking. 67 */ 68 public final class HotSpotJVMCIRuntime implements HotSpotJVMCIRuntimeProvider { 69 70 @SuppressWarnings("try") 71 static class DelayedInit { 72 private static final HotSpotJVMCIRuntime instance; 73 74 static { 75 try (InitTimer t = timer("HotSpotJVMCIRuntime.<init>")) { 76 instance = new HotSpotJVMCIRuntime(); 77 } 78 } 79 } 80 81 /** 82 * Gets the singleton {@link HotSpotJVMCIRuntime} object. 83 */ 84 public static HotSpotJVMCIRuntime runtime() { 85 JVMCI.initialize(); 86 return DelayedInit.instance; 87 } 88 89 /** 90 * A list of all supported JVMCI options. 91 */ 92 public enum Option { 93 // @formatter:off 94 Compiler(String.class, null, "Selects the system compiler."), 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 // @formatter:on 104 105 /** 106 * The prefix for system properties that are JVMCI options. 107 */ 108 private static final String JVMCI_OPTION_PROPERTY_PREFIX = "jvmci."; 109 110 /** 111 * Marker for uninitialized flags. 112 */ 113 private static final String UNINITIALIZED = "UNINITIALIZED"; 114 115 private final Class<?> type; 116 private Object value; 117 private final Object defaultValue; 118 private boolean isDefault; 119 private final String[] helpLines; 120 121 Option(Class<?> type, Object defaultValue, String... helpLines) { 122 assert Character.isUpperCase(name().charAt(0)) : "Option name must start with upper-case letter: " + name(); 123 this.type = type; 124 this.value = UNINITIALIZED; 125 this.defaultValue = defaultValue; 126 this.helpLines = helpLines; 127 } 128 129 @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "sentinel must be String since it's a static final in an enum") 130 private Object getValue() { 131 if (value == UNINITIALIZED) { 132 String propertyValue = VM.getSavedProperty(getPropertyName()); 133 if (propertyValue == null) { 134 this.value = defaultValue; 135 this.isDefault = true; 136 } else { 137 if (type == Boolean.class) { 138 this.value = Boolean.parseBoolean(propertyValue); 139 } else if (type == String.class) { 140 this.value = propertyValue; 141 } else { 142 throw new JVMCIError("Unexpected option type " + type); 143 } 144 this.isDefault = false; 145 } 146 // Saved properties should not be interned - let's be sure 147 assert value != UNINITIALIZED; 148 } 149 return value; 150 } 151 152 /** 153 * Gets the name of system property from which this option gets its value. 154 */ 155 public String getPropertyName() { 156 return JVMCI_OPTION_PROPERTY_PREFIX + name(); 157 } 158 159 /** 160 * Returns the option's value as boolean. 161 * 162 * @return option's value 163 */ 164 public boolean getBoolean() { 165 return (boolean) getValue(); 166 } 167 168 /** 169 * Returns the option's value as String. 170 * 171 * @return option's value 172 */ 173 public String getString() { 174 return (String) getValue(); 175 } 176 177 /** 178 * Prints a description of the properties used to configure shared JVMCI code. 179 * 180 * @param out stream to print to 181 */ 182 public static void printProperties(PrintStream out) { 183 out.println("[JVMCI properties]"); 184 int typeWidth = 0; 185 int nameWidth = 0; 186 Option[] values = values(); 187 for (Option option : values) { 188 typeWidth = Math.max(typeWidth, option.type.getSimpleName().length()); 189 nameWidth = Math.max(nameWidth, option.getPropertyName().length()); 190 } 191 for (Option option : values) { 192 Object value = option.getValue(); 193 if (value instanceof String) { 194 value = '"' + String.valueOf(value) + '"'; 195 } 196 String assign = option.isDefault ? " =" : ":="; 197 String format = "%" + (typeWidth + 1) + "s %-" + (nameWidth + 1) + "s %s %s%n"; 198 out.printf(format, option.type.getSimpleName(), option.getPropertyName(), assign, value); 199 String helpFormat = "%" + (typeWidth + 1) + "s %s%n"; 200 for (String line : option.helpLines) { 201 out.printf(helpFormat, "", line); 202 } 203 } 204 } 205 } 206 207 public static HotSpotJVMCIBackendFactory findFactory(String architecture) { 208 for (HotSpotJVMCIBackendFactory factory : Services.load(HotSpotJVMCIBackendFactory.class)) { 209 if (factory.getArchitecture().equalsIgnoreCase(architecture)) { 210 return factory; 211 } 212 } 213 214 throw new JVMCIError("No JVMCI runtime available for the %s architecture", architecture); 215 } 216 217 /** 218 * Gets the kind of a word value on the {@linkplain #getHostJVMCIBackend() host} backend. 219 */ 220 public static JavaKind getHostWordKind() { 221 return runtime().getHostJVMCIBackend().getCodeCache().getTarget().wordJavaKind; 222 } 223 224 protected final CompilerToVM compilerToVm; 225 226 protected final HotSpotVMConfigStore configStore; 227 protected final HotSpotVMConfig config; 228 private final JVMCIBackend hostBackend; 229 230 private final JVMCICompilerFactory compilerFactory; 231 private final HotSpotJVMCICompilerFactory hsCompilerFactory; 232 private volatile JVMCICompiler compiler; 233 protected final HotSpotJVMCIMetaAccessContext metaAccessContext; 234 235 /** 236 * Stores the result of {@link HotSpotJVMCICompilerFactory#getCompilationLevelAdjustment} so 237 * that it can be read from the VM. 238 */ 239 @SuppressWarnings("unused") private final int compilationLevelAdjustment; 240 241 private final Map<Class<? extends Architecture>, JVMCIBackend> backends = new HashMap<>(); 242 243 private volatile List<HotSpotVMEventListener> vmEventListeners; 244 245 private Iterable<HotSpotVMEventListener> getVmEventListeners() { 246 if (vmEventListeners == null) { 247 synchronized (this) { 248 if (vmEventListeners == null) { 249 List<HotSpotVMEventListener> listeners = new ArrayList<>(); 250 for (HotSpotVMEventListener vmEventListener : ServiceLoader.load(HotSpotVMEventListener.class)) { 251 listeners.add(vmEventListener); 252 } 253 vmEventListeners = listeners; 254 } 255 } 256 } 257 return vmEventListeners; 258 } 259 260 /** 261 * Stores the result of {@link HotSpotJVMCICompilerFactory#getTrivialPrefixes()} so that it can 262 * be read from the VM. 263 */ 264 @SuppressWarnings("unused") private final String[] trivialPrefixes; 265 266 @SuppressWarnings("try") 267 private HotSpotJVMCIRuntime() { 268 compilerToVm = new CompilerToVM(); 269 270 try (InitTimer t = timer("HotSpotVMConfig<init>")) { 271 configStore = new HotSpotVMConfigStore(compilerToVm); 272 config = new HotSpotVMConfig(configStore); 273 } 274 275 String hostArchitecture = config.getHostArchitectureName(); 276 277 HotSpotJVMCIBackendFactory factory; 278 try (InitTimer t = timer("find factory:", hostArchitecture)) { 279 factory = findFactory(hostArchitecture); 280 } 281 282 try (InitTimer t = timer("create JVMCI backend:", hostArchitecture)) { 283 hostBackend = registerBackend(factory.createJVMCIBackend(this, null)); 284 } 285 286 metaAccessContext = new HotSpotJVMCIMetaAccessContext(); 287 288 compilerFactory = HotSpotJVMCICompilerConfig.getCompilerFactory(); 289 if (compilerFactory instanceof HotSpotJVMCICompilerFactory) { 290 hsCompilerFactory = (HotSpotJVMCICompilerFactory) compilerFactory; 291 trivialPrefixes = hsCompilerFactory.getTrivialPrefixes(); 292 switch (hsCompilerFactory.getCompilationLevelAdjustment()) { 293 case None: 294 compilationLevelAdjustment = config.compLevelAdjustmentNone; 295 break; 296 case ByHolder: 297 compilationLevelAdjustment = config.compLevelAdjustmentByHolder; 298 break; 299 case ByFullSignature: 300 compilationLevelAdjustment = config.compLevelAdjustmentByFullSignature; 301 break; 302 default: 303 compilationLevelAdjustment = config.compLevelAdjustmentNone; 304 break; 305 } 306 } else { 307 hsCompilerFactory = null; 308 trivialPrefixes = null; 309 compilationLevelAdjustment = config.compLevelAdjustmentNone; 310 } 311 312 if (config.getFlag("JVMCIPrintProperties", Boolean.class)) { 313 PrintStream out = new PrintStream(getLogStream()); 314 Option.printProperties(out); 315 compilerFactory.printProperties(out); 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" : 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 TreeMap<String, Long> typeSizes = new TreeMap<>(store.getTypeSizes()); 517 for (Map.Entry<String, Long> e : typeSizes.entrySet()) { 518 printConfigLine(vm, "[vmconfig:type size] %s = %d%n", e.getKey(), 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 /** 553 * Collects the current values of all JVMCI benchmark counters, summed up over all threads. 554 */ 555 public long[] collectCounters() { 556 return compilerToVm.collectCounters(); 557 } 558 }