--- old/src/share/vm/compiler/compilerDirectives.hpp 2016-03-11 14:44:40.738147685 +0100 +++ new/src/share/vm/compiler/compilerDirectives.hpp 2016-03-11 14:44:40.630147680 +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); --- old/src/share/vm/compiler/directivesParser.cpp 2016-03-11 14:44:40.742147685 +0100 +++ new/src/share/vm/compiler/directivesParser.cpp 2016-03-11 14:44:40.634147680 +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; } } --- old/src/share/vm/compiler/compileBroker.hpp 2016-03-11 14:44:40.750147685 +0100 +++ new/src/share/vm/compiler/compileBroker.hpp 2016-03-11 14:44:40.642147681 +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); --- /dev/null 2016-01-16 19:27:27.460446328 +0100 +++ new/test/compiler/whitebox/BlockingCompilation.java 2016-03-11 14:44:40.642147681 +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; + } + } + } + } +} --- old/src/share/vm/compiler/compilerDirectives.cpp 2016-03-11 14:44:40.770147686 +0100 +++ new/src/share/vm/compiler/compilerDirectives.cpp 2016-03-11 14:44:40.666147682 +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() { --- old/src/share/vm/services/diagnosticCommand.cpp 2016-03-11 14:44:40.786147687 +0100 +++ new/src/share/vm/services/diagnosticCommand.cpp 2016-03-11 14:44:40.682147682 +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) { --- old/src/share/vm/runtime/advancedThresholdPolicy.cpp 2016-03-11 14:44:40.794147687 +0100 +++ new/src/share/vm/runtime/advancedThresholdPolicy.cpp 2016-03-11 14:44:40.642147681 +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)) { --- old/src/share/vm/compiler/directivesParser.hpp 2016-03-11 14:44:40.798147687 +0100 +++ new/src/share/vm/compiler/directivesParser.hpp 2016-03-11 14:44:40.650147681 +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); --- old/src/share/vm/compiler/compileBroker.cpp 2016-03-11 14:44:40.806147688 +0100 +++ new/src/share/vm/compiler/compileBroker.cpp 2016-03-11 14:44:40.662147682 +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) { --- old/src/share/vm/prims/whitebox.cpp 2016-03-11 14:44:40.822147688 +0100 +++ new/src/share/vm/prims/whitebox.cpp 2016-03-11 14:44:40.666147682 +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" @@ -1502,6 +1503,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 +1699,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