Browse Source

Analyzing part of the optimizer.

cl-refactor
chriseth 10 years ago
parent
commit
9b021fc77c
  1. 2
      libevmcore/Assembly.cpp
  2. 195
      libevmcore/CommonSubexpressionEliminator.cpp
  3. 40
      libevmcore/CommonSubexpressionEliminator.h
  4. 1
      libevmcore/Exceptions.h

2
libevmcore/Assembly.cpp

@ -421,7 +421,7 @@ Assembly& Assembly::optimise(bool _enable)
optimizedItems.push_back(*iter);
}
copt << "Old size: " << m_items.size() << ", new size: " << optimizedItems.size();
swap(m_items, optimizedItems);
// swap(m_items, optimizedItems);
copt << *this;

195
libevmcore/CommonSubexpressionEliminator.cpp

@ -21,6 +21,7 @@
* Optimizer step for common subexpression elimination and stack reorganisation.
*/
#include <functional>
#include <libevmcore/CommonSubexpressionEliminator.h>
#include <libevmcore/Assembly.h>
@ -30,14 +31,204 @@ using namespace dev::eth;
vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems() const
{
auto streamEquivalenceClass = [this](ostream& _out, EquivalenceClass _id)
{
auto const& eqClass = m_equivalenceClasses[_id];
_out << " " << _id << ": " << *eqClass.first;
_out << "(";
for (EquivalenceClass arg: eqClass.second)
_out << dec << arg << ",";
_out << ")" << endl;
};
cout << dec;
cout << "Optimizer results:" << endl;
cout << "Final stack height: " << m_stackHeight << endl;
cout << "Stack elements: " << endl;
for (auto const& it: m_stackElements)
{
cout
<< " " << dec << it.first.first << "(" << it.first.second << ") = ";
streamEquivalenceClass(cout, it.second);
}
cout << "Equivalence classes: " << endl;
for (EquivalenceClass eqClass = 0; eqClass < m_equivalenceClasses.size(); ++eqClass)
streamEquivalenceClass(cout, eqClass);
cout << "----------------------------" << endl;
if (m_stackElements.size() == 0)
{
}
int stackHeight;
// m_stackElements
// for all stack elements from most neg to most pos:
//
return vector<AssemblyItem>();
}
bool CommonSubexpressionEliminator::breaksBasicBlock(AssemblyItem const&)
bool CommonSubexpressionEliminator::breaksBasicBlock(AssemblyItem const& _item)
{
switch (_item.type())
{
case UndefinedItem:
case Tag:
return true;
case Push:
case PushString:
case PushTag:
case PushSub:
case PushSubSize:
case PushProgramSize:
case PushData:
return false;
case Operation:
return instructionInfo(_item.instruction()).sideEffects;
}
}
void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item)
{
cout << _item << endl;
if (_item.type() != Operation)
{
if (_item.deposit() != 1)
BOOST_THROW_EXCEPTION(InvalidDeposit());
setStackElement(++m_stackHeight, getClass(_item, {}));
}
else
{
Instruction instruction = _item.instruction();
InstructionInfo info = instructionInfo(instruction);
if (Instruction::DUP1 <= instruction && instruction <= Instruction::DUP16)
setStackElement(
m_stackHeight + 1,
getStackElement(m_stackHeight - int(instruction) + int(Instruction::DUP1))
);
else if (Instruction::SWAP1 <= instruction && instruction <= Instruction::SWAP16)
swapStackElements(
m_stackHeight,
m_stackHeight - 1 - int(instruction) + int(Instruction::SWAP1)
);
else if (instruction != Instruction::POP)
{
vector<EquivalenceClass> arguments(info.args);
for (int i = 0; i < info.args; ++i)
arguments[i] = getStackElement(m_stackHeight - i);
setStackElement(m_stackHeight + info.ret - info.args, getClass(_item, arguments));
}
m_stackHeight += info.ret - info.args;
}
}
void CommonSubexpressionEliminator::setStackElement(int _stackHeight, EquivalenceClass _class)
{
unsigned nextSequence = getNextStackElementSequence(_stackHeight);
m_stackElements[make_pair(_stackHeight, nextSequence)] = _class;
}
void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _stackHeightB)
{
if (_stackHeightA == _stackHeightB)
BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Swap on same stack elements."));
EquivalenceClass classA = getStackElement(_stackHeightA);
EquivalenceClass classB = getStackElement(_stackHeightB);
unsigned nextSequenceA = getNextStackElementSequence(_stackHeightA);
unsigned nextSequenceB = getNextStackElementSequence(_stackHeightB);
m_stackElements[make_pair(_stackHeightA, nextSequenceA)] = classB;
m_stackElements[make_pair(_stackHeightB, nextSequenceB)] = classA;
}
CommonSubexpressionEliminator::EquivalenceClass CommonSubexpressionEliminator::getStackElement(int _stackHeight)
{
// retrieve class by last sequence number
unsigned nextSequence = getNextStackElementSequence(_stackHeight);
if (nextSequence > 0)
return m_stackElements[make_pair(_stackHeight, nextSequence - 1)];
// Stack element not found (not assigned yet), create new equivalence class.
if (_stackHeight > 0)
BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Stack element accessed before assignment."));
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<AssemblyItem>(dupInstruction(1 - _stackHeight)));
m_equivalenceClasses.push_back(make_pair(m_spareAssemblyItem.back().get(), EquivalenceClasses()));
return m_stackElements[make_pair(_stackHeight, nextSequence)] = EquivalenceClass(m_equivalenceClasses.size() - 1);
}
CommonSubexpressionEliminator::EquivalenceClass CommonSubexpressionEliminator::getClass(
const AssemblyItem& _item,
EquivalenceClasses const& _arguments
)
{
// 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
// - for commutative opcodes, sort the arguments before searching
for (EquivalenceClass c = 0; c < m_equivalenceClasses.size(); ++c)
{
AssemblyItem const& classItem = *m_equivalenceClasses[c].first;
if (classItem != _item)
continue;
if (_arguments.size() != m_equivalenceClasses[c].second.size())
BOOST_THROW_EXCEPTION(
OptimizerException() <<
errinfo_comment("Equal assembly items with different number of arguments.")
);
if (equal(_arguments.begin(), _arguments.end(), m_equivalenceClasses[c].second.begin()))
return c;
}
if (_item.type() == Operation && _arguments.size() == 2 && all_of(
_arguments.begin(),
_arguments.end(),
[this](EquivalenceClass eqc) { return m_equivalenceClasses[eqc].first->match(Push); }))
{
map<Instruction, function<u256(u256, u256)>> const arithmetics =
{
//@todo these are not correct (e.g. for div by zero)
{ Instruction::SUB, [](u256 a, u256 b)->u256{return a - b;} },
{ Instruction::DIV, [](u256 a, u256 b)->u256{return a / b;} },
{ Instruction::SDIV, [](u256 a, u256 b)->u256{return s2u(u2s(a) / u2s(b));} },
{ Instruction::MOD, [](u256 a, u256 b)->u256{return a % b;} },
{ Instruction::SMOD, [](u256 a, u256 b)->u256{return 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())(
m_equivalenceClasses[_arguments[0]].first->data(),
m_equivalenceClasses[_arguments[1]].first->data()
);
m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(result));
return getClass(*m_spareAssemblyItem.back());
}
}
m_equivalenceClasses.push_back(make_pair(&_item, _arguments));
return m_equivalenceClasses.size() - 1;
}
void CommonSubexpressionEliminator::feedItem(AssemblyItem const&)
unsigned CommonSubexpressionEliminator::getNextStackElementSequence(int _stackHeight)
{
auto it = m_stackElements.upper_bound(make_pair(_stackHeight, unsigned(-1)));
if (it == m_stackElements.begin())
return 0;
--it;
if (it->first.first == _stackHeight)
return it->first.second + 1;
else
return 0;
}

