8137167: JEP165: Compiler Control: Implementation task
Summary: Compiler Control JEP
Reviewed-by: roland, twisti, zmajo, simonis
--- a/hotspot/src/share/vm/c1/c1_Compilation.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/c1/c1_Compilation.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -34,6 +34,7 @@
#include "c1/c1_ValueStack.hpp"
#include "code/debugInfoRec.hpp"
#include "compiler/compileLog.hpp"
+#include "compiler/compilerDirectives.hpp"
#include "runtime/sharedRuntime.hpp"
typedef enum {
@@ -418,9 +419,9 @@
exception_handler_table(),
implicit_exception_table(),
compiler(),
- _env->comp_level(),
has_unsafe_access(),
- SharedRuntime::is_wide_vector(max_vector_size())
+ SharedRuntime::is_wide_vector(max_vector_size()),
+ directive()
);
}
@@ -445,7 +446,7 @@
dependency_recorder()->assert_evol_method(method());
}
- if (method()->break_at_execute()) {
+ if (directive()->BreakAtCompileOption) {
BREAKPOINT;
}
@@ -534,9 +535,10 @@
Compilation::Compilation(AbstractCompiler* compiler, ciEnv* env, ciMethod* method,
- int osr_bci, BufferBlob* buffer_blob)
+ int osr_bci, BufferBlob* buffer_blob, DirectiveSet* directive)
: _compiler(compiler)
, _env(env)
+, _directive(directive)
, _log(env->log())
, _method(method)
, _osr_bci(osr_bci)
@@ -587,7 +589,6 @@
_env->set_compiler_data(NULL);
}
-
void Compilation::add_exception_handlers_for_pco(int pco, XHandlers* exception_handlers) {
#ifndef PRODUCT
if (PrintExceptionHandlers && Verbose) {
--- a/hotspot/src/share/vm/c1/c1_Compilation.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/c1/c1_Compilation.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -67,6 +67,7 @@
int _next_id;
int _next_block_id;
AbstractCompiler* _compiler;
+ DirectiveSet* _directive;
ciEnv* _env;
CompileLog* _log;
ciMethod* _method;
@@ -118,7 +119,7 @@
public:
// creation
Compilation(AbstractCompiler* compiler, ciEnv* env, ciMethod* method,
- int osr_bci, BufferBlob* buffer_blob);
+ int osr_bci, BufferBlob* buffer_blob, DirectiveSet* directive);
~Compilation();
@@ -128,6 +129,7 @@
// accessors
ciEnv* env() const { return _env; }
+ DirectiveSet* directive() const { return _directive; }
CompileLog* log() const { return _log; }
AbstractCompiler* compiler() const { return _compiler; }
bool has_exception_handlers() const { return _has_exception_handlers; }
--- a/hotspot/src/share/vm/c1/c1_Compiler.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/c1/c1_Compiler.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -238,7 +238,7 @@
return true;
}
-void Compiler::compile_method(ciEnv* env, ciMethod* method, int entry_bci) {
+void Compiler::compile_method(ciEnv* env, ciMethod* method, int entry_bci, DirectiveSet* directive) {
BufferBlob* buffer_blob = CompilerThread::current()->get_buffer_blob();
assert(buffer_blob != NULL, "Must exist");
// invoke compilation
@@ -247,7 +247,7 @@
// of Compilation to occur before we release the any
// competing compiler thread
ResourceMark rm;
- Compilation c(this, env, method, entry_bci, buffer_blob);
+ Compilation c(this, env, method, entry_bci, buffer_blob, directive);
}
}
--- a/hotspot/src/share/vm/c1/c1_Compiler.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/c1/c1_Compiler.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -26,6 +26,7 @@
#define SHARE_VM_C1_C1_COMPILER_HPP
#include "compiler/abstractCompiler.hpp"
+#include "compiler/compilerDirectives.hpp"
// There is one instance of the Compiler per CompilerThread.
@@ -50,7 +51,7 @@
virtual void initialize();
// Compilation entry point for methods
- virtual void compile_method(ciEnv* env, ciMethod* target, int entry_bci);
+ virtual void compile_method(ciEnv* env, ciMethod* target, int entry_bci, DirectiveSet* directive);
// Print compilation timers and statistics
virtual void print_timers();
--- a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -3365,7 +3365,7 @@
// negative filter: should callee NOT be inlined? returns NULL, ok to inline, or rejection msg
const char* GraphBuilder::should_not_inline(ciMethod* callee) const {
- if ( callee->should_not_inline()) return "disallowed by CompileCommand";
+ if ( compilation()->directive()->should_not_inline(callee)) return "disallowed by CompileCommand";
if ( callee->dont_inline()) return "don't inline by annotation";
return NULL;
}
@@ -3494,8 +3494,7 @@
{
VM_ENTRY_MARK;
methodHandle mh(THREAD, callee->get_Method());
- methodHandle ct(THREAD, method()->get_Method());
- is_available = _compilation->compiler()->is_intrinsic_available(mh, ct);
+ is_available = _compilation->compiler()->is_intrinsic_available(mh, _compilation->directive());
}
if (!is_available) {
@@ -3690,13 +3689,14 @@
}
// now perform tests that are based on flag settings
- if (callee->force_inline() || callee->should_inline()) {
+ bool inlinee_by_directive = compilation()->directive()->should_inline(callee);
+ if (callee->force_inline() || inlinee_by_directive) {
if (inline_level() > MaxForceInlineLevel ) INLINE_BAILOUT("MaxForceInlineLevel");
if (recursive_inline_level(callee) > MaxRecursiveInlineLevel) INLINE_BAILOUT("recursive inlining too deep");
const char* msg = "";
if (callee->force_inline()) msg = "force inline by annotation";
- if (callee->should_inline()) msg = "force inline by CompileCommand";
+ if (inlinee_by_directive) msg = "force inline by CompileCommand";
print_inlining(callee, msg);
} else {
// use heuristic controls on inlining
@@ -4207,7 +4207,8 @@
event.commit();
}
#endif // INCLUDE_TRACE
- if (!PrintInlining && !compilation()->method()->has_option("PrintInlining")) {
+
+ if (!compilation()->directive()->PrintInliningOption) {
return;
}
CompileTask::print_inlining_tty(callee, scope()->level(), bci(), msg);
--- a/hotspot/src/share/vm/ci/ciEnv.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/ci/ciEnv.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -38,7 +38,8 @@
#include "code/scopeDesc.hpp"
#include "compiler/compileBroker.hpp"
#include "compiler/compileLog.hpp"
-#include "compiler/compilerOracle.hpp"
+#include "compiler/compilerDirectives.hpp"
+#include "compiler/disassembler.hpp"
#include "gc/shared/collectedHeap.inline.hpp"
#include "interpreter/linkResolver.hpp"
#include "memory/allocation.inline.hpp"
@@ -956,9 +957,9 @@
ExceptionHandlerTable* handler_table,
ImplicitExceptionTable* inc_table,
AbstractCompiler* compiler,
- int comp_level,
bool has_unsafe_access,
bool has_wide_vectors,
+ DirectiveSet* directives,
RTMState rtm_state) {
VM_ENTRY_MARK;
nmethod* nm = NULL;
@@ -1034,11 +1035,20 @@
debug_info(), dependencies(), code_buffer,
frame_words, oop_map_set,
handler_table, inc_table,
- compiler, comp_level);
+ compiler, task()->comp_level());
+
// Free codeBlobs
code_buffer->free_blob();
if (nm != NULL) {
+ bool printnmethods = directives->PrintAssemblyOption || directives->PrintNMethodsOption;
+ if (printnmethods || PrintDebugInfo || PrintRelocations || PrintDependencies || PrintExceptionHandlers) {
+ nm->print_nmethod(printnmethods);
+ }
+ if (directives->PrintAssemblyOption) {
+ Disassembler::decode(nm);
+ }
+
nm->set_has_unsafe_access(has_unsafe_access);
nm->set_has_wide_vectors(has_wide_vectors);
#if INCLUDE_RTM_OPT
@@ -1069,7 +1079,7 @@
char *method_name = method->name_and_sig_as_C_string();
ttyLocker ttyl;
tty->print_cr("Installing method (%d) %s ",
- comp_level,
+ task()->comp_level(),
method_name);
}
// Allow the code to be executed
@@ -1080,7 +1090,7 @@
char *method_name = method->name_and_sig_as_C_string();
ttyLocker ttyl;
tty->print_cr("Installing osr method (%d) %s @ %d",
- comp_level,
+ task()->comp_level(),
method_name,
entry_bci);
}
--- a/hotspot/src/share/vm/ci/ciEnv.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/ci/ciEnv.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -32,9 +32,11 @@
#include "code/dependencies.hpp"
#include "code/exceptionHandlerTable.hpp"
#include "compiler/oopMap.hpp"
+#include "compiler/compilerDirectives.hpp"
#include "runtime/thread.hpp"
class CompileTask;
+class DirectiveSet;
// ciEnv
//
@@ -352,6 +354,7 @@
// The compiler task which has created this env.
// May be useful to find out compile_id, comp_level, etc.
CompileTask* task() { return _task; }
+
// Handy forwards to the task:
int comp_level(); // task()->comp_level()
uint compile_id(); // task()->compile_id()
@@ -367,9 +370,9 @@
ExceptionHandlerTable* handler_table,
ImplicitExceptionTable* inc_table,
AbstractCompiler* compiler,
- int comp_level,
bool has_unsafe_access,
bool has_wide_vectors,
+ DirectiveSet* directives,
RTMState rtm_state = NoRTM);
--- a/hotspot/src/share/vm/ci/ciMethod.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/ci/ciMethod.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -35,7 +35,6 @@
#include "ci/ciUtilities.hpp"
#include "classfile/systemDictionary.hpp"
#include "compiler/abstractCompiler.hpp"
-#include "compiler/compilerOracle.hpp"
#include "compiler/methodLiveness.hpp"
#include "interpreter/interpreter.hpp"
#include "interpreter/linkResolver.hpp"
@@ -1044,51 +1043,6 @@
}
// ------------------------------------------------------------------
-// ciMethod::should_inline
-//
-// Should this method be inlined during compilation?
-bool ciMethod::should_inline() {
- check_is_loaded();
- VM_ENTRY_MARK;
- methodHandle mh(THREAD, get_Method());
- return CompilerOracle::should_inline(mh);
-}
-
-// ------------------------------------------------------------------
-// ciMethod::should_not_inline
-//
-// Should this method be disallowed from inlining during compilation?
-bool ciMethod::should_not_inline() {
- check_is_loaded();
- VM_ENTRY_MARK;
- methodHandle mh(THREAD, get_Method());
- return CompilerOracle::should_not_inline(mh);
-}
-
-// ------------------------------------------------------------------
-// ciMethod::should_print_assembly
-//
-// Should the compiler print the generated code for this method?
-bool ciMethod::should_print_assembly() {
- check_is_loaded();
- VM_ENTRY_MARK;
- methodHandle mh(THREAD, get_Method());
- return CompilerOracle::should_print(mh);
-}
-
-// ------------------------------------------------------------------
-// ciMethod::break_at_execute
-//
-// Should the compiler insert a breakpoint into the generated code
-// method?
-bool ciMethod::break_at_execute() {
- check_is_loaded();
- VM_ENTRY_MARK;
- methodHandle mh(THREAD, get_Method());
- return CompilerOracle::should_break_at(mh);
-}
-
-// ------------------------------------------------------------------
// ciMethod::has_option
//
bool ciMethod::has_option(const char* option) {
@@ -1101,20 +1055,12 @@
// ------------------------------------------------------------------
// ciMethod::has_option_value
//
-template<typename T>
-bool ciMethod::has_option_value(const char* option, T& value) {
+bool ciMethod::has_option_value(const char* option, double& value) {
check_is_loaded();
VM_ENTRY_MARK;
methodHandle mh(THREAD, get_Method());
return CompilerOracle::has_option_value(mh, option, value);
}
-// Explicit instantiation for all OptionTypes supported.
-template bool ciMethod::has_option_value<intx>(const char* option, intx& value);
-template bool ciMethod::has_option_value<uintx>(const char* option, uintx& value);
-template bool ciMethod::has_option_value<bool>(const char* option, bool& value);
-template bool ciMethod::has_option_value<ccstr>(const char* option, ccstr& value);
-template bool ciMethod::has_option_value<double>(const char* option, double& value);
-
// ------------------------------------------------------------------
// ciMethod::can_be_compiled
//
--- a/hotspot/src/share/vm/ci/ciMethod.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/ci/ciMethod.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -104,8 +104,6 @@
void load_code();
- void check_is_loaded() const { assert(is_loaded(), "not loaded"); }
-
bool ensure_method_data(methodHandle h_m);
void code_at_put(int bci, Bytecodes::Code code) {
@@ -120,6 +118,8 @@
void assert_call_type_ok(int bci);
public:
+ void check_is_loaded() const { assert(is_loaded(), "not loaded"); }
+
// Basic method information.
ciFlags flags() const { check_is_loaded(); return _flags; }
ciSymbol* name() const { return _name; }
@@ -265,14 +265,8 @@
// Find the proper vtable index to invoke this method.
int resolve_vtable_index(ciKlass* caller, ciKlass* receiver);
- // Compilation directives
- bool should_inline();
- bool should_not_inline();
- bool should_print_assembly();
- bool break_at_execute();
bool has_option(const char *option);
- template<typename T>
- bool has_option_value(const char* option, T& value);
+ bool has_option_value(const char* option, double& value);
bool can_be_compiled();
bool can_be_osr_compiled(int entry_bci);
void set_not_compilable(const char* reason = NULL);
--- a/hotspot/src/share/vm/classfile/vmSymbols.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/classfile/vmSymbols.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -417,36 +417,10 @@
}
}
-bool vmIntrinsics::is_disabled_by_flags(methodHandle method, methodHandle compilation_context) {
+bool vmIntrinsics::is_disabled_by_flags(methodHandle method) {
vmIntrinsics::ID id = method->intrinsic_id();
assert(id != vmIntrinsics::_none, "must be a VM intrinsic");
- // Check if the intrinsic corresponding to 'method' has been disabled on
- // the command line by using the DisableIntrinsic flag (either globally
- // or on a per-method level, see src/share/vm/compiler/abstractCompiler.hpp
- // for details).
- // Usually, the compilation context is the caller of the method 'method'.
- // The only case when for a non-recursive method 'method' the compilation context
- // is not the caller of the 'method' (but it is the method itself) is
- // java.lang.ref.Referene::get.
- // For java.lang.ref.Reference::get, the intrinsic version is used
- // instead of the compiled version so that the value in the referent
- // field can be registered by the G1 pre-barrier code. The intrinsified
- // version of Reference::get also adds a memory barrier to prevent
- // commoning reads from the referent field across safepoint since GC
- // can change the referent field's value. See Compile::Compile()
- // in src/share/vm/opto/compile.cpp or
- // GraphBuilder::GraphBuilder() in src/share/vm/c1/c1_GraphBuilder.cpp
- // for more details.
- ccstr disable_intr = NULL;
- if ((DisableIntrinsic[0] != '\0' && strstr(DisableIntrinsic, vmIntrinsics::name_at(id)) != NULL) ||
- (!compilation_context.is_null() &&
- CompilerOracle::has_option_value(compilation_context, "DisableIntrinsic", disable_intr) &&
- strstr(disable_intr, vmIntrinsics::name_at(id)) != NULL)
- ) {
- return true;
- }
-
// -XX:-InlineNatives disables nearly all intrinsics except the ones listed in
// the following switch statement.
if (!InlineNatives) {
--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -1402,7 +1402,7 @@
// Returns true if a compiler intrinsic is disabled by command-line flags
// and false otherwise.
- static bool is_disabled_by_flags(methodHandle method, methodHandle compilation_context);
+ static bool is_disabled_by_flags(methodHandle method);
};
#endif // SHARE_VM_CLASSFILE_VMSYMBOLS_HPP
--- a/hotspot/src/share/vm/code/nmethod.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/code/nmethod.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -32,7 +32,7 @@
#include "compiler/abstractCompiler.hpp"
#include "compiler/compileBroker.hpp"
#include "compiler/compileLog.hpp"
-#include "compiler/compilerOracle.hpp"
+#include "compiler/compilerDirectives.hpp"
#include "compiler/disassembler.hpp"
#include "interpreter/bytecode.hpp"
#include "oops/methodData.hpp"
@@ -582,9 +582,6 @@
basic_lock_owner_sp_offset,
basic_lock_sp_offset, oop_maps);
NOT_PRODUCT(if (nm != NULL) native_nmethod_stats.note_native_nmethod(nm));
- if ((PrintAssembly || CompilerOracle::should_print(method)) && nm != NULL) {
- Disassembler::decode(nm);
- }
}
// verify nmethod
debug_only(if (nm) nm->verify();) // might block
@@ -666,9 +663,6 @@
}
}
NOT_PRODUCT(if (nm != NULL) note_java_nmethod(nm));
- if (PrintAssembly || CompilerOracle::has_option_string(method, "PrintAssembly")) {
- Disassembler::decode(nm);
- }
}
}
// Do verification and logging outside CodeCache_lock.
@@ -908,13 +902,6 @@
_method->is_static() == (entry_point() == _verified_entry_point),
" entry points must be same for static methods and vice versa");
}
-
- bool printnmethods = PrintNMethods || PrintNMethodsAtLevel == _comp_level
- || CompilerOracle::should_print(_method)
- || CompilerOracle::has_option_string(_method, "PrintNMethods");
- if (printnmethods || PrintDebugInfo || PrintRelocations || PrintDependencies || PrintExceptionHandlers) {
- print_nmethod(printnmethods);
- }
}
// Print a short set of xml attributes to identify this nmethod. The
--- a/hotspot/src/share/vm/compiler/abstractCompiler.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/compiler/abstractCompiler.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -26,6 +26,7 @@
#define SHARE_VM_COMPILER_ABSTRACTCOMPILER_HPP
#include "ci/compilerInterface.hpp"
+#include "compiler/compilerDirectives.hpp"
typedef void (*initializer)(void);
@@ -114,36 +115,33 @@
// Determine if the current compiler provides an intrinsic
// for method 'method'. An intrinsic is available if:
- // - the intrinsic is enabled (by using the appropriate command-line flag) and
+ // - the intrinsic is enabled (by using the appropriate command-line flag,
+ // the command-line compile ommand, or a compiler directive)
// - the platform on which the VM is running supports the intrinsic
// (i.e., the platform provides the instructions necessary for the compiler
// to generate the intrinsic code).
//
- // The second parameter, 'compilation_context', is needed to implement functionality
- // related to the DisableIntrinsic command-line flag. The DisableIntrinsic flag can
- // be used to prohibit the compilers to use an intrinsic. There are three ways to
- // disable an intrinsic using the DisableIntrinsic flag:
+ // The directive provides the compilation context and includes pre-evaluated values
+ // dependent on VM flags, compile commands, and compiler directives.
//
- // (1) -XX:DisableIntrinsic=_hashCode,_getClass
- // Disables intrinsification of _hashCode and _getClass globally
- // (i.e., the intrinsified version the methods will not be used at all).
- // (2) -XX:CompileCommand=option,aClass::aMethod,ccstr,DisableIntrinsic,_hashCode
- // Disables intrinsification of _hashCode if it is called from
- // aClass::aMethod (but not for any other call site of _hashCode)
- // (3) -XX:CompileCommand=option,java.lang.ref.Reference::get,ccstr,DisableIntrinsic,_Reference_get
- // Some methods are not compiled by C2. Instead, the C2 compiler
- // returns directly the intrinsified version of these methods.
- // The command above forces C2 to compile _Reference_get, but
- // allows using the intrinsified version of _Reference_get at all
- // other call sites.
- //
- // From the modes above, (1) disable intrinsics globally, (2) and (3)
- // disable intrinsics on a per-method basis. In cases (2) and (3) the
- // compilation context is aClass::aMethod and java.lang.ref.Reference::get,
- // respectively.
- virtual bool is_intrinsic_available(methodHandle method, methodHandle compilation_context) {
+ // Usually, the compilation context is the caller of the method 'method'.
+ // The only case when for a non-recursive method 'method' the compilation context
+ // is not the caller of the 'method' (but it is the method itself) is
+ // java.lang.ref.Referene::get.
+ // For java.lang.ref.Reference::get, the intrinsic version is used
+ // instead of the compiled version so that the value in the referent
+ // field can be registered by the G1 pre-barrier code. The intrinsified
+ // version of Reference::get also adds a memory barrier to prevent
+ // commoning reads from the referent field across safepoint since GC
+ // can change the referent field's value. See Compile::Compile()
+ // in src/share/vm/opto/compile.cpp or
+ // GraphBuilder::GraphBuilder() in src/share/vm/c1/c1_GraphBuilder.cpp
+ // for more details.
+
+ virtual bool is_intrinsic_available(methodHandle method, DirectiveSet* directive) {
return is_intrinsic_supported(method) &&
- !vmIntrinsics::is_disabled_by_flags(method, compilation_context);
+ !directive->is_intrinsic_disabled(method) &&
+ !vmIntrinsics::is_disabled_by_flags(method);
}
// Determines if an intrinsic is supported by the compiler, that is,
@@ -176,7 +174,7 @@
void set_state (int state);
void set_shut_down () { set_state(shut_down); }
// Compilation entry point for methods
- virtual void compile_method(ciEnv* env, ciMethod* target, int entry_bci) {
+ virtual void compile_method(ciEnv* env, ciMethod* target, int entry_bci, DirectiveSet* directive) {
ShouldNotReachHere();
}
--- a/hotspot/src/share/vm/compiler/compileBroker.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/compiler/compileBroker.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -29,6 +29,7 @@
#include "compiler/compileBroker.hpp"
#include "compiler/compileLog.hpp"
#include "compiler/compilerOracle.hpp"
+#include "compiler/directivesParser.hpp"
#include "interpreter/linkResolver.hpp"
#include "memory/allocation.inline.hpp"
#include "oops/methodData.hpp"
@@ -202,10 +203,22 @@
static CompilationLog* _compilation_log = NULL;
-void compileBroker_init() {
+bool compileBroker_init() {
if (LogEvents) {
_compilation_log = new CompilationLog();
}
+
+ // init directives stack, adding default directive
+ DirectivesStack::init();
+
+ if (DirectivesParser::has_file()) {
+ return DirectivesParser::parse_from_flag();
+ } else if (PrintCompilerDirectives) {
+ // Print default directive even when no other was added
+ DirectivesStack::print(tty);
+ }
+
+ return true;
}
CompileTaskWrapper::CompileTaskWrapper(CompileTask* task) {
@@ -1180,11 +1193,15 @@
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.
- bool quietly;
double scale;
- if (CompilerOracle::should_exclude(method, quietly)
- || (CompilerOracle::has_option_value(method, "CompileThresholdScaling", scale) && scale == 0)) {
+ if (excluded || (CompilerOracle::has_option_value(method, "CompileThresholdScaling", scale) && scale == 0)) {
+ bool quietly = CompilerOracle::should_exclude_quietly();
if (!quietly) {
// This does not happen quietly...
ResourceMark rm;
@@ -1194,7 +1211,7 @@
method->print_short_name(tty);
tty->cr();
}
- method->set_not_compilable(CompLevel_all, !quietly, "excluded by CompileCommand");
+ method->set_not_compilable(comp_level, !quietly, "excluded by CompileCommand");
}
return false;
@@ -1357,7 +1374,6 @@
ThreadInVMfromNative tv(thread);
ResetNoHandleMark rnhm;
-
if (!comp->is_shark()) {
// Perform per-thread and global initializations
comp->initialize();
@@ -1629,6 +1645,10 @@
}
}
+int DirectivesStack::_depth = 0;
+CompilerDirectives* DirectivesStack::_top = NULL;
+CompilerDirectives* DirectivesStack::_bottom = NULL;
+
// ------------------------------------------------------------------
// CompileBroker::invoke_compiler_on_method
//
@@ -1655,16 +1675,20 @@
bool should_log = (thread->log() != NULL);
bool should_break = false;
int task_level = task->comp_level();
+
+ // Look up matching directives
+ DirectiveSet* directive = DirectivesStack::getMatchingDirective(task->method(), compiler(task_level));
+
+ should_break = directive->BreakAtExecuteOption || task->check_break_at_flags();
+ if (should_log && !directive->LogOption) {
+ should_log = false;
+ }
{
// create the handle inside it's own block so it can't
// accidentally be referenced once the thread transitions to
// native. The NoHandleMark before the transition should catch
// any cases where this occurs in the future.
methodHandle method(thread, task->method());
- should_break = check_break_at(method, compile_id, is_osr);
- if (should_log && !CompilerOracle::should_log(method)) {
- should_log = false;
- }
assert(!method->is_native(), "no longer compile natives");
// Save information about this method in case of failure.
@@ -1732,7 +1756,7 @@
locker.wait(Mutex::_no_safepoint_check_flag);
}
}
- comp->compile_method(&ci_env, target, osr_bci);
+ comp->compile_method(&ci_env, target, osr_bci, directive);
}
if (!ci_env.failing() && task->code() == NULL) {
@@ -1762,6 +1786,7 @@
post_compile(thread, task, event, !ci_env.failing(), &ci_env);
}
+ DirectivesStack::release(directive);
pop_jni_handle_block();
methodHandle method(thread, task->method());
@@ -1947,21 +1972,6 @@
JNIHandleBlock::release_block(compile_handles, thread); // may block
}
-
-// ------------------------------------------------------------------
-// CompileBroker::check_break_at
-//
-// Should the compilation break at the current compilation.
-bool CompileBroker::check_break_at(methodHandle method, int compile_id, bool is_osr) {
- if (CICountOSR && is_osr && (compile_id == CIBreakAtOSR)) {
- return true;
- } else if( CompilerOracle::should_break_at(method) ) { // break when compiling
- return true;
- } else {
- return (compile_id == CIBreakAt);
- }
-}
-
// ------------------------------------------------------------------
// CompileBroker::collect_statistics
//
@@ -2232,3 +2242,4 @@
st->cr();
#endif
}
+
--- a/hotspot/src/share/vm/compiler/compileBroker.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/compiler/compileBroker.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -28,8 +28,10 @@
#include "ci/compilerInterface.hpp"
#include "compiler/abstractCompiler.hpp"
#include "compiler/compileTask.hpp"
+#include "compiler/compilerDirectives.hpp"
#include "runtime/perfData.hpp"
#include "trace/tracing.hpp"
+#include "utilities/stack.hpp"
class nmethod;
class nmethodLocker;
@@ -129,13 +131,12 @@
~CompileTaskWrapper();
};
-
// Compilation
//
// The broker for all compilation requests.
class CompileBroker: AllStatic {
friend class Threads;
- friend class CompileTaskWrapper;
+ friend class CompileTaskWrapper;
public:
enum {
@@ -238,7 +239,6 @@
static void set_last_compile(CompilerThread *thread, methodHandle method, bool is_osr, int comp_level);
static void push_jni_handle_block();
static void pop_jni_handle_block();
- static bool check_break_at(methodHandle method, int compile_id, bool is_osr);
static void collect_statistics(CompilerThread* thread, elapsedTimer time, CompileTask* task);
static void compile_method_base(methodHandle method,
@@ -253,7 +253,11 @@
static bool init_compiler_runtime();
static void shutdown_compiler_runtime(AbstractCompiler* comp, CompilerThread* thread);
- public:
+public:
+
+ static DirectivesStack* dirstack();
+ static void set_dirstack(DirectivesStack* stack);
+
enum {
// The entry bci used for non-OSR compilations.
standard_entry_bci = InvocationEntryBci
@@ -267,6 +271,7 @@
static bool compilation_is_in_queue(methodHandle method);
static void print_compile_queues(outputStream* st);
+ static void print_directives(outputStream* st);
static int queue_size(int comp_level) {
CompileQueue *q = compile_queue(comp_level);
return q != NULL ? q->size() : 0;
--- a/hotspot/src/share/vm/compiler/compileTask.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/compiler/compileTask.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -26,6 +26,7 @@
#include "compiler/compileTask.hpp"
#include "compiler/compileLog.hpp"
#include "compiler/compileBroker.hpp"
+#include "compiler/compilerDirectives.hpp"
CompileTask* CompileTask::_task_free_list = NULL;
#ifdef ASSERT
@@ -372,6 +373,19 @@
}
// ------------------------------------------------------------------
+// CompileTask::check_break_at_flags
+bool CompileTask::check_break_at_flags() {
+ int compile_id = this->_compile_id;
+ bool is_osr = (_osr_bci != CompileBroker::standard_entry_bci);
+
+ if (CICountOSR && is_osr && (compile_id == CIBreakAtOSR)) {
+ return true;
+ } else {
+ return (compile_id == CIBreakAt);
+ }
+}
+
+// ------------------------------------------------------------------
// CompileTask::print_inlining
void CompileTask::print_inlining_inner(outputStream* st, ciMethod* method, int inline_level, int bci, const char* msg) {
// 1234567
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/compiler/compilerDirectives.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 1998, 2014, 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
+ * 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "ci/ciMethod.hpp"
+#include "ci/ciUtilities.hpp"
+#include "compiler/abstractCompiler.hpp"
+#include "compiler/compilerDirectives.hpp"
+#include "compiler/compilerOracle.hpp"
+
+CompilerDirectives::CompilerDirectives() :_match(NULL), _next(NULL), _ref_count(0) {
+ _c1_store = new DirectiveSet(this);
+ _c2_store = new DirectiveSet(this);
+};
+
+CompilerDirectives::~CompilerDirectives() {
+ if (_c1_store != NULL) {
+ delete _c1_store;
+ }
+ if (_c2_store != NULL) {
+ delete _c2_store;
+ }
+
+ // remove all linked method matchers
+ BasicMatcher* tmp = _match;
+ while (tmp != NULL) {
+ BasicMatcher* next = tmp->next();
+ delete tmp;
+ tmp = next;
+ }
+}
+
+void CompilerDirectives::print(outputStream* st) {
+ assert(DirectivesStack_lock->owned_by_self(), "");
+ if (_match != NULL) {
+ st->cr();
+ st->print("Directive:");
+ if (is_default_directive()) {
+ st->print_cr(" (default)");
+ } else {
+ st->cr();
+ }
+ st->print(" matching: ");
+ _match->print(st);
+ BasicMatcher* tmp = _match->next();
+ while (tmp != NULL) {
+ st->print(", ");
+ tmp->print(st);
+ tmp = tmp->next();
+ }
+ st->cr();
+ } else {
+ assert(0, "There should always be a match");
+ }
+
+ if (_c1_store != NULL) {
+ st->print_cr(" c1 directives:");
+ _c1_store->print(st);
+ }
+ if (_c2_store != NULL) {
+ st->cr();
+ st->print_cr(" c2 directives:");
+ _c2_store->print(st);
+ }
+ //---
+}
+
+void CompilerDirectives::finalize() {
+ if (_c1_store != NULL) {
+ _c1_store->finalize();
+ }
+ if (_c2_store != NULL) {
+ _c2_store->finalize();
+ }
+}
+
+void DirectiveSet::finalize() {
+ // if any flag has been modified - set directive as enabled
+ // unless it already has been explicitly set.
+ if (!_modified[EnableIndex]) {
+ if (_inlinematchers != NULL) {
+ EnableOption = true;
+ return;
+ }
+ int i;
+ for (i = 0; i < number_of_flags; i++) {
+ if (_modified[i]) {
+ EnableOption = true;
+ return;
+ }
+ }
+ }
+}
+
+CompilerDirectives* CompilerDirectives::next() {
+ return _next;
+}
+
+bool CompilerDirectives::match(methodHandle method) {
+ if (is_default_directive()) {
+ return true;
+ }
+ if (method == NULL) {
+ return false;
+ }
+ if (_match->match(method)) {
+ return true;
+ }
+ return false;
+}
+
+bool CompilerDirectives::add_match(char* str, const char*& error_msg) {
+ BasicMatcher* bm = BasicMatcher::parse_method_pattern(str, error_msg);
+ if (bm == NULL) {
+ assert(error_msg != NULL, "Must have error message");
+ return false;
+ } else {
+ bm->set_next(_match);
+ _match = bm;
+ return true;
+ }
+}
+
+void CompilerDirectives::inc_refcount() {
+ assert(DirectivesStack_lock->owned_by_self(), "");
+ _ref_count++;
+}
+
+void CompilerDirectives::dec_refcount() {
+ assert(DirectivesStack_lock->owned_by_self(), "");
+ _ref_count--;
+}
+
+int CompilerDirectives::refcount() {
+ assert(DirectivesStack_lock->owned_by_self(), "");
+ return _ref_count;
+}
+
+DirectiveSet* CompilerDirectives::get_for(AbstractCompiler *comp) {
+ assert(DirectivesStack_lock->owned_by_self(), "");
+ inc_refcount(); // The compiling thread is responsible to decrement this when finished.
+ if (comp == NULL) { // Xint
+ return _c1_store;
+ } else if (comp->is_c2()) {
+ return _c2_store;
+ } else if (comp->is_c1()) {
+ return _c1_store;
+ } else if (comp->is_shark()) {
+ return NULL;
+ } else if (comp->is_jvmci()) {
+ return NULL;
+ }
+ ShouldNotReachHere();
+ return NULL;
+}
+
+DirectiveSet::DirectiveSet(CompilerDirectives* d) :_inlinematchers(NULL), _directive(d) {
+#define init_defaults_definition(name, type, dvalue, compiler) this->name##Option = dvalue;
+ compilerdirectives_common_flags(init_defaults_definition)
+ compilerdirectives_c2_flags(init_defaults_definition)
+ compilerdirectives_c1_flags(init_defaults_definition)
+ memset(_modified, 0, sizeof _modified);
+}
+
+DirectiveSet::~DirectiveSet() {
+ // remove all linked methodmatchers
+ InlineMatcher* tmp = _inlinematchers;
+ while (tmp != NULL) {
+ InlineMatcher* next = tmp->next();
+ delete tmp;
+ tmp = next;
+ }
+
+ // Free if modified, otherwise it just points to the global vm flag value
+ // or to the Compile command option
+ if (_modified[DisableIntrinsicIndex]) {
+ assert(this->DisableIntrinsicOption != NULL, "");
+ FREE_C_HEAP_ARRAY(char, (void *)this->DisableIntrinsicOption);
+ }
+}
+
+// Backward compatibility for CompileCommands
+// Breaks the abstraction and causes lots of extra complexity
+// - if some option is changed we need to copy directiveset since it no longer can be shared
+// - Need to free copy after use
+// - Requires a modified bit so we don't overwrite options that is set by directives
+
+DirectiveSet* DirectiveSet::compilecommand_compatibility_init(methodHandle method) {
+ // Early bail out - checking all options is expensive - we rely on them not being used
+ // Only set a flag if it has not been modified and value changes.
+ // Only copy set if a flag needs to be set
+ if (!CompilerDirectivesIgnoreCompileCommandsOption && CompilerOracle::has_any_option()) {
+ DirectiveSet* set = DirectiveSet::clone(this);
+
+ bool changed = false; // Track if we actually change anything
+
+ // All CompileCommands are not equal so this gets a bit verbose
+ // When CompileCommands have been refactored less clutter will remain.
+ if (CompilerOracle::should_break_at(method)) {
+ if (!_modified[BreakAtCompileIndex]) {
+ set->BreakAtCompileOption = true;
+ changed = true;
+ }
+ if (!_modified[BreakAtExecuteIndex]) {
+ set->BreakAtExecuteOption = true;
+ changed = true;
+ }
+ }
+ if (CompilerOracle::should_log(method)) {
+ if (!_modified[LogIndex]) {
+ set->LogOption = true;
+ changed = true;
+ }
+ }
+ if (CompilerOracle::should_print(method)) {
+ if (!_modified[PrintAssemblyIndex]) {
+ set->PrintAssemblyOption = true;
+ changed = true;
+ }
+ }
+ // Exclude as in should not compile == Enabled
+ if (CompilerOracle::should_exclude(method)) {
+ if (!_modified[ExcludeIndex]) {
+ set->ExcludeOption = true;
+ changed = true;
+ }
+ }
+
+ // inline and dontinline (including exclude) are implemented in the directiveset accessors
+#define init_default_cc(name, type, dvalue, cc_flag) { type v; if (!_modified[name##Index] && CompilerOracle::has_option_value(method, #cc_flag, v) && v != this->name##Option) { set->name##Option = v; changed = true;} }
+ compilerdirectives_common_flags(init_default_cc)
+ compilerdirectives_c2_flags(init_default_cc)
+ compilerdirectives_c1_flags(init_default_cc)
+
+ if (!changed) {
+ // We didn't actually update anything, discard.
+ delete set;
+ } else {
+ // We are returning a (parentless) copy. The originals parent don't need to account for this.
+ DirectivesStack::release(this);
+ return set;
+ }
+ }
+ // Nothing changed
+ return this;
+}
+
+CompilerDirectives* DirectiveSet::directive() {
+ assert(_directive != NULL, "Must have been initialized");
+ return _directive;
+}
+
+bool DirectiveSet::matches_inline(methodHandle method, int inline_action) {
+ if (_inlinematchers != NULL) {
+ if (_inlinematchers->match(method, InlineMatcher::force_inline)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DirectiveSet::should_inline(ciMethod* inlinee) {
+ inlinee->check_is_loaded();
+ VM_ENTRY_MARK;
+ methodHandle mh(THREAD, inlinee->get_Method());
+
+ if (matches_inline(mh, InlineMatcher::force_inline)) {
+ return true;
+ }
+ if (!CompilerDirectivesIgnoreCompileCommandsOption && CompilerOracle::should_inline(mh)) {
+ return true;
+ }
+ return false;
+}
+
+bool DirectiveSet::should_not_inline(ciMethod* inlinee) {
+ inlinee->check_is_loaded();
+ VM_ENTRY_MARK;
+ methodHandle mh(THREAD, inlinee->get_Method());
+
+ if (matches_inline(mh, InlineMatcher::dont_inline)) {
+ return true;
+ }
+ if (!CompilerDirectivesIgnoreCompileCommandsOption && CompilerOracle::should_not_inline(mh)) {
+ return true;
+ }
+ return false;
+}
+
+bool DirectiveSet::parse_and_add_inline(char* str, const char*& error_msg) {
+ InlineMatcher* m = InlineMatcher::parse_inline_pattern(str, error_msg);
+ if (m != NULL) {
+ // add matcher last in chain - the order is significant
+ append_inline(m);
+ return true;
+ } else {
+ assert(error_msg != NULL, "Error message must be set");
+ return false;
+ }
+}
+
+void DirectiveSet::append_inline(InlineMatcher* m) {
+ if (_inlinematchers == NULL) {
+ _inlinematchers = m;
+ return;
+ }
+ InlineMatcher* tmp = _inlinematchers;
+ while (tmp->next() != NULL) {
+ tmp = tmp->next();
+ }
+ tmp->set_next(m);
+}
+
+void DirectiveSet::print_inline(outputStream* st) {
+ if (_inlinematchers == NULL) {
+ st->print_cr(" inline: -");
+ } else {
+ st->print(" inline: ");
+ _inlinematchers->print(st);
+ InlineMatcher* tmp = _inlinematchers->next();
+ while (tmp != NULL) {
+ st->print(", ");
+ tmp->print(st);
+ tmp = tmp->next();
+ }
+ st->cr();
+ }
+}
+
+bool DirectiveSet::is_intrinsic_disabled(methodHandle method) {
+ vmIntrinsics::ID id = method->intrinsic_id();
+ assert(id != vmIntrinsics::_none, "must be a VM intrinsic");
+
+ ccstr disable_intr = DisableIntrinsicOption;
+ return ((disable_intr != '\0') && strstr(disable_intr, vmIntrinsics::name_at(id)) != NULL);
+}
+
+DirectiveSet* DirectiveSet::clone(DirectiveSet const* src) {
+ DirectiveSet* set = new DirectiveSet(NULL);
+ memcpy(set->_modified, src->_modified, sizeof(src->_modified));
+
+ InlineMatcher* tmp = src->_inlinematchers;
+ while (tmp != NULL) {
+ set->append_inline(tmp->clone());
+ tmp = tmp->next();
+ }
+
+ #define copy_members_definition(name, type, dvalue, cc_flag) set->name##Option = src->name##Option;
+ compilerdirectives_common_flags(copy_members_definition)
+ compilerdirectives_c2_flags(copy_members_definition)
+ compilerdirectives_c1_flags(copy_members_definition)
+
+ // Must duplicate ccstr option if it was modified, otherwise it is global.
+ if (src->_modified[DisableIntrinsicIndex]) {
+ assert(src->DisableIntrinsicOption != NULL, "");
+ size_t len = strlen(src->DisableIntrinsicOption) + 1;
+ char* s = NEW_C_HEAP_ARRAY(char, len, mtCompiler);
+ strncpy(s, src->DisableIntrinsicOption, len);
+ assert(s[len-1] == '\0', "");
+ set->DisableIntrinsicOption = s;
+ }
+ return set;
+}
+
+// Create a new dirstack and push a default directive
+void DirectivesStack::init() {
+ CompilerDirectives* _default_directives = new CompilerDirectives();
+ char str[] = "*.*";
+ const char* error_msg = NULL;
+ _default_directives->add_match(str, error_msg);
+#ifdef COMPILER1
+ _default_directives->_c1_store->EnableOption = true;
+#endif
+#ifdef COMPILER2
+ _default_directives->_c2_store->EnableOption = true;
+#endif
+ assert(error_msg == NULL, "Must succeed.");
+ push(_default_directives);
+}
+
+DirectiveSet* DirectivesStack::getDefaultDirective(AbstractCompiler* comp) {
+ MutexLockerEx locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
+
+ assert(_bottom != NULL, "Must never be empty");
+ return _bottom->get_for(comp);
+}
+
+void DirectivesStack::push(CompilerDirectives* directive) {
+ MutexLockerEx locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
+
+ directive->inc_refcount();
+ if (_top == NULL) {
+ assert(_bottom == NULL, "There can only be one default directive");
+ _bottom = directive; // default directive, can never be removed.
+ }
+
+ directive->set_next(_top);
+ _top = directive;
+ _depth++;
+}
+
+void DirectivesStack::pop() {
+ MutexLockerEx locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
+ pop_inner();
+}
+
+void DirectivesStack::pop_inner() {
+ assert(DirectivesStack_lock->owned_by_self(), "");
+
+ if (_top->next() == NULL) {
+ return; // Do nothing - don't allow an empty stack
+ }
+ CompilerDirectives* tmp = _top;
+ _top = _top->next();
+ _depth--;
+
+ DirectivesStack::release(tmp);
+}
+
+void DirectivesStack::clear() {
+ // holding the lock during the whole operation ensuring consistent result
+ MutexLockerEx locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
+ while (_top->next() != NULL) {
+ pop_inner();
+ }
+}
+
+void DirectivesStack::print(outputStream* st) {
+ MutexLockerEx locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
+ CompilerDirectives* tmp = _top;
+ while (tmp != NULL) {
+ tmp->print(st);
+ tmp = tmp->next();
+ st->cr();
+ }
+}
+
+void DirectivesStack::release(DirectiveSet* set) {
+ MutexLockerEx locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
+ if (set->is_exclusive_copy()) {
+ // Old CompilecCmmands forced us to create an exclusive copy
+ delete set;
+ } else {
+ assert(set->directive() != NULL, "");
+ release(set->directive());
+ }
+}
+
+
+void DirectivesStack::release(CompilerDirectives* dir) {
+ assert(DirectivesStack_lock->owned_by_self(), "");
+ dir->dec_refcount();
+ if (dir->refcount() == 0) {
+ delete dir;
+ }
+}
+
+DirectiveSet* DirectivesStack::getMatchingDirective(methodHandle method, AbstractCompiler *comp) {
+ assert(_depth > 0, "Must never be empty");
+ CompilerDirectives* dir = _top;
+ assert(dir != NULL, "Must be initialized");
+
+ DirectiveSet* match = NULL;
+ {
+ MutexLockerEx locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
+ while (dir != NULL) {
+ if (dir->is_default_directive() || dir->match(method)) {
+ match = dir->get_for(comp);
+ if (match->EnableOption) {
+ // The directiveSet for this compile is also enabled -> success
+ break;
+ }
+ }
+ dir = dir->next();
+ }
+ }
+
+ guarantee(match != NULL, "There should always be a default directive that matches");
+ // Check for legacy compile commands update, without DirectivesStack_lock
+ return match->compilecommand_compatibility_init(method);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/compiler/compilerDirectives.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 1998, 2014, 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
+ * 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.
+ *
+ */
+
+#ifndef SHARE_VM_COMPILER_COMPILERDIRECTIVES_HPP
+#define SHARE_VM_COMPILER_COMPILERDIRECTIVES_HPP
+
+#include "ci/ciMetadata.hpp"
+#include "ci/ciMethod.hpp"
+#include "ci/ciUtilities.hpp"
+#include "compiler/methodMatcher.hpp"
+#include "compiler/compilerOracle.hpp"
+#include "oops/oop.inline.hpp"
+#include "utilities/exceptions.hpp"
+
+ // Directives flag name, type, default value, compile command name
+ #define compilerdirectives_common_flags(cflags) \
+ cflags(Enable, bool, false, X) \
+ cflags(Exclude, bool, false, X) \
+ cflags(BreakAtExecute, bool, false, X) \
+ cflags(BreakAtCompile, bool, false, X) \
+ cflags(Log, bool, false, X) \
+ cflags(PrintAssembly, bool, PrintAssembly, PrintAssembly) \
+ cflags(PrintInlining, bool, PrintInlining, PrintInlining) \
+ cflags(PrintNMethods, bool, PrintNMethods, PrintNMethods) \
+ cflags(ReplayInline, bool, false, ReplayInline) \
+ cflags(DumpReplay, bool, false, DumpReplay) \
+ cflags(DumpInline, bool, false, DumpInline) \
+ cflags(CompilerDirectivesIgnoreCompileCommands, bool, CompilerDirectivesIgnoreCompileCommands, X) \
+ cflags(DisableIntrinsic, ccstr, DisableIntrinsic, DisableIntrinsic)
+
+#ifdef COMPILER1
+ #define compilerdirectives_c1_flags(cflags)
+#else
+ #define compilerdirectives_c1_flags(cflags)
+#endif
+
+#ifdef COMPILER2
+ #define compilerdirectives_c2_flags(cflags) \
+ cflags(BlockLayoutByFrequency, bool, BlockLayoutByFrequency, BlockLayoutByFrequency) \
+ cflags(PrintOptoAssembly, bool, PrintOptoAssembly, PrintOptoAssembly) \
+ cflags(PrintIntrinsics, bool, PrintIntrinsics, PrintIntrinsics) \
+ cflags(TraceOptoPipelining, bool, false, TraceOptoPipelining) \
+ cflags(TraceOptoOutput, bool, false, TraceOptoOutput) \
+ cflags(TraceSpilling, bool, TraceSpilling, TraceSpilling) \
+ cflags(Vectorize, bool, false, Vectorize) \
+ cflags(VectorizeDebug, bool, false, VectorizeDebug) \
+ cflags(CloneMapDebug, bool, false, CloneMapDebug) \
+ cflags(DoReserveCopyInSuperWordDebug, bool, false, DoReserveCopyInSuperWordDebug) \
+ cflags(IGVPrintLevel, intx, PrintIdealGraphLevel, IGVPrintLevel) \
+ cflags(MaxNodeLimit, intx, MaxNodeLimit, MaxNodeLimit)
+#else
+ #define compilerdirectives_c2_flags(cflags)
+#endif
+
+class CompilerDirectives;
+class DirectiveSet;
+
+class DirectivesStack : AllStatic {
+private:
+ static CompilerDirectives* _top;
+ static CompilerDirectives* _bottom;
+ static int _depth;
+
+ static void pop_inner(); // no lock version of pop
+public:
+ static void init();
+ static DirectiveSet* getMatchingDirective(methodHandle mh, AbstractCompiler* comp);
+ static DirectiveSet* getDefaultDirective(AbstractCompiler* comp);
+ static void push(CompilerDirectives* directive);
+ static void pop();
+ static void clear();
+ static void print(outputStream* st);
+ static void release(DirectiveSet* set);
+ static void release(CompilerDirectives* dir);
+};
+
+class DirectiveSet : public CHeapObj<mtCompiler> {
+private:
+ InlineMatcher* _inlinematchers;
+ CompilerDirectives* _directive;
+
+public:
+ DirectiveSet(CompilerDirectives* directive);
+ ~DirectiveSet();
+ CompilerDirectives* directive();
+ bool parse_and_add_inline(char* str, const char*& error_msg);
+ void append_inline(InlineMatcher* m);
+ bool should_inline(ciMethod* inlinee);
+ bool should_not_inline(ciMethod* inlinee);
+ void print_inline(outputStream* st);
+ DirectiveSet* compilecommand_compatibility_init(methodHandle method);
+ bool is_exclusive_copy() { return _directive == NULL; }
+ bool matches_inline(methodHandle method, int inline_action);
+ static DirectiveSet* clone(DirectiveSet const* src);
+ bool is_intrinsic_disabled(methodHandle method);
+ void finalize();
+
+ typedef enum {
+#define enum_of_flags(name, type, dvalue, cc_flag) name##Index,
+ compilerdirectives_common_flags(enum_of_flags)
+ compilerdirectives_c2_flags(enum_of_flags)
+ compilerdirectives_c1_flags(enum_of_flags)
+ number_of_flags
+ } flags;
+
+ bool _modified[number_of_flags];
+
+#define flag_store_definition(name, type, dvalue, cc_flag) type name##Option;
+ compilerdirectives_common_flags(flag_store_definition)
+ compilerdirectives_c2_flags(flag_store_definition)
+ compilerdirectives_c1_flags(flag_store_definition)
+
+// Casting to get the same function signature for all setters. Used from parser.
+#define set_function_definition(name, type, dvalue, cc_flag) void set_##name(void* value) { type val = *(type*)value; name##Option = val; _modified[name##Index] = 1; }
+ compilerdirectives_common_flags(set_function_definition)
+ compilerdirectives_c2_flags(set_function_definition)
+ compilerdirectives_c1_flags(set_function_definition)
+
+ void print_intx(outputStream* st, ccstr n, intx v, bool mod) { if (mod) { st->print("%s:" INTX_FORMAT " ", n, v); } }
+ void print_bool(outputStream* st, ccstr n, bool v, bool mod) { if (mod) { st->print("%s:%s ", n, v ? "true" : "false"); } }
+ void print_double(outputStream* st, ccstr n, double v, bool mod) { if (mod) { st->print("%s:%f ", n, v); } }
+ void print_ccstr(outputStream* st, ccstr n, ccstr v, bool mod) { if (mod) { st->print("%s:%s ", n, v); } }
+
+void print(outputStream* st) {
+ print_inline(st);
+ st->print(" ");
+#define print_function_definition(name, type, dvalue, cc_flag) print_##type(st, #name, this->name##Option, true);//(bool)_modified[name##Index]);
+ compilerdirectives_common_flags(print_function_definition)
+ compilerdirectives_c2_flags(print_function_definition)
+ compilerdirectives_c1_flags(print_function_definition)
+ st->cr();
+ }
+};
+
+class CompilerDirectives : public CHeapObj<mtCompiler> {
+private:
+ CompilerDirectives* _next;
+ BasicMatcher* _match;
+ int _ref_count;
+
+public:
+
+ CompilerDirectives();
+ ~CompilerDirectives();
+
+ CompilerDirectives* next();
+ void set_next(CompilerDirectives* next) {_next = next; }
+
+ bool match(methodHandle method);
+ BasicMatcher* match() { return _match; }
+ bool add_match(char* str, const char*& error_msg);
+ DirectiveSet* get_for(AbstractCompiler *comp);
+ void print(outputStream* st);
+ bool is_default_directive() { return _next == NULL; }
+ void finalize();
+
+ void inc_refcount();
+ void dec_refcount();
+ int refcount();
+
+ DirectiveSet* _c1_store;
+ DirectiveSet* _c2_store;
+};
+
+#endif // SHARE_VM_COMPILER_COMPILERDIRECTIVES_HPP
--- a/hotspot/src/share/vm/compiler/compilerOracle.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/compiler/compilerOracle.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -106,6 +106,7 @@
static BasicMatcher* lists[OracleCommandCount] = { 0, };
static TypedMethodOptionMatcher* option_list = NULL;
+static bool any_set = false;
class TypedMethodOptionMatcher : public MethodMatcher {
private:
@@ -292,6 +293,7 @@
matcher->init(option, get_type_for<T>(), option_list);
matcher->set_value<T>(value);
option_list = matcher;
+ any_set = true;
return;
}
@@ -308,7 +310,9 @@
}
bm->set_next(lists[command]);
lists[command] = bm;
-
+ if ((command != DontInlineCommand) && (command != InlineCommand)) {
+ any_set = true;
+ }
return;
}
@@ -324,6 +328,10 @@
return false;
}
+bool CompilerOracle::has_any_option() {
+ return any_set;
+}
+
// Explicit instantiation for all OptionTypes supported.
template bool CompilerOracle::has_option_value<intx>(methodHandle method, const char* option, intx& value);
template bool CompilerOracle::has_option_value<uintx>(methodHandle method, const char* option, uintx& value);
@@ -337,15 +345,10 @@
return value;
}
-bool CompilerOracle::should_exclude(methodHandle method, bool& quietly) {
- quietly = true;
- if (lists[ExcludeCommand] != NULL) {
- if (lists[ExcludeCommand]->match(method)) {
- quietly = _quiet;
- return true;
- }
+bool CompilerOracle::should_exclude(methodHandle method) {
+ if (check_predicate(ExcludeCommand, method)) {
+ return true;
}
-
if (lists[CompileOnlyCommand] != NULL) {
return !lists[CompileOnlyCommand]->match(method);
}
@@ -356,8 +359,6 @@
return (check_predicate(InlineCommand, method));
}
-// Check both DontInlineCommand and ExcludeCommand here
-// - consistent behavior for all compilers
bool CompilerOracle::should_not_inline(methodHandle method) {
return check_predicate(DontInlineCommand, method) || check_predicate(ExcludeCommand, method);
}
--- a/hotspot/src/share/vm/compiler/compilerOracle.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/compiler/compilerOracle.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -46,7 +46,8 @@
static void parse_from_file();
// Tells whether we to exclude compilation of method
- static bool should_exclude(methodHandle method, bool& quietly);
+ static bool should_exclude(methodHandle method);
+ static bool should_exclude_quietly() { return _quiet; }
// Tells whether we want to inline this method
static bool should_inline(methodHandle method);
@@ -71,6 +72,9 @@
template<typename T>
static bool has_option_value(methodHandle method, const char* option, T& value);
+ // Fast check if there is any option available that compile control needs to know about
+ static bool has_any_option();
+
// Reads from string instead of file
static void parse_from_string(const char* command_string, void (*parser)(char*));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/compiler/directivesParser.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,726 @@
+/*
+ * Copyright (c) 2015, 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
+ * 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "compiler/compileBroker.hpp"
+#include "compiler/directivesParser.hpp"
+#include "memory/allocation.inline.hpp"
+#include "runtime/os.hpp"
+#include <string.h>
+
+void DirectivesParser::push_tmp(CompilerDirectives* dir) {
+ dir->set_next(_tmp_top);
+ _tmp_top = dir;
+}
+
+CompilerDirectives* DirectivesParser::pop_tmp() {
+ if (_tmp_top == NULL) {
+ return NULL;
+ }
+ CompilerDirectives* tmp = _tmp_top;
+ _tmp_top = _tmp_top->next();
+ tmp->set_next(NULL);
+ return tmp;
+}
+
+bool DirectivesParser::parse_string(const char* text, outputStream* st) {
+ DirectivesParser cd(text, st);
+ if (cd.valid()) {
+ return cd.install_directives();
+ }
+ st->flush();
+ st->print_cr("Parsing of compiler directives failed");
+ return false;
+}
+
+bool DirectivesParser::has_file() {
+ return CompilerDirectivesFile != NULL;
+}
+
+bool DirectivesParser::parse_from_flag() {
+ return parse_from_file(CompilerDirectivesFile, tty);
+}
+
+bool DirectivesParser::parse_from_file(const char* filename, outputStream* st) {
+ assert(filename != NULL, "Test before calling this");
+ if (!parse_from_file_inner(filename, st)) {
+ st->print_cr("Could not load file: %s", filename);
+ return false;
+ }
+ return true;
+}
+
+bool DirectivesParser::parse_from_file_inner(const char* filename, outputStream* stream) {
+ struct stat st;
+ ResourceMark rm;
+ if (os::stat(filename, &st) == 0) {
+ // found file, open it
+ int file_handle = os::open(filename, 0, 0);
+ if (file_handle != -1) {
+ // read contents into resource array
+ char* buffer = NEW_RESOURCE_ARRAY(char, st.st_size+1);
+ size_t num_read = os::read(file_handle, (char*) buffer, st.st_size);
+ buffer[num_read] = '\0';
+ // close file
+ os::close(file_handle);
+ return parse_string(buffer, stream);
+ }
+ }
+ return false;
+}
+
+bool DirectivesParser::install_directives() {
+ // Pop from internal temporary stack and push to compileBroker.
+ CompilerDirectives* tmp = pop_tmp();
+ int i = 0;
+ while (tmp != NULL) {
+ i++;
+ DirectivesStack::push(tmp);
+ tmp = pop_tmp();
+ }
+ if (i == 0) {
+ _st->print_cr("No directives in file");
+ return false;
+ } else {
+ _st->print_cr("%i compiler directives added", i);
+ if (PrintCompilerDirectives) {
+ // Print entire directives stack after new has been pushed.
+ DirectivesStack::print(_st);
+ }
+ return true;
+ }
+}
+
+DirectivesParser::DirectivesParser(const char* text, outputStream* st)
+: JSON(text, false, st), depth(0), current_directive(NULL), current_directiveset(NULL), _tmp_top(NULL) {
+#ifndef PRODUCT
+ memset(stack, 0, MAX_DEPTH * sizeof(stack[0]));
+#endif
+ parse();
+}
+
+DirectivesParser::~DirectivesParser() {
+}
+
+const DirectivesParser::key DirectivesParser::keys[] = {
+ // name, keytype, allow_array, allowed_mask, set_function
+ { "c1", type_c1, 0, mask(type_directives), NULL, UnknownFlagType },
+ { "c2", type_c2, 0, mask(type_directives), NULL, UnknownFlagType },
+ { "match", type_match, 1, mask(type_directives), NULL, UnknownFlagType },
+ { "inline", type_inline, 1, mask(type_directives) | mask(type_c1) | mask(type_c2), NULL, UnknownFlagType },
+ { "enable", type_enable, 1, mask(type_directives) | mask(type_c1) | mask(type_c2), NULL, UnknownFlagType },
+ { "preset", type_preset, 0, mask(type_c1) | mask(type_c2), NULL, UnknownFlagType },
+
+ // Global flags
+ #define common_flag_key(name, type, dvalue, compiler) \
+ { #name, type_flag, 0, mask(type_directives) | mask(type_c1) | mask(type_c2), &DirectiveSet::set_##name, type##Flag},
+ compilerdirectives_common_flags(common_flag_key)
+ compilerdirectives_c2_flags(common_flag_key)
+ compilerdirectives_c1_flags(common_flag_key)
+ #undef common_flag_key
+};
+
+const DirectivesParser::key DirectivesParser::dir_array_key = {
+ "top level directives array", type_dir_array, 0, 1 // Lowest bit means allow at top level
+};
+const DirectivesParser::key DirectivesParser::dir_key = {
+ "top level directive", type_directives, 0, mask(type_dir_array) | 1 // Lowest bit means allow at top level
+};
+const DirectivesParser::key DirectivesParser::value_array_key = {
+ "value array", type_value_array, 0, UINT_MAX // Allow all, checked by allow_array on other keys, not by allowed_mask from this key
+};
+
+const DirectivesParser::key* DirectivesParser::lookup_key(const char* str, size_t len) {
+ for (size_t i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) {
+ if (strncasecmp(keys[i].name, str, len) == 0) {
+ return &keys[i];
+ }
+ }
+ return NULL;
+}
+
+uint DirectivesParser::mask(keytype kt) {
+ return 1 << (kt + 1);
+}
+
+bool DirectivesParser::push_key(const char* str, size_t len) {
+ bool result = true;
+ const key* k = lookup_key(str, len);
+
+ if (k == NULL) {
+ // os::strdup
+ char* s = NEW_C_HEAP_ARRAY(char, len + 1, mtCompiler);
+ strncpy(s, str, len);
+ s[len] = '\0';
+ error(KEY_ERROR, "No such key: '%s'.", s);
+ FREE_C_HEAP_ARRAY(char, s);
+ return false;
+ }
+
+ return push_key(k);
+}
+
+bool DirectivesParser::push_key(const key* k) {
+ assert(k->allowedmask != 0, "not allowed anywhere?");
+
+ // Exceeding the stack should not be possible with a valid compiler directive,
+ // and an invalid should abort before this happens
+ assert(depth < MAX_DEPTH, "exceeded stack depth");
+ if (depth >= MAX_DEPTH) {
+ error(INTERNAL_ERROR, "Stack depth exceeded.");
+ return false;
+ }
+
+ assert(stack[depth] == NULL, "element not nulled, something is wrong");
+
+ if (depth == 0 && !(k->allowedmask & 1)) {
+ error(KEY_ERROR, "Key '%s' not allowed at top level.", k->name);
+ return false;
+ }
+
+ if (depth > 0) {
+ const key* prev = stack[depth - 1];
+ if (!(k->allowedmask & mask(prev->type))) {
+ error(KEY_ERROR, "Key '%s' not allowed after '%s' key.", k->name, prev->name);
+ return false;
+ }
+ }
+
+ stack[depth] = k;
+ depth++;
+ return true;
+}
+
+const DirectivesParser::key* DirectivesParser::current_key() {
+ assert(depth > 0, "getting key from empty stack");
+ if (depth == 0) {
+ return NULL;
+ }
+ return stack[depth - 1];
+}
+
+const DirectivesParser::key* DirectivesParser::pop_key() {
+ assert(depth > 0, "popping empty stack");
+ if (depth == 0) {
+ error(INTERNAL_ERROR, "Popping empty stack.");
+ return NULL;
+ }
+ depth--;
+
+ const key* k = stack[depth];
+#ifndef PRODUCT
+ stack[depth] = NULL;
+#endif
+
+ return k;
+}
+
+bool DirectivesParser::set_option_flag(JSON_TYPE t, JSON_VAL* v, const key* option_key, DirectiveSet* set) {
+
+ void (DirectiveSet::*test)(void *args);
+ test = option_key->set;
+
+ switch (t) {
+ case JSON_TRUE:
+ if (option_key->flag_type != boolFlag) {
+ error(VALUE_ERROR, "Cannot use bool value for an %s flag", flag_type_names[option_key->flag_type]);
+ return false;
+ } else {
+ bool val = true;
+ (set->*test)((void *)&val);
+ }
+ break;
+
+ case JSON_FALSE:
+ if (option_key->flag_type != boolFlag) {
+ error(VALUE_ERROR, "Cannot use bool value for an %s flag", flag_type_names[option_key->flag_type]);
+ return false;
+ } else {
+ bool val = false;
+ (set->*test)((void *)&val);
+ }
+ break;
+
+ case JSON_NUMBER_INT:
+ if (option_key->flag_type != intxFlag) {
+ if (option_key->flag_type == doubleFlag) {
+ double dval = (double)v->int_value;
+ (set->*test)((void *)&dval);
+ break;
+ }
+ error(VALUE_ERROR, "Cannot use int value for an %s flag", flag_type_names[option_key->flag_type]);
+ return false;
+ } else {
+ intx ival = v->int_value;
+ (set->*test)((void *)&ival);
+ }
+ break;
+
+ case JSON_NUMBER_FLOAT:
+ if (option_key->flag_type != doubleFlag) {
+ error(VALUE_ERROR, "Cannot use double value for an %s flag", flag_type_names[option_key->flag_type]);
+ return false;
+ } else {
+ double dval = v->double_value;
+ (set->*test)((void *)&dval);
+ }
+ break;
+
+ case JSON_STRING:
+ if (option_key->flag_type != ccstrFlag) {
+ error(VALUE_ERROR, "Cannot use string value for a %s flag", flag_type_names[option_key->flag_type]);
+ return false;
+ } else {
+ char* s = NEW_C_HEAP_ARRAY(char, v->str.length+1, mtCompiler);
+ strncpy(s, v->str.start, v->str.length + 1);
+ s[v->str.length] = '\0';
+ (set->*test)((void *)&s);
+ }
+ break;
+
+ default:
+ assert(0, "Should not reach here.");
+ }
+ return true;
+}
+
+bool DirectivesParser::set_option(JSON_TYPE t, JSON_VAL* v) {
+
+ const key* option_key = pop_key();
+ const key* enclosing_key = current_key();
+
+ if (option_key->type == value_array_key.type) {
+ // Multi value array, we are really setting the value
+ // for the key one step further up.
+ option_key = pop_key();
+ enclosing_key = current_key();
+
+ // Repush option_key and multi value marker, since
+ // we need to keep them until all multi values are set.
+ push_key(option_key);
+ push_key(&value_array_key);
+ }
+
+ switch (option_key->type) {
+ case type_flag:
+ {
+ if (current_directiveset == NULL) {
+ assert(depth == 2, "Must not have active directive set");
+
+ if (!set_option_flag(t, v, option_key, current_directive->_c1_store)) {
+ return false;
+ }
+ if(!set_option_flag(t, v, option_key, current_directive->_c2_store)) {
+ return false;
+ }
+ } else {
+ assert(depth > 2, "Must have active current directive set");
+ if (!set_option_flag(t, v, option_key, current_directiveset)) {
+ return false;
+ }
+ }
+ break;
+ }
+
+ case type_match:
+ if (t != JSON_STRING) {
+ error(VALUE_ERROR, "Key of type %s needs a value of type string", option_key->name);
+ return false;
+ }
+ if (enclosing_key->type != type_directives) {
+ error(SYNTAX_ERROR, "Match keyword can only exist inside a directive");
+ return false;
+ }
+ {
+ char* s = NEW_C_HEAP_ARRAY(char, v->str.length + 1, mtCompiler);
+ strncpy(s, v->str.start, v->str.length);
+ s[v->str.length] = '\0';
+
+ const char* error_msg = NULL;
+ if (!current_directive->add_match(s, error_msg)) {
+ assert (error_msg != NULL, "Must have valid error message");
+ error(VALUE_ERROR, "Method pattern error: %s", error_msg);
+ }
+ FREE_C_HEAP_ARRAY(char, s);
+ }
+ break;
+
+ case type_inline:
+ if (t != JSON_STRING) {
+ error(VALUE_ERROR, "Key of type %s needs a value of type string", option_key->name);
+ return false;
+ }
+ {
+ //char* s = strndup(v->str.start, v->str.length);
+ char* s = NEW_C_HEAP_ARRAY(char, v->str.length + 1, mtCompiler);
+ strncpy(s, v->str.start, v->str.length);
+ s[v->str.length] = '\0';
+
+ const char* error_msg = NULL;
+ if (current_directiveset == NULL) {
+ if (!current_directive->_c1_store->parse_and_add_inline(s, error_msg)) {
+ assert (error_msg != NULL, "Must have valid error message");
+ error(VALUE_ERROR, "Method pattern error: %s", error_msg);
+ }
+ if (!current_directive->_c2_store->parse_and_add_inline(s, error_msg)) {
+ assert (error_msg != NULL, "Must have valid error message");
+ error(VALUE_ERROR, "Method pattern error: %s", error_msg);
+ }
+ } else {
+ if (!current_directiveset->parse_and_add_inline(s, error_msg)) {
+ assert (error_msg != NULL, "Must have valid error message");
+ error(VALUE_ERROR, "Method pattern error: %s", error_msg);
+ }
+ }
+ FREE_C_HEAP_ARRAY(char, s);
+ }
+ break;
+
+ case type_c1:
+ current_directiveset = current_directive->_c1_store;
+ if (t != JSON_TRUE && t != JSON_FALSE) {
+ error(VALUE_ERROR, "Key of type %s needs a true or false value", option_key->name);
+ return false;
+ }
+ break;
+
+ case type_c2:
+ current_directiveset = current_directive->_c2_store;
+ if (t != JSON_TRUE && t != JSON_FALSE) {
+ error(VALUE_ERROR, "Key of type %s needs a true or false value", option_key->name);
+ return false;
+ }
+ break;
+
+ case type_enable:
+ switch (enclosing_key->type) {
+ case type_c1:
+ case type_c2:
+ {
+ if (t != JSON_TRUE && t != JSON_FALSE) {
+ error(VALUE_ERROR, "Key of type %s enclosed in a %s key needs a true or false value", option_key->name, enclosing_key->name);
+ return false;
+ }
+ int val = (t == JSON_TRUE);
+ current_directiveset->set_Enable(&val);
+ break;
+ }
+
+ case type_directives:
+ error(VALUE_ERROR, "Enable keyword not available for generic directive");
+ return false;
+
+ default:
+ error(INTERNAL_ERROR, "Unexpected enclosing type for key %s: %s", option_key->name, enclosing_key->name);
+ ShouldNotReachHere();
+ return false;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool DirectivesParser::callback(JSON_TYPE t, JSON_VAL* v, uint rlimit) {
+ const key* k;
+
+ if (depth == 0) {
+ switch (t) {
+ case JSON_ARRAY_BEGIN:
+ return push_key(&dir_array_key);
+
+ case JSON_OBJECT_BEGIN:
+ // push synthetic dir_array
+ push_key(&dir_array_key);
+ assert(depth == 1, "Make sure the stack are aligned with the directives");
+ break;
+
+ default:
+ error(SYNTAX_ERROR, "DirectivesParser can only start with an array containing directive objects, or one single directive.");
+ return false;
+ }
+ }
+ if (depth == 1) {
+ switch (t) {
+ case JSON_OBJECT_BEGIN:
+ // Parsing a new directive.
+ current_directive = new CompilerDirectives();
+ return push_key(&dir_key);
+
+ case JSON_ARRAY_END:
+ k = pop_key();
+
+ if (k->type != type_dir_array) {
+ error(SYNTAX_ERROR, "Expected end of directives array");
+ return false;
+ }
+ return true;
+
+ default:
+ error(SYNTAX_ERROR, "DirectivesParser can only start with an array containing directive objects, or one single directive.");
+ return false;
+ }
+ } else {
+ switch (t) {
+ case JSON_OBJECT_BEGIN:
+ k = current_key();
+ switch (k->type) {
+ case type_c1:
+ current_directiveset = current_directive->_c1_store;
+ return true;
+ case type_c2:
+ current_directiveset = current_directive->_c2_store;
+ return true;
+
+ case type_dir_array:
+ return push_key(&dir_key);
+
+ default:
+ error(SYNTAX_ERROR, "The key '%s' does not allow an object to follow.", k->name);
+ return false;
+ }
+ return false;
+
+ case JSON_OBJECT_END:
+ k = pop_key();
+ switch (k->type) {
+ case type_c1:
+ case type_c2:
+ // This is how we now if options apply to a single or both directive sets
+ current_directiveset = NULL;
+ break;
+
+ case type_directives:
+ // Check, finish and push to stack!
+ if (current_directive->match() == NULL) {
+ error(INTERNAL_ERROR, "Directive missing required match.");
+ return false;
+ }
+ current_directive->finalize();
+ push_tmp(current_directive);
+ current_directive = NULL;
+ break;
+
+ default:
+ error(INTERNAL_ERROR, "Object end with wrong key type on stack: %s.", k->name);
+ ShouldNotReachHere();
+ return false;
+ }
+ return true;
+
+ case JSON_ARRAY_BEGIN:
+ k = current_key();
+ if (!(k->allow_array_value)) {
+ if (k->type == type_dir_array) {
+ error(SYNTAX_ERROR, "Array not allowed inside top level array, expected directive object.");
+ } else {
+ error(VALUE_ERROR, "The key '%s' does not allow an array of values.", k->name);
+ }
+ return false;
+ }
+ return push_key(&value_array_key);
+
+ case JSON_ARRAY_END:
+ k = pop_key(); // Pop multi value marker
+ assert(k->type == value_array_key.type, "array end for level != 0 should terminate multi value");
+ k = pop_key(); // Pop key for option that was set
+ return true;
+
+ case JSON_KEY:
+ return push_key(v->str.start, v->str.length);
+
+ case JSON_STRING:
+ case JSON_NUMBER_INT:
+ case JSON_NUMBER_FLOAT:
+ case JSON_TRUE:
+ case JSON_FALSE:
+ case JSON_NULL:
+ return set_option(t, v);
+
+ default:
+ error(INTERNAL_ERROR, "Unknown JSON type: %d.", t);
+ ShouldNotReachHere();
+ return false;
+ }
+ }
+}
+
+#ifndef PRODUCT
+void DirectivesParser::test(const char* text, bool should_pass) {
+ DirectivesParser cd(text, tty);
+ if (should_pass) {
+ assert(cd.valid() == true, "failed on a valid DirectivesParser string");
+ if (VerboseInternalVMTests) {
+ tty->print("-- DirectivesParser test passed as expected --\n");
+ }
+ } else {
+ assert(cd.valid() == false, "succeeded on an invalid DirectivesParser string");
+ if (VerboseInternalVMTests) {
+ tty->print("-- DirectivesParser test failed as expected --\n");
+ }
+ }
+}
+
+bool DirectivesParser::test() {
+ DirectivesParser::test("{}", false);
+ DirectivesParser::test("[]", true);
+ DirectivesParser::test("[{}]", false);
+ DirectivesParser::test("[{},{}]", false);
+ DirectivesParser::test("{},{}", false);
+
+ DirectivesParser::test(
+ "[" "\n"
+ " {" "\n"
+ " match: \"foo/bar.*\"," "\n"
+ " inline : \"+java/util.*\"," "\n"
+ " PrintAssembly: true," "\n"
+ " BreakAtExecute: true," "\n"
+ " }" "\n"
+ "]" "\n", true);
+
+ DirectivesParser::test(
+ "[" "\n"
+ " [" "\n"
+ " {" "\n"
+ " match: \"foo/bar.*\"," "\n"
+ " inline : \"+java/util.*\"," "\n"
+ " PrintAssembly: true," "\n"
+ " BreakAtExecute: true," "\n"
+ " }" "\n"
+ " ]" "\n"
+ "]" "\n", false);
+
+ /*DirectivesParser::test(
+ "[" "\n"
+ " {" "\n"
+ " match: \"foo/bar.*\"," "\n"
+ " c1: {"
+ " PrintIntrinsics: false," "\n"
+ " }" "\n"
+ " }" "\n"
+ "]" "\n", false);*/
+
+ DirectivesParser::test(
+ "[" "\n"
+ " {" "\n"
+ " match: \"foo/bar.*\"," "\n"
+ " c2: {" "\n"
+ " PrintInlining: false," "\n"
+ " }" "\n"
+ " }" "\n"
+ "]" "\n", true);
+
+ DirectivesParser::test(
+ "[" "\n"
+ " {" "\n"
+ " match: \"foo/bar.*\"," "\n"
+ " PrintInlining: [" "\n"
+ " true," "\n"
+ " false" "\n"
+ " ]," "\n"
+ " }" "\n"
+ "]" "\n", false);
+
+ DirectivesParser::test(
+ "[" "\n"
+ " {"
+ " // pattern to match against class+method+signature" "\n"
+ " // leading and trailing wildcard (*) allowed" "\n"
+ " match: \"foo/bar.*\"," "\n"
+ "" "\n"
+ " // override defaults for specified compiler" "\n"
+ " // we may differentiate between levels too. TBD." "\n"
+ " c1: {" "\n"
+ " //override c1 presets " "\n"
+ " DumpReplay: false," "\n"
+ " BreakAtCompile: true," "\n"
+ " }," "\n"
+ "" "\n"
+ " c2: {" "\n"
+ " // control inlining of method" "\n"
+ " // + force inline, - dont inline" "\n"
+ " inline : \"+java/util.*\"," "\n"
+ " PrintInlining: true," "\n"
+ " }," "\n"
+ "" "\n"
+ " // directives outside a specific preset applies to all compilers" "\n"
+ " inline : [ \"+java/util.*\", \"-com/sun.*\"]," "\n"
+ " BreakAtExecute: true," "\n"
+ " Log: true," "\n"
+ " }," "\n"
+ " {" "\n"
+ " // matching several patterns require an array" "\n"
+ " match: [\"baz.*\",\"frob.*\"]," "\n"
+ "" "\n"
+ " // applies to all compilers" "\n"
+ " // + force inline, - dont inline" "\n"
+ " inline : [ \"+java/util.*\", \"-com/sun.*\" ]," "\n"
+ " PrintInlining: true," "\n"
+ "" "\n"
+ " // force matching compiles to be blocking/syncronous" "\n"
+ " PrintNMethods: true" "\n"
+ " }," "\n"
+ "]" "\n", true);
+
+ // Test max stack depth
+ DirectivesParser::test(
+ "[" "\n" // depth 1: type_dir_array
+ " {" "\n" // depth 2: type_directives
+ " match: \"*.*\"," // match required
+ " c1:" "\n" // depth 3: type_c1
+ " {" "\n"
+ " inline:" "\n" // depth 4: type_inline
+ " [" "\n" // depth 5: type_value_array
+ " \"foo\"," "\n"
+ " \"bar\"," "\n"
+ " ]" "\n" // depth 3: pop type_value_array and type_inline keys
+ " }" "\n" // depth 2: pop type_c1 key
+ " }" "\n" // depth 1: pop type_directives key
+ "]" "\n", true); // depth 0: pop type_dir_array key
+
+ // Test max stack depth
+ DirectivesParser::test(
+ "[{c1:{c1:{c1:{c1:{c1:{c1:{c1:{}}}}}}}}]", false);
+
+ DirectivesParser::test(
+ "[" "\n"
+ " {" "\n"
+ " c1: true," "\n"
+ " c2: true," "\n"
+ " match: true," "\n"
+ " inline: true," "\n"
+ " enable: true," "\n"
+ " c1: {" "\n"
+ " preset: true," "\n"
+ " }" "\n"
+ " }" "\n"
+ "]" "\n", false);
+
+ return true;
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/compiler/directivesParser.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2015, 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
+ * 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.
+ *
+ */
+
+#ifndef SHARE_VM_COMPILER_DIRECTIVESPARSER_HPP
+#define SHARE_VM_COMPILER_DIRECTIVESPARSER_HPP
+
+#include "utilities/json.hpp"
+#include "compiler/compilerDirectives.hpp"
+
+enum FlagType {
+ boolFlag,
+ intxFlag,
+ doubleFlag,
+ ccstrFlag,
+ UnknownFlagType
+};
+
+static const char* flag_type_names[] = {
+ "bool",
+ "int",
+ "double",
+ "string",
+ "unknown"
+};
+
+class DirectivesParser : public JSON {
+public:
+ 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();
+
+private:
+ DirectivesParser(const char* text, outputStream* st);
+ ~DirectivesParser();
+
+ bool callback(JSON_TYPE t, JSON_VAL* v, uint level);
+ static bool parse_from_file_inner(const char* filename, outputStream* st);
+
+ // types of "keys". i.e recognized <key>:<value> pairs in our JSON syntax
+ typedef enum {
+ type_c1,
+ type_c2,
+ type_enable,
+ type_preset,
+ type_match,
+ type_inline,
+
+ // After here, there is no correlation between
+ // keytype and keys array
+ //type_strategy,
+ type_flag,
+ //type_dir,
+
+ // Synthetic.
+ type_dir_array,
+ type_directives,
+ type_value_array
+ } keytype;
+
+ // name, type, dtd info and maybe a setter
+ // this is how we map key-values
+ typedef struct {
+ const char *name;
+ keytype type;
+ uint allow_array_value : 1;
+ uint allowedmask;
+ void (DirectiveSet::*set)(void* arg);
+ FlagType flag_type;
+ } key;
+
+ // Array with valid keys for the directive file
+ static const key keys[];
+ // Marker for outermost moosewings/array
+ static const key dir_array_key;
+ // Marker for a directives set (these are "implicit" objects, as in not named)
+ static const key dir_key;
+ // Marker for a multi value
+ static const key value_array_key;
+
+ // A compiler directive shouldn't be able to use more than 5 stack slots.
+ // Example of max stack usage:
+ // depth 1: type_dir_array [
+ // depth 2: type_directives {
+ // depth 3: type_c1 c1: {
+ // depth 4: type_inline inline:
+ // depth 5: type_value_array [ ...
+ static const uint MAX_DEPTH = 5;
+ const key* stack[MAX_DEPTH];
+ uint depth;
+
+ bool push_key(const char* str, size_t len);
+ bool push_key(const key* k);
+ const key* current_key();
+ const key* pop_key();
+ static const key* lookup_key(const char* s, size_t len);
+
+ bool set_option(JSON_TYPE t, JSON_VAL* v);
+ bool set_option_flag(JSON_TYPE t, JSON_VAL* v, const key* option_key, DirectiveSet* set);
+
+ CompilerDirectives* current_directive;
+ DirectiveSet* current_directiveset;
+
+ void push_tmp(CompilerDirectives* dir);
+ CompilerDirectives* pop_tmp();
+ CompilerDirectives* _tmp_top; // temporary storage for dirs while parsing
+
+ static uint mask(keytype kt);
+
+#ifndef PRODUCT
+ static void test(const char* json, bool valid);
+public:
+ static bool test();
+#endif
+};
+
+#endif // SHARE_VM_COMPILER_DIRECTIVESPARSER_HPP
--- a/hotspot/src/share/vm/compiler/methodMatcher.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/compiler/methodMatcher.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -342,6 +342,107 @@
}
}
+BasicMatcher* BasicMatcher::parse_method_pattern(char* line, const char*& error_msg) {
+ assert(error_msg == NULL, "Don't call here with error_msg already set");
+ BasicMatcher* bm = new BasicMatcher();
+ MethodMatcher::parse_method_pattern(line, error_msg, bm);
+ if (error_msg != NULL) {
+ delete bm;
+ return NULL;
+ }
+ // check for bad trailing characters
+ int bytes_read = 0;
+ sscanf(line, "%*[ \t]%n", &bytes_read);
+ if (line[bytes_read] != '\0') {
+ error_msg = "Unrecognized trailing text after method pattern";
+ delete bm;
+ return NULL;
+ }
+ return bm;
+}
+
+bool BasicMatcher::match(methodHandle method) {
+ for (BasicMatcher* current = this; current != NULL; current = current->next()) {
+ if (current->matches(method)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void InlineMatcher::print(outputStream* st) {
+ if (_inline_action == InlineMatcher::force_inline) {
+ st->print("+");
+ } else {
+ st->print("-");
+ }
+ print_base(st);
+}
+
+InlineMatcher* InlineMatcher::parse_method_pattern(char* line, const char*& error_msg) {
+ assert(error_msg == NULL, "Dont call here with error_msg already set");
+ InlineMatcher* im = new InlineMatcher();
+ MethodMatcher::parse_method_pattern(line, error_msg, im);
+ if (error_msg != NULL) {
+ delete im;
+ return NULL;
+ }
+ return im;
+}
+bool InlineMatcher::match(methodHandle method, int inline_action) {
+ for (InlineMatcher* current = this; current != NULL; current = current->next()) {
+ if (current->matches(method)) {
+ return (current->_inline_action == inline_action);
+ }
+ }
+ return false;
+}
+InlineMatcher* InlineMatcher::parse_inline_pattern(char* str, const char*& error_msg) {
+ // check first token is +/-
+ InlineType _inline_action;
+ switch (str[0]) {
+ case '-':
+ _inline_action = InlineMatcher::dont_inline;
+ break;
+ case '+':
+ _inline_action = InlineMatcher::force_inline;
+ break;
+ default:
+ error_msg = "Missing leading inline type (+/-)";
+ return NULL;
+ }
+ str++;
+
+ int bytes_read = 0;
+ assert(error_msg== NULL, "error_msg must not be set yet");
+ InlineMatcher* im = InlineMatcher::parse_method_pattern(str, error_msg);
+ if (im == NULL) {
+ assert(error_msg != NULL, "Must have error message");
+ return NULL;
+ }
+ im->set_action(_inline_action);
+ return im;
+}
+
+InlineMatcher* InlineMatcher::clone() {
+ InlineMatcher* m = new InlineMatcher();
+ m->_class_mode = _class_mode;
+ m->_method_mode = _method_mode;
+ m->_inline_action = _inline_action;
+ m->_class_name = _class_name;
+ if(_class_name != NULL) {
+ _class_name->increment_refcount();
+ }
+ m->_method_name = _method_name;
+ if (_method_name != NULL) {
+ _method_name->increment_refcount();
+ }
+ m->_signature = _signature;
+ if (_signature != NULL) {
+ _signature->increment_refcount();
+ }
+ return m;
+}
--- a/hotspot/src/share/vm/compiler/methodMatcher.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/compiler/methodMatcher.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -81,35 +81,8 @@
_next(next) {
}
- static BasicMatcher* parse_method_pattern(char* line, const char*& error_msg) {
- assert(error_msg == NULL, "Dont call here with error_msg already set");
- BasicMatcher* bm = new BasicMatcher();
- MethodMatcher::parse_method_pattern(line, error_msg, bm);
- if (error_msg != NULL) {
- delete bm;
- return NULL;
- }
-
- // check for bad trailing characters
- int bytes_read = 0;
- sscanf(line, "%*[ \t]%n", &bytes_read);
- if (line[bytes_read] != '\0') {
- error_msg = "Unrecognized trailing text after method pattern";
- delete bm;
- return NULL;
- }
- return bm;
- }
-
- bool match(methodHandle method) {
- for (BasicMatcher* current = this; current != NULL; current = current->next()) {
- if (current->matches(method)) {
- return true;
- }
- }
- return false;
- }
-
+ static BasicMatcher* parse_method_pattern(char* line, const char*& error_msg);
+ bool match(methodHandle method);
void set_next(BasicMatcher* next) { _next = next; }
BasicMatcher* next() { return _next; }
@@ -122,5 +95,33 @@
}
};
+class InlineMatcher : public MethodMatcher {
+public:
+ enum InlineType {
+ unknown_inline,
+ dont_inline,
+ force_inline
+ };
+
+private:
+ InlineType _inline_action;
+ InlineMatcher * _next;
+
+ InlineMatcher() : MethodMatcher(),
+ _inline_action(unknown_inline), _next(NULL) {
+ }
+
+public:
+ static InlineMatcher* parse_method_pattern(char* line, const char*& error_msg);
+ bool match(methodHandle method, int inline_action);
+ void print(outputStream* st);
+ void set_next(InlineMatcher* next) { _next = next; }
+ InlineMatcher* next() { return _next; }
+ void set_action(InlineType inline_action) { _inline_action = inline_action; }
+ int inline_action() { return _inline_action; }
+ static InlineMatcher* parse_inline_pattern(char* line, const char*& error_msg);
+ InlineMatcher* clone();
+};
+
#endif // SHARE_VM_COMPILER_METHODMATCHER_HPP
--- a/hotspot/src/share/vm/jvmci/jvmciCompiler.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/jvmci/jvmciCompiler.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -145,7 +145,7 @@
// Compilation entry point for methods
-void JVMCICompiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci) {
+void JVMCICompiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, DirectiveSet* directive) {
ShouldNotReachHere();
}
--- a/hotspot/src/share/vm/jvmci/jvmciCompiler.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/jvmci/jvmciCompiler.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -69,7 +69,7 @@
void bootstrap();
// Compilation entry point for methods
- virtual void compile_method(ciEnv* env, ciMethod* target, int entry_bci);
+ virtual void compile_method(ciEnv* env, ciMethod* target, int entry_bci, DirectiveSet* directive);
void compile_method(methodHandle target, int entry_bci, JVMCIEnv* env);
--- a/hotspot/src/share/vm/opto/block.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/block.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -25,6 +25,7 @@
#include "precompiled.hpp"
#include "libadt/vectset.hpp"
#include "memory/allocation.inline.hpp"
+#include "compiler/compilerDirectives.hpp"
#include "opto/block.hpp"
#include "opto/cfgnode.hpp"
#include "opto/chaitin.hpp"
@@ -365,7 +366,7 @@
, _node_to_block_mapping(arena)
, _node_latency(NULL)
#ifndef PRODUCT
-, _trace_opto_pipelining(TraceOptoPipelining || C->method_has_option("TraceOptoPipelining"))
+, _trace_opto_pipelining(C->directive()->TraceOptoPipeliningOption)
#endif
#ifdef ASSERT
, _raw_oops(arena)
--- a/hotspot/src/share/vm/opto/block.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/block.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -368,7 +368,6 @@
class PhaseCFG : public Phase {
friend class VMStructs;
private:
-
// Root of whole program
RootNode* _root;
--- a/hotspot/src/share/vm/opto/bytecodeInfo.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/bytecodeInfo.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -108,7 +108,7 @@
int caller_bci, ciCallProfile& profile,
WarmCallInfo* wci_result) {
// Allows targeted inlining
- if (callee_method->should_inline()) {
+ if (C->directive()->should_inline(callee_method)) {
*wci_result = *(WarmCallInfo::always_hot());
if (C->print_inlining() && Verbose) {
CompileTask::print_inline_indent(inline_level());
@@ -222,12 +222,12 @@
}
// ignore heuristic controls on inlining
- if (callee_method->should_inline()) {
+ if (C->directive()->should_inline(callee_method)) {
set_msg("force inline by CompileCommand");
return false;
}
- if (callee_method->should_not_inline()) {
+ if (C->directive()->should_not_inline(callee_method)) {
set_msg("disallowed by CompileCommand");
return true;
}
--- a/hotspot/src/share/vm/opto/c2_globals.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/c2_globals.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -154,7 +154,7 @@
notproduct(bool, PrintOptoStatistics, false, \
"Print New compiler statistics") \
\
- notproduct(bool, PrintOptoAssembly, false, \
+ diagnostic(bool, PrintOptoAssembly, false, \
"Print New compiler assembly output") \
\
develop_pd(bool, OptoPeephole, \
@@ -632,7 +632,7 @@
develop(bool, PrintDominators, false, \
"Print out dominator trees for GVN") \
\
- notproduct(bool, TraceSpilling, false, \
+ diagnostic(bool, TraceSpilling, false, \
"Trace spilling") \
\
diagnostic(bool, TraceTypeProfile, false, \
--- a/hotspot/src/share/vm/opto/c2compiler.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/c2compiler.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -94,15 +94,16 @@
}
}
-void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci) {
+void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, DirectiveSet* directive) {
assert(is_initialized(), "Compiler thread must be initialized");
bool subsume_loads = SubsumeLoads;
bool do_escape_analysis = DoEscapeAnalysis && !env->should_retain_local_variables();
bool eliminate_boxing = EliminateAutoBox;
+
while (!env->failing()) {
// Attempt to compile while subsuming loads into machine instructions.
- Compile C(env, this, target, entry_bci, subsume_loads, do_escape_analysis, eliminate_boxing);
+ Compile C(env, this, target, entry_bci, subsume_loads, do_escape_analysis, eliminate_boxing, directive);
// Check result and retry if appropriate.
if (C.failure_reason() != NULL) {
--- a/hotspot/src/share/vm/opto/c2compiler.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/c2compiler.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -41,7 +41,8 @@
// Compilation entry point for methods
void compile_method(ciEnv* env,
ciMethod* target,
- int entry_bci);
+ int entry_bci,
+ DirectiveSet* directive);
// sentinel value used to trigger backtracking in compile_method().
static const char* retry_no_subsuming_loads();
--- a/hotspot/src/share/vm/opto/chaitin.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/chaitin.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -211,7 +211,7 @@
, _scratch_int_pressure(0, INTPRESSURE)
, _scratch_float_pressure(0, FLOATPRESSURE)
#ifndef PRODUCT
- , _trace_spilling(TraceSpilling || C->method_has_option("TraceSpilling"))
+ , _trace_spilling(C->directive()->TraceSpillingOption)
#endif
{
Compile::TracePhase tp("ctorChaitin", &timers[_t_ctorChaitin]);
--- a/hotspot/src/share/vm/opto/compile.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/compile.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -464,7 +464,7 @@
Type::Initialize(compile);
_compile->set_scratch_buffer_blob(NULL);
_compile->begin_method();
- _compile->clone_map().set_debug(_compile->has_method() && _compile->method_has_option(_compile->clone_map().debug_option_name));
+ _compile->clone_map().set_debug(_compile->has_method() && _compile->directive()->CloneMapDebugOption);
}
CompileWrapper::~CompileWrapper() {
_compile->end_method();
@@ -496,7 +496,7 @@
tty->print_cr("** Bailout: Recompile without boxing elimination **");
tty->print_cr("*********************************************************");
}
- if (env()->break_at_compile()) {
+ if (C->directive()->BreakAtCompileOption) {
// Open the debugger when compiling this method.
tty->print("### Breaking when compiling: ");
method()->print_short_name();
@@ -617,9 +617,10 @@
Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr_bci,
- bool subsume_loads, bool do_escape_analysis, bool eliminate_boxing )
+ bool subsume_loads, bool do_escape_analysis, bool eliminate_boxing, DirectiveSet* directive)
: Phase(Compiler),
_env(ci_env),
+ _directive(directive),
_log(ci_env->log()),
_compile_id(ci_env->compile_id()),
_save_argument_registers(false),
@@ -649,7 +650,7 @@
_dead_node_list(comp_arena()),
_dead_node_count(0),
#ifndef PRODUCT
- _trace_opto_output(TraceOptoOutput || method()->has_option("TraceOptoOutput")),
+ _trace_opto_output(directive->TraceOptoOutputOption),
_in_dump_cnt(0),
_printer(IdealGraphPrinter::printer()),
#endif
@@ -673,7 +674,11 @@
_interpreter_frame_size(0),
_max_node_limit(MaxNodeLimit) {
C = this;
-
+#ifndef PRODUCT
+ if (_printer != NULL) {
+ _printer->set_compile(this);
+ }
+#endif
CompileWrapper cw(this);
if (CITimeVerbose) {
@@ -687,9 +692,9 @@
TraceTime t2(NULL, &_t_methodCompilation, CITime, false);
#ifndef PRODUCT
- bool print_opto_assembly = PrintOptoAssembly || _method->has_option("PrintOptoAssembly");
+ bool print_opto_assembly = directive->PrintOptoAssemblyOption;
if (!print_opto_assembly) {
- bool print_assembly = (PrintAssembly || _method->should_print_assembly());
+ bool print_assembly = directive->PrintAssemblyOption;
if (print_assembly && !Disassembler::can_decode()) {
tty->print_cr("PrintAssembly request changed to PrintOptoAssembly");
print_opto_assembly = true;
@@ -698,12 +703,12 @@
set_print_assembly(print_opto_assembly);
set_parsed_irreducible_loop(false);
- if (method()->has_option("ReplayInline")) {
+ if (directive->ReplayInlineOption) {
_replay_inline_data = ciReplay::load_inline_data(method(), entry_bci(), ci_env->comp_level());
}
#endif
- set_print_inlining(PrintInlining || method()->has_option("PrintInlining") NOT_PRODUCT( || PrintOptoInlining));
- set_print_intrinsics(PrintIntrinsics || method()->has_option("PrintIntrinsics"));
+ set_print_inlining(directive->PrintInliningOption NOT_PRODUCT( || PrintOptoInlining));
+ set_print_intrinsics(directive->PrintIntrinsicsOption);
set_has_irreducible_loop(true); // conservative until build_loop_tree() reset it
if (ProfileTraps RTM_OPT_ONLY( || UseRTMLocking )) {
@@ -837,8 +842,8 @@
// Drain the list.
Finish_Warm();
#ifndef PRODUCT
- if (_printer && _printer->should_print(_method)) {
- _printer->print_inlining(this);
+ if (_printer && _printer->should_print(1)) {
+ _printer->print_inlining();
}
#endif
@@ -871,10 +876,10 @@
NOT_PRODUCT( verify_barriers(); )
// Dump compilation data to replay it.
- if (method()->has_option("DumpReplay")) {
+ if (directive->DumpReplayOption) {
env()->dump_replay_data(_compile_id);
}
- if (method()->has_option("DumpInline") && (ilt() != NULL)) {
+ if (directive->DumpInlineOption && (ilt() != NULL)) {
env()->dump_inline_data(_compile_id);
}
@@ -918,9 +923,9 @@
frame_size_in_words(), _oop_map_set,
&_handler_table, &_inc_table,
compiler,
- env()->comp_level(),
has_unsafe_access(),
SharedRuntime::is_wide_vector(max_vector_size()),
+ _directive,
rtm_state()
);
@@ -938,9 +943,11 @@
int is_fancy_jump,
bool pass_tls,
bool save_arg_registers,
- bool return_pc )
+ bool return_pc,
+ DirectiveSet* directive)
: Phase(Compiler),
_env(ci_env),
+ _directive(directive),
_log(ci_env->log()),
_compile_id(0),
_save_argument_registers(save_arg_registers),
@@ -1090,7 +1097,7 @@
Copy::zero_to_bytes(_trap_hist, sizeof(_trap_hist));
set_decompile_count(0);
- set_do_freq_based_layout(BlockLayoutByFrequency || method_has_option("BlockLayoutByFrequency"));
+ set_do_freq_based_layout(_directive->BlockLayoutByFrequencyOption);
set_num_loop_opts(LoopOptsCount);
set_do_inlining(Inline);
set_max_inline_size(MaxInlineSize);
@@ -1103,7 +1110,7 @@
bool do_vector = false;
if (AllowVectorizeOnDemand) {
- if (has_method() && (method()->has_option("Vectorize") || method()->has_option("VectorizeDebug"))) {
+ if (has_method() && (_directive->VectorizeOption || _directive->VectorizeDebugOption)) {
set_do_vector_loop(true);
} else if (has_method() && method()->name() != 0 &&
method()->intrinsic_id() == vmIntrinsics::_forEachRemaining) {
@@ -1118,7 +1125,8 @@
set_age_code(has_method() && method()->profile_aging());
set_rtm_state(NoRTM); // No RTM lock eliding by default
- method_has_option_value("MaxNodeLimit", _max_node_limit);
+ _max_node_limit = _directive->MaxNodeLimitOption;
+
#if INCLUDE_RTM_OPT
if (UseRTMLocking && has_method() && (method()->method_data_or_null() != NULL)) {
int rtm_state = method()->method_data()->rtm_state();
@@ -2091,7 +2099,7 @@
TracePhase tp("optimizer", &timers[_t_optimizer]);
#ifndef PRODUCT
- if (env()->break_at_compile()) {
+ if (_directive->BreakAtCompileOption) {
BREAKPOINT;
}
@@ -4357,7 +4365,6 @@
return (os::random() & RANDOMIZED_DOMAIN_MASK) < (RANDOMIZED_DOMAIN / count);
}
-const char* CloneMap::debug_option_name = "CloneMapDebug";
CloneMap& Compile::clone_map() { return _clone_map; }
void Compile::set_clone_map(Dict* d) { _clone_map._dict = d; }
--- a/hotspot/src/share/vm/opto/compile.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/compile.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -391,6 +391,7 @@
// Compilation environment.
Arena _comp_arena; // Arena with lifetime equivalent to Compile
ciEnv* _env; // CI interface
+ DirectiveSet* _directive; // Compiler directive
CompileLog* _log; // from CompilerThread
const char* _failure_reason; // for record_failure/failing pattern
GrowableArray<CallGenerator*>* _intrinsics; // List of intrinsics.
@@ -527,6 +528,10 @@
print_inlining_stream()->print("%s", ss.as_string());
}
+#ifndef PRODUCT
+ IdealGraphPrinter* printer() { return _printer; }
+#endif
+
void log_late_inline(CallGenerator* cg);
void log_inline_id(CallGenerator* cg);
void log_inline_failure(const char* msg);
@@ -578,6 +583,7 @@
// ID for this compilation. Useful for setting breakpoints in the debugger.
int compile_id() const { return _compile_id; }
+ DirectiveSet* directive() const { return _directive; }
// Does this compilation allow instructions to subsume loads? User
// instructions that subsume a load may result in an unschedulable
@@ -671,10 +677,7 @@
bool method_has_option(const char * option) {
return method() != NULL && method()->has_option(option);
}
- template<typename T>
- bool method_has_option_value(const char * option, T& value) {
- return method() != NULL && method()->has_option_value(option, value);
- }
+
#ifndef PRODUCT
bool trace_opto_output() const { return _trace_opto_output; }
bool parsed_irreducible_loop() const { return _parsed_irreducible_loop; }
@@ -692,8 +695,8 @@
void begin_method() {
#ifndef PRODUCT
- if (_printer && _printer->should_print(_method)) {
- _printer->begin_method(this);
+ if (_printer && _printer->should_print(1)) {
+ _printer->begin_method();
}
#endif
C->_latest_stage_start_counter.stamp();
@@ -711,8 +714,8 @@
#ifndef PRODUCT
- if (_printer && _printer->should_print(_method)) {
- _printer->print_method(this, CompilerPhaseTypeHelper::to_string(cpt), level);
+ if (_printer && _printer->should_print(level)) {
+ _printer->print_method(CompilerPhaseTypeHelper::to_string(cpt), level);
}
#endif
C->_latest_stage_start_counter.stamp();
@@ -728,7 +731,7 @@
event.commit();
}
#ifndef PRODUCT
- if (_printer && _printer->should_print(_method)) {
+ if (_printer && _printer->should_print(level)) {
_printer->end_method();
}
#endif
@@ -1107,7 +1110,7 @@
// continuation.
Compile(ciEnv* ci_env, C2Compiler* compiler, ciMethod* target,
int entry_bci, bool subsume_loads, bool do_escape_analysis,
- bool eliminate_boxing);
+ bool eliminate_boxing, DirectiveSet* directive);
// Second major entry point. From the TypeFunc signature, generate code
// to pass arguments from the Java calling convention to the C calling
@@ -1115,7 +1118,7 @@
Compile(ciEnv* ci_env, const TypeFunc *(*gen)(),
address stub_function, const char *stub_name,
int is_fancy_jump, bool pass_tls,
- bool save_arg_registers, bool return_pc);
+ bool save_arg_registers, bool return_pc, DirectiveSet* directive);
// From the TypeFunc signature, generate code to pass arguments
// from Compiled calling convention to Interpreter's calling convention
--- a/hotspot/src/share/vm/opto/idealGraphPrinter.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/idealGraphPrinter.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -292,11 +292,11 @@
}
-void IdealGraphPrinter::print_inlining(Compile* compile) {
+void IdealGraphPrinter::print_inlining() {
// Print inline tree
if (_should_send_method) {
- InlineTree *inlineTree = compile->ilt();
+ InlineTree *inlineTree = C->ilt();
if (inlineTree != NULL) {
print_inline_tree(inlineTree);
} else {
@@ -306,9 +306,9 @@
}
// Has to be called whenever a method is compiled
-void IdealGraphPrinter::begin_method(Compile* compile) {
+void IdealGraphPrinter::begin_method() {
- ciMethod *method = compile->method();
+ ciMethod *method = C->method();
assert(_output, "output stream must exist!");
assert(method, "null methods are not allowed!");
assert(!_current_method, "current method must be null!");
@@ -662,16 +662,14 @@
}
}
-void IdealGraphPrinter::print_method(Compile* compile, const char *name, int level, bool clear_nodes) {
- print(compile, name, (Node *)compile->root(), level, clear_nodes);
+void IdealGraphPrinter::print_method(const char *name, int level, bool clear_nodes) {
+ print(name, (Node *)C->root(), level, clear_nodes);
}
// Print current ideal graph
-void IdealGraphPrinter::print(Compile* compile, const char *name, Node *node, int level, bool clear_nodes) {
+void IdealGraphPrinter::print(const char *name, Node *node, int level, bool clear_nodes) {
- if (!_current_method || !_should_send_method || !should_print(_current_method, level)) return;
-
- this->C = compile;
+ if (!_current_method || !_should_send_method || !should_print(level)) return;
// Warning, unsafe cast?
_chaitin = (PhaseChaitin *)C->regalloc();
@@ -722,10 +720,8 @@
}
// Should method be printed?
-bool IdealGraphPrinter::should_print(ciMethod* method, int level) {
- intx ideal_graph_level = PrintIdealGraphLevel;
- method->has_option_value("PrintIdealGraphLevel", ideal_graph_level); // update value with per-method value (if available)
- return ideal_graph_level >= level;
+bool IdealGraphPrinter::should_print(int level) {
+ return C->directive()->IGVPrintLevelOption >= level;
}
extern const char *NodeClassNames[];
--- a/hotspot/src/share/vm/opto/idealGraphPrinter.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/idealGraphPrinter.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -127,13 +127,14 @@
bool traverse_outs();
void set_traverse_outs(bool b);
- void print_inlining(Compile* compile);
- void begin_method(Compile* compile);
+ void print_inlining();
+ void begin_method();
void end_method();
- void print_method(Compile* compile, const char *name, int level=1, bool clear_nodes = false);
- void print(Compile* compile, const char *name, Node *root, int level=1, bool clear_nodes = false);
+ void print_method(const char *name, int level=1, bool clear_nodes = false);
+ void print(const char *name, Node *root, int level=1, bool clear_nodes = false);
void print_xml(const char *name);
- static bool should_print(ciMethod* method, int level = 1);
+ bool should_print(int level);
+ void set_compile(Compile* compile) {C = compile; }
};
#endif
--- a/hotspot/src/share/vm/opto/library_call.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/library_call.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -326,9 +326,10 @@
// methods access VM-internal data.
VM_ENTRY_MARK;
methodHandle mh(THREAD, m->get_Method());
- methodHandle ct(THREAD, method()->get_Method());
is_available = compiler->is_intrinsic_supported(mh, is_virtual) &&
- !vmIntrinsics::is_disabled_by_flags(mh, ct);
+ !C->directive()->is_intrinsic_disabled(mh) &&
+ !vmIntrinsics::is_disabled_by_flags(mh);
+
}
if (is_available) {
--- a/hotspot/src/share/vm/opto/output.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/output.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -28,6 +28,7 @@
#include "code/debugInfo.hpp"
#include "code/debugInfoRec.hpp"
#include "compiler/compileBroker.hpp"
+#include "compiler/compilerDirectives.hpp"
#include "compiler/oopMap.hpp"
#include "memory/allocation.inline.hpp"
#include "opto/ad.hpp"
@@ -89,9 +90,8 @@
}
-
// Break before main entry point
- if( (_method && _method->break_at_execute())
+ if( (_method && C->directive()->BreakAtExecuteOption)
#ifndef PRODUCT
||(OptoBreakpoint && is_method_compilation())
||(OptoBreakpointOSR && is_osr_compilation())
--- a/hotspot/src/share/vm/opto/parse2.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/parse2.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -2378,13 +2378,13 @@
}
#ifndef PRODUCT
- IdealGraphPrinter *printer = IdealGraphPrinter::printer();
- if (printer && printer->should_print(_method)) {
+ IdealGraphPrinter *printer = C->printer();
+ if (printer && printer->should_print(1)) {
char buffer[256];
sprintf(buffer, "Bytecode %d: %s", bci(), Bytecodes::name(bc()));
bool old = printer->traverse_outs();
printer->set_traverse_outs(true);
- printer->print_method(C, buffer, 4);
+ printer->print_method(buffer, 4);
printer->set_traverse_outs(old);
}
#endif
--- a/hotspot/src/share/vm/opto/runtime.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/runtime.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -159,9 +159,13 @@
const char *name, int is_fancy_jump,
bool pass_tls,
bool save_argument_registers,
- bool return_pc ) {
+ bool return_pc) {
+
+ // Matching the default directive, we currently have no method to match.
+ DirectiveSet* directive = DirectivesStack::getDefaultDirective(CompileBroker::compiler(CompLevel_full_optimization));
ResourceMark rm;
- Compile C( env, gen, C_function, name, is_fancy_jump, pass_tls, save_argument_registers, return_pc );
+ Compile C( env, gen, C_function, name, is_fancy_jump, pass_tls, save_argument_registers, return_pc, directive);
+ DirectivesStack::release(directive);
return C.stub_entry_point();
}
--- a/hotspot/src/share/vm/opto/superword.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/opto/superword.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -79,11 +79,12 @@
#ifndef PRODUCT
_vector_loop_debug = 0;
if (_phase->C->method() != NULL) {
- _phase->C->method()->has_option_value("VectorizeDebug", _vector_loop_debug);
+ _vector_loop_debug = phase->C->directive()->VectorizeDebugOption;
}
+
_CountedLoopReserveKit_debug = 0;
if (_phase->C->method() != NULL) {
- _phase->C->method()->has_option_value("DoReserveCopyInSuperWordDebug", _CountedLoopReserveKit_debug);
+ _CountedLoopReserveKit_debug = phase->C->directive()->DoReserveCopyInSuperWordDebugOption;
}
#endif
}
--- a/hotspot/src/share/vm/prims/jni.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/prims/jni.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -3840,7 +3840,9 @@
#if INCLUDE_ALL_GCS
#include "gc/g1/heapRegionRemSet.hpp"
#endif
+#include "compiler/directivesParser.hpp"
#include "memory/guardedMemory.hpp"
+#include "utilities/json.hpp"
#include "utilities/ostream.hpp"
#include "utilities/quickSort.hpp"
#if INCLUDE_VM_STRUCTS
@@ -3903,6 +3905,8 @@
run_unit_test(ObjectMonitor::sanity_checks());
run_unit_test(Test_linked_list());
run_unit_test(TestChunkedList_test());
+ run_unit_test(JSONTest::test());
+ run_unit_test(DirectivesParser::test());
#if INCLUDE_VM_STRUCTS
run_unit_test(VMStructs::test());
#endif
--- a/hotspot/src/share/vm/prims/whitebox.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/prims/whitebox.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -551,14 +551,20 @@
method_id = reflected_method_to_jmid(thread, env, method);
CHECK_JNI_EXCEPTION_(env, JNI_FALSE);
methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(method_id));
+
+ DirectiveSet* directive;
if (compilation_context != NULL) {
compilation_context_id = reflected_method_to_jmid(thread, env, compilation_context);
CHECK_JNI_EXCEPTION_(env, JNI_FALSE);
methodHandle cch(THREAD, Method::checked_resolve_jmethod_id(compilation_context_id));
- return CompileBroker::compiler(compLevel)->is_intrinsic_available(mh, cch);
+ directive = DirectivesStack::getMatchingDirective(cch, CompileBroker::compiler((int)compLevel));
} else {
- return CompileBroker::compiler(compLevel)->is_intrinsic_available(mh, NULL);
+ // Calling with NULL matches default directive
+ directive = DirectivesStack::getDefaultDirective(CompileBroker::compiler((int)compLevel));
}
+ bool result = CompileBroker::compiler(compLevel)->is_intrinsic_available(mh, directive);
+ DirectivesStack::release(directive);
+ return result;
WB_END
WB_ENTRY(jint, WB_GetMethodCompilationLevel(JNIEnv* env, jobject o, jobject method, jboolean is_osr))
@@ -624,6 +630,47 @@
return (mh->queued_for_compilation() || nm != NULL);
WB_END
+WB_ENTRY(jboolean, WB_ShouldPrintAssembly(JNIEnv* env, jobject o, jobject method))
+ jmethodID jmid = reflected_method_to_jmid(thread, env, method);
+ CHECK_JNI_EXCEPTION_(env, JNI_FALSE);
+
+ methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid));
+ DirectiveSet* directive = DirectivesStack::getMatchingDirective(mh, CompileBroker::compiler(CompLevel_simple));
+ bool result = directive->PrintAssemblyOption;
+ DirectivesStack::release(directive);
+
+ return result;
+WB_END
+
+WB_ENTRY(jint, WB_MatchesInline(JNIEnv* env, jobject o, jobject method, jstring pattern))
+ jmethodID jmid = reflected_method_to_jmid(thread, env, method);
+ CHECK_JNI_EXCEPTION_(env, JNI_FALSE);
+
+ methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid));
+
+ ResourceMark rm;
+ const char* error_msg = NULL;
+ char* method_str = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(pattern));
+ InlineMatcher* m = InlineMatcher::parse_inline_pattern(method_str, error_msg);
+
+ if (m == NULL) {
+ assert(error_msg != NULL, "Always have an error message");
+ tty->print_cr("Got error: %s", error_msg);
+ return -1; // Pattern failed
+ }
+
+ // Pattern works - now check if it matches
+ int result;
+ if (m->match(mh, InlineMatcher::force_inline)) {
+ result = 2; // Force inline match
+ } else if (m->match(mh, InlineMatcher::dont_inline)) {
+ result = 1; // Dont inline match
+ } else {
+ result = 0; // No match
+ }
+ delete m;
+ return result;
+WB_END
WB_ENTRY(jint, WB_MatchesMethod(JNIEnv* env, jobject o, jobject method, jstring pattern))
jmethodID jmid = reflected_method_to_jmid(thread, env, method);
@@ -1475,6 +1522,13 @@
{CC"matchesMethod",
CC"(Ljava/lang/reflect/Executable;Ljava/lang/String;)I",
(void*)&WB_MatchesMethod},
+ {CC"matchesInline",
+ CC"(Ljava/lang/reflect/Executable;Ljava/lang/String;)I",
+ (void*)&WB_MatchesInline},
+ {CC"shouldPrintAssembly",
+ CC"(Ljava/lang/reflect/Executable;)Z",
+ (void*)&WB_ShouldPrintAssembly},
+
{CC"isConstantVMFlag", CC"(Ljava/lang/String;)Z", (void*)&WB_IsConstantVMFlag},
{CC"isLockedVMFlag", CC"(Ljava/lang/String;)Z", (void*)&WB_IsLockedVMFlag},
{CC"setBooleanVMFlag", CC"(Ljava/lang/String;Z)V",(void*)&WB_SetBooleanVMFlag},
--- a/hotspot/src/share/vm/runtime/globals.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/runtime/globals.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -3650,6 +3650,9 @@
product(ccstr, CompileCommandFile, NULL, \
"Read compiler commands from this file [.hotspot_compiler]") \
\
+ diagnostic(ccstr, CompilerDirectivesFile, NULL, \
+ "Read compiler directives from this file") \
+ \
product(ccstrlist, CompileCommand, "", \
"Prepend to .hotspot_compiler; e.g. log,java/lang/String.<init>") \
\
@@ -4233,7 +4236,13 @@
"(3) no orphan methods exist for class C (i.e., methods for " \
"which the VM declares an intrinsic but that are not declared "\
"in the loaded class C. " \
- "Check (3) is available only in debug builds.")
+ "Check (3) is available only in debug builds.") \
+ \
+ diagnostic(bool, CompilerDirectivesIgnoreCompileCommands, false, \
+ "Disable backwards compatibility for compile commands.") \
+ \
+ diagnostic(bool, PrintCompilerDirectives, false, \
+ "Print compiler directives on installation.")
/*
* Macros for factoring of globals
--- a/hotspot/src/share/vm/runtime/init.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/runtime/init.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -71,7 +71,7 @@
void vtableStubs_init();
void InlineCacheBuffer_init();
void compilerOracle_init();
-void compileBroker_init();
+bool compileBroker_init();
// Initialization after compiler initialization
bool universe_post_init(); // must happen after compiler_init
@@ -131,7 +131,9 @@
vtableStubs_init();
InlineCacheBuffer_init();
compilerOracle_init();
- compileBroker_init();
+ if (!compileBroker_init()) {
+ return JNI_EINVAL;
+ }
VMRegImpl::set_regName();
if (!universe_post_init()) {
--- a/hotspot/src/share/vm/runtime/mutexLocker.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -90,6 +90,7 @@
Monitor* Compilation_lock = NULL;
Mutex* CompileTaskAlloc_lock = NULL;
Mutex* CompileStatistics_lock = NULL;
+Mutex* DirectivesStack_lock = NULL;
Mutex* MultiArray_lock = NULL;
Monitor* Terminator_lock = NULL;
Monitor* BeforeExit_lock = NULL;
@@ -264,6 +265,7 @@
def(CompiledIC_lock , Mutex , nonleaf+2, false, Monitor::_safepoint_check_always); // locks VtableStubs_lock, InlineCacheBuffer_lock
def(CompileTaskAlloc_lock , Mutex , nonleaf+2, true, Monitor::_safepoint_check_always);
def(CompileStatistics_lock , Mutex , nonleaf+2, false, Monitor::_safepoint_check_always);
+ def(DirectivesStack_lock , Mutex , special, true, Monitor::_safepoint_check_never);
def(MultiArray_lock , Mutex , nonleaf+2, false, Monitor::_safepoint_check_always); // locks SymbolTable_lock
def(JvmtiThreadState_lock , Mutex , nonleaf+2, false, Monitor::_safepoint_check_always); // Used by JvmtiThreadState/JvmtiEventController
--- a/hotspot/src/share/vm/runtime/mutexLocker.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -93,6 +93,7 @@
extern Monitor* Compilation_lock; // a lock used to pause compilation
extern Mutex* CompileTaskAlloc_lock; // a lock held when CompileTasks are allocated
extern Mutex* CompileStatistics_lock; // a lock held when updating compilation statistics
+extern Mutex* DirectivesStack_lock; // a lock held when mutating the dirstack and ref counting directives
extern Mutex* MultiArray_lock; // a lock used to guard allocation of multi-dim arrays
extern Monitor* Terminator_lock; // a lock used to guard termination of the vm
extern Monitor* BeforeExit_lock; // a lock used to guard cleanups and shutdown hooks
--- a/hotspot/src/share/vm/runtime/sharedRuntime.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -2696,6 +2696,12 @@
if (nm != NULL) {
method->set_code(method, nm);
+
+ DirectiveSet* directive = DirectivesStack::getDefaultDirective(CompileBroker::compiler(CompLevel_simple));
+ if (directive->PrintAssemblyOption) {
+ Disassembler::decode(nm, tty);
+ }
+ DirectivesStack::release(directive);
}
}
} // Unlock AdapterHandlerLibrary_lock
--- a/hotspot/src/share/vm/runtime/vm_operations.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/runtime/vm_operations.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -28,7 +28,6 @@
#include "code/codeCache.hpp"
#include "code/codeCacheExtensions.hpp"
#include "compiler/compileBroker.hpp"
-#include "compiler/compilerOracle.hpp"
#include "gc/shared/isGCActiveMark.hpp"
#include "memory/heapInspection.hpp"
#include "memory/resourceArea.hpp"
--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -25,6 +25,8 @@
#include "precompiled.hpp"
#include "classfile/classLoaderStats.hpp"
#include "classfile/compactHashtable.hpp"
+#include "compiler/compileBroker.hpp"
+#include "compiler/directivesParser.hpp"
#include "gc/shared/vmGCOperations.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/globals.hpp"
@@ -77,6 +79,11 @@
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeCacheDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<TouchedMethodsDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CompilerDirectivesPrintDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CompilerDirectivesAddDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CompilerDirectivesRemoveDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CompilerDirectivesClearDCmd>(full_export, true, false));
+
// Enhanced JMX Agent Support
// These commands won't be exported via the DiagnosticCommandMBean until an
// appropriate permission is created for them
@@ -837,6 +844,38 @@
VMThread::execute(&printCodeCacheOp);
}
+void CompilerDirectivesPrintDCmd::execute(DCmdSource source, TRAPS) {
+ DirectivesStack::print(output());
+}
+
+CompilerDirectivesAddDCmd::CompilerDirectivesAddDCmd(outputStream* output, bool heap) :
+ DCmdWithParser(output, heap),
+ _filename("filename","Name of the directives file", "STRING",true) {
+ _dcmdparser.add_dcmd_argument(&_filename);
+}
+
+void CompilerDirectivesAddDCmd::execute(DCmdSource source, TRAPS) {
+ DirectivesParser::parse_from_file(_filename.value(), output());
+}
+
+int CompilerDirectivesAddDCmd::num_arguments() {
+ ResourceMark rm;
+ CompilerDirectivesAddDCmd* dcmd = new CompilerDirectivesAddDCmd(NULL, false);
+ if (dcmd != NULL) {
+ DCmdMark mark(dcmd);
+ return dcmd->_dcmdparser.num_arguments();
+ } else {
+ return 0;
+ }
+}
+
+void CompilerDirectivesRemoveDCmd::execute(DCmdSource source, TRAPS) {
+ DirectivesStack::pop();
+}
+
+void CompilerDirectivesClearDCmd::execute(DCmdSource source, TRAPS) {
+ DirectivesStack::clear();
+}
#if INCLUDE_SERVICES
ClassHierarchyDCmd::ClassHierarchyDCmd(outputStream* output, bool heap) :
DCmdWithParser(output, heap),
--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -613,4 +613,90 @@
virtual void execute(DCmdSource source, TRAPS);
};
+class CompilerDirectivesPrintDCmd : public DCmd {
+public:
+ CompilerDirectivesPrintDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}
+ static const char* name() {
+ return "Compiler.directives_print";
+ }
+ static const char* description() {
+ return "Print all active compiler directives.";
+ }
+ static const char* impact() {
+ return "Low";
+ }
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.lang.management.ManagementPermission",
+ "monitor", NULL};
+ return p;
+ }
+ static int num_arguments() { return 0; }
+ virtual void execute(DCmdSource source, TRAPS);
+};
+
+class CompilerDirectivesRemoveDCmd : public DCmd {
+public:
+ CompilerDirectivesRemoveDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}
+ static const char* name() {
+ return "Compiler.directives_remove";
+ }
+ static const char* description() {
+ return "Remove latest added compiler directive.";
+ }
+ static const char* impact() {
+ return "Low";
+ }
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.lang.management.ManagementPermission",
+ "monitor", NULL};
+ return p;
+ }
+ static int num_arguments() { return 0; }
+ virtual void execute(DCmdSource source, TRAPS);
+};
+
+class CompilerDirectivesAddDCmd : public DCmdWithParser {
+protected:
+ DCmdArgument<char*> _filename;
+public:
+ CompilerDirectivesAddDCmd(outputStream* output, bool heap);
+ static const char* name() {
+ return "Compiler.directives_add";
+ }
+ static const char* description() {
+ return "Add compiler directives from file.";
+ }
+ static const char* impact() {
+ return "Low";
+ }
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.lang.management.ManagementPermission",
+ "monitor", NULL};
+ return p;
+ }
+ static int num_arguments();
+ virtual void execute(DCmdSource source, TRAPS);
+};
+
+class CompilerDirectivesClearDCmd : public DCmd {
+public:
+ CompilerDirectivesClearDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}
+ static const char* name() {
+ return "Compiler.directives_clear";
+ }
+ static const char* description() {
+ return "Remove all compiler directives.";
+ }
+ static const char* impact() {
+ return "Low";
+ }
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.lang.management.ManagementPermission",
+ "monitor", NULL};
+ return p;
+ }
+ static int num_arguments() { return 0; }
+ virtual void execute(DCmdSource source, TRAPS);
+};
+
#endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP
--- a/hotspot/src/share/vm/shark/sharkCompiler.cpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/shark/sharkCompiler.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -145,7 +145,8 @@
void SharkCompiler::compile_method(ciEnv* env,
ciMethod* target,
- int entry_bci) {
+ int entry_bci,
+ DirectiveSet* directive) {
assert(is_initialized(), "should be");
ResourceMark rm;
const char *name = methodname(
@@ -216,8 +217,8 @@
&handler_table,
&inc_table,
this,
- env->comp_level(),
false,
+ directive(),
false);
}
--- a/hotspot/src/share/vm/shark/sharkCompiler.hpp Tue Oct 20 13:36:20 2015 +0000
+++ b/hotspot/src/share/vm/shark/sharkCompiler.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -30,6 +30,7 @@
#include "ci/ciMethod.hpp"
#include "compiler/abstractCompiler.hpp"
#include "compiler/compileBroker.hpp"
+#include "compiler/compilerDirectives.hpp"
#include "shark/llvmHeaders.hpp"
#include "shark/sharkMemoryManager.hpp"
@@ -54,7 +55,7 @@
void initialize();
// Compile a normal (bytecode) method and install it in the VM
- void compile_method(ciEnv* env, ciMethod* target, int entry_bci);
+ void compile_method(ciEnv* env, ciMethod* target, int entry_bci, DirectiveSet* dirset);
// Print compilation timers and statistics
void print_timers();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/utilities/json.cpp Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,956 @@
+/*
+ * Copyright (c) 2015, 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
+ * 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.
+ *
+ */
+
+/*
+ * This is not really json in the state it is now.
+ * Some differences:
+ * - Double quotes around the key in an object is not enforced.
+ * i.e you can write: { foo : "bar" } instead of { "foo" : "bar" }.
+ * - Comments are allowed.
+ * - The last element in an object or array can have an ending comma.
+ */
+
+#include "precompiled.hpp"
+#include "utilities/json.hpp"
+#include "utilities/ostream.hpp"
+#include <math.h>
+
+const char* strchrnul_(const char *s, int c) {
+ const char* tmp = strchr(s, c);
+ return tmp == NULL ? s + strlen(s) : tmp;
+}
+
+JSON::JSON(const char* text, bool silent, outputStream* st)
+: start(text), pos(text), mark(text),
+ level(0), line(1), column(0), silent(silent), _valid(true), _st(st)
+{
+}
+
+void JSON::parse() {
+ assert(start != NULL, "Need something to parse");
+ if (start == NULL) {
+ _valid = false;
+ error(INTERNAL_ERROR, "JSON parser was called with a string that was NULL.");
+ } else {
+ _valid = parse_json_value();
+ }
+}
+
+bool JSON::valid() {
+ return _valid;
+}
+
+bool JSON::parse_json_value() {
+ int c;
+
+ c = skip_to_token();
+ if (c == -1) {
+ return false;
+ }
+
+ // Must start with object or array
+ if (level == 0) {
+
+ switch (c) {
+ case '{':
+ if (parse_json_object() == false) {
+ return false;
+ }
+ c = skip_to_token();
+ if (c > 0) {
+ mark_pos();
+ error(SYNTAX_ERROR, "Only one top level object/array is allowed.");
+ return false;
+ } else if (c < 0) {
+ return false;
+ }
+ return true;
+
+ case '[':
+ if (parse_json_array() == false) {
+ return false;
+ }
+ c = skip_to_token();
+ if (c > 0) {
+ mark_pos();
+ error(SYNTAX_ERROR, "Only one top level object/array is allowed.");
+ return false;
+ } else if (c < 0) {
+ return false;
+ }
+ return true;
+
+ case 0:
+ error(SYNTAX_ERROR, "EOS was encountered before any json declarations");
+ return false;
+
+ default:
+ error(SYNTAX_ERROR, "Json must start with an object or an array.");
+ return false;
+ }
+ } else { // level > 0
+ switch (c) {
+ case '{':
+ return parse_json_object();
+
+ case '[':
+ return parse_json_array();
+
+ case '"':
+ return parse_json_string();
+
+ case '-': case '0':
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ return parse_json_number();
+
+ case 't':
+ return parse_json_symbol("true", JSON_TRUE);
+
+ case 'f':
+ return parse_json_symbol("false", JSON_FALSE);
+
+ case 'n':
+ return parse_json_symbol("null", JSON_NULL);
+
+ case 0:
+ error(SYNTAX_ERROR, "EOS was encountered when expecting a json value.");
+ return false;
+
+ default:
+ error(SYNTAX_ERROR, "Could not parse as a json value (did you forget to quote your strings?).");
+ return false;
+ }
+ }
+}
+
+// Should only be called when we actually have the start of an object
+// Otherwise it is an internal error
+bool JSON::parse_json_object() {
+ NOT_PRODUCT(const char* prev_pos);
+ int c;
+
+ mark_pos();
+ // Check that we are not called in error
+ if (expect_any("{", "object start", INTERNAL_ERROR) <= 0) {
+ return false;
+ }
+
+ if (!callback(JSON_OBJECT_BEGIN, NULL, level++)) {
+ return false;
+ }
+
+ for (;;) {
+ mark_pos();
+ c = skip_to_token();
+ if (c == 0) {
+ error(SYNTAX_ERROR, "EOS when expecting an object key or object end");
+ return false;
+ } else if (c < 0) {
+ return false;
+ } else if (c == '}') {
+ // We got here from either empty object "{}" or ending comma "{a:1,}"
+ next();
+ break;
+ }
+
+ NOT_PRODUCT(prev_pos = pos);
+ if (parse_json_key() == false) {
+ return false;
+ }
+ assert(pos > prev_pos, "parsing stalled");
+
+ skip_to_token();
+ mark_pos();
+ if (expect_any(":", "object key-value separator") <= 0) {
+ return false;
+ }
+
+ skip_to_token();
+ mark_pos();
+ NOT_PRODUCT(prev_pos = pos);
+ if (parse_json_value() == false) {
+ return false;
+ }
+ assert(pos > prev_pos, "parsing stalled");
+
+ c = skip_to_token();
+ mark_pos();
+ if (expect_any(",}", "value separator or object end") <= 0) {
+ return false;
+ }
+ if (c == '}') {
+ break;
+ }
+ }
+
+ assert(c == '}', "array parsing ended without object end token ('}')");
+ return callback(JSON_OBJECT_END, NULL, --level);
+}
+
+// Should only be called when we actually have the start of an array
+// Otherwise it is an internal error
+bool JSON::parse_json_array() {
+ NOT_PRODUCT(const char* prev_pos);
+ int c;
+
+ mark_pos();
+ // Check that we are not called in error
+ if (expect_any("[", "array start character", INTERNAL_ERROR) <= 0) {
+ return false;
+ }
+
+ if (!callback(JSON_ARRAY_BEGIN, NULL, level++)) {
+ return false;
+ }
+
+ for (;;) {
+ mark_pos();
+ c = skip_to_token();
+ if (c == 0) {
+ error(SYNTAX_ERROR, "EOS when expecting a json value or array end");
+ return false;
+ } else if (c < 0) {
+ return false;
+ } else if (c == ']') {
+ // We got here from either empty array "[]" or ending comma "[1,]"
+ next();
+ break;
+ }
+
+ mark_pos();
+ NOT_PRODUCT(prev_pos = pos);
+ if (parse_json_value() == false) {
+ return false;
+ }
+ assert(pos > prev_pos, "parsing stalled");
+
+ c = skip_to_token();
+ mark_pos();
+ if (expect_any(",]", "value separator or array end") <= 0) {
+ return false;
+ }
+ if (c == ']') {
+ break;
+ }
+ }
+
+ assert(c == ']', "array parsing ended without array end token (']')");
+ return callback(JSON_ARRAY_END, NULL, --level);
+}
+
+bool JSON::parse_json_string(bool key) {
+ const char* end;
+ JSON_VAL v;
+
+ mark_pos();
+ if (expect_any("\"", "string start character", INTERNAL_ERROR) <= 0) {
+ return false;
+ }
+
+ end = strchr(pos, '"'); // TODO: escapes
+ if (end == NULL) {
+ error(SYNTAX_ERROR, "String started here never ended. Expected \'\"\' before EOS.");
+ return false;
+ }
+
+ v.str.start = pos;
+ v.str.length = end - pos;
+ skip(end - pos);
+
+ if (expect_any("\"", "string end character", INTERNAL_ERROR) <= 0) {
+ return false;
+ }
+
+ if (key == true) {
+ return callback(JSON_KEY, &v, level);
+ } else {
+ return callback(JSON_STRING, &v, level);
+ }
+}
+
+// TODO: hotspot equivalents?
+static bool is_alpha(u_char c) {
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+static bool is_numeric(u_char c) {
+ return (c >= '0' && c <= '9');
+}
+static bool is_alnum(u_char c) {
+ return is_alpha(c) || is_numeric(c);
+}
+static bool is_word(u_char c) {
+ return c == '_' || is_alnum(c);
+}
+
+// Allow object keys to be without quotation,
+// but then restrict to ([a-zA-Z0-9_])+
+bool JSON::parse_json_key() {
+ const char* begin;
+ JSON_VAL v;
+ u_char c;
+
+ mark_pos();
+ c = peek();
+ if (c == '"') {
+ return parse_json_string(true);
+ }
+
+ begin = pos;
+ c = peek();
+ if (c == 0) {
+ error(SYNTAX_ERROR, "Got EOS when expecting an object key.");
+ return false;
+ } else if (is_word(c) == false) {
+ error(SYNTAX_ERROR, "Expected an object key, which can be a double-quoted (\") string or a simple string (only alphanumeric characters and underscore, separated by whitespace) that doesn't need to be quoted.");
+ return false;
+ }
+
+ for (;;) {
+ c = peek();
+ // Allow the key to be delimited by control characters and the object key-value separator ':'
+ if (c <= ' ' || c == ':') {
+ break;
+ } else if (is_word(c) == false) {
+ error(SYNTAX_ERROR, "Object key need to be quoted, or consist entirely of alphanumeric characters and underscores.");
+ return false;
+ }
+ next();
+ }
+
+ v.str.start = begin;
+ v.str.length = pos - begin;
+ return callback(JSON_KEY, &v, level);
+}
+
+bool JSON::parse_json_number() {
+ double double_value;
+ int tokens, read;
+ JSON_VAL v;
+
+ mark_pos();
+
+ // Parsing number - for simplicity ints are limited to 2**53
+ // sscanf as a double and check if part is 0.
+ tokens = sscanf(pos, "%lf%n", &double_value, &read);
+ assert(tokens <= 1, "scanf implementation that counts as a token, parsing json numbers will always fail");
+ if (tokens == 1) {
+ assert(read > 0, "sanity");
+
+ if (floor(double_value) == double_value) {
+ // No exponent - treat as an int
+ v.int_value = (int)double_value;
+ if (!callback(JSON_NUMBER_INT, &v, level)) {
+ return false;
+ }
+ } else {
+ v.double_value = double_value;
+ if (!callback(JSON_NUMBER_FLOAT, &v, level)) {
+ return false;
+ }
+ }
+ skip(read);
+ return true;
+ }
+
+ error(SYNTAX_ERROR, "Couldn't parse json number (note that exponents are not supported).");
+ return false;
+}
+
+bool JSON::parse_json_symbol(const char* name, JSON_TYPE symbol) {
+ if (expect_string(name, "maybe you forgot to quote your strings?") == false) {
+ mark_pos();
+ return false;
+ }
+ return callback(symbol, NULL, level);
+}
+
+void JSON::mark_pos() {
+ assert((mark == start || *(mark - 1)) != 0, "buffer overrun");
+ assert(mark <= pos, "mark runahead");
+
+ u_char c;
+
+ while (mark < pos) {
+ c = *mark;
+ assert(c != 0, "pos buffer overrun?");
+ if (c != 0) {
+ mark++;
+ column++;
+ }
+ if (c == '\n') {
+ line++;
+ column = 0;
+ }
+ }
+
+ assert(mark <= pos, "mark runahead");
+}
+
+u_char JSON::next() {
+ assert((pos == start || *(pos - 1)) != 0, "buffer overrun");
+
+ u_char c = *pos;
+ if (c != 0) {
+ pos++;
+ }
+ return c;
+}
+
+u_char JSON::peek() {
+ return *pos;
+}
+
+// Peek ahead i chars (0 is same as peek())
+u_char JSON::peek(size_t i) {
+ u_char c;
+ const char* p;
+
+ p = pos;
+ c = *p;
+ while (i > 0 && c != 0) {
+ i--;
+ p++;
+ c = *p;
+ }
+ return c;
+}
+
+/*
+ * Check that one of the expected characters is next in the stream.
+ * If not, it is an error.
+ * Returns 0 if EOS is encountered.
+ * Returns -1 if the next character was not one of the expected.
+ * Otherwise consumes and returns the expected character that was encountered.
+ */
+int JSON::expect_any(const char* valid_chars, const char* error_msg, JSON_ERROR e) {
+ size_t len;
+ u_char c;
+
+ len = strlen(valid_chars);
+ assert(len > 0, "need non-empty string");
+
+ c = peek();
+ if (c == 0) {
+ error(e, "Got EOS when expecting %s (%s\'%s\').", error_msg, len > 1 ? "one of " : "", valid_chars);
+ return 0;
+ }
+ for (size_t i = 0; i < len; i++) {
+ if (c == valid_chars[i]) {
+ return next();
+ }
+ }
+ error(e, "Expected %s (%s\'%s\').", error_msg, len > 1 ? "one of " : "", valid_chars);
+ return -1;
+}
+
+/*
+ * Check that the expected string is next in the stream.
+ * If not, it is an error.
+ * Consumes the expected characters if they are present.
+ * Returns true if the expected characters were present, otherwise false.
+ */
+bool JSON::expect_string(const char* expected_string, const char* error_msg, JSON_ERROR e) {
+ u_char c, expected_char;
+ size_t len;
+
+ assert(expected_string != NULL, "need non-null string");
+ len = strlen(expected_string);
+ assert(len > 0, "need non-empty string");
+
+ for (size_t i = 0; i < len; i++) {
+ expected_char = expected_string[i];
+ assert(expected_char > ' ', "not sane for control characters");
+ if (expected_char <= ' ') {
+ error(INTERNAL_ERROR, "expect got a control char");
+ }
+ c = pos[i];
+ if (c == 0) {
+ error(e, "EOS encountered when expecting %s (\"%s\")", error_msg, expected_string);
+ return false;
+ } else if (c != expected_char) {
+ error(e, "Expected \"%s\" (%s)", expected_string, error_msg);
+ return false;
+ }
+ }
+ skip(len);
+ return true;
+}
+
+/*
+ * Skip i characters.
+ * Returns number of characters skipped.
+ */
+size_t JSON::skip(size_t i) {
+ u_char c;
+ size_t j;
+
+ c = peek();
+ for (j = i; c != 0 && j > 0; j--) {
+ c = next();
+ }
+ return i - j;
+}
+
+/*
+ * Skip whitespace and comments.
+ * Returns the first token after whitespace/comments without consuming it
+ * Returns 0 if EOS is encountered.
+ * Returns -1 if there is an error
+ */
+int JSON::skip_to_token() {
+ for (;;) {
+ int c = peek(0);
+ if (c == '/') {
+ u_char c2 = peek(1);
+ if (c2 == '/') {
+ c = skip_line_comment();
+ } else if (c2 == '*') {
+ c = skip_block_comment();
+ if (c < 0) {
+ return -1;
+ }
+ }
+ // Fall through to keep checking if there
+ // are more whitespace / comments to skip
+ }
+ if (c == 0 || c > ' ') {
+ return c;
+ }
+ next();
+ }
+ return 0;
+}
+
+/*
+ * Skip to, and return the wanted char without consuming it
+ * Returns 0 if EOS is encountered.
+ */
+u_char JSON::skip_to(u_char want) {
+ // We want the bookkeeping done in next().
+ // Otherwise strchr could have been used.
+ u_char c;
+ for(;;) {
+ c = peek();
+ if (c == 0 || c == want) {
+ return c;
+ }
+ next();
+ }
+}
+
+/*
+ * Should only be called when we actually have a line comment to skip.
+ * Otherwise it is an internal error.
+ *
+ * Will return the first token after the line comment without consuming it.
+ * Returns 0 if EOS is encoutered.
+ */
+u_char JSON::skip_line_comment() {
+ u_char c;
+
+ // Check that we are not called in error
+ expect_any("/", "line comment start", INTERNAL_ERROR);
+ expect_any("/", "line comment start", INTERNAL_ERROR);
+
+ c = skip_to('\n');
+ if (c == 0) {
+ return 0;
+ }
+ next();
+ return next();
+}
+
+/*
+ * Should only be called when we actually have a block comment to skip.
+ * Otherwise it is an internal error.
+ *
+ * Returns the first token after the block comment without consuming it.
+ * Returns -1 if EOS is encountered in the middle of a comment.
+ */
+int JSON::skip_block_comment() {
+ const char* current;
+
+ // Check that we are not called in error.
+ if (peek() != '/' || peek(1) != '*') {
+ // Let expect handle EOS.
+ expect_string("/*", "block comment start", INTERNAL_ERROR);
+ return 0;
+ }
+
+ current = pos;
+ for (;;) {
+ current = strchrnul_(current, '*');
+
+ if (current[0] == 0 || current[1] == 0) {
+ // Advance error marker to start of block comment
+ mark_pos();
+ error(SYNTAX_ERROR, "Block comment started here never ended. Expected \"*/\" before EOS.");
+ return -1;
+ }
+
+ if (current[1] == '/') {
+ pos = current;
+ if (expect_string("*/", "block comment end", INTERNAL_ERROR) == false) {
+ return -1;
+ }
+ // Found block comment end
+ return peek();
+ }
+ current++;
+ }
+}
+
+const char* JSON::strerror(JSON_ERROR e) {
+ switch (e) {
+ case SYNTAX_ERROR:
+ return "Syntax error";
+ case INTERNAL_ERROR:
+ return "Internal error";
+ case KEY_ERROR:
+ return "Key error";
+ case VALUE_ERROR:
+ return "Value error";
+ default:
+ ShouldNotReachHere();
+ return "Unknown error";
+ }
+}
+
+void JSON::error(JSON_ERROR e, const char* format, ...) {
+ _valid = false;
+
+ if (!silent) {
+ const char* line_start;
+ const char* tmp;
+ size_t line_length;
+ va_list args;
+ u_char c;
+
+ _st->print("%s on line %u byte %u: ", JSON::strerror(e), line, column + 1);
+ va_start(args, format);
+ _st->vprint(format, args);
+ _st->cr();
+ va_end(args);
+
+ line_start = mark - column;
+ assert(line_start >= start, "out of bounds");
+ assert(line_start <= mark, "out of bounds");
+ assert(line_start == start || line_start[-1] == '\n', "line counting error");
+
+ c = *pos;
+ if (c == 0) {
+ _st->print(" Got ");
+ _st->print_cr("EOS.");
+ }
+ tmp = mark;
+ c = *tmp;
+ if (c > ' ') {
+ _st->print(" At ");
+ _st->print("'");
+ while (c > ' ') {
+ _st->print("%c", c);
+ tmp++;
+ c = *tmp;
+ }
+ _st->print_cr("'.");
+ }
+
+ // Skip to newline or EOS
+ tmp = strchrnul_(mark, '\n');
+ line_length = tmp - line_start;
+
+ _st->print_cr("%s", line_start);
+ }
+}
+
+#ifndef PRODUCT
+void JSONTest::test(const char* text, bool should_pass) {
+ JSONTest json(text);
+ if (should_pass) {
+ assert(json.valid() == true, "failed on a valid json string");
+ if (VerboseInternalVMTests) {
+ tty->print_cr("-- json test passed as expected --");
+ }
+ } else {
+ assert(json.valid() == false, "succeeded on an invalid json string");
+ if (VerboseInternalVMTests) {
+ tty->print_cr("-- json test failed as expected --");
+ }
+ }
+}
+
+JSONTest::JSONTest(const char* text) : JSON(text, !VerboseInternalVMTests, tty) {
+ prev = JSON_NONE;
+ parse();
+}
+
+bool JSONTest::test() {
+ JSONTest::test("{}", true);
+ JSONTest::test("[]", true);
+ JSONTest::test(" { } ", true);
+ JSONTest::test(" [ ] ", true);
+
+ JSONTest::test("\"error\"", false);
+ JSONTest::test("error", false);
+ JSONTest::test("1", false);
+ JSONTest::test("1.2", false);
+ JSONTest::test("true", false);
+ JSONTest::test("false", false);
+ JSONTest::test("null", false);
+
+ JSONTest::test("[ 1 ]", true);
+ JSONTest::test("[ 1, ]", true);
+ JSONTest::test("[ true ]", true);
+ JSONTest::test("[ true, ]", true);
+ JSONTest::test("[ false ]", true);
+ JSONTest::test("[ false, ]", true);
+ JSONTest::test("[ null ]", true);
+ JSONTest::test("[ null, ]", true);
+ JSONTest::test("[ \"\" ]", true);
+ JSONTest::test("[ \"\", ]", true);
+ JSONTest::test("[ \"elem1\" ]", true);
+ JSONTest::test("[ \"elem1\", ]", true);
+ JSONTest::test("[ \"elem1\", ]", true);
+ JSONTest::test("[ \"elem1\" ]", true);
+ JSONTest::test("[ \"elem1\", \"elem2\" ]", true);
+ JSONTest::test("[ \"elem1\", \"elem2\", ]", true);
+
+
+ JSONTest::test("[ \"elem1\" ] { }", false);
+ JSONTest::test("[ elem1, \"elem2\" ]", false);
+ JSONTest::test("[ \"elem1\"", false);
+ JSONTest::test("[ \"elem1 ]", false);
+ JSONTest::test("[ \"elem1\", \"elem2\"", false);
+ JSONTest::test("[ truefoo ]", false);
+ JSONTest::test("[ falsefoo ]", false);
+ JSONTest::test("[ nullfoo ]", false);
+
+ JSONTest::test("{ key : 1 }", true);
+ JSONTest::test("{ key : 1, }", true);
+ JSONTest::test("{ key : 1.2 }", true);
+ JSONTest::test("{ key : true }", true);
+ JSONTest::test("{ key : true, }", true);
+ JSONTest::test("{ key : false }", true);
+ JSONTest::test("{ key : false, }", true);
+ JSONTest::test("{ key : null }", true);
+ JSONTest::test("{ key : null, }", true);
+ JSONTest::test("{ \"\" : \"\" }", true);
+ JSONTest::test("{ \"\" : \"\", }", true);
+ JSONTest::test("{ \"key1\" : \"val1\" }", true);
+ JSONTest::test("{ \"key1\" : \"val1\", }", true);
+ JSONTest::test("{ \"key1\" : \"val1\", \"key2\" : \"val2\" }", true);
+ JSONTest::test("{ \"key1\" : \"val1\", \"key2\" : \"val2\", }", true);
+
+ JSONTest::test("{ \"key\" : \"val\" } [ \"error\" ]", false);
+ JSONTest::test("{ \"key\" : \"val\" ", false);
+
+ JSONTest::test("/**/ { }", true);
+ JSONTest::test("/* */ { }", true);
+ JSONTest::test("/*foo*/ { }", true);
+ JSONTest::test("/* *foo */ { }", true);
+ JSONTest::test("/* *foo* */ { }", true);
+ JSONTest::test("/* /*foo */ { }", true);
+ JSONTest::test("{ } /* foo */", true);
+ JSONTest::test("{ } /* foo */ ", true);
+ JSONTest::test("{ } //", true);
+ JSONTest::test("{ } // ", true);
+ JSONTest::test("{ } // foo", true);
+
+ JSONTest::test("/* * / { }", false);
+ JSONTest::test("/ * */ { }", false);
+ JSONTest::test("// { }", false);
+ JSONTest::test("/* { } */", false);
+ JSONTest::test("/* { } */ ", false);
+ JSONTest::test("/* { } ", false);
+ JSONTest::test("{ } /* ", false);
+ JSONTest::test("/* { } *", false);
+ JSONTest::test("{ /* } */", false);
+ JSONTest::test("[ /* ] */", false);
+ JSONTest::test("{ key : \"val\", /* } */", false);
+ JSONTest::test("[ \"val\", /* ] */", false);
+
+ JSONTest::test("/* comment */{ key1 : { \"key2\" : { \"key3\" : [ \"elem1\", \"elem2\", { \"key4\" : null }, 3 , 2 , 1 , 0 , -1 , -2 , -3 , true, false, null, ] }, \"key5\" : true }, \"key6\" : [ \"☃\" ], key7 : \"val\",}", true);
+ JSONTest::test("/* comment */ { \"key1\" : { \"key2\" : { \"key3\" : [ \"elem1\", \"elem2\", { \"key4\" : null }, 3 , 2 , 1 , 0 , -1 , -2 , -3 , true, false, null, ] }, \"key5\" : true }, \"key6\" : [ \"☃\" ], key7 : \"val\",}", true);
+ JSONTest::test("/*comment*/{\"ff1 fsd\":{\"☃\":{\"☃\":[\"☃\",\"☃\"]},\"☃\":true},\"☃\":[\"☃\"],\"foo\":\"☃\",}", true);
+ JSONTest::test("/* comment */ { key1 error : { \"☃\" : { \"☃\" : [ \"☃\", \"☃\" ] }, \"☃\" : true }, \"baz\" : [ \"☃\" ], foo : \"☃\",}", false); // first key needs to be quoted since it contains a space
+
+
+ JSONTest::test("[\n]", true);
+
+ JSONTest::test(
+ "[" "\n"
+ " {"
+ " // pattern to match against class+method+signature" "\n"
+ " // leading and trailing wildcard (*) allowed" "\n"
+ " match: \"foo.bar.*\"," "\n"
+ " " "\n"
+ " // override defaults for specified compiler" "\n"
+ " // we may differentiate between levels too. TBD." "\n"
+ " c1: {" "\n"
+ " //override c1 presets " "\n"
+ " array_bounds_check_removal: false" "\n"
+ " }," "\n"
+ "" "\n"
+ " c2: {" "\n"
+ " // control inlining of method" "\n"
+ " // + force inline, - dont inline" "\n"
+ " inline : [ \"+java.util.*\", \"-com.sun.*\"]," "\n"
+ " }," "\n"
+ "" "\n"
+ " // directives outside a specific preset applies to all compilers" "\n"
+ " inline : [ \"+java.util.*\", \"-com.sun.*\"]," "\n"
+ " print_assembly: true," "\n"
+ " verify_oopmaps: true," "\n"
+ " max_loop_unrolling: 5" "\n"
+ " }," "\n"
+ " {" "\n"
+ " // matching several patterns require an array" "\n"
+ " match: [\"baz.*\",\"frob*\"]," "\n"
+ "" "\n"
+ " // only enable c1 for this directive" "\n"
+ " // all enabled by default. Command disables all not listed" "\n"
+ " enable: \"c1\"," "\n"
+ "" "\n"
+ " // applies to all compilers" "\n"
+ " // + force inline, - dont inline" "\n"
+ " inline : [ \"+java.util.*\", \"-com.sun.*\"]," "\n"
+ " print_inlining: true," "\n"
+ "" "\n"
+ " // force matching compiles to be blocking/syncronous" "\n"
+ " blocking_compile: true" "\n"
+ " }," "\n"
+ "]" "\n", true);
+
+ return true;
+}
+
+void JSONTest::log(uint indent, const char* format, ...) {
+ if (VerboseInternalVMTests) {
+ if (prev != JSON_KEY) {
+ for (uint i = 0; i < indent; i++) {
+ _st->print(" ");
+ }
+ }
+ va_list args;
+ va_start(args, format);
+ _st->vprint(format, args);
+ va_end(args);
+ }
+}
+
+bool JSONTest::callback(JSON_TYPE t, JSON_VAL* v, uint rlevel) {
+ switch (t) {
+ case JSON_OBJECT_BEGIN:
+ log(rlevel, "{\n");
+ prev = JSON_NONE; // Only care about JSON_KEY, to indent correctly
+ return true;
+
+ case JSON_OBJECT_END:
+ log(rlevel, "},\n");
+ prev = JSON_NONE;
+ return true;
+
+ case JSON_ARRAY_BEGIN:
+ log(rlevel, "[\n");
+ prev = JSON_NONE;
+ return true;
+
+ case JSON_ARRAY_END:
+ log(rlevel, "],\n");
+ prev = JSON_NONE;
+ return true;
+
+ case JSON_KEY:
+ if (VerboseInternalVMTests) {
+ for (uint i = 0; i < rlevel; i++) {
+ _st->print(" ");
+ }
+ _st->print("<key>");
+ for (size_t i = 0; i < v->str.length; i++) {
+ u_char c = v->str.start[i];
+ assert(c != 0, "string overrun");
+ if (c == 0) {
+ return false;
+ }
+ _st->print("%c", c);
+ }
+ _st->print(" : ");
+ }
+ prev = JSON_KEY;
+ return true;
+
+ case JSON_STRING:
+ if (VerboseInternalVMTests) {
+ if (prev != JSON_KEY) {
+ for (uint i = 0; i < rlevel; i++) {
+ _st->print(" ");
+ }
+ }
+ _st->print("<str>");
+ for (size_t i = 0; i < v->str.length; i++) {
+ u_char c = v->str.start[i];
+ assert(c != 0, "string overrun");
+ if (c == 0) {
+ return false;
+ }
+ _st->print("%c", c);
+ }
+ _st->print(",\n");
+ }
+ prev = JSON_NONE;
+ return true;
+
+ case JSON_NUMBER_INT:
+ log(rlevel, "<int>%" PRId64 ",\n", v->int_value);
+ prev = JSON_NONE;
+ return true;
+
+ case JSON_NUMBER_FLOAT:
+ log(rlevel, "<double>%lf,\n", v->double_value);
+ prev = JSON_NONE;
+ return true;
+
+ case JSON_TRUE:
+ log(rlevel, "<true>,\n");
+ prev = JSON_NONE;
+ return true;
+
+ case JSON_FALSE:
+ log(rlevel, "<false>,\n");
+ prev = JSON_NONE;
+ return true;
+
+ case JSON_NULL:
+ log(rlevel, "<null>,\n");
+ prev = JSON_NONE;
+ return true;
+
+ default:
+ error(INTERNAL_ERROR, "unknown JSON type");
+ return false;
+ }
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/utilities/json.hpp Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2015, 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
+ * 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.
+ *
+ */
+
+#ifndef SHARE_VM_UTILITIES_JSON_HPP
+#define SHARE_VM_UTILITIES_JSON_HPP
+
+#include "memory/allocation.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/ostream.hpp"
+
+class JSON : public ResourceObj {
+ protected:
+ JSON(const char* text, bool silent, outputStream* st);
+ void parse();
+ bool valid();
+
+ typedef enum {
+ JSON_NONE,
+ JSON_OBJECT_BEGIN,
+ JSON_OBJECT_END,
+ JSON_ARRAY_BEGIN,
+ JSON_ARRAY_END,
+ JSON_KEY,
+ JSON_STRING,
+ JSON_NUMBER_INT,
+ JSON_NUMBER_FLOAT,
+ JSON_TRUE,
+ JSON_FALSE,
+ JSON_NULL
+ } JSON_TYPE;
+
+ typedef union {
+ int64_t int_value;
+ double double_value;
+
+ struct {
+ const char* start;
+ size_t length;
+ } str;
+ } JSON_VAL;
+
+ typedef enum {
+ INTERNAL_ERROR,
+ SYNTAX_ERROR,
+ KEY_ERROR,
+ VALUE_ERROR
+ } JSON_ERROR;
+
+ void error(JSON_ERROR e, const char* format, ...) ATTRIBUTE_PRINTF(3, 4);
+ outputStream* _st;
+
+ private:
+ const char* start;
+ const char* pos;
+
+ // For error printing
+ const char* mark; // Error marker
+ uint level;
+ uint line;
+ uint column;
+
+ bool silent;
+ bool _valid;
+
+ bool parse_json_value();
+ bool parse_json_object();
+ bool parse_json_array();
+ bool parse_json_string(bool key = false);
+ bool parse_json_key();
+ bool parse_json_number();
+ bool parse_json_symbol(const char* name, JSON_TYPE symbol);
+
+ virtual bool callback(JSON_TYPE t, JSON_VAL* v, uint level) = 0;
+
+ void mark_pos();
+ u_char next();
+ u_char peek();
+ u_char peek(size_t i);
+ int expect_any(const char* valid_chars, const char* error_msg, JSON_ERROR e = SYNTAX_ERROR);
+ bool expect_string(const char* expected_string, const char* error_msg = "", JSON_ERROR e = SYNTAX_ERROR);
+ size_t skip(size_t i);
+ int skip_to_token();
+ u_char skip_to(u_char want);
+ u_char skip_line_comment();
+ int skip_block_comment();
+
+ const char* strerror(JSON_ERROR e);
+};
+
+#ifndef PRODUCT
+class JSONTest : public JSON {
+ public:
+ static bool test();
+
+ private:
+ JSONTest(const char* text);
+ static void test(const char* json, bool valid);
+
+ void log(uint level, const char* format, ...) ATTRIBUTE_PRINTF(3, 4);
+
+ bool callback(JSON_TYPE t, JSON_VAL* v, uint level);
+ JSON_TYPE prev;
+};
+#endif
+
+#endif // SHARE_VM_UTILITIES_JSON_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/compilercontrol/InlineMatcherTest.java Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2015, 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
+ * 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 InlineMatcherTest
+ * @bug 8074095
+ * @library /testlibrary /../../test/lib
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI InlineMatcherTest
+ * @summary Testing of compiler/InlineMatcher
+ */
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import sun.hotspot.WhiteBox;
+
+public class InlineMatcherTest {
+
+ /** Instance of WhiteBox */
+ protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+
+ Method helper;
+ Method getDate;
+ Method inner;
+ Method toString;
+
+ final public static int FORCE_INLINE = 2;
+ final public static int DONT_INLINE = 1;
+ final public static int NO_MATCH = 0;
+ final public static int PARSING_FAILURE = -1;
+
+ public InlineMatcherTest() {
+
+ }
+
+ public void test() throws Exception {
+ // instantiate before calling getMethod on innerHelper
+ TestCases testCases = new TestCases();
+
+ helper = getMethod(InlineMatcherTest.class, "helper");
+
+ testCases.add(helper, "*.*", PARSING_FAILURE);
+ testCases.add(helper, "+*.*", FORCE_INLINE);
+ testCases.add(helper, "++*.*", NO_MATCH); // + is a valid part of the
+ // class name
+ testCases.add(helper, "-*.*", DONT_INLINE);
+ testCases.add(helper, "--*.*", NO_MATCH); // - is a valid part of the
+ // class name
+
+ testCases.add(helper, "+InlineMatcherTest.*", FORCE_INLINE);
+ testCases.add(helper, "+InlineMatcherTest.helper", FORCE_INLINE);
+ testCases.add(helper, "+InlineMatcherTest.helper()", FORCE_INLINE);
+ testCases.add(helper, "+InlineMatcherTest.helper()V", FORCE_INLINE);
+ testCases.add(helper, "+InlineMatcherTest.helper(", FORCE_INLINE);
+
+ testCases.add(helper, "-InlineMatcherTest.*", DONT_INLINE);
+ testCases.add(helper, "-InlineMatcherTest.helper", DONT_INLINE);
+ testCases.add(helper, "-InlineMatcherTest.helper()", DONT_INLINE);
+ testCases.add(helper, "-InlineMatcherTest.helper()V", DONT_INLINE);
+ testCases.add(helper, "-InlineMatcherTest.helper(", DONT_INLINE);
+
+ testCases.add(helper, "+abc.*", NO_MATCH);
+ testCases.add(helper, "+*.abc", NO_MATCH);
+ testCases.add(helper, "-abc.*", NO_MATCH);
+ testCases.add(helper, "-*.abcls ", NO_MATCH);
+
+ int failures = 0;
+
+ for (TestCase t : testCases) {
+ System.out.println("Test case: " + t.pattern);
+ if (!t.test()) {
+ failures++;
+ System.out.println(" * FAILED");
+ }
+ }
+ if (failures != 0) {
+ throw new Exception("There where " + failures + " failures in this test");
+ }
+ }
+
+ public static void main(String... args) throws Exception {
+ InlineMatcherTest test = new InlineMatcherTest();
+ test.test();
+ }
+
+ public void helper() {
+
+ }
+
+ private static Method getMethod(Class klass, String name, Class<?>... parameterTypes) {
+ try {
+ return klass.getDeclaredMethod(name, parameterTypes);
+ } catch (NoSuchMethodException | SecurityException e) {
+ throw new RuntimeException("exception on getting method Helper." + name, e);
+ }
+ }
+
+ class TestCase {
+ String pattern;
+ Method testTarget;
+ int expectedResult;
+
+ public TestCase(Method testTarget, String pattern, int expectedResult) {
+ this.testTarget = testTarget;
+ this.pattern = pattern;
+ this.expectedResult = expectedResult;
+ }
+
+ public String resultAsStr(int errorCode) {
+ switch (errorCode) {
+ case PARSING_FAILURE:
+ return "Parsing failed";
+ case NO_MATCH:
+ return "No match";
+ case DONT_INLINE:
+ return "Dont Inline";
+ case FORCE_INLINE:
+ return "Force Inline";
+ default:
+ return "Unknown error";
+ }
+ }
+
+ boolean test() {
+ int result = WHITE_BOX.matchesInline(testTarget, pattern);
+ if (result != expectedResult) {
+ System.out
+ .println("FAIL Wrong result, Got: " + resultAsStr(result) + "\n TestCase: " + this.toString());
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Method: '" + testTarget.toString() + "' Pattern: '" + pattern + "' Expected: "
+ + resultAsStr(expectedResult);
+ }
+
+ public void innerHelper() {
+
+ }
+ }
+
+ class TestCases extends ArrayList<TestCase> {
+ private static final long serialVersionUID = 1L;
+
+ public boolean add(Method testTarget, String pattern, int expectedResult) {
+ return super.add(new TestCase(testTarget, pattern, expectedResult));
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityBase.java Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2015, 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
+ * 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 TestCompilerDirectivesCompatibilityBase
+ * @bug 8137167
+ * @library /testlibrary /../../test/lib
+ * @modules java.base/sun.misc
+ * java.compiler
+ * java.management
+ * @build jdk.test.lib.*
+ * @build jdk.test.lib.dcmd.*
+ * @build sun.hotspot.WhiteBox.*
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run testng/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI TestCompilerDirectivesCompatibilityBase
+ * @summary Test compiler control compatibility with compile command
+ */
+
+import jdk.test.lib.dcmd.CommandExecutor;
+import jdk.test.lib.dcmd.JMXExecutor;
+
+import org.testng.annotations.Test;
+import org.testng.Assert;
+
+import sun.hotspot.WhiteBox;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.StringReader;
+import java.lang.reflect.Method;
+import java.util.Objects;
+
+public class TestCompilerDirectivesCompatibilityBase {
+
+ public static final WhiteBox WB = WhiteBox.getWhiteBox();
+ public static String control_on, control_off;
+ Method method, nomatch;
+
+ public void run(CommandExecutor executor) throws Exception {
+
+ control_on = System.getProperty("test.src", ".") + File.separator + "control_on.txt";
+ control_off = System.getProperty("test.src", ".") + File.separator + "control_off.txt";
+ method = getMethod(TestCompilerDirectivesCompatibilityBase.class, "helper");
+ nomatch = getMethod(TestCompilerDirectivesCompatibilityBase.class, "another");
+
+ testCompatibility(executor);
+ }
+
+ public void testCompatibility(CommandExecutor executor) throws Exception {
+
+ // Call all validation twice to catch error when overwriting a directive
+ // Flag is default off
+ expect(!WB.getBooleanVMFlag("PrintAssembly"));
+ expect(!WB.shouldPrintAssembly(method));
+ expect(!WB.shouldPrintAssembly(nomatch));
+ expect(!WB.shouldPrintAssembly(method));
+ expect(!WB.shouldPrintAssembly(nomatch));
+
+ // load directives that turn it on
+ executor.execute("Compiler.directives_add " + control_on);
+ expect(WB.shouldPrintAssembly(method));
+ expect(!WB.shouldPrintAssembly(nomatch));
+ expect(WB.shouldPrintAssembly(method));
+ expect(!WB.shouldPrintAssembly(nomatch));
+
+ // remove and see that it is true again
+ executor.execute("Compiler.directives_remove");
+ expect(!WB.shouldPrintAssembly(method));
+ expect(!WB.shouldPrintAssembly(nomatch));
+ expect(!WB.shouldPrintAssembly(method));
+ expect(!WB.shouldPrintAssembly(nomatch));
+ }
+
+ public void expect(boolean test) throws Exception {
+ if (!test) {
+ throw new Exception("Test failed");
+ }
+ }
+
+ public void expect(boolean test, String msg) throws Exception {
+ if (!test) {
+ throw new Exception(msg);
+ }
+ }
+
+ @Test
+ public void jmx() throws Exception {
+ run(new JMXExecutor());
+ }
+
+ public void helper() {
+ }
+
+ public void another() {
+ }
+
+ public static Method getMethod(Class klass, String name, Class<?>... parameterTypes) {
+ try {
+ return klass.getDeclaredMethod(name, parameterTypes);
+ } catch (NoSuchMethodException | SecurityException e) {
+ throw new RuntimeException("exception on getting method Helper." + name, e);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityCommandOff.java Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2015, 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
+ * 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 TestCompilerDirectivesCompatibilityCommandOff
+ * @bug 8137167
+ * @library /testlibrary /../../test/lib
+ * @modules java.base/sun.misc
+ * java.compiler
+ * java.management
+ * @build jdk.test.lib.*
+ * @build jdk.test.lib.dcmd.*
+ * @build sun.hotspot.WhiteBox.*
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run testng/othervm -Xbootclasspath/a:. -XX:-PrintAssembly -XX:CompileCommand=option,*.helper,bool,PrintAssembly,false -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI TestCompilerDirectivesCompatibilityCommandOff
+ * @summary Test compiler control compatibility with compile command
+ */
+
+// import jdk.test.lib.OutputAnalyzer;
+import jdk.test.lib.dcmd.CommandExecutor;
+import jdk.test.lib.dcmd.JMXExecutor;
+
+import org.testng.annotations.Test;
+import org.testng.Assert;
+
+import sun.hotspot.WhiteBox;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.StringReader;
+import java.lang.reflect.Method;
+import java.util.Objects;
+
+public class TestCompilerDirectivesCompatibilityCommandOff extends TestCompilerDirectivesCompatibilityBase {
+
+ public void testCompatibility(CommandExecutor executor) throws Exception {
+
+ // Call all validation twice to catch error when overwriting a directive
+ // Flag is default off
+ expect(!WB.shouldPrintAssembly(method));
+ expect(!WB.shouldPrintAssembly(nomatch));
+ expect(!WB.shouldPrintAssembly(method));
+ expect(!WB.shouldPrintAssembly(nomatch));
+
+ // load directives that turn it on
+ executor.execute("Compiler.directives_add " + control_on);
+ expect(WB.shouldPrintAssembly(method));
+ expect(!WB.shouldPrintAssembly(nomatch));
+ expect(WB.shouldPrintAssembly(method));
+ expect(!WB.shouldPrintAssembly(nomatch));
+
+ // remove and see that it is false again
+ executor.execute("Compiler.directives_remove");
+ expect(!WB.shouldPrintAssembly(method));
+ expect(!WB.shouldPrintAssembly(nomatch));
+ expect(!WB.shouldPrintAssembly(method));
+ expect(!WB.shouldPrintAssembly(nomatch));
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityCommandOn.java Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2015, 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
+ * 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 TestCompilerDirectivesCompatibilityCommandOn
+ * @bug 8137167
+ * @library /testlibrary /../../test/lib
+ * @modules java.base/sun.misc
+ * java.compiler
+ * java.management
+ * @build jdk.test.lib.*
+ * @build jdk.test.lib.dcmd.*
+ * @build sun.hotspot.WhiteBox.*
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run testng/othervm -Xbootclasspath/a:. -XX:-PrintAssembly -XX:CompileCommand=print,*.* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI TestCompilerDirectivesCompatibilityCommandOn
+ * @summary Test compiler control compatibility with compile command
+ */
+
+// import jdk.test.lib.OutputAnalyzer;
+import jdk.test.lib.dcmd.CommandExecutor;
+import jdk.test.lib.dcmd.JMXExecutor;
+
+import org.testng.annotations.Test;
+import org.testng.Assert;
+
+import sun.hotspot.WhiteBox;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.StringReader;
+import java.lang.reflect.Method;
+import java.util.Objects;
+
+public class TestCompilerDirectivesCompatibilityCommandOn extends TestCompilerDirectivesCompatibilityBase{
+
+ public void testCompatibility(CommandExecutor executor) throws Exception {
+
+ // Call all validation twice to catch error when overwriting a directive
+ // Flag is default on
+ expect(WB.shouldPrintAssembly(method));
+ expect(WB.shouldPrintAssembly(nomatch));
+ expect(WB.shouldPrintAssembly(method));
+ expect(WB.shouldPrintAssembly(nomatch));
+
+ // load directives that turn it off
+ executor.execute("Compiler.directives_add " + control_off);
+ expect(!WB.shouldPrintAssembly(method));
+ expect(WB.shouldPrintAssembly(nomatch));
+ expect(!WB.shouldPrintAssembly(method));
+ expect(WB.shouldPrintAssembly(nomatch));
+
+ // remove and see that it is true again
+ executor.execute("Compiler.directives_remove");
+ expect(WB.shouldPrintAssembly(method));
+ expect(WB.shouldPrintAssembly(nomatch));
+ expect(WB.shouldPrintAssembly(method));
+ expect(WB.shouldPrintAssembly(nomatch));
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityFlag.java Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2015, 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
+ * 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 TestCompilerDirectivesCompatibilityFlag
+ * @bug 8137167
+ * @library /testlibrary /../../test/lib
+ * @modules java.base/sun.misc
+ * java.compiler
+ * java.management
+ * @build jdk.test.lib.*
+ * @build jdk.test.lib.dcmd.*
+ * @build sun.hotspot.WhiteBox.*
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run testng/othervm -Xbootclasspath/a:. -XX:+PrintAssembly -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI TestCompilerDirectivesCompatibilityFlag
+ * @summary Test compiler control compatibility with compile command
+ */
+
+import jdk.test.lib.dcmd.CommandExecutor;
+import jdk.test.lib.dcmd.JMXExecutor;
+
+import org.testng.annotations.Test;
+import org.testng.Assert;
+
+import sun.hotspot.WhiteBox;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.StringReader;
+import java.lang.reflect.Method;
+import java.util.Objects;
+
+public class TestCompilerDirectivesCompatibilityFlag extends TestCompilerDirectivesCompatibilityBase {
+
+ public void testCompatibility(CommandExecutor executor) throws Exception {
+
+ // Call all validation twice to catch error when overwriting a directive
+ // Flag is default on
+ expect(WB.getBooleanVMFlag("PrintAssembly"));
+ expect(WB.shouldPrintAssembly(method));
+ expect(WB.shouldPrintAssembly(nomatch));
+ expect(WB.shouldPrintAssembly(method));
+ expect(WB.shouldPrintAssembly(nomatch));
+
+ // load directives that turn it off
+ executor.execute("Compiler.directives_add " + control_off);
+ expect(!WB.shouldPrintAssembly(method));
+ expect(WB.shouldPrintAssembly(nomatch));
+ expect(!WB.shouldPrintAssembly(method));
+ expect(WB.shouldPrintAssembly(nomatch));
+
+ // remove and see that it is true again
+ executor.execute("Compiler.directives_remove");
+ expect(WB.shouldPrintAssembly(method));
+ expect(WB.shouldPrintAssembly(nomatch));
+ expect(WB.shouldPrintAssembly(method));
+ expect(WB.shouldPrintAssembly(nomatch));
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/compilercontrol/control_off.txt Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,7 @@
+[
+ {
+ match: "*.helper",
+ PrintAssembly: false,
+ DisableIntrinsic:"x"
+ }
+]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/compilercontrol/control_on.txt Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,7 @@
+[
+ {
+ match: "*.helper",
+ PrintAssembly: true,
+ DisableIntrinsic:"_dsin"
+ }
+]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/serviceability/dcmd/compiler/CompilerDirectivesDCMDTest.java Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2015, 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
+ * 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 CompilerDirectivesDCMDTest
+ * @bug 8137167
+ * @library /testlibrary
+ * @modules java.base/sun.misc
+ * java.compiler
+ * java.management
+ * @build jdk.test.lib.*
+ * @build jdk.test.lib.dcmd.*
+ * @run main ClassFileInstaller jdk.test.lib.Platform
+ * @run testng/othervm CompilerDirectivesDCMDTest
+ * @summary Test of diagnostic command
+ */
+
+import jdk.test.lib.OutputAnalyzer;
+import jdk.test.lib.dcmd.CommandExecutor;
+import jdk.test.lib.dcmd.JMXExecutor;
+import jdk.test.lib.Platform;
+import org.testng.annotations.Test;
+import org.testng.Assert;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.StringReader;
+
+public class CompilerDirectivesDCMDTest {
+
+ public static String filename;
+
+ public void run(CommandExecutor executor) {
+
+ if (Platform.isServer()) {
+ filename = System.getProperty("test.src", ".") + File.separator + "control2.txt";
+ } else {
+ filename = System.getProperty("test.src", ".") + File.separator + "control1.txt";
+ }
+ testPrintCommand(executor);
+ testAddAndRemoveCommand(executor);
+ }
+
+ public static void testPrintCommand(CommandExecutor executor) {
+
+ // Get output from dcmd (diagnostic command)
+ OutputAnalyzer output = executor.execute("Compiler.directives_print");
+ int count = find(output, "Directive:");
+ if (count < 1) {
+ Assert.fail("Expected at least one directive - found " + count);
+ }
+ }
+
+ public static void testAddAndRemoveCommand(CommandExecutor executor) {
+ OutputAnalyzer output;
+ int count = 0;
+
+ // Start with clearing stack - expect only default directive left
+ output = executor.execute("Compiler.directives_clear");
+ output = executor.execute("Compiler.directives_print");
+ count = find(output, "Directive:");
+ if (count != 1) {
+ Assert.fail("Expected one directives - found " + count);
+ }
+
+ // Test that we can not remove the default directive
+ output = executor.execute("Compiler.directives_remove");
+ output = executor.execute("Compiler.directives_print");
+ count = find(output, "Directive:");
+ if (count != 1) {
+ Assert.fail("Expected one directives - found " + count);
+ }
+
+ // Test adding some directives from file
+ output = executor.execute("Compiler.directives_add " + filename);
+ output = executor.execute("Compiler.directives_print");
+ count = find(output, "Directive:");
+ if (count != 3) {
+ Assert.fail("Expected three directives - found " + count);
+ }
+
+ // Test remove one directive
+ output = executor.execute("Compiler.directives_remove");
+ output = executor.execute("Compiler.directives_print");
+ count = find(output, "Directive:");
+ if (count != 2) {
+ Assert.fail("Expected two directives - found " + count);
+ }
+
+ // Test adding directives again
+ output = executor.execute("Compiler.directives_add " + filename);
+ output = executor.execute("Compiler.directives_print");
+ count = find(output, "Directive:");
+ if (count != 4) {
+ Assert.fail("Expected four directives - found " + count);
+ }
+
+ // Test clearing
+ output = executor.execute("Compiler.directives_clear");
+ output = executor.execute("Compiler.directives_print");
+ count = find(output, "Directive:");
+ if (count != 1) {
+ Assert.fail("Expected one directives - found " + count);
+ }
+
+ // Test clear when already cleared
+ output = executor.execute("Compiler.directives_clear");
+ output = executor.execute("Compiler.directives_print");
+ count = find(output, "Directive:");
+ if (count != 1) {
+ Assert.fail("Expected one directives - found " + count);
+ }
+
+ // Test remove one directive when empty
+ output = executor.execute("Compiler.directives_remove");
+ output = executor.execute("Compiler.directives_print");
+ count = find(output, "Directive:");
+ if (count != 1) {
+ Assert.fail("Expected one directive - found " + count);
+ }
+ }
+
+ public static int find(OutputAnalyzer output, String find) {
+ int count = 0;
+
+ for (String line : output.asLines()) {
+ if (line.startsWith(find)) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ @Test
+ public void jmx() {
+ run(new JMXExecutor());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/serviceability/dcmd/compiler/control1.txt Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,17 @@
+[
+ {
+ match: "foo/bar.*",
+ PrintAssembly: false,
+ c1: {
+ BreakAtExecute: false,
+ },
+ inline : [ "+javax/util.*", "-comx/sun.*"],
+ PrintAssembly: false,
+ },
+ {
+ match: ["baz.*","frob.*"],
+ inline : [ "+java/util.*", "-com/sun.*" ],
+ PrintAssembly: false,
+ BreakAtExecute: false,
+ }
+]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/serviceability/dcmd/compiler/control2.txt Tue Oct 20 18:07:28 2015 +0200
@@ -0,0 +1,22 @@
+[
+ {
+ match: "foo/bar.*",
+ PrintAssembly: false,
+ c1: {
+ BreakAtExecute: false,
+ },
+ c2: {
+ inline : "+java/util.*",
+ BreakAtCompile: true
+ },
+ inline : [ "+javax/util.*", "-comx/sun.*"],
+ PrintAssembly: false,
+ IGVPrintLevel: 2
+ },
+ {
+ match: ["baz.*","frob.*"],
+ inline : [ "+java/util.*", "-com/sun.*" ],
+ PrintAssembly: false,
+ BreakAtExecute: false,
+ }
+]