Mercurial > hg > truffle
comparison jvmci/com.oracle.jvmci.hotspot/src/com/oracle/jvmci/hotspot/CompileTheWorld.java @ 21798:395ac43a8578
moved JVMCI sources from graal/ to jvmci/ directory
author | Doug Simon <doug.simon@oracle.com> |
---|---|
date | Tue, 09 Jun 2015 00:22:49 +0200 |
parents | graal/com.oracle.jvmci.hotspot/src/com/oracle/jvmci/hotspot/CompileTheWorld.java@5b9adb645217 |
children | 90e3fecd4143 |
comparison
equal
deleted
inserted
replaced
21797:42452d2dfbec | 21798:395ac43a8578 |
---|---|
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.jvmci.hotspot; | |
24 | |
25 import static com.oracle.jvmci.compiler.Compiler.*; | |
26 import static com.oracle.jvmci.debug.internal.MemUseTrackerImpl.*; | |
27 | |
28 import java.io.*; | |
29 import java.lang.annotation.*; | |
30 import java.lang.reflect.*; | |
31 import java.net.*; | |
32 import java.util.*; | |
33 import java.util.concurrent.*; | |
34 import java.util.concurrent.atomic.*; | |
35 import java.util.jar.*; | |
36 import java.util.stream.*; | |
37 | |
38 import com.oracle.jvmci.compiler.*; | |
39 import com.oracle.jvmci.compiler.CompilerThreadFactory.DebugConfigAccess; | |
40 import com.oracle.jvmci.compiler.Compiler; | |
41 import com.oracle.jvmci.debug.*; | |
42 import com.oracle.jvmci.debug.internal.*; | |
43 import com.oracle.jvmci.meta.*; | |
44 import com.oracle.jvmci.options.*; | |
45 import com.oracle.jvmci.options.OptionUtils.OptionConsumer; | |
46 import com.oracle.jvmci.options.OptionValue.OverrideScope; | |
47 import com.oracle.jvmci.runtime.*; | |
48 | |
49 /** | |
50 * This class implements compile-the-world functionality with JVMCI. | |
51 */ | |
52 public final class CompileTheWorld { | |
53 | |
54 /** | |
55 * Magic token to trigger reading files from the boot class path. | |
56 */ | |
57 public static final String SUN_BOOT_CLASS_PATH = "sun.boot.class.path"; | |
58 | |
59 public static class Options { | |
60 // @formatter:off | |
61 @Option(help = "Compile all methods in all classes on given class path", type = OptionType.Debug) | |
62 public static final OptionValue<String> CompileTheWorldClasspath = new OptionValue<>(SUN_BOOT_CLASS_PATH); | |
63 @Option(help = "Verbose CompileTheWorld operation", type = OptionType.Debug) | |
64 public static final OptionValue<Boolean> CompileTheWorldVerbose = new OptionValue<>(true); | |
65 @Option(help = "The number of CompileTheWorld iterations to perform", type = OptionType.Debug) | |
66 public static final OptionValue<Integer> CompileTheWorldIterations = new OptionValue<>(1); | |
67 @Option(help = "Only compile methods matching this filter", type = OptionType.Debug) | |
68 public static final OptionValue<String> CompileTheWorldMethodFilter = new OptionValue<>(null); | |
69 @Option(help = "Exclude methods matching this filter from compilation", type = OptionType.Debug) | |
70 public static final OptionValue<String> CompileTheWorldExcludeMethodFilter = new OptionValue<>(null); | |
71 @Option(help = "First class to consider when using -XX:+CompileTheWorld", type = OptionType.Debug) | |
72 public static final OptionValue<Integer> CompileTheWorldStartAt = new OptionValue<>(1); | |
73 @Option(help = "Last class to consider when using -XX:+CompileTheWorld", type = OptionType.Debug) | |
74 public static final OptionValue<Integer> CompileTheWorldStopAt = new OptionValue<>(Integer.MAX_VALUE); | |
75 @Option(help = "Option value overrides to use during compile the world. For example, " + | |
76 "to disable inlining and partial escape analysis specify '-PartialEscapeAnalysis -Inline'. " + | |
77 "The format for each option is the same as on the command line just without the '-G:' prefix.", type = OptionType.Debug) | |
78 public static final OptionValue<String> CompileTheWorldConfig = new OptionValue<>(null); | |
79 | |
80 @Option(help = "Run CTW using as many threads as there are processors on the system", type = OptionType.Debug) | |
81 public static final OptionValue<Boolean> CompileTheWorldMultiThreaded = new OptionValue<>(false); | |
82 @Option(help = "Number of threads to use for multithreaded CTW. Defaults to Runtime.getRuntime().availableProcessors()", type = OptionType.Debug) | |
83 public static final OptionValue<Integer> CompileTheWorldThreads = new OptionValue<>(0); | |
84 // @formatter:on | |
85 | |
86 /** | |
87 * Overrides {@link #CompileTheWorldStartAt} and {@link #CompileTheWorldStopAt} from | |
88 * {@code -XX} HotSpot options of the same name if the latter have non-default values. | |
89 */ | |
90 public static void overrideWithNativeOptions(HotSpotVMConfig c) { | |
91 if (c.compileTheWorldStartAt != 1) { | |
92 CompileTheWorldStartAt.setValue(c.compileTheWorldStartAt); | |
93 } | |
94 if (c.compileTheWorldStopAt != Integer.MAX_VALUE) { | |
95 CompileTheWorldStopAt.setValue(c.compileTheWorldStopAt); | |
96 } | |
97 } | |
98 } | |
99 | |
100 /** | |
101 * A mechanism for overriding JVMCI options that affect compilation. A {@link Config} object | |
102 * should be used in a try-with-resources statement to ensure overriding of options is scoped | |
103 * properly. For example: | |
104 * | |
105 * <pre> | |
106 * Config config = ...; | |
107 * try (AutoCloseable s = config == null ? null : config.apply()) { | |
108 * // perform a JVMCI compilation | |
109 * } | |
110 * </pre> | |
111 */ | |
112 @SuppressWarnings("serial") | |
113 public static class Config extends HashMap<OptionValue<?>, Object> implements OptionConsumer { | |
114 /** | |
115 * Creates a {@link Config} object by parsing a set of space separated override options. | |
116 * | |
117 * @param options a space separated set of option value settings with each option setting in | |
118 * a format compatible with | |
119 * {@link OptionUtils#parseOption(String, OptionConsumer)}. Ignored if null. | |
120 */ | |
121 public Config(String options) { | |
122 if (options != null) { | |
123 for (String option : options.split("\\s+")) { | |
124 OptionUtils.parseOption(option, this); | |
125 } | |
126 } | |
127 } | |
128 | |
129 /** | |
130 * Applies the overrides represented by this object. The overrides are in effect until | |
131 * {@link OverrideScope#close()} is called on the returned object. | |
132 */ | |
133 OverrideScope apply() { | |
134 return OptionValue.override(this); | |
135 } | |
136 | |
137 public void set(OptionDescriptor desc, Object value) { | |
138 put(desc.getOptionValue(), value); | |
139 } | |
140 } | |
141 | |
142 /** List of Zip/Jar files to compile (see {@link Options#CompileTheWorldClasspath}). */ | |
143 private final String files; | |
144 | |
145 /** Class index to start compilation at (see {@link Options#CompileTheWorldStartAt}). */ | |
146 private final int startAt; | |
147 | |
148 /** Class index to stop compilation at (see {@link Options#CompileTheWorldStopAt}). */ | |
149 private final int stopAt; | |
150 | |
151 /** Only compile methods matching one of the filters in this array if the array is non-null. */ | |
152 private final MethodFilter[] methodFilters; | |
153 | |
154 /** Exclude methods matching one of the filters in this array if the array is non-null. */ | |
155 private final MethodFilter[] excludeMethodFilters; | |
156 | |
157 // Counters | |
158 private int classFileCounter = 0; | |
159 private AtomicLong compiledMethodsCounter = new AtomicLong(); | |
160 private AtomicLong compileTime = new AtomicLong(); | |
161 private AtomicLong memoryUsed = new AtomicLong(); | |
162 | |
163 private boolean verbose; | |
164 private final Config config; | |
165 | |
166 /** | |
167 * Signal that the threads should start compiling in multithreaded mode. | |
168 */ | |
169 private boolean running; | |
170 | |
171 private ThreadPoolExecutor threadPool; | |
172 | |
173 /** | |
174 * Creates a compile-the-world instance. | |
175 * | |
176 * @param files {@link File#pathSeparator} separated list of Zip/Jar files to compile | |
177 * @param startAt index of the class file to start compilation at | |
178 * @param stopAt index of the class file to stop compilation at | |
179 * @param methodFilters | |
180 * @param excludeMethodFilters | |
181 */ | |
182 public CompileTheWorld(String files, Config config, int startAt, int stopAt, String methodFilters, String excludeMethodFilters, boolean verbose) { | |
183 this.files = files; | |
184 this.startAt = startAt; | |
185 this.stopAt = stopAt; | |
186 this.methodFilters = methodFilters == null || methodFilters.isEmpty() ? null : MethodFilter.parse(methodFilters); | |
187 this.excludeMethodFilters = excludeMethodFilters == null || excludeMethodFilters.isEmpty() ? null : MethodFilter.parse(excludeMethodFilters); | |
188 this.verbose = verbose; | |
189 this.config = config; | |
190 | |
191 // We don't want the VM to exit when a method fails to compile... | |
192 config.putIfAbsent(ExitVMOnException, false); | |
193 | |
194 // ...but we want to see exceptions. | |
195 config.putIfAbsent(PrintBailout, true); | |
196 config.putIfAbsent(PrintStackTraceOnException, true); | |
197 config.putIfAbsent(HotSpotResolvedJavaMethodImpl.Options.UseProfilingInformation, false); | |
198 } | |
199 | |
200 /** | |
201 * Compiles all methods in all classes in the Zip/Jar archive files in | |
202 * {@link Options#CompileTheWorldClasspath}. If {@link Options#CompileTheWorldClasspath} | |
203 * contains the magic token {@link #SUN_BOOT_CLASS_PATH} passed up from HotSpot we take the | |
204 * files from the boot class path. | |
205 */ | |
206 public void compile() throws Throwable { | |
207 // By default only report statistics for the CTW threads themselves | |
208 if (JVMCIDebugConfig.DebugValueThreadFilter.hasDefaultValue()) { | |
209 JVMCIDebugConfig.DebugValueThreadFilter.setValue("^CompileTheWorld"); | |
210 } | |
211 | |
212 if (SUN_BOOT_CLASS_PATH.equals(files)) { | |
213 final String[] entries = System.getProperty(SUN_BOOT_CLASS_PATH).split(File.pathSeparator); | |
214 String bcpFiles = ""; | |
215 for (int i = 0; i < entries.length; i++) { | |
216 final String entry = entries[i]; | |
217 | |
218 // We stop at rt.jar, unless it is the first boot class path entry. | |
219 if (entry.endsWith("rt.jar") && (i > 0)) { | |
220 break; | |
221 } | |
222 if (i > 0) { | |
223 bcpFiles += File.pathSeparator; | |
224 } | |
225 bcpFiles += entry; | |
226 } | |
227 compile(bcpFiles); | |
228 } else { | |
229 compile(files); | |
230 } | |
231 } | |
232 | |
233 public void println() { | |
234 println(""); | |
235 } | |
236 | |
237 public void println(String format, Object... args) { | |
238 println(String.format(format, args)); | |
239 } | |
240 | |
241 public void println(String s) { | |
242 if (verbose) { | |
243 TTY.println(s); | |
244 } | |
245 } | |
246 | |
247 @SuppressWarnings("unused") | |
248 private static void dummy() { | |
249 } | |
250 | |
251 /** | |
252 * Compiles all methods in all classes in the Zip/Jar files passed. | |
253 * | |
254 * @param fileList {@link File#pathSeparator} separated list of Zip/Jar files to compile | |
255 * @throws IOException | |
256 */ | |
257 private void compile(String fileList) throws IOException { | |
258 final String[] entries = fileList.split(File.pathSeparator); | |
259 long start = System.currentTimeMillis(); | |
260 | |
261 CompilerThreadFactory factory = new CompilerThreadFactory("CompileTheWorld", new DebugConfigAccess() { | |
262 public JVMCIDebugConfig getDebugConfig() { | |
263 if (Debug.isEnabled() && DebugScope.getConfig() == null) { | |
264 return DebugEnvironment.initialize(System.out); | |
265 } | |
266 return null; | |
267 } | |
268 }); | |
269 | |
270 try { | |
271 // compile dummy method to get compiler initilized outside of the config debug override. | |
272 HotSpotResolvedJavaMethod dummyMethod = (HotSpotResolvedJavaMethod) JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess().lookupJavaMethod( | |
273 CompileTheWorld.class.getDeclaredMethod("dummy")); | |
274 CompilationTask task = new CompilationTask(dummyMethod, Compiler.INVOCATION_ENTRY_BCI, 0L, dummyMethod.allocateCompileId(Compiler.INVOCATION_ENTRY_BCI), false); | |
275 task.runCompilation(); | |
276 } catch (NoSuchMethodException | SecurityException e1) { | |
277 e1.printStackTrace(); | |
278 } | |
279 | |
280 /* | |
281 * Always use a thread pool, even for single threaded mode since it simplifies the use of | |
282 * DebugValueThreadFilter to filter on the thread names. | |
283 */ | |
284 int threadCount = 1; | |
285 if (Options.CompileTheWorldMultiThreaded.getValue()) { | |
286 threadCount = Options.CompileTheWorldThreads.getValue(); | |
287 if (threadCount == 0) { | |
288 threadCount = Runtime.getRuntime().availableProcessors(); | |
289 } | |
290 } else { | |
291 running = true; | |
292 } | |
293 threadPool = new ThreadPoolExecutor(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), factory); | |
294 | |
295 try (OverrideScope s = config.apply()) { | |
296 for (int i = 0; i < entries.length; i++) { | |
297 final String entry = entries[i]; | |
298 | |
299 // For now we only compile all methods in all classes in zip/jar files. | |
300 if (!entry.endsWith(".zip") && !entry.endsWith(".jar")) { | |
301 println("CompileTheWorld : Skipped classes in " + entry); | |
302 println(); | |
303 continue; | |
304 } | |
305 | |
306 if (methodFilters == null || methodFilters.length == 0) { | |
307 println("CompileTheWorld : Compiling all classes in " + entry); | |
308 } else { | |
309 String include = Arrays.asList(methodFilters).stream().map(MethodFilter::toString).collect(Collectors.joining(", ")); | |
310 println("CompileTheWorld : Compiling all methods in " + entry + " matching one of the following filters: " + include); | |
311 } | |
312 if (excludeMethodFilters != null && excludeMethodFilters.length > 0) { | |
313 String exclude = Arrays.asList(excludeMethodFilters).stream().map(MethodFilter::toString).collect(Collectors.joining(", ")); | |
314 println("CompileTheWorld : Excluding all methods matching one of the following filters: " + exclude); | |
315 } | |
316 println(); | |
317 | |
318 URL url = new URL("jar", "", "file:" + entry + "!/"); | |
319 ClassLoader loader = new URLClassLoader(new URL[]{url}); | |
320 | |
321 JarFile jarFile = new JarFile(entry); | |
322 Enumeration<JarEntry> e = jarFile.entries(); | |
323 | |
324 while (e.hasMoreElements()) { | |
325 JarEntry je = e.nextElement(); | |
326 if (je.isDirectory() || !je.getName().endsWith(".class")) { | |
327 continue; | |
328 } | |
329 | |
330 // Are we done? | |
331 if (classFileCounter >= stopAt) { | |
332 break; | |
333 } | |
334 | |
335 String className = je.getName().substring(0, je.getName().length() - ".class".length()); | |
336 String dottedClassName = className.replace('/', '.'); | |
337 classFileCounter++; | |
338 | |
339 if (methodFilters != null && !MethodFilter.matchesClassName(methodFilters, dottedClassName)) { | |
340 continue; | |
341 } | |
342 if (excludeMethodFilters != null && MethodFilter.matchesClassName(excludeMethodFilters, dottedClassName)) { | |
343 continue; | |
344 } | |
345 | |
346 if (dottedClassName.startsWith("jdk.management.") || dottedClassName.startsWith("jdk.internal.cmm.*")) { | |
347 continue; | |
348 } | |
349 | |
350 try { | |
351 // Load and initialize class | |
352 Class<?> javaClass = Class.forName(dottedClassName, true, loader); | |
353 | |
354 // Pre-load all classes in the constant pool. | |
355 try { | |
356 HotSpotResolvedObjectType objectType = HotSpotResolvedObjectTypeImpl.fromObjectClass(javaClass); | |
357 ConstantPool constantPool = objectType.constantPool(); | |
358 for (int cpi = 1; cpi < constantPool.length(); cpi++) { | |
359 constantPool.loadReferencedType(cpi, HotSpotConstantPool.Bytecodes.LDC); | |
360 } | |
361 } catch (Throwable t) { | |
362 // If something went wrong during pre-loading we just ignore it. | |
363 println("Preloading failed for (%d) %s: %s", classFileCounter, className, t); | |
364 } | |
365 | |
366 // Are we compiling this class? | |
367 MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess(); | |
368 if (classFileCounter >= startAt) { | |
369 println("CompileTheWorld (%d) : %s", classFileCounter, className); | |
370 | |
371 // Compile each constructor/method in the class. | |
372 for (Constructor<?> constructor : javaClass.getDeclaredConstructors()) { | |
373 HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(constructor); | |
374 if (canBeCompiled(javaMethod, constructor.getModifiers())) { | |
375 compileMethod(javaMethod); | |
376 } | |
377 } | |
378 for (Method method : javaClass.getDeclaredMethods()) { | |
379 HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(method); | |
380 if (canBeCompiled(javaMethod, method.getModifiers())) { | |
381 compileMethod(javaMethod); | |
382 } | |
383 } | |
384 } | |
385 } catch (Throwable t) { | |
386 println("CompileTheWorld (%d) : Skipping %s %s", classFileCounter, className, t.toString()); | |
387 t.printStackTrace(); | |
388 } | |
389 } | |
390 jarFile.close(); | |
391 } | |
392 } | |
393 | |
394 if (!running) { | |
395 startThreads(); | |
396 } | |
397 int wakeups = 0; | |
398 while (threadPool.getCompletedTaskCount() != threadPool.getTaskCount()) { | |
399 if (wakeups % 15 == 0) { | |
400 TTY.println("CompileTheWorld : Waiting for " + (threadPool.getTaskCount() - threadPool.getCompletedTaskCount()) + " compiles"); | |
401 } | |
402 try { | |
403 threadPool.awaitTermination(1, TimeUnit.SECONDS); | |
404 wakeups++; | |
405 } catch (InterruptedException e) { | |
406 } | |
407 } | |
408 threadPool = null; | |
409 | |
410 long elapsedTime = System.currentTimeMillis() - start; | |
411 | |
412 println(); | |
413 if (Options.CompileTheWorldMultiThreaded.getValue()) { | |
414 TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms elapsed, %d ms compile time, %d bytes of memory used)", classFileCounter, compiledMethodsCounter.get(), elapsedTime, | |
415 compileTime.get(), memoryUsed.get()); | |
416 } else { | |
417 TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms, %d bytes of memory used)", classFileCounter, compiledMethodsCounter.get(), compileTime.get(), memoryUsed.get()); | |
418 } | |
419 } | |
420 | |
421 private synchronized void startThreads() { | |
422 running = true; | |
423 // Wake up any waiting threads | |
424 notifyAll(); | |
425 } | |
426 | |
427 private synchronized void waitToRun() { | |
428 while (!running) { | |
429 try { | |
430 wait(); | |
431 } catch (InterruptedException e) { | |
432 } | |
433 } | |
434 } | |
435 | |
436 private void compileMethod(HotSpotResolvedJavaMethod method) throws InterruptedException, ExecutionException { | |
437 if (methodFilters != null && !MethodFilter.matches(methodFilters, method)) { | |
438 return; | |
439 } | |
440 if (excludeMethodFilters != null && MethodFilter.matches(excludeMethodFilters, method)) { | |
441 return; | |
442 } | |
443 Future<?> task = threadPool.submit(new Runnable() { | |
444 public void run() { | |
445 waitToRun(); | |
446 try (OverrideScope s = config.apply()) { | |
447 compileMethod(method, classFileCounter); | |
448 } | |
449 } | |
450 }); | |
451 if (threadPool.getCorePoolSize() == 1) { | |
452 task.get(); | |
453 } | |
454 } | |
455 | |
456 /** | |
457 * Compiles a method and gathers some statistics. | |
458 */ | |
459 private void compileMethod(HotSpotResolvedJavaMethod method, int counter) { | |
460 try { | |
461 long start = System.currentTimeMillis(); | |
462 long allocatedAtStart = getCurrentThreadAllocatedBytes(); | |
463 | |
464 CompilationTask task = new CompilationTask(method, Compiler.INVOCATION_ENTRY_BCI, 0L, method.allocateCompileId(Compiler.INVOCATION_ENTRY_BCI), false); | |
465 task.runCompilation(); | |
466 | |
467 memoryUsed.getAndAdd(getCurrentThreadAllocatedBytes() - allocatedAtStart); | |
468 compileTime.getAndAdd(System.currentTimeMillis() - start); | |
469 compiledMethodsCounter.incrementAndGet(); | |
470 } catch (Throwable t) { | |
471 // Catch everything and print a message | |
472 println("CompileTheWorld (%d) : Error compiling method: %s", counter, method.format("%H.%n(%p):%r")); | |
473 t.printStackTrace(TTY.cachedOut); | |
474 } | |
475 } | |
476 | |
477 /** | |
478 * Determines if a method should be compiled (Cf. CompilationPolicy::can_be_compiled). | |
479 * | |
480 * @return true if it can be compiled, false otherwise | |
481 */ | |
482 private static boolean canBeCompiled(HotSpotResolvedJavaMethod javaMethod, int modifiers) { | |
483 if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) { | |
484 return false; | |
485 } | |
486 HotSpotVMConfig c = HotSpotJVMCIRuntime.runtime().getConfig(); | |
487 if (c.dontCompileHugeMethods && javaMethod.getCodeSize() > c.hugeMethodLimit) { | |
488 return false; | |
489 } | |
490 // Allow use of -XX:CompileCommand=dontinline to exclude problematic methods | |
491 if (!javaMethod.canBeInlined()) { | |
492 return false; | |
493 } | |
494 // Skip @Snippets for now | |
495 for (Annotation annotation : javaMethod.getAnnotations()) { | |
496 if (annotation.annotationType().getName().equals("com.oracle.graal.replacements.Snippet")) { | |
497 return false; | |
498 } | |
499 } | |
500 return true; | |
501 } | |
502 | |
503 } |