--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorld.java Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,818 @@
+/*
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.hotspot.test;
+
+import static java.util.Collections.singletonList;
+import static org.graalvm.compiler.core.CompilationWrapper.ExceptionAction.Print;
+import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationBailoutAction;
+import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationFailureAction;
+import static org.graalvm.compiler.core.test.ReflectionOptionDescriptors.extractEntries;
+import static org.graalvm.compiler.debug.MemUseTrackerKey.getCurrentThreadAllocatedBytes;
+import static org.graalvm.compiler.hotspot.test.CompileTheWorld.Options.DESCRIPTORS;
+import static org.graalvm.compiler.serviceprovider.JDK9Method.Java8OrEarlier;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.stream.Collectors;
+
+import org.graalvm.compiler.api.replacements.Snippet;
+import org.graalvm.compiler.bytecode.Bytecodes;
+import org.graalvm.compiler.core.CompilerThreadFactory;
+import org.graalvm.compiler.core.test.ReflectionOptionDescriptors;
+import org.graalvm.compiler.debug.DebugOptions;
+import org.graalvm.compiler.debug.GraalError;
+import org.graalvm.compiler.debug.MethodFilter;
+import org.graalvm.compiler.debug.TTY;
+import org.graalvm.compiler.hotspot.CompilationTask;
+import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
+import org.graalvm.compiler.hotspot.HotSpotGraalCompiler;
+import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
+import org.graalvm.compiler.options.OptionDescriptors;
+import org.graalvm.compiler.options.OptionKey;
+import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.options.OptionsParser;
+import org.graalvm.compiler.serviceprovider.JDK9Method;
+import org.graalvm.util.EconomicMap;
+import org.graalvm.util.UnmodifiableEconomicMap;
+
+import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider;
+import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
+import jdk.vm.ci.hotspot.HotSpotInstalledCode;
+import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
+import jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider;
+import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
+import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
+import jdk.vm.ci.meta.ConstantPool;
+import jdk.vm.ci.meta.MetaAccessProvider;
+import jdk.vm.ci.runtime.JVMCI;
+import jdk.vm.ci.runtime.JVMCICompiler;
+
+/**
+ * This class implements compile-the-world functionality with JVMCI.
+ */
+public final class CompileTheWorld {
+
+ /**
+ * Magic token to denote that JDK classes are to be compiled. If
+ * {@link JDK9Method#Java8OrEarlier}, then the classes in {@code rt.jar} are compiled. Otherwise
+ * the classes in the Java runtime image are compiled.
+ */
+ public static final String SUN_BOOT_CLASS_PATH = "sun.boot.class.path";
+
+ /**
+ * Magic token to denote the classes in the Java runtime image (i.e. in the {@code jrt:/} file
+ * system).
+ */
+ public static final String JRT_CLASS_PATH_ENTRY = "<jrt>";
+
+ /**
+ * @param options a space separated set of option value settings with each option setting in a
+ * {@code -Dgraal.<name>=<value>} format but without the leading {@code -Dgraal.}.
+ * Ignored if null.
+ */
+ public static EconomicMap<OptionKey<?>, Object> parseOptions(String options) {
+ if (options != null) {
+ EconomicMap<String, String> optionSettings = EconomicMap.create();
+ for (String optionSetting : options.split("\\s+|#")) {
+ OptionsParser.parseOptionSettingTo(optionSetting, optionSettings);
+ }
+ EconomicMap<OptionKey<?>, Object> values = OptionValues.newOptionMap();
+ ServiceLoader<OptionDescriptors> loader = ServiceLoader.load(OptionDescriptors.class, OptionDescriptors.class.getClassLoader());
+ OptionsParser.parseOptions(optionSettings, values, loader);
+ return values;
+ }
+ return EconomicMap.create();
+ }
+
+ private final HotSpotJVMCIRuntimeProvider jvmciRuntime;
+
+ private final HotSpotGraalCompiler compiler;
+
+ /**
+ * Class path denoting classes to compile.
+ *
+ * @see Options#Classpath
+ */
+ private final String inputClassPath;
+
+ /**
+ * Class index to start compilation at.
+ *
+ * @see Options#StartAt
+ */
+ private final int startAt;
+
+ /**
+ * Class index to stop compilation at.
+ *
+ * @see Options#StopAt
+ */
+ private final int stopAt;
+
+ /** Only compile methods matching one of the filters in this array if the array is non-null. */
+ private final MethodFilter[] methodFilters;
+
+ /** Exclude methods matching one of the filters in this array if the array is non-null. */
+ private final MethodFilter[] excludeMethodFilters;
+
+ // Counters
+ private int classFileCounter = 0;
+ private AtomicLong compiledMethodsCounter = new AtomicLong();
+ private AtomicLong compileTime = new AtomicLong();
+ private AtomicLong memoryUsed = new AtomicLong();
+
+ private boolean verbose;
+
+ /**
+ * Signal that the threads should start compiling in multithreaded mode.
+ */
+ private boolean running;
+
+ private ThreadPoolExecutor threadPool;
+
+ private OptionValues currentOptions;
+ private final UnmodifiableEconomicMap<OptionKey<?>, Object> compilationOptions;
+
+ /**
+ * Creates a compile-the-world instance.
+ *
+ * @param files {@link File#pathSeparator} separated list of Zip/Jar files to compile
+ * @param startAt index of the class file to start compilation at
+ * @param stopAt index of the class file to stop compilation at
+ * @param methodFilters
+ * @param excludeMethodFilters
+ */
+ public CompileTheWorld(HotSpotJVMCIRuntimeProvider jvmciRuntime, HotSpotGraalCompiler compiler, String files, int startAt, int stopAt, String methodFilters, String excludeMethodFilters,
+ boolean verbose, OptionValues initialOptions, EconomicMap<OptionKey<?>, Object> compilationOptions) {
+ this.jvmciRuntime = jvmciRuntime;
+ this.compiler = compiler;
+ this.inputClassPath = files;
+ this.startAt = startAt;
+ this.stopAt = stopAt;
+ this.methodFilters = methodFilters == null || methodFilters.isEmpty() ? null : MethodFilter.parse(methodFilters);
+ this.excludeMethodFilters = excludeMethodFilters == null || excludeMethodFilters.isEmpty() ? null : MethodFilter.parse(excludeMethodFilters);
+ this.verbose = verbose;
+ this.currentOptions = initialOptions;
+
+ // Copy the initial options and add in any extra options
+ EconomicMap<OptionKey<?>, Object> compilationOptionsCopy = EconomicMap.create(initialOptions.getMap());
+ compilationOptionsCopy.putAll(compilationOptions);
+
+ // We want to see stack traces when a method fails to compile
+ CompilationBailoutAction.putIfAbsent(compilationOptionsCopy, Print);
+ CompilationFailureAction.putIfAbsent(compilationOptionsCopy, Print);
+
+ // By default only report statistics for the CTW threads themselves
+ DebugOptions.MetricsThreadFilter.putIfAbsent(compilationOptionsCopy, "^CompileTheWorld");
+ this.compilationOptions = compilationOptionsCopy;
+ }
+
+ public CompileTheWorld(HotSpotJVMCIRuntimeProvider jvmciRuntime, HotSpotGraalCompiler compiler, OptionValues options) {
+ this(jvmciRuntime, compiler, Options.Classpath.getValue(options),
+ Options.StartAt.getValue(options),
+ Options.StopAt.getValue(options),
+ Options.MethodFilter.getValue(options),
+ Options.ExcludeMethodFilter.getValue(options),
+ Options.Verbose.getValue(options),
+ options,
+ parseOptions(Options.Config.getValue(options)));
+ }
+
+ /**
+ * Compiles all methods in all classes in {@link #inputClassPath}. If {@link #inputClassPath}
+ * equals {@link #SUN_BOOT_CLASS_PATH} the boot classes are used.
+ */
+ public void compile() throws Throwable {
+ if (SUN_BOOT_CLASS_PATH.equals(inputClassPath)) {
+ String bcpEntry = null;
+ if (Java8OrEarlier) {
+ final String[] entries = System.getProperty(SUN_BOOT_CLASS_PATH).split(File.pathSeparator);
+ for (int i = 0; i < entries.length && bcpEntry == null; i++) {
+ String entry = entries[i];
+ File entryFile = new File(entry);
+ if (entryFile.getName().endsWith("rt.jar") && entryFile.isFile()) {
+ bcpEntry = entry;
+ }
+ }
+ if (bcpEntry == null) {
+ throw new GraalError("Could not find rt.jar on boot class path %s", System.getProperty(SUN_BOOT_CLASS_PATH));
+ }
+ } else {
+ bcpEntry = JRT_CLASS_PATH_ENTRY;
+ }
+ compile(bcpEntry);
+ } else {
+ compile(inputClassPath);
+ }
+ }
+
+ public void println() {
+ println("");
+ }
+
+ public void println(String format, Object... args) {
+ println(String.format(format, args));
+ }
+
+ public void println(String s) {
+ println(verbose, s);
+ }
+
+ public static void println(boolean cond, String s) {
+ if (cond) {
+ TTY.println(s);
+ }
+ }
+
+ public void printStackTrace(Throwable t) {
+ if (verbose) {
+ t.printStackTrace(TTY.out);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static void dummy() {
+ }
+
+ /**
+ * Abstraction over different types of class path entries.
+ */
+ abstract static class ClassPathEntry implements Closeable {
+ final String name;
+
+ ClassPathEntry(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Creates a {@link ClassLoader} for loading classes from this entry.
+ */
+ public abstract ClassLoader createClassLoader() throws IOException;
+
+ /**
+ * Gets the list of classes available under this entry.
+ */
+ public abstract List<String> getClassNames() throws IOException;
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+ }
+
+ /**
+ * A class path entry that is a normal file system directory.
+ */
+ static class DirClassPathEntry extends ClassPathEntry {
+
+ private final File dir;
+
+ DirClassPathEntry(String name) {
+ super(name);
+ dir = new File(name);
+ assert dir.isDirectory();
+ }
+
+ @Override
+ public ClassLoader createClassLoader() throws IOException {
+ URL url = dir.toURI().toURL();
+ return new URLClassLoader(new URL[]{url});
+ }
+
+ @Override
+ public List<String> getClassNames() throws IOException {
+ List<String> classNames = new ArrayList<>();
+ String root = dir.getPath();
+ SimpleFileVisitor<Path> visitor = new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ if (attrs.isRegularFile()) {
+ File path = file.toFile();
+ if (path.getName().endsWith(".class")) {
+ String pathString = path.getPath();
+ assert pathString.startsWith(root);
+ String classFile = pathString.substring(root.length() + 1);
+ String className = classFile.replace(File.separatorChar, '.');
+ classNames.add(className.replace('/', '.').substring(0, className.length() - ".class".length()));
+ }
+ }
+ return super.visitFile(file, attrs);
+ }
+ };
+ Files.walkFileTree(dir.toPath(), visitor);
+ return classNames;
+ }
+ }
+
+ /**
+ * A class path entry that is a jar or zip file.
+ */
+ static class JarClassPathEntry extends ClassPathEntry {
+
+ private final JarFile jarFile;
+
+ JarClassPathEntry(String name) throws IOException {
+ super(name);
+ jarFile = new JarFile(name);
+ }
+
+ @Override
+ public ClassLoader createClassLoader() throws IOException {
+ URL url = new URL("jar", "", "file:" + name + "!/");
+ return new URLClassLoader(new URL[]{url});
+ }
+
+ @Override
+ public List<String> getClassNames() throws IOException {
+ Enumeration<JarEntry> e = jarFile.entries();
+ List<String> classNames = new ArrayList<>(jarFile.size());
+ while (e.hasMoreElements()) {
+ JarEntry je = e.nextElement();
+ if (je.isDirectory() || !je.getName().endsWith(".class")) {
+ continue;
+ }
+ String className = je.getName().substring(0, je.getName().length() - ".class".length());
+ classNames.add(className.replace('/', '.'));
+ }
+ return classNames;
+ }
+
+ @Override
+ public void close() throws IOException {
+ jarFile.close();
+ }
+ }
+
+ /**
+ * A class path entry representing the {@code jrt:/} file system.
+ */
+ static class JRTClassPathEntry extends ClassPathEntry {
+
+ private final String limitModules;
+
+ JRTClassPathEntry(String name, String limitModules) {
+ super(name);
+ this.limitModules = limitModules;
+ }
+
+ @Override
+ public ClassLoader createClassLoader() throws IOException {
+ URL url = URI.create("jrt:/").toURL();
+ return new URLClassLoader(new URL[]{url});
+ }
+
+ @Override
+ public List<String> getClassNames() throws IOException {
+ Set<String> negative = new HashSet<>();
+ Set<String> positive = new HashSet<>();
+ if (limitModules != null && !limitModules.isEmpty()) {
+ for (String s : limitModules.split(",")) {
+ if (s.startsWith("~")) {
+ negative.add(s.substring(1));
+ } else {
+ positive.add(s);
+ }
+ }
+ }
+ List<String> classNames = new ArrayList<>();
+ FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Collections.emptyMap());
+ Path top = fs.getPath("/modules/");
+ Files.find(top, Integer.MAX_VALUE,
+ (path, attrs) -> attrs.isRegularFile()).forEach(p -> {
+ int nameCount = p.getNameCount();
+ if (nameCount > 2) {
+ String base = p.getName(nameCount - 1).toString();
+ if (base.endsWith(".class") && !base.equals("module-info.class")) {
+ String module = p.getName(1).toString();
+ if (positive.isEmpty() || positive.contains(module)) {
+ if (negative.isEmpty() || !negative.contains(module)) {
+ // Strip module prefix and convert to dotted form
+ String className = p.subpath(2, nameCount).toString().replace('/', '.');
+ // Strip ".class" suffix
+ className = className.replace('/', '.').substring(0, className.length() - ".class".length());
+ classNames.add(className);
+ }
+ }
+ }
+ }
+ });
+ return classNames;
+ }
+ }
+
+ private boolean isClassIncluded(String className) {
+ if (methodFilters != null && !MethodFilter.matchesClassName(methodFilters, className)) {
+ return false;
+ }
+ if (excludeMethodFilters != null && MethodFilter.matchesClassName(excludeMethodFilters, className)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Compiles all methods in all classes in a given class path.
+ *
+ * @param classPath class path denoting classes to compile
+ * @throws IOException
+ */
+ @SuppressWarnings("try")
+ private void compile(String classPath) throws IOException {
+ final String[] entries = classPath.split(File.pathSeparator);
+ long start = System.currentTimeMillis();
+
+ try {
+ // compile dummy method to get compiler initialized outside of the
+ // config debug override.
+ HotSpotResolvedJavaMethod dummyMethod = (HotSpotResolvedJavaMethod) JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess().lookupJavaMethod(
+ CompileTheWorld.class.getDeclaredMethod("dummy"));
+ int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI;
+ boolean useProfilingInfo = false;
+ boolean installAsDefault = false;
+ CompilationTask task = new CompilationTask(jvmciRuntime, compiler, new HotSpotCompilationRequest(dummyMethod, entryBCI, 0L), useProfilingInfo, installAsDefault, currentOptions);
+ task.runCompilation();
+ } catch (NoSuchMethodException | SecurityException e1) {
+ printStackTrace(e1);
+ }
+
+ /*
+ * Always use a thread pool, even for single threaded mode since it simplifies the use of
+ * DebugValueThreadFilter to filter on the thread names.
+ */
+ int threadCount = 1;
+ if (Options.MultiThreaded.getValue(currentOptions)) {
+ threadCount = Options.Threads.getValue(currentOptions);
+ if (threadCount == 0) {
+ threadCount = Runtime.getRuntime().availableProcessors();
+ }
+ } else {
+ running = true;
+ }
+
+ OptionValues savedOptions = currentOptions;
+ currentOptions = new OptionValues(compilationOptions);
+ threadPool = new ThreadPoolExecutor(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new CompilerThreadFactory("CompileTheWorld"));
+
+ try {
+ for (int i = 0; i < entries.length; i++) {
+ final String entry = entries[i];
+
+ ClassPathEntry cpe;
+ if (entry.endsWith(".zip") || entry.endsWith(".jar")) {
+ cpe = new JarClassPathEntry(entry);
+ } else if (entry.equals(JRT_CLASS_PATH_ENTRY)) {
+ cpe = new JRTClassPathEntry(entry, Options.LimitModules.getValue(currentOptions));
+ } else {
+ if (!new File(entry).isDirectory()) {
+ println("CompileTheWorld : Skipped classes in " + entry);
+ println();
+ continue;
+ }
+ cpe = new DirClassPathEntry(entry);
+ }
+
+ if (methodFilters == null || methodFilters.length == 0) {
+ println("CompileTheWorld : Compiling all classes in " + entry);
+ } else {
+ String include = Arrays.asList(methodFilters).stream().map(MethodFilter::toString).collect(Collectors.joining(", "));
+ println("CompileTheWorld : Compiling all methods in " + entry + " matching one of the following filters: " + include);
+ }
+ if (excludeMethodFilters != null && excludeMethodFilters.length > 0) {
+ String exclude = Arrays.asList(excludeMethodFilters).stream().map(MethodFilter::toString).collect(Collectors.joining(", "));
+ println("CompileTheWorld : Excluding all methods matching one of the following filters: " + exclude);
+ }
+ println();
+
+ ClassLoader loader = cpe.createClassLoader();
+
+ for (String className : cpe.getClassNames()) {
+
+ // Are we done?
+ if (classFileCounter >= stopAt) {
+ break;
+ }
+
+ classFileCounter++;
+
+ if (className.startsWith("jdk.management.") || className.startsWith("jdk.internal.cmm.*")) {
+ continue;
+ }
+
+ try {
+ // Load and initialize class
+ Class<?> javaClass = Class.forName(className, true, loader);
+
+ // Pre-load all classes in the constant pool.
+ try {
+ HotSpotResolvedObjectType objectType = HotSpotResolvedObjectType.fromObjectClass(javaClass);
+ ConstantPool constantPool = objectType.getConstantPool();
+ for (int cpi = 1; cpi < constantPool.length(); cpi++) {
+ constantPool.loadReferencedType(cpi, Bytecodes.LDC);
+ }
+ } catch (Throwable t) {
+ // If something went wrong during pre-loading we just ignore it.
+ if (isClassIncluded(className)) {
+ println("Preloading failed for (%d) %s: %s", classFileCounter, className, t);
+ }
+ continue;
+ }
+
+ /*
+ * Only check filters after class loading and resolution to mitigate impact
+ * on reproducibility.
+ */
+ if (!isClassIncluded(className)) {
+ continue;
+ }
+
+ // Are we compiling this class?
+ MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess();
+ if (classFileCounter >= startAt) {
+ println("CompileTheWorld (%d) : %s", classFileCounter, className);
+
+ // Compile each constructor/method in the class.
+ for (Constructor<?> constructor : javaClass.getDeclaredConstructors()) {
+ HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(constructor);
+ if (canBeCompiled(javaMethod, constructor.getModifiers())) {
+ compileMethod(javaMethod);
+ }
+ }
+ for (Method method : javaClass.getDeclaredMethods()) {
+ HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(method);
+ if (canBeCompiled(javaMethod, method.getModifiers())) {
+ compileMethod(javaMethod);
+ }
+ }
+
+ // Also compile the class initializer if it exists
+ HotSpotResolvedJavaMethod clinit = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaType(javaClass).getClassInitializer();
+ if (clinit != null && canBeCompiled(clinit, clinit.getModifiers())) {
+ compileMethod(clinit);
+ }
+ }
+ } catch (Throwable t) {
+ if (isClassIncluded(className)) {
+ println("CompileTheWorld (%d) : Skipping %s %s", classFileCounter, className, t.toString());
+ printStackTrace(t);
+ }
+ }
+ }
+ cpe.close();
+ }
+ } finally {
+ currentOptions = savedOptions;
+ }
+
+ if (!running) {
+ startThreads();
+ }
+ int wakeups = 0;
+ while (threadPool.getCompletedTaskCount() != threadPool.getTaskCount()) {
+ if (wakeups % 15 == 0) {
+ TTY.println("CompileTheWorld : Waiting for " + (threadPool.getTaskCount() - threadPool.getCompletedTaskCount()) + " compiles");
+ }
+ try {
+ threadPool.awaitTermination(1, TimeUnit.SECONDS);
+ wakeups++;
+ } catch (InterruptedException e) {
+ }
+ }
+ threadPool = null;
+
+ long elapsedTime = System.currentTimeMillis() - start;
+
+ println();
+ if (Options.MultiThreaded.getValue(currentOptions)) {
+ TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms elapsed, %d ms compile time, %d bytes of memory used)", classFileCounter, compiledMethodsCounter.get(), elapsedTime,
+ compileTime.get(), memoryUsed.get());
+ } else {
+ TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms, %d bytes of memory used)", classFileCounter, compiledMethodsCounter.get(), compileTime.get(), memoryUsed.get());
+ }
+ }
+
+ private synchronized void startThreads() {
+ running = true;
+ // Wake up any waiting threads
+ notifyAll();
+ }
+
+ private synchronized void waitToRun() {
+ while (!running) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ @SuppressWarnings("try")
+ private void compileMethod(HotSpotResolvedJavaMethod method) throws InterruptedException, ExecutionException {
+ if (methodFilters != null && !MethodFilter.matches(methodFilters, method)) {
+ return;
+ }
+ if (excludeMethodFilters != null && MethodFilter.matches(excludeMethodFilters, method)) {
+ return;
+ }
+ Future<?> task = threadPool.submit(new Runnable() {
+ @Override
+ public void run() {
+ waitToRun();
+ OptionValues savedOptions = currentOptions;
+ currentOptions = new OptionValues(compilationOptions);
+ try {
+ compileMethod(method, classFileCounter);
+ } finally {
+ currentOptions = savedOptions;
+ }
+ }
+ });
+ if (threadPool.getCorePoolSize() == 1) {
+ task.get();
+ }
+ }
+
+ /**
+ * Compiles a method and gathers some statistics.
+ */
+ private void compileMethod(HotSpotResolvedJavaMethod method, int counter) {
+ try {
+ long start = System.currentTimeMillis();
+ long allocatedAtStart = getCurrentThreadAllocatedBytes();
+ int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI;
+ HotSpotCompilationRequest request = new HotSpotCompilationRequest(method, entryBCI, 0L);
+ // For more stable CTW execution, disable use of profiling information
+ boolean useProfilingInfo = false;
+ boolean installAsDefault = false;
+ CompilationTask task = new CompilationTask(jvmciRuntime, compiler, request, useProfilingInfo, installAsDefault, currentOptions);
+ task.runCompilation();
+
+ // Invalidate the generated code so the code cache doesn't fill up
+ HotSpotInstalledCode installedCode = task.getInstalledCode();
+ if (installedCode != null) {
+ installedCode.invalidate();
+ }
+
+ memoryUsed.getAndAdd(getCurrentThreadAllocatedBytes() - allocatedAtStart);
+ compileTime.getAndAdd(System.currentTimeMillis() - start);
+ compiledMethodsCounter.incrementAndGet();
+ } catch (Throwable t) {
+ // Catch everything and print a message
+ println("CompileTheWorld (%d) : Error compiling method: %s", counter, method.format("%H.%n(%p):%r"));
+ printStackTrace(t);
+ }
+ }
+
+ /**
+ * Determines if a method should be compiled (Cf. CompilationPolicy::can_be_compiled).
+ *
+ * @return true if it can be compiled, false otherwise
+ */
+ private boolean canBeCompiled(HotSpotResolvedJavaMethod javaMethod, int modifiers) {
+ if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) {
+ return false;
+ }
+ GraalHotSpotVMConfig c = compiler.getGraalRuntime().getVMConfig();
+ if (c.dontCompileHugeMethods && javaMethod.getCodeSize() > c.hugeMethodLimit) {
+ println(verbose || methodFilters != null,
+ String.format("CompileTheWorld (%d) : Skipping huge method %s (use -XX:-DontCompileHugeMethods or -XX:HugeMethodLimit=%d to include it)", classFileCounter,
+ javaMethod.format("%H.%n(%p):%r"),
+ javaMethod.getCodeSize()));
+ return false;
+ }
+ // Allow use of -XX:CompileCommand=dontinline to exclude problematic methods
+ if (!javaMethod.canBeInlined()) {
+ return false;
+ }
+ // Skip @Snippets for now
+ for (Annotation annotation : javaMethod.getAnnotations()) {
+ if (annotation.annotationType().equals(Snippet.class)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static class Options {
+ // @formatter:off
+ public static final OptionKey<Boolean> Help = new OptionKey<>(false);
+ public static final OptionKey<String> Classpath = new OptionKey<>(CompileTheWorld.SUN_BOOT_CLASS_PATH);
+ public static final OptionKey<Boolean> Verbose = new OptionKey<>(true);
+ /**
+ * Ignore Graal classes by default to avoid problems associated with compiling
+ * snippets and method substitutions.
+ */
+ public static final OptionKey<String> LimitModules = new OptionKey<>("~jdk.internal.vm.compiler");
+ public static final OptionKey<Integer> Iterations = new OptionKey<>(1);
+ public static final OptionKey<String> MethodFilter = new OptionKey<>(null);
+ public static final OptionKey<String> ExcludeMethodFilter = new OptionKey<>(null);
+ public static final OptionKey<Integer> StartAt = new OptionKey<>(1);
+ public static final OptionKey<Integer> StopAt = new OptionKey<>(Integer.MAX_VALUE);
+ public static final OptionKey<String> Config = new OptionKey<>(null);
+ public static final OptionKey<Boolean> MultiThreaded = new OptionKey<>(false);
+ public static final OptionKey<Integer> Threads = new OptionKey<>(0);
+
+ static final ReflectionOptionDescriptors DESCRIPTORS = new ReflectionOptionDescriptors(Options.class,
+ "Help", "List options and their help messages and then exit.",
+ "Classpath", "Class path denoting methods to compile. Default is to compile boot classes.",
+ "Verbose", "Verbose operation.",
+ "LimitModules", "Comma separated list of module names to which compilation should be limited. " +
+ "Module names can be prefixed with \"~\" to exclude the named module.",
+ "Iterations", "The number of iterations to perform.",
+ "MethodFilter", "Only compile methods matching this filter.",
+ "ExcludeMethodFilter", "Exclude methods matching this filter from compilation.",
+ "StartAt", "First class to consider for compilation.",
+ "StopAt", "Last class to consider for compilation.",
+ "Config", "Option value overrides to use during compile the world. For example, " +
+ "to disable inlining and partial escape analysis specify 'PartialEscapeAnalysis=false Inline=false'. " +
+ "The format for each option is the same as on the command line just without the '-Dgraal.' prefix.",
+ "MultiThreaded", "Run using multiple threads for compilation.",
+ "Threads", "Number of threads to use for multithreaded execution. Defaults to Runtime.getRuntime().availableProcessors().");
+ // @formatter:on
+ }
+
+ public static OptionValues loadOptions(OptionValues initialValues) {
+ EconomicMap<OptionKey<?>, Object> values = OptionValues.newOptionMap();
+ List<OptionDescriptors> loader = singletonList(DESCRIPTORS);
+ OptionsParser.parseOptions(extractEntries(System.getProperties(), "CompileTheWorld.", true), values, loader);
+ OptionValues options = new OptionValues(initialValues, values);
+ if (Options.Help.getValue(options)) {
+ options.printHelp(loader, System.out, "CompileTheWorld.");
+ System.exit(0);
+ }
+ return options;
+ }
+
+ public static void main(String[] args) throws Throwable {
+ HotSpotJVMCIRuntime jvmciRuntime = HotSpotJVMCIRuntime.runtime();
+ HotSpotGraalCompiler compiler = (HotSpotGraalCompiler) jvmciRuntime.getCompiler();
+ HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime();
+ HotSpotCodeCacheProvider codeCache = graalRuntime.getHostProviders().getCodeCache();
+ OptionValues options = loadOptions(graalRuntime.getOptions());
+
+ int iterations = Options.Iterations.getValue(options);
+ for (int i = 0; i < iterations; i++) {
+ codeCache.resetCompilationStatistics();
+ TTY.println("CompileTheWorld : iteration " + i);
+
+ CompileTheWorld ctw = new CompileTheWorld(jvmciRuntime, compiler, options);
+ ctw.compile();
+ }
+ // This is required as non-daemon threads can be started by class initializers
+ System.exit(0);
+ }
+}