8150646: Add support for blocking compiles though whitebox API
authorsimonis
Thu, 03 Mar 2016 16:21:16 +0100
changeset 36597 ee256e343585
parent 36595 3322a76f3a00
child 36598 ecfd16d711d6
8150646: Add support for blocking compiles though whitebox API Reviewed-by: kvn, ppunegov, simonis, neliasso Contributed-by: nils.eliasson@oracle.com, volker.simonis@gmail.com
hotspot/src/share/vm/compiler/compileBroker.cpp
hotspot/src/share/vm/compiler/compileBroker.hpp
hotspot/src/share/vm/compiler/compilerDirectives.cpp
hotspot/src/share/vm/compiler/compilerDirectives.hpp
hotspot/src/share/vm/compiler/directivesParser.cpp
hotspot/src/share/vm/compiler/directivesParser.hpp
hotspot/src/share/vm/prims/whitebox.cpp
hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp
hotspot/src/share/vm/services/diagnosticCommand.cpp
hotspot/test/compiler/whitebox/BlockingCompilation.java
--- a/hotspot/src/share/vm/compiler/compileBroker.cpp	Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/compiler/compileBroker.cpp	Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -838,12 +838,8 @@
                                         const methodHandle& hot_method,
                                         int hot_count,
                                         const char* comment,
+                                        bool blocking,
                                         Thread* thread) {
-  // do nothing if compiler thread(s) is not available
-  if (!_initialized) {
-    return;
-  }
-
   guarantee(!method->is_abstract(), "cannot compile abstract methods");
   assert(method->method_holder()->is_instance_klass(),
          "sanity check");
@@ -916,7 +912,6 @@
 
   // Outputs from the following MutexLocker block:
   CompileTask* task     = NULL;
-  bool         blocking = false;
   CompileQueue* queue  = compile_queue(comp_level);
 
   // Acquire our lock.
@@ -946,9 +941,6 @@
       return;
     }
 
-    // Should this thread wait for completion of the compile?
-    blocking = is_compile_blocking();
-
 #if INCLUDE_JVMCI
     if (UseJVMCICompiler) {
       if (blocking) {
@@ -1034,11 +1026,28 @@
   }
 }
 
-
 nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci,
                                        int comp_level,
                                        const methodHandle& hot_method, int hot_count,
                                        const char* comment, Thread* THREAD) {
+  // do nothing if compilebroker is not available
+  if (!_initialized) {
+    return NULL;
+  }
+  AbstractCompiler *comp = CompileBroker::compiler(comp_level);
+  assert(comp != NULL, "Ensure we don't compile before compilebroker init");
+  DirectiveSet* directive = DirectivesStack::getMatchingDirective(method, comp);
+  nmethod* nm = CompileBroker::compile_method(method, osr_bci, comp_level, hot_method, hot_count, comment, directive, THREAD);
+  DirectivesStack::release(directive);
+  return nm;
+}
+
+nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci,
+                                         int comp_level,
+                                         const methodHandle& hot_method, int hot_count,
+                                         const char* comment, DirectiveSet* directive,
+                                         Thread* THREAD) {
+
   // make sure arguments make sense
   assert(method->method_holder()->is_instance_klass(), "not an instance method");
   assert(osr_bci == InvocationEntryBci || (0 <= osr_bci && osr_bci < method->code_size()), "bci out of range");
@@ -1051,8 +1060,8 @@
   // lock, make sure that the compilation
   // isn't prohibited in a straightforward way.
   AbstractCompiler *comp = CompileBroker::compiler(comp_level);
-  if (comp == NULL || !comp->can_compile_method(method) ||
-      compilation_is_prohibited(method, osr_bci, comp_level)) {
+  if (!comp->can_compile_method(method) ||
+      compilation_is_prohibited(method, osr_bci, comp_level, directive->ExcludeOption)) {
     return NULL;
   }
 
@@ -1160,7 +1169,7 @@
       CompilationPolicy::policy()->delay_compilation(method());
       return NULL;
     }
-    compile_method_base(method, osr_bci, comp_level, hot_method, hot_count, comment, THREAD);
+    compile_method_base(method, osr_bci, comp_level, hot_method, hot_count, comment, !directive->BackgroundCompilationOption, THREAD);
   }
 
   // return requested nmethod
