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