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