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.service.Services;
  53 import sun.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,
  93         InitTimer,  // Note: Not used because of visibility issues (see InitTimer.ENABLED).
  94         PrintConfig,
  95         PrintFlags,
  96         ShowFlags,
  97         TraceMethodDataFilter,
  98         TrustFinalDefaultFields;
  99 
 100         /**
 101          * Prints all option flags to {@code out}.
 102          *
 103          * @param out stream to print to
 104          */
 105         public static void printFlags(PrintStream out) {
 106             out.println("[List of JVMCI options]");
 107             for (Option e : values()) {
 108                 out.println(e);
 109             }
 110         }
 111     }
 112 
 113     /**
 114      * The prefix for system properties that are JVMCI options.
 115      */
 116     private static final String JVMCI_OPTION_PROPERTY_PREFIX = "jvmci.";
 117 
 118     /**
 119      * Gets a String value based on a system property {@linkplain VM#getSavedProperty(String) saved}
 120      * at system initialization time. The property name must start with an upper-case letter and is
 121      * prefixed with {@link #JVMCI_OPTION_PROPERTY_PREFIX}.
 122      *
 123      * @param option the name of the system property
 124      * @param def the value to return if there is no system property corresponding to {@code name}
 125      */
 126     public static String getProperty(Option option, String def) {
 127         String optionName = option.name();
 128         if (!Character.isUpperCase(optionName.charAt(0))) {
 129             throw new JVMCIError("Option name must start with upper-case letter: " + optionName);
 130         }
 131         String value = VM.getSavedProperty(JVMCI_OPTION_PROPERTY_PREFIX + optionName);
 132         if (value == null) {
 133             return def;
 134         }
 135         return value;
 136     }
 137 
 138     /**
 139      * Gets a boolean value based on a system property {@linkplain VM#getSavedProperty(String)
 140      * saved} at system initialization time. The property name must start with an upper-case letter
 141      * and is prefixed with {@link #JVMCI_OPTION_PROPERTY_PREFIX}.
 142      *
 143      * @param option the name of the system property to derive a boolean value from using
 144      *            {@link Boolean#parseBoolean(String)}
 145      * @param def the value to return if there is no system property corresponding to {@code name}
 146      */
 147     public static boolean getBooleanProperty(Option option, boolean def) {
 148         String value = getProperty(option, null);
 149         if (value == null) {
 150             return def;
 151         }
 152         return Boolean.parseBoolean(value);
 153     }
 154 
 155     public static HotSpotJVMCIBackendFactory findFactory(String architecture) {
 156         for (HotSpotJVMCIBackendFactory factory : Services.load(HotSpotJVMCIBackendFactory.class)) {
 157             if (factory.getArchitecture().equalsIgnoreCase(architecture)) {
 158                 return factory;
 159             }
 160         }
 161 
 162         throw new JVMCIError("No JVMCI runtime available for the %s architecture", architecture);
 163     }
 164 
 165     /**
 166      * Gets the kind of a word value on the {@linkplain #getHostJVMCIBackend() host} backend.
 167      */
 168     public static JavaKind getHostWordKind() {
 169         return runtime().getHostJVMCIBackend().getCodeCache().getTarget().wordJavaKind;
 170     }
 171 
 172     protected final CompilerToVM compilerToVm;
 173 
 174     protected final HotSpotVMConfig config;
 175     private final JVMCIBackend hostBackend;
 176 
 177     private volatile JVMCICompiler compiler;
 178     protected final JVMCIMetaAccessContext metaAccessContext;
 179 
 180     private final Map<Class<? extends Architecture>, JVMCIBackend> backends = new HashMap<>();
 181 
 182     private final Iterable<HotSpotVMEventListener> vmEventListeners;
 183 
 184     @SuppressWarnings("unused") private final String[] trivialPrefixes;
 185 
 186     @SuppressWarnings("try")
 187     private HotSpotJVMCIRuntime() {
 188         compilerToVm = new CompilerToVM();
 189 
 190         try (InitTimer t = timer("HotSpotVMConfig<init>")) {
 191             config = new HotSpotVMConfig(compilerToVm);
 192         }
 193 
 194         String hostArchitecture = config.getHostArchitectureName();
 195 
 196         HotSpotJVMCIBackendFactory factory;
 197         try (InitTimer t = timer("find factory:", hostArchitecture)) {
 198             factory = findFactory(hostArchitecture);
 199         }
 200 
 201         try (InitTimer t = timer("create JVMCI backend:", hostArchitecture)) {
 202             hostBackend = registerBackend(factory.createJVMCIBackend(this, null));
 203         }
 204 
 205         vmEventListeners = Services.load(HotSpotVMEventListener.class);
 206 
 207         JVMCIMetaAccessContext context = null;
 208         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 209             context = vmEventListener.createMetaAccessContext(this);
 210             if (context != null) {
 211                 break;
 212             }
 213         }
 214         if (context == null) {
 215             context = new HotSpotJVMCIMetaAccessContext();
 216         }
 217         metaAccessContext = context;
 218 
 219         boolean printFlags = getBooleanProperty(Option.PrintFlags, false);
 220         boolean showFlags = getBooleanProperty(Option.ShowFlags, false);
 221         if (printFlags || showFlags) {
 222             Option.printFlags(System.out);
 223             if (printFlags) {
 224                 System.exit(0);
 225             }
 226         }
 227 
 228         if (getBooleanProperty(Option.PrintConfig, false)) {
 229             printConfig(config, compilerToVm);
 230         }
 231 
 232         trivialPrefixes = HotSpotJVMCICompilerConfig.getCompilerFactory().getTrivialPrefixes();
 233     }
 234 
 235     private JVMCIBackend registerBackend(JVMCIBackend backend) {
 236         Class<? extends Architecture> arch = backend.getCodeCache().getTarget().arch.getClass();
 237         JVMCIBackend oldValue = backends.put(arch, backend);
 238         assert oldValue == null : "cannot overwrite existing backend for architecture " + arch.getSimpleName();
 239         return backend;
 240     }
 241 
 242     public ResolvedJavaType fromClass(Class<?> javaClass) {
 243         return metaAccessContext.fromClass(javaClass);
 244     }
 245 
 246     public HotSpotVMConfig getConfig() {
 247         return config;
 248     }
 249 
 250     public CompilerToVM getCompilerToVM() {
 251         return compilerToVm;
 252     }
 253 
 254     public JVMCIMetaAccessContext getMetaAccessContext() {
 255         return metaAccessContext;
 256     }
 257 
 258     public JVMCICompiler getCompiler() {
 259         if (compiler == null) {
 260             synchronized (this) {
 261                 if (compiler == null) {
 262                     compiler = HotSpotJVMCICompilerConfig.getCompilerFactory().createCompiler(this);
 263                 }
 264             }
 265         }
 266         return compiler;
 267     }
 268 
 269     public JavaType lookupType(String name, HotSpotResolvedObjectType accessingType, boolean resolve) {
 270         Objects.requireNonNull(accessingType, "cannot resolve type without an accessing class");
 271         // If the name represents a primitive type we can short-circuit the lookup.
 272         if (name.length() == 1) {
 273             JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0));
 274             return fromClass(kind.toJavaClass());
 275         }
 276 
 277         // Resolve non-primitive types in the VM.
 278         HotSpotResolvedObjectTypeImpl hsAccessingType = (HotSpotResolvedObjectTypeImpl) accessingType;
 279         final HotSpotResolvedObjectTypeImpl klass = compilerToVm.lookupType(name, hsAccessingType.mirror(), resolve);
 280 
 281         if (klass == null) {
 282             assert resolve == false;
 283             return HotSpotUnresolvedJavaType.create(this, name);
 284         }
 285         return klass;
 286     }
 287 
 288     public JVMCIBackend getHostJVMCIBackend() {
 289         return hostBackend;
 290     }
 291 
 292     public <T extends Architecture> JVMCIBackend getJVMCIBackend(Class<T> arch) {
 293         assert arch != Architecture.class;
 294         return backends.get(arch);
 295     }
 296 
 297     public Map<Class<? extends Architecture>, JVMCIBackend> getJVMCIBackends() {
 298         return Collections.unmodifiableMap(backends);
 299     }
 300 
 301     /**
 302      * Called from the VM.
 303      */
 304     @SuppressWarnings({"unused"})
 305     private void compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long jvmciEnv, int id) {
 306         getCompiler().compileMethod(new HotSpotCompilationRequest(method, entryBCI, jvmciEnv, id));
 307     }
 308 
 309     /**
 310      * Shuts down the runtime.
 311      *
 312      * Called from the VM.
 313      */
 314     @SuppressWarnings({"unused"})
 315     private void shutdown() throws Exception {
 316         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 317             vmEventListener.notifyShutdown();
 318         }
 319     }
 320 
 321     /**
 322      * Notify on successful install into the CodeCache.
 323      *
 324      * @param hotSpotCodeCacheProvider
 325      * @param installedCode
 326      * @param compResult
 327      */
 328     void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompilationResult compResult) {
 329         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 330             vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compResult);
 331         }
 332     }
 333 
 334     private static void printConfig(HotSpotVMConfig config, CompilerToVM vm) {
 335         Field[] fields = config.getClass().getDeclaredFields();
 336         Map<String, Field> sortedFields = new TreeMap<>();
 337         for (Field f : fields) {
 338             if (!f.isSynthetic() && !Modifier.isStatic(f.getModifiers())) {
 339                 f.setAccessible(true);
 340                 sortedFields.put(f.getName(), f);
 341             }
 342         }
 343         for (Field f : sortedFields.values()) {
 344             try {
 345                 String line = String.format("%9s %-40s = %s%n", f.getType().getSimpleName(), f.getName(), pretty(f.get(config)));
 346                 byte[] lineBytes = line.getBytes();
 347                 vm.writeDebugOutput(lineBytes, 0, lineBytes.length);
 348                 vm.flushDebugOutput();
 349             } catch (Exception e) {
 350             }
 351         }
 352     }
 353 
 354     private static String pretty(Object value) {
 355         if (value == null) {
 356             return "null";
 357         }
 358 
 359         Class<?> klass = value.getClass();
 360         if (value instanceof String) {
 361             return "\"" + value + "\"";
 362         } else if (value instanceof Method) {
 363             return "method \"" + ((Method) value).getName() + "\"";
 364         } else if (value instanceof Class<?>) {
 365             return "class \"" + ((Class<?>) value).getSimpleName() + "\"";
 366         } else if (value instanceof Integer) {
 367             if ((Integer) value < 10) {
 368                 return value.toString();
 369             }
 370             return value + " (0x" + Integer.toHexString((Integer) value) + ")";
 371         } else if (value instanceof Long) {
 372             if ((Long) value < 10 && (Long) value > -10) {
 373                 return value + "l";
 374             }
 375             return value + "l (0x" + Long.toHexString((Long) value) + "l)";
 376         } else if (klass.isArray()) {
 377             StringBuilder str = new StringBuilder();
 378             int dimensions = 0;
 379             while (klass.isArray()) {
 380                 dimensions++;
 381                 klass = klass.getComponentType();
 382             }
 383             int length = Array.getLength(value);
 384             str.append(klass.getSimpleName()).append('[').append(length).append(']');
 385             for (int i = 1; i < dimensions; i++) {
 386                 str.append("[]");
 387             }
 388             str.append(" {");
 389             for (int i = 0; i < length; i++) {
 390                 str.append(pretty(Array.get(value, i)));
 391                 if (i < length - 1) {
 392                     str.append(", ");
 393                 }
 394             }
 395             str.append('}');
 396             return str.toString();
 397         }
 398         return value.toString();
 399     }
 400 
 401     public OutputStream getLogStream() {
 402         return new OutputStream() {
 403 
 404             @Override
 405             public void write(byte[] b, int off, int len) throws IOException {
 406                 if (b == null) {
 407                     throw new NullPointerException();
 408                 } else if (off < 0 || off > b.length || len < 0 || (off + len) > b.length || (off + len) < 0) {
 409                     throw new IndexOutOfBoundsException();
 410                 } else if (len == 0) {
 411                     return;
 412                 }
 413                 compilerToVm.writeDebugOutput(b, off, len);
 414             }
 415 
 416             @Override
 417             public void write(int b) throws IOException {
 418                 write(new byte[]{(byte) b}, 0, 1);
 419             }
 420 
 421             @Override
 422             public void flush() throws IOException {
 423                 compilerToVm.flushDebugOutput();
 424             }
 425         };
 426     }
 427 
 428     /**
 429      * Collects the current values of all JVMCI benchmark counters, summed up over all threads.
 430      */
 431     public long[] collectCounters() {
 432         return compilerToVm.collectCounters();
 433     }
 434 }