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 }