diff --git a/Changelog.md b/Changelog.md index c62d35f5a73e..bbe620ff5db6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Language Features: Compiler Features: * General: Speed up SHA-256 hashing (`picosha2`). +* General: Speed up compilation times. Bugfixes: * NatSpec: Disallow `@return` tag in event documentation. diff --git a/libyul/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp index 1a6f3911a1bb..caff4abc2614 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.cpp +++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp @@ -31,6 +31,8 @@ #include #include +#include + using namespace solidity; using namespace solidity::yul; using namespace solidity::util; @@ -129,6 +131,10 @@ void CommonSubexpressionEliminator::visit(Expression& _e) void CommonSubexpressionEliminator::assignValue(YulName _variable, Expression const* _value) { if (_value) - m_replacementCandidates[*_value].insert(_variable); + { + auto& candidates = m_replacementCandidates[*_value]; + if (std::find(candidates.begin(), candidates.end(), _variable) == candidates.end()) + candidates.emplace_back(_variable); + } DataFlowAnalyzer::assignValue(_variable, _value); } diff --git a/libyul/optimiser/CommonSubexpressionEliminator.h b/libyul/optimiser/CommonSubexpressionEliminator.h index 713c35f0229c..f8ba90c5916a 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.h +++ b/libyul/optimiser/CommonSubexpressionEliminator.h @@ -27,7 +27,8 @@ #include #include -#include +#include +#include namespace solidity::yul { @@ -62,10 +63,10 @@ class CommonSubexpressionEliminator: public DataFlowAnalyzer void assignValue(YulName _variable, Expression const* _value) override; private: - std::set m_returnVariables; + std::unordered_set m_returnVariables; std::unordered_map< std::reference_wrapper, - std::set, + std::vector, ExpressionHash, SyntacticallyEqualExpression > m_replacementCandidates; diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index 1aecd6bd4a91..694cc7fdacd4 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -107,7 +107,7 @@ void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl) std::set names; for (auto const& var: _varDecl.variables) names.emplace(var.name); - m_variableScopes.back().variables += names; + m_variableScopes.back().variables.insert(names.begin(), names.end()); if (_varDecl.value) { @@ -346,17 +346,24 @@ void DataFlowAnalyzer::clearValues(std::set const& _variablesToClear) _variablesToClear.count(_item.second); }); - // Also clear variables that reference variables to be cleared. - std::set referencingVariablesToClear; + // Also collect variables that directly reference variables to be cleared. + // Keep the original variables separate from the referencing ones; duplicate erases are harmless. + std::vector referencingVariablesToClear; std::vector const sortedVariablesToClear(_variablesToClear.begin(), _variablesToClear.end()); for (auto const& [referencingVariable, referencedVariables]: m_state.sortedReferences) // instead of checking each variable in `referencedVariables`, we check if there is any intersection making use of the // sortedness of the vectors, which can increase performance by up to 50% in pathological cases if (hasNonemptyIntersectionSorted(referencedVariables, sortedVariablesToClear)) - referencingVariablesToClear.emplace(referencingVariable); + referencingVariablesToClear.emplace_back(referencingVariable); - // Clear the value and update the reference relation. - for (auto const& name: _variablesToClear + referencingVariablesToClear) + // Clear values and reference information for the requested variables and their direct dependents. + // Duplicate erases are safe and are faster than a separate deduplication pass + for (auto const& name: _variablesToClear) + { + m_state.value.erase(name); + m_state.sortedReferences.erase(name); + } + for (auto const& name: referencingVariablesToClear) { m_state.value.erase(name); m_state.sortedReferences.erase(name); diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h index 7b731a4eb877..ffca1ddd7d01 100644 --- a/libyul/optimiser/DataFlowAnalyzer.h +++ b/libyul/optimiser/DataFlowAnalyzer.h @@ -34,6 +34,7 @@ #include #include +#include namespace solidity::yul { @@ -105,7 +106,7 @@ class DataFlowAnalyzer: public ASTModifier /// @returns the current value of the given variable, if known - always movable. AssignedValue const* variableValue(YulName _variable) const { return util::valueOrNullptr(m_state.value, _variable); } std::vector const* sortedReferences(YulName _variable) const { return util::valueOrNullptr(m_state.sortedReferences, _variable); } - std::map const& allValues() const { return m_state.value; } + std::unordered_map const& allValues() const { return m_state.value; } std::optional storageValue(YulName _key) const; std::optional memoryValue(YulName _key) const; std::optional keccakValue(YulName _start, YulName _length) const; @@ -178,7 +179,7 @@ class DataFlowAnalyzer: public ASTModifier struct State { /// Current values of variables, always movable. - std::map value; + std::unordered_map value; /// m_references[a].contains(b) <=> the current expression assigned to a references b /// The mapped vectors _must always_ be sorted std::unordered_map> sortedReferences; @@ -213,7 +214,7 @@ class DataFlowAnalyzer: public ASTModifier struct Scope { explicit Scope(bool _isFunction): isFunction(_isFunction) {} - std::set variables; + std::unordered_set variables; bool isFunction; }; /// Special expression whose address will be used in m_value.