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.lang.reflect.Array;
  30 import java.lang.reflect.Field;
  31 import java.lang.reflect.Method;
  32 import java.lang.reflect.Modifier;
  33 import java.util.Collections;
  34 import java.util.HashMap;
  35 import java.util.Map;
  36 import java.util.Objects;
  37 import java.util.TreeMap;
  38 
  39 import jdk.vm.ci.code.Architecture;
  40 import jdk.vm.ci.code.CompilationResult;
  41 import jdk.vm.ci.code.InstalledCode;
  42 import jdk.vm.ci.common.JVMCIError;
  43 import jdk.vm.ci.inittimer.InitTimer;
  44 import jdk.vm.ci.meta.JVMCIMetaAccessContext;
  45 import jdk.vm.ci.meta.JavaKind;
  46 import jdk.vm.ci.meta.JavaType;
  47 import jdk.vm.ci.meta.ResolvedJavaType;
  48 import jdk.vm.ci.runtime.JVMCI;
  49 import jdk.vm.ci.runtime.JVMCIBackend;
  50 import jdk.vm.ci.runtime.JVMCICompiler;
  51 import jdk.vm.ci.services.Services;
  52 import sun.misc.VM;
  53 
  54 //JaCoCo Exclude
  55 
  56 /**
  57  * HotSpot implementation of a JVMCI runtime.
  58  *
  59  * The initialization of this class is very fragile since it's initialized both through
  60  * {@link JVMCI#initialize()} or through calling {@link HotSpotJVMCIRuntime#runtime()} and
  61  * {@link HotSpotJVMCIRuntime#runtime()} is also called by {@link JVMCI#initialize()}. So this class
  62  * can't have a static initializer and any required initialization must be done as part of
  63  * {@link #runtime()}. This allows the initialization to funnel back through
  64  * {@link JVMCI#initialize()} without deadlocking.
  65  */
  66 public final class HotSpotJVMCIRuntime implements HotSpotJVMCIRuntimeProvider, HotSpotProxified {
  67 
  68     @SuppressWarnings("try")
  69     static class DelayedInit {
  70         private static final HotSpotJVMCIRuntime instance;
  71 
  72         static {
  73             try (InitTimer t = timer("HotSpotJVMCIRuntime.<init>")) {
  74                 instance = new HotSpotJVMCIRuntime();
  75             }
  76         }
  77     }
  78 
  79     /**
  80      * Gets the singleton {@link HotSpotJVMCIRuntime} object.
  81      */
  82     public static HotSpotJVMCIRuntime runtime() {
  83         JVMCI.initialize();
  84         return DelayedInit.instance;
  85     }
  86 
  87     /**
  88      * Gets a boolean value based on a system property {@linkplain VM#getSavedProperty(String)
  89      * saved} at system initialization time. The property name is prefixed with "{@code jvmci.}".
  90      *
  91      * @param name the name of the system property to derive a boolean value from using
  92      *            {@link Boolean#parseBoolean(String)}
  93      * @param def the value to return if there is no system property corresponding to {@code name}
  94      */
  95     public static boolean getBooleanProperty(String name, boolean def) {
  96         String value = VM.getSavedProperty("jvmci." + name);
  97         if (value == null) {
  98             return def;
  99         }
 100         return Boolean.parseBoolean(value);
 101     }
 102 
 103     public static HotSpotJVMCIBackendFactory findFactory(String architecture) {
 104         for (HotSpotJVMCIBackendFactory factory : Services.load(HotSpotJVMCIBackendFactory.class)) {
 105             if (factory.getArchitecture().equalsIgnoreCase(architecture)) {
 106                 return factory;
 107             }
 108         }
 109 
 110         throw new JVMCIError("No JVMCI runtime available for the %s architecture", architecture);
 111     }
 112 
 113     /**
 114      * Gets the kind of a word value on the {@linkplain #getHostJVMCIBackend() host} backend.
 115      */
 116     public static JavaKind getHostWordKind() {
 117         return runtime().getHostJVMCIBackend().getCodeCache().getTarget().wordJavaKind;
 118     }
 119 
 120     protected final CompilerToVM compilerToVm;
 121 
 122     protected final HotSpotVMConfig config;
 123     private final JVMCIBackend hostBackend;
 124 
 125     private volatile JVMCICompiler compiler;
 126     protected final JVMCIMetaAccessContext metaAccessContext;
 127 
 128     private final Map<Class<? extends Architecture>, JVMCIBackend> backends = new HashMap<>();
 129 
 130     private final Iterable<HotSpotVMEventListener> vmEventListeners;
 131 
 132     @SuppressWarnings("unused") private final String[] trivialPrefixes;
 133 
 134     @SuppressWarnings("try")
 135     private HotSpotJVMCIRuntime() {
 136         compilerToVm = new CompilerToVM();
 137 
 138         try (InitTimer t = timer("HotSpotVMConfig<init>")) {
 139             config = new HotSpotVMConfig(compilerToVm);
 140         }
 141 
 142         String hostArchitecture = config.getHostArchitectureName();
 143 
 144         HotSpotJVMCIBackendFactory factory;
 145         try (InitTimer t = timer("find factory:", hostArchitecture)) {
 146             factory = findFactory(hostArchitecture);
 147         }
 148 
 149         try (InitTimer t = timer("create JVMCI backend:", hostArchitecture)) {
 150             hostBackend = registerBackend(factory.createJVMCIBackend(this, null));
 151         }
 152 
 153         vmEventListeners = Services.load(HotSpotVMEventListener.class);
 154 
 155         JVMCIMetaAccessContext context = null;
 156         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 157             context = vmEventListener.createMetaAccessContext(this);
 158             if (context != null) {
 159                 break;
 160             }
 161         }
 162         if (context == null) {
 163             context = new HotSpotJVMCIMetaAccessContext();
 164         }
 165         metaAccessContext = context;
 166 
 167         if (Boolean.valueOf(System.getProperty("jvmci.printconfig"))) {
 168             printConfig(config, compilerToVm);
 169         }
 170 
 171         trivialPrefixes = HotSpotJVMCICompilerConfig.getCompilerFactory().getTrivialPrefixes();
 172     }
 173 
 174     private JVMCIBackend registerBackend(JVMCIBackend backend) {
 175         Class<? extends Architecture> arch = backend.getCodeCache().getTarget().arch.getClass();
 176         JVMCIBackend oldValue = backends.put(arch, backend);
 177         assert oldValue == null : "cannot overwrite existing backend for architecture " + arch.getSimpleName();
 178         return backend;
 179     }
 180 
 181     public ResolvedJavaType fromClass(Class<?> javaClass) {
 182         return metaAccessContext.fromClass(javaClass);
 183     }
 184 
 185     public HotSpotVMConfig getConfig() {
 186         return config;
 187     }
 188 
 189     public CompilerToVM getCompilerToVM() {
 190         return compilerToVm;
 191     }
 192 
 193     public JVMCIMetaAccessContext getMetaAccessContext() {
 194         return metaAccessContext;
 195     }
 196 
 197     public JVMCICompiler getCompiler() {
 198         if (compiler == null) {
 199             synchronized (this) {
 200                 if (compiler == null) {
 201                     compiler = HotSpotJVMCICompilerConfig.getCompilerFactory().createCompiler(this);
 202                 }
 203             }
 204         }
 205         return compiler;
 206     }
 207 
 208     public JavaType lookupType(String name, HotSpotResolvedObjectType accessingType, boolean resolve) {
 209         Objects.requireNonNull(accessingType, "cannot resolve type without an accessing class");
 210         // If the name represents a primitive type we can short-circuit the lookup.
 211         if (name.length() == 1) {
 212             JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0));
 213             return fromClass(kind.toJavaClass());
 214         }
 215 
 216         // Resolve non-primitive types in the VM.
 217         HotSpotResolvedObjectTypeImpl hsAccessingType = (HotSpotResolvedObjectTypeImpl) accessingType;
 218         final HotSpotResolvedObjectTypeImpl klass = compilerToVm.lookupType(name, hsAccessingType.mirror(), resolve);
 219 
 220         if (klass == null) {
 221             assert resolve == false;
 222             return HotSpotUnresolvedJavaType.create(this, name);
 223         }
 224         return klass;
 225     }
 226 
 227     public JVMCIBackend getHostJVMCIBackend() {
 228         return hostBackend;
 229     }
 230 
 231     public <T extends Architecture> JVMCIBackend getJVMCIBackend(Class<T> arch) {
 232         assert arch != Architecture.class;
 233         return backends.get(arch);
 234     }
 235 
 236     public Map<Class<? extends Architecture>, JVMCIBackend> getJVMCIBackends() {
 237         return Collections.unmodifiableMap(backends);
 238     }
 239 
 240     /**
 241      * Called from the VM.
 242      */
 243     @SuppressWarnings({"unused"})
 244     private void compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long jvmciEnv, int id) {
 245         getCompiler().compileMethod(new HotSpotCompilationRequest(method, entryBCI, jvmciEnv, id));
 246     }
 247 
 248     /**
 249      * Shuts down the runtime.
 250      *
 251      * Called from the VM.
 252      */
 253     @SuppressWarnings({"unused"})
 254     private void shutdown() throws Exception {
 255         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 256             vmEventListener.notifyShutdown();
 257         }
 258     }
 259 
 260     /**
 261      * Notify on successful install into the CodeCache.
 262      *
 263      * @param hotSpotCodeCacheProvider
 264      * @param installedCode
 265      * @param compResult
 266      */
 267     void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompilationResult compResult) {
 268         for (HotSpotVMEventListener vmEventListener : vmEventListeners) {
 269             vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compResult);
 270         }
 271     }
 272 
 273     private static void printConfig(HotSpotVMConfig config, CompilerToVM vm) {
 274         Field[] fields = config.getClass().getDeclaredFields();
 275         Map<String, Field> sortedFields = new TreeMap<>();
 276         for (Field f : fields) {
 277             if (!f.isSynthetic() && !Modifier.isStatic(f.getModifiers())) {
 278                 f.setAccessible(true);
 279                 sortedFields.put(f.getName(), f);
 280             }
 281         }
 282         for (Field f : sortedFields.values()) {
 283             try {
 284                 String line = String.format("%9s %-40s = %s%n", f.getType().getSimpleName(), f.getName(), pretty(f.get(config)));
 285                 byte[] lineBytes = line.getBytes();
 286                 vm.writeDebugOutput(lineBytes, 0, lineBytes.length);
 287                 vm.flushDebugOutput();
 288             } catch (Exception e) {
 289             }
 290         }
 291     }
 292 
 293     private static String pretty(Object value) {
 294         if (value == null) {
 295             return "null";
 296         }
 297 
 298         Class<?> klass = value.getClass();
 299         if (value instanceof String) {
 300             return "\"" + value + "\"";
 301         } else if (value instanceof Method) {
 302             return "method \"" + ((Method) value).getName() + "\"";
 303         } else if (value instanceof Class<?>) {
 304             return "class \"" + ((Class<?>) value).getSimpleName() + "\"";
 305         } else if (value instanceof Integer) {
 306             if ((Integer) value < 10) {
 307                 return value.toString();
 308             }
 309             return value + " (0x" + Integer.toHexString((Integer) value) + ")";
 310         } else if (value instanceof Long) {
 311             if ((Long) value < 10 && (Long) value > -10) {
 312                 return value + "l";
 313             }
 314             return value + "l (0x" + Long.toHexString((Long) value) + "l)";
 315         } else if (klass.isArray()) {
 316             StringBuilder str = new StringBuilder();
 317             int dimensions = 0;
 318             while (klass.isArray()) {
 319                 dimensions++;
 320                 klass = klass.getComponentType();
 321             }
 322             int length = Array.getLength(value);
 323             str.append(klass.getSimpleName()).append('[').append(length).append(']');
 324             for (int i = 1; i < dimensions; i++) {
 325                 str.append("[]");
 326             }
 327             str.append(" {");
 328             for (int i = 0; i < length; i++) {
 329                 str.append(pretty(Array.get(value, i)));
 330                 if (i < length - 1) {
 331                     str.append(", ");
 332                 }
 333             }
 334             str.append('}');
 335             return str.toString();
 336         }
 337         return value.toString();
 338     }
 339 
 340     public OutputStream getLogStream() {
 341         return new OutputStream() {
 342 
 343             @Override
 344             public void write(byte[] b, int off, int len) throws IOException {
 345                 if (b == null) {
 346                     throw new NullPointerException();
 347                 } else if (off < 0 || off > b.length || len < 0 || (off + len) > b.length || (off + len) < 0) {
 348                     throw new IndexOutOfBoundsException();
 349                 } else if (len == 0) {
 350                     return;
 351                 }
 352                 compilerToVm.writeDebugOutput(b, off, len);
 353             }
 354 
 355             @Override
 356             public void write(int b) throws IOException {
 357                 write(new byte[]{(byte) b}, 0, 1);
 358             }
 359 
 360             @Override
 361             public void flush() throws IOException {
 362                 compilerToVm.flushDebugOutput();
 363             }
 364         };
 365     }
 366 
 367     /**
 368      * Collects the current values of all JVMCI benchmark counters, summed up over all threads.
 369      */
 370     public long[] collectCounters() {
 371         return compilerToVm.collectCounters();
 372     }
 373 }