1 /*
   2  * Copyright (c) 2015, 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.CompilationResult;
  43 import jdk.vm.ci.code.InstalledCode;
  44 import jdk.vm.ci.common.JVMCIError;
  45 import jdk.vm.ci.inittimer.InitTimer;
  46 import jdk.vm.ci.meta.JVMCIMetaAccessContext;
  47 import jdk.vm.ci.meta.JavaKind;
  48 import jdk.vm.ci.meta.JavaType;
  49 import jdk.vm.ci.meta.ResolvedJavaType;
  50 import jdk.vm.ci.runtime.JVMCI;
  51 import jdk.vm.ci.runtime.JVMCIBackend;
  52 import jdk.vm.ci.runtime.JVMCICompiler;
  53 import jdk.vm.ci.services.Services;
  54 import jdk.internal.misc.VM;
  55 
  56 //JaCoCo Exclude
  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, HotSpotProxified {
  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         ImplicitStableValues(boolean.class, true, "Mark well-known stable fields as such."),
  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         TrustFinalDefaultFields(boolean.class, true, "Determines whether to treat final fields with default values as constant.");
 101 
 102         /**
 103          * The prefix for system properties that are JVMCI options.
 104          */
 105         private static final String JVMCI_OPTION_PROPERTY_PREFIX = "jvmci.";
 106 
 107         /**
 108          * Marker for uninitialized flags.
 109          */
 110         private static final String UNINITIALIZED = "UNINITIALIZED";
 111 
 112         private final Class<?> type;
 113         private Object value;
 114         private final Object defaultValue;
 115         private boolean isDefault;
 116         private final String help;
 117 
 118         private Option(Class<?> type, Object defaultValue, String help) {
 119             assert Character.isUpperCase(name().charAt(0)) : "Option name must start with upper-case letter: " + name();
 120             this.type = type;
 121             this.value = UNINITIALIZED;
 122             this.defaultValue = defaultValue;
 123             this.help = help;
 124         }
 125 
 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 volatile JVMCICompiler compiler;
 204     protected final JVMCIMetaAccessContext metaAccessContext;
 205 
 206     private final Map<Class<? extends Architecture>, JVMCIBackend> backends = new HashMap<>();
 207 
 208     private final Iterable<HotSpotVMEventListener> vmEventListeners;
 209 
 210     @SuppressWarnings("unused") private final String[] trivialPrefixes;
 211 
 212     @SuppressWarnings("try")
 213     private HotSpotJVMCIRuntime() {
 214         compilerToVm = new CompilerToVM();
 215 
 216         try (InitTimer t = timer("HotSpotVMConfig<init>")) {
 217             config = new HotSpotVMConfig(compilerToVm);
 218         }
 219 
 220         String hostArchitecture = config.getHostArchitectureName();
 221 
 222         HotSpotJVMCIBackendFactory factory;
 223         try (InitTimer t = timer("find factory:", hostArchitecture)) {
 224             factory = findFactory(hostArchitecture);
 225         }
 226 
 227         try (InitTimer t = timer("create JVMCI backend:", hostArchitecture)) {
 228             hostBackend = registerBackend(factory.createJVMCIBackend(this, null));
 229         }
 230 
 231         vmEventListeners = Services.load(HotSpotVMEventListener.class);
 232 
 233         JVMCIMetaAccessContext context = null;
 234         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 235             context = vmEventListener.createMetaAccessContext(this);
 236             if (context != null) {
 237                 break;
 238             }
 239         }
 240         if (context == null) {
 241             context = new HotSpotJVMCIMetaAccessContext();
 242         }
 243         metaAccessContext = context;
 244 
 245         boolean printFlags = Option.PrintFlags.getBoolean();
 246         boolean showFlags = Option.ShowFlags.getBoolean();
 247         if (printFlags || showFlags) {
 248             Option.printFlags(System.out);
 249             if (printFlags) {
 250                 System.exit(0);
 251             }
 252         }
 253 
 254         if (Option.PrintConfig.getBoolean()) {
 255             printConfig(config, compilerToVm);
 256         }
 257 
 258         trivialPrefixes = HotSpotJVMCICompilerConfig.getCompilerFactory().getTrivialPrefixes();
 259     }
 260 
 261     private JVMCIBackend registerBackend(JVMCIBackend backend) {
 262         Class<? extends Architecture> arch = backend.getCodeCache().getTarget().arch.getClass();
 263         JVMCIBackend oldValue = backends.put(arch, backend);
 264         assert oldValue == null : "cannot overwrite existing backend for architecture " + arch.getSimpleName();
 265         return backend;
 266     }
 267 
 268     public ResolvedJavaType fromClass(Class<?> javaClass) {
 269         return metaAccessContext.fromClass(javaClass);
 270     }
 271 
 272     public HotSpotVMConfig getConfig() {
 273         return config;
 274     }
 275 
 276     public CompilerToVM getCompilerToVM() {
 277         return compilerToVm;
 278     }
 279 
 280     public JVMCIMetaAccessContext getMetaAccessContext() {
 281         return metaAccessContext;
 282     }
 283 
 284     public JVMCICompiler getCompiler() {
 285         if (compiler == null) {
 286             synchronized (this) {
 287                 if (compiler == null) {
 288                     compiler = HotSpotJVMCICompilerConfig.getCompilerFactory().createCompiler(this);
 289                 }
 290             }
 291         }
 292         return compiler;
 293     }
 294 
 295     public JavaType lookupType(String name, HotSpotResolvedObjectType accessingType, boolean resolve) {
 296         Objects.requireNonNull(accessingType, "cannot resolve type without an accessing class");
 297         // If the name represents a primitive type we can short-circuit the lookup.
 298         if (name.length() == 1) {
 299             JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0));
 300             return fromClass(kind.toJavaClass());
 301         }
 302 
 303         // Resolve non-primitive types in the VM.
 304         HotSpotResolvedObjectTypeImpl hsAccessingType = (HotSpotResolvedObjectTypeImpl) accessingType;
 305         final HotSpotResolvedObjectTypeImpl klass = compilerToVm.lookupType(name, hsAccessingType.mirror(), resolve);
 306 
 307         if (klass == null) {
 308             assert resolve == false;
 309             return HotSpotUnresolvedJavaType.create(this, name);
 310         }
 311         return klass;
 312     }
 313 
 314     public JVMCIBackend getHostJVMCIBackend() {
 315         return hostBackend;
 316     }
 317 
 318     public <T extends Architecture> JVMCIBackend getJVMCIBackend(Class<T> arch) {
 319         assert arch != Architecture.class;
 320         return backends.get(arch);
 321     }
 322 
 323     public Map<Class<? extends Architecture>, JVMCIBackend> getJVMCIBackends() {
 324         return Collections.unmodifiableMap(backends);
 325     }
 326 
 327     /**
 328      * Called from the VM.
 329      */
 330     @SuppressWarnings({"unused"})
 331     private CompilationRequestResult compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long jvmciEnv, int id) {
 332         return getCompiler().compileMethod(new HotSpotCompilationRequest(method, entryBCI, jvmciEnv, id));
 333     }
 334 
 335     /**
 336      * Shuts down the runtime.
 337      *
 338      * Called from the VM.
 339      */
 340     @SuppressWarnings({"unused"})
 341     private void shutdown() throws Exception {
 342         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 343             vmEventListener.notifyShutdown();
 344         }
 345     }
 346 
 347     /**
 348      * Notify on successful install into the CodeCache.
 349      *
 350      * @param hotSpotCodeCacheProvider
 351      * @param installedCode
 352      * @param compResult
 353      */
 354     void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompilationResult compResult) {
 355         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 356             vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compResult);
 357         }
 358     }
 359 
 360     private static void printConfig(HotSpotVMConfig config, CompilerToVM vm) {
 361         Field[] fields = config.getClass().getDeclaredFields();
 362         Map<String, Field> sortedFields = new TreeMap<>();
 363         for (Field f : fields) {
 364             if (!f.isSynthetic() && !Modifier.isStatic(f.getModifiers())) {
 365                 f.setAccessible(true);
 366                 sortedFields.put(f.getName(), f);
 367             }
 368         }
 369         for (Field f : sortedFields.values()) {
 370             try {
 371                 String line = String.format("%9s %-40s = %s%n", f.getType().getSimpleName(), f.getName(), pretty(f.get(config)));
 372                 byte[] lineBytes = line.getBytes();
 373                 vm.writeDebugOutput(lineBytes, 0, lineBytes.length);
 374                 vm.flushDebugOutput();
 375             } catch (Exception e) {
 376             }
 377         }
 378     }
 379 
 380     private static String pretty(Object value) {
 381         if (value == null) {
 382             return "null";
 383         }
 384 
 385         Class<?> klass = value.getClass();
 386         if (value instanceof String) {
 387             return "\"" + value + "\"";
 388         } else if (value instanceof Method) {
 389             return "method \"" + ((Method) value).getName() + "\"";
 390         } else if (value instanceof Class<?>) {
 391             return "class \"" + ((Class<?>) value).getSimpleName() + "\"";
 392         } else if (value instanceof Integer) {
 393             if ((Integer) value < 10) {
 394                 return value.toString();
 395             }
 396             return value + " (0x" + Integer.toHexString((Integer) value) + ")";
 397         } else if (value instanceof Long) {
 398             if ((Long) value < 10 && (Long) value > -10) {
 399                 return value + "l";
 400             }
 401             return value + "l (0x" + Long.toHexString((Long) value) + "l)";
 402         } else if (klass.isArray()) {
 403             StringBuilder str = new StringBuilder();
 404             int dimensions = 0;
 405             while (klass.isArray()) {
 406                 dimensions++;
 407                 klass = klass.getComponentType();
 408             }
 409             int length = Array.getLength(value);
 410             str.append(klass.getSimpleName()).append('[').append(length).append(']');
 411             for (int i = 1; i < dimensions; i++) {
 412                 str.append("[]");
 413             }
 414             str.append(" {");
 415             for (int i = 0; i < length; i++) {
 416                 str.append(pretty(Array.get(value, i)));
 417                 if (i < length - 1) {
 418                     str.append(", ");
 419                 }
 420             }
 421             str.append('}');
 422             return str.toString();
 423         }
 424         return value.toString();
 425     }
 426 
 427     public OutputStream getLogStream() {
 428         return new OutputStream() {
 429 
 430             @Override
 431             public void write(byte[] b, int off, int len) throws IOException {
 432                 if (b == null) {
 433                     throw new NullPointerException();
 434                 } else if (off < 0 || off > b.length || len < 0 || (off + len) > b.length || (off + len) < 0) {
 435                     throw new IndexOutOfBoundsException();
 436                 } else if (len == 0) {
 437                     return;
 438                 }
 439                 compilerToVm.writeDebugOutput(b, off, len);
 440             }
 441 
 442             @Override
 443             public void write(int b) throws IOException {
 444                 write(new byte[]{(byte) b}, 0, 1);
 445             }
 446 
 447             @Override
 448             public void flush() throws IOException {
 449                 compilerToVm.flushDebugOutput();
 450             }
 451         };
 452     }
 453 
 454     /**
 455      * Collects the current values of all JVMCI benchmark counters, summed up over all threads.
 456      */
 457     public long[] collectCounters() {
 458         return compilerToVm.collectCounters();
 459     }
 460 }