@@ -1217,7 +1226,7 @@
 // CompileBroker::compilation_is_prohibited
 //
 // See if this compilation is not allowed.
-bool CompileBroker::compilation_is_prohibited(const methodHandle& method, int osr_bci, int comp_level) {
+bool CompileBroker::compilation_is_prohibited(const methodHandle& method, int osr_bci, int comp_level, bool excluded) {
   bool is_native = method->is_native();
   // Some compilers may not support the compilation of natives.
   AbstractCompiler *comp = compiler(comp_level);
@@ -1235,11 +1244,6 @@
     return true;
   }
 
-  // Breaking the abstraction - directives are only used inside a compilation otherwise.
-  DirectiveSet* directive = DirectivesStack::getMatchingDirective(method, comp);
-  bool excluded = directive->ExcludeOption;
-  DirectivesStack::release(directive);
-
   // The method may be explicitly excluded by the user.
   double scale;
   if (excluded || (CompilerOracle::has_option_value(method, "CompileThresholdScaling", scale) && scale == 0)) {
@@ -1304,16 +1308,6 @@
   return assign_compile_id(method, osr_bci);
 }
 
-/**
- * Should the current thread block until this compilation request
- * has been fulfilled?
- */
-bool CompileBroker::is_compile_blocking() {
-  assert(!InstanceRefKlass::owns_pending_list_lock(JavaThread::current()), "possible deadlock");
-  return !BackgroundCompilation;
-}
-
-
 // ------------------------------------------------------------------
 // CompileBroker::preload_classes
 void CompileBroker::preload_classes(const methodHandle& method, TRAPS) {
--- a/hotspot/src/share/vm/compiler/compileBroker.hpp	Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/compiler/compileBroker.hpp	Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -222,8 +222,7 @@
   static JavaThread* make_thread(const char* name, CompileQueue* queue, CompilerCounters* counters, AbstractCompiler* comp, bool compiler_thread, TRAPS);
   static void init_compiler_sweeper_threads(int c1_compiler_count, int c2_compiler_count);
   static bool compilation_is_complete  (const methodHandle& method, int osr_bci, int comp_level);
-  static bool compilation_is_prohibited(const methodHandle& method, int osr_bci, int comp_level);
-  static bool is_compile_blocking();
+  static bool compilation_is_prohibited(const methodHandle& method, int osr_bci, int comp_level, bool excluded);
   static void preload_classes          (const methodHandle& method, TRAPS);
 
   static CompileTask* create_compile_task(CompileQueue*       queue,
@@ -253,6 +252,7 @@
                                   const methodHandle& hot_method,
                                   int hot_count,
                                   const char* comment,
+                                  bool blocking,
                                   Thread* thread);
 
   static CompileQueue* compile_queue(int comp_level);
@@ -291,6 +291,15 @@
                                  int hot_count,
                                  const char* comment, Thread* thread);
 
+  static nmethod* compile_method(const methodHandle& method,
+                                   int osr_bci,
+                                   int comp_level,
+                                   const methodHandle& hot_method,
+                                   int hot_count,
+                                   const char* comment,
+                                   DirectiveSet* directive,
+                                   Thread* thread);
+
   // Acquire any needed locks and assign a compile id
   static uint assign_compile_id_unlocked(Thread* thread, const methodHandle& method, int osr_bci);
 
--- a/hotspot/src/share/vm/compiler/compilerDirectives.cpp	Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/compiler/compilerDirectives.cpp	Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -472,9 +472,12 @@
   _depth++;
 }
 
