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.HotSpotVMEventListener; 46 import jdk.vm.ci.inittimer.InitTimer; 47 import jdk.vm.ci.inittimer.SuppressFBWarnings; 48 import jdk.vm.ci.meta.JVMCIMetaAccessContext; 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.services.Services; 56 import jdk.internal.misc.VM; 57 58 //JaCoCo Exclude 59 60 /** 61 * HotSpot implementation of a JVMCI runtime. 62 * 63 * The initialization of this class is very fragile since it's initialized both through 64 * {@link JVMCI#initialize()} or through calling {@link HotSpotJVMCIRuntime#runtime()} and 65 * {@link HotSpotJVMCIRuntime#runtime()} is also called by {@link JVMCI#initialize()}. So this class 66 * can't have a static initializer and any required initialization must be done as part of 67 * {@link #runtime()}. This allows the initialization to funnel back through 68 * {@link JVMCI#initialize()} without deadlocking. 69 */ 70 public final class HotSpotJVMCIRuntime implements HotSpotJVMCIRuntimeProvider, HotSpotProxified { 71 72 @SuppressWarnings("try") 73 static class DelayedInit { 74 private static final HotSpotJVMCIRuntime instance; 75 76 static { 77 try (InitTimer t = timer("HotSpotJVMCIRuntime.<init>")) { 78 instance = new HotSpotJVMCIRuntime(); 79 } 80 } 81 } 82 83 /** 84 * Gets the singleton {@link HotSpotJVMCIRuntime} object. 85 */ 86 public static HotSpotJVMCIRuntime runtime() { 87 JVMCI.initialize(); 88 return DelayedInit.instance; 89 } 90 91 /** 92 * A list of all supported JVMCI options. 93 */ 94 public enum Option { 95 Compiler(String.class, null, "Selects the system compiler."), 96 ImplicitStableValues(boolean.class, true, "Mark well-known stable fields as such."), 97 // Note: The following one is not used (see InitTimer.ENABLED). 98 InitTimer(boolean.class, false, "Specifies if initialization timing is enabled."), 99 PrintConfig(boolean.class, false, "Prints all HotSpotVMConfig fields."), 100 PrintFlags(boolean.class, false, "Prints all JVMCI flags and exits."), 101 ShowFlags(boolean.class, false, "Prints all JVMCI flags and continues."), 102 TraceMethodDataFilter(String.class, null, ""), 103 TrustFinalDefaultFields(boolean.class, true, "Determines whether to treat final fields with default values as constant."); 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 volatile JVMCICompiler compiler; 208 protected final JVMCIMetaAccessContext metaAccessContext; 209 210 private final Map<Class<? extends Architecture>, JVMCIBackend> backends = new HashMap<>(); 211 212 private final Iterable<HotSpotVMEventListener> vmEventListeners; 213 214 @SuppressWarnings("unused") private final String[] trivialPrefixes; 215 216 @SuppressWarnings("try") 217 private HotSpotJVMCIRuntime() { 218 compilerToVm = new CompilerToVM(); 219 220 try (InitTimer t = timer("HotSpotVMConfig<init>")) { 221 config = new HotSpotVMConfig(compilerToVm); 222 } 223 224 String hostArchitecture = config.getHostArchitectureName(); 225 226 HotSpotJVMCIBackendFactory factory; 227 try (InitTimer t = timer("find factory:", hostArchitecture)) { 228 factory = findFactory(hostArchitecture); 229 } 230 231 try (InitTimer t = timer("create JVMCI backend:", hostArchitecture)) { 232 hostBackend = registerBackend(factory.createJVMCIBackend(this, null)); 233 } 234 235 vmEventListeners = Services.load(HotSpotVMEventListener.class); 236 237 JVMCIMetaAccessContext context = null; 238 for (HotSpotVMEventListener vmEventListener : vmEventListeners) { 239 context = vmEventListener.createMetaAccessContext(this); 240 if (context != null) { 241 break; 242 } 243 } 244 if (context == null) { 245 context = new HotSpotJVMCIMetaAccessContext(); 246 } 247 metaAccessContext = context; 248 249 boolean printFlags = Option.PrintFlags.getBoolean(); 250 boolean showFlags = Option.ShowFlags.getBoolean(); 251 if (printFlags || showFlags) { 252 Option.printFlags(System.out); 253 if (printFlags) { 254 System.exit(0); 255 } 256 } 257 258 if (Option.PrintConfig.getBoolean()) { 259 printConfig(config, compilerToVm); 260 } 261 262 trivialPrefixes = HotSpotJVMCICompilerConfig.getCompilerFactory().getTrivialPrefixes(); 263 } 264 265 private JVMCIBackend registerBackend(JVMCIBackend backend) { 266 Class<? extends Architecture> arch = backend.getCodeCache().getTarget().arch.getClass(); 267 JVMCIBackend oldValue = backends.put(arch, backend); 268 assert oldValue == null : "cannot overwrite existing backend for architecture " + arch.getSimpleName(); 269 return backend; 270 } 271 272 public ResolvedJavaType fromClass(Class<?> javaClass) { 273 return metaAccessContext.fromClass(javaClass); 274 } 275 276 public HotSpotVMConfig getConfig() { 277 return config; 278 } 279 280 public CompilerToVM getCompilerToVM() { 281 return compilerToVm; 282 } 283 284 public JVMCIMetaAccessContext getMetaAccessContext() { 285 return metaAccessContext; 286 } 287 288 public JVMCICompiler getCompiler() { 289 if (compiler == null) { 290 synchronized (this) { 291 if (compiler == null) { 292 compiler = HotSpotJVMCICompilerConfig.getCompilerFactory().createCompiler(this); 293 } 294 } 295 } 296 return compiler; 297 } 298 299 public JavaType lookupType(String name, HotSpotResolvedObjectType accessingType, boolean resolve) { 300 Objects.requireNonNull(accessingType, "cannot resolve type without an accessing class"); 301 // If the name represents a primitive type we can short-circuit the lookup. 302 if (name.length() == 1) { 303 JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0)); 304 return fromClass(kind.toJavaClass()); 305 } 306 307 // Resolve non-primitive types in the VM. 308 HotSpotResolvedObjectTypeImpl hsAccessingType = (HotSpotResolvedObjectTypeImpl) accessingType; 309 final HotSpotResolvedObjectTypeImpl klass = compilerToVm.lookupType(name, hsAccessingType.mirror(), resolve); 310 311 if (klass == null) { 312 assert resolve == false; 313 return HotSpotUnresolvedJavaType.create(this, name); 314 } 315 return klass; 316 } 317 318 public JVMCIBackend getHostJVMCIBackend() { 319 return hostBackend; 320 } 321 322 public <T extends Architecture> JVMCIBackend getJVMCIBackend(Class<T> arch) { 323 assert arch != Architecture.class; 324 return backends.get(arch); 325 } 326 327 public Map<Class<? extends Architecture>, JVMCIBackend> getJVMCIBackends() { 328 return Collections.unmodifiableMap(backends); 329 } 330 331 /** 332 * Called from the VM. 333 */ 334 @SuppressWarnings({"unused"}) 335 private CompilationRequestResult compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long jvmciEnv, int id) { 336 CompilationRequestResult result = getCompiler().compileMethod(new HotSpotCompilationRequest(method, entryBCI, jvmciEnv, id)); 337 assert result != null : "compileMethod must always return something"; 338 return result; 339 } 340 341 /** 342 * Shuts down the runtime. 343 * 344 * Called from the VM. 345 */ 346 @SuppressWarnings({"unused"}) 347 private void shutdown() throws Exception { 348 for (HotSpotVMEventListener vmEventListener : vmEventListeners) { 349 vmEventListener.notifyShutdown(); 350 } 351 } 352 353 /** 354 * Notify on completion of a bootstrap. 355 * 356 * Called from the VM. 357 */ 358 @SuppressWarnings({"unused"}) 359 private void bootstrapFinished() throws Exception { 360 for (HotSpotVMEventListener vmEventListener : vmEventListeners) { 361 vmEventListener.notifyBootstrapFinished(); 362 } 363 } 364 365 /** 366 * Notify on successful install into the CodeCache. 367 * 368 * @param hotSpotCodeCacheProvider 369 * @param installedCode 370 * @param compiledCode 371 */ 372 void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompiledCode compiledCode) { 373 for (HotSpotVMEventListener vmEventListener : vmEventListeners) { 374 vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compiledCode); 375 } 376 } 377 378 private static void printConfig(HotSpotVMConfig config, CompilerToVM vm) { 379 Field[] fields = config.getClass().getDeclaredFields(); 380 Map<String, Field> sortedFields = new TreeMap<>(); 381 for (Field f : fields) { 382 if (!f.isSynthetic() && !Modifier.isStatic(f.getModifiers())) { 383 f.setAccessible(true); 384 sortedFields.put(f.getName(), f); 385 } 386 } 387 for (Field f : sortedFields.values()) { 388 try { 389 String line = String.format("%9s %-40s = %s%n", f.getType().getSimpleName(), f.getName(), pretty(f.get(config))); 390 byte[] lineBytes = line.getBytes(); 391 vm.writeDebugOutput(lineBytes, 0, lineBytes.length); 392 vm.flushDebugOutput(); 393 } catch (Exception e) { 394 } 395 } 396 } 397 398 private static String pretty(Object value) { 399 if (value == null) { 400 return "null"; 401 } 402 403 Class<?> klass = value.getClass(); 404 if (value instanceof String) { 405 return "\"" + value + "\""; 406 } else if (value instanceof Method) { 407 return "method \"" + ((Method) value).getName() + "\""; 408 } else if (value instanceof Class<?>) { 409 return "class \"" + ((Class<?>) value).getSimpleName() + "\""; 410 } else if (value instanceof Integer) { 411 if ((Integer) value < 10) { 412 return value.toString(); 413 } 414 return value + " (0x" + Integer.toHexString((Integer) value) + ")"; 415 } else if (value instanceof Long) { 416 if ((Long) value < 10 && (Long) value > -10) { 417 return value + "l"; 418 } 419 return value + "l (0x" + Long.toHexString((Long) value) + "l)"; 420 } else if (klass.isArray()) { 421 StringBuilder str = new StringBuilder(); 422 int dimensions = 0; 423 while (klass.isArray()) { 424 dimensions++; 425 klass = klass.getComponentType(); 426 } 427 int length = Array.getLength(value); 428 str.append(klass.getSimpleName()).append('[').append(length).append(']'); 429 for (int i = 1; i < dimensions; i++) { 430 str.append("[]"); 431 } 432 str.append(" {"); 433 for (int i = 0; i < length; i++) { 434 str.append(pretty(Array.get(value, i))); 435 if (i < length - 1) { 436 str.append(", "); 437 } 438 } 439 str.append('}'); 440 return str.toString(); 441 } 442 return value.toString(); 443 } 444 445 public OutputStream getLogStream() { 446 return new OutputStream() { 447 448 @Override 449 public void write(byte[] b, int off, int len) throws IOException { 450 if (b == null) { 451 throw new NullPointerException(); 452 } else if (off < 0 || off > b.length || len < 0 || (off + len) > b.length || (off + len) < 0) { 453 throw new IndexOutOfBoundsException(); 454 } else if (len == 0) { 455 return; 456 } 457 compilerToVm.writeDebugOutput(b, off, len); 458 } 459 460 @Override 461 public void write(int b) throws IOException { 462 write(new byte[]{(byte) b}, 0, 1); 463 } 464 465 @Override 466 public void flush() throws IOException { 467 compilerToVm.flushDebugOutput(); 468 } 469 }; 470 } 471 472 /** 473 * Collects the current values of all JVMCI benchmark counters, summed up over all threads. 474 */ 475 public long[] collectCounters() { 476 return compilerToVm.collectCounters(); 477 } 478 }