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