diff --git a/libyul/optimiser/LoopInvariantCodeMotion.cpp b/libyul/optimiser/LoopInvariantCodeMotion.cpp index 9a81e5fc8..3cc8397a9 100644 --- a/libyul/optimiser/LoopInvariantCodeMotion.cpp +++ b/libyul/optimiser/LoopInvariantCodeMotion.cpp @@ -35,9 +35,9 @@ void LoopInvariantCodeMotion::run(OptimiserStepContext& _context, Block& _ast) { map functionSideEffects = SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast)); - + bool containsMSize = MSizeFinder::containsMSize(_context.dialect, _ast); set ssaVars = SSAValueTracker::ssaVariables(_ast); - LoopInvariantCodeMotion{_context.dialect, ssaVars, functionSideEffects}(_ast); + LoopInvariantCodeMotion{_context.dialect, ssaVars, functionSideEffects, containsMSize}(_ast); } void LoopInvariantCodeMotion::operator()(Block& _block) @@ -57,7 +57,8 @@ void LoopInvariantCodeMotion::operator()(Block& _block) bool LoopInvariantCodeMotion::canBePromoted( VariableDeclaration const& _varDecl, - set const& _varsDefinedInCurrentScope + set const& _varsDefinedInCurrentScope, + SideEffects const& _forLoopSideEffects ) const { // A declaration can be promoted iff @@ -73,7 +74,8 @@ bool LoopInvariantCodeMotion::canBePromoted( for (auto const& ref: ReferencesCounter::countReferences(*_varDecl.value, ReferencesCounter::OnlyVariables)) if (_varsDefinedInCurrentScope.count(ref.first) || !m_ssaVariables.count(ref.first)) return false; - if (!SideEffectsCollector{m_dialect, *_varDecl.value, &m_functionSideEffects}.movable()) + SideEffectsCollector sideEffects{m_dialect, *_varDecl.value, &m_functionSideEffects}; + if (!sideEffects.movableRelativeTo(_forLoopSideEffects, m_containsMSize)) return false; } return true; @@ -82,6 +84,10 @@ bool LoopInvariantCodeMotion::canBePromoted( optional> LoopInvariantCodeMotion::rewriteLoop(ForLoop& _for) { assertThrow(_for.pre.statements.empty(), OptimizerException, ""); + + auto forLoopSideEffects = + SideEffectsCollector{m_dialect, _for, &m_functionSideEffects}.sideEffects(); + vector replacement; for (Block* block: {&_for.post, &_for.body}) { @@ -93,7 +99,7 @@ optional> LoopInvariantCodeMotion::rewriteLoop(ForLoop& _for) if (holds_alternative(_s)) { VariableDeclaration const& varDecl = std::get(_s); - if (canBePromoted(varDecl, varsDefinedInScope)) + if (canBePromoted(varDecl, varsDefinedInScope, forLoopSideEffects)) { replacement.emplace_back(std::move(_s)); // Do not add the variables declared here to varsDefinedInScope because we are moving them. diff --git a/libyul/optimiser/LoopInvariantCodeMotion.h b/libyul/optimiser/LoopInvariantCodeMotion.h index a3bf1784f..1b2647924 100644 --- a/libyul/optimiser/LoopInvariantCodeMotion.h +++ b/libyul/optimiser/LoopInvariantCodeMotion.h @@ -49,17 +49,24 @@ private: explicit LoopInvariantCodeMotion( Dialect const& _dialect, std::set const& _ssaVariables, - std::map const& _functionSideEffects + std::map const& _functionSideEffects, + bool _containsMSize ): + m_containsMSize(_containsMSize), m_dialect(_dialect), m_ssaVariables(_ssaVariables), m_functionSideEffects(_functionSideEffects) { } /// @returns true if the given variable declaration can be moved to in front of the loop. - bool canBePromoted(VariableDeclaration const& _varDecl, std::set const& _varsDefinedInCurrentScope) const; + bool canBePromoted( + VariableDeclaration const& _varDecl, + std::set const& _varsDefinedInCurrentScope, + SideEffects const& _forLoopSideEffects + ) const; std::optional> rewriteLoop(ForLoop& _for); + bool m_containsMSize = true; Dialect const& m_dialect; std::set const& m_ssaVariables; std::map const& m_functionSideEffects; diff --git a/libyul/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp index 1ceeec3e2..735ed13d0 100644 --- a/libyul/optimiser/Semantics.cpp +++ b/libyul/optimiser/Semantics.cpp @@ -62,6 +62,16 @@ SideEffectsCollector::SideEffectsCollector( operator()(_ast); } +SideEffectsCollector::SideEffectsCollector( + Dialect const& _dialect, + ForLoop const& _ast, + map const* _functionSideEffects +): + SideEffectsCollector(_dialect, _functionSideEffects) +{ + operator()(_ast); +} + void SideEffectsCollector::operator()(FunctionCall const& _functionCall) { ASTWalker::operator()(_functionCall); diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h index 1ff7e0d7e..70ec119ac 100644 --- a/libyul/optimiser/Semantics.h +++ b/libyul/optimiser/Semantics.h @@ -54,11 +54,48 @@ public: Block const& _ast, std::map const* _functionSideEffects = nullptr ); + SideEffectsCollector( + Dialect const& _dialect, + ForLoop const& _ast, + std::map const* _functionSideEffects = nullptr + ); using ASTWalker::operator(); void operator()(FunctionCall const& _functionCall) override; bool movable() const { return m_sideEffects.movable; } + + bool movableRelativeTo(SideEffects const& _other, bool _codeContainsMSize) + { + if (!m_sideEffects.cannotLoop) + return false; + + if (m_sideEffects.movable) + return true; + + if ( + !m_sideEffects.movableApartFromEffects || + m_sideEffects.storage == SideEffects::Write || + m_sideEffects.otherState == SideEffects::Write || + m_sideEffects.memory == SideEffects::Write + ) + return false; + + if (m_sideEffects.otherState == SideEffects::Read) + if (_other.otherState == SideEffects::Write) + return false; + + if (m_sideEffects.storage == SideEffects::Read) + if (_other.storage == SideEffects::Write) + return false; + + if (m_sideEffects.memory == SideEffects::Read) + if (_codeContainsMSize || _other.memory == SideEffects::Write) + return false; + + return true; + } + bool canBeRemoved(bool _allowMSizeModification = false) const { if (_allowMSizeModification) @@ -70,6 +107,7 @@ public: bool invalidatesStorage() const { return m_sideEffects.storage == SideEffects::Write; } bool invalidatesMemory() const { return m_sideEffects.memory == SideEffects::Write; } + SideEffects sideEffects() { return m_sideEffects; } private: Dialect const& m_dialect;