mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Expression simplifier.
This commit is contained in:
parent
591813638e
commit
9eea3f29ba
@ -43,6 +43,11 @@ template <class S> S modWorkaround(S const& _a, S const& _b)
|
|||||||
return (S)(bigint(_a) % bigint(_b));
|
return (S)(bigint(_a) % bigint(_b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add a parameter that will cause rules with swapped arguments
|
||||||
|
// to be added explicitly. This is needed by the new optimizer, but not
|
||||||
|
// by the old. The new optimizer requires that the order of arbitrary
|
||||||
|
// expressions is not altered.
|
||||||
|
|
||||||
/// @returns a list of simplification rules given certain match placeholders.
|
/// @returns a list of simplification rules given certain match placeholders.
|
||||||
/// A, B and C should represent constants, X and Y arbitrary expressions.
|
/// A, B and C should represent constants, X and Y arbitrary expressions.
|
||||||
/// The third element in the tuple is a boolean flag that indicates whether
|
/// The third element in the tuple is a boolean flag that indicates whether
|
||||||
|
49
libjulia/optimiser/ExpressionSimplifier.cpp
Normal file
49
libjulia/optimiser/ExpressionSimplifier.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Optimiser component that uses the simplification rules to simplify expressions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libjulia/optimiser/ExpressionSimplifier.h>
|
||||||
|
|
||||||
|
#include <libjulia/optimiser/SimplificationRules.h>
|
||||||
|
#include <libjulia/optimiser/Semantics.h>
|
||||||
|
|
||||||
|
#include <libsolidity/inlineasm/AsmData.h>
|
||||||
|
|
||||||
|
#include <libsolidity/interface/Exceptions.h>
|
||||||
|
|
||||||
|
#include <libdevcore/CommonData.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
|
using namespace dev::julia;
|
||||||
|
using namespace dev::solidity;
|
||||||
|
|
||||||
|
|
||||||
|
void ExpressionSimplifier::visit(Expression& _expression)
|
||||||
|
{
|
||||||
|
ASTModifier::visit(_expression);
|
||||||
|
while (auto match = SimplificationRules::findFirstMatch(_expression))
|
||||||
|
{
|
||||||
|
// TODO: The check could actually be less strict than "movable".
|
||||||
|
// We only require "Does not cause side-effects".
|
||||||
|
if (std::get<2>(*match) && !MovableChecker(_expression).movable())
|
||||||
|
return;
|
||||||
|
_expression = std::get<1>(*match)().toExpression(locationOf(_expression));
|
||||||
|
}
|
||||||
|
}
|
45
libjulia/optimiser/ExpressionSimplifier.h
Normal file
45
libjulia/optimiser/ExpressionSimplifier.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Optimiser component that uses the simplification rules to simplify expressions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libjulia/ASTDataForward.h>
|
||||||
|
|
||||||
|
#include <libjulia/optimiser/ASTWalker.h>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace julia
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies simplification rules to all expressions.
|
||||||
|
*/
|
||||||
|
class ExpressionSimplifier: public ASTModifier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using ASTModifier::operator();
|
||||||
|
virtual void visit(Expression& _expression);
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
172
libjulia/optimiser/SimplificationRules.cpp
Normal file
172
libjulia/optimiser/SimplificationRules.cpp
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Module for applying replacement rules against Expressions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libjulia/optimiser/SimplificationRules.h>
|
||||||
|
|
||||||
|
#include <libjulia/optimiser/Utilities.h>
|
||||||
|
#include <libjulia/optimiser/ASTCopier.h>
|
||||||
|
|
||||||
|
#include <libsolidity/inlineasm/AsmData.h>
|
||||||
|
|
||||||
|
#include <libevmasm/RuleList.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
|
using namespace dev::julia;
|
||||||
|
|
||||||
|
|
||||||
|
tuple<Pattern, function<Pattern()>, bool> const* SimplificationRules::findFirstMatch(Expression const& _expr)
|
||||||
|
{
|
||||||
|
if (_expr.type() != typeid(FunctionalInstruction))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
static SimplificationRules rules;
|
||||||
|
|
||||||
|
FunctionalInstruction const& instruction = boost::get<FunctionalInstruction const&>(_expr);
|
||||||
|
for (auto const& rule: rules.m_rules[byte(instruction.instruction)])
|
||||||
|
{
|
||||||
|
rules.resetMatchGroups();
|
||||||
|
if (std::get<0>(rule).matches(_expr))
|
||||||
|
return &rule;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplificationRules::addRules(vector<tuple<Pattern, function<Pattern()>, bool>> const& _rules)
|
||||||
|
{
|
||||||
|
for (auto const& r: _rules)
|
||||||
|
addRule(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplificationRules::addRule(tuple<Pattern, function<Pattern()>, bool> const& _rule)
|
||||||
|
{
|
||||||
|
m_rules[byte(std::get<0>(_rule).instruction())].push_back(_rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
SimplificationRules::SimplificationRules()
|
||||||
|
{
|
||||||
|
// Multiple occurences 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern::Pattern(solidity::Instruction _instruction, vector<Pattern> const& _arguments):
|
||||||
|
m_kind(PatternKind::Operation),
|
||||||
|
m_instruction(_instruction),
|
||||||
|
m_arguments(_arguments)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pattern::setMatchGroup(unsigned _group, map<unsigned, Expression const*>& _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<Literal const&>(_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<FunctionalInstruction const&>(_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 do not support matching multiple expressions that require the same value.
|
||||||
|
if (m_matchGroup)
|
||||||
|
{
|
||||||
|
if (m_matchGroups->count(m_matchGroup))
|
||||||
|
return false;
|
||||||
|
(*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, m_data->str(), ""};
|
||||||
|
}
|
||||||
|
else if (m_kind == PatternKind::Operation)
|
||||||
|
{
|
||||||
|
vector<Expression> 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<Literal const&>(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];
|
||||||
|
}
|
116
libjulia/optimiser/SimplificationRules.h
Normal file
116
libjulia/optimiser/SimplificationRules.h
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Module for applying replacement rules against Expressions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libevmasm/ExpressionClasses.h>
|
||||||
|
|
||||||
|
#include <libjulia/ASTDataForward.h>
|
||||||
|
|
||||||
|
#include <libsolidity/inlineasm/AsmData.h>
|
||||||
|
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace julia
|
||||||
|
{
|
||||||
|
|
||||||
|
class Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container for all simplification rules.
|
||||||
|
*/
|
||||||
|
class SimplificationRules: public boost::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SimplificationRules();
|
||||||
|
|
||||||
|
/// @returns a pointer to the first matching pattern and sets the match
|
||||||
|
/// groups accordingly.
|
||||||
|
static std::tuple<Pattern, std::function<Pattern()>, bool> const* findFirstMatch(Expression const& _expr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addRules(std::vector<std::tuple<Pattern, std::function<Pattern()>, bool>> const& _rules);
|
||||||
|
void addRule(std::tuple<Pattern, std::function<Pattern()>, bool> const& _rule);
|
||||||
|
|
||||||
|
void resetMatchGroups() { m_matchGroups.clear(); }
|
||||||
|
|
||||||
|
std::map<unsigned, Expression const*> m_matchGroups;
|
||||||
|
std::vector<std::tuple<Pattern, std::function<Pattern()>, bool>> m_rules[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class PatternKind
|
||||||
|
{
|
||||||
|
Operation,
|
||||||
|
Constant,
|
||||||
|
Any
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pattern to match against an expression.
|
||||||
|
* Also stores matched expressions to retrieve them later, for constructing new expressions using
|
||||||
|
* ExpressionTemplate.
|
||||||
|
*/
|
||||||
|
class Pattern
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Matches any expression.
|
||||||
|
Pattern(PatternKind _kind = PatternKind::Any): m_kind(_kind) {}
|
||||||
|
// Matches a specific constant value.
|
||||||
|
Pattern(unsigned _value): Pattern(u256(_value)) {}
|
||||||
|
// Matches a specific constant value.
|
||||||
|
Pattern(u256 const& _value): m_kind(PatternKind::Constant), m_data(std::make_shared<u256>(_value)) {}
|
||||||
|
// Matches a given instruction with given arguments
|
||||||
|
Pattern(solidity::Instruction _instruction, std::vector<Pattern> const& _arguments = {});
|
||||||
|
/// Sets this pattern to be part of the match group with the identifier @a _group.
|
||||||
|
/// Inside one rule, all patterns in the same match group have to match expressions from the
|
||||||
|
/// same expression equivalence class.
|
||||||
|
void setMatchGroup(unsigned _group, std::map<unsigned, Expression const*>& _matchGroups);
|
||||||
|
unsigned matchGroup() const { return m_matchGroup; }
|
||||||
|
bool matches(Expression const& _expr) const;
|
||||||
|
|
||||||
|
std::vector<Pattern> arguments() const { return m_arguments; }
|
||||||
|
|
||||||
|
/// @returns the data of the matched expression if this pattern is part of a match group.
|
||||||
|
u256 d() const;
|
||||||
|
|
||||||
|
solidity::Instruction instruction() const;
|
||||||
|
|
||||||
|
/// Turns this pattern into an actual expression. Should only be called
|
||||||
|
/// for patterns resulting from an action, i.e. with match groups assigned.
|
||||||
|
Expression toExpression(SourceLocation const& _location) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Expression const& matchGroupValue() const;
|
||||||
|
|
||||||
|
PatternKind m_kind = PatternKind::Any;
|
||||||
|
solidity::Instruction m_instruction; ///< Only valid if m_kind is Operation
|
||||||
|
std::shared_ptr<u256> m_data; ///< Only valid if m_kind is Constant
|
||||||
|
std::vector<Pattern> m_arguments;
|
||||||
|
unsigned m_matchGroup = 0;
|
||||||
|
std::map<unsigned, Expression const*>* m_matchGroups = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
66
test/libjulia/Simplifier.cpp
Normal file
66
test/libjulia/Simplifier.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @date 2017
|
||||||
|
* Unit tests for the expression simplifier optimizer stage.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <test/libjulia/Common.h>
|
||||||
|
|
||||||
|
#include <libjulia/optimiser/ExpressionSimplifier.h>
|
||||||
|
|
||||||
|
#include <libsolidity/inlineasm/AsmPrinter.h>
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include <boost/range/adaptors.hpp>
|
||||||
|
#include <boost/algorithm/string/join.hpp>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
|
using namespace dev::julia;
|
||||||
|
using namespace dev::julia::test;
|
||||||
|
using namespace dev::solidity;
|
||||||
|
|
||||||
|
|
||||||
|
#define CHECK(_original, _expectation)\
|
||||||
|
do\
|
||||||
|
{\
|
||||||
|
assembly::AsmPrinter p;\
|
||||||
|
Block b = *(parse(_original, false).first);\
|
||||||
|
(ExpressionSimplifier{})(b);\
|
||||||
|
string result = p(b);\
|
||||||
|
BOOST_CHECK_EQUAL(result, format(_expectation, false));\
|
||||||
|
}\
|
||||||
|
while(false)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(IuliaSimplifier)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(smoke_test)
|
||||||
|
{
|
||||||
|
CHECK("{ }", "{ }");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(constants)
|
||||||
|
{
|
||||||
|
CHECK(
|
||||||
|
"{ let a := add(1, mul(3, 4)) }",
|
||||||
|
"{ let a := 13 }"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
Loading…
Reference in New Issue
Block a user