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