-void DirectivesStack::pop() {
+void DirectivesStack::pop(int count) {
   MutexLockerEx locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
-  pop_inner();
+  assert(count > -1, "No negative values");
+  for (int i = 0; i < count; i++) {
+    pop_inner();
+  }
 }
 
 void DirectivesStack::pop_inner() {
--- a/hotspot/src/share/vm/compiler/compilerDirectives.hpp	Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/compiler/compilerDirectives.hpp	Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -42,6 +42,7 @@
     cflags(PrintAssembly,           bool, PrintAssembly, PrintAssembly) \
     cflags(PrintInlining,           bool, PrintInlining, PrintInlining) \
     cflags(PrintNMethods,           bool, PrintNMethods, PrintNMethods) \
+    cflags(BackgroundCompilation,   bool, BackgroundCompilation, BackgroundCompilation) \
     cflags(ReplayInline,            bool, false, ReplayInline) \
     cflags(DumpReplay,              bool, false, DumpReplay) \
     cflags(DumpInline,              bool, false, DumpInline) \
@@ -87,7 +88,7 @@
   static DirectiveSet* getMatchingDirective(methodHandle mh, AbstractCompiler* comp);
   static DirectiveSet* getDefaultDirective(AbstractCompiler* comp);
   static void push(CompilerDirectives* directive);
-  static void pop();
+  static void pop(int count);
   static bool check_capacity(int request_size, outputStream* st);
   static void clear();
   static void print(outputStream* st);
--- a/hotspot/src/share/vm/compiler/directivesParser.cpp	Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/compiler/directivesParser.cpp	Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -55,7 +55,7 @@
   assert(_tmp_depth == 0, "Consistency");
 }
 
-bool DirectivesParser::parse_string(const char* text, outputStream* st) {
+int DirectivesParser::parse_string(const char* text, outputStream* st) {
   DirectivesParser cd(text, st);
   if (cd.valid()) {
     return cd.install_directives();
@@ -63,7 +63,7 @@
     cd.clean_tmp();
     st->flush();
     st->print_cr("Parsing of compiler directives failed");
-    return false;
+    return -1;
   }
 }
 
@@ -97,17 +97,17 @@
       buffer[num_read] = '\0';
       // close file
       os::close(file_handle);
-      return parse_string(buffer, stream);
+      return parse_string(buffer, stream) != -1;
     }
   }
   return false;
 }
 
-bool DirectivesParser::install_directives() {
+int DirectivesParser::install_directives() {
   // Check limit
   if (!DirectivesStack::check_capacity(_tmp_depth, _st)) {
     clean_tmp();
-    return false;
+    return 0;
   }
 
   // Pop from internal temporary stack and push to compileBroker.
@@ -120,14 +120,14 @@
   }
   if (i == 0) {
     _st->print_cr("No directives in file");
-    return false;
+    return 0;
   } else {
     _st->print_cr("%i compiler directives added", i);
     if (CompilerDirectivesPrint) {
       // Print entire directives stack after new has been pushed.
       DirectivesStack::print(_st);
     }
-    return true;
+    return i;
   }
 }
 
--- a/hotspot/src/share/vm/compiler/directivesParser.hpp	Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/compiler/directivesParser.hpp	Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -51,8 +51,8 @@
   static bool has_file();
   static bool parse_from_flag();
   static bool parse_from_file(const char* filename, outputStream* st);
-  static bool parse_string(const char* string, outputStream* st);
-  bool install_directives();
+  static int  parse_string(const char* string, outputStream* st);
+  int install_directives();
 
 private:
   DirectivesParser(const char* text, outputStream* st);
--- a/hotspot/src/share/vm/prims/whitebox.cpp	Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/prims/whitebox.cpp	Thu Mar 03 16:21:16 2016 +0100
@@ -30,6 +30,7 @@
 #include "classfile/stringTable.hpp"
 #include "code/codeCache.hpp"
 #include "compiler/methodMatcher.hpp"
+#include "compiler/directivesParser.hpp"
 #include "jvmtifiles/jvmtiEnv.hpp"
 #include "memory/metadataFactory.hpp"
 #include "memory/metaspaceShared.hpp"
@@ -636,6 +637,10 @@
 WB_END
 
 WB_ENTRY(jboolean, WB_EnqueueMethodForCompilation(JNIEnv* env, jobject o, jobject method, jint comp_level, jint bci))
+  // Screen for unavailable/bad comp level
+  if (CompileBroker::compiler(comp_level) == NULL) {
+    return false;
+  }
   jmethodID jmid = reflected_method_to_jmid(thread, env, method);
   CHECK_JNI_EXCEPTION_(env, JNI_FALSE);
   methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid));
@@ -1502,6 +1507,27 @@
   }
 }
 
