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