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 }