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