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