CompileTheWorld.java revision 13304:5e9c41536bd2
1266692Sgshapiro/*
2132943Sgshapiro * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
3132943Sgshapiro * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4132943Sgshapiro *
5132943Sgshapiro * This code is free software; you can redistribute it and/or modify it
6132943Sgshapiro * under the terms of the GNU General Public License version 2 only, as
7132943Sgshapiro * published by the Free Software Foundation.
8132943Sgshapiro *
9132943Sgshapiro * This code is distributed in the hope that it will be useful, but WITHOUT
10132943Sgshapiro * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11132943Sgshapiro * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12173340Sgshapiro * version 2 for more details (a copy is included in the LICENSE file that
13285303Sgshapiro * accompanied this code).
14285303Sgshapiro *
15285303Sgshapiro * You should have received a copy of the GNU General Public License version
16285303Sgshapiro * 2 along with this work; if not, write to the Free Software Foundation,
17285303Sgshapiro * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18285303Sgshapiro *
19285303Sgshapiro * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20285303Sgshapiro * or visit www.oracle.com if you need additional information or have any
21285303Sgshapiro * questions.
22285303Sgshapiro */
23285303Sgshapiropackage org.graalvm.compiler.hotspot.test;
24285303Sgshapiro
25285303Sgshapiroimport static java.util.Collections.singletonList;
26285303Sgshapiroimport static org.graalvm.compiler.core.CompilationWrapper.ExceptionAction.Print;
27285303Sgshapiroimport static org.graalvm.compiler.core.GraalCompilerOptions.CompilationBailoutAction;
28285303Sgshapiroimport static org.graalvm.compiler.core.GraalCompilerOptions.CompilationFailureAction;
29285303Sgshapiroimport static org.graalvm.compiler.core.test.ReflectionOptionDescriptors.extractEntries;
30285303Sgshapiroimport static org.graalvm.compiler.debug.MemUseTrackerKey.getCurrentThreadAllocatedBytes;
31285303Sgshapiroimport static org.graalvm.compiler.hotspot.test.CompileTheWorld.Options.DESCRIPTORS;
32285303Sgshapiroimport static org.graalvm.compiler.serviceprovider.JDK9Method.Java8OrEarlier;
33285303Sgshapiro
34285303Sgshapiroimport java.io.Closeable;
35285303Sgshapiroimport java.io.File;
36285303Sgshapiroimport java.io.IOException;
37285303Sgshapiroimport java.lang.annotation.Annotation;
38285303Sgshapiroimport java.lang.reflect.Constructor;
39285303Sgshapiroimport java.lang.reflect.Method;
40285303Sgshapiroimport java.lang.reflect.Modifier;
41285303Sgshapiroimport java.net.URI;
42285303Sgshapiroimport java.net.URL;
43285303Sgshapiroimport java.net.URLClassLoader;
44285303Sgshapiroimport java.nio.file.FileSystem;
45285303Sgshapiroimport java.nio.file.FileSystems;
46285303Sgshapiroimport java.nio.file.FileVisitResult;
47285303Sgshapiroimport java.nio.file.Files;
48285303Sgshapiroimport java.nio.file.Path;
49285303Sgshapiroimport java.nio.file.SimpleFileVisitor;
50285303Sgshapiroimport java.nio.file.attribute.BasicFileAttributes;
51285303Sgshapiroimport java.util.ArrayList;
52285303Sgshapiroimport java.util.Arrays;
53285303Sgshapiroimport java.util.Collections;
54285303Sgshapiroimport java.util.Enumeration;
55285303Sgshapiroimport java.util.HashSet;
56285303Sgshapiroimport java.util.List;
57285303Sgshapiroimport java.util.ServiceLoader;
58285303Sgshapiroimport java.util.Set;
59285303Sgshapiroimport java.util.concurrent.ExecutionException;
60285303Sgshapiroimport java.util.concurrent.Future;
61285303Sgshapiroimport java.util.concurrent.LinkedBlockingQueue;
62285303Sgshapiroimport java.util.concurrent.ThreadPoolExecutor;
63285303Sgshapiroimport java.util.concurrent.TimeUnit;
64285303Sgshapiroimport java.util.concurrent.atomic.AtomicLong;
65285303Sgshapiroimport java.util.jar.JarEntry;
66285303Sgshapiroimport java.util.jar.JarFile;
67285303Sgshapiroimport java.util.stream.Collectors;
68285303Sgshapiro
69285303Sgshapiroimport org.graalvm.compiler.api.replacements.Snippet;
70285303Sgshapiroimport org.graalvm.compiler.bytecode.Bytecodes;
71285303Sgshapiroimport org.graalvm.compiler.core.CompilerThreadFactory;
72285303Sgshapiroimport org.graalvm.compiler.core.test.ReflectionOptionDescriptors;
73285303Sgshapiroimport org.graalvm.compiler.debug.DebugOptions;
74285303Sgshapiroimport org.graalvm.compiler.debug.GraalError;
75285303Sgshapiroimport org.graalvm.compiler.debug.MethodFilter;
76285303Sgshapiroimport org.graalvm.compiler.debug.TTY;
77285303Sgshapiroimport org.graalvm.compiler.hotspot.CompilationTask;
78285303Sgshapiroimport org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
79285303Sgshapiroimport org.graalvm.compiler.hotspot.HotSpotGraalCompiler;
80285303Sgshapiroimport org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
81285303Sgshapiroimport org.graalvm.compiler.options.OptionDescriptors;
82285303Sgshapiroimport org.graalvm.compiler.options.OptionKey;
83285303Sgshapiroimport org.graalvm.compiler.options.OptionValues;
84285303Sgshapiroimport org.graalvm.compiler.options.OptionsParser;
85285303Sgshapiroimport org.graalvm.compiler.serviceprovider.JDK9Method;
86285303Sgshapiroimport org.graalvm.util.EconomicMap;
87285303Sgshapiroimport org.graalvm.util.UnmodifiableEconomicMap;
88285303Sgshapiro
89285303Sgshapiroimport jdk.vm.ci.hotspot.HotSpotCodeCacheProvider;
90285303Sgshapiroimport jdk.vm.ci.hotspot.HotSpotCompilationRequest;
91285303Sgshapiroimport jdk.vm.ci.hotspot.HotSpotInstalledCode;
92285303Sgshapiroimport jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
93285303Sgshapiroimport jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider;
94285303Sgshapiroimport jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
95285303Sgshapiroimport jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
96285303Sgshapiroimport jdk.vm.ci.meta.ConstantPool;
97285303Sgshapiroimport jdk.vm.ci.meta.MetaAccessProvider;
98285303Sgshapiroimport jdk.vm.ci.runtime.JVMCI;
99285303Sgshapiroimport jdk.vm.ci.runtime.JVMCICompiler;
100285303Sgshapiro
101285303Sgshapiro/**
102285303Sgshapiro * This class implements compile-the-world functionality with JVMCI.
103285303Sgshapiro */
104285303Sgshapiropublic final class CompileTheWorld {
105285303Sgshapiro
106285303Sgshapiro    /**
107285303Sgshapiro     * Magic token to denote that JDK classes are to be compiled. If
108285303Sgshapiro     * {@link JDK9Method#Java8OrEarlier}, then the classes in {@code rt.jar} are compiled. Otherwise
109249729Sgshapiro     * the classes in the Java runtime image are compiled.
110249729Sgshapiro     */
111249729Sgshapiro    public static final String SUN_BOOT_CLASS_PATH = "sun.boot.class.path";
112132943Sgshapiro
113249729Sgshapiro    /**
114249729Sgshapiro     * Magic token to denote the classes in the Java runtime image (i.e. in the {@code jrt:/} file
115249729Sgshapiro     * system).
116132943Sgshapiro     */
117132943Sgshapiro    public static final String JRT_CLASS_PATH_ENTRY = "<jrt>";
118249729Sgshapiro
119249729Sgshapiro    /**
120249729Sgshapiro     * @param options a space separated set of option value settings with each option setting in a
121249729Sgshapiro     *            {@code -Dgraal.<name>=<value>} format but without the leading {@code -Dgraal.}.
122249729Sgshapiro     *            Ignored if null.
123249729Sgshapiro     */
124249729Sgshapiro    public static EconomicMap<OptionKey<?>, Object> parseOptions(String options) {
125249729Sgshapiro        if (options != null) {
126249729Sgshapiro            EconomicMap<String, String> optionSettings = EconomicMap.create();
127249729Sgshapiro            for (String optionSetting : options.split("\\s+|#")) {
128249729Sgshapiro                OptionsParser.parseOptionSettingTo(optionSetting, optionSettings);
129249729Sgshapiro            }
130249729Sgshapiro            EconomicMap<OptionKey<?>, Object> values = OptionValues.newOptionMap();
131249729Sgshapiro            ServiceLoader<OptionDescriptors> loader = ServiceLoader.load(OptionDescriptors.class, OptionDescriptors.class.getClassLoader());
132249729Sgshapiro            OptionsParser.parseOptions(optionSettings, values, loader);
133249729Sgshapiro            return values;
134249729Sgshapiro        }
135249729Sgshapiro        return EconomicMap.create();
136249729Sgshapiro    }
137249729Sgshapiro
138132943Sgshapiro    private final HotSpotJVMCIRuntimeProvider jvmciRuntime;
139132943Sgshapiro
140132943Sgshapiro    private final HotSpotGraalCompiler compiler;
141249729Sgshapiro
142132943Sgshapiro    /**
143249729Sgshapiro     * Class path denoting classes to compile.
144249729Sgshapiro     *
145249729Sgshapiro     * @see Options#Classpath
146132943Sgshapiro     */
147132943Sgshapiro    private final String inputClassPath;
148249729Sgshapiro
149132943Sgshapiro    /**
150249729Sgshapiro     * Class index to start compilation at.
151132943Sgshapiro     *
152249729Sgshapiro     * @see Options#StartAt
153249729Sgshapiro     */
154249729Sgshapiro    private final int startAt;
155249729Sgshapiro
156249729Sgshapiro    /**
157249729Sgshapiro     * Class index to stop compilation at.
158249729Sgshapiro     *
159249729Sgshapiro     * @see Options#StopAt
160249729Sgshapiro     */
161249729Sgshapiro    private final int stopAt;
162249729Sgshapiro
163249729Sgshapiro    /** Only compile methods matching one of the filters in this array if the array is non-null. */
164249729Sgshapiro    private final MethodFilter[] methodFilters;
165249729Sgshapiro
166249729Sgshapiro    /** Exclude methods matching one of the filters in this array if the array is non-null. */
167249729Sgshapiro    private final MethodFilter[] excludeMethodFilters;
168249729Sgshapiro
169132943Sgshapiro    // Counters
170249729Sgshapiro    private int classFileCounter = 0;
171132943Sgshapiro    private AtomicLong compiledMethodsCounter = new AtomicLong();
172132943Sgshapiro    private AtomicLong compileTime = new AtomicLong();
173249729Sgshapiro    private AtomicLong memoryUsed = new AtomicLong();
174249729Sgshapiro
175173340Sgshapiro    private boolean verbose;
176173340Sgshapiro
177249729Sgshapiro    /**
178249729Sgshapiro     * Signal that the threads should start compiling in multithreaded mode.
179249729Sgshapiro     */
180249729Sgshapiro    private boolean running;
181249729Sgshapiro
182249729Sgshapiro    private ThreadPoolExecutor threadPool;
183249729Sgshapiro
184249729Sgshapiro    private OptionValues currentOptions;
185249729Sgshapiro    private final UnmodifiableEconomicMap<OptionKey<?>, Object> compilationOptions;
186249729Sgshapiro
187173340Sgshapiro    /**
188249729Sgshapiro     * Creates a compile-the-world instance.
189249729Sgshapiro     *
190249729Sgshapiro     * @param files {@link File#pathSeparator} separated list of Zip/Jar files to compile
191249729Sgshapiro     * @param startAt index of the class file to start compilation at
192249729Sgshapiro     * @param stopAt index of the class file to stop compilation at
193249729Sgshapiro     * @param methodFilters
194249729Sgshapiro     * @param excludeMethodFilters
195249729Sgshapiro     */
196249729Sgshapiro    public CompileTheWorld(HotSpotJVMCIRuntimeProvider jvmciRuntime, HotSpotGraalCompiler compiler, String files, int startAt, int stopAt, String methodFilters, String excludeMethodFilters,
197249729Sgshapiro                    boolean verbose, OptionValues initialOptions, EconomicMap<OptionKey<?>, Object> compilationOptions) {
198132943Sgshapiro        this.jvmciRuntime = jvmciRuntime;
199        this.compiler = compiler;
200        this.inputClassPath = files;
201        this.startAt = startAt;
202        this.stopAt = stopAt;
203        this.methodFilters = methodFilters == null || methodFilters.isEmpty() ? null : MethodFilter.parse(methodFilters);
204        this.excludeMethodFilters = excludeMethodFilters == null || excludeMethodFilters.isEmpty() ? null : MethodFilter.parse(excludeMethodFilters);
205        this.verbose = verbose;
206        this.currentOptions = initialOptions;
207
208        // Copy the initial options and add in any extra options
209        EconomicMap<OptionKey<?>, Object> compilationOptionsCopy = EconomicMap.create(initialOptions.getMap());
210        compilationOptionsCopy.putAll(compilationOptions);
211
212        // We want to see stack traces when a method fails to compile
213        CompilationBailoutAction.putIfAbsent(compilationOptionsCopy, Print);
214        CompilationFailureAction.putIfAbsent(compilationOptionsCopy, Print);
215
216        // By default only report statistics for the CTW threads themselves
217        DebugOptions.MetricsThreadFilter.putIfAbsent(compilationOptionsCopy, "^CompileTheWorld");
218        this.compilationOptions = compilationOptionsCopy;
219    }
220
221    public CompileTheWorld(HotSpotJVMCIRuntimeProvider jvmciRuntime, HotSpotGraalCompiler compiler, OptionValues options) {
222        this(jvmciRuntime, compiler, Options.Classpath.getValue(options),
223                        Options.StartAt.getValue(options),
224                        Options.StopAt.getValue(options),
225                        Options.MethodFilter.getValue(options),
226                        Options.ExcludeMethodFilter.getValue(options),
227                        Options.Verbose.getValue(options),
228                        options,
229                        parseOptions(Options.Config.getValue(options)));
230    }
231
232    /**
233     * Compiles all methods in all classes in {@link #inputClassPath}. If {@link #inputClassPath}
234     * equals {@link #SUN_BOOT_CLASS_PATH} the boot classes are used.
235     */
236    public void compile() throws Throwable {
237        if (SUN_BOOT_CLASS_PATH.equals(inputClassPath)) {
238            String bcpEntry = null;
239            if (Java8OrEarlier) {
240                final String[] entries = System.getProperty(SUN_BOOT_CLASS_PATH).split(File.pathSeparator);
241                for (int i = 0; i < entries.length && bcpEntry == null; i++) {
242                    String entry = entries[i];
243                    File entryFile = new File(entry);
244                    if (entryFile.getName().endsWith("rt.jar") && entryFile.isFile()) {
245                        bcpEntry = entry;
246                    }
247                }
248                if (bcpEntry == null) {
249                    throw new GraalError("Could not find rt.jar on boot class path %s", System.getProperty(SUN_BOOT_CLASS_PATH));
250                }
251            } else {
252                bcpEntry = JRT_CLASS_PATH_ENTRY;
253            }
254            compile(bcpEntry);
255        } else {
256            compile(inputClassPath);
257        }
258    }
259
260    public void println() {
261        println("");
262    }
263
264    public void println(String format, Object... args) {
265        println(String.format(format, args));
266    }
267
268    public void println(String s) {
269        println(verbose, s);
270    }
271
272    public static void println(boolean cond, String s) {
273        if (cond) {
274            TTY.println(s);
275        }
276    }
277
278    public void printStackTrace(Throwable t) {
279        if (verbose) {
280            t.printStackTrace(TTY.out);
281        }
282    }
283
284    @SuppressWarnings("unused")
285    private static void dummy() {
286    }
287
288    /**
289     * Abstraction over different types of class path entries.
290     */
291    abstract static class ClassPathEntry implements Closeable {
292        final String name;
293
294        ClassPathEntry(String name) {
295            this.name = name;
296        }
297
298        /**
299         * Creates a {@link ClassLoader} for loading classes from this entry.
300         */
301        public abstract ClassLoader createClassLoader() throws IOException;
302
303        /**
304         * Gets the list of classes available under this entry.
305         */
306        public abstract List<String> getClassNames() throws IOException;
307
308        @Override
309        public String toString() {
310            return name;
311        }
312
313        @Override
314        public void close() throws IOException {
315        }
316    }
317
318    /**
319     * A class path entry that is a normal file system directory.
320     */
321    static class DirClassPathEntry extends ClassPathEntry {
322
323        private final File dir;
324
325        DirClassPathEntry(String name) {
326            super(name);
327            dir = new File(name);
328            assert dir.isDirectory();
329        }
330
331        @Override
332        public ClassLoader createClassLoader() throws IOException {
333            URL url = dir.toURI().toURL();
334            return new URLClassLoader(new URL[]{url});
335        }
336
337        @Override
338        public List<String> getClassNames() throws IOException {
339            List<String> classNames = new ArrayList<>();
340            String root = dir.getPath();
341            SimpleFileVisitor<Path> visitor = new SimpleFileVisitor<Path>() {
342                @Override
343                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
344                    if (attrs.isRegularFile()) {
345                        File path = file.toFile();
346                        if (path.getName().endsWith(".class")) {
347                            String pathString = path.getPath();
348                            assert pathString.startsWith(root);
349                            String classFile = pathString.substring(root.length() + 1);
350                            String className = classFile.replace(File.separatorChar, '.');
351                            classNames.add(className.replace('/', '.').substring(0, className.length() - ".class".length()));
352                        }
353                    }
354                    return super.visitFile(file, attrs);
355                }
356            };
357            Files.walkFileTree(dir.toPath(), visitor);
358            return classNames;
359        }
360    }
361
362    /**
363     * A class path entry that is a jar or zip file.
364     */
365    static class JarClassPathEntry extends ClassPathEntry {
366
367        private final JarFile jarFile;
368
369        JarClassPathEntry(String name) throws IOException {
370            super(name);
371            jarFile = new JarFile(name);
372        }
373
374        @Override
375        public ClassLoader createClassLoader() throws IOException {
376            URL url = new URL("jar", "", "file:" + name + "!/");
377            return new URLClassLoader(new URL[]{url});
378        }
379
380        @Override
381        public List<String> getClassNames() throws IOException {
382            Enumeration<JarEntry> e = jarFile.entries();
383            List<String> classNames = new ArrayList<>(jarFile.size());
384            while (e.hasMoreElements()) {
385                JarEntry je = e.nextElement();
386                if (je.isDirectory() || !je.getName().endsWith(".class")) {
387                    continue;
388                }
389                String className = je.getName().substring(0, je.getName().length() - ".class".length());
390                classNames.add(className.replace('/', '.'));
391            }
392            return classNames;
393        }
394
395        @Override
396        public void close() throws IOException {
397            jarFile.close();
398        }
399    }
400
401    /**
402     * A class path entry representing the {@code jrt:/} file system.
403     */
404    static class JRTClassPathEntry extends ClassPathEntry {
405
406        private final String limitModules;
407
408        JRTClassPathEntry(String name, String limitModules) {
409            super(name);
410            this.limitModules = limitModules;
411        }
412
413        @Override
414        public ClassLoader createClassLoader() throws IOException {
415            URL url = URI.create("jrt:/").toURL();
416            return new URLClassLoader(new URL[]{url});
417        }
418
419        @Override
420        public List<String> getClassNames() throws IOException {
421            Set<String> negative = new HashSet<>();
422            Set<String> positive = new HashSet<>();
423            if (limitModules != null && !limitModules.isEmpty()) {
424                for (String s : limitModules.split(",")) {
425                    if (s.startsWith("~")) {
426                        negative.add(s.substring(1));
427                    } else {
428                        positive.add(s);
429                    }
430                }
431            }
432            List<String> classNames = new ArrayList<>();
433            FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Collections.emptyMap());
434            Path top = fs.getPath("/modules/");
435            Files.find(top, Integer.MAX_VALUE,
436                            (path, attrs) -> attrs.isRegularFile()).forEach(p -> {
437                                int nameCount = p.getNameCount();
438                                if (nameCount > 2) {
439                                    String base = p.getName(nameCount - 1).toString();
440                                    if (base.endsWith(".class") && !base.equals("module-info.class")) {
441                                        String module = p.getName(1).toString();
442                                        if (positive.isEmpty() || positive.contains(module)) {
443                                            if (negative.isEmpty() || !negative.contains(module)) {
444                                                // Strip module prefix and convert to dotted form
445                                                String className = p.subpath(2, nameCount).toString().replace('/', '.');
446                                                // Strip ".class" suffix
447                                                className = className.replace('/', '.').substring(0, className.length() - ".class".length());
448                                                classNames.add(className);
449                                            }
450                                        }
451                                    }
452                                }
453                            });
454            return classNames;
455        }
456    }
457
458    private boolean isClassIncluded(String className) {
459        if (methodFilters != null && !MethodFilter.matchesClassName(methodFilters, className)) {
460            return false;
461        }
462        if (excludeMethodFilters != null && MethodFilter.matchesClassName(excludeMethodFilters, className)) {
463            return false;
464        }
465        return true;
466    }
467
468    /**
469     * Compiles all methods in all classes in a given class path.
470     *
471     * @param classPath class path denoting classes to compile
472     * @throws IOException
473     */
474    @SuppressWarnings("try")
475    private void compile(String classPath) throws IOException {
476        final String[] entries = classPath.split(File.pathSeparator);
477        long start = System.currentTimeMillis();
478
479        try {
480            // compile dummy method to get compiler initialized outside of the
481            // config debug override.
482            HotSpotResolvedJavaMethod dummyMethod = (HotSpotResolvedJavaMethod) JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess().lookupJavaMethod(
483                            CompileTheWorld.class.getDeclaredMethod("dummy"));
484            int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI;
485            boolean useProfilingInfo = false;
486            boolean installAsDefault = false;
487            CompilationTask task = new CompilationTask(jvmciRuntime, compiler, new HotSpotCompilationRequest(dummyMethod, entryBCI, 0L), useProfilingInfo, installAsDefault, currentOptions);
488            task.runCompilation();
489        } catch (NoSuchMethodException | SecurityException e1) {
490            printStackTrace(e1);
491        }
492
493        /*
494         * Always use a thread pool, even for single threaded mode since it simplifies the use of
495         * DebugValueThreadFilter to filter on the thread names.
496         */
497        int threadCount = 1;
498        if (Options.MultiThreaded.getValue(currentOptions)) {
499            threadCount = Options.Threads.getValue(currentOptions);
500            if (threadCount == 0) {
501                threadCount = Runtime.getRuntime().availableProcessors();
502            }
503        } else {
504            running = true;
505        }
506
507        OptionValues savedOptions = currentOptions;
508        currentOptions = new OptionValues(compilationOptions);
509        threadPool = new ThreadPoolExecutor(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new CompilerThreadFactory("CompileTheWorld"));
510
511        try {
512            for (int i = 0; i < entries.length; i++) {
513                final String entry = entries[i];
514
515                ClassPathEntry cpe;
516                if (entry.endsWith(".zip") || entry.endsWith(".jar")) {
517                    cpe = new JarClassPathEntry(entry);
518                } else if (entry.equals(JRT_CLASS_PATH_ENTRY)) {
519                    cpe = new JRTClassPathEntry(entry, Options.LimitModules.getValue(currentOptions));
520                } else {
521                    if (!new File(entry).isDirectory()) {
522                        println("CompileTheWorld : Skipped classes in " + entry);
523                        println();
524                        continue;
525                    }
526                    cpe = new DirClassPathEntry(entry);
527                }
528
529                if (methodFilters == null || methodFilters.length == 0) {
530                    println("CompileTheWorld : Compiling all classes in " + entry);
531                } else {
532                    String include = Arrays.asList(methodFilters).stream().map(MethodFilter::toString).collect(Collectors.joining(", "));
533                    println("CompileTheWorld : Compiling all methods in " + entry + " matching one of the following filters: " + include);
534                }
535                if (excludeMethodFilters != null && excludeMethodFilters.length > 0) {
536                    String exclude = Arrays.asList(excludeMethodFilters).stream().map(MethodFilter::toString).collect(Collectors.joining(", "));
537                    println("CompileTheWorld : Excluding all methods matching one of the following filters: " + exclude);
538                }
539                println();
540
541                ClassLoader loader = cpe.createClassLoader();
542
543                for (String className : cpe.getClassNames()) {
544
545                    // Are we done?
546                    if (classFileCounter >= stopAt) {
547                        break;
548                    }
549
550                    classFileCounter++;
551
552                    if (className.startsWith("jdk.management.") || className.startsWith("jdk.internal.cmm.*")) {
553                        continue;
554                    }
555
556                    try {
557                        // Load and initialize class
558                        Class<?> javaClass = Class.forName(className, true, loader);
559
560                        // Pre-load all classes in the constant pool.
561                        try {
562                            HotSpotResolvedObjectType objectType = HotSpotResolvedObjectType.fromObjectClass(javaClass);
563                            ConstantPool constantPool = objectType.getConstantPool();
564                            for (int cpi = 1; cpi < constantPool.length(); cpi++) {
565                                constantPool.loadReferencedType(cpi, Bytecodes.LDC);
566                            }
567                        } catch (Throwable t) {
568                            // If something went wrong during pre-loading we just ignore it.
569                            if (isClassIncluded(className)) {
570                                println("Preloading failed for (%d) %s: %s", classFileCounter, className, t);
571                            }
572                            continue;
573                        }
574
575                        /*
576                         * Only check filters after class loading and resolution to mitigate impact
577                         * on reproducibility.
578                         */
579                        if (!isClassIncluded(className)) {
580                            continue;
581                        }
582
583                        // Are we compiling this class?
584                        MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess();
585                        if (classFileCounter >= startAt) {
586                            println("CompileTheWorld (%d) : %s", classFileCounter, className);
587
588                            // Compile each constructor/method in the class.
589                            for (Constructor<?> constructor : javaClass.getDeclaredConstructors()) {
590                                HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(constructor);
591                                if (canBeCompiled(javaMethod, constructor.getModifiers())) {
592                                    compileMethod(javaMethod);
593                                }
594                            }
595                            for (Method method : javaClass.getDeclaredMethods()) {
596                                HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(method);
597                                if (canBeCompiled(javaMethod, method.getModifiers())) {
598                                    compileMethod(javaMethod);
599                                }
600                            }
601
602                            // Also compile the class initializer if it exists
603                            HotSpotResolvedJavaMethod clinit = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaType(javaClass).getClassInitializer();
604                            if (clinit != null && canBeCompiled(clinit, clinit.getModifiers())) {
605                                compileMethod(clinit);
606                            }
607                        }
608                    } catch (Throwable t) {
609                        if (isClassIncluded(className)) {
610                            println("CompileTheWorld (%d) : Skipping %s %s", classFileCounter, className, t.toString());
611                            printStackTrace(t);
612                        }
613                    }
614                }
615                cpe.close();
616            }
617        } finally {
618            currentOptions = savedOptions;
619        }
620
621        if (!running) {
622            startThreads();
623        }
624        int wakeups = 0;
625        while (threadPool.getCompletedTaskCount() != threadPool.getTaskCount()) {
626            if (wakeups % 15 == 0) {
627                TTY.println("CompileTheWorld : Waiting for " + (threadPool.getTaskCount() - threadPool.getCompletedTaskCount()) + " compiles");
628            }
629            try {
630                threadPool.awaitTermination(1, TimeUnit.SECONDS);
631                wakeups++;
632            } catch (InterruptedException e) {
633            }
634        }
635        threadPool = null;
636
637        long elapsedTime = System.currentTimeMillis() - start;
638
639        println();
640        if (Options.MultiThreaded.getValue(currentOptions)) {
641            TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms elapsed, %d ms compile time, %d bytes of memory used)", classFileCounter, compiledMethodsCounter.get(), elapsedTime,
642                            compileTime.get(), memoryUsed.get());
643        } else {
644            TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms, %d bytes of memory used)", classFileCounter, compiledMethodsCounter.get(), compileTime.get(), memoryUsed.get());
645        }
646    }
647
648    private synchronized void startThreads() {
649        running = true;
650        // Wake up any waiting threads
651        notifyAll();
652    }
653
654    private synchronized void waitToRun() {
655        while (!running) {
656            try {
657                wait();
658            } catch (InterruptedException e) {
659            }
660        }
661    }
662
663    @SuppressWarnings("try")
664    private void compileMethod(HotSpotResolvedJavaMethod method) throws InterruptedException, ExecutionException {
665        if (methodFilters != null && !MethodFilter.matches(methodFilters, method)) {
666            return;
667        }
668        if (excludeMethodFilters != null && MethodFilter.matches(excludeMethodFilters, method)) {
669            return;
670        }
671        Future<?> task = threadPool.submit(new Runnable() {
672            @Override
673            public void run() {
674                waitToRun();
675                OptionValues savedOptions = currentOptions;
676                currentOptions = new OptionValues(compilationOptions);
677                try {
678                    compileMethod(method, classFileCounter);
679                } finally {
680                    currentOptions = savedOptions;
681                }
682            }
683        });
684        if (threadPool.getCorePoolSize() == 1) {
685            task.get();
686        }
687    }
688
689    /**
690     * Compiles a method and gathers some statistics.
691     */
692    private void compileMethod(HotSpotResolvedJavaMethod method, int counter) {
693        try {
694            long start = System.currentTimeMillis();
695            long allocatedAtStart = getCurrentThreadAllocatedBytes();
696            int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI;
697            HotSpotCompilationRequest request = new HotSpotCompilationRequest(method, entryBCI, 0L);
698            // For more stable CTW execution, disable use of profiling information
699            boolean useProfilingInfo = false;
700            boolean installAsDefault = false;
701            CompilationTask task = new CompilationTask(jvmciRuntime, compiler, request, useProfilingInfo, installAsDefault, currentOptions);
702            task.runCompilation();
703
704            // Invalidate the generated code so the code cache doesn't fill up
705            HotSpotInstalledCode installedCode = task.getInstalledCode();
706            if (installedCode != null) {
707                installedCode.invalidate();
708            }
709
710            memoryUsed.getAndAdd(getCurrentThreadAllocatedBytes() - allocatedAtStart);
711            compileTime.getAndAdd(System.currentTimeMillis() - start);
712            compiledMethodsCounter.incrementAndGet();
713        } catch (Throwable t) {
714            // Catch everything and print a message
715            println("CompileTheWorld (%d) : Error compiling method: %s", counter, method.format("%H.%n(%p):%r"));
716            printStackTrace(t);
717        }
718    }
719
720    /**
721     * Determines if a method should be compiled (Cf. CompilationPolicy::can_be_compiled).
722     *
723     * @return true if it can be compiled, false otherwise
724     */
725    private boolean canBeCompiled(HotSpotResolvedJavaMethod javaMethod, int modifiers) {
726        if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) {
727            return false;
728        }
729        GraalHotSpotVMConfig c = compiler.getGraalRuntime().getVMConfig();
730        if (c.dontCompileHugeMethods && javaMethod.getCodeSize() > c.hugeMethodLimit) {
731            println(verbose || methodFilters != null,
732                            String.format("CompileTheWorld (%d) : Skipping huge method %s (use -XX:-DontCompileHugeMethods or -XX:HugeMethodLimit=%d to include it)", classFileCounter,
733                                            javaMethod.format("%H.%n(%p):%r"),
734                                            javaMethod.getCodeSize()));
735            return false;
736        }
737        // Allow use of -XX:CompileCommand=dontinline to exclude problematic methods
738        if (!javaMethod.canBeInlined()) {
739            return false;
740        }
741        // Skip @Snippets for now
742        for (Annotation annotation : javaMethod.getAnnotations()) {
743            if (annotation.annotationType().equals(Snippet.class)) {
744                return false;
745            }
746        }
747        return true;
748    }
749
750    static class Options {
751        // @formatter:off
752        public static final OptionKey<Boolean> Help = new OptionKey<>(false);
753        public static final OptionKey<String> Classpath = new OptionKey<>(CompileTheWorld.SUN_BOOT_CLASS_PATH);
754        public static final OptionKey<Boolean> Verbose = new OptionKey<>(true);
755        /**
756         * Ignore Graal classes by default to avoid problems associated with compiling
757         * snippets and method substitutions.
758         */
759        public static final OptionKey<String> LimitModules = new OptionKey<>("~jdk.internal.vm.compiler");
760        public static final OptionKey<Integer> Iterations = new OptionKey<>(1);
761        public static final OptionKey<String> MethodFilter = new OptionKey<>(null);
762        public static final OptionKey<String> ExcludeMethodFilter = new OptionKey<>(null);
763        public static final OptionKey<Integer> StartAt = new OptionKey<>(1);
764        public static final OptionKey<Integer> StopAt = new OptionKey<>(Integer.MAX_VALUE);
765        public static final OptionKey<String> Config = new OptionKey<>(null);
766        public static final OptionKey<Boolean> MultiThreaded = new OptionKey<>(false);
767        public static final OptionKey<Integer> Threads = new OptionKey<>(0);
768
769        static final ReflectionOptionDescriptors DESCRIPTORS = new ReflectionOptionDescriptors(Options.class,
770                           "Help", "List options and their help messages and then exit.",
771                      "Classpath", "Class path denoting methods to compile. Default is to compile boot classes.",
772                        "Verbose", "Verbose operation.",
773                   "LimitModules", "Comma separated list of module names to which compilation should be limited. " +
774                                   "Module names can be prefixed with \"~\" to exclude the named module.",
775                     "Iterations", "The number of iterations to perform.",
776                   "MethodFilter", "Only compile methods matching this filter.",
777            "ExcludeMethodFilter", "Exclude methods matching this filter from compilation.",
778                        "StartAt", "First class to consider for compilation.",
779                         "StopAt", "Last class to consider for compilation.",
780                         "Config", "Option value overrides to use during compile the world. For example, " +
781                                   "to disable inlining and partial escape analysis specify 'PartialEscapeAnalysis=false Inline=false'. " +
782                                   "The format for each option is the same as on the command line just without the '-Dgraal.' prefix.",
783                  "MultiThreaded", "Run using multiple threads for compilation.",
784                        "Threads", "Number of threads to use for multithreaded execution. Defaults to Runtime.getRuntime().availableProcessors().");
785        // @formatter:on
786    }
787
788    public static OptionValues loadOptions(OptionValues initialValues) {
789        EconomicMap<OptionKey<?>, Object> values = OptionValues.newOptionMap();
790        List<OptionDescriptors> loader = singletonList(DESCRIPTORS);
791        OptionsParser.parseOptions(extractEntries(System.getProperties(), "CompileTheWorld.", true), values, loader);
792        OptionValues options = new OptionValues(initialValues, values);
793        if (Options.Help.getValue(options)) {
794            options.printHelp(loader, System.out, "CompileTheWorld.");
795            System.exit(0);
796        }
797        return options;
798    }
799
800    public static void main(String[] args) throws Throwable {
801        HotSpotJVMCIRuntime jvmciRuntime = HotSpotJVMCIRuntime.runtime();
802        HotSpotGraalCompiler compiler = (HotSpotGraalCompiler) jvmciRuntime.getCompiler();
803        HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime();
804        HotSpotCodeCacheProvider codeCache = graalRuntime.getHostProviders().getCodeCache();
805        OptionValues options = loadOptions(graalRuntime.getOptions());
806
807        int iterations = Options.Iterations.getValue(options);
808        for (int i = 0; i < iterations; i++) {
809            codeCache.resetCompilationStatistics();
810            TTY.println("CompileTheWorld : iteration " + i);
811
812            CompileTheWorld ctw = new CompileTheWorld(jvmciRuntime, compiler, options);
813            ctw.compile();
814        }
815        // This is required as non-daemon threads can be started by class initializers
816        System.exit(0);
817    }
818}
819