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 successful install into the CodeCache. 355 * 356 * @param hotSpotCodeCacheProvider 357 * @param installedCode 358 * @param compiledCode 359 */ 360 void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompiledCode compiledCode) { 361 for (HotSpotVMEventListener vmEventListener : vmEventListeners) { 362 vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compiledCode); 363 } 364 } 365 366 private static void printConfig(HotSpotVMConfig config, CompilerToVM vm) { 367 Field[] fields = config.getClass().getDeclaredFields(); 368 Map<String, Field> sortedFields = new TreeMap<>(); 369 for (Field f : fields) { 370 if (!f.isSynthetic() && !Modifier.isStatic(f.getModifiers())) { 371 f.setAccessible(true); 372 sortedFields.put(f.getName(), f); 373 } 374 } 375 for (Field f : sortedFields.values()) { 376 try { 377 String line = String.format("%9s %-40s = %s%n", f.getType().getSimpleName(), f.getName(), pretty(f.get(config))); 378 byte[] lineBytes = line.getBytes(); 379 vm.writeDebugOutput(lineBytes, 0, lineBytes.length); 380 vm.flushDebugOutput(); 381 } catch (Exception e) { 382 } 383 } 384 } 385 386 private static String pretty(Object value) { 387 if (value == null) { 388 return "null"; 389 } 390 391 Class<?> klass = value.getClass(); 392 if (value instanceof String) { 393 return "\"" + value + "\""; 394 } else if (value instanceof Method) { 395 return "method \"" + ((Method) value).getName() + "\""; 396 } else if (value instanceof Class<?>) { 397 return "class \"" + ((Class<?>) value).getSimpleName() + "\""; 398 } else if (value instanceof Integer) { 399 if ((Integer) value < 10) { 400 return value.toString(); 401 } 402 return value + " (0x" + Integer.toHexString((Integer) value) + ")"; 403 } else if (value instanceof Long) { 404 if ((Long) value < 10 && (Long) value > -10) { 405 return value + "l"; 406 } 407 return value + "l (0x" + Long.toHexString((Long) value) + "l)"; 408 } else if (klass.isArray()) { 409 StringBuilder str = new StringBuilder(); 410 int dimensions = 0; 411 while (klass.isArray()) { 412 dimensions++; 413 klass = klass.getComponentType(); 414 } 415 int length = Array.getLength(value); 416 str.append(klass.getSimpleName()).append('[').append(length).append(']'); 417 for (int i = 1; i < dimensions; i++) { 418 str.append("[]"); 419 } 420 str.append(" {"); 421 for (int i = 0; i < length; i++) { 422 str.append(pretty(Array.get(value, i))); 423 if (i < length - 1) { 424 str.append(", "); 425 } 426 } 427 str.append('}'); 428 return str.toString(); 429 } 430 return value.toString(); 431 } 432 433 public OutputStream getLogStream() { 434 return new OutputStream() { 435 436 @Override 437 public void write(byte[] b, int off, int len) throws IOException { 438 if (b == null) { 439 throw new NullPointerException(); 440 } else if (off < 0 || off > b.length || len < 0 || (off + len) > b.length || (off + len) < 0) { 441 throw new IndexOutOfBoundsException(); 442 } else if (len == 0) { 443 return; 444 } 445 compilerToVm.writeDebugOutput(b, off, len); 446 } 447 448 @Override 449 public void write(int b) throws IOException { 450 write(new byte[]{(byte) b}, 0, 1); 451 } 452 453 @Override 454 public void flush() throws IOException { 455 compilerToVm.flushDebugOutput(); 456 } 457 }; 458 } 459 460 /** 461 * Collects the current values of all JVMCI benchmark counters, summed up over all threads. 462 */ 463 public long[] collectCounters() { 464 return compilerToVm.collectCounters(); 465 } 466 }