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