8137167: JEP165: Compiler Control: Implementation task
Compiler Control JEP Reviewed-by: roland, twisti, zmajo, simonis
This commit is contained in:
parent
857b7eb968
commit
5a5faf94bf
@ -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 @@ void Compilation::install_code(int frame_size) {
|
||||
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 @@ void Compilation::compile_method() {
|
||||
dependency_recorder()->assert_evol_method(method());
|
||||
}
|
||||
|
||||
if (method()->break_at_execute()) {
|
||||
if (directive()->BreakAtCompileOption) {
|
||||
BREAKPOINT;
|
||||
}
|
||||
|
||||
@ -534,9 +535,10 @@ void Compilation::generate_exception_handler_table() {
|
||||
|
||||
|
||||
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 @@ Compilation::~Compilation() {
|
||||
_env->set_compiler_data(NULL);
|
||||
}
|
||||
|
||||
|
||||
void Compilation::add_exception_handlers_for_pco(int pco, XHandlers* exception_handlers) {
|
||||
#ifndef PRODUCT
|
||||
if (PrintExceptionHandlers && Verbose) {
|
||||
|
@ -67,6 +67,7 @@ class Compilation: public StackObj {
|
||||
int _next_id;
|
||||
int _next_block_id;
|
||||
AbstractCompiler* _compiler;
|
||||
DirectiveSet* _directive;
|
||||
ciEnv* _env;
|
||||
CompileLog* _log;
|
||||
ciMethod* _method;
|
||||
@ -118,7 +119,7 @@ class Compilation: public StackObj {
|
||||
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 @@ class Compilation: public StackObj {
|
||||
|
||||
// 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; }
|
||||
|
@ -238,7 +238,7 @@ bool Compiler::is_intrinsic_supported(methodHandle method) {
|
||||
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 @@ void Compiler::compile_method(ciEnv* env, ciMethod* method, int entry_bci) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 @@ class Compiler: public AbstractCompiler {
|
||||
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();
|
||||
|
@ -3365,7 +3365,7 @@ const char* GraphBuilder::check_can_parse(ciMethod* callee) const {
|
||||
|
||||
// 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 @@ bool GraphBuilder::try_inline_intrinsics(ciMethod* callee) {
|
||||
{
|
||||
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 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, Bytecode
|
||||
}
|
||||
|
||||
// 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 @@ void GraphBuilder::print_inlining(ciMethod* callee, const char* msg, bool succes
|
||||
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);
|
||||
|
@ -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 @@ void ciEnv::register_method(ciMethod* target,
|
||||
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 @@ void ciEnv::register_method(ciMethod* target,
|
||||
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 @@ void ciEnv::register_method(ciMethod* target,
|
||||
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 @@ void ciEnv::register_method(ciMethod* target,
|
||||
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);
|
||||
}
|
||||
|
@ -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 @@ public:
|
||||
// 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 @@ public:
|
||||
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);
|
||||
|
||||
|
||||
|
@ -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"
|
||||
@ -1043,51 +1042,6 @@ MethodCounters* ciMethod::ensure_method_counters() {
|
||||
return method_counters;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 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
|
||||
//
|
||||
@ -1101,20 +1055,12 @@ bool ciMethod::has_option(const char* option) {
|
||||
// ------------------------------------------------------------------
|
||||
// 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
|
||||
//
|
||||
|
@ -104,8 +104,6 @@ class ciMethod : public ciMetadata {
|
||||
|
||||
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 @@ class ciMethod : public ciMetadata {
|
||||
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 @@ class ciMethod : public ciMetadata {
|
||||
// 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);
|
||||
|
@ -417,36 +417,10 @@ int vmIntrinsics::predicates_needed(vmIntrinsics::ID id) {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -1402,7 +1402,7 @@ public:
|
||||
|
||||
// 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
|
||||
|
@ -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 @@ nmethod* nmethod::new_native_nmethod(methodHandle method,
|
||||
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 @@ nmethod* nmethod::new_nmethod(methodHandle method,
|
||||
}
|
||||
}
|
||||
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 @@ nmethod::nmethod(
|
||||
_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
|
||||
|
@ -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 @@ class AbstractCompiler : public CHeapObj<mtCompiler> {
|
||||
|
||||
// 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 @@ class AbstractCompiler : public CHeapObj<mtCompiler> {
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -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 @@ class CompilationLog : public StringEventLog {
|
||||
|
||||
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 @@ bool CompileBroker::compilation_is_prohibited(methodHandle method, int osr_bci,
|
||||
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 @@ bool CompileBroker::compilation_is_prohibited(methodHandle method, int osr_bci,
|
||||
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 @@ bool CompileBroker::init_compiler_runtime() {
|
||||
ThreadInVMfromNative tv(thread);
|
||||
ResetNoHandleMark rnhm;
|
||||
|
||||
|
||||
if (!comp->is_shark()) {
|
||||
// Perform per-thread and global initializations
|
||||
comp->initialize();
|
||||
@ -1629,6 +1645,10 @@ void CompileBroker::post_compile(CompilerThread* thread, CompileTask* task, Even
|
||||
}
|
||||
}
|
||||
|
||||
int DirectivesStack::_depth = 0;
|
||||
CompilerDirectives* DirectivesStack::_top = NULL;
|
||||
CompilerDirectives* DirectivesStack::_bottom = NULL;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// CompileBroker::invoke_compiler_on_method
|
||||
//
|
||||
@ -1655,16 +1675,20 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) {
|
||||
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 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) {
|
||||
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 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) {
|
||||
|
||||
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 @@ void CompileBroker::pop_jni_handle_block() {
|
||||
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 @@ void CompileBroker::print_compiler_threads_on(outputStream* st) {
|
||||
st->cr();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -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 @@ public:
|
||||
~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 @@ class CompileBroker: AllStatic {
|
||||
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 @@ class CompileBroker: AllStatic {
|
||||
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 @@ class CompileBroker: AllStatic {
|
||||
|
||||
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;
|
||||
|
@ -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
|
||||
@ -371,6 +372,19 @@ void CompileTask::log_task_done(CompileLog* log) {
|
||||
log->mark_file_end();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 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) {
|
||||
|
502
hotspot/src/share/vm/compiler/compilerDirectives.cpp
Normal file
502
hotspot/src/share/vm/compiler/compilerDirectives.cpp
Normal file
@ -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);
|
||||
}
|
186
hotspot/src/share/vm/compiler/compilerDirectives.hpp
Normal file
186
hotspot/src/share/vm/compiler/compilerDirectives.hpp
Normal file
@ -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
|
@ -106,6 +106,7 @@ class TypedMethodOptionMatcher;
|
||||
|
||||
static BasicMatcher* lists[OracleCommandCount] = { 0, };
|
||||
static TypedMethodOptionMatcher* option_list = NULL;
|
||||
static bool any_set = false;
|
||||
|
||||
class TypedMethodOptionMatcher : public MethodMatcher {
|
||||
private:
|
||||
@ -292,6 +293,7 @@ static void add_option_string(TypedMethodOptionMatcher* matcher,
|
||||
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 @@ static void add_predicate(OracleCommand command, BasicMatcher* bm) {
|
||||
}
|
||||
bm->set_next(lists[command]);
|
||||
lists[command] = bm;
|
||||
|
||||
if ((command != DontInlineCommand) && (command != InlineCommand)) {
|
||||
any_set = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -324,6 +328,10 @@ bool CompilerOracle::has_option_value(methodHandle method, const char* option, T
|
||||
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 @@ bool CompilerOracle::has_option_string(methodHandle method, const char* option)
|
||||
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 @@ bool CompilerOracle::should_inline(methodHandle method) {
|
||||
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);
|
||||
}
|
||||
|
@ -46,7 +46,8 @@ class CompilerOracle : AllStatic {
|
||||
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 @@ class CompilerOracle : AllStatic {
|
||||
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*));
|
||||
|
||||
|
726
hotspot/src/share/vm/compiler/directivesParser.cpp
Normal file
726
hotspot/src/share/vm/compiler/directivesParser.cpp
Normal file
@ -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
|
139
hotspot/src/share/vm/compiler/directivesParser.hpp
Normal file
139
hotspot/src/share/vm/compiler/directivesParser.hpp
Normal file
@ -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
|
@ -342,6 +342,107 @@ void MethodMatcher::print_base(outputStream* st) {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -81,35 +81,8 @@ public:
|
||||
_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 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
|
@ -145,7 +145,7 @@ void JVMCICompiler::compile_method(methodHandle method, int entry_bci, JVMCIEnv*
|
||||
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ public:
|
||||
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);
|
||||
|
||||
|
@ -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 @@ PhaseCFG::PhaseCFG(Arena* arena, RootNode* root, Matcher& matcher)
|
||||
, _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)
|
||||
|
@ -368,7 +368,6 @@ public:
|
||||
class PhaseCFG : public Phase {
|
||||
friend class VMStructs;
|
||||
private:
|
||||
|
||||
// Root of whole program
|
||||
RootNode* _root;
|
||||
|
||||
|
@ -108,7 +108,7 @@ bool InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method,
|
||||
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 @@ bool InlineTree::should_not_inline(ciMethod *callee_method,
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
@ -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, \
|
||||
|
@ -94,15 +94,16 @@ void C2Compiler::initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -41,7 +41,8 @@ public:
|
||||
// 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();
|
||||
|
@ -211,7 +211,7 @@ PhaseChaitin::PhaseChaitin(uint unique, PhaseCFG &cfg, Matcher &matcher, bool sc
|
||||
, _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]);
|
||||
|
@ -464,7 +464,7 @@ CompileWrapper::CompileWrapper(Compile* compile) : _compile(compile) {
|
||||
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 @@ void Compile::print_compile_messages() {
|
||||
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 @@ debug_only( int Compile::_debug_idx = 100000; )
|
||||
|
||||
|
||||
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 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
|
||||
_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 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
|
||||
_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 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
|
||||
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 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
|
||||
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 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
|
||||
// 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 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
|
||||
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 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
|
||||
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 @@ Compile::Compile( ciEnv* ci_env,
|
||||
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 @@ void Compile::Init(int aliaslevel) {
|
||||
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 @@ void Compile::Init(int aliaslevel) {
|
||||
|
||||
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 @@ void Compile::Init(int aliaslevel) {
|
||||
|
||||
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 @@ void Compile::Optimize() {
|
||||
TracePhase tp("optimizer", &timers[_t_optimizer]);
|
||||
|
||||
#ifndef PRODUCT
|
||||
if (env()->break_at_compile()) {
|
||||
if (_directive->BreakAtCompileOption) {
|
||||
BREAKPOINT;
|
||||
}
|
||||
|
||||
@ -4357,7 +4365,6 @@ bool Compile::randomized_select(int count) {
|
||||
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; }
|
||||
|
||||
|
@ -391,6 +391,7 @@ class Compile : public Phase {
|
||||
// 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 @@ class Compile : public Phase {
|
||||
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 @@ class Compile : public Phase {
|
||||
|
||||
// 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 @@ class Compile : public Phase {
|
||||
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 @@ class Compile : public Phase {
|
||||
|
||||
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 @@ class Compile : public Phase {
|
||||
|
||||
|
||||
#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 @@ class Compile : public Phase {
|
||||
event.commit();
|
||||
}
|
||||
#ifndef PRODUCT
|
||||
if (_printer && _printer->should_print(_method)) {
|
||||
if (_printer && _printer->should_print(level)) {
|
||||
_printer->end_method();
|
||||
}
|
||||
#endif
|
||||
@ -1107,7 +1110,7 @@ class Compile : public Phase {
|
||||
// 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 @@ class Compile : public Phase {
|
||||
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
|
||||
|
@ -292,11 +292,11 @@ void IdealGraphPrinter::print_inline_tree(InlineTree *tree) {
|
||||
|
||||
}
|
||||
|
||||
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 @@ void IdealGraphPrinter::print_inlining(Compile* compile) {
|
||||
}
|
||||
|
||||
// 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::walk_nodes(Node *start, bool edges, VectorSet* temp_set)
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ void IdealGraphPrinter::print(Compile* compile, const char *name, Node *node, in
|
||||
}
|
||||
|
||||
// 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[];
|
||||
|
@ -127,13 +127,14 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
|
||||
|
||||
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
|
||||
|
@ -326,9 +326,10 @@ CallGenerator* Compile::make_vm_intrinsic(ciMethod* m, bool is_virtual) {
|
||||
// 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) {
|
||||
|
@ -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 @@ void Compile::Output() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 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())
|
||||
|
@ -2378,13 +2378,13 @@ void Parse::do_one_bytecode() {
|
||||
}
|
||||
|
||||
#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
|
||||
|
@ -159,9 +159,13 @@ address OptoRuntime::generate_stub( ciEnv* env,
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -79,11 +79,12 @@ SuperWord::SuperWord(PhaseIdealLoop* phase) :
|
||||
#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
|
||||
}
|
||||
|
@ -3840,7 +3840,9 @@ _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_GetDefaultJavaVMInitArgs(void *args_) {
|
||||
#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 @@ void execute_internal_vm_tests() {
|
||||
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
|
||||
|
@ -551,14 +551,20 @@ WB_ENTRY(jboolean, WB_IsIntrinsicAvailable(JNIEnv* env, jobject o, jobject metho
|
||||
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 @@ WB_ENTRY(jboolean, WB_EnqueueMethodForCompilation(JNIEnv* env, jobject o, jobjec
|
||||
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 @@ static JNINativeMethod methods[] = {
|
||||
{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},
|
||||
|
@ -3650,6 +3650,9 @@ public:
|
||||
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 @@ public:
|
||||
"(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
|
||||
|
@ -71,7 +71,7 @@ void vmStructs_init();
|
||||
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 @@ jint init_globals() {
|
||||
vtableStubs_init();
|
||||
InlineCacheBuffer_init();
|
||||
compilerOracle_init();
|
||||
compileBroker_init();
|
||||
if (!compileBroker_init()) {
|
||||
return JNI_EINVAL;
|
||||
}
|
||||
VMRegImpl::set_regName();
|
||||
|
||||
if (!universe_post_init()) {
|
||||
|
@ -90,6 +90,7 @@ Monitor* CompileThread_lock = NULL;
|
||||
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 @@ void mutex_init() {
|
||||
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
|
||||
|
@ -93,6 +93,7 @@ extern Monitor* CompileThread_lock; // a lock held by compile threa
|
||||
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
|
||||
|
@ -2696,6 +2696,12 @@ void AdapterHandlerLibrary::create_native_wrapper(methodHandle method) {
|
||||
|
||||
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
|
||||
|
@ -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"
|
||||
|
@ -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 @@ void DCmdRegistrant::register_dcmds(){
|
||||
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 @@ void CodeCacheDCmd::execute(DCmdSource source, TRAPS) {
|
||||
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),
|
||||
|
@ -613,4 +613,90 @@ public:
|
||||
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
|
||||
|
@ -145,7 +145,8 @@ void SharkCompiler::initialize() {
|
||||
|
||||
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 @@ void SharkCompiler::compile_method(ciEnv* env,
|
||||
&handler_table,
|
||||
&inc_table,
|
||||
this,
|
||||
env->comp_level(),
|
||||
false,
|
||||
directive(),
|
||||
false);
|
||||
}
|
||||
|
||||
|
@ -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 @@ class SharkCompiler : public AbstractCompiler {
|
||||
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();
|
||||
|
956
hotspot/src/share/vm/utilities/json.cpp
Normal file
956
hotspot/src/share/vm/utilities/json.cpp
Normal file
@ -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
|
127
hotspot/src/share/vm/utilities/json.hpp
Normal file
127
hotspot/src/share/vm/utilities/json.hpp
Normal file
@ -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
|
173
hotspot/test/compiler/compilercontrol/InlineMatcherTest.java
Normal file
173
hotspot/test/compiler/compilercontrol/InlineMatcherTest.java
Normal file
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
7
hotspot/test/compiler/compilercontrol/control_off.txt
Normal file
7
hotspot/test/compiler/compilercontrol/control_off.txt
Normal file
@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
match: "*.helper",
|
||||
PrintAssembly: false,
|
||||
DisableIntrinsic:"x"
|
||||
}
|
||||
]
|
7
hotspot/test/compiler/compilercontrol/control_on.txt
Normal file
7
hotspot/test/compiler/compilercontrol/control_on.txt
Normal file
@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
match: "*.helper",
|
||||
PrintAssembly: true,
|
||||
DisableIntrinsic:"_dsin"
|
||||
}
|
||||
]
|
@ -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());
|
||||
}
|
||||
}
|
17
hotspot/test/serviceability/dcmd/compiler/control1.txt
Normal file
17
hotspot/test/serviceability/dcmd/compiler/control1.txt
Normal file
@ -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,
|
||||
}
|
||||
]
|
22
hotspot/test/serviceability/dcmd/compiler/control2.txt
Normal file
22
hotspot/test/serviceability/dcmd/compiler/control2.txt
Normal file
@ -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,
|
||||
}
|
||||
]
|
Loading…
Reference in New Issue
Block a user