+WB_ENTRY(jint, WB_AddCompilerDirective(JNIEnv* env, jobject o, jstring compDirect))
+  // can't be in VM when we call JNI
+  ThreadToNativeFromVM ttnfv(thread);
+  const char* dir = env->GetStringUTFChars(compDirect, NULL);
+  int ret;
+  {
+    ThreadInVMfromNative ttvfn(thread); // back to VM
+    ret = DirectivesParser::parse_string(dir, tty);
+  }
+  env->ReleaseStringUTFChars(compDirect, dir);
+  // -1 for error parsing directive. Return 0 as number of directives added.
+  if (ret == -1) {
+    ret = 0;
+  }
+  return (jint) ret;
+WB_END
+
+WB_ENTRY(void, WB_RemoveCompilerDirective(JNIEnv* env, jobject o, jint count))
+  DirectivesStack::pop(count);
+WB_END
+
 #define CC (char*)
 
 static JNINativeMethod methods[] = {
@@ -1677,6 +1703,9 @@
   {CC"isShared",           CC"(Ljava/lang/Object;)Z", (void*)&WB_IsShared },
   {CC"areSharedStringsIgnored",           CC"()Z",    (void*)&WB_AreSharedStringsIgnored },
   {CC"clearInlineCaches",  CC"()V",                   (void*)&WB_ClearInlineCaches },
+  {CC"addCompilerDirective",    CC"(Ljava/lang/String;)I",
+                                                      (void*)&WB_AddCompilerDirective },
+  {CC"removeCompilerDirective", CC"(I)V",             (void*)&WB_RemoveCompilerDirective },
 };
 
 #undef CC
