| /* | |
| * Copyright (c) 1998, 2025, 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 "ci/ciMethod.hpp" | |
| #include "ci/ciUtilities.inline.hpp" | |
| #include "compiler/abstractCompiler.hpp" | |
| #include "compiler/compilerDefinitions.inline.hpp" | |
| #include "compiler/compilerDirectives.hpp" | |
| #include "compiler/compilerOracle.hpp" | |
| #include "memory/allocation.inline.hpp" | |
| #include "memory/resourceArea.hpp" | |
| #include "opto/phasetype.hpp" | |
| #include "opto/traceAutoVectorizationTag.hpp" | |
| #include "opto/traceMergeStoresTag.hpp" | |
| #include "runtime/globals_extension.hpp" | |
| CompilerDirectives::CompilerDirectives() : _next(nullptr), _match(nullptr), _ref_count(0) { | |
| _c1_store = new DirectiveSet(this); | |
| _c1_store->init_control_intrinsic(); | |
| _c2_store = new DirectiveSet(this); | |
| _c2_store->init_control_intrinsic(); | |
| }; | |
| CompilerDirectives::~CompilerDirectives() { | |
| if (_c1_store != nullptr) { | |
| delete _c1_store; | |
| } | |
| if (_c2_store != nullptr) { | |
| delete _c2_store; | |
| } | |
| // remove all linked method matchers | |
| BasicMatcher* tmp = _match; | |
| while (tmp != nullptr) { | |
| BasicMatcher* next = tmp->next(); | |
| delete tmp; | |
| tmp = next; | |
| } | |
| } | |
| void CompilerDirectives::print(outputStream* st) { | |
| assert(DirectivesStack_lock->owned_by_self(), ""); | |
| if (_match != nullptr) { | |
| 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 != nullptr) { | |
| st->print(", "); | |
| tmp->print(st); | |
| tmp = tmp->next(); | |
| } | |
| st->cr(); | |
| } else { | |
| assert(0, "There should always be a match"); | |
| } | |
| if (_c1_store != nullptr) { | |
| st->print_cr(" c1 directives:"); | |
| _c1_store->print(st); | |
| } | |
| if (_c2_store != nullptr) { | |
| st->cr(); | |
| st->print_cr(" c2 directives:"); | |
| _c2_store->print(st); | |
| } | |
| //--- | |
| } | |
| void CompilerDirectives::finalize(outputStream* st) { | |
| if (_c1_store != nullptr) { | |
| _c1_store->finalize(st); | |
| } | |
| if (_c2_store != nullptr) { | |
| _c2_store->finalize(st); | |
| } | |
| } | |
| void DirectiveSet::finalize(outputStream* st) { | |
| const char* level; | |
| if (is_c1(this->directive())) { | |
| level = "c1"; | |
| } else if (is_c2(this->directive())) { | |
| level = "c2"; | |
| } else { | |
| ShouldNotReachHere(); | |
| } | |
| if (LogOption && !LogCompilation) { | |
| st->print_cr("Warning: %s: +LogCompilation must be set to enable compilation logging from directives", level); | |
| } | |
| if (PrintAssemblyOption && FLAG_IS_DEFAULT(DebugNonSafepoints)) { | |
| warning("%s: printing of assembly code is enabled; turning on DebugNonSafepoints to gain additional output", level); | |
| DebugNonSafepoints = true; | |
| } | |
| // if any flag has been modified - set directive as enabled | |
| // unless it already has been explicitly set. | |
| if (!_modified[EnableIndex]) { | |
| if (_inlinematchers != nullptr) { | |
| 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(const methodHandle& method) { | |
| if (is_default_directive()) { | |
| return true; | |
| } | |
| if (method == nullptr) { | |
| 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, false); | |
| if (bm == nullptr) { | |
| assert(error_msg != nullptr, "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(), ""); | |
| if (comp == nullptr) { // Xint | |
| return _c1_store; | |
| } else if (comp->is_c2()) { | |
| return _c2_store; | |
| } else { | |
| // use c1_store as default | |
| assert(comp->is_c1() || comp->is_jvmci(), ""); | |
| return _c1_store; | |
| } | |
| } | |
| bool DirectiveSet::is_c1(CompilerDirectives* directive) const { | |
| return this == directive->_c1_store; | |
| } | |
| bool DirectiveSet::is_c2(CompilerDirectives* directive) const { | |
| return this == directive->_c2_store; | |
| } | |
| bool DirectiveSet::should_collect_memstat() const { | |
| // MemLimit requires the memory statistic to be active | |
| return MemStatOption > 0 || MemLimitOption != 0; | |
| } | |
| bool DirectiveSet::should_print_memstat() const { | |
| return MemStatOption == (uintx)MemStatAction::print; | |
| } | |
| size_t DirectiveSet::mem_limit() const { | |
| return MemLimitOption < 0 ? -MemLimitOption : MemLimitOption; | |
| } | |
| bool DirectiveSet::should_crash_at_mem_limit() const { | |
| // The sign encodes the action to be taken when reaching | |
| // the memory limit (+ stop - crash) | |
| return MemLimitOption < 0; | |
| } | |
| // In the list of Control/disabled intrinsics, the ID of the control intrinsics can separated: | |
| // - by ',' (if -XX:Control/DisableIntrinsic is used once when invoking the VM) or | |
| // - by '\n' (if -XX:Control/DisableIntrinsic is used multiple times when invoking the VM) or | |
| // - by ' ' (if Control/DisableIntrinsic is used on a per-method level, e.g., with CompileCommand). | |
| // | |
| // To simplify the processing of the list, the canonicalize_control_intrinsic() method | |
| // returns a new copy of the list in which '\n' and ' ' is replaced with ','. | |
| ccstrlist DirectiveSet::canonicalize_control_intrinsic(ccstrlist option_value) { | |
| char* canonicalized_list = NEW_C_HEAP_ARRAY(char, strlen(option_value) + 1, mtCompiler); | |
| int i = 0; | |
| char current; | |
| while ((current = option_value[i]) != '\0') { | |
| if (current == '\n' || current == ' ') { | |
| canonicalized_list[i] = ','; | |
| } else { | |
| canonicalized_list[i] = current; | |
| } | |
| i++; | |
| } | |
| canonicalized_list[i] = '\0'; | |
| return canonicalized_list; | |
| } | |
| ControlIntrinsicIter::ControlIntrinsicIter(ccstrlist option_value, bool disable_all) | |
| : _disableIntrinsic(disable_all) { | |
| _list = (char*)DirectiveSet::canonicalize_control_intrinsic(option_value); | |
| _saved_ptr = _list; | |
| _enabled = false; | |
| _token = strtok_r(_saved_ptr, ",", &_saved_ptr); | |
| next_token(); | |
| } | |
| ControlIntrinsicIter::~ControlIntrinsicIter() { | |
| FREE_C_HEAP_ARRAY(char, _list); | |
| } | |
| // pre-increment | |
| ControlIntrinsicIter& ControlIntrinsicIter::operator++() { | |
| _token = strtok_r(nullptr, ",", &_saved_ptr); | |
| next_token(); | |
| return *this; | |
| } | |
| void ControlIntrinsicIter::next_token() { | |
| if (_token && !_disableIntrinsic) { | |
| char ch = _token[0]; | |
| if (ch != '+' && ch != '-') { | |
| warning("failed to parse %s. must start with +/-!", _token); | |
| } else { | |
| _enabled = ch == '+'; | |
| _token++; | |
| } | |
| } | |
| } | |
| void DirectiveSet::init_control_intrinsic() { | |
| for (ControlIntrinsicIter iter(ControlIntrinsic); *iter != nullptr; ++iter) { | |
| vmIntrinsics::ID id = vmIntrinsics::find_id(*iter); | |
| if (id != vmIntrinsics::_none) { | |
| _intrinsic_control_words[vmIntrinsics::as_int(id)] = iter.is_enabled(); | |
| } | |
| } | |
| // Order matters, DisableIntrinsic can overwrite ControlIntrinsic | |
| for (ControlIntrinsicIter iter(DisableIntrinsic, true/*disable_all*/); *iter != nullptr; ++iter) { | |
| vmIntrinsics::ID id = vmIntrinsics::find_id(*iter); | |
| if (id != vmIntrinsics::_none) { | |
| _intrinsic_control_words[vmIntrinsics::as_int(id)] = false; | |
| } | |
| } | |
| } | |
| DirectiveSet::DirectiveSet(CompilerDirectives* d) : | |
| _inlinematchers(nullptr), | |
| _directive(d), | |
| _ideal_phase_name_set(PHASE_NUM_TYPES, mtCompiler), | |
| _trace_auto_vectorization_tags(TRACE_AUTO_VECTORIZATION_TAG_NUM, mtCompiler), | |
| _trace_merge_stores_tags(TraceMergeStores::TAG_NUM, mtCompiler) | |
| { | |
| #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) | |
| #undef init_defaults_definition | |
| memset(_modified, 0, sizeof(_modified)); | |
| _intrinsic_control_words.fill_in(/*default value*/TriBool()); | |
| } | |
| DirectiveSet::~DirectiveSet() { | |
| // remove all linked methodmatchers | |
| InlineMatcher* tmp = _inlinematchers; | |
| while (tmp != nullptr) { | |
| InlineMatcher* next = tmp->next(); | |
| delete tmp; | |
| tmp = next; | |
| } | |
| #define free_string_flags(name, type, dvalue, cc_flag) if (_modified[name##Index]) os::free(const_cast<char*>(name##Option)); | |
| compilerdirectives_common_string_flags(free_string_flags) | |
| compilerdirectives_c2_string_flags(free_string_flags) | |
| compilerdirectives_c1_string_flags(free_string_flags) | |
| #undef free_string_flags | |
| } | |
| // A smart pointer of DirectiveSet. It uses Copy-on-Write strategy to avoid cloning. | |
| // It provides 2 accesses of the underlying raw pointer. | |
| // 1) operator->() returns a pointer to a constant DirectiveSet. It's read-only. | |
| // 2) cloned() returns a pointer that points to the cloned DirectiveSet. | |
| // Users should only use cloned() when they need to update DirectiveSet. | |
| // | |
| // In the end, users need to invoke commit() to finalize the pending changes. | |
| // If cloning happens, the smart pointer will return the new pointer after releasing the original | |
| // one on DirectivesStack. If cloning doesn't happen, it returns the original intact pointer. | |
| class DirectiveSetPtr { | |
| private: | |
| DirectiveSet* _origin; | |
| DirectiveSet* _clone; | |
| NONCOPYABLE(DirectiveSetPtr); | |
| public: | |
| DirectiveSetPtr(DirectiveSet* origin): _origin(origin), _clone(nullptr) { | |
| assert(origin != nullptr, "DirectiveSetPtr cannot be initialized with a nullptr pointer."); | |
| } | |
| DirectiveSet const* operator->() { | |
| return (_clone == nullptr) ? _origin : _clone; | |
| } | |
| DirectiveSet* cloned() { | |
| if (_clone == nullptr) { | |
| _clone = DirectiveSet::clone(_origin); | |
| } | |
| return _clone; | |
| } | |
| DirectiveSet* commit() { | |
| if (_clone != nullptr) { | |
| // We are returning a (parentless) copy. The originals parent don't need to account for this. | |
| DirectivesStack::release(_origin); | |
| _origin = _clone; | |
| _clone = nullptr; | |
| } | |
| return _origin; | |
| } | |
| }; | |
| // 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(const 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_command_set()) { | |
| DirectiveSetPtr set(this); | |
| #ifdef COMPILER1 | |
| if (C1Breakpoint) { | |
| // If the directives didn't have 'BreakAtExecute', | |
| // the command 'C1Breakpoint' would become effective. | |
| if (!_modified[BreakAtExecuteIndex]) { | |
| set.cloned()->BreakAtExecuteOption = true; | |
| } | |
| } | |
| #endif | |
| // 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 the directives didn't have 'BreakAtCompile' or 'BreakAtExecute', | |
| // the sub-command 'Break' of the 'CompileCommand' would become effective. | |
| if (!_modified[BreakAtCompileIndex]) { | |
| set.cloned()->BreakAtCompileOption = true; | |
| } | |
| if (!_modified[BreakAtExecuteIndex]) { | |
| set.cloned()->BreakAtExecuteOption = true; | |
| } | |
| } | |
| if (!_modified[LogIndex]) { | |
| bool log = CompilerOracle::should_log(method); | |
| if (log != set->LogOption) { | |
| set.cloned()->LogOption = log; | |
| } | |
| } | |
| if (CompilerOracle::should_print(method)) { | |
| if (!_modified[PrintAssemblyIndex]) { | |
| set.cloned()->PrintAssemblyOption = true; | |
| } | |
| } | |
| // Exclude as in should not compile == Enabled | |
| if (CompilerOracle::should_exclude(method)) { | |
| if (!_modified[ExcludeIndex]) { | |
| set.cloned()->ExcludeOption = 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] && CompileCommandEnum::cc_flag != CompileCommandEnum::Unknown && CompilerOracle::has_option_value(method, CompileCommandEnum::cc_flag, v) && v != this->name##Option) { set.cloned()->name##Option = v; } } | |
| compilerdirectives_common_flags(init_default_cc) | |
| compilerdirectives_c2_flags(init_default_cc) | |
| compilerdirectives_c1_flags(init_default_cc) | |
| #undef init_default_cc | |
| #ifndef PRODUCT | |
| #ifdef COMPILER2 | |
| if (!_modified[TraceAutoVectorizationIndex]) { | |
| // Parse ccstr and create mask | |
| ccstrlist option; | |
| if (CompilerOracle::has_option_value(method, CompileCommandEnum::TraceAutoVectorization, option)) { | |
| TraceAutoVectorizationTagValidator validator(option, false); | |
| if (validator.is_valid()) { | |
| set.cloned()->set_trace_auto_vectorization_tags(validator.tags()); | |
| } | |
| } | |
| } | |
| if (!_modified[TraceMergeStoresIndex]) { | |
| // Parse ccstr and create mask | |
| ccstrlist option; | |
| if (CompilerOracle::has_option_value(method, CompileCommandEnum::TraceMergeStores, option)) { | |
| TraceMergeStores::TagValidator validator(option, false); | |
| if (validator.is_valid()) { | |
| set.cloned()->set_trace_merge_stores_tags(validator.tags()); | |
| } | |
| } | |
| } | |
| // Parse PrintIdealPhaseName and create a lookup set | |
| if (!_modified[PrintIdealPhaseIndex]) { | |
| // Parse ccstr and create set | |
| ccstrlist option; | |
| if (CompilerOracle::has_option_value(method, CompileCommandEnum::PrintIdealPhase, option)) { | |
| PhaseNameValidator validator(option); | |
| if (validator.is_valid()) { | |
| assert(!validator.phase_name_set().is_empty(), "Phase name set must be non-empty"); | |
| set.cloned()->set_ideal_phase_name_set(validator.phase_name_set()); | |
| } | |
| } | |
| } | |
| #endif | |
| #endif | |
| // Canonicalize DisableIntrinsic to contain only ',' as a separator. | |
| ccstrlist option_value; | |
| bool need_reset = true; // if Control/DisableIntrinsic redefined, only need to reset control_words once | |
| if (!_modified[ControlIntrinsicIndex] && | |
| CompilerOracle::has_option_value(method, CompileCommandEnum::ControlIntrinsic, option_value)) { | |
| ControlIntrinsicIter iter(option_value); | |
| if (need_reset) { | |
| set.cloned()->_intrinsic_control_words.fill_in(TriBool()); | |
| need_reset = false; | |
| } | |
| while (*iter != nullptr) { | |
| vmIntrinsics::ID id = vmIntrinsics::find_id(*iter); | |
| if (id != vmIntrinsics::_none) { | |
| set.cloned()->_intrinsic_control_words[vmIntrinsics::as_int(id)] = iter.is_enabled(); | |
| } | |
| ++iter; | |
| } | |
| } | |
| if (!_modified[DisableIntrinsicIndex] && | |
| CompilerOracle::has_option_value(method, CompileCommandEnum::DisableIntrinsic, option_value)) { | |
| ControlIntrinsicIter iter(option_value, true/*disable_all*/); | |
| if (need_reset) { | |
| set.cloned()->_intrinsic_control_words.fill_in(TriBool()); | |
| need_reset = false; | |
| } | |
| while (*iter != nullptr) { | |
| vmIntrinsics::ID id = vmIntrinsics::find_id(*iter); | |
| if (id != vmIntrinsics::_none) { | |
| set.cloned()->_intrinsic_control_words[vmIntrinsics::as_int(id)] = false; | |
| } | |
| ++iter; | |
| } | |
| } | |
| return set.commit(); | |
| } | |
| // Nothing changed | |
| return this; | |
| } | |
| CompilerDirectives* DirectiveSet::directive() { | |
| assert(_directive != nullptr, "Must have been initialized"); | |
| return _directive; | |
| } | |
| bool DirectiveSet::matches_inline(const methodHandle& method, int inline_action) { | |
| if (_inlinematchers != nullptr) { | |
| if (_inlinematchers->match(method, inline_action)) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool DirectiveSet::should_inline(ciMethod* inlinee) { | |
| inlinee->check_is_loaded(); | |
| VM_ENTRY_MARK; | |
| methodHandle mh(THREAD, inlinee->get_Method()); | |
| if (_inlinematchers != nullptr) { | |
| return matches_inline(mh, InlineMatcher::force_inline); | |
| } | |
| if (!CompilerDirectivesIgnoreCompileCommandsOption) { | |
| return CompilerOracle::should_inline(mh); | |
| } | |
| return false; | |
| } | |
| bool DirectiveSet::should_not_inline(ciMethod* inlinee) { | |
| inlinee->check_is_loaded(); | |
| VM_ENTRY_MARK; | |
| methodHandle mh(THREAD, inlinee->get_Method()); | |
| if (_inlinematchers != nullptr) { | |
| return matches_inline(mh, InlineMatcher::dont_inline); | |
| } | |
| if (!CompilerDirectivesIgnoreCompileCommandsOption) { | |
| return CompilerOracle::should_not_inline(mh); | |
| } | |
| 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 != nullptr) { | |
| // add matcher last in chain - the order is significant | |
| append_inline(m); | |
| return true; | |
| } else { | |
| assert(error_msg != nullptr, "Error message must be set"); | |
| return false; | |
| } | |
| } | |
| void DirectiveSet::append_inline(InlineMatcher* m) { | |
| if (_inlinematchers == nullptr) { | |
| _inlinematchers = m; | |
| return; | |
| } | |
| InlineMatcher* tmp = _inlinematchers; | |
| while (tmp->next() != nullptr) { | |
| tmp = tmp->next(); | |
| } | |
| tmp->set_next(m); | |
| } | |
| void DirectiveSet::print_inline(outputStream* st) { | |
| if (_inlinematchers == nullptr) { | |
| st->print_cr(" inline: -"); | |
| } else { | |
| st->print(" inline: "); | |
| _inlinematchers->print(st); | |
| InlineMatcher* tmp = _inlinematchers->next(); | |
| while (tmp != nullptr) { | |
| st->print(", "); | |
| tmp->print(st); | |
| tmp = tmp->next(); | |
| } | |
| st->cr(); | |
| } | |
| } | |
| bool DirectiveSet::is_intrinsic_disabled(vmIntrinsics::ID id) { | |
| assert(id > vmIntrinsics::_none && id < vmIntrinsics::ID_LIMIT, "invalid intrinsic_id!"); | |
| TriBool b = _intrinsic_control_words[vmIntrinsics::as_int(id)]; | |
| if (b.is_default()) { | |
| return false; // if unset, every intrinsic is enabled. | |
| } else { | |
| return !b; | |
| } | |
| } | |
| DirectiveSet* DirectiveSet::clone(DirectiveSet const* src) { | |
| DirectiveSet* set = new DirectiveSet(nullptr); | |
| // Ordinary allocations of DirectiveSet would call init_control_intrinsic() | |
| // immediately to create a new copy for set->Control/DisableIntrinsicOption. | |
| // However, here it does not need to because the code below creates | |
| // a copy of src->Control/DisableIntrinsicOption that initializes | |
| // set->Control/DisableIntrinsicOption. | |
| memcpy(set->_modified, src->_modified, sizeof(src->_modified)); | |
| InlineMatcher* tmp = src->_inlinematchers; | |
| while (tmp != nullptr) { | |
| 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_other_flags(copy_members_definition) | |
| compilerdirectives_c2_other_flags(copy_members_definition) | |
| compilerdirectives_c1_other_flags(copy_members_definition) | |
| #undef copy_members_definition | |
| #define copy_string_members_definition(name, type, dvalue, cc_flag) \ | |
| if (src->_modified[name##Index]) { \ | |
| set->name##Option = os::strdup_check_oom(src->name##Option, mtCompiler); \ | |
| } else { \ | |
| set->name##Option = src->name##Option; \ | |
| } | |
| compilerdirectives_common_string_flags(copy_string_members_definition) | |
| compilerdirectives_c2_string_flags(copy_string_members_definition) | |
| compilerdirectives_c1_string_flags(copy_string_members_definition) | |
| #undef copy_string_members_definition | |
| set->_intrinsic_control_words = src->_intrinsic_control_words; | |
| set->set_ideal_phase_name_set(src->_ideal_phase_name_set); | |
| 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 = nullptr; | |
| _default_directives->add_match(str, error_msg); | |
| #if defined(COMPILER1) || INCLUDE_JVMCI | |
| _default_directives->_c1_store->EnableOption = true; | |
| #endif | |
| #ifdef COMPILER2 | |
| if (CompilerConfig::is_c2_enabled()) { | |
| _default_directives->_c2_store->EnableOption = true; | |
| } | |
| #endif | |
| assert(error_msg == nullptr, "Must succeed."); | |
| push(_default_directives); | |
| } | |
| DirectiveSet* DirectivesStack::getDefaultDirective(AbstractCompiler* comp) { | |
| MutexLocker locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag); | |
| assert(_bottom != nullptr, "Must never be empty"); | |
| _bottom->inc_refcount(); | |
| return _bottom->get_for(comp); | |
| } | |
| void DirectivesStack::push(CompilerDirectives* directive) { | |
| MutexLocker locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag); | |
| directive->inc_refcount(); | |
| if (_top == nullptr) { | |
| assert(_bottom == nullptr, "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(int count) { | |
| MutexLocker locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag); | |
| assert(count > -1, "No negative values"); | |
| for (int i = 0; i < count; i++) { | |
| pop_inner(); | |
| } | |
| } | |
| void DirectivesStack::pop_inner() { | |
| assert(DirectivesStack_lock->owned_by_self(), ""); | |
| if (_top->next() == nullptr) { | |
| return; // Do nothing - don't allow an empty stack | |
| } | |
| CompilerDirectives* tmp = _top; | |
| _top = _top->next(); | |
| _depth--; | |
| DirectivesStack::release(tmp); | |
| } | |
| bool DirectivesStack::check_capacity(int request_size, outputStream* st) { | |
| if ((request_size + _depth) > CompilerDirectivesLimit) { | |
| st->print_cr("Could not add %i more directives. Currently %i/%i directives.", request_size, _depth, CompilerDirectivesLimit); | |
| return false; | |
| } | |
| return true; | |
| } | |
| void DirectivesStack::clear() { | |
| // holding the lock during the whole operation ensuring consistent result | |
| MutexLocker locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag); | |
| while (_top->next() != nullptr) { | |
| pop_inner(); | |
| } | |
| } | |
| void DirectivesStack::print(outputStream* st) { | |
| MutexLocker locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag); | |
| CompilerDirectives* tmp = _top; | |
| while (tmp != nullptr) { | |
| tmp->print(st); | |
| tmp = tmp->next(); | |
| st->cr(); | |
| } | |
| } | |
| void DirectivesStack::release(DirectiveSet* set) { | |
| assert(set != nullptr, "Never nullptr"); | |
| MutexLocker 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() != nullptr, "Never nullptr"); | |
| 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(const methodHandle& method, AbstractCompiler *comp) { | |
| assert(_depth > 0, "Must never be empty"); | |
| DirectiveSet* match = nullptr; | |
| { | |
| MutexLocker locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag); | |
| CompilerDirectives* dir = _top; | |
| assert(dir != nullptr, "Must be initialized"); | |
| while (dir != nullptr) { | |
| if (dir->is_default_directive() || dir->match(method)) { | |
| match = dir->get_for(comp); | |
| assert(match != nullptr, "Consistency"); | |
| if (match->EnableOption || dir->is_default_directive()) { | |
| // The directiveSet for this compile is also enabled -> success | |
| dir->inc_refcount(); | |
| break; | |
| } | |
| } | |
| dir = dir->next(); | |
| } | |
| } | |
| guarantee(match != nullptr, "There should always be a default directive that matches"); | |
| // Check for legacy compile commands update, without DirectivesStack_lock | |
| return match->compilecommand_compatibility_init(method); | |
| } |