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