From 81f24f24e6d827d45b1ae1b22e88388d30db3dd0 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 10 Jan 2019 20:29:30 +0100 Subject: [PATCH] Add equivalent function combiner as Yul optimizer step. --- libdevcore/CommonData.h | 6 + libyul/CMakeLists.txt | 4 + .../CommonSubexpressionEliminator.cpp | 2 +- .../optimiser/EquivalentFunctionCombiner.cpp | 41 ++++ libyul/optimiser/EquivalentFunctionCombiner.h | 49 +++++ .../optimiser/EquivalentFunctionDetector.cpp | 63 ++++++ libyul/optimiser/EquivalentFunctionDetector.h | 71 +++++++ libyul/optimiser/SimplificationRules.cpp | 2 +- libyul/optimiser/Suite.cpp | 4 + libyul/optimiser/SyntacticalEquality.cpp | 195 ++++++++++++++---- libyul/optimiser/SyntacticalEquality.h | 60 +++++- test/libyul/YulOptimizerTest.cpp | 6 + .../multiple_complex.yul | 114 ++++++++++ .../equivalentFunctionCombiner/simple.yul | 20 ++ .../simple_different_vars.yul | 22 ++ .../switch_case_order.yul | 32 +++ .../yulOptimizerTests/fullSuite/abi2.yul | 38 ++-- .../fullSuite/abi_example1.yul | 157 +++++++------- .../yulOptimizerTests/fullSuite/aztec.yul | 36 ++-- test/tools/yulopti.cpp | 6 +- 20 files changed, 762 insertions(+), 166 deletions(-) create mode 100644 libyul/optimiser/EquivalentFunctionCombiner.cpp create mode 100644 libyul/optimiser/EquivalentFunctionCombiner.h create mode 100644 libyul/optimiser/EquivalentFunctionDetector.cpp create mode 100644 libyul/optimiser/EquivalentFunctionDetector.h create mode 100644 test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul create mode 100644 test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul create mode 100644 test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul create mode 100644 test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 7c59c505c..1d668f268 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -275,4 +275,10 @@ std::string getChecksummedAddress(std::string const& _addr); bool isValidHex(std::string const& _string); bool isValidDecimal(std::string const& _string); +template +bool containerEqual(Container const& _lhs, Container const& _rhs, Compare&& _compare) +{ + return std::equal(std::begin(_lhs), std::end(_lhs), std::begin(_rhs), std::end(_rhs), std::forward(_compare)); +} + } diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index ad9812bd6..44af2fc33 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -44,6 +44,10 @@ add_library(yul optimiser/DataFlowAnalyzer.h optimiser/Disambiguator.cpp optimiser/Disambiguator.h + optimiser/EquivalentFunctionDetector.cpp + optimiser/EquivalentFunctionDetector.h + optimiser/EquivalentFunctionCombiner.cpp + optimiser/EquivalentFunctionCombiner.h optimiser/ExpressionInliner.cpp optimiser/ExpressionInliner.h optimiser/ExpressionJoiner.cpp diff --git a/libyul/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp index 8ce003e70..5f182ebaf 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.cpp +++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp @@ -74,7 +74,7 @@ void CommonSubexpressionEliminator::visit(Expression& _e) { assertThrow(var.second, OptimizerException, ""); assertThrow(inScope(var.first), OptimizerException, ""); - if (SyntacticalEqualityChecker::equal(_e, *var.second)) + if (SyntacticallyEqual{}(_e, *var.second)) { _e = Identifier{locationOf(_e), var.first}; break; diff --git a/libyul/optimiser/EquivalentFunctionCombiner.cpp b/libyul/optimiser/EquivalentFunctionCombiner.cpp new file mode 100644 index 000000000..939e63d23 --- /dev/null +++ b/libyul/optimiser/EquivalentFunctionCombiner.cpp @@ -0,0 +1,41 @@ +/* + 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 . +*/ +/** + * Optimiser component that combines syntactically equivalent functions. + */ + +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace yul; +using namespace dev::solidity; + +void EquivalentFunctionCombiner::run(Block& _ast) +{ + EquivalentFunctionCombiner{EquivalentFunctionDetector::run(_ast)}(_ast); +} + +void EquivalentFunctionCombiner::operator()(FunctionCall& _funCall) +{ + auto it = m_duplicates.find(_funCall.functionName.name); + if (it != m_duplicates.end()) + _funCall.functionName.name = it->second->name; + ASTModifier::operator()(_funCall); +} diff --git a/libyul/optimiser/EquivalentFunctionCombiner.h b/libyul/optimiser/EquivalentFunctionCombiner.h new file mode 100644 index 000000000..0c766ded4 --- /dev/null +++ b/libyul/optimiser/EquivalentFunctionCombiner.h @@ -0,0 +1,49 @@ +/* + 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 . +*/ +/** + * Optimiser component that combines syntactically equivalent functions. + */ +#pragma once + +#include +#include +#include + +namespace yul +{ + +/** + * Optimiser component that detects syntactically equivalent functions and replaces all calls to any of them by calls + * to one particular of them. + * + * Prerequisite: Disambiguator, Function Hoister + */ +class EquivalentFunctionCombiner: public ASTModifier +{ +public: + static void run(Block& _ast); + + using ASTModifier::operator(); + void operator()(FunctionCall& _funCall) override; + +private: + EquivalentFunctionCombiner(std::map _duplicates): m_duplicates(std::move(_duplicates)) {} + std::map m_duplicates; +}; + + +} diff --git a/libyul/optimiser/EquivalentFunctionDetector.cpp b/libyul/optimiser/EquivalentFunctionDetector.cpp new file mode 100644 index 000000000..d3a697bdc --- /dev/null +++ b/libyul/optimiser/EquivalentFunctionDetector.cpp @@ -0,0 +1,63 @@ +/* + 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 . +*/ +/** + * Optimiser component that combines syntactically equivalent functions. + */ + +#include +#include + +#include +#include + +using namespace std; +using namespace dev; +using namespace yul; +using namespace solidity; + +void EquivalentFunctionDetector::operator()(FunctionDefinition const& _fun) +{ + RoughHeuristic heuristic(_fun); + auto& candidates = m_candidates[heuristic]; + for (auto const& candidate: candidates) + if (SyntacticallyEqual{}.statementEqual(_fun, *candidate)) + { + m_duplicates[_fun.name] = candidate; + return; + } + candidates.push_back(&_fun); +} + +bool EquivalentFunctionDetector::RoughHeuristic::operator<(EquivalentFunctionDetector::RoughHeuristic const& _rhs) const +{ + if ( + std::make_tuple(m_fun.parameters.size(), m_fun.returnVariables.size()) == + std::make_tuple(_rhs.m_fun.parameters.size(), _rhs.m_fun.returnVariables.size()) + ) + return codeSize() < _rhs.codeSize(); + else + return + std::make_tuple(m_fun.parameters.size(), m_fun.returnVariables.size()) < + std::make_tuple(_rhs.m_fun.parameters.size(), _rhs.m_fun.returnVariables.size()); +} + +size_t EquivalentFunctionDetector::RoughHeuristic::codeSize() const +{ + if (!m_codeSize) + m_codeSize = CodeSize::codeSize(m_fun.body); + return *m_codeSize; +} diff --git a/libyul/optimiser/EquivalentFunctionDetector.h b/libyul/optimiser/EquivalentFunctionDetector.h new file mode 100644 index 000000000..329fd385e --- /dev/null +++ b/libyul/optimiser/EquivalentFunctionDetector.h @@ -0,0 +1,71 @@ +/* + 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 . +*/ +/** + * Optimiser component that combines syntactically equivalent functions. + */ +#pragma once + +#include +#include + +namespace yul +{ + +/** + * Optimiser component that detects syntactically equivalent functions. + * + * Prerequisite: Disambiguator + */ +class EquivalentFunctionDetector: public ASTWalker +{ +public: + static std::map run(Block& _block) + { + EquivalentFunctionDetector detector{}; + detector(_block); + return std::move(detector.m_duplicates); + } + + using ASTWalker::operator(); + void operator()(FunctionDefinition const& _fun) override; + +private: + EquivalentFunctionDetector() = default; + /** + * Fast heuristic to detect distinct, resp. potentially equal functions. + * + * Defines a partial order on function definitions. If two functions + * are comparable (one is "less" than the other), they are distinct. + * If not (neither is "less" than the other), they are *potentially* equal. + */ + class RoughHeuristic + { + public: + RoughHeuristic(FunctionDefinition const& _fun): m_fun(_fun) {} + bool operator<(RoughHeuristic const& _rhs) const; + private: + std::size_t codeSize() const; + FunctionDefinition const& m_fun; + mutable boost::optional m_codeSize; + // In case the heuristic doesn't turn out to be good enough, we might want to define a hash function for code blocks. + }; + std::map> m_candidates; + std::map m_duplicates; +}; + + +} diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 037dad972..1b620b641 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -171,7 +171,7 @@ bool Pattern::matches( Expression const* firstMatch = (*m_matchGroups)[m_matchGroup]; assertThrow(firstMatch, OptimizerException, "Match set but to null."); return - SyntacticalEqualityChecker::equal(*firstMatch, _expr) && + SyntacticallyEqual{}(*firstMatch, _expr) && MovableChecker(_dialect, _expr).movable(); } else if (m_kind == PatternKind::Any) diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 48914cf85..38c0bf493 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,8 @@ void OptimiserSuite::run( (FunctionHoister{})(ast); (BlockFlattener{})(ast); (FunctionGrouper{})(ast); + EquivalentFunctionCombiner::run(ast); + UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); (ForLoopInitRewriter{})(ast); (BlockFlattener{})(ast); StructuralSimplifier{_dialect}(ast); @@ -101,6 +104,7 @@ void OptimiserSuite::run( CommonSubexpressionEliminator{_dialect}(ast); (FunctionGrouper{})(ast); + EquivalentFunctionCombiner::run(ast); FullInliner{ast, dispenser}.run(); SSATransform::run(ast, dispenser); diff --git a/libyul/optimiser/SyntacticalEquality.cpp b/libyul/optimiser/SyntacticalEquality.cpp index 99ce06e5c..ba8cc7934 100644 --- a/libyul/optimiser/SyntacticalEquality.cpp +++ b/libyul/optimiser/SyntacticalEquality.cpp @@ -22,6 +22,7 @@ #include #include +#include #include @@ -29,48 +30,166 @@ using namespace std; using namespace dev; using namespace yul; -bool SyntacticalEqualityChecker::equal(Expression const& _e1, Expression const& _e2) +bool SyntacticallyEqual::operator()(Expression const& _lhs, Expression const& _rhs) { - if (_e1.type() != _e2.type()) + return boost::apply_visitor([this](auto&& _lhsExpr, auto&& _rhsExpr) -> bool { + // ``this->`` is redundant, but required to work around a bug present in gcc 6.x. + return this->expressionEqual(_lhsExpr, _rhsExpr); + }, _lhs, _rhs); +} + +bool SyntacticallyEqual::operator()(Statement const& _lhs, Statement const& _rhs) +{ + return boost::apply_visitor([this](auto&& _lhsStmt, auto&& _rhsStmt) -> bool { + // ``this->`` is redundant, but required to work around a bug present in gcc 6.x. + return this->statementEqual(_lhsStmt, _rhsStmt); + }, _lhs, _rhs); +} + +bool SyntacticallyEqual::expressionEqual(FunctionalInstruction const& _lhs, FunctionalInstruction const& _rhs) +{ + return + _lhs.instruction == _rhs.instruction && + containerEqual(_lhs.arguments, _rhs.arguments, [this](Expression const& _lhsExpr, Expression const& _rhsExpr) -> bool { + return (*this)(_lhsExpr, _rhsExpr); + }); +} + +bool SyntacticallyEqual::expressionEqual(FunctionCall const& _lhs, FunctionCall const& _rhs) +{ + return + expressionEqual(_lhs.functionName, _rhs.functionName) && + containerEqual(_lhs.arguments, _rhs.arguments, [this](Expression const& _lhsExpr, Expression const& _rhsExpr) -> bool { + return (*this)(_lhsExpr, _rhsExpr); + }); +} + +bool SyntacticallyEqual::expressionEqual(Identifier const& _lhs, Identifier const& _rhs) +{ + auto lhsIt = m_identifiersLHS.find(_lhs.name); + auto rhsIt = m_identifiersRHS.find(_rhs.name); + return + (lhsIt == m_identifiersLHS.end() && rhsIt == m_identifiersRHS.end() && _lhs.name == _rhs.name) || + (lhsIt != m_identifiersLHS.end() && rhsIt != m_identifiersRHS.end() && lhsIt->second == rhsIt->second); +} +bool SyntacticallyEqual::expressionEqual(Literal const& _lhs, Literal const& _rhs) +{ + if (_lhs.kind != _rhs.kind || _lhs.type != _rhs.type) return false; - // TODO This somehow calls strcmp - WHERE? - - // TODO This should be replaced by some kind of AST walker as soon as it gets - // more complex. - if (_e1.type() == typeid(FunctionalInstruction)) - { - auto const& e1 = boost::get(_e1); - auto const& e2 = boost::get(_e2); - return - e1.instruction == e2.instruction && - equalVector(e1.arguments, e2.arguments); - } - else if (_e1.type() == typeid(FunctionCall)) - { - auto const& e1 = boost::get(_e1); - auto const& e2 = boost::get(_e2); - return - equal(e1.functionName, e2.functionName) && - equalVector(e1.arguments, e2.arguments); - } - else if (_e1.type() == typeid(Identifier)) - return boost::get(_e1).name == boost::get(_e2).name; - else if (_e1.type() == typeid(Literal)) - { - auto const& e1 = boost::get(_e1); - auto const& e2 = boost::get(_e2); - return e1.kind == e2.kind && e1.value == e2.value && e1.type == e2.type; - } + if (_lhs.kind == LiteralKind::Number) + return valueOfNumberLiteral(_lhs) == valueOfNumberLiteral(_rhs); else - { - assertThrow(false, OptimizerException, "Invalid expression"); - } - return false; + return _lhs.value == _rhs.value; } -bool SyntacticalEqualityChecker::equalVector(vector const& _e1, vector const& _e2) +bool SyntacticallyEqual::statementEqual(ExpressionStatement const& _lhs, ExpressionStatement const& _rhs) { - return _e1.size() == _e2.size() && - std::equal(begin(_e1), end(_e1), begin(_e2), SyntacticalEqualityChecker::equal); - + return (*this)(_lhs.expression, _rhs.expression); +} +bool SyntacticallyEqual::statementEqual(Assignment const& _lhs, Assignment const& _rhs) +{ + return containerEqual( + _lhs.variableNames, + _rhs.variableNames, + [this](Identifier const& _lhsVarName, Identifier const& _rhsVarName) -> bool { + return this->expressionEqual(_lhsVarName, _rhsVarName); + } + ) && (*this)(*_lhs.value, *_rhs.value); +} + +bool SyntacticallyEqual::statementEqual(VariableDeclaration const& _lhs, VariableDeclaration const& _rhs) +{ + // first visit expression, then variable declarations + if (!compareSharedPtr(_lhs.value, _rhs.value)) + return false; + return containerEqual(_lhs.variables, _rhs.variables, [this](TypedName const& _lhsVarName, TypedName const& _rhsVarName) -> bool { + return this->visitDeclaration(_lhsVarName, _rhsVarName); + }); +} + +bool SyntacticallyEqual::statementEqual(FunctionDefinition const& _lhs, FunctionDefinition const& _rhs) +{ + auto compare = [this](TypedName const& _lhsVarName, TypedName const& _rhsVarName) -> bool { + return this->visitDeclaration(_lhsVarName, _rhsVarName); + }; + // first visit parameter declarations, then body + if (!containerEqual(_lhs.parameters, _rhs.parameters, compare)) + return false; + if (!containerEqual(_lhs.returnVariables, _rhs.returnVariables, compare)) + return false; + return statementEqual(_lhs.body, _rhs.body); +} + +bool SyntacticallyEqual::statementEqual(If const& _lhs, If const& _rhs) +{ + return + compareSharedPtr(_lhs.condition, _rhs.condition) && + statementEqual(_lhs.body, _rhs.body); +} + +bool SyntacticallyEqual::statementEqual(Switch const& _lhs, Switch const& _rhs) +{ + static auto const sortCasesByValue = [](Case const* _lhsCase, Case const* _rhsCase) -> bool { + return Less{}(_lhsCase->value.get(), _rhsCase->value.get()); + }; + std::set lhsCases(sortCasesByValue); + std::set rhsCases(sortCasesByValue); + for (auto const& lhsCase: _lhs.cases) + lhsCases.insert(&lhsCase); + for (auto const& rhsCase: _rhs.cases) + rhsCases.insert(&rhsCase); + return + compareSharedPtr(_lhs.expression, _rhs.expression) && + containerEqual(lhsCases, rhsCases, [this](Case const* _lhsCase, Case const* _rhsCase) -> bool { + return this->switchCaseEqual(*_lhsCase, *_rhsCase); + }); +} + + +bool SyntacticallyEqual::switchCaseEqual(Case const& _lhs, Case const& _rhs) +{ + return + compareSharedPtr(_lhs.value, _rhs.value) && + statementEqual(_lhs.body, _rhs.body); +} + +bool SyntacticallyEqual::statementEqual(ForLoop const& _lhs, ForLoop const& _rhs) +{ + return + statementEqual(_lhs.pre, _rhs.pre) && + compareSharedPtr(_lhs.condition, _rhs.condition) && + statementEqual(_lhs.body, _rhs.body) && + statementEqual(_lhs.post, _rhs.post); +} + +bool SyntacticallyEqual::statementEqual(Instruction const&, Instruction const&) +{ + assertThrow(false, OptimizerException, ""); +} + +bool SyntacticallyEqual::statementEqual(Label const&, Label const&) +{ + assertThrow(false, OptimizerException, ""); +} + +bool SyntacticallyEqual::statementEqual(StackAssignment const&, StackAssignment const&) +{ + assertThrow(false, OptimizerException, ""); +} + +bool SyntacticallyEqual::statementEqual(Block const& _lhs, Block const& _rhs) +{ + return containerEqual(_lhs.statements, _rhs.statements, [this](Statement const& _lhsStmt, Statement const& _rhsStmt) -> bool { + return (*this)(_lhsStmt, _rhsStmt); + }); +} + +bool SyntacticallyEqual::visitDeclaration(TypedName const& _lhs, TypedName const& _rhs) +{ + if (_lhs.type != _rhs.type) + return false; + std::size_t id = m_idsUsed++; + m_identifiersLHS[_lhs.name] = id; + m_identifiersRHS[_rhs.name] = id; + return true; } diff --git a/libyul/optimiser/SyntacticalEquality.h b/libyul/optimiser/SyntacticalEquality.h index 63c51b4fe..d4630d957 100644 --- a/libyul/optimiser/SyntacticalEquality.h +++ b/libyul/optimiser/SyntacticalEquality.h @@ -21,27 +21,69 @@ #pragma once #include +#include -#include +#include +#include namespace yul { + /** * Component that can compare ASTs for equality on a syntactic basis. - * Ignores source locations but requires exact matches otherwise. + * Ignores source locations and allows for different variable names but requires exact matches otherwise. * - * TODO: Only implemented for Expressions for now. - * A future version might also recognize renamed variables and thus could be used to - * remove duplicate functions. + * Prerequisite: Disambiguator (unless only expressions are compared) */ -class SyntacticalEqualityChecker +class SyntacticallyEqual { public: - static bool equal(Expression const& _e1, Expression const& _e2); + bool operator()(Expression const& _lhs, Expression const& _rhs); + bool operator()(Statement const& _lhs, Statement const& _rhs); -protected: - static bool equalVector(std::vector const& _e1, std::vector const& _e2); + bool expressionEqual(FunctionalInstruction const& _lhs, FunctionalInstruction const& _rhs); + bool expressionEqual(FunctionCall const& _lhs, FunctionCall const& _rhs); + bool expressionEqual(Identifier const& _lhs, Identifier const& _rhs); + bool expressionEqual(Literal const& _lhs, Literal const& _rhs); + + bool statementEqual(ExpressionStatement const& _lhs, ExpressionStatement const& _rhs); + bool statementEqual(Assignment const& _lhs, Assignment const& _rhs); + bool statementEqual(VariableDeclaration const& _lhs, VariableDeclaration const& _rhs); + bool statementEqual(FunctionDefinition const& _lhs, FunctionDefinition const& _rhs); + bool statementEqual(If const& _lhs, If const& _rhs); + bool statementEqual(Switch const& _lhs, Switch const& _rhs); + bool switchCaseEqual(Case const& _lhs, Case const& _rhs); + bool statementEqual(ForLoop const& _lhs, ForLoop const& _rhs); + bool statementEqual(Block const& _lhs, Block const& _rhs); +private: + bool statementEqual(Instruction const& _lhs, Instruction const& _rhs); + bool statementEqual(Label const& _lhs, Label const& _rhs); + bool statementEqual(StackAssignment const& _lhs, StackAssignment const& _rhs); + + bool visitDeclaration(TypedName const& _lhs, TypedName const& _rhs); + + template + bool expressionEqual(U const&, V const&, std::enable_if_t::value>* = nullptr) + { + return false; + } + + template + bool statementEqual(U const&, V const&, std::enable_if_t::value>* = nullptr) + { + return false; + } + + template + bool compareSharedPtr(std::shared_ptr const& _lhs, std::shared_ptr const& _rhs) + { + return (_lhs == _rhs) || (_lhs && _rhs && (this->*CompareMember)(*_lhs, *_rhs)); + } + + std::size_t m_idsUsed = 0; + std::map m_identifiersLHS; + std::map m_identifiersRHS; }; } diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 0e3203cbe..59cde4fe7 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -217,6 +218,11 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con disambiguate(); StructuralSimplifier{*m_dialect}(*m_ast); } + else if (m_optimizerStep == "equivalentFunctionCombiner") + { + disambiguate(); + EquivalentFunctionCombiner::run(*m_ast); + } else if (m_optimizerStep == "fullSuite") OptimiserSuite::run(*m_dialect, *m_ast, *m_analysisInfo); else diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul new file mode 100644 index 000000000..380f9f035 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul @@ -0,0 +1,114 @@ +{ + pop(f(1,2,3)) + pop(g(4,5,6)) + pop(h(7,8,9)) + function f(f1, f2, f3) -> rf + { + switch f1 + case 0 { + if f2 + { + rf := f3 + } + if not(f2) + { + rf := f1 + } + } + default { + rf := 3 + } + } + function g(g1, g2, g3) -> rg + { + switch g1 + case 0 { + if g2 + { + rg := g3 + } + if not(g2) + { + rg := g1 + } + } + default { + rg := 3 + } + } + function h(h1, h2, h3) -> rh + { + switch h1 + case 1 { + if h2 + { + rh := h3 + } + if not(h2) + { + rh := h1 + } + } + default { + rh := 3 + } + } +} +// ---- +// equivalentFunctionCombiner +// { +// pop(f(1, 2, 3)) +// pop(f(4, 5, 6)) +// pop(h(7, 8, 9)) +// function f(f1, f2, f3) -> rf +// { +// switch f1 +// case 0 { +// if f2 +// { +// rf := f3 +// } +// if not(f2) +// { +// rf := f1 +// } +// } +// default { +// rf := 3 +// } +// } +// function g(g1, g2, g3) -> rg +// { +// switch g1 +// case 0 { +// if g2 +// { +// rg := g3 +// } +// if not(g2) +// { +// rg := g1 +// } +// } +// default { +// rg := 3 +// } +// } +// function h(h1, h2, h3) -> rh +// { +// switch h1 +// case 1 { +// if h2 +// { +// rh := h3 +// } +// if not(h2) +// { +// rh := h1 +// } +// } +// default { +// rh := 3 +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul new file mode 100644 index 000000000..2d5b3ef8f --- /dev/null +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul @@ -0,0 +1,20 @@ +{ + f() + g() + function f() { mstore(1, mload(0)) } + function g() { mstore(1, mload(0)) } +} +// ---- +// equivalentFunctionCombiner +// { +// f() +// f() +// function f() +// { +// mstore(1, mload(0)) +// } +// function g() +// { +// mstore(1, mload(0)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul new file mode 100644 index 000000000..d38a3d2ea --- /dev/null +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul @@ -0,0 +1,22 @@ +{ + pop(f()) + pop(g()) + function f() -> b { let a := mload(0) b := a } + function g() -> a { let b := mload(0) a := b } +} +// ---- +// equivalentFunctionCombiner +// { +// pop(f()) +// pop(f()) +// function f() -> b +// { +// let a := mload(0) +// b := a +// } +// function g() -> a_1 +// { +// let b_2 := mload(0) +// a_1 := b_2 +// } +// } diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul new file mode 100644 index 000000000..4f3cad360 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul @@ -0,0 +1,32 @@ +{ + f(0) + g(1) + function f(x) { switch x case 0 { mstore(0, 42) } case 1 { mstore(1, 42) } } + function g(x) { switch x case 1 { mstore(1, 42) } case 0 { mstore(0, 42) } } +} +// ---- +// equivalentFunctionCombiner +// { +// f(0) +// f(1) +// function f(x) +// { +// switch x +// case 0 { +// mstore(0, 42) +// } +// case 1 { +// mstore(1, 42) +// } +// } +// function g(x_1) +// { +// switch x_1 +// case 1 { +// mstore(1, 42) +// } +// case 0 { +// mstore(0, 42) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul index bb60b1fa7..887399b64 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul @@ -1073,12 +1073,12 @@ // fullSuite // { // let _2 := mload(1) -// let _1042 := mload(0) -// if slt(sub(_2, _1042), 64) +// let _172 := mload(0) +// if slt(sub(_2, _172), 64) // { // revert(0, 0) // } -// sstore(0, and(calldataload(_1042), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) +// sstore(0, and(calldataload(_172), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) // let x0, x1, x2, x3, x4 := abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949(mload(7), mload(8)) // sstore(x1, x0) // sstore(x3, x2) @@ -1093,40 +1093,40 @@ // value0_57 := and(calldataload(add(headStart_55, value4)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) // value1_58 := calldataload(add(headStart_55, 32)) // let offset_62 := calldataload(add(headStart_55, 64)) -// let _1090 := 0xffffffffffffffff -// if gt(offset_62, _1090) +// let _220 := 0xffffffffffffffff +// if gt(offset_62, _220) // { // revert(value4, value4) // } -// let _1092 := add(headStart_55, offset_62) -// if iszero(slt(add(_1092, 0x1f), dataEnd_56)) +// let _222 := add(headStart_55, offset_62) +// if iszero(slt(add(_222, 0x1f), dataEnd_56)) // { // revert(value4, value4) // } -// let abi_decode_length_15_689 := calldataload(_1092) -// if gt(abi_decode_length_15_689, _1090) +// let abi_decode_length_15_116 := calldataload(_222) +// if gt(abi_decode_length_15_116, _220) // { // revert(value4, value4) // } -// if gt(add(add(_1092, abi_decode_length_15_689), 0x20), dataEnd_56) +// if gt(add(add(_222, abi_decode_length_15_116), 32), dataEnd_56) // { // revert(value4, value4) // } -// value2_59 := add(_1092, 0x20) -// value3 := abi_decode_length_15_689 -// let _1095 := calldataload(add(headStart_55, 96)) -// if iszero(lt(_1095, 3)) +// value2_59 := add(_222, 32) +// value3 := abi_decode_length_15_116 +// let _225 := calldataload(add(headStart_55, 96)) +// if iszero(lt(_225, 3)) // { // revert(value4, value4) // } -// value4 := _1095 +// value4 := _225 // } // function abi_encode_tuple_t_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_$1949_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256__to_t_bytes32_t_address_t_uint256_t_bytes32_t_uint8_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256_(headStart_252, value10_253, value9_254, value8_255, value7_256, value6_257, value5_258, value4_259, value3_260, value2_261, value1_262, value0_263) -> tail_264 // { // tail_264 := add(headStart_252, 352) // mstore(headStart_252, value0_263) -// let _1307 := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -// mstore(add(headStart_252, 32), and(value1_262, _1307)) +// let _439 := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +// mstore(add(headStart_252, 32), and(value1_262, _439)) // mstore(add(headStart_252, 64), value2_261) // mstore(add(headStart_252, 96), value3_260) // if iszero(lt(value4_259, 3)) @@ -1137,8 +1137,8 @@ // mstore(add(headStart_252, 160), value5_258) // mstore(add(headStart_252, 192), value6_257) // mstore(add(headStart_252, 224), value7_256) -// mstore(add(headStart_252, 256), and(value8_255, _1307)) -// mstore(add(headStart_252, 288), and(value9_254, _1307)) +// mstore(add(headStart_252, 256), and(value8_255, _439)) +// mstore(add(headStart_252, 288), and(value9_254, _439)) // mstore(add(headStart_252, 320), value10_253) // } // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index c909d39e2..a8cac6c63 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -458,13 +458,14 @@ // ---- // fullSuite // { +// let _1 := 0x20 // let _2 := 0 -// let _485 := mload(_2) -// let abi_encode_pos := 0x20 -// let abi_encode_length_68 := mload(_485) -// mstore(0x20, abi_encode_length_68) +// let _268 := mload(_2) +// let abi_encode_pos := _1 +// let abi_encode_length_68 := mload(_268) +// mstore(_1, abi_encode_length_68) // abi_encode_pos := 64 -// let abi_encode_srcPtr := add(_485, 0x20) +// let abi_encode_srcPtr := add(_268, _1) // let abi_encode_i_69 := _2 // for { // } @@ -473,31 +474,45 @@ // abi_encode_i_69 := add(abi_encode_i_69, 1) // } // { -// abi_encode_t_array$_t_contract$_C_$55_$3_memory_to_t_array$_t_address_$3_memory_ptr(mload(abi_encode_srcPtr), abi_encode_pos) -// abi_encode_srcPtr := add(abi_encode_srcPtr, 0x20) +// let _668 := mload(abi_encode_srcPtr) +// let abi_encode_pos_71_760 := abi_encode_pos +// let abi_encode_srcPtr_73_762 := _668 +// let abi_encode_i_74_763 := _2 +// for { +// } +// lt(abi_encode_i_74_763, 0x3) +// { +// abi_encode_i_74_763 := add(abi_encode_i_74_763, 1) +// } +// { +// mstore(abi_encode_pos_71_760, and(mload(abi_encode_srcPtr_73_762), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) +// abi_encode_srcPtr_73_762 := add(abi_encode_srcPtr_73_762, _1) +// abi_encode_pos_71_760 := add(abi_encode_pos_71_760, _1) +// } +// abi_encode_srcPtr := add(abi_encode_srcPtr, _1) // abi_encode_pos := add(abi_encode_pos, 0x60) // } -// let _487 := mload(0x40) -// let _488 := mload(0x20) -// if slt(sub(_487, _488), 128) +// let _270 := mload(64) +// let _271 := mload(_1) +// if slt(sub(_270, _271), 128) // { // revert(_2, _2) // } -// let abi_decode_offset_64 := calldataload(add(_488, 64)) -// let abi_decode__165 := 0xffffffffffffffff -// if gt(abi_decode_offset_64, abi_decode__165) +// let abi_decode_offset_64 := calldataload(add(_271, 64)) +// let abi_decode__74 := 0xffffffffffffffff +// if gt(abi_decode_offset_64, abi_decode__74) // { // revert(_2, _2) // } -// let abi_decode_value2_587 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_488, abi_decode_offset_64), _487) -// let abi_decode_offset_65 := calldataload(add(_488, 96)) -// if gt(abi_decode_offset_65, abi_decode__165) +// let abi_decode_value2_367 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_271, abi_decode_offset_64), _270) +// let abi_decode_offset_65 := calldataload(add(_271, 96)) +// if gt(abi_decode_offset_65, abi_decode__74) // { // revert(_2, _2) // } -// let abi_decode_value3_588 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_488, abi_decode_offset_65), _487) -// sstore(calldataload(_488), calldataload(add(_488, 32))) -// sstore(abi_decode_value2_587, abi_decode_value3_588) +// let abi_decode_value3_368 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_271, abi_decode_offset_65), _270) +// sstore(calldataload(_271), calldataload(add(_271, _1))) +// sstore(abi_decode_value2_367, abi_decode_value3_368) // sstore(_2, abi_encode_pos) // function abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(offset_3, end_4) -> array_5 // { @@ -506,23 +521,19 @@ // revert(0, 0) // } // let length_6 := calldataload(offset_3) -// if gt(length_6, 0xffffffffffffffff) +// let array_5_115 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_6)) +// array_5 := array_5_115 +// let dst_7 := array_5_115 +// mstore(array_5_115, length_6) +// let _16 := 0x20 +// dst_7 := add(array_5_115, _16) +// let src_8 := add(offset_3, _16) +// if gt(add(add(offset_3, mul(length_6, 0x40)), _16), end_4) // { // revert(0, 0) // } -// let array_allo__217 := 0x20 -// let array_5_254 := allocateMemory(add(mul(length_6, array_allo__217), array_allo__217)) -// array_5 := array_5_254 -// let dst_7 := array_5_254 -// mstore(array_5_254, length_6) -// dst_7 := add(array_5_254, array_allo__217) -// let src_8 := add(offset_3, array_allo__217) -// if gt(add(add(offset_3, mul(length_6, 0x40)), array_allo__217), end_4) -// { -// revert(0, 0) -// } -// let i_9_566 := 0 -// let i_9 := i_9_566 +// let i_9_346 := 0 +// let i_9 := i_9_346 // for { // } // lt(i_9, length_6) @@ -532,16 +543,17 @@ // { // if iszero(slt(add(src_8, 0x1f), end_4)) // { -// revert(i_9_566, i_9_566) +// revert(i_9_346, i_9_346) // } -// let abi_decode_array_13_263 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2)) -// let abi_decode_dst_15 := abi_decode_array_13_263 +// let abi_decode_array_13_124 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2)) +// let abi_decode_dst_15 := abi_decode_array_13_124 // let abi_decode_src_16 := src_8 -// if gt(add(src_8, 64), end_4) +// let abi_decode__289 := add(src_8, 0x40) +// if gt(abi_decode__289, end_4) // { -// revert(i_9_566, i_9_566) +// revert(i_9_346, i_9_346) // } -// let abi_decode_i_17 := i_9_566 +// let abi_decode_i_17 := i_9_346 // for { // } // lt(abi_decode_i_17, 0x2) @@ -550,12 +562,12 @@ // } // { // mstore(abi_decode_dst_15, calldataload(abi_decode_src_16)) -// abi_decode_dst_15 := add(abi_decode_dst_15, array_allo__217) -// abi_decode_src_16 := add(abi_decode_src_16, array_allo__217) +// abi_decode_dst_15 := add(abi_decode_dst_15, _16) +// abi_decode_src_16 := add(abi_decode_src_16, _16) // } -// mstore(dst_7, abi_decode_array_13_263) -// dst_7 := add(dst_7, array_allo__217) -// src_8 := add(src_8, 0x40) +// mstore(dst_7, abi_decode_array_13_124) +// dst_7 := add(dst_7, _16) +// src_8 := abi_decode__289 // } // } // function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset_27, end_28) -> array_29 @@ -565,19 +577,14 @@ // revert(0, 0) // } // let length_30 := calldataload(offset_27) -// if gt(length_30, 0xffffffffffffffff) -// { -// revert(0, 0) -// } -// let array_allo__234 := 0x20 -// let array_allo__560 := mul(length_30, array_allo__234) -// let array_29_279 := allocateMemory(add(array_allo__560, array_allo__234)) -// array_29 := array_29_279 -// let dst_31 := array_29_279 -// mstore(array_29_279, length_30) -// dst_31 := add(array_29_279, array_allo__234) -// let src_32 := add(offset_27, array_allo__234) -// if gt(add(add(offset_27, array_allo__560), array_allo__234), end_28) +// let array_29_131 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_30)) +// array_29 := array_29_131 +// let dst_31 := array_29_131 +// mstore(array_29_131, length_30) +// let _52 := 0x20 +// dst_31 := add(array_29_131, _52) +// let src_32 := add(offset_27, _52) +// if gt(add(add(offset_27, mul(length_30, _52)), _52), end_28) // { // revert(0, 0) // } @@ -590,37 +597,29 @@ // } // { // mstore(dst_31, calldataload(src_32)) -// dst_31 := add(dst_31, array_allo__234) -// src_32 := add(src_32, array_allo__234) -// } -// } -// function abi_encode_t_array$_t_contract$_C_$55_$3_memory_to_t_array$_t_address_$3_memory_ptr(value_70, pos_71) -// { -// let srcPtr_73 := value_70 -// let i_74 := 0 -// for { -// } -// lt(i_74, 0x3) -// { -// i_74 := add(i_74, 1) -// } -// { -// mstore(pos_71, and(mload(srcPtr_73), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) -// srcPtr_73 := add(srcPtr_73, 0x20) -// pos_71 := add(pos_71, 0x20) +// dst_31 := add(dst_31, _52) +// src_32 := add(src_32, _52) // } // } // function allocateMemory(size) -> memPtr // { -// let memPtr_315 := mload(64) -// memPtr := memPtr_315 -// let newFreePtr := add(memPtr_315, size) -// if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr_315)) +// let memPtr_157 := mload(64) +// memPtr := memPtr_157 +// let newFreePtr := add(memPtr_157, size) +// if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr_157)) // { // revert(0, 0) // } // mstore(64, newFreePtr) // } +// function array_allocation_size_t_array$_t_address_$dyn_memory(length_90) -> size_91 +// { +// if gt(length_90, 0xffffffffffffffff) +// { +// revert(0, 0) +// } +// size_91 := add(mul(length_90, 0x20), 0x20) +// } // function array_allocation_size_t_array$_t_uint256_$2_memory(length_94) -> size_95 // { // if gt(length_94, 0xffffffffffffffff) diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul index 4fcdaaf18..fc09b5d9e 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -264,10 +264,10 @@ // } // { // let validateJo__34 := 0x20 -// let validateJo__376 := add(validateJo__10, mul(validateJo_i, 0xc0)) -// let validateJo_noteIndex := add(validateJo__376, 36) +// let validateJo__373 := add(validateJo__10, mul(validateJo_i, 0xc0)) +// let validateJo_noteIndex := add(validateJo__373, 0x24) // let validateJo_k := validateJo_i_290 -// let validateJo_a_292 := calldataload(add(validateJo__376, 68)) +// let validateJo_a_292 := calldataload(add(validateJo__373, 0x44)) // let validateJo_a := validateJo_a_292 // let validateJo_c := validateJo_challenge // let validateJo__39 := add(validateJo_i, 0x01) @@ -286,18 +286,18 @@ // switch gt(validateJo__39, validateJo_m) // case 1 { // validateJo_kn := addmod(validateJo_kn, sub(validateJo_gen_order, validateJo_k), validateJo_gen_order) -// let validateJo_x := mod(mload(0x00), validateJo_gen_order) +// let validateJo_x := mod(mload(validateJo_i_290), validateJo_gen_order) // validateJo_k := mulmod(validateJo_k, validateJo_x, validateJo_gen_order) // validateJo_a := mulmod(validateJo_a_292, validateJo_x, validateJo_gen_order) // validateJo_c := mulmod(validateJo_challenge, validateJo_x, validateJo_gen_order) -// mstore(0x00, keccak256(0x00, validateJo__34)) +// mstore(validateJo_i_290, keccak256(validateJo_i_290, validateJo__34)) // } // case 0 { // validateJo_kn := addmod(validateJo_kn, validateJo_k, validateJo_gen_order) // } // let validateJo__52 := 0x40 -// calldatacopy(0xe0, add(validateJo__376, 164), validateJo__52) -// calldatacopy(validateJo__34, add(validateJo__376, 100), validateJo__52) +// calldatacopy(0xe0, add(validateJo__373, 164), validateJo__52) +// calldatacopy(validateJo__34, add(validateJo__373, 100), validateJo__52) // let validateJo__61 := 0x120 // mstore(validateJo__61, sub(validateJo_gen_order, validateJo_c)) // let validateJo__62 := 0x60 @@ -329,8 +329,8 @@ // } // if iszero(validateJo_result) // { -// mstore(0x00, 400) -// revert(0x00, validateJo__34) +// mstore(validateJo_i_290, 400) +// revert(validateJo_i_290, validateJo__34) // } // validateJo_b := add(validateJo_b, validateJo__52) // } @@ -340,13 +340,13 @@ // } // if iszero(eq(mod(keccak256(validateJo__24, add(validateJo_b, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd60)), validateJo_gen_order), validateJo_challenge)) // { -// mstore(0x00, 404) -// revert(0x00, 0x20) +// mstore(validateJo_i_290, 404) +// revert(validateJo_i_290, 0x20) // } -// mstore(0x00, 0x01) -// return(0x00, 0x20) -// mstore(0x00, 404) -// revert(0x00, 0x20) +// mstore(validateJo_i_290, 0x01) +// return(validateJo_i_290, 0x20) +// mstore(validateJo_i_290, 404) +// revert(validateJo_i_290, 0x20) // function validatePairing(t2) // { // let t2_x_1 := calldataload(t2) @@ -379,8 +379,8 @@ // let success := call(gas(), 8, 0, _165, _216, _165, _165) // if or(iszero(success), iszero(mload(_165))) // { -// mstore(0x00, 400) -// revert(0x00, _165) +// mstore(0, 400) +// revert(0, _165) // } // } // function validateCommitment(note, k_1, a_2) @@ -409,6 +409,6 @@ // { // calldatacopy(add(0x300, mul(i_7, 0x80)), add(add(notes_5, mul(i_7, 0xc0)), 0x60), 0x80) // } -// mstore(0x00, keccak256(0x300, mul(n_6, 0x80))) +// mstore(0, keccak256(0x300, mul(n_6, 0x80))) // } // } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index ac21fd91f..efd1ba05f 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -128,7 +129,7 @@ public: cout << "(q)quit/(f)flatten/(c)se/initialize var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; cout << " (e)xpr inline/(i)nline/(s)implify/(u)nusedprune/ss(a) transform/" << endl; cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-pre-rewriter/" << endl; - cout << " s(t)ructural simplifier? " << endl; + cout << " s(t)ructural simplifier/equi(v)alent function combiner? " << endl; cout.flush(); int option = readStandardInputChar(); cout << ' ' << char(option) << endl; @@ -184,6 +185,9 @@ public: case 'm': Rematerialiser::run(*m_dialect, *m_ast); break; + case 'v': + EquivalentFunctionCombiner::run(*m_ast); + break; default: cout << "Unknown option." << endl; }