--- a/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp	Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp	Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -177,9 +177,7 @@
 
 // Called with the queue locked and with at least one element
 CompileTask* AdvancedThresholdPolicy::select_task(CompileQueue* compile_queue) {
-#if INCLUDE_JVMCI
   CompileTask *max_blocking_task = NULL;
-#endif
   CompileTask *max_task = NULL;
   Method* max_method = NULL;
   jlong t = os::javaTimeMillis();
@@ -193,7 +191,8 @@
       max_method = method;
     } else {
       // If a method has been stale for some time, remove it from the queue.
-      if (is_stale(t, TieredCompileTaskTimeout, method) && !is_old(method)) {
+      // Blocking tasks don't become stale
+      if (!task->is_blocking() && is_stale(t, TieredCompileTaskTimeout, method) && !is_old(method)) {
         if (PrintTieredEvents) {
           print_event(REMOVE_FROM_QUEUE, method, method, task->osr_bci(), (CompLevel)task->comp_level());
         }
@@ -210,29 +209,25 @@
         max_method = method;
       }
     }
-#if INCLUDE_JVMCI
-    if (UseJVMCICompiler && task->is_blocking()) {
+
+    if (task->is_blocking()) {
       if (max_blocking_task == NULL || compare_methods(method, max_blocking_task->method())) {
         max_blocking_task = task;
       }
     }
-#endif
+
     task = next_task;
   }
 
-#if INCLUDE_JVMCI
-  if (UseJVMCICompiler) {
-    if (max_blocking_task != NULL) {
-      // In blocking compilation mode, the CompileBroker will make
-      // compilations submitted by a JVMCI compiler thread non-blocking. These
-      // compilations should be scheduled after all blocking compilations
-      // to service non-compiler related compilations sooner and reduce the
-      // chance of such compilations timing out.
-      max_task = max_blocking_task;
-      max_method = max_task->method();
-    }
+  if (max_blocking_task != NULL) {
+    // In blocking compilation mode, the CompileBroker will make
+    // compilations submitted by a JVMCI compiler thread non-blocking. These
+    // compilations should be scheduled after all blocking compilations
+    // to service non-compiler related compilations sooner and reduce the
+    // chance of such compilations timing out.
+    max_task = max_blocking_task;
+    max_method = max_task->method();
   }
-#endif
 
   if (max_task->comp_level() == CompLevel_full_profile && TieredStopAtLevel > CompLevel_full_profile
       && is_method_profiled(max_method)) {
--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp	Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp	Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -931,7 +931,7 @@
 }
 
 void CompilerDirectivesRemoveDCmd::execute(DCmdSource source, TRAPS) {
-  DirectivesStack::pop();
+  DirectivesStack::pop(1);
 }
 
 void CompilerDirectivesClearDCmd::execute(DCmdSource source, TRAPS) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/whitebox/BlockingCompilation.java	Thu Mar 03 16:21:16 2016 +0100
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2016 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8150646
+ * @summary Add support for blocking compiles through whitebox API
+ * @library /testlibrary /test/lib /
+ * @build sun.hotspot.WhiteBox
+ *        compiler.testlibrary.CompilerUtils
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ *                              sun.hotspot.WhiteBox$WhiteBoxPermission
+ *
+ * @run main/othervm
+ *        -Xbootclasspath/a:.
+ *        -Xmixed
+ *        -XX:+UnlockDiagnosticVMOptions
+ *        -XX:+WhiteBoxAPI
+ *        -XX:+PrintCompilation
+ *        -XX:CompileCommand=option,BlockingCompilation::foo,PrintInlining
+ *        BlockingCompilation
+ */
+
+import java.lang.reflect.Method;
+import java.util.Random;
+
+import sun.hotspot.WhiteBox;
+import compiler.testlibrary.CompilerUtils;
+
+public class BlockingCompilation {
+    private static final WhiteBox WB = WhiteBox.getWhiteBox();
+    private static final Random RANDOM = new Random();
+
+    public static int foo() {
+        return RANDOM.nextInt();
+    }
+
+    public static void main(String[] args) throws Exception {
+        long sum = 0;
+        int level = 0;
+        boolean enqued = false;
+        Method m = BlockingCompilation.class.getMethod("foo");
+        int[] levels = CompilerUtils.getAvailableCompilationLevels();
+
+        // If there are no compilers available these tests don't make any sense.
+        if (levels.length == 0) return;
+        int max_level = levels[levels.length - 1];
+
+        // Normal, non-blocking compilation
+        for (int i = 0; i < 500_000; i++) {
+            sum += foo();
+            if (!enqued && WB.isMethodQueuedForCompilation(m)) {
+                System.out.println("==> " + m + " enqued for compilation in iteration " + i);
+                enqued = true;
+            }
+            if (WB.isMethodCompiled(m)) {
+                if (WB.getMethodCompilationLevel(m) != level) {
+                    level = WB.getMethodCompilationLevel(m);
+                    System.out.println("==> " + m + " compiled at level " + level + " in iteration " + i);
+                    enqued = false;
+                    if (level == max_level) break;
+                }
+            }
+        }
+
+        // This is necessarry because WB.deoptimizeMethod doesn't clear the methods
+        // MDO and therefore level 3 compilations will be downgraded to level 2.
+        WB.clearMethodState(m);
+
+        // Blocking compilations on all levels, using the default versions of
+        // WB.enqueueMethodForCompilation() and manually setting compiler directives.
+        String directive = "[{ match: \"BlockingCompilation.foo\", BackgroundCompilation: false }]";
+        WB.addCompilerDirective(directive);
+
+        for (int l : levels) {
+            WB.deoptimizeMethod(m);
+            WB.enqueueMethodForCompilation(m, l);
+
+            if (!WB.isMethodCompiled(m) || WB.getMethodCompilationLevel(m) != l) {
+                String msg = m + " should be compiled at level " + l +
+                             "(but is actually compiled at level " +
+                             WB.getMethodCompilationLevel(m) + ")";
+                System.out.println("==> " + msg);
+                throw new Exception(msg);
+            }
+        }
+
+        WB.removeCompilerDirective(1);
+
+        WB.deoptimizeMethod(m);
+        WB.clearMethodState(m);
+        level = 0;
+        enqued = false;
+        int iteration = 0;
+
+        // Normal, non-blocking compilation
+        for (int i = 0; i < 500_000; i++) {
+            sum += foo();
+            if (!enqued && WB.isMethodQueuedForCompilation(m)) {
+                System.out.println("==> " + m + " enqued for compilation in iteration " + i);
+                iteration = i;
+                enqued = true;
+            }
+            if (WB.isMethodCompiled(m)) {
+                if (WB.getMethodCompilationLevel(m) != level) {
+                    level = WB.getMethodCompilationLevel(m);
+                    System.out.println("==> " + m + " compiled at level " + level + " in iteration " + i);
+                    if (level == 4 && iteration == i) {
+                        throw new Exception("This seems to be a blocking compilation although it shouldn't.");
+                    }
+                    enqued = false;
+                    if (level == max_level) break;
+                }
+            }
+        }
+    }
+}