40
libevmcore/CommonSubexpressionEliminator.h

@ -24,6 +24,8 @@
#pragma once
#include <vector>
#include <map>
#include <libdevcore/CommonIO.h>
namespace dev
{
@ -36,6 +38,14 @@ class AssemblyItem;
* Optimizer step that performs common subexpression elimination and stack reorginasation,
* i.e. it tries to infer equality among expressions and compute the values of two expressions
* known to be equal only once.
*
* The general workings are that for each assembly item that is fed into the eliminator, an
* equivalence class is derived from the operation and the equivalence class of its arguments and
* it is assigned to the next sequence number of a stack item. DUPi, SWAPi and some arithmetic
* instructions are used to infer equivalences while these classes are determined.
*
* When the list of optimized items is requested, they are generated in a bottom-up fashion,
* adding code for equivalence classes that were not yet computed.
*/
class CommonSubexpressionEliminator
{
@ -49,10 +59,37 @@ public:
std::vector<AssemblyItem> getOptimizedItems() const;
private:
using EquivalenceClass = unsigned;
using EquivalenceClasses = std::vector<EquivalenceClass>;
/// @returns true if the given items starts a new basic block
bool breaksBasicBlock(AssemblyItem const& _item);
/// Feeds the item into the system for analysis.
void feedItem(AssemblyItem const& _item);
/// Assigns a new equivalence class to the next sequence number of the given stack element.
void setStackElement(int _stackHeight, EquivalenceClass _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).
EquivalenceClass getStackElement(int _stackHeight);
/// Retrieves the equivalence class resulting from the given item applied to the given classes,
/// might also create a new one.
EquivalenceClass getClass(AssemblyItem const& _item, EquivalenceClasses const& _arguments = {});
/// @returns the next sequence number of the given stack element.
unsigned getNextStackElementSequence(int _stackHeight);
/// Current stack height, can be negative.
int m_stackHeight = 0;
/// Mapping (stack height, sequence number) -> equivalence class
std::map<std::pair<int, unsigned>, EquivalenceClass> 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<std::pair<AssemblyItem const*, EquivalenceClasses>> m_equivalenceClasses;
/// List of items generated during analysis.
std::vector<std::shared_ptr<AssemblyItem>> m_spareAssemblyItem;
};
template <class _AssemblyItemIterator>
@ -61,7 +98,8 @@ _AssemblyItemIterator CommonSubexpressionEliminator::feedItems(
_AssemblyItemIterator _end
)
{
while (_iterator != _end && !breaksBasicBlock(*_iterator))
std::cout << "---------------Feeding items to the CSE engine:--------------" << std::endl;
for (; _iterator != _end && !breaksBasicBlock(*_iterator); ++_iterator)
feedItem(*_iterator);
return _iterator;
}

1
libevmcore/Exceptions.h

@ -31,6 +31,7 @@ namespace eth
struct AssemblyException: virtual Exception {};
struct InvalidDeposit: virtual AssemblyException {};
struct InvalidOpcode: virtual AssemblyException {};
struct OptimizerException: virtual AssemblyException {};
}
}

Loading…
Cancel
Save