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