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