Skip to content

Commit

Permalink
8249809: avoid calling DirectiveSet::clone(this) in compilecommand_co…
Browse files Browse the repository at this point in the history
…mpatibility_init

Add DirectiveSet smart pointer to isolate cloning

Reviewed-by: simonis, thartmann
  • Loading branch information
Xin Liu committed Jul 31, 2020
1 parent 024fa09 commit a9ad296
Showing 1 changed file with 55 additions and 27 deletions.
82 changes: 55 additions & 27 deletions src/hotspot/share/compiler/compilerDirectives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,49 @@ DirectiveSet::~DirectiveSet() {
}
}

// 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 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 NULL 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
Expand All @@ -285,46 +328,39 @@ DirectiveSet* DirectiveSet::compilecommand_compatibility_init(const methodHandle
// 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
DirectiveSetPtr set(this);

// 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;
set.cloned()->BreakAtCompileOption = true;
}
if (!_modified[BreakAtExecuteIndex]) {
set->BreakAtExecuteOption = true;
changed = true;
set.cloned()->BreakAtExecuteOption = true;
}
}
if (!_modified[LogIndex]) {
bool log = CompilerOracle::should_log(method);
if (log != set->LogOption) {
set->LogOption = log;
changed = true;
set.cloned()->LogOption = log;
}
}

if (CompilerOracle::should_print(method)) {
if (!_modified[PrintAssemblyIndex]) {
set->PrintAssemblyOption = true;
changed = true;
set.cloned()->PrintAssemblyOption = true;
}
}
// Exclude as in should not compile == Enabled
if (CompilerOracle::should_exclude(method)) {
if (!_modified[ExcludeIndex]) {
set->ExcludeOption = true;
changed = true;
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] && CompilerOracle::has_option_value(method, #cc_flag, v) && v != this->name##Option) { set->name##Option = v; changed = true;} }
#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.cloned()->name##Option = v; } }
compilerdirectives_common_flags(init_default_cc)
compilerdirectives_c2_flags(init_default_cc)
compilerdirectives_c1_flags(init_default_cc)
Expand All @@ -338,14 +374,14 @@ DirectiveSet* DirectiveSet::compilecommand_compatibility_init(const methodHandle
ControlIntrinsicIter iter(option_value);

if (need_reset) {
set->_intrinsic_control_words.fill_in(TriBool());
set.cloned()->_intrinsic_control_words.fill_in(TriBool());
need_reset = false;
}

while (*iter != NULL) {
vmIntrinsics::ID id = vmIntrinsics::find_id(*iter);
if (id != vmIntrinsics::_none) {
set->_intrinsic_control_words[id] = iter.is_enabled();
set.cloned()->_intrinsic_control_words[id] = iter.is_enabled();
}

++iter;
Expand All @@ -358,29 +394,21 @@ DirectiveSet* DirectiveSet::compilecommand_compatibility_init(const methodHandle
ControlIntrinsicIter iter(option_value, true/*disable_all*/);

if (need_reset) {
set->_intrinsic_control_words.fill_in(TriBool());
set.cloned()->_intrinsic_control_words.fill_in(TriBool());
need_reset = false;
}

while (*iter != NULL) {
vmIntrinsics::ID id = vmIntrinsics::find_id(*iter);
if (id != vmIntrinsics::_none) {
set->_intrinsic_control_words[id] = false;
set.cloned()->_intrinsic_control_words[id] = false;
}

++iter;
}
}


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;
}
return set.commit();
}
// Nothing changed
return this;
Expand Down

0 comments on commit a9ad296

Please sign in to comment.