From dd8f12760b0ef6630a40d3a4d5eec6a3aadbe95f Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 4 Nov 2021 15:40:57 +0100 Subject: [PATCH] Introduce forEach for yul ast nodes. --- libyul/CMakeLists.txt | 2 - libyul/ControlFlowSideEffectsCollector.cpp | 2 - libyul/optimiser/ASTWalker.h | 31 +++++++++++++ libyul/optimiser/DataFlowAnalyzer.cpp | 26 +++++------ .../optimiser/FunctionDefinitionCollector.cpp | 36 --------------- .../optimiser/FunctionDefinitionCollector.h | 44 ------------------- libyul/optimiser/NameCollector.cpp | 26 ++++++++--- libyul/optimiser/NameCollector.h | 22 ++++------ libyul/optimiser/SSATransform.cpp | 22 +++------- libyul/optimiser/StackLimitEvader.cpp | 4 +- libyul/optimiser/StackToMemoryMover.cpp | 4 +- 11 files changed, 79 insertions(+), 140 deletions(-) delete mode 100644 libyul/optimiser/FunctionDefinitionCollector.cpp delete mode 100644 libyul/optimiser/FunctionDefinitionCollector.h diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 30b97b5f7..3c980fbb8 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -140,8 +140,6 @@ add_library(yul optimiser/FullInliner.h optimiser/FunctionCallFinder.cpp optimiser/FunctionCallFinder.h - optimiser/FunctionDefinitionCollector.cpp - optimiser/FunctionDefinitionCollector.h optimiser/FunctionGrouper.cpp optimiser/FunctionGrouper.h optimiser/FunctionHoister.cpp diff --git a/libyul/ControlFlowSideEffectsCollector.cpp b/libyul/ControlFlowSideEffectsCollector.cpp index f907f83f5..bdcf70a78 100644 --- a/libyul/ControlFlowSideEffectsCollector.cpp +++ b/libyul/ControlFlowSideEffectsCollector.cpp @@ -18,8 +18,6 @@ #include -#include - #include #include diff --git a/libyul/optimiser/ASTWalker.h b/libyul/optimiser/ASTWalker.h index 107658dfc..cd98a90ca 100644 --- a/libyul/optimiser/ASTWalker.h +++ b/libyul/optimiser/ASTWalker.h @@ -102,4 +102,35 @@ protected: } }; +namespace detail +{ +template < + typename Node, + typename Visitor, + typename Base = std::conditional_t, ASTWalker, ASTModifier> +> +struct ForEach: Base +{ + template + ForEach(Callable&& _visitor): visitor(std::forward(_visitor)) {} + + using Base::operator(); + void operator()(Node& _node) override + { + visitor(_node); + Base::operator()(_node); + } + + Visitor visitor; +}; +} + +/// Helper function that traverses the AST and calls the visitor for each +/// node of a specific type. +template +void forEach(Entry&& _entry, Visitor&& _visitor) +{ + detail::ForEach>{std::forward(_visitor)}(std::forward(_entry)); +} + } diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index c6d9decaf..af25cde59 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -45,9 +45,9 @@ DataFlowAnalyzer::DataFlowAnalyzer( Dialect const& _dialect, map _functionSideEffects ): -m_dialect(_dialect), -m_functionSideEffects(std::move(_functionSideEffects)), -m_knowledgeBase(_dialect, m_value) + m_dialect(_dialect), + m_functionSideEffects(std::move(_functionSideEffects)), + m_knowledgeBase(_dialect, m_value) { if (auto const* builtin = _dialect.memoryStoreFunction(YulString{})) m_storeFunctionName[static_cast(StoreLoadLocation::Memory)] = builtin->name; @@ -123,9 +123,7 @@ void DataFlowAnalyzer::operator()(If& _if) joinKnowledge(storage, memory); - Assignments assignments; - assignments(_if.body); - clearValues(assignments.names()); + clearValues(assignedVariableNames(_if.body)); } void DataFlowAnalyzer::operator()(Switch& _switch) @@ -140,11 +138,10 @@ void DataFlowAnalyzer::operator()(Switch& _switch) (*this)(_case.body); joinKnowledge(storage, memory); - Assignments assignments; - assignments(_case.body); - assignedVariables += assignments.names(); + set variables = assignedVariableNames(_case.body); + assignedVariables += variables; // This is a little too destructive, we could retain the old values. - clearValues(assignments.names()); + clearValues(variables); clearKnowledgeIfInvalidated(_case.body); } for (auto& _case: _switch.cases) @@ -190,10 +187,9 @@ void DataFlowAnalyzer::operator()(ForLoop& _for) AssignmentsSinceContinue assignmentsSinceCont; assignmentsSinceCont(_for.body); - Assignments assignments; - assignments(_for.body); - assignments(_for.post); - clearValues(assignments.names()); + set assignedVariables = + assignedVariableNames(_for.body) + assignedVariableNames(_for.post); + clearValues(assignedVariables); // break/continue are tricky for storage and thus we almost always clear here. clearKnowledgeIfInvalidated(*_for.condition); @@ -205,7 +201,7 @@ void DataFlowAnalyzer::operator()(ForLoop& _for) clearValues(assignmentsSinceCont.names()); clearKnowledgeIfInvalidated(_for.body); (*this)(_for.post); - clearValues(assignments.names()); + clearValues(assignedVariables); clearKnowledgeIfInvalidated(*_for.condition); clearKnowledgeIfInvalidated(_for.post); clearKnowledgeIfInvalidated(_for.body); diff --git a/libyul/optimiser/FunctionDefinitionCollector.cpp b/libyul/optimiser/FunctionDefinitionCollector.cpp deleted file mode 100644 index dff57a32a..000000000 --- a/libyul/optimiser/FunctionDefinitionCollector.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity 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 for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see . -*/ - -#include -#include - -using namespace std; -using namespace solidity; -using namespace solidity::yul; - -map FunctionDefinitionCollector::run(Block const& _block) -{ - FunctionDefinitionCollector functionDefinitionCollector; - functionDefinitionCollector(_block); - return functionDefinitionCollector.m_functionDefinitions; -} - -void FunctionDefinitionCollector::operator()(FunctionDefinition const& _functionDefinition) -{ - m_functionDefinitions[_functionDefinition.name] = &_functionDefinition; - ASTWalker::operator()(_functionDefinition); -} diff --git a/libyul/optimiser/FunctionDefinitionCollector.h b/libyul/optimiser/FunctionDefinitionCollector.h deleted file mode 100644 index c9828aced..000000000 --- a/libyul/optimiser/FunctionDefinitionCollector.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity 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 for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see . -*/ -/** - * AST walker that finds all function definitions and stores them into a map indexed by the function names. - */ -#pragma once - -#include - -#include - -namespace solidity::yul -{ - -/** - * AST walker that finds all function definitions and stores them into a map indexed by the function names. - * - * Prerequisite: Disambiguator - */ -class FunctionDefinitionCollector: ASTWalker -{ -public: - static std::map run(Block const& _block); -private: - using ASTWalker::operator(); - void operator()(FunctionDefinition const& _functionDefinition) override; - std::map m_functionDefinitions; -}; - -} diff --git a/libyul/optimiser/NameCollector.cpp b/libyul/optimiser/NameCollector.cpp index 69e627fbb..eca509f43 100644 --- a/libyul/optimiser/NameCollector.cpp +++ b/libyul/optimiser/NameCollector.cpp @@ -78,13 +78,6 @@ map ReferencesCounter::countReferences(Expression const& _exp return counter.references(); } -void Assignments::operator()(Assignment const& _assignment) -{ - for (auto const& var: _assignment.variableNames) - m_names.emplace(var.name); -} - - void AssignmentsSinceContinue::operator()(ForLoop const& _forLoop) { m_forLoopDepth++; @@ -109,3 +102,22 @@ void AssignmentsSinceContinue::operator()(FunctionDefinition const&) { yulAssert(false, ""); } + +std::set solidity::yul::assignedVariableNames(Block const& _code) +{ + std::set names; + forEach(_code, [&](Assignment const& _assignment) { + for (auto const& var: _assignment.variableNames) + names.emplace(var.name); + }); + return names; +} + +map solidity::yul::allFunctionDefinitions(Block const& _block) +{ + std::map result; + forEach(_block, [&](FunctionDefinition const& _function) { + result[_function.name] = &_function; + }); + return result; +} diff --git a/libyul/optimiser/NameCollector.h b/libyul/optimiser/NameCollector.h index 17afcec1b..8c9dc14ff 100644 --- a/libyul/optimiser/NameCollector.h +++ b/libyul/optimiser/NameCollector.h @@ -91,20 +91,6 @@ private: std::map m_references; }; -/** - * Specific AST walker that finds all variables that are assigned to. - */ -class Assignments: public ASTWalker -{ -public: - using ASTWalker::operator (); - void operator()(Assignment const& _assignment) override; - - std::set const& names() const { return m_names; } -private: - std::set m_names; -}; - /** * Collects all names from a given continue statement on onwards. * @@ -130,4 +116,12 @@ private: std::set m_names; }; +/// @returns the names of all variables that are assigned to inside @a _code. +/// (ignores variable declarations) +std::set assignedVariableNames(Block const& _code); + +/// @returns all function definitions anywhere in the AST. +/// Requires disambiguated source. +std::map allFunctionDefinitions(Block const& _block); + } diff --git a/libyul/optimiser/SSATransform.cpp b/libyul/optimiser/SSATransform.cpp index f0ce68c92..d86e6fbee 100644 --- a/libyul/optimiser/SSATransform.cpp +++ b/libyul/optimiser/SSATransform.cpp @@ -196,12 +196,7 @@ void IntroduceControlFlowSSA::operator()(ForLoop& _for) { yulAssert(_for.pre.statements.empty(), "For loop init rewriter not run."); - Assignments assignments; - assignments(_for.body); - assignments(_for.post); - - - for (auto const& var: assignments.names()) + for (auto const& var: assignedVariableNames(_for.body) + assignedVariableNames(_for.post)) if (m_variablesInScope.count(var)) m_variablesToReassign.insert(var); @@ -359,11 +354,7 @@ void PropagateValues::operator()(ForLoop& _for) { yulAssert(_for.pre.statements.empty(), "For loop init rewriter not run."); - Assignments assignments; - assignments(_for.body); - assignments(_for.post); - - for (auto const& var: assignments.names()) + for (auto const& var: assignedVariableNames(_for.body) + assignedVariableNames(_for.post)) m_currentVariableValues.erase(var); visit(*_for.condition); @@ -389,11 +380,10 @@ void PropagateValues::operator()(Block& _block) void SSATransform::run(OptimiserStepContext& _context, Block& _ast) { TypeInfo typeInfo(_context.dialect, _ast); - Assignments assignments; - assignments(_ast); - IntroduceSSA{_context.dispenser, assignments.names(), typeInfo}(_ast); - IntroduceControlFlowSSA{_context.dispenser, assignments.names(), typeInfo}(_ast); - PropagateValues{assignments.names()}(_ast); + set assignedVariables = assignedVariableNames(_ast); + IntroduceSSA{_context.dispenser, assignedVariables, typeInfo}(_ast); + IntroduceControlFlowSSA{_context.dispenser, assignedVariables, typeInfo}(_ast); + PropagateValues{assignedVariables}(_ast); } diff --git a/libyul/optimiser/StackLimitEvader.cpp b/libyul/optimiser/StackLimitEvader.cpp index b77bd4f38..b4b9d5469 100644 --- a/libyul/optimiser/StackLimitEvader.cpp +++ b/libyul/optimiser/StackLimitEvader.cpp @@ -18,8 +18,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -193,7 +193,7 @@ void StackLimitEvader::run( if (_unreachableVariables.count(function)) return; - map functionDefinitions = FunctionDefinitionCollector::run(*_object.code); + map functionDefinitions = allFunctionDefinitions(*_object.code); MemoryOffsetAllocator memoryOffsetAllocator{_unreachableVariables, callGraph.functionCalls, functionDefinitions}; uint64_t requiredSlots = memoryOffsetAllocator.run(); diff --git a/libyul/optimiser/StackToMemoryMover.cpp b/libyul/optimiser/StackToMemoryMover.cpp index dcc5f45f6..425680923 100644 --- a/libyul/optimiser/StackToMemoryMover.cpp +++ b/libyul/optimiser/StackToMemoryMover.cpp @@ -15,7 +15,7 @@ along with solidity. If not, see . */ #include -#include +#include #include #include @@ -87,7 +87,7 @@ void StackToMemoryMover::run( _context, memoryOffsetTracker, util::applyMap( - FunctionDefinitionCollector::run(_block), + allFunctionDefinitions(_block), util::mapTuple([](YulString _name, FunctionDefinition const* _funDef) { return make_pair(_name, _funDef->returnVariables); }),