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