Browse Source

Refactoring: Extract equivalence class container.

cl-refactor
chriseth 10 years ago
parent
commit
ea6d5c2c98
  1. 154
      libevmcore/CommonSubexpressionEliminator.cpp
  2. 52
      libevmcore/CommonSubexpressionEliminator.h
  3. 110
      libevmcore/ExpressionClasses.cpp
  4. 70
      libevmcore/ExpressionClasses.h

154
libevmcore/CommonSubexpressionEliminator.cpp

@ -32,17 +32,17 @@ using namespace dev::eth;
vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems() vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
{ {
map<int, EquivalenceClassId> initialStackContents; map<int, ExpressionClasses::Id> initialStackContents;
map<int, EquivalenceClassId> targetStackContents; map<int, ExpressionClasses::Id> targetStackContents;
int minHeight = m_stackHeight + 1; int minHeight = m_stackHeight + 1;
if (!m_stackElements.empty()) if (!m_stackElements.empty())
minHeight = min(minHeight, m_stackElements.begin()->first.first); minHeight = min(minHeight, m_stackElements.begin()->first.first);
for (int height = minHeight; height <= max(0, m_stackHeight); ++height) for (int height = minHeight; height <= max(0, m_stackHeight); ++height)
{ {
// make sure it is created // make sure it is created
EquivalenceClassId c = getStackElement(height); ExpressionClasses::Id c = getStackElement(height);
if (height <= 0) if (height <= 0)
initialStackContents[height] = getClass(AssemblyItem(dupInstruction(1 - height))); initialStackContents[height] = m_expressionClasses.find(AssemblyItem(dupInstruction(1 - height)));
if (height <= m_stackHeight) if (height <= m_stackHeight)
targetStackContents[height] = c; targetStackContents[height] = c;
} }
@ -50,21 +50,21 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
// Debug info: // Debug info:
//stream(cout, currentStackContents, targetStackContents); //stream(cout, currentStackContents, targetStackContents);
return CSECodeGenerator().generateCode(initialStackContents, targetStackContents, m_equivalenceClasses); return CSECodeGenerator(m_expressionClasses).generateCode(initialStackContents, targetStackContents);
} }
ostream& CommonSubexpressionEliminator::stream( ostream& CommonSubexpressionEliminator::stream(
ostream& _out, ostream& _out,
map<int, EquivalenceClassId> _currentStack, map<int, ExpressionClasses::Id> _currentStack,
map<int, EquivalenceClassId> _targetStack map<int, ExpressionClasses::Id> _targetStack
) const ) const
{ {
auto streamEquivalenceClass = [this](ostream& _out, EquivalenceClassId _id) auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id)
{ {
auto const& eqClass = m_equivalenceClasses.at(_id); auto const& expr = m_expressionClasses.representative(_id);
_out << " " << _id << ": " << *eqClass.first; _out << " " << _id << ": " << *expr.item;
_out << "("; _out << "(";
for (EquivalenceClassId arg: eqClass.second) for (ExpressionClasses::Id arg: expr.arguments)
_out << dec << arg << ","; _out << dec << arg << ",";
_out << ")" << endl; _out << ")" << endl;
}; };
@ -75,23 +75,23 @@ ostream& CommonSubexpressionEliminator::stream(
for (auto const& it: m_stackElements) for (auto const& it: m_stackElements)
{ {
_out << " " << dec << it.first.first << "(" << it.first.second << ") = "; _out << " " << dec << it.first.first << "(" << it.first.second << ") = ";
streamEquivalenceClass(_out, it.second); streamExpressionClass(_out, it.second);
} }
_out << "Equivalence classes: " << endl; _out << "Equivalence classes: " << endl;
for (EquivalenceClassId eqClass = 0; eqClass < m_equivalenceClasses.size(); ++eqClass) for (ExpressionClasses::Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass)
streamEquivalenceClass(_out, eqClass); streamExpressionClass(_out, eqClass);
_out << "Current stack: " << endl; _out << "Current stack: " << endl;
for (auto const& it: _currentStack) for (auto const& it: _currentStack)
{ {
_out << " " << dec << it.first << ": "; _out << " " << dec << it.first << ": ";
streamEquivalenceClass(_out, it.second); streamExpressionClass(_out, it.second);
} }
_out << "Target stack: " << endl; _out << "Target stack: " << endl;
for (auto const& it: _targetStack) for (auto const& it: _targetStack)
{ {
_out << " " << dec << it.first << ": "; _out << " " << dec << it.first << ": ";
streamEquivalenceClass(_out, it.second); streamExpressionClass(_out, it.second);
} }
return _out; return _out;
@ -103,7 +103,7 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item)
{ {
if (_item.deposit() != 1) if (_item.deposit() != 1)
BOOST_THROW_EXCEPTION(InvalidDeposit()); BOOST_THROW_EXCEPTION(InvalidDeposit());
setStackElement(++m_stackHeight, getClass(_item, {})); setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}));
} }
else else
{ {
@ -121,16 +121,16 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item)
); );
else if (instruction != Instruction::POP) else if (instruction != Instruction::POP)
{ {
vector<EquivalenceClassId> arguments(info.args); vector<ExpressionClasses::Id> arguments(info.args);
for (int i = 0; i < info.args; ++i) for (int i = 0; i < info.args; ++i)
arguments[i] = getStackElement(m_stackHeight - 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(); m_stackHeight += _item.deposit();
} }
} }
void CommonSubexpressionEliminator::setStackElement(int _stackHeight, EquivalenceClassId _class) void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class)
{ {
unsigned nextSequence = getNextStackElementSequence(_stackHeight); unsigned nextSequence = getNextStackElementSequence(_stackHeight);
m_stackElements[make_pair(_stackHeight, nextSequence)] = _class; m_stackElements[make_pair(_stackHeight, nextSequence)] = _class;
@ -140,8 +140,8 @@ void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _st
{ {
if (_stackHeightA == _stackHeightB) if (_stackHeightA == _stackHeightB)
BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Swap on same stack elements.")); BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Swap on same stack elements."));
EquivalenceClassId classA = getStackElement(_stackHeightA); ExpressionClasses::Id classA = getStackElement(_stackHeightA);
EquivalenceClassId classB = getStackElement(_stackHeightB); ExpressionClasses::Id classB = getStackElement(_stackHeightB);
unsigned nextSequenceA = getNextStackElementSequence(_stackHeightA); unsigned nextSequenceA = getNextStackElementSequence(_stackHeightA);
unsigned nextSequenceB = getNextStackElementSequence(_stackHeightB); unsigned nextSequenceB = getNextStackElementSequence(_stackHeightB);
@ -149,7 +149,7 @@ void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _st
m_stackElements[make_pair(_stackHeightB, nextSequenceB)] = classA; m_stackElements[make_pair(_stackHeightB, nextSequenceB)] = classA;
} }
EquivalenceClassId CommonSubexpressionEliminator::getStackElement(int _stackHeight) ExpressionClasses::Id CommonSubexpressionEliminator::getStackElement(int _stackHeight)
{ {
// retrieve class by last sequence number // retrieve class by last sequence number
unsigned nextSequence = getNextStackElementSequence(_stackHeight); unsigned nextSequence = getNextStackElementSequence(_stackHeight);
@ -162,86 +162,8 @@ EquivalenceClassId CommonSubexpressionEliminator::getStackElement(int _stackHeig
if (_stackHeight <= -16) if (_stackHeight <= -16)
BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Stack too deep.")); 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. // This is a special assembly item that refers to elements pre-existing on the initial stack.
m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(dupInstruction(1 - _stackHeight))); return m_stackElements[make_pair(_stackHeight, nextSequence)] =
m_equivalenceClasses.push_back(make_pair(m_spareAssemblyItem.back().get(), EquivalenceClassIds())); m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight)));
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<Instruction, function<u256(u256 const&, u256 const&)>> 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<AssemblyItem>(result));
return getClass(*m_spareAssemblyItem.back());
}
}
m_equivalenceClasses.push_back(make_pair(&_item, args));
return m_equivalenceClasses.size() - 1;
} }
unsigned CommonSubexpressionEliminator::getNextStackElementSequence(int _stackHeight) unsigned CommonSubexpressionEliminator::getNextStackElementSequence(int _stackHeight)
@ -318,15 +240,11 @@ bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
} }
AssemblyItems CSECodeGenerator::generateCode( AssemblyItems CSECodeGenerator::generateCode(
map<int, EquivalenceClassId> const& _initialStack, map<int, ExpressionClasses::Id> const& _initialStack,
map<int, EquivalenceClassId> const& _targetStackContents, map<int, ExpressionClasses::Id> const& _targetStackContents
vector<pair<AssemblyItem const*, EquivalenceClassIds>> const& _equivalenceClasses
) )
{ {
// reset
*this = move(CSECodeGenerator());
m_stack = _initialStack; m_stack = _initialStack;
m_equivalenceClasses = _equivalenceClasses;
for (auto const& item: m_stack) for (auto const& item: m_stack)
if (!m_classPositions.count(item.second)) if (!m_classPositions.count(item.second))
m_classPositions[item.second] = item.first; m_classPositions[item.second] = item.first;
@ -377,18 +295,18 @@ AssemblyItems CSECodeGenerator::generateCode(
return m_generatedItems; return m_generatedItems;
} }
void CSECodeGenerator::addDependencies(EquivalenceClassId _c) void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c)
{ {
if (m_neededBy.count(_c)) if (m_neededBy.count(_c))
return; return;
for (EquivalenceClassId argument: m_equivalenceClasses.at(_c).second) for (ExpressionClasses::Id argument: m_expressionClasses.representative(_c).arguments)
{ {
addDependencies(argument); addDependencies(argument);
m_neededBy.insert(make_pair(argument, _c)); m_neededBy.insert(make_pair(argument, _c));
} }
} }
int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
{ {
if (m_classPositions.count(_c)) if (m_classPositions.count(_c))
{ {
@ -399,8 +317,8 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c)
); );
return m_classPositions[_c]; return m_classPositions[_c];
} }
EquivalenceClassIds const& arguments = m_equivalenceClasses.at(_c).second; ExpressionClasses::Ids const& arguments = m_expressionClasses.representative(_c).arguments;
for (EquivalenceClassId arg: boost::adaptors::reverse(arguments)) for (ExpressionClasses::Id arg: boost::adaptors::reverse(arguments))
generateClassElement(arg); generateClassElement(arg);
// The arguments are somewhere on the stack now, so it remains to move them at the correct place. // 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) for (size_t i = 0; i < arguments.size(); ++i)
assertThrow(m_stack[m_stackHeight - i] == arguments[i], OptimizerException, "Expected arguments not present." ); 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) && while (SemanticInformation::isCommutativeOperation(item) &&
!m_generatedItems.empty() && !m_generatedItems.empty() &&
m_generatedItems.back() == AssemblyItem(Instruction::SWAP1)) m_generatedItems.back() == AssemblyItem(Instruction::SWAP1))
@ -469,12 +387,12 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c)
m_classPositions[arg] = c_invalidPosition; m_classPositions[arg] = c_invalidPosition;
for (size_t i = 0; i < arguments.size(); ++i) for (size_t i = 0; i < arguments.size(); ++i)
m_stack.erase(m_stackHeight - i); m_stack.erase(m_stackHeight - i);
appendItem(*m_equivalenceClasses.at(_c).first); appendItem(*m_expressionClasses.representative(_c).item);
m_stack[m_stackHeight] = _c; m_stack[m_stackHeight] = _c;
return m_classPositions[_c] = m_stackHeight; 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 // 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. // 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()) if (m_stack.empty())
return false; return false;
assertThrow(m_stack.count(m_stackHeight), OptimizerException, ""); assertThrow(m_stack.count(m_stackHeight), OptimizerException, "");
EquivalenceClassId top = m_stack[m_stackHeight]; ExpressionClasses::Id top = m_stack[m_stackHeight];
if (!canBeRemoved(top)) if (!canBeRemoved(top))
return false; return false;
m_generatedItems.push_back(AssemblyItem(Instruction::POP)); m_generatedItems.push_back(AssemblyItem(Instruction::POP));

