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.common.InitTimer.timer;
  26 
  27 import java.io.IOException;
  28 import java.io.OutputStream;
  29 import java.io.PrintStream;
  30 import java.util.ArrayList;
  31 import java.util.Collections;
  32 import java.util.HashMap;
  33 import java.util.List;
  34 import java.util.Map;
  35 import java.util.Objects;
  36 import java.util.ServiceLoader;
  37 import java.util.TreeMap;
  38 
  39 import jdk.internal.misc.VM;
  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.InitTimer;
  45 import jdk.vm.ci.common.JVMCIError;
  46 import jdk.vm.ci.hotspot.services.HotSpotJVMCICompilerFactory;
  47 import jdk.vm.ci.hotspot.services.HotSpotJVMCICompilerFactory.CompilationLevel;
  48 import jdk.vm.ci.hotspot.services.HotSpotVMEventListener;
  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.runtime.services.JVMCICompilerFactory;
  56 import jdk.vm.ci.services.Services;
  57 
  58 /**
  59  * HotSpot implementation of a JVMCI runtime.
  60  *
  61  * The initialization of this class is very fragile since it's initialized both through
  62  * {@link JVMCI#initialize()} or through calling {@link HotSpotJVMCIRuntime#runtime()} and
  63  * {@link HotSpotJVMCIRuntime#runtime()} is also called by {@link JVMCI#initialize()}. So this class
  64  * can't have a static initializer and any required initialization must be done as part of
  65  * {@link #runtime()}. This allows the initialization to funnel back through
  66  * {@link JVMCI#initialize()} without deadlocking.
  67  */
  68 public final class HotSpotJVMCIRuntime implements HotSpotJVMCIRuntimeProvider {
  69 
  70     @SuppressWarnings("try")
  71     static class DelayedInit {
  72         private static final HotSpotJVMCIRuntime instance;
  73 
  74         static {
  75             try (InitTimer t = timer("HotSpotJVMCIRuntime.<init>")) {
  76                 instance = new HotSpotJVMCIRuntime();
  77             }
  78         }
  79     }
  80 
  81     /**
  82      * Gets the singleton {@link HotSpotJVMCIRuntime} object.
  83      */
  84     public static HotSpotJVMCIRuntime runtime() {
  85         JVMCI.initialize();
  86         return DelayedInit.instance;
  87     }
  88 
  89     /**
  90      * A list of all supported JVMCI options.
  91      */
  92     public enum Option {
  93         // @formatter:off
  94         Compiler(String.class, null, "Selects the system compiler."),
  95         // Note: The following one is not used (see InitTimer.ENABLED). It is added here
  96         // so that -XX:+JVMCIPrintProperties shows the option.
  97         InitTimer(Boolean.class, false, "Specifies if initialization timing is enabled."),
  98         PrintConfig(Boolean.class, false, "Prints VM configuration available via JVMCI."),
  99         TraceMethodDataFilter(String.class, null,
 100                         "Enables tracing of profiling info when read by JVMCI.",
 101                         "Empty value: trace all methods",
 102                         "Non-empty value: trace methods whose fully qualified name contains the value.");
 103         // @formatter:on
 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[] helpLines;
 120 
 121         Option(Class<?> type, Object defaultValue, String... helpLines) {
 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.helpLines = helpLines;
 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(getPropertyName());
 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          * Gets the name of system property from which this option gets its value.
 154          */
 155         public String getPropertyName() {
 156             return JVMCI_OPTION_PROPERTY_PREFIX + name();
 157         }
 158 
 159         /**
 160          * Returns the option's value as boolean.
 161          *
 162          * @return option's value
 163          */
 164         public boolean getBoolean() {
 165             return (boolean) getValue();
 166         }
 167 
 168         /**
 169          * Returns the option's value as String.
 170          *
 171          * @return option's value
 172          */
 173         public String getString() {
 174             return (String) getValue();
 175         }
 176 
 177         /**
 178          * Prints a description of the properties used to configure shared JVMCI code.
 179          *
 180          * @param out stream to print to
 181          */
 182         public static void printProperties(PrintStream out) {
 183             out.println("[JVMCI properties]");
 184             int typeWidth = 0;
 185             int nameWidth = 0;
 186             Option[] values = values();
 187             for (Option option : values) {
 188                 typeWidth = Math.max(typeWidth, option.type.getSimpleName().length());
 189                 nameWidth = Math.max(nameWidth, option.getPropertyName().length());
 190             }
 191             for (Option option : values) {
 192                 Object value = option.getValue();
 193                 if (value instanceof String) {
 194                     value = '"' + String.valueOf(value) + '"';
 195                 }
 196                 String assign = option.isDefault ? " =" : ":=";
 197                 String format = "%" + (typeWidth + 1) + "s %-" + (nameWidth + 1) + "s %s %s%n";
 198                 out.printf(format, option.type.getSimpleName(), option.getPropertyName(), assign, value);
 199                 String helpFormat = "%" + (typeWidth + 1) + "s %s%n";
 200                 for (String line : option.helpLines) {
 201                     out.printf(helpFormat, "", line);
 202                 }
 203             }
 204         }
 205     }
 206 
 207     public static HotSpotJVMCIBackendFactory findFactory(String architecture) {
 208         for (HotSpotJVMCIBackendFactory factory : Services.load(HotSpotJVMCIBackendFactory.class)) {
 209             if (factory.getArchitecture().equalsIgnoreCase(architecture)) {
 210                 return factory;
 211             }
 212         }
 213 
 214         throw new JVMCIError("No JVMCI runtime available for the %s architecture", architecture);
 215     }
 216 
 217     /**
 218      * Gets the kind of a word value on the {@linkplain #getHostJVMCIBackend() host} backend.
 219      */
 220     public static JavaKind getHostWordKind() {
 221         return runtime().getHostJVMCIBackend().getCodeCache().getTarget().wordJavaKind;
 222     }
 223 
 224     protected final CompilerToVM compilerToVm;
 225 
 226     protected final HotSpotVMConfigStore configStore;
 227     protected final HotSpotVMConfig config;
 228     private final JVMCIBackend hostBackend;
 229 
 230     private final JVMCICompilerFactory compilerFactory;
 231     private final HotSpotJVMCICompilerFactory hsCompilerFactory;
 232     private volatile JVMCICompiler compiler;
 233     protected final HotSpotJVMCIMetaAccessContext metaAccessContext;
 234 
 235     /**
 236      * Stores the result of {@link HotSpotJVMCICompilerFactory#getCompilationLevelAdjustment} so
 237      * that it can be read from the VM.
 238      */
 239     @SuppressWarnings("unused") private final int compilationLevelAdjustment;
 240 
 241     private final Map<Class<? extends Architecture>, JVMCIBackend> backends = new HashMap<>();
 242 
 243     private volatile List<HotSpotVMEventListener> vmEventListeners;
 244 
 245     private Iterable<HotSpotVMEventListener> getVmEventListeners() {
 246         if (vmEventListeners == null) {
 247             synchronized (this) {
 248                 if (vmEventListeners == null) {
 249                     List<HotSpotVMEventListener> listeners = new ArrayList<>();
 250                     for (HotSpotVMEventListener vmEventListener : ServiceLoader.load(HotSpotVMEventListener.class)) {
 251                         listeners.add(vmEventListener);
 252                     }
 253                     vmEventListeners = listeners;
 254                 }
 255             }
 256         }
 257         return vmEventListeners;
 258     }
 259 
 260     /**
 261      * Stores the result of {@link HotSpotJVMCICompilerFactory#getTrivialPrefixes()} so that it can
 262      * be read from the VM.
 263      */
 264     @SuppressWarnings("unused") private final String[] trivialPrefixes;
 265 
 266     @SuppressWarnings("try")
 267     private HotSpotJVMCIRuntime() {
 268         compilerToVm = new CompilerToVM();
 269 
 270         try (InitTimer t = timer("HotSpotVMConfig<init>")) {
 271             configStore = new HotSpotVMConfigStore(compilerToVm);
 272             config = new HotSpotVMConfig(configStore);
 273         }
 274 
 275         String hostArchitecture = config.getHostArchitectureName();
 276 
 277         HotSpotJVMCIBackendFactory factory;
 278         try (InitTimer t = timer("find factory:", hostArchitecture)) {
 279             factory = findFactory(hostArchitecture);
 280         }
 281 
 282         try (InitTimer t = timer("create JVMCI backend:", hostArchitecture)) {
 283             hostBackend = registerBackend(factory.createJVMCIBackend(this, null));
 284         }
 285 
 286         metaAccessContext = new HotSpotJVMCIMetaAccessContext();
 287 
 288         compilerFactory = HotSpotJVMCICompilerConfig.getCompilerFactory();
 289         if (compilerFactory instanceof HotSpotJVMCICompilerFactory) {
 290             hsCompilerFactory = (HotSpotJVMCICompilerFactory) compilerFactory;
 291             trivialPrefixes = hsCompilerFactory.getTrivialPrefixes();
 292             switch (hsCompilerFactory.getCompilationLevelAdjustment()) {
 293                 case None:
 294                     compilationLevelAdjustment = config.compLevelAdjustmentNone;
 295                     break;
 296                 case ByHolder:
 297                     compilationLevelAdjustment = config.compLevelAdjustmentByHolder;
 298                     break;
 299                 case ByFullSignature:
 300                     compilationLevelAdjustment = config.compLevelAdjustmentByFullSignature;
 301                     break;
 302                 default:
 303                     compilationLevelAdjustment = config.compLevelAdjustmentNone;
 304                     break;
 305             }
 306         } else {
 307             hsCompilerFactory = null;
 308             trivialPrefixes = null;
 309             compilationLevelAdjustment = config.compLevelAdjustmentNone;
 310         }
 311 
 312         if (config.getFlag("JVMCIPrintProperties", Boolean.class)) {
 313             PrintStream out = new PrintStream(getLogStream());
 314             Option.printProperties(out);
 315             compilerFactory.printProperties(out);
 316         }
 317 
 318         if (Option.PrintConfig.getBoolean()) {
 319             printConfig(configStore, compilerToVm);
 320         }
 321     }
 322 
 323     private JVMCIBackend registerBackend(JVMCIBackend backend) {
 324         Class<? extends Architecture> arch = backend.getCodeCache().getTarget().arch.getClass();
 325         JVMCIBackend oldValue = backends.put(arch, backend);
 326         assert oldValue == null : "cannot overwrite existing backend for architecture " + arch.getSimpleName();
 327         return backend;
 328     }
 329 
 330     public ResolvedJavaType fromClass(Class<?> javaClass) {
 331         return metaAccessContext.fromClass(javaClass);
 332     }
 333 
 334     public HotSpotVMConfigStore getConfigStore() {
 335         return configStore;
 336     }
 337 
 338     public HotSpotVMConfig getConfig() {
 339         return config;
 340     }
 341 
 342     public CompilerToVM getCompilerToVM() {
 343         return compilerToVm;
 344     }
 345 
 346     public JVMCICompiler getCompiler() {
 347         if (compiler == null) {
 348             synchronized (this) {
 349                 if (compiler == null) {
 350                     compiler = compilerFactory.createCompiler(this);
 351                 }
 352             }
 353         }
 354         return compiler;
 355     }
 356 
 357     public JavaType lookupType(String name, HotSpotResolvedObjectType accessingType, boolean resolve) {
 358         Objects.requireNonNull(accessingType, "cannot resolve type without an accessing class");
 359         // If the name represents a primitive type we can short-circuit the lookup.
 360         if (name.length() == 1) {
 361             JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0));
 362             return fromClass(kind.toJavaClass());
 363         }
 364 
 365         // Resolve non-primitive types in the VM.
 366         HotSpotResolvedObjectTypeImpl hsAccessingType = (HotSpotResolvedObjectTypeImpl) accessingType;
 367         final HotSpotResolvedObjectTypeImpl klass = compilerToVm.lookupType(name, hsAccessingType.mirror(), resolve);
 368 
 369         if (klass == null) {
 370             assert resolve == false;
 371             return HotSpotUnresolvedJavaType.create(this, name);
 372         }
 373         return klass;
 374     }
 375 
 376     public JVMCIBackend getHostJVMCIBackend() {
 377         return hostBackend;
 378     }
 379 
 380     public <T extends Architecture> JVMCIBackend getJVMCIBackend(Class<T> arch) {
 381         assert arch != Architecture.class;
 382         return backends.get(arch);
 383     }
 384 
 385     public Map<Class<? extends Architecture>, JVMCIBackend> getJVMCIBackends() {
 386         return Collections.unmodifiableMap(backends);
 387     }
 388 
 389     /**
 390      * Called from the VM.
 391      */
 392     @SuppressWarnings({"unused"})
 393     private int adjustCompilationLevel(Class<?> declaringClass, String name, String signature, boolean isOsr, int level) {
 394         CompilationLevel curLevel;
 395         if (level == config.compilationLevelNone) {
 396             curLevel = CompilationLevel.None;
 397         } else if (level == config.compilationLevelSimple) {
 398             curLevel = CompilationLevel.Simple;
 399         } else if (level == config.compilationLevelLimitedProfile) {
 400             curLevel = CompilationLevel.LimitedProfile;
 401         } else if (level == config.compilationLevelFullProfile) {
 402             curLevel = CompilationLevel.FullProfile;
 403         } else if (level == config.compilationLevelFullOptimization) {
 404             curLevel = CompilationLevel.FullOptimization;
 405         } else {
 406             throw JVMCIError.shouldNotReachHere();
 407         }
 408 
 409         switch (hsCompilerFactory.adjustCompilationLevel(declaringClass, name, signature, isOsr, curLevel)) {
 410             case None:
 411                 return config.compilationLevelNone;
 412             case Simple:
 413                 return config.compilationLevelSimple;
 414             case LimitedProfile:
 415                 return config.compilationLevelLimitedProfile;
 416             case FullProfile:
 417                 return config.compilationLevelFullProfile;
 418             case FullOptimization:
 419                 return config.compilationLevelFullOptimization;
 420             default:
 421                 return level;
 422         }
 423     }
 424 
 425     /**
 426      * Called from the VM.
 427      */
 428     @SuppressWarnings({"unused"})
 429     private HotSpotCompilationRequestResult compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long jvmciEnv, int id) {
 430         CompilationRequestResult result = getCompiler().compileMethod(new HotSpotCompilationRequest(method, entryBCI, jvmciEnv, id));
 431         assert result != null : "compileMethod must always return something";
 432         HotSpotCompilationRequestResult hsResult;
 433         if (result instanceof HotSpotCompilationRequestResult) {
 434             hsResult = (HotSpotCompilationRequestResult) result;
 435         } else {
 436             Object failure = result.getFailure();
 437             if (failure != null) {
 438                 boolean retry = false; // Be conservative with unknown compiler
 439                 hsResult = HotSpotCompilationRequestResult.failure(failure.toString(), retry);
 440             } else {
 441                 int inlinedBytecodes = -1;
 442                 hsResult = HotSpotCompilationRequestResult.success(inlinedBytecodes);
 443             }
 444         }
 445 
 446         return hsResult;
 447     }
 448 
 449     /**
 450      * Shuts down the runtime.
 451      *
 452      * Called from the VM.
 453      */
 454     @SuppressWarnings({"unused"})
 455     private void shutdown() throws Exception {
 456         for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) {
 457             vmEventListener.notifyShutdown();
 458         }
 459     }
 460 
 461     /**
 462      * Notify on completion of a bootstrap.
 463      *
 464      * Called from the VM.
 465      */
 466     @SuppressWarnings({"unused"})
 467     private void bootstrapFinished() throws Exception {
 468         for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) {
 469             vmEventListener.notifyBootstrapFinished();
 470         }
 471     }
 472 
 473     /**
 474      * Notify on successful install into the CodeCache.
 475      *
 476      * @param hotSpotCodeCacheProvider
 477      * @param installedCode
 478      * @param compiledCode
 479      */
 480     void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompiledCode compiledCode) {
 481         for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) {
 482             vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compiledCode);
 483         }
 484     }
 485 
 486     @SuppressFBWarnings(value = "DM_DEFAULT_ENCODING", justification = "no localization here please!")
 487     private static void printConfigLine(CompilerToVM vm, String format, Object... args) {
 488         String line = String.format(format, args);
 489         byte[] lineBytes = line.getBytes();
 490         vm.writeDebugOutput(lineBytes, 0, lineBytes.length);
 491         vm.flushDebugOutput();
 492     }
 493 
 494     private static void printConfig(HotSpotVMConfigStore store, CompilerToVM vm) {
 495         TreeMap<String, VMField> fields = new TreeMap<>(store.getFields());
 496         for (VMField field : fields.values()) {
 497             if (!field.isStatic()) {
 498                 printConfigLine(vm, "[vmconfig:instance field] %s %s {offset=%d[0x%x]}%n", field.type, field.name, field.offset, field.offset);
 499             } else {
 500                 String value = field.value == null ? "null" : String.format("%d[0x%x]", field.value, field.value);
 501                 printConfigLine(vm, "[vmconfig:static field] %s %s = %s {address=0x%x}%n", field.type, field.name, value, field.address);
 502             }
 503         }
 504         TreeMap<String, VMFlag> flags = new TreeMap<>(store.getFlags());
 505         for (VMFlag flag : flags.values()) {
 506             printConfigLine(vm, "[vmconfig:flag] %s %s = %s%n", flag.type, flag.name, flag.value);
 507         }
 508         TreeMap<String, Long> addresses = new TreeMap<>(store.getAddresses());
 509         for (Map.Entry<String, Long> e : addresses.entrySet()) {
 510             printConfigLine(vm, "[vmconfig:address] %s = %d[0x%x]%n", e.getKey(), e.getValue(), e.getValue());
 511         }
 512         TreeMap<String, Long> constants = new TreeMap<>(store.getConstants());
 513         for (Map.Entry<String, Long> e : constants.entrySet()) {
 514             printConfigLine(vm, "[vmconfig:constant] %s = %d[0x%x]%n", e.getKey(), e.getValue(), e.getValue());
 515         }
 516         TreeMap<String, Long> typeSizes = new TreeMap<>(store.getTypeSizes());
 517         for (Map.Entry<String, Long> e : typeSizes.entrySet()) {
 518             printConfigLine(vm, "[vmconfig:type size] %s = %d%n", e.getKey(), e.getValue());
 519         }
 520         for (VMIntrinsicMethod e : store.getIntrinsics()) {
 521             printConfigLine(vm, "[vmconfig:intrinsic] %d = %s.%s %s%n", e.id, e.declaringClass, e.name, e.descriptor);
 522         }
 523     }
 524 
 525     public OutputStream getLogStream() {
 526         return new OutputStream() {
 527 
 528             @Override
 529             public void write(byte[] b, int off, int len) throws IOException {
 530                 if (b == null) {
 531                     throw new NullPointerException();
 532                 } else if (off < 0 || off > b.length || len < 0 || (off + len) > b.length || (off + len) < 0) {
 533                     throw new IndexOutOfBoundsException();
 534                 } else if (len == 0) {
 535                     return;
 536                 }
 537                 compilerToVm.writeDebugOutput(b, off, len);
 538             }
 539 
 540             @Override
 541             public void write(int b) throws IOException {
 542                 write(new byte[]{(byte) b}, 0, 1);
 543             }
 544 
 545             @Override
 546             public void flush() throws IOException {
 547                 compilerToVm.flushDebugOutput();
 548             }
 549         };
 550     }
 551 
 552     /**
 553      * Collects the current values of all JVMCI benchmark counters, summed up over all threads.
 554      */
 555     public long[] collectCounters() {
 556         return compilerToVm.collectCounters();
 557     }
 558 }