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