1 /* 2 * Copyright (c) 2015, 2020, 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.common.InitTimer.timer; 26 import static jdk.vm.ci.hotspot.HotSpotJVMCICompilerFactory.CompilationLevelAdjustment.None; 27 import static jdk.vm.ci.services.Services.IS_BUILDING_NATIVE_IMAGE; 28 import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE; 29 30 import java.io.IOException; 31 import java.io.OutputStream; 32 import java.io.PrintStream; 33 import java.io.Serializable; 34 import java.lang.invoke.CallSite; 35 import java.lang.invoke.ConstantCallSite; 36 import java.lang.invoke.MethodHandle; 37 import java.lang.ref.WeakReference; 38 import java.util.ArrayList; 39 import java.util.Collections; 40 import java.util.Formatter; 41 import java.util.HashMap; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Objects; 45 import java.util.ServiceLoader; 46 import java.util.function.Predicate; 47 48 import jdk.vm.ci.code.Architecture; 49 import jdk.vm.ci.code.CompilationRequestResult; 50 import jdk.vm.ci.code.CompiledCode; 51 import jdk.vm.ci.code.InstalledCode; 52 import jdk.vm.ci.common.InitTimer; 53 import jdk.vm.ci.common.JVMCIError; 54 import jdk.vm.ci.common.NativeImageReinitialize; 55 import jdk.vm.ci.meta.JavaKind; 56 import jdk.vm.ci.meta.JavaType; 57 import jdk.vm.ci.meta.ResolvedJavaType; 58 import jdk.vm.ci.meta.UnresolvedJavaType; 59 import jdk.vm.ci.runtime.JVMCI; 60 import jdk.vm.ci.runtime.JVMCIBackend; 61 import jdk.vm.ci.runtime.JVMCICompiler; 62 import jdk.vm.ci.runtime.JVMCICompilerFactory; 63 import jdk.vm.ci.runtime.JVMCIRuntime; 64 import jdk.vm.ci.services.JVMCIServiceLocator; 65 import jdk.vm.ci.services.Services; 66 67 /** 68 * HotSpot implementation of a JVMCI runtime. 69 */ 70 public final class HotSpotJVMCIRuntime implements JVMCIRuntime { 71 72 /** 73 * Singleton instance lazily initialized via double-checked locking. 74 */ 75 @NativeImageReinitialize private static volatile HotSpotJVMCIRuntime instance; 76 77 private HotSpotResolvedObjectTypeImpl javaLangObject; 78 private HotSpotResolvedObjectTypeImpl javaLangInvokeMethodHandle; 79 private HotSpotResolvedObjectTypeImpl constantCallSiteType; 80 private HotSpotResolvedObjectTypeImpl callSiteType; 81 private HotSpotResolvedObjectTypeImpl javaLangString; 82 private HotSpotResolvedObjectTypeImpl javaLangClass; 83 private HotSpotResolvedObjectTypeImpl throwableType; 84 private HotSpotResolvedObjectTypeImpl serializableType; 85 private HotSpotResolvedObjectTypeImpl cloneableType; 86 private HotSpotResolvedObjectTypeImpl enumType; 87 88 HotSpotResolvedObjectTypeImpl getJavaLangObject() { 89 if (javaLangObject == null) { 90 javaLangObject = (HotSpotResolvedObjectTypeImpl) fromClass(Object.class); 91 } 92 return javaLangObject; 93 } 94 95 HotSpotResolvedObjectTypeImpl getJavaLangString() { 96 if (javaLangString == null) { 97 javaLangString = (HotSpotResolvedObjectTypeImpl) fromClass(String.class); 98 } 99 return javaLangString; 100 } 101 102 HotSpotResolvedObjectTypeImpl getJavaLangClass() { 103 if (javaLangClass == null) { 104 javaLangClass = (HotSpotResolvedObjectTypeImpl) fromClass(Class.class); 105 } 106 return javaLangClass; 107 } 108 109 HotSpotResolvedObjectTypeImpl getJavaLangCloneable() { 110 if (cloneableType == null) { 111 cloneableType = (HotSpotResolvedObjectTypeImpl) fromClass(Cloneable.class); 112 } 113 return cloneableType; 114 } 115 116 HotSpotResolvedObjectTypeImpl getJavaLangSerializable() { 117 if (serializableType == null) { 118 serializableType = (HotSpotResolvedObjectTypeImpl) fromClass(Serializable.class); 119 } 120 return serializableType; 121 } 122 123 HotSpotResolvedObjectTypeImpl getJavaLangThrowable() { 124 if (throwableType == null) { 125 throwableType = (HotSpotResolvedObjectTypeImpl) fromClass(Throwable.class); 126 } 127 return throwableType; 128 } 129 130 HotSpotResolvedObjectTypeImpl getJavaLangEnum() { 131 if (enumType == null) { 132 enumType = (HotSpotResolvedObjectTypeImpl) fromClass(Enum.class); 133 } 134 return enumType; 135 } 136 137 HotSpotResolvedObjectTypeImpl getConstantCallSite() { 138 if (constantCallSiteType == null) { 139 constantCallSiteType = (HotSpotResolvedObjectTypeImpl) fromClass(ConstantCallSite.class); 140 } 141 return constantCallSiteType; 142 } 143 144 HotSpotResolvedObjectTypeImpl getCallSite() { 145 if (callSiteType == null) { 146 callSiteType = (HotSpotResolvedObjectTypeImpl) fromClass(CallSite.class); 147 } 148 return callSiteType; 149 } 150 151 HotSpotResolvedObjectType getMethodHandleClass() { 152 if (javaLangInvokeMethodHandle == null) { 153 javaLangInvokeMethodHandle = (HotSpotResolvedObjectTypeImpl) fromClass(MethodHandle.class); 154 } 155 return javaLangInvokeMethodHandle; 156 } 157 158 /** 159 * Gets the singleton {@link HotSpotJVMCIRuntime} object. 160 */ 161 @VMEntryPoint 162 @SuppressWarnings("try") 163 public static HotSpotJVMCIRuntime runtime() { 164 HotSpotJVMCIRuntime result = instance; 165 if (result == null) { 166 // Synchronize on JVMCI.class to avoid deadlock 167 // between the two JVMCI initialization paths: 168 // HotSpotJVMCIRuntime.runtime() and JVMCI.getRuntime(). 169 synchronized (JVMCI.class) { 170 result = instance; 171 if (result == null) { 172 try (InitTimer t = timer("HotSpotJVMCIRuntime.<init>")) { 173 instance = result = new HotSpotJVMCIRuntime(); 174 175 // Can only do eager initialization of the JVMCI compiler 176 // once the singleton instance is available. 177 if (result.config.getFlag("EagerJVMCI", Boolean.class)) { 178 result.getCompiler(); 179 } 180 } 181 // Ensures JVMCIRuntime::_HotSpotJVMCIRuntime_instance is 182 // initialized. 183 JVMCI.getRuntime(); 184 } 185 // Make sure all the primitive box caches are populated (required to properly 186 // materialize boxed primitives 187 // during deoptimization). 188 Boolean.valueOf(false); 189 Byte.valueOf((byte) 0); 190 Short.valueOf((short) 0); 191 Character.valueOf((char) 0); 192 Integer.valueOf(0); 193 Long.valueOf(0); 194 } 195 } 196 return result; 197 } 198 199 @VMEntryPoint 200 static Throwable decodeThrowable(String encodedThrowable) throws Throwable { 201 return TranslatedException.decodeThrowable(encodedThrowable); 202 } 203 204 @VMEntryPoint 205 static String encodeThrowable(Throwable throwable) throws Throwable { 206 return TranslatedException.encodeThrowable(throwable); 207 } 208 209 @VMEntryPoint 210 static String callToString(Object o) { 211 return o.toString(); 212 } 213 214 /** 215 * Set of recognized {@code "jvmci.*"} system properties. Entries not associated with an 216 * {@link Option} have this object as their value. 217 */ 218 static final Map<String, Object> options = new HashMap<>(); 219 static { 220 options.put("jvmci.class.path.append", options); 221 } 222 223 /** 224 * A list of all supported JVMCI options. 225 */ 226 public enum Option { 227 // @formatter:off 228 Compiler(String.class, null, "Selects the system compiler. This must match the getCompilerName() value returned " + 229 "by a jdk.vm.ci.runtime.JVMCICompilerFactory provider. " + 230 "An empty string or the value \"null\" selects a compiler " + 231 "that will raise an exception upon receiving a compilation request."), 232 // Note: The following one is not used (see InitTimer.ENABLED). It is added here 233 // so that -XX:+JVMCIPrintProperties shows the option. 234 InitTimer(Boolean.class, false, "Specifies if initialization timing is enabled."), 235 PrintConfig(Boolean.class, false, "Prints VM configuration available via JVMCI."), 236 AuditHandles(Boolean.class, false, "Record stack trace along with scoped foreign object reference wrappers " + 237 "to debug issue with a wrapper being used after its scope has closed."), 238 TraceMethodDataFilter(String.class, null, 239 "Enables tracing of profiling info when read by JVMCI.", 240 "Empty value: trace all methods", 241 "Non-empty value: trace methods whose fully qualified name contains the value."), 242 UseProfilingInformation(Boolean.class, true, ""); 243 // @formatter:on 244 245 /** 246 * The prefix for system properties that are JVMCI options. 247 */ 248 private static final String JVMCI_OPTION_PROPERTY_PREFIX = "jvmci."; 249 250 /** 251 * Sentinel for value initialized to {@code null} since {@code null} means uninitialized. 252 */ 253 private static final String NULL_VALUE = "NULL"; 254 255 private final Class<?> type; 256 @NativeImageReinitialize private Object value; 257 private final Object defaultValue; 258 private boolean isDefault = true; 259 private final String[] helpLines; 260 261 Option(Class<?> type, Object defaultValue, String... helpLines) { 262 assert Character.isUpperCase(name().charAt(0)) : "Option name must start with upper-case letter: " + name(); 263 this.type = type; 264 this.defaultValue = defaultValue; 265 this.helpLines = helpLines; 266 Object existing = options.put(getPropertyName(), this); 267 assert existing == null : getPropertyName(); 268 } 269 270 @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "sentinel must be String since it's a static final in an enum") 271 private void init(String propertyValue) { 272 assert value == null : "cannot re-initialize " + name(); 273 if (propertyValue == null) { 274 this.value = defaultValue == null ? NULL_VALUE : defaultValue; 275 this.isDefault = true; 276 } else { 277 if (type == Boolean.class) { 278 this.value = Boolean.parseBoolean(propertyValue); 279 } else if (type == String.class) { 280 this.value = propertyValue; 281 } else { 282 throw new JVMCIError("Unexpected option type " + type); 283 } 284 this.isDefault = false; 285 } 286 } 287 288 @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "sentinel must be String since it's a static final in an enum") 289 private Object getValue() { 290 if (value == NULL_VALUE) { 291 return null; 292 } 293 if (value == null) { 294 return defaultValue; 295 } 296 return value; 297 } 298 299 /** 300 * Gets the name of system property from which this option gets its value. 301 */ 302 public String getPropertyName() { 303 return JVMCI_OPTION_PROPERTY_PREFIX + name(); 304 } 305 306 /** 307 * Returns the option's value as boolean. 308 * 309 * @return option's value 310 */ 311 public boolean getBoolean() { 312 return (boolean) getValue(); 313 } 314 315 /** 316 * Returns the option's value as String. 317 * 318 * @return option's value 319 */ 320 public String getString() { 321 return (String) getValue(); 322 } 323 324 private static final int PROPERTY_LINE_WIDTH = 80; 325 private static final int PROPERTY_HELP_INDENT = 10; 326 327 /** 328 * Prints a description of the properties used to configure shared JVMCI code. 329 * 330 * @param out stream to print to 331 */ 332 public static void printProperties(PrintStream out) { 333 out.println("[JVMCI properties]"); 334 Option[] values = values(); 335 for (Option option : values) { 336 Object value = option.getValue(); 337 if (value instanceof String) { 338 value = '"' + String.valueOf(value) + '"'; 339 } 340 341 String name = option.getPropertyName(); 342 String assign = option.isDefault ? "=" : ":="; 343 String typeName = option.type.getSimpleName(); 344 String linePrefix = String.format("%s %s %s ", name, assign, value); 345 int typeStartPos = PROPERTY_LINE_WIDTH - typeName.length(); 346 int linePad = typeStartPos - linePrefix.length(); 347 if (linePad > 0) { 348 out.printf("%s%-" + linePad + "s[%s]%n", linePrefix, "", typeName); 349 } else { 350 out.printf("%s[%s]%n", linePrefix, typeName); 351 } 352 for (String line : option.helpLines) { 353 out.printf("%" + PROPERTY_HELP_INDENT + "s%s%n", "", line); 354 } 355 } 356 } 357 358 /** 359 * Compute string similarity based on Dice's coefficient. 360 * 361 * Ported from str_similar() in globals.cpp. 362 */ 363 static float stringSimiliarity(String str1, String str2) { 364 int hit = 0; 365 for (int i = 0; i < str1.length() - 1; ++i) { 366 for (int j = 0; j < str2.length() - 1; ++j) { 367 if ((str1.charAt(i) == str2.charAt(j)) && (str1.charAt(i + 1) == str2.charAt(j + 1))) { 368 ++hit; 369 break; 370 } 371 } 372 } 373 return 2.0f * hit / (str1.length() + str2.length()); 374 } 375 376 private static final float FUZZY_MATCH_THRESHOLD = 0.7F; 377 378 /** 379 * Parses all system properties starting with {@value #JVMCI_OPTION_PROPERTY_PREFIX} and 380 * initializes the options based on their values. 381 */ 382 static void parse() { 383 Map<String, String> savedProps = jdk.vm.ci.services.Services.getSavedProperties(); 384 for (Map.Entry<String, String> e : savedProps.entrySet()) { 385 String name = e.getKey(); 386 if (name.startsWith(Option.JVMCI_OPTION_PROPERTY_PREFIX)) { 387 Object value = options.get(name); 388 if (value == null) { 389 List<String> matches = new ArrayList<>(); 390 for (String pn : options.keySet()) { 391 float score = stringSimiliarity(pn, name); 392 if (score >= FUZZY_MATCH_THRESHOLD) { 393 matches.add(pn); 394 } 395 } 396 Formatter msg = new Formatter(); 397 msg.format("Could not find option %s", name); 398 if (!matches.isEmpty()) { 399 msg.format("%nDid you mean one of the following?"); 400 for (String match : matches) { 401 msg.format("%n %s=<value>", match); 402 } 403 } 404 throw new IllegalArgumentException(msg.toString()); 405 } else if (value instanceof Option) { 406 Option option = (Option) value; 407 option.init(e.getValue()); 408 } 409 } 410 } 411 } 412 } 413 414 private static HotSpotJVMCIBackendFactory findFactory(String architecture) { 415 Iterable<HotSpotJVMCIBackendFactory> factories = getHotSpotJVMCIBackendFactories(); 416 assert factories != null : "sanity"; 417 for (HotSpotJVMCIBackendFactory factory : factories) { 418 if (factory.getArchitecture().equalsIgnoreCase(architecture)) { 419 return factory; 420 } 421 } 422 423 throw new JVMCIError("No JVMCI runtime available for the %s architecture", architecture); 424 } 425 426 private static volatile List<HotSpotJVMCIBackendFactory> cachedHotSpotJVMCIBackendFactories; 427 428 @SuppressFBWarnings(value = "LI_LAZY_INIT_UPDATE_STATIC", justification = "not sure about this") 429 private static Iterable<HotSpotJVMCIBackendFactory> getHotSpotJVMCIBackendFactories() { 430 if (IS_IN_NATIVE_IMAGE || cachedHotSpotJVMCIBackendFactories != null) { 431 return cachedHotSpotJVMCIBackendFactories; 432 } 433 Iterable<HotSpotJVMCIBackendFactory> result = ServiceLoader.load(HotSpotJVMCIBackendFactory.class, ClassLoader.getSystemClassLoader()); 434 if (IS_BUILDING_NATIVE_IMAGE) { 435 cachedHotSpotJVMCIBackendFactories = new ArrayList<>(); 436 for (HotSpotJVMCIBackendFactory factory : result) { 437 cachedHotSpotJVMCIBackendFactories.add(factory); 438 } 439 } 440 return result; 441 } 442 443 /** 444 * Gets the kind of a word value on the {@linkplain #getHostJVMCIBackend() host} backend. 445 */ 446 public static JavaKind getHostWordKind() { 447 return runtime().getHostJVMCIBackend().getCodeCache().getTarget().wordJavaKind; 448 } 449 450 protected final CompilerToVM compilerToVm; 451 452 protected final HotSpotVMConfigStore configStore; 453 protected final HotSpotVMConfig config; 454 private final JVMCIBackend hostBackend; 455 456 private final JVMCICompilerFactory compilerFactory; 457 private final HotSpotJVMCICompilerFactory hsCompilerFactory; 458 private volatile JVMCICompiler compiler; 459 protected final HotSpotJVMCIReflection reflection; 460 461 @NativeImageReinitialize private volatile boolean creatingCompiler; 462 463 /** 464 * Cache for speeding up {@link #fromClass(Class)}. 465 */ 466 @NativeImageReinitialize private volatile ClassValue<WeakReferenceHolder<HotSpotResolvedJavaType>> resolvedJavaType; 467 468 /** 469 * To avoid calling ClassValue.remove to refresh the weak reference, which under certain 470 * circumstances can lead to an infinite loop, we use a permanent holder with a mutable field 471 * that we refresh. 472 */ 473 private static class WeakReferenceHolder<T> { 474 private volatile WeakReference<T> ref; 475 476 WeakReferenceHolder(T value) { 477 set(value); 478 } 479 480 void set(T value) { 481 ref = new WeakReference<>(value); 482 } 483 484 T get() { 485 return ref.get(); 486 } 487 } 488 489 @NativeImageReinitialize private HashMap<Long, WeakReference<ResolvedJavaType>> resolvedJavaTypes; 490 491 /** 492 * Stores the value set by {@link #excludeFromJVMCICompilation(Module...)} so that it can be 493 * read from the VM. 494 */ 495 @SuppressWarnings("unused")// 496 @NativeImageReinitialize private Module[] excludeFromJVMCICompilation; 497 498 private final Map<Class<? extends Architecture>, JVMCIBackend> backends = new HashMap<>(); 499 500 private volatile List<HotSpotVMEventListener> vmEventListeners; 501 502 private Iterable<HotSpotVMEventListener> getVmEventListeners() { 503 if (vmEventListeners == null) { 504 synchronized (this) { 505 if (vmEventListeners == null) { 506 vmEventListeners = JVMCIServiceLocator.getProviders(HotSpotVMEventListener.class); 507 } 508 } 509 } 510 return vmEventListeners; 511 } 512 513 @SuppressWarnings("try") 514 private HotSpotJVMCIRuntime() { 515 compilerToVm = new CompilerToVM(); 516 517 try (InitTimer t = timer("HotSpotVMConfig<init>")) { 518 configStore = new HotSpotVMConfigStore(compilerToVm); 519 config = new HotSpotVMConfig(configStore); 520 } 521 522 reflection = IS_IN_NATIVE_IMAGE ? new SharedLibraryJVMCIReflection() : new HotSpotJDKReflection(); 523 524 PrintStream vmLogStream = null; 525 if (IS_IN_NATIVE_IMAGE) { 526 // Redirect System.out and System.err to HotSpot's TTY stream 527 vmLogStream = new PrintStream(getLogStream()); 528 System.setOut(vmLogStream); 529 System.setErr(vmLogStream); 530 } 531 532 // Initialize the Option values. 533 Option.parse(); 534 535 String hostArchitecture = config.getHostArchitectureName(); 536 537 HotSpotJVMCIBackendFactory factory; 538 try (InitTimer t = timer("find factory:", hostArchitecture)) { 539 factory = findFactory(hostArchitecture); 540 } 541 542 try (InitTimer t = timer("create JVMCI backend:", hostArchitecture)) { 543 hostBackend = registerBackend(factory.createJVMCIBackend(this, null)); 544 } 545 546 compilerFactory = HotSpotJVMCICompilerConfig.getCompilerFactory(); 547 if (compilerFactory instanceof HotSpotJVMCICompilerFactory) { 548 hsCompilerFactory = (HotSpotJVMCICompilerFactory) compilerFactory; 549 if (hsCompilerFactory.getCompilationLevelAdjustment() != None) { 550 String name = HotSpotJVMCICompilerFactory.class.getName(); 551 String msg = String.format("%s.getCompilationLevelAdjustment() is no longer supported. " + 552 "Use %s.excludeFromJVMCICompilation() instead.", name, name); 553 throw new UnsupportedOperationException(msg); 554 } 555 } else { 556 hsCompilerFactory = null; 557 } 558 559 if (config.getFlag("JVMCIPrintProperties", Boolean.class)) { 560 if (vmLogStream == null) { 561 vmLogStream = new PrintStream(getLogStream()); 562 } 563 Option.printProperties(vmLogStream); 564 compilerFactory.printProperties(vmLogStream); 565 System.exit(0); 566 } 567 568 if (Option.PrintConfig.getBoolean()) { 569 configStore.printConfig(); 570 } 571 } 572 573 HotSpotResolvedJavaType createClass(Class<?> javaClass) { 574 if (javaClass.isPrimitive()) { 575 return HotSpotResolvedPrimitiveType.forKind(JavaKind.fromJavaClass(javaClass)); 576 } 577 if (IS_IN_NATIVE_IMAGE) { 578 try { 579 return compilerToVm.lookupType(javaClass.getName().replace('.', '/'), null, true); 580 } catch (ClassNotFoundException e) { 581 throw new JVMCIError(e); 582 } 583 } 584 return compilerToVm.lookupClass(javaClass); 585 } 586 587 private HotSpotResolvedJavaType fromClass0(Class<?> javaClass) { 588 if (resolvedJavaType == null) { 589 synchronized (this) { 590 if (resolvedJavaType == null) { 591 resolvedJavaType = new ClassValue<>() { 592 @Override 593 protected WeakReferenceHolder<HotSpotResolvedJavaType> computeValue(Class<?> type) { 594 return new WeakReferenceHolder<>(createClass(type)); 595 } 596 }; 597 } 598 } 599 } 600 601 WeakReferenceHolder<HotSpotResolvedJavaType> ref = resolvedJavaType.get(javaClass); 602 HotSpotResolvedJavaType javaType = ref.get(); 603 if (javaType == null) { 604 /* 605 * If the referent has become null, create a new value and update cached weak reference. 606 */ 607 javaType = createClass(javaClass); 608 ref.set(javaType); 609 } 610 return javaType; 611 } 612 613 /** 614 * Gets the JVMCI mirror for a {@link Class} object. 615 * 616 * @return the {@link ResolvedJavaType} corresponding to {@code javaClass} 617 */ 618 HotSpotResolvedJavaType fromClass(Class<?> javaClass) { 619 if (javaClass == null) { 620 return null; 621 } 622 return fromClass0(javaClass); 623 } 624 625 synchronized HotSpotResolvedObjectTypeImpl fromMetaspace(long klassPointer, String signature) { 626 if (resolvedJavaTypes == null) { 627 resolvedJavaTypes = new HashMap<>(); 628 } 629 assert klassPointer != 0; 630 WeakReference<ResolvedJavaType> klassReference = resolvedJavaTypes.get(klassPointer); 631 HotSpotResolvedObjectTypeImpl javaType = null; 632 if (klassReference != null) { 633 javaType = (HotSpotResolvedObjectTypeImpl) klassReference.get(); 634 } 635 if (javaType == null) { 636 javaType = new HotSpotResolvedObjectTypeImpl(klassPointer, signature); 637 resolvedJavaTypes.put(klassPointer, new WeakReference<>(javaType)); 638 } 639 return javaType; 640 } 641 642 private JVMCIBackend registerBackend(JVMCIBackend backend) { 643 Class<? extends Architecture> arch = backend.getCodeCache().getTarget().arch.getClass(); 644 JVMCIBackend oldValue = backends.put(arch, backend); 645 assert oldValue == null : "cannot overwrite existing backend for architecture " + arch.getSimpleName(); 646 return backend; 647 } 648 649 public HotSpotVMConfigStore getConfigStore() { 650 return configStore; 651 } 652 653 public HotSpotVMConfig getConfig() { 654 return config; 655 } 656 657 public CompilerToVM getCompilerToVM() { 658 return compilerToVm; 659 } 660 661 HotSpotJVMCIReflection getReflection() { 662 return reflection; 663 } 664 665 /** 666 * Gets a predicate that determines if a given type can be considered trusted for the purpose of 667 * intrinsifying methods it declares. 668 * 669 * @param compilerLeafClasses classes in the leaves of the module graph comprising the JVMCI 670 * compiler. 671 */ 672 public Predicate<ResolvedJavaType> getIntrinsificationTrustPredicate(Class<?>... compilerLeafClasses) { 673 return new Predicate<>() { 674 @Override 675 public boolean test(ResolvedJavaType type) { 676 if (type instanceof HotSpotResolvedObjectTypeImpl) { 677 HotSpotResolvedObjectTypeImpl hsType = (HotSpotResolvedObjectTypeImpl) type; 678 return compilerToVm.isTrustedForIntrinsics(hsType); 679 } else { 680 return false; 681 } 682 } 683 }; 684 } 685 686 /** 687 * Get the {@link Class} corresponding to {@code type}. 688 * 689 * @param type the type for which a {@link Class} is requested 690 * @return the original Java class corresponding to {@code type} or {@code null} if this runtime 691 * does not support mapping {@link ResolvedJavaType} instances to {@link Class} 692 * instances 693 */ 694 public Class<?> getMirror(ResolvedJavaType type) { 695 if (type instanceof HotSpotResolvedJavaType && reflection instanceof HotSpotJDKReflection) { 696 return ((HotSpotJDKReflection) reflection).getMirror((HotSpotResolvedJavaType) type); 697 } 698 return null; 699 } 700 701 @Override 702 public JVMCICompiler getCompiler() { 703 if (compiler == null) { 704 synchronized (this) { 705 if (compiler == null) { 706 assert !creatingCompiler : "recursive compiler creation"; 707 creatingCompiler = true; 708 compiler = compilerFactory.createCompiler(this); 709 creatingCompiler = false; 710 } 711 } 712 } 713 return compiler; 714 } 715 716 /** 717 * Converts a name to a Java type. This method attempts to resolve {@code name} to a 718 * {@link ResolvedJavaType}. 719 * 720 * @param name a well formed Java type in {@linkplain JavaType#getName() internal} format 721 * @param accessingType the context of resolution which must be non-null 722 * @param resolve specifies whether resolution failure results in an unresolved type being 723 * return or a {@link LinkageError} being thrown 724 * @return a Java type for {@code name} which is guaranteed to be of type 725 * {@link ResolvedJavaType} if {@code resolve == true} 726 * @throws LinkageError if {@code resolve == true} and the resolution failed 727 * @throws NullPointerException if {@code accessingClass} is {@code null} 728 */ 729 public JavaType lookupType(String name, HotSpotResolvedObjectType accessingType, boolean resolve) { 730 Objects.requireNonNull(accessingType, "cannot resolve type without an accessing class"); 731 return lookupTypeInternal(name, accessingType, resolve); 732 } 733 734 JavaType lookupTypeInternal(String name, HotSpotResolvedObjectType accessingType, boolean resolve) { 735 // If the name represents a primitive type we can short-circuit the lookup. 736 if (name.length() == 1) { 737 JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0)); 738 return HotSpotResolvedPrimitiveType.forKind(kind); 739 } 740 741 // Resolve non-primitive types in the VM. 742 HotSpotResolvedObjectTypeImpl hsAccessingType = (HotSpotResolvedObjectTypeImpl) accessingType; 743 try { 744 final HotSpotResolvedJavaType klass = compilerToVm.lookupType(name, hsAccessingType, resolve); 745 746 if (klass == null) { 747 assert resolve == false : name; 748 return UnresolvedJavaType.create(name); 749 } 750 return klass; 751 } catch (ClassNotFoundException e) { 752 throw (NoClassDefFoundError) new NoClassDefFoundError().initCause(e); 753 } 754 } 755 756 @Override 757 public JVMCIBackend getHostJVMCIBackend() { 758 return hostBackend; 759 } 760 761 @Override 762 public <T extends Architecture> JVMCIBackend getJVMCIBackend(Class<T> arch) { 763 assert arch != Architecture.class; 764 return backends.get(arch); 765 } 766 767 public Map<Class<? extends Architecture>, JVMCIBackend> getJVMCIBackends() { 768 return Collections.unmodifiableMap(backends); 769 } 770 771 @SuppressWarnings("try") 772 @VMEntryPoint 773 private HotSpotCompilationRequestResult compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long compileState, int id) { 774 HotSpotCompilationRequest request = new HotSpotCompilationRequest(method, entryBCI, compileState, id); 775 CompilationRequestResult result = getCompiler().compileMethod(request); 776 assert result != null : "compileMethod must always return something"; 777 HotSpotCompilationRequestResult hsResult; 778 if (result instanceof HotSpotCompilationRequestResult) { 779 hsResult = (HotSpotCompilationRequestResult) result; 780 } else { 781 Object failure = result.getFailure(); 782 if (failure != null) { 783 boolean retry = false; // Be conservative with unknown compiler 784 hsResult = HotSpotCompilationRequestResult.failure(failure.toString(), retry); 785 } else { 786 int inlinedBytecodes = -1; 787 hsResult = HotSpotCompilationRequestResult.success(inlinedBytecodes); 788 } 789 } 790 return hsResult; 791 } 792 793 /** 794 * Guard to ensure shut down actions are performed at most once. 795 */ 796 private boolean isShutdown; 797 798 /** 799 * Shuts down the runtime. 800 */ 801 @VMEntryPoint 802 private synchronized void shutdown() throws Exception { 803 if (!isShutdown) { 804 isShutdown = true; 805 // Cleaners are normally only processed when a new Cleaner is 806 // instantiated so process all remaining cleaners now. 807 Cleaner.clean(); 808 809 for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) { 810 vmEventListener.notifyShutdown(); 811 } 812 } 813 } 814 815 /** 816 * Notify on completion of a bootstrap. 817 */ 818 @VMEntryPoint 819 private void bootstrapFinished() throws Exception { 820 for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) { 821 vmEventListener.notifyBootstrapFinished(); 822 } 823 } 824 825 /** 826 * Notify on successful install into the CodeCache. 827 * 828 * @param hotSpotCodeCacheProvider 829 * @param installedCode 830 * @param compiledCode 831 */ 832 void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompiledCode compiledCode) { 833 for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) { 834 vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compiledCode); 835 } 836 } 837 838 /** 839 * Writes {@code length} bytes from {@code bytes} starting at offset {@code offset} to HotSpot's 840 * log stream. 841 * 842 * @param flush specifies if the log stream should be flushed after writing 843 * @param canThrow specifies if an error in the {@code bytes}, {@code offset} or {@code length} 844 * arguments should result in an exception or a negative return value. If 845 * {@code false}, this call will not perform any heap allocation 846 * @return 0 on success, -1 if {@code bytes == null && !canThrow}, -2 if {@code !canThrow} and 847 * copying would cause access of data outside array bounds 848 * @throws NullPointerException if {@code bytes == null} 849 * @throws IndexOutOfBoundsException if copying would cause access of data outside array bounds 850 */ 851 public int writeDebugOutput(byte[] bytes, int offset, int length, boolean flush, boolean canThrow) { 852 return compilerToVm.writeDebugOutput(bytes, offset, length, flush, canThrow); 853 } 854 855 /** 856 * Gets an output stream that writes to HotSpot's {@code tty} stream. 857 */ 858 public OutputStream getLogStream() { 859 return new OutputStream() { 860 861 @Override 862 public void write(byte[] b, int off, int len) throws IOException { 863 if (b == null) { 864 throw new NullPointerException(); 865 } else if (off < 0 || off > b.length || len < 0 || (off + len) > b.length || (off + len) < 0) { 866 throw new IndexOutOfBoundsException(); 867 } else if (len == 0) { 868 return; 869 } 870 compilerToVm.writeDebugOutput(b, off, len, false, true); 871 } 872 873 @Override 874 public void write(int b) throws IOException { 875 write(new byte[]{(byte) b}, 0, 1); 876 } 877 878 @Override 879 public void flush() throws IOException { 880 compilerToVm.flushDebugOutput(); 881 } 882 }; 883 } 884 885 /** 886 * Collects the current values of all JVMCI benchmark counters, summed up over all threads. 887 */ 888 public long[] collectCounters() { 889 return compilerToVm.collectCounters(); 890 } 891 892 /** 893 * @return the current number of per thread counters. May be set through 894 * {@code -XX:JVMCICompilerSize=} command line option or the 895 * {@link #setCountersSize(int)} call. 896 */ 897 public int getCountersSize() { 898 return compilerToVm.getCountersSize(); 899 } 900 901 /** 902 * Attempt to enlarge the number of per thread counters available. Requires a safepoint so 903 * resizing should be rare to avoid performance effects. 904 * 905 * @param newSize 906 * @return false if the resizing failed 907 */ 908 public boolean setCountersSize(int newSize) { 909 return compilerToVm.setCountersSize(newSize); 910 } 911 912 /** 913 * The offset from the origin of an array to the first element. 914 * 915 * @return the offset in bytes 916 */ 917 public int getArrayBaseOffset(JavaKind kind) { 918 switch (kind) { 919 case Boolean: 920 return compilerToVm.ARRAY_BOOLEAN_BASE_OFFSET; 921 case Byte: 922 return compilerToVm.ARRAY_BYTE_BASE_OFFSET; 923 case Char: 924 return compilerToVm.ARRAY_CHAR_BASE_OFFSET; 925 case Short: 926 return compilerToVm.ARRAY_SHORT_BASE_OFFSET; 927 case Int: 928 return compilerToVm.ARRAY_INT_BASE_OFFSET; 929 case Long: 930 return compilerToVm.ARRAY_LONG_BASE_OFFSET; 931 case Float: 932 return compilerToVm.ARRAY_FLOAT_BASE_OFFSET; 933 case Double: 934 return compilerToVm.ARRAY_DOUBLE_BASE_OFFSET; 935 case Object: 936 return compilerToVm.ARRAY_OBJECT_BASE_OFFSET; 937 default: 938 throw new JVMCIError("%s", kind); 939 } 940 941 } 942 943 /** 944 * The scale used for the index when accessing elements of an array of this kind. 945 * 946 * @return the scale in order to convert the index into a byte offset 947 */ 948 public int getArrayIndexScale(JavaKind kind) { 949 switch (kind) { 950 case Boolean: 951 return compilerToVm.ARRAY_BOOLEAN_INDEX_SCALE; 952 case Byte: 953 return compilerToVm.ARRAY_BYTE_INDEX_SCALE; 954 case Char: 955 return compilerToVm.ARRAY_CHAR_INDEX_SCALE; 956 case Short: 957 return compilerToVm.ARRAY_SHORT_INDEX_SCALE; 958 case Int: 959 return compilerToVm.ARRAY_INT_INDEX_SCALE; 960 case Long: 961 return compilerToVm.ARRAY_LONG_INDEX_SCALE; 962 case Float: 963 return compilerToVm.ARRAY_FLOAT_INDEX_SCALE; 964 case Double: 965 return compilerToVm.ARRAY_DOUBLE_INDEX_SCALE; 966 case Object: 967 return compilerToVm.ARRAY_OBJECT_INDEX_SCALE; 968 default: 969 throw new JVMCIError("%s", kind); 970 971 } 972 } 973 974 /** 975 * Links each native method in {@code clazz} to an implementation in the JVMCI shared library. 976 * <p> 977 * A use case for this is a JVMCI compiler implementation that offers an API to Java code 978 * executing in HotSpot to exercise functionality (mostly) in the JVMCI shared library. For 979 * example: 980 * 981 * <pre> 982 * package com.jcompile; 983 * 984 * import java.lang.reflect.Method; 985 * 986 * public static class JCompile { 987 * static { 988 * HotSpotJVMCIRuntime.runtime().registerNativeMethods(JCompile.class); 989 * } 990 * public static boolean compile(Method method, String[] options) { 991 * // Convert to simpler data types for passing/serializing across native interface 992 * long metaspaceMethodHandle = getHandle(method); 993 * char[] opts = convertToCharArray(options); 994 * return compile(metaspaceMethodHandle, opts); 995 * } 996 * private static native boolean compile0(long metaspaceMethodHandle, char[] options); 997 * 998 * private static long getHandle(Method method) { ... } 999 * private static char[] convertToCharArray(String[] a) { ... } 1000 * } 1001 * </pre> 1002 * 1003 * The implementation of the native {@code JCompile.compile0} method would be in the JVMCI 1004 * shared library that contains the JVMCI compiler. The {@code JCompile.compile0} implementation 1005 * must be exported as the following JNI-compatible symbol: 1006 * 1007 * <pre> 1008 * Java_com_jcompile_JCompile_compile0 1009 * </pre> 1010 * 1011 * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names" 1012 * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#creating_the_vm" 1013 * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#invocation_api_functions" 1014 * 1015 * 1016 * @return info about the Java VM in the JVMCI shared library {@code JavaVM*}. The info is 1017 * encoded in a long array as follows: 1018 * 1019 * <pre> 1020 * long[] info = { 1021 * javaVM, // the {@code JavaVM*} value 1022 * javaVM->functions->reserved0, 1023 * javaVM->functions->reserved1, 1024 * javaVM->functions->reserved2 1025 * } 1026 * </pre> 1027 * 1028 * @throws NullPointerException if {@code clazz == null} 1029 * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e. 1030 * {@code -XX:-UseJVMCINativeLibrary}) 1031 * @throws IllegalStateException if the current execution context is the JVMCI shared library 1032 * @throws IllegalArgumentException if {@code clazz} is {@link Class#isPrimitive()} 1033 * @throws UnsatisfiedLinkError if there's a problem linking a native method in {@code clazz} 1034 * (no matching JNI symbol or the native method is already linked to a different 1035 * address) 1036 */ 1037 public long[] registerNativeMethods(Class<?> clazz) { 1038 return compilerToVm.registerNativeMethods(clazz); 1039 } 1040 1041 /** 1042 * Creates or retrieves an object in the peer runtime that mirrors {@code obj}. The types whose 1043 * objects can be translated are: 1044 * <ul> 1045 * <li>{@link HotSpotResolvedJavaMethodImpl},</li> 1046 * <li>{@link HotSpotResolvedObjectTypeImpl},</li> 1047 * <li>{@link HotSpotResolvedPrimitiveType},</li> 1048 * <li>{@link IndirectHotSpotObjectConstantImpl},</li> 1049 * <li>{@link DirectHotSpotObjectConstantImpl} and</li> 1050 * <li>{@link HotSpotNmethod}</li> 1051 * </ul> 1052 * 1053 * This mechanism can be used to pass and return values between the HotSpot and JVMCI shared 1054 * library runtimes. In the receiving runtime, the value can be converted back to an object with 1055 * {@link #unhand(Class, long)}. 1056 * 1057 * @param obj an object for which an equivalent instance in the peer runtime is requested 1058 * @return a JNI global reference to the mirror of {@code obj} in the peer runtime 1059 * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e. 1060 * {@code -XX:-UseJVMCINativeLibrary}) 1061 * @throws IllegalArgumentException if {@code obj} is not of a translatable type 1062 * 1063 * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#global_and_local_references" 1064 */ 1065 public long translate(Object obj) { 1066 return compilerToVm.translate(obj); 1067 } 1068 1069 /** 1070 * Dereferences and returns the object referred to by the JNI global reference {@code handle}. 1071 * The global reference is deleted prior to returning. Any further use of {@code handle} is 1072 * invalid. 1073 * 1074 * @param handle a JNI global reference to an object in the current runtime 1075 * @return the object referred to by {@code handle} 1076 * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e. 1077 * {@code -XX:-UseJVMCINativeLibrary}) 1078 * @throws ClassCastException if the returned object cannot be cast to {@code type} 1079 * 1080 * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#global_and_local_references" 1081 * 1082 */ 1083 public <T> T unhand(Class<T> type, long handle) { 1084 return type.cast(compilerToVm.unhand(handle)); 1085 } 1086 1087 /** 1088 * Determines if the current thread is attached to the peer runtime. 1089 * 1090 * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e. 1091 * {@code -XX:-UseJVMCINativeLibrary}) 1092 * @throws IllegalStateException if the peer runtime has not been initialized 1093 */ 1094 public boolean isCurrentThreadAttached() { 1095 return compilerToVm.isCurrentThreadAttached(); 1096 } 1097 1098 /** 1099 * Gets the address of the HotSpot {@code JavaThread} C++ object for the current thread. This 1100 * will return {@code 0} if called from an unattached JVMCI shared library thread. 1101 */ 1102 public long getCurrentJavaThread() { 1103 return compilerToVm.getCurrentJavaThread(); 1104 } 1105 1106 /** 1107 * Ensures the current thread is attached to the peer runtime. 1108 * 1109 * @param asDaemon if the thread is not yet attached, should it be attached as a daemon 1110 * @return {@code true} if this call attached the current thread, {@code false} if the current 1111 * thread was already attached 1112 * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e. 1113 * {@code -XX:-UseJVMCINativeLibrary}) 1114 * @throws IllegalStateException if the peer runtime has not been initialized or there is an 1115 * error while trying to attach the thread 1116 * @throws ArrayIndexOutOfBoundsException if {@code javaVMInfo} is non-null and is shorter than 1117 * the length of the array returned by {@link #registerNativeMethods} 1118 */ 1119 public boolean attachCurrentThread(boolean asDaemon) { 1120 return compilerToVm.attachCurrentThread(asDaemon); 1121 } 1122 1123 /** 1124 * Detaches the current thread from the peer runtime. 1125 * 1126 * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e. 1127 * {@code -XX:-UseJVMCINativeLibrary}) 1128 * @throws IllegalStateException if the peer runtime has not been initialized or if the current 1129 * thread is not attached or if there is an error while trying to detach the thread 1130 */ 1131 public void detachCurrentThread() { 1132 compilerToVm.detachCurrentThread(); 1133 } 1134 1135 /** 1136 * Informs HotSpot that no method whose module is in {@code modules} is to be compiled 1137 * with {@link #compileMethod}. 1138 * 1139 * @param modules the set of modules containing JVMCI compiler classes 1140 */ 1141 public void excludeFromJVMCICompilation(Module...modules) { 1142 this.excludeFromJVMCICompilation = modules.clone(); 1143 } 1144 1145 /** 1146 * Calls {@link System#exit(int)} in HotSpot's runtime. 1147 */ 1148 public void exitHotSpot(int status) { 1149 if (!IS_IN_NATIVE_IMAGE) { 1150 System.exit(status); 1151 } 1152 compilerToVm.callSystemExit(status); 1153 } 1154 }