diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index aa532b2ff..8102721cc 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -32,17 +32,17 @@ using namespace dev::eth; vector CommonSubexpressionEliminator::getOptimizedItems() { - map initialStackContents; - map targetStackContents; + map initialStackContents; + map targetStackContents; int minHeight = m_stackHeight + 1; if (!m_stackElements.empty()) minHeight = min(minHeight, m_stackElements.begin()->first.first); for (int height = minHeight; height <= max(0, m_stackHeight); ++height) { // make sure it is created - EquivalenceClassId c = getStackElement(height); + ExpressionClasses::Id c = getStackElement(height); if (height <= 0) - initialStackContents[height] = getClass(AssemblyItem(dupInstruction(1 - height))); + initialStackContents[height] = m_expressionClasses.find(AssemblyItem(dupInstruction(1 - height))); if (height <= m_stackHeight) targetStackContents[height] = c; } @@ -50,21 +50,21 @@ vector CommonSubexpressionEliminator::getOptimizedItems() // Debug info: //stream(cout, currentStackContents, targetStackContents); - return CSECodeGenerator().generateCode(initialStackContents, targetStackContents, m_equivalenceClasses); + return CSECodeGenerator(m_expressionClasses).generateCode(initialStackContents, targetStackContents); } ostream& CommonSubexpressionEliminator::stream( ostream& _out, - map _currentStack, - map _targetStack + map _currentStack, + map _targetStack ) const { - auto streamEquivalenceClass = [this](ostream& _out, EquivalenceClassId _id) + auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id) { - auto const& eqClass = m_equivalenceClasses.at(_id); - _out << " " << _id << ": " << *eqClass.first; + auto const& expr = m_expressionClasses.representative(_id); + _out << " " << _id << ": " << *expr.item; _out << "("; - for (EquivalenceClassId arg: eqClass.second) + for (ExpressionClasses::Id arg: expr.arguments) _out << dec << arg << ","; _out << ")" << endl; }; @@ -75,23 +75,23 @@ ostream& CommonSubexpressionEliminator::stream( for (auto const& it: m_stackElements) { _out << " " << dec << it.first.first << "(" << it.first.second << ") = "; - streamEquivalenceClass(_out, it.second); + streamExpressionClass(_out, it.second); } _out << "Equivalence classes: " << endl; - for (EquivalenceClassId eqClass = 0; eqClass < m_equivalenceClasses.size(); ++eqClass) - streamEquivalenceClass(_out, eqClass); + for (ExpressionClasses::Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass) + streamExpressionClass(_out, eqClass); _out << "Current stack: " << endl; for (auto const& it: _currentStack) { _out << " " << dec << it.first << ": "; - streamEquivalenceClass(_out, it.second); + streamExpressionClass(_out, it.second); } _out << "Target stack: " << endl; for (auto const& it: _targetStack) { _out << " " << dec << it.first << ": "; - streamEquivalenceClass(_out, it.second); + streamExpressionClass(_out, it.second); } return _out; @@ -103,7 +103,7 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) { if (_item.deposit() != 1) BOOST_THROW_EXCEPTION(InvalidDeposit()); - setStackElement(++m_stackHeight, getClass(_item, {})); + setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {})); } else { @@ -121,16 +121,16 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) ); else if (instruction != Instruction::POP) { - vector arguments(info.args); + vector arguments(info.args); for (int i = 0; i < info.args; ++i) arguments[i] = getStackElement(m_stackHeight - i); - setStackElement(m_stackHeight + _item.deposit(), getClass(_item, arguments)); + setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments)); } m_stackHeight += _item.deposit(); } } -void CommonSubexpressionEliminator::setStackElement(int _stackHeight, EquivalenceClassId _class) +void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class) { unsigned nextSequence = getNextStackElementSequence(_stackHeight); m_stackElements[make_pair(_stackHeight, nextSequence)] = _class; @@ -140,8 +140,8 @@ void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _st { if (_stackHeightA == _stackHeightB) BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Swap on same stack elements.")); - EquivalenceClassId classA = getStackElement(_stackHeightA); - EquivalenceClassId classB = getStackElement(_stackHeightB); + ExpressionClasses::Id classA = getStackElement(_stackHeightA); + ExpressionClasses::Id classB = getStackElement(_stackHeightB); unsigned nextSequenceA = getNextStackElementSequence(_stackHeightA); unsigned nextSequenceB = getNextStackElementSequence(_stackHeightB); @@ -149,7 +149,7 @@ void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _st m_stackElements[make_pair(_stackHeightB, nextSequenceB)] = classA; } -EquivalenceClassId CommonSubexpressionEliminator::getStackElement(int _stackHeight) +ExpressionClasses::Id CommonSubexpressionEliminator::getStackElement(int _stackHeight) { // retrieve class by last sequence number unsigned nextSequence = getNextStackElementSequence(_stackHeight); @@ -162,86 +162,8 @@ EquivalenceClassId CommonSubexpressionEliminator::getStackElement(int _stackHeig if (_stackHeight <= -16) BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Stack too deep.")); // This is a special assembly item that refers to elements pre-existing on the initial stack. - m_spareAssemblyItem.push_back(make_shared(dupInstruction(1 - _stackHeight))); - m_equivalenceClasses.push_back(make_pair(m_spareAssemblyItem.back().get(), EquivalenceClassIds())); - return m_stackElements[make_pair(_stackHeight, nextSequence)] = EquivalenceClassId(m_equivalenceClasses.size() - 1); -} - -EquivalenceClassId CommonSubexpressionEliminator::getClass( - const AssemblyItem& _item, - EquivalenceClassIds const& _arguments -) -{ - // 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 - - EquivalenceClassIds args = _arguments; - if (SemanticInformation::isCommutativeOperation(_item)) - sort(args.begin(), args.end()); - - //@todo use a better data structure for search here - for (EquivalenceClassId c = 0; c < m_equivalenceClasses.size(); ++c) - { - AssemblyItem const& classItem = *m_equivalenceClasses.at(c).first; - if (classItem != _item) - continue; - - assertThrow( - args.size() == m_equivalenceClasses.at(c).second.size(), - OptimizerException, - "Equal assembly items with different number of arguments." - ); - if (equal(args.begin(), args.end(), m_equivalenceClasses.at(c).second.begin())) - return c; - } - // constant folding - if (_item.type() == Operation && args.size() == 2 && all_of( - args.begin(), - args.end(), - [this](EquivalenceClassId eqc) { return m_equivalenceClasses.at(eqc).first->match(Push); })) - { - auto signextend = [](u256 const& _a, u256 const& _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> const arithmetics = - { - { Instruction::SUB, [](u256 const& _a, u256 const& _b) -> u256 {return _a - _b; } }, - { Instruction::DIV, [](u256 const& _a, u256 const& _b) -> u256 {return _b == 0 ? 0 : _a / _b; } }, - { Instruction::SDIV, [](u256 const& _a, u256 const& _b) -> u256 { return _b == 0 ? 0 : s2u(u2s(_a) / u2s(_b)); } }, - { Instruction::MOD, [](u256 const& _a, u256 const& _b) -> u256 { return _b == 0 ? 0 : _a % _b; } }, - { Instruction::SMOD, [](u256 const& _a, u256 const& _b) -> u256 { return _b == 0 ? 0 : s2u(u2s(_a) % u2s(_b)); } }, - { Instruction::EXP, [](u256 const& _a, u256 const& _b) -> u256 { return (u256)boost::multiprecision::powm(bigint(_a), bigint(_b), bigint(1) << 256); } }, - { Instruction::SIGNEXTEND, signextend }, - { Instruction::LT, [](u256 const& _a, u256 const& _b) -> u256 { return _a < _b ? 1 : 0; } }, - { Instruction::GT, [](u256 const& _a, u256 const& _b) -> u256 { return _a > _b ? 1 : 0; } }, - { Instruction::SLT, [](u256 const& _a, u256 const& _b) -> u256 { return u2s(_a) < u2s(_b) ? 1 : 0; } }, - { Instruction::SGT, [](u256 const& _a, u256 const& _b) -> u256 { return u2s(_a) > u2s(_b) ? 1 : 0; } }, - { Instruction::EQ, [](u256 const& _a, u256 const& _b) -> u256 { return _a == _b ? 1 : 0; } }, - { Instruction::ADD, [](u256 const& _a, u256 const& _b) -> u256 { return _a + _b; } }, - { Instruction::MUL, [](u256 const& _a, u256 const& _b) -> u256 { return _a * _b; } }, - { Instruction::AND, [](u256 const& _a, u256 const& _b) -> u256 { return _a & _b; } }, - { Instruction::OR, [](u256 const& _a, u256 const& _b) -> u256 { return _a | _b; } }, - { Instruction::XOR, [](u256 const& _a, u256 const& _b) -> u256 { return _a ^ _b; } }, - }; - if (arithmetics.count(_item.instruction())) - { - u256 result = arithmetics.at(_item.instruction())( - m_equivalenceClasses.at(args[0]).first->data(), - m_equivalenceClasses.at(args[1]).first->data() - ); - m_spareAssemblyItem.push_back(make_shared(result)); - return getClass(*m_spareAssemblyItem.back()); - } - } - m_equivalenceClasses.push_back(make_pair(&_item, args)); - return m_equivalenceClasses.size() - 1; + return m_stackElements[make_pair(_stackHeight, nextSequence)] = + m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight))); } unsigned CommonSubexpressionEliminator::getNextStackElementSequence(int _stackHeight) @@ -318,15 +240,11 @@ bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item) } AssemblyItems CSECodeGenerator::generateCode( - map const& _initialStack, - map const& _targetStackContents, - vector> const& _equivalenceClasses + map const& _initialStack, + map const& _targetStackContents ) { - // reset - *this = move(CSECodeGenerator()); m_stack = _initialStack; - m_equivalenceClasses = _equivalenceClasses; for (auto const& item: m_stack) if (!m_classPositions.count(item.second)) m_classPositions[item.second] = item.first; @@ -377,18 +295,18 @@ AssemblyItems CSECodeGenerator::generateCode( return m_generatedItems; } -void CSECodeGenerator::addDependencies(EquivalenceClassId _c) +void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c) { if (m_neededBy.count(_c)) return; - for (EquivalenceClassId argument: m_equivalenceClasses.at(_c).second) + for (ExpressionClasses::Id argument: m_expressionClasses.representative(_c).arguments) { addDependencies(argument); m_neededBy.insert(make_pair(argument, _c)); } } -int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) +int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c) { if (m_classPositions.count(_c)) { @@ -399,8 +317,8 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) ); return m_classPositions[_c]; } - EquivalenceClassIds const& arguments = m_equivalenceClasses.at(_c).second; - for (EquivalenceClassId arg: boost::adaptors::reverse(arguments)) + ExpressionClasses::Ids const& arguments = m_expressionClasses.representative(_c).arguments; + for (ExpressionClasses::Id arg: boost::adaptors::reverse(arguments)) generateClassElement(arg); // The arguments are somewhere on the stack now, so it remains to move them at the correct place. @@ -458,7 +376,7 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) for (size_t i = 0; i < arguments.size(); ++i) assertThrow(m_stack[m_stackHeight - i] == arguments[i], OptimizerException, "Expected arguments not present." ); - AssemblyItem const& item = *m_equivalenceClasses.at(_c).first; + AssemblyItem const& item = *m_expressionClasses.representative(_c).item; while (SemanticInformation::isCommutativeOperation(item) && !m_generatedItems.empty() && m_generatedItems.back() == AssemblyItem(Instruction::SWAP1)) @@ -469,12 +387,12 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) m_classPositions[arg] = c_invalidPosition; for (size_t i = 0; i < arguments.size(); ++i) m_stack.erase(m_stackHeight - i); - appendItem(*m_equivalenceClasses.at(_c).first); + appendItem(*m_expressionClasses.representative(_c).item); m_stack[m_stackHeight] = _c; return m_classPositions[_c] = m_stackHeight; } -bool CSECodeGenerator::canBeRemoved(EquivalenceClassId _element, EquivalenceClassId _result) +bool CSECodeGenerator::canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result) { // Returns false if _element is finally needed or is needed by a class that has not been // computed yet. Note that m_classPositions also includes classes that were deleted in the meantime. @@ -493,7 +411,7 @@ bool CSECodeGenerator::removeStackTopIfPossible() if (m_stack.empty()) return false; assertThrow(m_stack.count(m_stackHeight), OptimizerException, ""); - EquivalenceClassId top = m_stack[m_stackHeight]; + ExpressionClasses::Id top = m_stack[m_stackHeight]; if (!canBeRemoved(top)) return false; m_generatedItems.push_back(AssemblyItem(Instruction::POP)); diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index edf1e3f38..2a49d888d 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace dev { @@ -37,9 +38,6 @@ namespace eth class AssemblyItem; using AssemblyItems = std::vector; -using EquivalenceClassId = unsigned; -using EquivalenceClassIds = std::vector; - /** * Optimizer step that performs common subexpression elimination and stack reorganisation, * i.e. it tries to infer equality among expressions and compute the values of two expressions @@ -67,24 +65,22 @@ public: /// Streams debugging information to @a _out. std::ostream& stream( std::ostream& _out, - std::map _currentStack = std::map(), - std::map _targetStack = std::map() + std::map _currentStack = std::map(), + std::map _targetStack = std::map() ) const; private: /// Feeds the item into the system for analysis. void feedItem(AssemblyItem const& _item); + /// Simplifies the given item using /// Assigns a new equivalence class to the next sequence number of the given stack element. - void setStackElement(int _stackHeight, EquivalenceClassId _class); + void setStackElement(int _stackHeight, ExpressionClasses::Id _class); /// Swaps the given stack elements in their next sequence number. void swapStackElements(int _stackHeightA, int _stackHeightB); /// Retrieves the current equivalence class fo the given stack element (or generates a new /// one if it does not exist yet). - EquivalenceClassId getStackElement(int _stackHeight); - /// Retrieves the equivalence class resulting from the given item applied to the given classes, - /// might also create a new one. - EquivalenceClassId getClass(AssemblyItem const& _item, EquivalenceClassIds const& _arguments = {}); + ExpressionClasses::Id getStackElement(int _stackHeight); /// @returns the next sequence number of the given stack element. unsigned getNextStackElementSequence(int _stackHeight); @@ -92,12 +88,9 @@ private: /// Current stack height, can be negative. int m_stackHeight = 0; /// Mapping (stack height, sequence number) -> equivalence class - std::map, EquivalenceClassId> m_stackElements; - /// Vector of equivalence class representatives - we only store one item of an equivalence - /// class and the index is used as identifier. - std::vector> m_equivalenceClasses; - /// List of items generated during analysis. - std::vector> m_spareAssemblyItem; + std::map, ExpressionClasses::Id> m_stackElements; + /// Structure containing the classes of equivalent expressions. + ExpressionClasses m_expressionClasses; }; /** @@ -121,27 +114,30 @@ struct SemanticInformation class CSECodeGenerator { public: + CSECodeGenerator(ExpressionClasses const& _expressionClasses): + m_expressionClasses(_expressionClasses) + {} + /// @returns the assembly items generated from the given requirements /// @param _initialStack current contents of the stack (up to stack height of zero) /// @param _targetStackContents final contents of the stack, by stack height relative to initial /// @param _equivalenceClasses equivalence classes as expressions of how to compute them - /// @note resuts the state of the object for each call. + /// @note should only be called once on each object. AssemblyItems generateCode( - std::map const& _initialStack, - std::map const& _targetStackContents, - std::vector> const& _equivalenceClasses + std::map const& _initialStack, + std::map const& _targetStackContents ); private: /// Recursively discovers all dependencies to @a m_requests. - void addDependencies(EquivalenceClassId _c); + void addDependencies(ExpressionClasses::Id _c); /// Produce code that generates the given element if it is not yet present. /// @returns the stack position of the element. - int generateClassElement(EquivalenceClassId _c); + int generateClassElement(ExpressionClasses::Id _c); /// @returns true if @a _element can be removed - in general or, if given, while computing @a _result. - bool canBeRemoved(EquivalenceClassId _element, EquivalenceClassId _result = EquivalenceClassId(-1)); + bool canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result = ExpressionClasses::Id(-1)); /// Appends code to remove the topmost stack element if it can be removed. bool removeStackTopIfPossible(); @@ -160,16 +156,16 @@ private: /// Current height of the stack relative to the start. int m_stackHeight = 0; /// If (b, a) is in m_requests then b is needed to compute a. - std::multimap m_neededBy; + std::multimap m_neededBy; /// Current content of the stack. - std::map m_stack; + std::map m_stack; /// Current positions of equivalence classes, equal to c_invalidPosition if already deleted. - std::map m_classPositions; + std::map m_classPositions; /// The actual eqivalence class items and how to compute them. - std::vector> m_equivalenceClasses; + ExpressionClasses const& m_expressionClasses; /// The set of equivalence classes that should be present on the stack at the end. - std::set m_finalClasses; + std::set m_finalClasses; }; template diff --git a/libevmcore/ExpressionClasses.cpp b/libevmcore/ExpressionClasses.cpp new file mode 100644 index 000000000..5d7b3e11b --- /dev/null +++ b/libevmcore/ExpressionClasses.cpp @@ -0,0 +1,110 @@ +/* + 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 + +using namespace std; +using namespace dev; +using namespace dev::eth; + + +ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids const& _arguments) +{ + // 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 + + 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 + for (Expression const& e: m_representatives) + if (std::tie(*e.item, e.arguments) == std::tie(*exp.item, exp.arguments)) + 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(); + } + else if (_item.type() == Operation) + { + //@todo try to avoid having to do this multiple times by storing not only one representative of + // an equivalence class + + // 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)) + { + 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> const arithmetics = + { + { 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())) + { + u256 result = arithmetics.at(_item.instruction())( + representative(exp.arguments[0]).item->data(), + representative(exp.arguments[1]).item->data() + ); + m_spareAssemblyItem.push_back(make_shared(result)); + return find(*m_spareAssemblyItem.back()); + } + } + } + exp.id = m_representatives.size(); + m_representatives.push_back(exp); + return exp.id; +} + diff --git a/libevmcore/ExpressionClasses.h b/libevmcore/ExpressionClasses.h new file mode 100644 index 000000000..89485214c --- /dev/null +++ b/libevmcore/ExpressionClasses.h @@ -0,0 +1,70 @@ +/* + 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.h + * @author Christian + * @date 2015 + * Container for equivalence classes of expressions for use in common subexpression elimination. + */ + +#pragma once + +#include +#include +#include + +namespace dev +{ +namespace eth +{ + +class AssemblyItem; + +/** + * Collection of classes of equivalent expressions that can also determine the class of an expression. + * Identifiers are contiguously assigned to new classes starting from zero. + */ +class ExpressionClasses +{ +public: + using Id = unsigned; + using Ids = std::vector; + + struct Expression + { + Id id; + AssemblyItem const* item; + Ids arguments; + }; + + /// Retrieves the id of the expression equivalence class resulting from the given item applied to the + /// given classes, might also create a new one. + Id find(AssemblyItem const& _item, Ids const& _arguments = {}); + /// @returns the canonical representative of an expression class. + Expression const& representative(Id _id) const { return m_representatives.at(_id); } + /// @returns the number of classes. + Id size() const { return m_representatives.size(); } + +private: + + /// Expression equivalence class representatives - we only store one item of an equivalence. + std::vector m_representatives; + std::vector> m_spareAssemblyItem; +}; + +} +}