|
|
@ -22,6 +22,9 @@ |
|
|
|
*/ |
|
|
|
|
|
|
|
#include <libevmcore/ExpressionClasses.h> |
|
|
|
#include <utility> |
|
|
|
#include <tuple> |
|
|
|
#include <functional> |
|
|
|
#include <boost/range/adaptor/reversed.hpp> |
|
|
|
#include <libevmcore/Assembly.h> |
|
|
|
#include <libevmcore/CommonSubexpressionEliminator.h> |
|
|
@ -31,22 +34,26 @@ using namespace dev; |
|
|
|
using namespace dev::eth; |
|
|
|
|
|
|
|
|
|
|
|
ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids const& _arguments) |
|
|
|
bool ExpressionClasses::Expression::operator<(const ExpressionClasses::Expression& _other) const |
|
|
|
{ |
|
|
|
// TODO: do a clever search, i.e.
|
|
|
|
// - check for the presence of constants in the argument classes and do arithmetic
|
|
|
|
// - check whether the two items are equal for a SUB instruction
|
|
|
|
// - check whether 0 or 1 is in one of the classes for a MUL
|
|
|
|
auto type = item->type(); |
|
|
|
auto otherType = _other.item->type(); |
|
|
|
return std::tie(type, item->data(), arguments) < |
|
|
|
std::tie(otherType, _other.item->data(), _other.arguments); |
|
|
|
} |
|
|
|
|
|
|
|
ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids const& _arguments) |
|
|
|
{ |
|
|
|
Expression exp; |
|
|
|
exp.item = &_item; |
|
|
|
exp.arguments = _arguments; |
|
|
|
|
|
|
|
if (SemanticInformation::isCommutativeOperation(_item)) |
|
|
|
sort(exp.arguments.begin(), exp.arguments.end()); |
|
|
|
|
|
|
|
//@todo use a data structure that allows better searches
|
|
|
|
//@todo store all class members (not only the representatives) in an efficient data structure to search here
|
|
|
|
for (Expression const& e: m_representatives) |
|
|
|
if (std::tie(*e.item, e.arguments) == std::tie(*exp.item, exp.arguments)) |
|
|
|
if (!(e < exp || exp < e)) |
|
|
|
return e.id; |
|
|
|
|
|
|
|
if (SemanticInformation::isDupInstruction(_item)) |
|
|
@ -55,56 +62,175 @@ ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids con |
|
|
|
m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(_item)); |
|
|
|
exp.item = m_spareAssemblyItem.back().get(); |
|
|
|
} |
|
|
|
else if (_item.type() == Operation) |
|
|
|
|
|
|
|
ExpressionClasses::Id id = tryToSimplify(exp); |
|
|
|
if (id < m_representatives.size()) |
|
|
|
return id; |
|
|
|
|
|
|
|
exp.id = m_representatives.size(); |
|
|
|
m_representatives.push_back(exp); |
|
|
|
return exp.id; |
|
|
|
} |
|
|
|
|
|
|
|
ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr, bool _secondRun) |
|
|
|
{ |
|
|
|
if (_expr.item->type() != Operation) |
|
|
|
return -1; |
|
|
|
|
|
|
|
// @todo:
|
|
|
|
// ISZERO ISZERO
|
|
|
|
// associative operations (as done in Assembly.cpp)
|
|
|
|
// 2 * x == x + x
|
|
|
|
|
|
|
|
Id arg1; |
|
|
|
Id arg2; |
|
|
|
Id arg3; |
|
|
|
u256 data1; |
|
|
|
u256 data2; |
|
|
|
u256 data3; |
|
|
|
switch (_expr.arguments.size()) |
|
|
|
{ |
|
|
|
//@todo try to avoid having to do this multiple times by storing not only one representative of
|
|
|
|
// an equivalence class
|
|
|
|
default: |
|
|
|
arg3 = _expr.arguments.at(2); |
|
|
|
data3 = representative(arg3).item->data(); |
|
|
|
case 2: |
|
|
|
arg2 = _expr.arguments.at(1); |
|
|
|
data2 = representative(arg2).item->data(); |
|
|
|
case 1: |
|
|
|
arg1 = _expr.arguments.at(0); |
|
|
|
data1 = representative(arg1).item->data(); |
|
|
|
case 0: |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
// constant folding
|
|
|
|
auto isConstant = [this](Id eqc) { return representative(eqc).item->match(Push); }; |
|
|
|
if (exp.arguments.size() == 2 && all_of(exp.arguments.begin(), exp.arguments.end(), isConstant)) |
|
|
|
/**
|
|
|
|
* Simplification rule. If _strict is false, Push or a constant matches any constant, |
|
|
|
* otherwise Push matches "0" and a constant matches itself. |
|
|
|
* "UndefinedItem" matches any expression, but all of them must be equal inside one rule. |
|
|
|
*/ |
|
|
|
struct Rule |
|
|
|
{ |
|
|
|
Rule(AssemblyItems const& _pattern, bool _strict, function<AssemblyItem()> const& _action): |
|
|
|
pattern(_pattern), |
|
|
|
assemblyItemAction(_action), |
|
|
|
strict(_strict) |
|
|
|
{} |
|
|
|
Rule(AssemblyItems const& _pattern, function<AssemblyItem()> _action): |
|
|
|
Rule(_pattern, false, _action) |
|
|
|
{} |
|
|
|
Rule(AssemblyItems const& _pattern, bool _strict, function<Id()> const& _action): |
|
|
|
pattern(_pattern), |
|
|
|
idAction(_action), |
|
|
|
strict(_strict) |
|
|
|
{} |
|
|
|
Rule(AssemblyItems const& _pattern, function<Id()> _action): |
|
|
|
Rule(_pattern, false, _action) |
|
|
|
{} |
|
|
|
bool matches(ExpressionClasses const& _classes, Expression const& _expr) const |
|
|
|
{ |
|
|
|
auto signextend = [](u256 a, u256 b) -> u256 |
|
|
|
{ |
|
|
|
if (a >= 31) |
|
|
|
return b; |
|
|
|
unsigned testBit = unsigned(a) * 8 + 7; |
|
|
|
u256 mask = (u256(1) << testBit) - 1; |
|
|
|
return boost::multiprecision::bit_test(b, testBit) ? b | ~mask : b & mask; |
|
|
|
}; |
|
|
|
map<Instruction, function<u256(u256, u256)>> const arithmetics = |
|
|
|
if (!_expr.item->match(pattern.front())) |
|
|
|
return false; |
|
|
|
assertThrow(_expr.arguments.size() == pattern.size() - 1, OptimizerException, ""); |
|
|
|
Id argRequiredToBeEqual(-1); |
|
|
|
for (size_t i = 1; i < pattern.size(); ++i) |
|
|
|
{ |
|
|
|
{ Instruction::SUB, [](u256 a, u256 b) -> u256 {return a - b; } }, |
|
|
|
{ Instruction::DIV, [](u256 a, u256 b) -> u256 {return b == 0 ? 0 : a / b; } }, |
|
|
|
{ Instruction::SDIV, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : s2u(u2s(a) / u2s(b)); } }, |
|
|
|
{ Instruction::MOD, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : a % b; } }, |
|
|
|
{ Instruction::SMOD, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : s2u(u2s(a) % u2s(b)); } }, |
|
|
|
{ Instruction::EXP, [](u256 a, u256 b) -> u256 { return (u256)boost::multiprecision::powm(bigint(a), bigint(b), bigint(1) << 256); } }, |
|
|
|
{ Instruction::SIGNEXTEND, signextend }, |
|
|
|
{ Instruction::LT, [](u256 a, u256 b) -> u256 { return a < b ? 1 : 0; } }, |
|
|
|
{ Instruction::GT, [](u256 a, u256 b) -> u256 { return a > b ? 1 : 0; } }, |
|
|
|
{ Instruction::SLT, [](u256 a, u256 b) -> u256 { return u2s(a) < u2s(b) ? 1 : 0; } }, |
|
|
|
{ Instruction::SGT, [](u256 a, u256 b) -> u256 { return u2s(a) > u2s(b) ? 1 : 0; } }, |
|
|
|
{ Instruction::EQ, [](u256 a, u256 b) -> u256 { return a == b ? 1 : 0; } }, |
|
|
|
{ Instruction::ADD, [](u256 a, u256 b) -> u256 { return a + b; } }, |
|
|
|
{ Instruction::MUL, [](u256 a, u256 b) -> u256 { return a * b; } }, |
|
|
|
{ Instruction::AND, [](u256 a, u256 b) -> u256 { return a & b; } }, |
|
|
|
{ Instruction::OR, [](u256 a, u256 b) -> u256 { return a | b; } }, |
|
|
|
{ Instruction::XOR, [](u256 a, u256 b) -> u256 { return a ^ b; } }, |
|
|
|
}; |
|
|
|
if (arithmetics.count(_item.instruction())) |
|
|
|
Id arg = _expr.arguments[i - 1]; |
|
|
|
if (pattern[i].type() == UndefinedItem) |
|
|
|
{ |
|
|
|
if (argRequiredToBeEqual == Id(-1)) |
|
|
|
argRequiredToBeEqual = arg; |
|
|
|
else if (argRequiredToBeEqual != arg) |
|
|
|
return false; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
AssemblyItem const& argItem = *_classes.representative(arg).item; |
|
|
|
if (strict && argItem != pattern[i]) |
|
|
|
return false; |
|
|
|
else if (!strict && !argItem.match(pattern[i])) |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
AssemblyItems pattern; |
|
|
|
function<AssemblyItem()> assemblyItemAction; |
|
|
|
function<Id()> idAction; |
|
|
|
bool strict; |
|
|
|
}; |
|
|
|
|
|
|
|
vector<Rule> c_singleLevel{ |
|
|
|
// arithmetics on constants involving only stack variables
|
|
|
|
{{Instruction::ADD, Push, Push}, [&]{ return data1 + data2; }}, |
|
|
|
{{Instruction::MUL, Push, Push}, [&]{ return data1 * data2; }}, |
|
|
|
{{Instruction::SUB, Push, Push}, [&]{ return data1 - data2; }}, |
|
|
|
{{Instruction::DIV, Push, Push}, [&]{ return data2 == 0 ? 0 : data1 / data2; }}, |
|
|
|
{{Instruction::SDIV, Push, Push}, [&]{ return data2 == 0 ? 0 : s2u(u2s(data1) / u2s(data2)); }}, |
|
|
|
{{Instruction::MOD, Push, Push}, [&]{ return data2 == 0 ? 0 : data1 % data2; }}, |
|
|
|
{{Instruction::SMOD, Push, Push}, [&]{ return data2 == 0 ? 0 : s2u(u2s(data1) % u2s(data2)); }}, |
|
|
|
{{Instruction::EXP, Push, Push}, [&]{ return u256(boost::multiprecision::powm(bigint(data1), bigint(data2), bigint(1) << 256)); }}, |
|
|
|
{{Instruction::NOT, Push}, [&]{ return ~data1; }}, |
|
|
|
{{Instruction::LT, Push, Push}, [&]() -> u256 { return data1 < data2 ? 1 : 0; }}, |
|
|
|
{{Instruction::GT, Push, Push}, [&]() -> u256 { return data1 > data2 ? 1 : 0; }}, |
|
|
|
{{Instruction::SLT, Push, Push}, [&]() -> u256 { return u2s(data1) < u2s( data2) ? 1 : 0; }}, |
|
|
|
{{Instruction::SGT, Push, Push}, [&]() -> u256 { return u2s(data1) > u2s( data2) ? 1 : 0; }}, |
|
|
|
{{Instruction::EQ, Push, Push}, [&]() -> u256 { return data1 == data2 ? 1 : 0; }}, |
|
|
|
{{Instruction::ISZERO, Push}, [&]() -> u256 { return data1 == 0 ? 1 : 0; }}, |
|
|
|
{{Instruction::AND, Push, Push}, [&]{ return data1 & data2; }}, |
|
|
|
{{Instruction::OR, Push, Push}, [&]{ return data1 | data2; }}, |
|
|
|
{{Instruction::XOR, Push, Push}, [&]{ return data1 ^ data2; }}, |
|
|
|
{{Instruction::BYTE, Push, Push}, [&]{ return data1 >= 32 ? 0 : (data2 >> unsigned(8 * (31 - data1))) & 0xff; }}, |
|
|
|
{{Instruction::ADDMOD, Push, Push, Push}, [&]{ return data3 == 0 ? 0 : u256((bigint(data1) + bigint(data2)) % data3); }}, |
|
|
|
{{Instruction::MULMOD, Push, Push, Push}, [&]{ return data3 == 0 ? 0 : u256((bigint(data1) * bigint(data2)) % data3); }}, |
|
|
|
{{Instruction::MULMOD, Push, Push, Push}, [&]{ return data1 * data2; }}, |
|
|
|
{{Instruction::SIGNEXTEND, Push, Push}, [&]{ |
|
|
|
if (data1 >= 31) |
|
|
|
return data2; |
|
|
|
unsigned testBit = unsigned(data1) * 8 + 7; |
|
|
|
u256 mask = (u256(1) << testBit) - 1; |
|
|
|
return u256(boost::multiprecision::bit_test(data2, testBit) ? data2 | ~mask : data2 & mask); |
|
|
|
}}, |
|
|
|
{{Instruction::ADD, UndefinedItem, u256(0)}, true, [&]{ return arg1; }}, |
|
|
|
{{Instruction::MUL, UndefinedItem, u256(1)}, true, [&]{ return arg1; }}, |
|
|
|
{{Instruction::OR, UndefinedItem, u256(0)}, true, [&]{ return arg1; }}, |
|
|
|
{{Instruction::XOR, UndefinedItem, u256(0)}, true, [&]{ return arg1; }}, |
|
|
|
{{Instruction::AND, UndefinedItem, ~u256(0)}, true, [&]{ return arg1; }}, |
|
|
|
{{Instruction::MUL, UndefinedItem, u256(0)}, true, [&]{ return u256(0); }}, |
|
|
|
{{Instruction::DIV, UndefinedItem, u256(0)}, true, [&]{ return u256(0); }}, |
|
|
|
{{Instruction::MOD, UndefinedItem, u256(0)}, true, [&]{ return u256(0); }}, |
|
|
|
{{Instruction::MOD, u256(0), UndefinedItem}, true, [&]{ return u256(0); }}, |
|
|
|
{{Instruction::AND, UndefinedItem, u256(0)}, true, [&]{ return u256(0); }}, |
|
|
|
{{Instruction::OR, UndefinedItem, ~u256(0)}, true, [&]{ return ~u256(0); }}, |
|
|
|
{{Instruction::AND, UndefinedItem, UndefinedItem}, true, [&]{ return arg1; }}, |
|
|
|
{{Instruction::OR, UndefinedItem, UndefinedItem}, true, [&]{ return arg1; }}, |
|
|
|
{{Instruction::SUB, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, |
|
|
|
{{Instruction::EQ, UndefinedItem, UndefinedItem}, true, [&]{ return u256(1); }}, |
|
|
|
{{Instruction::LT, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, |
|
|
|
{{Instruction::SLT, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, |
|
|
|
{{Instruction::GT, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, |
|
|
|
{{Instruction::SGT, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, |
|
|
|
{{Instruction::MOD, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, |
|
|
|
}; |
|
|
|
|
|
|
|
for (auto const& rule: c_singleLevel) |
|
|
|
if (rule.matches(*this, _expr)) |
|
|
|
{ |
|
|
|
if (rule.idAction) |
|
|
|
return rule.idAction(); |
|
|
|
else |
|
|
|
{ |
|
|
|
u256 result = arithmetics.at(_item.instruction())( |
|
|
|
representative(exp.arguments[0]).item->data(), |
|
|
|
representative(exp.arguments[1]).item->data() |
|
|
|
); |
|
|
|
m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(result)); |
|
|
|
m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(rule.assemblyItemAction())); |
|
|
|
return find(*m_spareAssemblyItem.back()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (!_secondRun && _expr.arguments.size() == 2 && SemanticInformation::isCommutativeOperation(*_expr.item)) |
|
|
|
{ |
|
|
|
Expression expr = _expr; |
|
|
|
swap(expr.arguments[0], expr.arguments[1]); |
|
|
|
return tryToSimplify(expr, true); |
|
|
|
} |
|
|
|
exp.id = m_representatives.size(); |
|
|
|
m_representatives.push_back(exp); |
|
|
|
return exp.id; |
|
|
|
} |
|
|
|
|
|
|
|
return -1; |
|
|
|
} |
|
|
|