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