--- /dev/null 2013-04-12 12:30:24.000000000 -0700 +++ new/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java 2013-04-12 12:30:22.000000000 -0700 @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2013, 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 com.oracle.graal.hotspot; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.*; +import java.util.Enumeration; +import java.util.jar.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.bytecode.Bytecodes; +import com.oracle.graal.debug.*; +import com.oracle.graal.hotspot.bridge.*; +import com.oracle.graal.hotspot.meta.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.phases.*; +import com.oracle.graal.replacements.*; + +/** + * This class implements compile-the-world functionality in Graal. + */ +public final class CompileTheWorld { + + /** + * This is our magic token to trigger reading files from the boot class path. + */ + public final static String SUN_BOOT_CLASS_PATH = "sun.boot.class.path"; + + // Some runtime instances we need. + private final HotSpotGraalRuntime graalRuntime = HotSpotGraalRuntime.getInstance(); + private final VMToCompilerImpl vmToCompiler = (VMToCompilerImpl) graalRuntime.getVMToCompiler(); + + /** List of Zip/Jar files to compile (see {@link GraalOptions.CompileTheWorld} */ + private final String files; + + /** Class index to start compilation at (see {@link GraalOptions.CompileTheWorldStartAt} */ + private final int startAt; + + /** Class index to stop compilation at (see {@link GraalOptions.CompileTheWorldStopAt} */ + private final int stopAt; + + // Counters + private int classFileCounter = 0; + private int compiledMethodsCounter = 0; + private long compileTime = 0; + + /** + * Create a compile-the-world instance with default values from + * {@link GraalOptions.CompileTheWorld}, {@link GraalOptions.CompileTheWorldStartAt} and + * {@link GraalOptions.CompileTheWorldStopAt}. + */ + public CompileTheWorld() { + this(GraalOptions.CompileTheWorld, GraalOptions.CompileTheWorldStartAt, GraalOptions.CompileTheWorldStopAt); + } + + /** + * Create a compile-the-world instance. + * + * @param files {@link File.pathSeparator} separated list of Zip/Jar files + * @param startAt index of the class file to start compilation at + * @param stopAt index of the class file to stop compilation at + */ + public CompileTheWorld(String files, int startAt, int stopAt) { + this.files = files; + this.startAt = startAt; + this.stopAt = stopAt; + + // We don't want the VM to exit when a method fails to compile. + GraalOptions.ExitVMOnException = false; + } + + /** + * Compile all methods in all classes in the Zip/Jar files in + * {@link GraalOptions.CompileTheWorld}. If the GraalOptions.CompileTheWorld contains the magic + * token {@link SUN_BOOT_CLASS_PATH} passed up from HotSpot we take the files from the boot + * class path. + * + * @throws Throwable + */ + public void compile() throws Throwable { + if (SUN_BOOT_CLASS_PATH.equals(files)) { + final String[] entries = System.getProperty(SUN_BOOT_CLASS_PATH).split(File.pathSeparator); + String files = ""; + for (int i = 0; i < entries.length; i++) { + final String entry = entries[i]; + + // We stop at rt.jar, unless it is the first boot class path entry. + if (entry.endsWith("rt.jar") && (i > 0)) { + break; + } + if (i > 0) { + files += File.pathSeparator; + } + files += entry; + } + compile(files); + } else { + compile(files); + } + } + + /** + * Compile all methods in all classes in the Zip/Jar files passed in files. + * + * @param files {@link File.pathSeparator} separated list of Zip/Jar files + * @throws Throwable + */ + private void compile(String files) throws Throwable { + final String[] entries = files.split(File.pathSeparator); + + for (int i = 0; i < entries.length; i++) { + final String entry = entries[i]; + + // For now we only compile all methods in all classes in zip/jar files. + if (!entry.endsWith(".zip") && !entry.endsWith(".jar")) { + TTY.println("CompileTheWorld : Skipped classes in " + entry); + TTY.println(); + continue; + } + + TTY.println("CompileTheWorld : Compiling all classes in " + entry); + TTY.println(); + + URL url = new URL("jar", "", "file:" + entry + "!/"); + ClassLoader loader = new URLClassLoader(new URL[]{url}); + + JarFile jarFile = new JarFile(entry); + Enumeration e = jarFile.entries(); + + while (e.hasMoreElements()) { + JarEntry je = e.nextElement(); + if (je.isDirectory() || !je.getName().endsWith(".class")) { + continue; + } + + // Are we done? + if (classFileCounter >= stopAt) { + break; + } + + String className = je.getName().substring(0, je.getName().length() - ".class".length()); + className = className.replace('/', '.'); + classFileCounter++; + + try { + // Load and initialize class + Class javaClass = Class.forName(className, true, loader); + + // Pre-load all classes in the constant pool. + try { + HotSpotResolvedObjectType objectType = (HotSpotResolvedObjectType) HotSpotResolvedObjectType.fromClass(javaClass); + ConstantPool constantPool = objectType.constantPool(); + 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. + TTY.println("CompileTheWorld (%d) : Preloading failed for %s", classFileCounter, className); + } + + // Are we compiling this class? + if (classFileCounter >= startAt) { + TTY.println("CompileTheWorld (%d) : %s", classFileCounter, className); + + // Enqueue each constructor/method in the class for compilation. + for (Constructor constructor : javaClass.getDeclaredConstructors()) { + HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) graalRuntime.getRuntime().lookupJavaConstructor(constructor); + if (canBeCompiled(javaMethod, constructor.getModifiers())) { + compileMethod(javaMethod); + } + } + for (Method method : javaClass.getDeclaredMethods()) { + HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) graalRuntime.getRuntime().lookupJavaMethod(method); + if (canBeCompiled(javaMethod, method.getModifiers())) { + compileMethod(javaMethod); + } + } + } + } catch (Throwable t) { + TTY.println("CompileTheWorld (%d) : Skipping %s", classFileCounter, className); + } + } + jarFile.close(); + } + + TTY.println(); + TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms)", classFileCounter, compiledMethodsCounter, compileTime); + } + + /** + * Helper method to schedule a method for compilation and gather some statistics. + */ + private void compileMethod(HotSpotResolvedJavaMethod method) { + try { + long start = System.currentTimeMillis(); + vmToCompiler.compileMethod(method, StructuredGraph.INVOCATION_ENTRY_BCI, true, 10); + compileTime += (System.currentTimeMillis() - start); + compiledMethodsCounter++; + method.reprofile(); // makes the method also not-entrant + } catch (Throwable t) { + // Catch everything and print a message + TTY.println("CompileTheWorld (%d) : Error compiling method: %s", classFileCounter, MetaUtil.format("%H.%n(%p):%r", method)); + t.printStackTrace(TTY.cachedOut); + } + } + + /** + * Helper method for CompileTheWorld to determine if a method should be compiled (Cf. + * CompilationPolicy::can_be_compiled). + * + * @return true if it can be compiled, false otherwise + */ + private static boolean canBeCompiled(HotSpotResolvedJavaMethod javaMethod, int modifiers) { + if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) { + return false; + } + // This number is from HotSpot: + final int HugeMethodLimit = 8000; + if (javaMethod.getCodeSize() > HugeMethodLimit) { + return false; + } + // Skip @Snippets for now + if (javaMethod.getAnnotation(Snippet.class) != null) { + return false; + } + return true; + } + +}