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.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.internal.misc.VM;
  41 import jdk.vm.ci.code.Architecture;
  42 import jdk.vm.ci.code.CompilationRequestResult;
  43 import jdk.vm.ci.code.CompiledCode;
  44 import jdk.vm.ci.code.InstalledCode;
  45 import jdk.vm.ci.common.InitTimer;
  46 import jdk.vm.ci.common.JVMCIError;
  47 import jdk.vm.ci.hotspot.services.HotSpotJVMCICompilerFactory;
  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         Compiler(String.class, null, "Selects the system compiler."),
  94         // Note: The following one is not used (see InitTimer.ENABLED).
  95         InitTimer(boolean.class, false, "Specifies if initialization timing is enabled."),
  96         PrintConfig(boolean.class, false, "Prints all HotSpotVMConfig fields."),
  97         PrintFlags(boolean.class, false, "Prints all JVMCI flags and exits."),
  98         ShowFlags(boolean.class, false, "Prints all JVMCI flags and continues."),
  99         TraceMethodDataFilter(String.class, null, "");
 100 
 101         /**
 102          * The prefix for system properties that are JVMCI options.
 103          */
 104         private static final String JVMCI_OPTION_PROPERTY_PREFIX = "jvmci.";
 105 
 106         /**
 107          * Marker for uninitialized flags.
 108          */
 109         private static final String UNINITIALIZED = "UNINITIALIZED";
 110 
 111         private final Class<?> type;
 112         private Object value;
 113         private final Object defaultValue;
 114         private boolean isDefault;
 115         private final String help;
 116 
 117         Option(Class<?> type, Object defaultValue, String help) {
 118             assert Character.isUpperCase(name().charAt(0)) : "Option name must start with upper-case letter: " + name();
 119             this.type = type;
 120             this.value = UNINITIALIZED;
 121             this.defaultValue = defaultValue;
 122             this.help = help;
 123         }
 124 
 125         @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "sentinel must be String since it's a static final in an enum")
 126         private Object getValue() {
 127             if (value == UNINITIALIZED) {
 128                 String propertyValue = VM.getSavedProperty(JVMCI_OPTION_PROPERTY_PREFIX + name());
 129                 if (propertyValue == null) {
 130                     this.value = defaultValue;
 131                     this.isDefault = true;
 132                 } else {
 133                     if (type == boolean.class) {
 134                         this.value = Boolean.parseBoolean(propertyValue);
 135                     } else if (type == String.class) {
 136                         this.value = propertyValue;
 137                     } else {
 138                         throw new JVMCIError("Unexpected option type " + type);
 139                     }
 140                     this.isDefault = false;
 141                 }
 142                 // Saved properties should not be interned - let's be sure
 143                 assert value != UNINITIALIZED;
 144             }
 145             return value;
 146         }
 147 
 148         /**
 149          * Returns the option's value as boolean.
 150          *
 151          * @return option's value
 152          */
 153         public boolean getBoolean() {
 154             return (boolean) getValue();
 155         }
 156 
 157         /**
 158          * Returns the option's value as String.
 159          *
 160          * @return option's value
 161          */
 162         public String getString() {
 163             return (String) getValue();
 164         }
 165 
 166         /**
 167          * Prints all option flags to {@code out}.
 168          *
 169          * @param out stream to print to
 170          */
 171         public static void printFlags(PrintStream out) {
 172             out.println("[List of JVMCI options]");
 173             for (Option option : values()) {
 174                 Object value = option.getValue();
 175                 String assign = option.isDefault ? ":=" : " =";
 176                 out.printf("%9s %-40s %s %-14s %s%n", option.type.getSimpleName(), option, assign, value, option.help);
 177             }
 178         }
 179     }
 180 
 181     public static HotSpotJVMCIBackendFactory findFactory(String architecture) {
 182         for (HotSpotJVMCIBackendFactory factory : Services.load(HotSpotJVMCIBackendFactory.class)) {
 183             if (factory.getArchitecture().equalsIgnoreCase(architecture)) {
 184                 return factory;
 185             }
 186         }
 187 
 188         throw new JVMCIError("No JVMCI runtime available for the %s architecture", architecture);
 189     }
 190 
 191     /**
 192      * Gets the kind of a word value on the {@linkplain #getHostJVMCIBackend() host} backend.
 193      */
 194     public static JavaKind getHostWordKind() {
 195         return runtime().getHostJVMCIBackend().getCodeCache().getTarget().wordJavaKind;
 196     }
 197 
 198     protected final CompilerToVM compilerToVm;
 199 
 200     protected final HotSpotVMConfig config;
 201     private final JVMCIBackend hostBackend;
 202 
 203     private final JVMCICompilerFactory compilerFactory;
 204     private final HotSpotJVMCICompilerFactory hsCompilerFactory;
 205     private volatile JVMCICompiler compiler;
 206     protected final HotSpotJVMCIMetaAccessContext metaAccessContext;
 207 
 208     /**
 209      * Stores the result of {@link HotSpotJVMCICompilerFactory#getCompilationLevelAdjustment} so
 210      * that it can be read from the VM.
 211      */
 212     @SuppressWarnings("unused") private final int compilationLevelAdjustment;
 213 
 214     private final Map<Class<? extends Architecture>, JVMCIBackend> backends = new HashMap<>();
 215 
 216     private final Iterable<HotSpotVMEventListener> vmEventListeners;
 217 
 218     /**
 219      * Stores the result of {@link HotSpotJVMCICompilerFactory#getTrivialPrefixes()} so that it can
 220      * be read from the VM.
 221      */
 222     @SuppressWarnings("unused") private final String[] trivialPrefixes;
 223 
 224     @SuppressWarnings("try")
 225     private HotSpotJVMCIRuntime() {
 226         compilerToVm = new CompilerToVM();
 227 
 228         try (InitTimer t = timer("HotSpotVMConfig<init>")) {
 229             config = new HotSpotVMConfig(compilerToVm);
 230         }
 231 
 232         String hostArchitecture = config.getHostArchitectureName();
 233 
 234         HotSpotJVMCIBackendFactory factory;
 235         try (InitTimer t = timer("find factory:", hostArchitecture)) {
 236             factory = findFactory(hostArchitecture);
 237         }
 238 
 239         try (InitTimer t = timer("create JVMCI backend:", hostArchitecture)) {
 240             hostBackend = registerBackend(factory.createJVMCIBackend(this, null));
 241         }
 242 
 243         vmEventListeners = Services.load(HotSpotVMEventListener.class);
 244 
 245         metaAccessContext = new HotSpotJVMCIMetaAccessContext();
 246 
 247         boolean printFlags = Option.PrintFlags.getBoolean();
 248         boolean showFlags = Option.ShowFlags.getBoolean();
 249         if (printFlags || showFlags) {
 250             Option.printFlags(System.out);
 251             if (printFlags) {
 252                 System.exit(0);
 253             }
 254         }
 255 
 256         if (Option.PrintConfig.getBoolean()) {
 257             printConfig(config, compilerToVm);
 258         }
 259 
 260         compilerFactory = HotSpotJVMCICompilerConfig.getCompilerFactory();
 261         if (compilerFactory instanceof HotSpotJVMCICompilerFactory) {
 262             hsCompilerFactory = (HotSpotJVMCICompilerFactory) compilerFactory;
 263             trivialPrefixes = hsCompilerFactory.getTrivialPrefixes();
 264             compilationLevelAdjustment = hsCompilerFactory.getCompilationLevelAdjustment(config);
 265         } else {
 266             hsCompilerFactory = null;
 267             trivialPrefixes = null;
 268             compilationLevelAdjustment = 0;
 269         }
 270     }
 271 
 272     private JVMCIBackend registerBackend(JVMCIBackend backend) {
 273         Class<? extends Architecture> arch = backend.getCodeCache().getTarget().arch.getClass();
 274         JVMCIBackend oldValue = backends.put(arch, backend);
 275         assert oldValue == null : "cannot overwrite existing backend for architecture " + arch.getSimpleName();
 276         return backend;
 277     }
 278 
 279     public ResolvedJavaType fromClass(Class<?> javaClass) {
 280         return metaAccessContext.fromClass(javaClass);
 281     }
 282 
 283     public HotSpotVMConfig getConfig() {
 284         return config;
 285     }
 286 
 287     public CompilerToVM getCompilerToVM() {
 288         return compilerToVm;
 289     }
 290 
 291     public JVMCICompiler getCompiler() {
 292         if (compiler == null) {
 293             synchronized (this) {
 294                 if (compiler == null) {
 295                     compiler = compilerFactory.createCompiler(this);
 296                 }
 297             }
 298         }
 299         return compiler;
 300     }
 301 
 302     public JavaType lookupType(String name, HotSpotResolvedObjectType accessingType, boolean resolve) {
 303         Objects.requireNonNull(accessingType, "cannot resolve type without an accessing class");
 304         // If the name represents a primitive type we can short-circuit the lookup.
 305         if (name.length() == 1) {
 306             JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0));
 307             return fromClass(kind.toJavaClass());
 308         }
 309 
 310         // Resolve non-primitive types in the VM.
 311         HotSpotResolvedObjectTypeImpl hsAccessingType = (HotSpotResolvedObjectTypeImpl) accessingType;
 312         final HotSpotResolvedObjectTypeImpl klass = compilerToVm.lookupType(name, hsAccessingType.mirror(), resolve);
 313 
 314         if (klass == null) {
 315             assert resolve == false;
 316             return HotSpotUnresolvedJavaType.create(this, name);
 317         }
 318         return klass;
 319     }
 320 
 321     public JVMCIBackend getHostJVMCIBackend() {
 322         return hostBackend;
 323     }
 324 
 325     public <T extends Architecture> JVMCIBackend getJVMCIBackend(Class<T> arch) {
 326         assert arch != Architecture.class;
 327         return backends.get(arch);
 328     }
 329 
 330     public Map<Class<? extends Architecture>, JVMCIBackend> getJVMCIBackends() {
 331         return Collections.unmodifiableMap(backends);
 332     }
 333 
 334     /**
 335      * Called from the VM.
 336      */
 337     @SuppressWarnings({"unused"})
 338     private int adjustCompilationLevel(Class<?> declaringClass, String name, String signature, boolean isOsr, int level) {
 339         return hsCompilerFactory.adjustCompilationLevel(config, declaringClass, name, signature, isOsr, level);
 340     }
 341 
 342     /**
 343      * Called from the VM.
 344      */
 345     @SuppressWarnings({"unused"})
 346     private HotSpotCompilationRequestResult compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long jvmciEnv, int id) {
 347         CompilationRequestResult result = getCompiler().compileMethod(new HotSpotCompilationRequest(method, entryBCI, jvmciEnv, id));
 348         assert result != null : "compileMethod must always return something";
 349         HotSpotCompilationRequestResult hsResult;
 350         if (result instanceof HotSpotCompilationRequestResult) {
 351             hsResult = (HotSpotCompilationRequestResult) result;
 352         } else {
 353             Object failure = result.getFailure();
 354             if (failure != null) {
 355                 boolean retry = false; // Be conservative with unknown compiler
 356                 hsResult = HotSpotCompilationRequestResult.failure(failure.toString(), retry);
 357             } else {
 358                 int inlinedBytecodes = -1;
 359                 hsResult = HotSpotCompilationRequestResult.success(inlinedBytecodes);
 360             }
 361         }
 362 
 363         return hsResult;
 364     }
 365 
 366     /**
 367      * Shuts down the runtime.
 368      *
 369      * Called from the VM.
 370      */
 371     @SuppressWarnings({"unused"})
 372     private void shutdown() throws Exception {
 373         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 374             vmEventListener.notifyShutdown();
 375         }
 376     }
 377 
 378     /**
 379      * Notify on completion of a bootstrap.
 380      *
 381      * Called from the VM.
 382      */
 383     @SuppressWarnings({"unused"})
 384     private void bootstrapFinished() throws Exception {
 385         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 386             vmEventListener.notifyBootstrapFinished();
 387         }
 388     }
 389 
 390     /**
 391      * Notify on successful install into the CodeCache.
 392      *
 393      * @param hotSpotCodeCacheProvider
 394      * @param installedCode
 395      * @param compiledCode
 396      */
 397     void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompiledCode compiledCode) {
 398         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 399             vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compiledCode);
 400         }
 401     }
 402 
 403     private static void printConfig(HotSpotVMConfig config, CompilerToVM vm) {
 404         Field[] fields = config.getClass().getDeclaredFields();
 405         Map<String, Field> sortedFields = new TreeMap<>();
 406         for (Field f : fields) {
 407             if (!f.isSynthetic() && !Modifier.isStatic(f.getModifiers())) {
 408                 f.setAccessible(true);
 409                 sortedFields.put(f.getName(), f);
 410             }
 411         }
 412         for (Field f : sortedFields.values()) {
 413             try {
 414                 String line = String.format("%9s %-40s = %s%n", f.getType().getSimpleName(), f.getName(), pretty(f.get(config)));
 415                 byte[] lineBytes = line.getBytes();
 416                 vm.writeDebugOutput(lineBytes, 0, lineBytes.length);
 417                 vm.flushDebugOutput();
 418             } catch (Exception e) {
 419             }
 420         }
 421     }
 422 
 423     private static String pretty(Object value) {
 424         if (value == null) {
 425             return "null";
 426         }
 427 
 428         Class<?> klass = value.getClass();
 429         if (value instanceof String) {
 430             return "\"" + value + "\"";
 431         } else if (value instanceof Method) {
 432             return "method \"" + ((Method) value).getName() + "\"";
 433         } else if (value instanceof Class<?>) {
 434             return "class \"" + ((Class<?>) value).getSimpleName() + "\"";
 435         } else if (value instanceof Integer) {
 436             if ((Integer) value < 10) {
 437                 return value.toString();
 438             }
 439             return value + " (0x" + Integer.toHexString((Integer) value) + ")";
 440         } else if (value instanceof Long) {
 441             if ((Long) value < 10 && (Long) value > -10) {
 442                 return value + "l";
 443             }
 444             return value + "l (0x" + Long.toHexString((Long) value) + "l)";
 445         } else if (klass.isArray()) {
 446             StringBuilder str = new StringBuilder();
 447             int dimensions = 0;
 448             while (klass.isArray()) {
 449                 dimensions++;
 450                 klass = klass.getComponentType();
 451             }
 452             int length = Array.getLength(value);
 453             str.append(klass.getSimpleName()).append('[').append(length).append(']');
 454             for (int i = 1; i < dimensions; i++) {
 455                 str.append("[]");
 456             }
 457             str.append(" {");
 458             for (int i = 0; i < length; i++) {
 459                 str.append(pretty(Array.get(value, i)));
 460                 if (i < length - 1) {
 461                     str.append(", ");
 462                 }
 463             }
 464             str.append('}');
 465             return str.toString();
 466         }
 467         return value.toString();
 468     }
 469 
 470     public OutputStream getLogStream() {
 471         return new OutputStream() {
 472 
 473             @Override
 474             public void write(byte[] b, int off, int len) throws IOException {
 475                 if (b == null) {
 476                     throw new NullPointerException();
 477                 } else if (off < 0 || off > b.length || len < 0 || (off + len) > b.length || (off + len) < 0) {
 478                     throw new IndexOutOfBoundsException();
 479                 } else if (len == 0) {
 480                     return;
 481                 }
 482                 compilerToVm.writeDebugOutput(b, off, len);
 483             }
 484 
 485             @Override
 486             public void write(int b) throws IOException {
 487                 write(new byte[]{(byte) b}, 0, 1);
 488             }
 489 
 490             @Override
 491             public void flush() throws IOException {
 492                 compilerToVm.flushDebugOutput();
 493             }
 494         };
 495     }
 496 
 497     /**
 498      * Collects the current values of all JVMCI benchmark counters, summed up over all threads.
 499      */
 500     public long[] collectCounters() {
 501         return compilerToVm.collectCounters();
 502     }
 503 }