src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorld.java
changeset 54601 c40b2a190173
parent 54204 55c262f4f5a1
child 55509 d58442b8abc1
child 58678 9cf78a70fa4f
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorld.java	Tue Apr 23 14:09:54 2019 -0400
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorld.java	Tue Apr 23 22:55:09 2019 +0200
@@ -32,7 +32,9 @@
 import static org.graalvm.compiler.debug.MemUseTrackerKey.getCurrentThreadAllocatedBytes;
 import static org.graalvm.compiler.hotspot.test.CompileTheWorld.Options.DESCRIPTORS;
 import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java8OrEarlier;
+import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET;
 
+import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
 import java.io.File;
 import java.io.IOException;
@@ -73,10 +75,11 @@
 import java.util.stream.Collectors;
 
 import jdk.internal.vm.compiler.collections.EconomicMap;
-import jdk.internal.vm.compiler.collections.UnmodifiableEconomicMap;
+import jdk.internal.vm.compiler.collections.UnmodifiableMapCursor;
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.bytecode.Bytecodes;
 import org.graalvm.compiler.core.CompilerThreadFactory;
+import org.graalvm.compiler.core.phases.HighTier;
 import org.graalvm.compiler.core.test.ReflectionOptionDescriptors;
 import org.graalvm.compiler.debug.DebugOptions;
 import org.graalvm.compiler.debug.GraalError;
@@ -86,11 +89,15 @@
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.hotspot.HotSpotGraalCompiler;
 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
+import org.graalvm.compiler.hotspot.test.CompileTheWorld.LibGraalParams.StackTraceBuffer;
 import org.graalvm.compiler.options.OptionDescriptors;
 import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.options.OptionsParser;
+import org.graalvm.compiler.serviceprovider.GraalUnsafeAccess;
 import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
+import jdk.internal.vm.compiler.libgraal.LibGraal;
+import jdk.internal.vm.compiler.libgraal.OptionsEncoder;
 
 import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider;
 import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
@@ -102,6 +109,7 @@
 import jdk.vm.ci.meta.MetaAccessProvider;
 import jdk.vm.ci.runtime.JVMCI;
 import jdk.vm.ci.runtime.JVMCICompiler;
+import sun.misc.Unsafe;
 
 /**
  * This class implements compile-the-world functionality with JVMCI.
@@ -127,17 +135,19 @@
      *            Ignored if null.
      */
     public static EconomicMap<OptionKey<?>, Object> parseOptions(String options) {
+        EconomicMap<OptionKey<?>, Object> values = OptionValues.newOptionMap();
         if (options != null) {
             EconomicMap<String, String> optionSettings = EconomicMap.create();
             for (String optionSetting : options.split("\\s+|#")) {
                 OptionsParser.parseOptionSettingTo(optionSetting, optionSettings);
             }
-            EconomicMap<OptionKey<?>, Object> values = OptionValues.newOptionMap();
             ServiceLoader<OptionDescriptors> loader = ServiceLoader.load(OptionDescriptors.class, OptionDescriptors.class.getClassLoader());
             OptionsParser.parseOptions(optionSettings, values, loader);
-            return values;
         }
-        return EconomicMap.create();
+        if (!values.containsKey(HighTier.Options.Inline)) {
+            values.put(HighTier.Options.Inline, false);
+        }
+        return values;
     }
 
     private final HotSpotJVMCIRuntime jvmciRuntime;
@@ -165,6 +175,13 @@
      */
     private final int stopAt;
 
+    /**
+     * Max classes to compile.
+     *
+     * @see Options#MaxClasses
+     */
+    private final int maxClasses;
+
     /** Only compile methods matching one of the filters in this array if the array is non-null. */
     private final MethodFilter[] methodFilters;
 
@@ -186,8 +203,130 @@
 
     private ThreadPoolExecutor threadPool;
 
