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