# HG changeset patch # User simonis # Date 1456936011 -3600 # Wed Mar 02 17:26:51 2016 +0100 # Node ID 6518734e19d2ee1cc2fc7edbb7ef00bf1dc467a0 # Parent 13d02d8f961664611eaf4693f6c9dd449c87b631 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 diff --git a/src/share/vm/compiler/compileBroker.cpp b/src/share/vm/compiler/compileBroker.cpp --- a/src/share/vm/compiler/compileBroker.cpp +++ b/src/share/vm/compiler/compileBroker.cpp @@ -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,6 +838,7 @@ 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) { @@ -916,7 +917,6 @@ // Outputs from the following MutexLocker block: CompileTask* task = NULL; - bool blocking = false; CompileQueue* queue = compile_queue(comp_level); // Acquire our lock. @@ -946,9 +946,6 @@ return; } - // Should this thread wait for completion of the compile? - blocking = is_compile_blocking(); - #if INCLUDE_JVMCI if (UseJVMCICompiler) { if (blocking) { @@ -1034,11 +1031,23 @@ } } - 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) { + AbstractCompiler *comp = CompileBroker::compiler(comp_level); + 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"); @@ -1052,7 +1061,7 @@ // 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)) { + 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) { diff --git a/src/share/vm/compiler/compileBroker.hpp b/src/share/vm/compiler/compileBroker.hpp --- a/src/share/vm/compiler/compileBroker.hpp +++ b/src/share/vm/compiler/compileBroker.hpp @@ -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); diff --git a/src/share/vm/compiler/compilerDirectives.cpp b/src/share/vm/compiler/compilerDirectives.cpp --- a/src/share/vm/compiler/compilerDirectives.cpp +++ b/src/share/vm/compiler/compilerDirectives.cpp @@ -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() { diff --git a/src/share/vm/compiler/compilerDirectives.hpp b/src/share/vm/compiler/compilerDirectives.hpp --- a/src/share/vm/compiler/compilerDirectives.hpp +++ b/src/share/vm/compiler/compilerDirectives.hpp @@ -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); diff --git a/src/share/vm/compiler/directivesParser.cpp b/src/share/vm/compiler/directivesParser.cpp --- a/src/share/vm/compiler/directivesParser.cpp +++ b/src/share/vm/compiler/directivesParser.cpp @@ -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 0; } } @@ -103,11 +103,11 @@ 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; } } diff --git a/src/share/vm/compiler/directivesParser.hpp b/src/share/vm/compiler/directivesParser.hpp --- a/src/share/vm/compiler/directivesParser.hpp +++ b/src/share/vm/compiler/directivesParser.hpp @@ -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); diff --git a/src/share/vm/prims/whitebox.cpp b/src/share/vm/prims/whitebox.cpp --- a/src/share/vm/prims/whitebox.cpp +++ b/src/share/vm/prims/whitebox.cpp @@ -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,23 @@ } } +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); + 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 +1695,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 diff --git a/src/share/vm/runtime/advancedThresholdPolicy.cpp b/src/share/vm/runtime/advancedThresholdPolicy.cpp --- a/src/share/vm/runtime/advancedThresholdPolicy.cpp +++ b/src/share/vm/runtime/advancedThresholdPolicy.cpp @@ -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 @@ -164,9 +164,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(); @@ -180,7 +178,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()); } @@ -197,29 +196,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)) { diff --git a/src/share/vm/services/diagnosticCommand.cpp b/src/share/vm/services/diagnosticCommand.cpp --- a/src/share/vm/services/diagnosticCommand.cpp +++ b/src/share/vm/services/diagnosticCommand.cpp @@ -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) { diff --git a/test/compiler/whitebox/BlockingCompilation.java b/test/compiler/whitebox/BlockingCompilation.java new file mode 100644 --- /dev/null +++ b/test/compiler/whitebox/BlockingCompilation.java @@ -0,0 +1,148 @@ +/* + * 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 + * @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; + +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"); + + // 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 == 4) 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 + // blocking versions of WB.enqueueMethodForCompilation() + for (level = 1; level <= 4; level++) { + WB.deoptimizeMethod(m); + WB.enqueueMethodForCompilation(m, level, true); + + if (!WB.isMethodCompiled(m) || WB.getMethodCompilationLevel(m) != level) { + String msg = m + " should be compiled at level " + level + + "(but is actually compiled at level " + + WB.getMethodCompilationLevel(m) + ")"; + System.out.println("==> " + msg); + throw new Exception(msg); + } + } + + WB.deoptimizeMethod(m); + 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 (level = 1; level <= 4; level++) { + WB.deoptimizeMethod(m); + WB.enqueueMethodForCompilation(m, level); + + if (!WB.isMethodCompiled(m) || WB.getMethodCompilationLevel(m) != level) { + String msg = m + " should be compiled at level " + level + + "(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 == 4) break; + } + } + } + } +}