-    private OptionValues currentOptions;
-    private final UnmodifiableEconomicMap<OptionKey<?>, Object> compilationOptions;
+    /**
+     * Values for {@link CompileTheWorld.Options}.
+     */
+    private final OptionValues harnessOptions;
+
+    /**
+     * Option values used during compilation.
+     */
+    private final OptionValues compilerOptions;
+
+    /**
+     * Manages native memory buffers for passing arguments into libgraal and receiving return
+     * values. The native memory buffers are freed when this object is {@linkplain #close() closed}.
+     */
+    static class LibGraalParams implements AutoCloseable {
+
+        static {
+            LibGraal.registerNativeMethods(HotSpotJVMCIRuntime.runtime(), CompileTheWorld.class);
+        }
+
+        /**
+         * Native memory containing {@linkplain OptionsEncoder encoded} {@link OptionValues}.
+         */
+        static class OptionsBuffer {
+            private long address;
+            final int size;
+            final int hash;
+
+            OptionsBuffer(OptionValues options) {
+                Map<String, Object> map = new HashMap<>();
+                UnmodifiableMapCursor<OptionKey<?>, Object> cursor = options.getMap().getEntries();
+                while (cursor.advance()) {
+                    final OptionKey<?> key = cursor.getKey();
+                    Object value = cursor.getValue();
+                    map.put(key.getName(), value);
+                }
+
+                byte[] encoded = OptionsEncoder.encode(map);
+                size = encoded.length;
+                hash = Arrays.hashCode(encoded);
+                address = UNSAFE.allocateMemory(encoded.length);
+                UNSAFE.copyMemory(encoded, ARRAY_BYTE_BASE_OFFSET, null, address, size);
+            }
+
+            long getAddress() {
+                if (address == 0) {
+                    throw new IllegalStateException();
+                }
+                return address;
+            }
+
+            void free() {
+                if (address != 0) {
+                    UNSAFE.freeMemory(address);
+                    address = 0;
+                }
+            }
+        }
+
+        /**
+         * Manages native memory for receiving a {@linkplain Throwable#printStackTrace() stack
+         * trace} from libgraal serialized via {@link ByteArrayOutputStream} to a byte array.
+         */
+        static class StackTraceBuffer {
+            final int size;
+            private long address;
+
+            StackTraceBuffer(int size) {
+                this.size = size;
+                address = UNSAFE.allocateMemory(size);
+            }
+
+            void free() {
+                if (address != 0L) {
+                    UNSAFE.freeMemory(address);
+                    address = 0L;
+                }
+            }
+
+            long getAddress() {
+                if (address == 0) {
+                    throw new IllegalStateException();
+                }
+                return address;
+            }
+        }
+
+        final OptionsBuffer options;
+
+        private final List<StackTraceBuffer> stackTraceBuffers = new ArrayList<>();
+
+        /**
+         * Gets a stack trace buffer for the current thread.
+         */
+        StackTraceBuffer getStackTraceBuffer() {
+            return stackTraceBuffer.get();
+        }
+
+        private final ThreadLocal<StackTraceBuffer> stackTraceBuffer = new ThreadLocal<StackTraceBuffer>() {
+            @Override
+            protected StackTraceBuffer initialValue() {
+                StackTraceBuffer buffer = new StackTraceBuffer(10_000);
+                synchronized (stackTraceBuffers) {
+                    stackTraceBuffers.add(buffer);
+                }
+                return buffer;
+            }
+        };
+
+        LibGraalParams(OptionValues options) {
+            this.options = new OptionsBuffer(options);
+        }
+
+        @Override
+        public void close() {
+            options.free();
+            synchronized (stackTraceBuffers) {
+                for (StackTraceBuffer buffer : stackTraceBuffers) {
+                    buffer.free();
+                }
+                stackTraceBuffers.clear();
+            }
+        }
+    }
 
     /**
      * Creates a compile-the-world instance.
@@ -195,73 +334,99 @@
      * @param files {@link File#pathSeparator} separated list of Zip/Jar files to compile
      * @param startAt index of the class file to start compilation at
      * @param stopAt index of the class file to stop compilation at
+     * @param maxClasses maximum number of classes to process
      * @param methodFilters
      * @param excludeMethodFilters
+     * @param harnessOptions values for {@link CompileTheWorld.Options}
+     * @param compilerOptions option values used by the compiler
      */
