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         // Note: The following one is not used (see InitTimer.ENABLED).
  99         InitTimer(boolean.class, false, "Specifies if initialization timing is enabled."),
 100         PrintConfig(boolean.class, false, "Prints all HotSpotVMConfig fields."),
 101         PrintFlags(boolean.class, false, "Prints all JVMCI flags and exits."),
 102         ShowFlags(boolean.class, false, "Prints all JVMCI flags and continues."),
 103         TraceMethodDataFilter(String.class, null, "");
 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 final JVMCICompilerFactory compilerFactory;
 208     private final HotSpotJVMCICompilerFactory hsCompilerFactory;
 209     private volatile JVMCICompiler compiler;
 210     protected final JVMCIMetaAccessContext metaAccessContext;
 211 
 212     /**
 213      * Stores the result of {@link HotSpotJVMCICompilerFactory#getCompilationLevelAdjustment} so
 214      * that it can be read from the VM.
 215      */
 216     @SuppressWarnings("unused") private final int compilationLevelAdjustment;
 217 
 218     private final Map<Class<? extends Architecture>, JVMCIBackend> backends = new HashMap<>();
 219 
 220     private final Iterable<HotSpotVMEventListener> vmEventListeners;
 221 
 222     /**
 223      * Stores the result of {@link HotSpotJVMCICompilerFactory#getTrivialPrefixes()} so that it can
 224      * be read from the VM.
 225      */
 226     @SuppressWarnings("unused") private final String[] trivialPrefixes;
 227 
 228     @SuppressWarnings("try")
 229     private HotSpotJVMCIRuntime() {
 230         compilerToVm = new CompilerToVM();
 231 
 232         try (InitTimer t = timer("HotSpotVMConfig<init>")) {
 233             config = new HotSpotVMConfig(compilerToVm);
 234         }
 235 
 236         String hostArchitecture = config.getHostArchitectureName();
 237 
 238         HotSpotJVMCIBackendFactory factory;
 239         try (InitTimer t = timer("find factory:", hostArchitecture)) {
 240             factory = findFactory(hostArchitecture);
 241         }
 242 
 243         try (InitTimer t = timer("create JVMCI backend:", hostArchitecture)) {
 244             hostBackend = registerBackend(factory.createJVMCIBackend(this, null));
 245         }
 246 
 247         vmEventListeners = Services.load(HotSpotVMEventListener.class);
 248 
 249         JVMCIMetaAccessContext context = null;
 250         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 251             context = vmEventListener.createMetaAccessContext(this);
 252             if (context != null) {
 253                 break;
 254             }
 255         }
 256         if (context == null) {
 257             context = new HotSpotJVMCIMetaAccessContext();
 258         }
 259         metaAccessContext = context;
 260 
 261         boolean printFlags = Option.PrintFlags.getBoolean();
 262         boolean showFlags = Option.ShowFlags.getBoolean();
 263         if (printFlags || showFlags) {
 264             Option.printFlags(System.out);
 265             if (printFlags) {
 266                 System.exit(0);
 267             }
 268         }
 269 
 270         if (Option.PrintConfig.getBoolean()) {
 271             printConfig(config, compilerToVm);
 272         }
 273 
 274         compilerFactory = HotSpotJVMCICompilerConfig.getCompilerFactory();
 275         if (compilerFactory instanceof HotSpotJVMCICompilerFactory) {
 276             hsCompilerFactory = (HotSpotJVMCICompilerFactory) compilerFactory;
 277             trivialPrefixes = hsCompilerFactory.getTrivialPrefixes();
 278             compilationLevelAdjustment = hsCompilerFactory.getCompilationLevelAdjustment(config);
 279         } else {
 280             hsCompilerFactory = null;
 281             trivialPrefixes = null;
 282             compilationLevelAdjustment = 0;
 283         }
 284     }
 285 
 286     private JVMCIBackend registerBackend(JVMCIBackend backend) {
 287         Class<? extends Architecture> arch = backend.getCodeCache().getTarget().arch.getClass();
 288         JVMCIBackend oldValue = backends.put(arch, backend);
 289         assert oldValue == null : "cannot overwrite existing backend for architecture " + arch.getSimpleName();
 290         return backend;
 291     }
 292 
 293     public ResolvedJavaType fromClass(Class<?> javaClass) {
 294         return metaAccessContext.fromClass(javaClass);
 295     }
 296 
 297     public HotSpotVMConfig getConfig() {
 298         return config;
 299     }
 300 
 301     public CompilerToVM getCompilerToVM() {
 302         return compilerToVm;
 303     }
 304 
 305     public JVMCIMetaAccessContext getMetaAccessContext() {
 306         return metaAccessContext;
 307     }
 308 
 309     public JVMCICompiler getCompiler() {
 310         if (compiler == null) {
 311             synchronized (this) {
 312                 if (compiler == null) {
 313                     compiler = compilerFactory.createCompiler(this);
 314                 }
 315             }
 316         }
 317         return compiler;
 318     }
 319 
 320     public JavaType lookupType(String name, HotSpotResolvedObjectType accessingType, boolean resolve) {
 321         Objects.requireNonNull(accessingType, "cannot resolve type without an accessing class");
 322         // If the name represents a primitive type we can short-circuit the lookup.
 323         if (name.length() == 1) {
 324             JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0));
 325             return fromClass(kind.toJavaClass());
 326         }
 327 
 328         // Resolve non-primitive types in the VM.
 329         HotSpotResolvedObjectTypeImpl hsAccessingType = (HotSpotResolvedObjectTypeImpl) accessingType;
 330         final HotSpotResolvedObjectTypeImpl klass = compilerToVm.lookupType(name, hsAccessingType.mirror(), resolve);
 331 
 332         if (klass == null) {
 333             assert resolve == false;
 334             return HotSpotUnresolvedJavaType.create(this, name);
 335         }
 336         return klass;
 337     }
 338 
 339     public JVMCIBackend getHostJVMCIBackend() {
 340         return hostBackend;
 341     }
 342 
 343     public <T extends Architecture> JVMCIBackend getJVMCIBackend(Class<T> arch) {
 344         assert arch != Architecture.class;
 345         return backends.get(arch);
 346     }
 347 
 348     public Map<Class<? extends Architecture>, JVMCIBackend> getJVMCIBackends() {
 349         return Collections.unmodifiableMap(backends);
 350     }
 351 
 352     /**
 353      * Called from the VM.
 354      */
 355     @SuppressWarnings({"unused"})
 356     private int adjustCompilationLevel(Class<?> declaringClass, String name, String signature, boolean isOsr, int level) {
 357         return hsCompilerFactory.adjustCompilationLevel(config, declaringClass, name, signature, isOsr, level);
 358     }
 359 
 360     /**
 361      * Called from the VM.
 362      */
 363     @SuppressWarnings({"unused"})
 364     private CompilationRequestResult compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long jvmciEnv, int id) {
 365         CompilationRequestResult result = getCompiler().compileMethod(new HotSpotCompilationRequest(method, entryBCI, jvmciEnv, id));
 366         assert result != null : "compileMethod must always return something";
 367         return result;
 368     }
 369 
 370     /**
 371      * Shuts down the runtime.
 372      *
 373      * Called from the VM.
 374      */
 375     @SuppressWarnings({"unused"})
 376     private void shutdown() throws Exception {
 377         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 378             vmEventListener.notifyShutdown();
 379         }
 380     }
 381 
 382     /**
 383      * Notify on completion of a bootstrap.
 384      *
 385      * Called from the VM.
 386      */
 387     @SuppressWarnings({"unused"})
 388     private void bootstrapFinished() throws Exception {
 389         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 390             vmEventListener.notifyBootstrapFinished();
 391         }
 392     }
 393 
 394     /**
 395      * Notify on successful install into the CodeCache.
 396      *
 397      * @param hotSpotCodeCacheProvider
 398      * @param installedCode
 399      * @param compiledCode
 400      */
 401     void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompiledCode compiledCode) {
 402         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 403             vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compiledCode);
 404         }
 405     }
 406 
 407     private static void printConfig(HotSpotVMConfig config, CompilerToVM vm) {
 408         Field[] fields = config.getClass().getDeclaredFields();
 409         Map<String, Field> sortedFields = new TreeMap<>();
 410         for (Field f : fields) {
 411             if (!f.isSynthetic() && !Modifier.isStatic(f.getModifiers())) {
 412                 f.setAccessible(true);
 413                 sortedFields.put(f.getName(), f);
 414             }
 415         }
 416         for (Field f : sortedFields.values()) {
 417             try {
 418                 String line = String.format("%9s %-40s = %s%n", f.getType().getSimpleName(), f.getName(), pretty(f.get(config)));
 419                 byte[] lineBytes = line.getBytes();
 420                 vm.writeDebugOutput(lineBytes, 0, lineBytes.length);
 421                 vm.flushDebugOutput();
 422             } catch (Exception e) {
 423             }
 424         }
 425     }
 426 
 427     private static String pretty(Object value) {
 428         if (value == null) {
 429             return "null";
 430         }
 431 
 432         Class<?> klass = value.getClass();
 433         if (value instanceof String) {
 434             return "\"" + value + "\"";
 435         } else if (value instanceof Method) {
 436             return "method \"" + ((Method) value).getName() + "\"";
 437         } else if (value instanceof Class<?>) {
 438             return "class \"" + ((Class<?>) value).getSimpleName() + "\"";
 439         } else if (value instanceof Integer) {
 440             if ((Integer) value < 10) {
 441                 return value.toString();
 442             }
 443             return value + " (0x" + Integer.toHexString((Integer) value) + ")";
 444         } else if (value instanceof Long) {
 445             if ((Long) value < 10 && (Long) value > -10) {
 446                 return value + "l";
 447             }
 448             return value + "l (0x" + Long.toHexString((Long) value) + "l)";
 449         } else if (klass.isArray()) {
 450             StringBuilder str = new StringBuilder();
 451             int dimensions = 0;
 452             while (klass.isArray()) {
 453                 dimensions++;
 454                 klass = klass.getComponentType();
 455             }
 456             int length = Array.getLength(value);
 457             str.append(klass.getSimpleName()).append('[').append(length).append(']');
 458             for (int i = 1; i < dimensions; i++) {
 459                 str.append("[]");
 460             }
 461             str.append(" {");
 462             for (int i = 0; i < length; i++) {
 463                 str.append(pretty(Array.get(value, i)));
 464                 if (i < length - 1) {
 465                     str.append(", ");
 466                 }
 467             }
 468             str.append('}');
 469             return str.toString();
 470         }
 471         return value.toString();
 472     }
 473 
 474     public OutputStream getLogStream() {
 475         return new OutputStream() {
 476 
 477             @Override
 478             public void write(byte[] b, int off, int len) throws IOException {
 479                 if (b == null) {
 480                     throw new NullPointerException();
 481                 } else if (off < 0 || off > b.length || len < 0 || (off + len) > b.length || (off + len) < 0) {
 482                     throw new IndexOutOfBoundsException();
 483                 } else if (len == 0) {
 484                     return;
 485                 }
 486                 compilerToVm.writeDebugOutput(b, off, len);
 487             }
 488 
 489             @Override
 490             public void write(int b) throws IOException {
 491                 write(new byte[]{(byte) b}, 0, 1);
 492             }
 493 
 494             @Override
 495             public void flush() throws IOException {
 496                 compilerToVm.flushDebugOutput();
 497             }
 498         };
 499     }
 500 
 501     /**
 502      * Collects the current values of all JVMCI benchmark counters, summed up over all threads.
 503      */
 504     public long[] collectCounters() {
 505         return compilerToVm.collectCounters();
 506     }
 507 }