9108
|
1 /*
|
|
2 * Copyright (c) 2013, 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 com.oracle.graal.hotspot;
|
|
24
|
|
25 import java.io.File;
|
|
26 import java.lang.reflect.Constructor;
|
|
27 import java.lang.reflect.Method;
|
|
28 import java.lang.reflect.Modifier;
|
|
29 import java.net.*;
|
|
30 import java.util.Enumeration;
|
|
31 import java.util.jar.*;
|
|
32
|
|
33 import com.oracle.graal.api.meta.*;
|
|
34 import com.oracle.graal.bytecode.Bytecodes;
|
|
35 import com.oracle.graal.debug.*;
|
|
36 import com.oracle.graal.hotspot.bridge.*;
|
|
37 import com.oracle.graal.hotspot.meta.*;
|
|
38 import com.oracle.graal.nodes.*;
|
|
39 import com.oracle.graal.phases.*;
|
|
40 import com.oracle.graal.replacements.*;
|
|
41
|
|
42 /**
|
|
43 * This class implements compile-the-world functionality in Graal.
|
|
44 */
|
|
45 public final class CompileTheWorld {
|
|
46
|
|
47 /**
|
|
48 * This is our magic token to trigger reading files from the boot class path.
|
|
49 */
|
|
50 public static final String SUN_BOOT_CLASS_PATH = "sun.boot.class.path";
|
|
51
|
|
52 // Some runtime instances we need.
|
|
53 private final HotSpotGraalRuntime graalRuntime = HotSpotGraalRuntime.getInstance();
|
|
54 private final VMToCompilerImpl vmToCompiler = (VMToCompilerImpl) graalRuntime.getVMToCompiler();
|
|
55
|
|
56 /** List of Zip/Jar files to compile (see {@link GraalOptions#CompileTheWorld}. */
|
|
57 private final String files;
|
|
58
|
|
59 /** Class index to start compilation at (see {@link GraalOptions#CompileTheWorldStartAt}. */
|
|
60 private final int startAt;
|
|
61
|
|
62 /** Class index to stop compilation at (see {@link GraalOptions#CompileTheWorldStopAt}. */
|
|
63 private final int stopAt;
|
|
64
|
|
65 // Counters
|
|
66 private int classFileCounter = 0;
|
|
67 private int compiledMethodsCounter = 0;
|
|
68 private long compileTime = 0;
|
|
69
|
|
70 /**
|
|
71 * Create a compile-the-world instance with default values from
|
|
72 * {@link GraalOptions#CompileTheWorld}, {@link GraalOptions#CompileTheWorldStartAt} and
|
|
73 * {@link GraalOptions#CompileTheWorldStopAt}.
|
|
74 */
|
|
75 public CompileTheWorld() {
|
|
76 this(GraalOptions.CompileTheWorld, GraalOptions.CompileTheWorldStartAt, GraalOptions.CompileTheWorldStopAt);
|
|
77 }
|
|
78
|
|
79 /**
|
|
80 * Create a compile-the-world instance.
|
|
81 *
|
|
82 * @param files {@link File#pathSeparator} separated list of Zip/Jar files to compile
|
|
83 * @param startAt index of the class file to start compilation at
|
|
84 * @param stopAt index of the class file to stop compilation at
|
|
85 */
|
|
86 public CompileTheWorld(String files, int startAt, int stopAt) {
|
|
87 this.files = files;
|
|
88 this.startAt = startAt;
|
|
89 this.stopAt = stopAt;
|
|
90
|
|
91 // We don't want the VM to exit when a method fails to compile.
|
|
92 GraalOptions.ExitVMOnException = false;
|
|
93 }
|
|
94
|
|
95 /**
|
|
96 * Compile all methods in all classes in the Zip/Jar files in
|
|
97 * {@link GraalOptions#CompileTheWorld}. If the GraalOptions.CompileTheWorld contains the magic
|
|
98 * token {@link CompileTheWorld#SUN_BOOT_CLASS_PATH} passed up from HotSpot we take the files
|
|
99 * from the boot class path.
|
|
100 *
|
|
101 * @throws Throwable
|
|
102 */
|
|
103 public void compile() throws Throwable {
|
|
104 if (SUN_BOOT_CLASS_PATH.equals(files)) {
|
|
105 final String[] entries = System.getProperty(SUN_BOOT_CLASS_PATH).split(File.pathSeparator);
|
|
106 String bcpFiles = "";
|
|
107 for (int i = 0; i < entries.length; i++) {
|
|
108 final String entry = entries[i];
|
|
109
|
|
110 // We stop at rt.jar, unless it is the first boot class path entry.
|
|
111 if (entry.endsWith("rt.jar") && (i > 0)) {
|
|
112 break;
|
|
113 }
|
|
114 if (i > 0) {
|
|
115 bcpFiles += File.pathSeparator;
|
|
116 }
|
|
117 bcpFiles += entry;
|
|
118 }
|
|
119 compile(bcpFiles);
|
|
120 } else {
|
|
121 compile(files);
|
|
122 }
|
|
123 }
|
|
124
|
|
125 /**
|
|
126 * Compile all methods in all classes in the Zip/Jar files passed.
|
|
127 *
|
|
128 * @param fileList {@link File#pathSeparator} separated list of Zip/Jar files to compile
|
|
129 * @throws Throwable
|
|
130 */
|
|
131 private void compile(String fileList) throws Throwable {
|
|
132 final String[] entries = fileList.split(File.pathSeparator);
|
|
133
|
|
134 for (int i = 0; i < entries.length; i++) {
|
|
135 final String entry = entries[i];
|
|
136
|
|
137 // For now we only compile all methods in all classes in zip/jar files.
|
|
138 if (!entry.endsWith(".zip") && !entry.endsWith(".jar")) {
|
|
139 TTY.println("CompileTheWorld : Skipped classes in " + entry);
|
|
140 TTY.println();
|
|
141 continue;
|
|
142 }
|
|
143
|
|
144 TTY.println("CompileTheWorld : Compiling all classes in " + entry);
|
|
145 TTY.println();
|
|
146
|
|
147 URL url = new URL("jar", "", "file:" + entry + "!/");
|
|
148 ClassLoader loader = new URLClassLoader(new URL[]{url});
|
|
149
|
|
150 JarFile jarFile = new JarFile(entry);
|
|
151 Enumeration<JarEntry> e = jarFile.entries();
|
|
152
|
|
153 while (e.hasMoreElements()) {
|
|
154 JarEntry je = e.nextElement();
|
|
155 if (je.isDirectory() || !je.getName().endsWith(".class")) {
|
|
156 continue;
|
|
157 }
|
|
158
|
|
159 // Are we done?
|
|
160 if (classFileCounter >= stopAt) {
|
|
161 break;
|
|
162 }
|
|
163
|
|
164 String className = je.getName().substring(0, je.getName().length() - ".class".length());
|
|
165 className = className.replace('/', '.');
|
|
166 classFileCounter++;
|
|
167
|
|
168 try {
|
|
169 // Load and initialize class
|
|
170 Class<?> javaClass = Class.forName(className, true, loader);
|
|
171
|
|
172 // Pre-load all classes in the constant pool.
|
|
173 try {
|
|
174 HotSpotResolvedObjectType objectType = (HotSpotResolvedObjectType) HotSpotResolvedObjectType.fromClass(javaClass);
|
|
175 ConstantPool constantPool = objectType.constantPool();
|
|
176 for (int cpi = 1; cpi < constantPool.length(); cpi++) {
|
|
177 constantPool.loadReferencedType(cpi, Bytecodes.LDC);
|
|
178 }
|
|
179 } catch (Throwable t) {
|
|
180 // If something went wrong during pre-loading we just ignore it.
|
|
181 TTY.println("CompileTheWorld (%d) : Preloading failed for %s", classFileCounter, className);
|
|
182 }
|
|
183
|
|
184 // Are we compiling this class?
|
|
185 if (classFileCounter >= startAt) {
|
|
186 TTY.println("CompileTheWorld (%d) : %s", classFileCounter, className);
|
|
187
|
|
188 // Enqueue each constructor/method in the class for compilation.
|
|
189 for (Constructor<?> constructor : javaClass.getDeclaredConstructors()) {
|
|
190 HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) graalRuntime.getRuntime().lookupJavaConstructor(constructor);
|
|
191 if (canBeCompiled(javaMethod, constructor.getModifiers())) {
|
|
192 compileMethod(javaMethod);
|
|
193 }
|
|
194 }
|
|
195 for (Method method : javaClass.getDeclaredMethods()) {
|
|
196 HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) graalRuntime.getRuntime().lookupJavaMethod(method);
|
|
197 if (canBeCompiled(javaMethod, method.getModifiers())) {
|
|
198 compileMethod(javaMethod);
|
|
199 }
|
|
200 }
|
|
201 }
|
|
202 } catch (Throwable t) {
|
|
203 TTY.println("CompileTheWorld (%d) : Skipping %s", classFileCounter, className);
|
|
204 }
|
|
205 }
|
|
206 jarFile.close();
|
|
207 }
|
|
208
|
|
209 TTY.println();
|
|
210 TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms)", classFileCounter, compiledMethodsCounter, compileTime);
|
|
211 }
|
|
212
|
|
213 /**
|
|
214 * Helper method to schedule a method for compilation and gather some statistics.
|
|
215 */
|
|
216 private void compileMethod(HotSpotResolvedJavaMethod method) {
|
|
217 try {
|
|
218 long start = System.currentTimeMillis();
|
|
219 vmToCompiler.compileMethod(method, StructuredGraph.INVOCATION_ENTRY_BCI, true, 10);
|
|
220 compileTime += (System.currentTimeMillis() - start);
|
|
221 compiledMethodsCounter++;
|
|
222 method.reprofile(); // makes the method also not-entrant
|
|
223 } catch (Throwable t) {
|
|
224 // Catch everything and print a message
|
|
225 TTY.println("CompileTheWorld (%d) : Error compiling method: %s", classFileCounter, MetaUtil.format("%H.%n(%p):%r", method));
|
|
226 t.printStackTrace(TTY.cachedOut);
|
|
227 }
|
|
228 }
|
|
229
|
|
230 /**
|
|
231 * Helper method for CompileTheWorld to determine if a method should be compiled (Cf.
|
|
232 * CompilationPolicy::can_be_compiled).
|
|
233 *
|
|
234 * @return true if it can be compiled, false otherwise
|
|
235 */
|
|
236 private static boolean canBeCompiled(HotSpotResolvedJavaMethod javaMethod, int modifiers) {
|
|
237 if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) {
|
|
238 return false;
|
|
239 }
|
|
240 // This number is from HotSpot:
|
|
241 final int hugeMethodLimit = 8000;
|
|
242 if (javaMethod.getCodeSize() > hugeMethodLimit) {
|
|
243 return false;
|
|
244 }
|
|
245 // Skip @Snippets for now
|
|
246 if (javaMethod.getAnnotation(Snippet.class) != null) {
|
|
247 return false;
|
|
248 }
|
|
249 return true;
|
|
250 }
|
|
251
|
|
252 }
|