-    public CompileTheWorld(HotSpotJVMCIRuntime jvmciRuntime, HotSpotGraalCompiler compiler, String files, int startAt, int stopAt, String methodFilters, String excludeMethodFilters,
-                    boolean verbose, OptionValues initialOptions, EconomicMap<OptionKey<?>, Object> compilationOptions) {
+    public CompileTheWorld(HotSpotJVMCIRuntime jvmciRuntime,
+                    HotSpotGraalCompiler compiler,
+                    String files,
+                    int startAt,
+                    int stopAt,
+                    int maxClasses,
+                    String methodFilters,
+                    String excludeMethodFilters,
+                    boolean verbose,
+                    OptionValues harnessOptions,
+                    OptionValues compilerOptions) {
         this.jvmciRuntime = jvmciRuntime;
         this.compiler = compiler;
         this.inputClassPath = files;
-        this.startAt = startAt;
-        this.stopAt = stopAt;
+        this.startAt = Math.max(startAt, 1);
+        this.stopAt = Math.max(stopAt, 1);
+        this.maxClasses = Math.max(maxClasses, 1);
         this.methodFilters = methodFilters == null || methodFilters.isEmpty() ? null : MethodFilter.parse(methodFilters);
         this.excludeMethodFilters = excludeMethodFilters == null || excludeMethodFilters.isEmpty() ? null : MethodFilter.parse(excludeMethodFilters);
         this.verbose = verbose;
-        this.currentOptions = initialOptions;
+        this.harnessOptions = harnessOptions;
 
         // Copy the initial options and add in any extra options
-        EconomicMap<OptionKey<?>, Object> compilationOptionsCopy = EconomicMap.create(initialOptions.getMap());
-        compilationOptionsCopy.putAll(compilationOptions);
+        EconomicMap<OptionKey<?>, Object> compilerOptionsMap = EconomicMap.create(compilerOptions.getMap());
 
         // We want to see stack traces when a method fails to compile
-        CompilationBailoutAsFailure.putIfAbsent(compilationOptionsCopy, true);
-        CompilationFailureAction.putIfAbsent(compilationOptionsCopy, Print);
+        CompilationBailoutAsFailure.putIfAbsent(compilerOptionsMap, true);
+        CompilationFailureAction.putIfAbsent(compilerOptionsMap, Print);
 
         // By default only report statistics for the CTW threads themselves
-        DebugOptions.MetricsThreadFilter.putIfAbsent(compilationOptionsCopy, "^CompileTheWorld");
-        this.compilationOptions = compilationOptionsCopy;
+        DebugOptions.MetricsThreadFilter.putIfAbsent(compilerOptionsMap, "^CompileTheWorld");
+        this.compilerOptions = new OptionValues(compilerOptionsMap);
     }
 
-    public CompileTheWorld(HotSpotJVMCIRuntime jvmciRuntime, HotSpotGraalCompiler compiler, OptionValues options) {
-        this(jvmciRuntime, compiler, Options.Classpath.getValue(options),
-                        Options.StartAt.getValue(options),
-                        Options.StopAt.getValue(options),
-                        Options.MethodFilter.getValue(options),
-                        Options.ExcludeMethodFilter.getValue(options),
-                        Options.Verbose.getValue(options),
-                        options,
-                        parseOptions(Options.Config.getValue(options)));
+    public CompileTheWorld(HotSpotJVMCIRuntime jvmciRuntime,
+                    HotSpotGraalCompiler compiler,
+                    OptionValues harnessOptions,
+                    OptionValues compilerOptions) {
+        this(jvmciRuntime, compiler, Options.Classpath.getValue(harnessOptions),
+                        Options.StartAt.getValue(harnessOptions),
+                        Options.StopAt.getValue(harnessOptions),
+                        Options.MaxClasses.getValue(harnessOptions),
+                        Options.MethodFilter.getValue(harnessOptions),
+                        Options.ExcludeMethodFilter.getValue(harnessOptions),
+                        Options.Verbose.getValue(harnessOptions),
+                        harnessOptions,
+                        new OptionValues(compilerOptions, parseOptions(Options.Config.getValue(harnessOptions))));
     }
 
     /**
      * Compiles all methods in all classes in {@link #inputClassPath}. If {@link #inputClassPath}
      * equals {@link #SUN_BOOT_CLASS_PATH} the boot classes are used.
      */
+    @SuppressWarnings("try")
     public void compile() throws Throwable {
-        if (SUN_BOOT_CLASS_PATH.equals(inputClassPath)) {
-            String bcpEntry = null;
-            if (Java8OrEarlier) {
-                final String[] entries = System.getProperty(SUN_BOOT_CLASS_PATH).split(File.pathSeparator);
-                for (int i = 0; i < entries.length && bcpEntry == null; i++) {
-                    String entry = entries[i];
-                    File entryFile = new File(entry);
-                    if (entryFile.getName().endsWith("rt.jar") && entryFile.isFile()) {
-                        bcpEntry = entry;
+        try (LibGraalParams libgraal = LibGraal.isAvailable() ? new LibGraalParams(compilerOptions) : null) {
+            if (SUN_BOOT_CLASS_PATH.equals(inputClassPath)) {
+                String bcpEntry = null;
+                if (Java8OrEarlier) {
+                    final String[] entries = System.getProperty(SUN_BOOT_CLASS_PATH).split(File.pathSeparator);
+                    for (int i = 0; i < entries.length && bcpEntry == null; i++) {
+                        String entry = entries[i];
+                        File entryFile = new File(entry);
+                        if (entryFile.getName().endsWith("rt.jar") && entryFile.isFile()) {
+                            bcpEntry = entry;
+                        }
                     }
+                    if (bcpEntry == null) {
+                        throw new GraalError("Could not find rt.jar on boot class path %s", System.getProperty(SUN_BOOT_CLASS_PATH));
+                    }
+                } else {
+                    bcpEntry = JRT_CLASS_PATH_ENTRY;
                 }
-                if (bcpEntry == null) {
-                    throw new GraalError("Could not find rt.jar on boot class path %s", System.getProperty(SUN_BOOT_CLASS_PATH));
-                }
+                compile(bcpEntry, libgraal);
             } else {
-                bcpEntry = JRT_CLASS_PATH_ENTRY;
+                compile(inputClassPath, libgraal);
             }
-            compile(bcpEntry);
-        } else {
-            compile(inputClassPath);
         }
     }
 
+    private AutoCloseable enterCompilation() {
+        if (!LibGraal.isAvailable()) {
+            return null;
+        }
+        return new LibGraalParams(compilerOptions);
+    }
+
     public void println() {
         println("");
     }
@@ -486,6 +651,19 @@
         return true;
     }
 
+    private ClassPathEntry openClassPathEntry(String entry) throws IOException {
+        if (entry.endsWith(".zip") || entry.endsWith(".jar")) {
+            return new JarClassPathEntry(entry);
+        } else if (entry.equals(JRT_CLASS_PATH_ENTRY)) {
+            return new JRTClassPathEntry(entry, Options.LimitModules.getValue(harnessOptions));
+        } else {
+            if (!new File(entry).isDirectory()) {
+                return null;
+            }
+            return new DirClassPathEntry(entry);
+        }
+    }
+
     /**
      * Compiles all methods in all classes in a given class path.
      *
@@ -493,23 +671,25 @@
      * @throws IOException
      */
     @SuppressWarnings("try")
-    private void compile(String classPath) throws IOException {
+    private void compile(String classPath, LibGraalParams libgraal) throws IOException {
         final String[] entries = classPath.split(File.pathSeparator);
         long start = System.currentTimeMillis();
         Map<Thread, StackTraceElement[]> initialThreads = Thread.getAllStackTraces();
 
-        try {
-            // compile dummy method to get compiler initialized outside of the
-            // config debug override.
-            HotSpotResolvedJavaMethod dummyMethod = (HotSpotResolvedJavaMethod) JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess().lookupJavaMethod(
-                            CompileTheWorld.class.getDeclaredMethod("dummy"));
-            int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI;
-            boolean useProfilingInfo = false;
-            boolean installAsDefault = false;
-            CompilationTask task = new CompilationTask(jvmciRuntime, compiler, new HotSpotCompilationRequest(dummyMethod, entryBCI, 0L), useProfilingInfo, installAsDefault, currentOptions);
-            task.runCompilation();
-        } catch (NoSuchMethodException | SecurityException e1) {
-            printStackTrace(e1);
+        if (libgraal == null) {
+            try {
+                // compile dummy method to get compiler initialized outside of the
+                // config debug override.
+                HotSpotResolvedJavaMethod dummyMethod = (HotSpotResolvedJavaMethod) JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess().lookupJavaMethod(
+                                CompileTheWorld.class.getDeclaredMethod("dummy"));
+                int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI;
+                boolean useProfilingInfo = false;
+                boolean installAsDefault = false;
+                CompilationTask task = new CompilationTask(jvmciRuntime, compiler, new HotSpotCompilationRequest(dummyMethod, entryBCI, 0L), useProfilingInfo, installAsDefault);
+                task.runCompilation(compilerOptions);
+            } catch (NoSuchMethodException | SecurityException e1) {
+                printStackTrace(e1);
+            }
         }
 
         /*
@@ -517,8 +697,8 @@
          * DebugValueThreadFilter to filter on the thread names.
          */
         int threadCount = 1;
-        if (Options.MultiThreaded.getValue(currentOptions)) {
-            threadCount = Options.Threads.getValue(currentOptions);
+        if (Options.MultiThreaded.getValue(harnessOptions)) {
+            threadCount = Options.Threads.getValue(harnessOptions);
             if (threadCount == 0) {
                 threadCount = Runtime.getRuntime().availableProcessors();
             }
@@ -526,26 +706,37 @@
             running = true;
         }
 
-        OptionValues savedOptions = currentOptions;
-        currentOptions = new OptionValues(compilationOptions);
         threadPool = new ThreadPoolExecutor(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new CompilerThreadFactory("CompileTheWorld"));
 
-        try {
-            for (int i = 0; i < entries.length; i++) {
-                final String entry = entries[i];
+        int compileStartAt = startAt;
+        int compileStopAt = stopAt;
+        int compileStep = 1;
+        if (maxClasses != Integer.MAX_VALUE) {
+            int totalClassFileCount = 0;
+            for (String entry : entries) {
+                try (ClassPathEntry cpe = openClassPathEntry(entry)) {
+                    if (cpe != null) {
+                        totalClassFileCount += cpe.getClassNames().size();
+                    }
+                }
+            }
 
-                ClassPathEntry cpe;
-                if (entry.endsWith(".zip") || entry.endsWith(".jar")) {
-                    cpe = new JarClassPathEntry(entry);
-                } else if (entry.equals(JRT_CLASS_PATH_ENTRY)) {
-                    cpe = new JRTClassPathEntry(entry, Options.LimitModules.getValue(currentOptions));
-                } else {
-                    if (!new File(entry).isDirectory()) {
-                        println("CompileTheWorld : Skipped classes in " + entry);
-                        println();
-                        continue;
-                    }
-                    cpe = new DirClassPathEntry(entry);
+            int lastClassFile = totalClassFileCount - 1;
+            compileStartAt = Math.min(startAt, lastClassFile);
+            compileStopAt = Math.min(stopAt, lastClassFile);
+            int range = compileStopAt - compileStartAt + 1;
+            if (maxClasses < range) {
+                compileStep = range / maxClasses;
+            }
+        }
+
+        for (int i = 0; i < entries.length; i++) {
+            final String entry = entries[i];
+            try (ClassPathEntry cpe = openClassPathEntry(entry)) {
+                if (cpe == null) {
+                    println("CompileTheWorld : Skipped classes in " + entry);
+                    println();
+                    continue;
                 }
 
                 if (methodFilters == null || methodFilters.length == 0) {
@@ -565,12 +756,16 @@
                 for (String className : cpe.getClassNames()) {
 
                     // Are we done?
-                    if (classFileCounter >= stopAt) {
+                    if (classFileCounter >= compileStopAt) {
                         break;
                     }
 
                     classFileCounter++;
 
+                    if (compileStep > 1 && ((classFileCounter - compileStartAt) % compileStep) != 0) {
+                        continue;
+                    }
+
                     if (className.startsWith("jdk.management.") ||
                                     className.startsWith("jdk.internal.cmm.*") ||
                                     // GR-5881: The class initializer for
@@ -606,28 +801,29 @@
                         }
 
                         // Are we compiling this class?
-                        if (classFileCounter >= startAt) {
-                            println("CompileTheWorld (%d) : %s", classFileCounter, className);
+                        if (classFileCounter >= compileStartAt) {
 
+                            long start0 = System.currentTimeMillis();
                             // Compile each constructor/method in the class.
                             for (Constructor<?> constructor : javaClass.getDeclaredConstructors()) {
                                 HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(constructor);
                                 if (canBeCompiled(javaMethod, constructor.getModifiers())) {
-                                    compileMethod(javaMethod);
+                                    compileMethod(javaMethod, libgraal);
                                 }
                             }
                             for (Method method : javaClass.getDeclaredMethods()) {
                                 HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(method);
                                 if (canBeCompiled(javaMethod, method.getModifiers())) {
-                                    compileMethod(javaMethod);
+                                    compileMethod(javaMethod, libgraal);
                                 }
                             }
 
                             // Also compile the class initializer if it exists
                             HotSpotResolvedJavaMethod clinit = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaType(javaClass).getClassInitializer();
                             if (clinit != null && canBeCompiled(clinit, clinit.getModifiers())) {
-                                compileMethod(clinit);
+                                compileMethod(clinit, libgraal);
                             }
+                            println("CompileTheWorld (%d) : %s (%d ms)", classFileCounter, className, System.currentTimeMillis() - start0);
                         }
                     } catch (Throwable t) {
                         if (isClassIncluded(className)) {
@@ -636,10 +832,7 @@
                         }
                     }
                 }
-                cpe.close();
             }
-        } finally {
-            currentOptions = savedOptions;
         }
 
         if (!running) {
@@ -661,11 +854,12 @@
         long elapsedTime = System.currentTimeMillis() - start;
 
         println();
-        if (Options.MultiThreaded.getValue(currentOptions)) {
-            TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms elapsed, %d ms compile time, %d bytes of memory used)", classFileCounter, compiledMethodsCounter.get(), elapsedTime,
+        int compiledClasses = classFileCounter > compileStartAt ? classFileCounter - compileStartAt : 0;
+        if (Options.MultiThreaded.getValue(harnessOptions)) {
+            TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms elapsed, %d ms compile time, %d bytes of memory used)", compiledClasses, compiledMethodsCounter.get(), elapsedTime,
                             compileTime.get(), memoryUsed.get());
         } else {
-            TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms, %d bytes of memory used)", classFileCounter, compiledMethodsCounter.get(), compileTime.get(), memoryUsed.get());
+            TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms, %d bytes of memory used)", compiledClasses, compiledMethodsCounter.get(), compileTime.get(), memoryUsed.get());
         }
 
         // Apart from the main thread, there should be only be daemon threads
@@ -712,7 +906,7 @@
     }
 
     @SuppressWarnings("try")
-    private void compileMethod(HotSpotResolvedJavaMethod method) throws InterruptedException, ExecutionException {
+    private void compileMethod(HotSpotResolvedJavaMethod method, LibGraalParams libgraal) throws InterruptedException, ExecutionException {
         if (methodFilters != null && !MethodFilter.matches(methodFilters, method)) {
             return;
         }
@@ -723,13 +917,7 @@
             @Override
             public void run() {
                 waitToRun();
-                OptionValues savedOptions = currentOptions;
-                currentOptions = new OptionValues(compilationOptions);
-                try {
-                    compileMethod(method, classFileCounter);
-                } finally {
-                    currentOptions = savedOptions;
-                }
+                compileMethod(method, classFileCounter, libgraal);
             }
         });
         if (threadPool.getCorePoolSize() == 1) {
@@ -737,23 +925,66 @@
         }
     }
 
+    private static final Unsafe UNSAFE = GraalUnsafeAccess.getUnsafe();
+
+    static native long compileMethodInLibgraal(long isolateThread,
+                    long methodHandle,
+                    boolean useProfilingInfo,
+                    boolean installAsDefault,
+                    long optionsAddress,
+                    int optionsSize,
+                    int optionsHash,
+                    long encodedThrowableBufferAddress,
+                    int encodedThrowableBufferSize);
+
     /**
      * Compiles a method and gathers some statistics.
      */
-    private void compileMethod(HotSpotResolvedJavaMethod method, int counter) {
+    private void compileMethod(HotSpotResolvedJavaMethod method, int counter, LibGraalParams libgraal) {
         try {
             long start = System.currentTimeMillis();
             long allocatedAtStart = getCurrentThreadAllocatedBytes();
-            int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI;
-            HotSpotCompilationRequest request = new HotSpotCompilationRequest(method, entryBCI, 0L);
             // For more stable CTW execution, disable use of profiling information
             boolean useProfilingInfo = false;
             boolean installAsDefault = false;
-            CompilationTask task = new CompilationTask(jvmciRuntime, compiler, request, useProfilingInfo, installAsDefault, currentOptions);
-            task.runCompilation();
+            HotSpotInstalledCode installedCode;
+            if (libgraal != null) {
+                HotSpotJVMCIRuntime runtime = HotSpotJVMCIRuntime.runtime();
+                long methodHandle = LibGraal.translate(runtime, method);
+                long isolateThread = LibGraal.getIsolateThread();
+
+                StackTraceBuffer stackTraceBuffer = libgraal.getStackTraceBuffer();
+
+                long stackTraceBufferAddress = stackTraceBuffer.getAddress();
+                long installedCodeHandle = compileMethodInLibgraal(isolateThread,
+                                methodHandle,
+                                useProfilingInfo,
+                                installAsDefault,
+                                libgraal.options.getAddress(),
+                                libgraal.options.size,
+                                libgraal.options.hash,
+                                stackTraceBufferAddress,
+                                stackTraceBuffer.size);
+
+                installedCode = LibGraal.unhand(runtime, HotSpotInstalledCode.class, installedCodeHandle);
+                if (installedCode == null) {
+                    int length = UNSAFE.getInt(stackTraceBufferAddress);
+                    byte[] data = new byte[length];
+                    UNSAFE.copyMemory(null, stackTraceBufferAddress + Integer.BYTES, data, ARRAY_BYTE_BASE_OFFSET, length);
+                    String stackTrace = new String(data).trim();
+                    println("CompileTheWorld (%d) : Error compiling method: %s", counter, method.format("%H.%n(%p):%r"));
+                    println(stackTrace);
+                }
+
+            } else {
+                int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI;
+                HotSpotCompilationRequest request = new HotSpotCompilationRequest(method, entryBCI, 0L);
+                CompilationTask task = new CompilationTask(jvmciRuntime, compiler, request, useProfilingInfo, installAsDefault);
+                task.runCompilation(compilerOptions);
+                installedCode = task.getInstalledCode();
+            }
 
             // Invalidate the generated code so the code cache doesn't fill up
-            HotSpotInstalledCode installedCode = task.getInstalledCode();
             if (installedCode != null) {
                 installedCode.invalidate();
             }
@@ -799,13 +1030,12 @@
     }
 
     static class Options {
-        // @formatter:off
         public static final OptionKey<Boolean> Help = new OptionKey<>(false);
         public static final OptionKey<String> Classpath = new OptionKey<>(CompileTheWorld.SUN_BOOT_CLASS_PATH);
         public static final OptionKey<Boolean> Verbose = new OptionKey<>(true);
         /**
-         * Ignore Graal classes by default to avoid problems associated with compiling
-         * snippets and method substitutions.
+         * Ignore Graal classes by default to avoid problems associated with compiling snippets and
+         * method substitutions.
          */
         public static final OptionKey<String> LimitModules = new OptionKey<>("~jdk.internal.vm.compiler");
         public static final OptionKey<Integer> Iterations = new OptionKey<>(1);
@@ -813,10 +1043,12 @@
         public static final OptionKey<String> ExcludeMethodFilter = new OptionKey<>(null);
         public static final OptionKey<Integer> StartAt = new OptionKey<>(1);
         public static final OptionKey<Integer> StopAt = new OptionKey<>(Integer.MAX_VALUE);
+        public static final OptionKey<Integer> MaxClasses = new OptionKey<>(Integer.MAX_VALUE);
         public static final OptionKey<String> Config = new OptionKey<>(null);
         public static final OptionKey<Boolean> MultiThreaded = new OptionKey<>(false);
         public static final OptionKey<Integer> Threads = new OptionKey<>(0);
 
+        // @formatter:off
         static final ReflectionOptionDescriptors DESCRIPTORS = new ReflectionOptionDescriptors(Options.class,
                            "Help", "List options and their help messages and then exit.",
                       "Classpath", "Class path denoting methods to compile. Default is to compile boot classes.",
@@ -826,21 +1058,24 @@
                      "Iterations", "The number of iterations to perform.",
                    "MethodFilter", "Only compile methods matching this filter.",
             "ExcludeMethodFilter", "Exclude methods matching this filter from compilation.",
-                        "StartAt", "First class to consider for compilation.",
-                         "StopAt", "Last class to consider for compilation.",
-                         "Config", "Option value overrides to use during compile the world. For example, " +
-                                   "to disable inlining and partial escape analysis specify 'PartialEscapeAnalysis=false Inline=false'. " +
-                                   "The format for each option is the same as on the command line just without the '-Dgraal.' prefix.",
+                        "StartAt", "First class to consider for compilation (default = 1).",
+                         "StopAt", "Last class to consider for compilation (default = <number of classes>).",
+                     "MaxClasses", "Maximum number of classes to process (default = <number of classes>). " +
+                                   "Ignored if less than (StopAt - StartAt + 1).",
+                         "Config", "Option values to use during compile the world compilations. For example, " +
+                                   "to disable partial escape analysis and print compilations specify " +
+                                   "'PartialEscapeAnalysis=false PrintCompilation=true'. " +
+                                   "Unless explicitly enabled with 'Inline=true' here, inlining is disabled.",
                   "MultiThreaded", "Run using multiple threads for compilation.",
                         "Threads", "Number of threads to use for multithreaded execution. Defaults to Runtime.getRuntime().availableProcessors().");
         // @formatter:on
     }
 
-    public static OptionValues loadOptions(OptionValues initialValues) {
+    public static OptionValues loadHarnessOptions() {
         EconomicMap<OptionKey<?>, Object> values = OptionValues.newOptionMap();
         List<OptionDescriptors> loader = singletonList(DESCRIPTORS);
         OptionsParser.parseOptions(extractEntries(System.getProperties(), "CompileTheWorld.", true), values, loader);
-        OptionValues options = new OptionValues(initialValues, values);
+        OptionValues options = new OptionValues(values);
         if (Options.Help.getValue(options)) {
             options.printHelp(loader, System.out, "CompileTheWorld.");
             System.exit(0);
@@ -853,14 +1088,14 @@
         HotSpotGraalCompiler compiler = (HotSpotGraalCompiler) jvmciRuntime.getCompiler();
         HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime();
         HotSpotCodeCacheProvider codeCache = graalRuntime.getHostProviders().getCodeCache();
-        OptionValues options = loadOptions(graalRuntime.getOptions());
+        OptionValues harnessOptions = loadHarnessOptions();
 
-        int iterations = Options.Iterations.getValue(options);
+        int iterations = Options.Iterations.getValue(harnessOptions);
         for (int i = 0; i < iterations; i++) {
             codeCache.resetCompilationStatistics();
             TTY.println("CompileTheWorld : iteration " + i);
 
-            CompileTheWorld ctw = new CompileTheWorld(jvmciRuntime, compiler, options);
+            CompileTheWorld ctw = new CompileTheWorld(jvmciRuntime, compiler, harnessOptions, graalRuntime.getOptions());
             ctw.compile();
         }
         // This is required as non-daemon threads can be started by class initializers