From 9a4bec7e474a310c7f93ff3b84928e0e9ba9cce6 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Mon, 15 Oct 2018 11:52:35 +0200 Subject: Renaming libjulia to libyul --- libyul/optimiser/SimplificationRules.cpp | 189 +++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 libyul/optimiser/SimplificationRules.cpp (limited to 'libyul/optimiser/SimplificationRules.cpp') diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp new file mode 100644 index 00000000..ca3f2984 --- /dev/null +++ b/libyul/optimiser/SimplificationRules.cpp @@ -0,0 +1,189 @@ +/* + 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 . +*/ +/** + * Module for applying replacement rules against Expressions. + */ + +#include + +#include +#include +#include +#include + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::julia; + + +SimplificationRule const* SimplificationRules::findFirstMatch(Expression const& _expr) +{ + if (_expr.type() != typeid(FunctionalInstruction)) + return nullptr; + + static SimplificationRules rules; + assertThrow(rules.isInitialized(), OptimizerException, "Rule list not properly initialized."); + + FunctionalInstruction const& instruction = boost::get(_expr); + for (auto const& rule: rules.m_rules[byte(instruction.instruction)]) + { + rules.resetMatchGroups(); + if (rule.pattern.matches(_expr)) + return &rule; + } + return nullptr; +} + +bool SimplificationRules::isInitialized() const +{ + return !m_rules[byte(solidity::Instruction::ADD)].empty(); +} + +void SimplificationRules::addRules(vector> const& _rules) +{ + for (auto const& r: _rules) + addRule(r); +} + +void SimplificationRules::addRule(SimplificationRule const& _rule) +{ + m_rules[byte(_rule.pattern.instruction())].push_back(_rule); +} + +SimplificationRules::SimplificationRules() +{ + // Multiple occurrences of one of these inside one rule must match the same equivalence class. + // Constants. + Pattern A(PatternKind::Constant); + Pattern B(PatternKind::Constant); + Pattern C(PatternKind::Constant); + // Anything. + Pattern X; + Pattern Y; + A.setMatchGroup(1, m_matchGroups); + B.setMatchGroup(2, m_matchGroups); + C.setMatchGroup(3, m_matchGroups); + X.setMatchGroup(4, m_matchGroups); + Y.setMatchGroup(5, m_matchGroups); + + addRules(simplificationRuleList(A, B, C, X, Y)); + assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized."); +} + +Pattern::Pattern(solidity::Instruction _instruction, vector const& _arguments): + m_kind(PatternKind::Operation), + m_instruction(_instruction), + m_arguments(_arguments) +{ +} + +void Pattern::setMatchGroup(unsigned _group, map& _matchGroups) +{ + m_matchGroup = _group; + m_matchGroups = &_matchGroups; +} + +bool Pattern::matches(Expression const& _expr) const +{ + if (m_kind == PatternKind::Constant) + { + if (_expr.type() != typeid(Literal)) + return false; + Literal const& literal = boost::get(_expr); + if (literal.kind != assembly::LiteralKind::Number) + return false; + if (m_data && *m_data != u256(literal.value)) + return false; + assertThrow(m_arguments.empty(), OptimizerException, ""); + } + else if (m_kind == PatternKind::Operation) + { + if (_expr.type() != typeid(FunctionalInstruction)) + return false; + FunctionalInstruction const& instr = boost::get(_expr); + if (m_instruction != instr.instruction) + return false; + assertThrow(m_arguments.size() == instr.arguments.size(), OptimizerException, ""); + for (size_t i = 0; i < m_arguments.size(); ++i) + if (!m_arguments[i].matches(instr.arguments.at(i))) + return false; + } + else + { + assertThrow(m_arguments.empty(), OptimizerException, ""); + } + // We support matching multiple expressions that require the same value + // based on identical ASTs, which have to be movable. + if (m_matchGroup) + { + if (m_matchGroups->count(m_matchGroup)) + { + Expression const* firstMatch = (*m_matchGroups)[m_matchGroup]; + assertThrow(firstMatch, OptimizerException, "Match set but to null."); + return + SyntacticalEqualityChecker::equal(*firstMatch, _expr) && + MovableChecker(_expr).movable(); + } + else + (*m_matchGroups)[m_matchGroup] = &_expr; + } + return true; +} + +solidity::Instruction Pattern::instruction() const +{ + assertThrow(m_kind == PatternKind::Operation, OptimizerException, ""); + return m_instruction; +} + +Expression Pattern::toExpression(SourceLocation const& _location) const +{ + if (matchGroup()) + return ASTCopier().translate(matchGroupValue()); + if (m_kind == PatternKind::Constant) + { + assertThrow(m_data, OptimizerException, "No match group and no constant value given."); + return Literal{_location, assembly::LiteralKind::Number, formatNumber(*m_data), ""}; + } + else if (m_kind == PatternKind::Operation) + { + vector arguments; + for (auto const& arg: m_arguments) + arguments.emplace_back(arg.toExpression(_location)); + return FunctionalInstruction{_location, m_instruction, std::move(arguments)}; + } + assertThrow(false, OptimizerException, "Pattern of kind 'any', but no match group."); +} + +u256 Pattern::d() const +{ + Literal const& literal = boost::get(matchGroupValue()); + assertThrow(literal.kind == assembly::LiteralKind::Number, OptimizerException, ""); + return u256(literal.value); +} + +Expression const& Pattern::matchGroupValue() const +{ + assertThrow(m_matchGroup > 0, OptimizerException, ""); + assertThrow(!!m_matchGroups, OptimizerException, ""); + assertThrow((*m_matchGroups)[m_matchGroup], OptimizerException, ""); + return *(*m_matchGroups)[m_matchGroup]; +} -- cgit v1.2.3 From 1304361b9c48438d5c55903492b5f11c3dac73e5 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Mon, 15 Oct 2018 11:58:51 +0200 Subject: Renaming namespace dev::julia to dev::yul. --- libyul/optimiser/SimplificationRules.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libyul/optimiser/SimplificationRules.cpp') diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index ca3f2984..762473e5 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -31,7 +31,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; SimplificationRule const* SimplificationRules::findFirstMatch(Expression const& _expr) -- cgit v1.2.3 From a320eec7d38f98a1fbb9f6267e5e30cfae5c59cf Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 2 Oct 2018 18:07:50 +0200 Subject: New simplifier via broken expressions. --- libyul/optimiser/SimplificationRules.cpp | 56 +++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 12 deletions(-) (limited to 'libyul/optimiser/SimplificationRules.cpp') diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 762473e5..4d0468c7 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -34,7 +34,10 @@ using namespace dev; using namespace dev::yul; -SimplificationRule const* SimplificationRules::findFirstMatch(Expression const& _expr) +SimplificationRule const* SimplificationRules::findFirstMatch( + Expression const& _expr, + map const& _ssaValues +) { if (_expr.type() != typeid(FunctionalInstruction)) return nullptr; @@ -46,7 +49,7 @@ SimplificationRule const* SimplificationRules::findFirstMatch(Expressio for (auto const& rule: rules.m_rules[byte(instruction.instruction)]) { rules.resetMatchGroups(); - if (rule.pattern.matches(_expr)) + if (rule.pattern.matches(_expr, _ssaValues)) return &rule; } return nullptr; @@ -101,13 +104,25 @@ void Pattern::setMatchGroup(unsigned _group, map& _ m_matchGroups = &_matchGroups; } -bool Pattern::matches(Expression const& _expr) const +bool Pattern::matches(Expression const& _expr, map const& _ssaValues) const { + Expression const* expr = &_expr; + + // Resolve the variable if possible. + // Do not do it for "Any" because we can check identity better for variables. + if (m_kind != PatternKind::Any && _expr.type() == typeid(Identifier)) + { + string const& varName = boost::get(_expr).name; + if (_ssaValues.count(varName)) + expr = _ssaValues.at(varName); + } + assertThrow(expr, OptimizerException, ""); + if (m_kind == PatternKind::Constant) { - if (_expr.type() != typeid(Literal)) + if (expr->type() != typeid(Literal)) return false; - Literal const& literal = boost::get(_expr); + Literal const& literal = boost::get(*expr); if (literal.kind != assembly::LiteralKind::Number) return false; if (m_data && *m_data != u256(literal.value)) @@ -116,34 +131,51 @@ bool Pattern::matches(Expression const& _expr) const } else if (m_kind == PatternKind::Operation) { - if (_expr.type() != typeid(FunctionalInstruction)) + if (expr->type() != typeid(FunctionalInstruction)) return false; - FunctionalInstruction const& instr = boost::get(_expr); + FunctionalInstruction const& instr = boost::get(*expr); if (m_instruction != instr.instruction) return false; assertThrow(m_arguments.size() == instr.arguments.size(), OptimizerException, ""); for (size_t i = 0; i < m_arguments.size(); ++i) - if (!m_arguments[i].matches(instr.arguments.at(i))) + if (!m_arguments[i].matches(instr.arguments.at(i), _ssaValues)) return false; } else { - assertThrow(m_arguments.empty(), OptimizerException, ""); + assertThrow(m_arguments.empty(), OptimizerException, "\"Any\" should not have arguments."); } - // We support matching multiple expressions that require the same value - // based on identical ASTs, which have to be movable. + if (m_matchGroup) { + // We support matching multiple expressions that require the same value + // based on identical ASTs, which have to be movable. + + // TODO: add tests: + // - { let x := mload(0) let y := and(x, x) } + // - { let x := 4 let y := and(x, y) } + + // This code uses `_expr` again for "Any", because we want the comparison to be done + // on the variables and not their values. + // The assumption is that CSE or local value numbering has been done prior to this step. + if (m_matchGroups->count(m_matchGroup)) { + assertThrow(m_kind == PatternKind::Any, OptimizerException, "Match group repetition for non-any."); Expression const* firstMatch = (*m_matchGroups)[m_matchGroup]; assertThrow(firstMatch, OptimizerException, "Match set but to null."); return SyntacticalEqualityChecker::equal(*firstMatch, _expr) && MovableChecker(_expr).movable(); } - else + else if (m_kind == PatternKind::Any) (*m_matchGroups)[m_matchGroup] = &_expr; + else + { + assertThrow(m_kind == PatternKind::Constant, OptimizerException, "Match group set for operation."); + // We do not use _expr here, because we want the actual number. + (*m_matchGroups)[m_matchGroup] = expr; + } } return true; } -- cgit v1.2.3 From 19be6cd818e4bb1a49325d8bfea7f7727d85c933 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 16 Oct 2018 17:58:17 +0200 Subject: Some well-formedness checks for the Yul AST. --- libyul/optimiser/SimplificationRules.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'libyul/optimiser/SimplificationRules.cpp') diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 4d0468c7..aca943b0 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -209,6 +209,7 @@ u256 Pattern::d() const { Literal const& literal = boost::get(matchGroupValue()); assertThrow(literal.kind == assembly::LiteralKind::Number, OptimizerException, ""); + assertThrow(isValidDecimal(literal.value) || isValidHex(literal.value), OptimizerException, ""); return u256(literal.value); } -- cgit v1.2.3 From ab0de38f16a9eff13ee5a32a3408b890d87941f6 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Wed, 7 Nov 2018 12:04:46 +0100 Subject: Eliminate `byte`-typedef and use `uint8_t` in all their places instead. This change is made to (easily) be forward compatible with future C++ standards, in order to allow compiling the code with newer standards at some point in the future. * Removed the `using byte = uint8_t;` line from Common.h * Mechanically change all uses of `byte` to `uint8_t`. Tested with GCC 7.3 in C++11/14/17 modes :-) --- libyul/optimiser/SimplificationRules.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'libyul/optimiser/SimplificationRules.cpp') diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index aca943b0..8e94fe4d 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -46,7 +46,7 @@ SimplificationRule const* SimplificationRules::findFirstMatch( assertThrow(rules.isInitialized(), OptimizerException, "Rule list not properly initialized."); FunctionalInstruction const& instruction = boost::get(_expr); - for (auto const& rule: rules.m_rules[byte(instruction.instruction)]) + for (auto const& rule: rules.m_rules[uint8_t(instruction.instruction)]) { rules.resetMatchGroups(); if (rule.pattern.matches(_expr, _ssaValues)) @@ -57,7 +57,7 @@ SimplificationRule const* SimplificationRules::findFirstMatch( bool SimplificationRules::isInitialized() const { - return !m_rules[byte(solidity::Instruction::ADD)].empty(); + return !m_rules[uint8_t(solidity::Instruction::ADD)].empty(); } void SimplificationRules::addRules(vector> const& _rules) @@ -68,7 +68,7 @@ void SimplificationRules::addRules(vector> const& _r void SimplificationRules::addRule(SimplificationRule const& _rule) { - m_rules[byte(_rule.pattern.instruction())].push_back(_rule); + m_rules[uint8_t(_rule.pattern.instruction())].push_back(_rule); } SimplificationRules::SimplificationRules() -- cgit v1.2.3 From 674e17c2a895eff6729357d8c10db709ac368b79 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 29 Oct 2018 15:12:02 +0100 Subject: Performance: Replace string by special single-copy YulString class. --- libyul/optimiser/SimplificationRules.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'libyul/optimiser/SimplificationRules.cpp') diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 8e94fe4d..5721042f 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -36,7 +36,7 @@ using namespace dev::yul; SimplificationRule const* SimplificationRules::findFirstMatch( Expression const& _expr, - map const& _ssaValues + map const& _ssaValues ) { if (_expr.type() != typeid(FunctionalInstruction)) @@ -104,7 +104,7 @@ void Pattern::setMatchGroup(unsigned _group, map& _ m_matchGroups = &_matchGroups; } -bool Pattern::matches(Expression const& _expr, map const& _ssaValues) const +bool Pattern::matches(Expression const& _expr, map const& _ssaValues) const { Expression const* expr = &_expr; @@ -112,7 +112,7 @@ bool Pattern::matches(Expression const& _expr, map co // Do not do it for "Any" because we can check identity better for variables. if (m_kind != PatternKind::Any && _expr.type() == typeid(Identifier)) { - string const& varName = boost::get(_expr).name; + YulString varName = boost::get(_expr).name; if (_ssaValues.count(varName)) expr = _ssaValues.at(varName); } @@ -125,7 +125,7 @@ bool Pattern::matches(Expression const& _expr, map co Literal const& literal = boost::get(*expr); if (literal.kind != assembly::LiteralKind::Number) return false; - if (m_data && *m_data != u256(literal.value)) + if (m_data && *m_data != u256(literal.value.str())) return false; assertThrow(m_arguments.empty(), OptimizerException, ""); } @@ -193,7 +193,7 @@ Expression Pattern::toExpression(SourceLocation const& _location) const if (m_kind == PatternKind::Constant) { assertThrow(m_data, OptimizerException, "No match group and no constant value given."); - return Literal{_location, assembly::LiteralKind::Number, formatNumber(*m_data), ""}; + return Literal{_location, assembly::LiteralKind::Number, YulString{formatNumber(*m_data)}, {}}; } else if (m_kind == PatternKind::Operation) { @@ -209,8 +209,8 @@ u256 Pattern::d() const { Literal const& literal = boost::get(matchGroupValue()); assertThrow(literal.kind == assembly::LiteralKind::Number, OptimizerException, ""); - assertThrow(isValidDecimal(literal.value) || isValidHex(literal.value), OptimizerException, ""); - return u256(literal.value); + assertThrow(isValidDecimal(literal.value.str()) || isValidHex(literal.value.str()), OptimizerException, ""); + return u256(literal.value.str()); } Expression const& Pattern::matchGroupValue() const -- cgit v1.2.3