52
libevmcore/CommonSubexpressionEliminator.h

@ -28,6 +28,7 @@
#include <ostream> #include <ostream>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
#include <libevmcore/ExpressionClasses.h>
namespace dev namespace dev
{ {
@ -37,9 +38,6 @@ namespace eth
class AssemblyItem; class AssemblyItem;
using AssemblyItems = std::vector<AssemblyItem>; using AssemblyItems = std::vector<AssemblyItem>;
using EquivalenceClassId = unsigned;
using EquivalenceClassIds = std::vector<EquivalenceClassId>;
/** /**
* Optimizer step that performs common subexpression elimination and stack reorganisation, * 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 * 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. /// Streams debugging information to @a _out.
std::ostream& stream( std::ostream& stream(
std::ostream& _out, std::ostream& _out,
std::map<int, EquivalenceClassId> _currentStack = std::map<int, EquivalenceClassId>(), std::map<int, ExpressionClasses::Id> _currentStack = std::map<int, ExpressionClasses::Id>(),
std::map<int, EquivalenceClassId> _targetStack = std::map<int, EquivalenceClassId>() std::map<int, ExpressionClasses::Id> _targetStack = std::map<int, ExpressionClasses::Id>()
) const; ) const;
private: private:
/// Feeds the item into the system for analysis. /// Feeds the item into the system for analysis.
void feedItem(AssemblyItem const& _item); 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. /// 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. /// Swaps the given stack elements in their next sequence number.
void swapStackElements(int _stackHeightA, int _stackHeightB); void swapStackElements(int _stackHeightA, int _stackHeightB);
/// Retrieves the current equivalence class fo the given stack element (or generates a new /// Retrieves the current equivalence class fo the given stack element (or generates a new
/// one if it does not exist yet). /// one if it does not exist yet).
EquivalenceClassId getStackElement(int _stackHeight); ExpressionClasses::Id 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 = {});
/// @returns the next sequence number of the given stack element. /// @returns the next sequence number of the given stack element.
unsigned getNextStackElementSequence(int _stackHeight); unsigned getNextStackElementSequence(int _stackHeight);
@ -92,12 +88,9 @@ private:
/// Current stack height, can be negative. /// Current stack height, can be negative.
int m_stackHeight = 0; int m_stackHeight = 0;
/// Mapping (stack height, sequence number) -> equivalence class /// Mapping (stack height, sequence number) -> equivalence class
std::map<std::pair<int, unsigned>, EquivalenceClassId> m_stackElements; std::map<std::pair<int, unsigned>, ExpressionClasses::Id> m_stackElements;
/// Vector of equivalence class representatives - we only store one item of an equivalence /// Structure containing the classes of equivalent expressions.
/// class and the index is used as identifier. ExpressionClasses m_expressionClasses;
std::vector<std::pair<AssemblyItem const*, EquivalenceClassIds>> m_equivalenceClasses;
/// List of items generated during analysis.
std::vector<std::shared_ptr<AssemblyItem>> m_spareAssemblyItem;
}; };
/** /**
@ -121,27 +114,30 @@ struct SemanticInformation
class CSECodeGenerator class CSECodeGenerator
{ {
public: public:
CSECodeGenerator(ExpressionClasses const& _expressionClasses):
m_expressionClasses(_expressionClasses)
{}
/// @returns the assembly items generated from the given requirements /// @returns the assembly items generated from the given requirements
/// @param _initialStack current contents of the stack (up to stack height of zero) /// @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 _targetStackContents final contents of the stack, by stack height relative to initial
/// @param _equivalenceClasses equivalence classes as expressions of how to compute them /// @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( AssemblyItems generateCode(
std::map<int, EquivalenceClassId> const& _initialStack, std::map<int, ExpressionClasses::Id> const& _initialStack,
std::map<int, EquivalenceClassId> const& _targetStackContents, std::map<int, ExpressionClasses::Id> const& _targetStackContents
std::vector<std::pair<AssemblyItem const*, EquivalenceClassIds>> const& _equivalenceClasses
); );
private: private:
/// Recursively discovers all dependencies to @a m_requests. /// 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. /// Produce code that generates the given element if it is not yet present.
/// @returns the stack position of the element. /// @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. /// @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. /// Appends code to remove the topmost stack element if it can be removed.
bool removeStackTopIfPossible(); bool removeStackTopIfPossible();
@ -160,16 +156,16 @@ private:
/// Current height of the stack relative to the start. /// Current height of the stack relative to the start.
int m_stackHeight = 0; int m_stackHeight = 0;
/// If (b, a) is in m_requests then b is needed to compute a. /// If (b, a) is in m_requests then b is needed to compute a.
std::multimap<EquivalenceClassId, EquivalenceClassId> m_neededBy; std::multimap<ExpressionClasses::Id, ExpressionClasses::Id> m_neededBy;
/// Current content of the stack. /// Current content of the stack.
std::map<int, EquivalenceClassId> m_stack; std::map<int, ExpressionClasses::Id> m_stack;
/// Current positions of equivalence classes, equal to c_invalidPosition if already deleted. /// Current positions of equivalence classes, equal to c_invalidPosition if already deleted.
std::map<EquivalenceClassId, int> m_classPositions; std::map<ExpressionClasses::Id, int> m_classPositions;
/// The actual eqivalence class items and how to compute them. /// The actual eqivalence class items and how to compute them.
std::vector<std::pair<AssemblyItem const*, EquivalenceClassIds>> m_equivalenceClasses; ExpressionClasses const& m_expressionClasses;
/// The set of equivalence classes that should be present on the stack at the end. /// The set of equivalence classes that should be present on the stack at the end.
std::set<EquivalenceClassId> m_finalClasses; std::set<ExpressionClasses::Id> m_finalClasses;
}; };
template <class _AssemblyItemIterator> template <class _AssemblyItemIterator>

110
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 <http://www.gnu.org/licenses/>.
*/
/**
* @file ExpressionClasses.cpp
* @author Christian <c@ethdev.com>
* @date 2015
* Container for equivalence classes of expressions for use in common subexpression elimination.
*/
#include <libevmcore/ExpressionClasses.h>
#include <boost/range/adaptor/reversed.hpp>
#include <libevmcore/Assembly.h>
#include <libevmcore/CommonSubexpressionEliminator.h>
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<AssemblyItem>(_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<Instruction, function<u256(u256, u256)>> 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<AssemblyItem>(result));
return find(*m_spareAssemblyItem.back());
}
}
}
exp.id = m_representatives.size();
m_representatives.push_back(exp);
return exp.id;
}

70
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 <http://www.gnu.org/licenses/>.
*/
/**
* @file ExpressionClasses.h
* @author Christian <c@ethdev.com>
* @date 2015
* Container for equivalence classes of expressions for use in common subexpression elimination.
*/
#pragma once
#include <vector>
#include <set>
#include <memory>
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<Id>;
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<Expression> m_representatives;
std::vector<std::shared_ptr<AssemblyItem>> m_spareAssemblyItem;
};
}
}
Loading…
Cancel
Save