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 successful install into the CodeCache.
 355      *
 356      * @param hotSpotCodeCacheProvider
 357      * @param installedCode
 358      * @param compiledCode
 359      */
 360     void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompiledCode compiledCode) {
 361         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 362             vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compiledCode);
 363         }
 364     }
 365 
 366     private static void printConfig(HotSpotVMConfig config, CompilerToVM vm) {
 367         Field[] fields = config.getClass().getDeclaredFields();
 368         Map<String, Field> sortedFields = new TreeMap<>();
 369         for (Field f : fields) {
 370             if (!f.isSynthetic() && !Modifier.isStatic(f.getModifiers())) {
 371                 f.setAccessible(true);
 372                 sortedFields.put(f.getName(), f);
 373             }
 374         }
 375         for (Field f : sortedFields.values()) {
 376             try {
 377                 String line = String.format("%9s %-40s = %s%n", f.getType().getSimpleName(), f.getName(), pretty(f.get(config)));
 378                 byte[] lineBytes = line.getBytes();
 379                 vm.writeDebugOutput(lineBytes, 0, lineBytes.length);
 380                 vm.flushDebugOutput();
 381             } catch (Exception e) {
 382             }
 383         }
 384     }
 385 
 386     private static String pretty(Object value) {
 387         if (value == null) {
 388             return "null";
 389         }
 390 
 391         Class<?> klass = value.getClass();
 392         if (value instanceof String) {
 393             return "\"" + value + "\"";
 394         } else if (value instanceof Method) {
 395             return "method \"" + ((Method) value).getName() + "\"";
 396         } else if (value instanceof Class<?>) {
 397             return "class \"" + ((Class<?>) value).getSimpleName() + "\"";
 398         } else if (value instanceof Integer) {
 399             if ((Integer) value < 10) {
 400                 return value.toString();
 401             }
 402             return value + " (0x" + Integer.toHexString((Integer) value) + ")";
 403         } else if (value instanceof Long) {
 404             if ((Long) value < 10 && (Long) value > -10) {
 405                 return value + "l";
 406             }
 407             return value + "l (0x" + Long.toHexString((Long) value) + "l)";
 408         } else if (klass.isArray()) {
 409             StringBuilder str = new StringBuilder();
 410             int dimensions = 0;
 411             while (klass.isArray()) {
 412                 dimensions++;
 413                 klass = klass.getComponentType();
 414             }
 415             int length = Array.getLength(value);
 416             str.append(klass.getSimpleName()).append('[').append(length).append(']');
 417             for (int i = 1; i < dimensions; i++) {
 418                 str.append("[]");
 419             }
 420             str.append(" {");
 421             for (int i = 0; i < length; i++) {
 422                 str.append(pretty(Array.get(value, i)));
 423                 if (i < length - 1) {
 424                     str.append(", ");
 425                 }
 426             }
 427             str.append('}');
 428             return str.toString();
 429         }
 430         return value.toString();
 431     }
 432 
 433     public OutputStream getLogStream() {
 434         return new OutputStream() {
 435 
 436             @Override
 437             public void write(byte[] b, int off, int len) throws IOException {
 438                 if (b == null) {
 439                     throw new NullPointerException();
 440                 } else if (off < 0 || off > b.length || len < 0 || (off + len) > b.length || (off + len) < 0) {
 441                     throw new IndexOutOfBoundsException();
 442                 } else if (len == 0) {
 443                     return;
 444                 }
 445                 compilerToVm.writeDebugOutput(b, off, len);
 446             }
 447 
 448             @Override
 449             public void write(int b) throws IOException {
 450                 write(new byte[]{(byte) b}, 0, 1);
 451             }
 452 
 453             @Override
 454             public void flush() throws IOException {
 455                 compilerToVm.flushDebugOutput();
 456             }
 457         };
 458     }
 459 
 460     /**
 461      * Collects the current values of all JVMCI benchmark counters, summed up over all threads.
 462      */
 463     public long[] collectCounters() {
 464         return compilerToVm.collectCounters();
 465     }
 466 }