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