/* This file is part of cpp-ethereum. cpp-ethereum 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. cpp-ethereum 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 cpp-ethereum. If not, see . */ /** * @file ExpressionClasses.cpp * @author Christian * @date 2015 * Container for equivalence classes of expressions for use in common subexpression elimination. */ #include #include #include #include #include #include #include using namespace std; using namespace dev; using namespace dev::eth; bool ExpressionClasses::Expression::operator<(const ExpressionClasses::Expression& _other) const { 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 store all class members (not only the representatives) in an efficient data structure to search here for (Expression const& e: m_representatives) if (!(e < exp || exp < e)) return e.id; if (SemanticInformation::isDupInstruction(_item)) { // Special item that refers to values pre-existing on the stack m_spareAssemblyItem.push_back(make_shared(_item)); exp.item = m_spareAssemblyItem.back().get(); } 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()) { 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; } /** * 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 const& _action): pattern(_pattern), assemblyItemAction(_action), strict(_strict) {} Rule(AssemblyItems const& _pattern, function _action): Rule(_pattern, false, _action) {} Rule(AssemblyItems const& _pattern, bool _strict, function const& _action): pattern(_pattern), idAction(_action), strict(_strict) {} Rule(AssemblyItems const& _pattern, function _action): Rule(_pattern, false, _action) {} bool matches(ExpressionClasses const& _classes, Expression const& _expr) const { 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) { 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 assemblyItemAction; function idAction; bool strict; }; vector 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 { m_spareAssemblyItem.push_back(make_shared(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); } return -1; }