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