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