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