Browse Source

Optimizing various single operations.

cl-refactor
chriseth 10 years ago
parent
commit
fd961605b4
  1. 39
      libevmcore/Assembly.cpp
  2. 12
      libevmcore/CommonSubexpressionEliminator.cpp
  3. 1
      libevmcore/Exceptions.h
  4. 226
      libevmcore/ExpressionClasses.cpp
  5. 7
      libevmcore/ExpressionClasses.h
  6. 78
      test/SolidityOptimizer.cpp

39
libevmcore/Assembly.cpp

@ -288,18 +288,6 @@ inline bool matches(AssemblyItemsConstRef _a, AssemblyItemsConstRef _b)
return true; return true;
} }
//@todo this has to move to a special optimizer class soon
template<class Iterator>
unsigned bytesRequiredBySlice(Iterator _begin, Iterator _end)
{
// this is only used in the optimizer, so we can provide a guess for the address length
unsigned addressLength = 4;
unsigned size = 0;
for (; _begin != _end; ++_begin)
size += _begin->bytesRequired(addressLength);
return size;
}
struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; }; struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; };
#define copt dev::LogOutputStream<OptimiserChannel, true>() #define copt dev::LogOutputStream<OptimiserChannel, true>()
@ -315,8 +303,6 @@ Assembly& Assembly::optimise(bool _enable)
{ Instruction::OR, [](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;} }, { Instruction::XOR, [](u256 a, u256 b)->u256{return a ^ b;} },
}; };
std::vector<pair<AssemblyItem, u256>> const c_identities =
{ { Instruction::ADD, 0}, { Instruction::MUL, 1}, { Instruction::MOD, 0}, { Instruction::OR, 0}, { Instruction::XOR, 0} };
std::vector<pair<AssemblyItems, function<AssemblyItems(AssemblyItemsConstRef)>>> rules = std::vector<pair<AssemblyItems, function<AssemblyItems(AssemblyItemsConstRef)>>> rules =
{ {
{ { Push, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, { { Push, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
@ -334,17 +320,13 @@ Assembly& Assembly::optimise(bool _enable)
rules.push_back({ { Push, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[1].data(), m[0].data()) }; } }); rules.push_back({ { Push, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[1].data(), m[0].data()) }; } });
rules.push_back({ { Push, i.first, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[2].data(), m[0].data()), i.first }; } }); rules.push_back({ { Push, i.first, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[2].data(), m[0].data()), i.first }; } });
} }
for (auto const& i: c_identities)
rules.push_back({{Push, i.first}, [&](AssemblyItemsConstRef m) -> AssemblyItems
{ return m[0].data() == i.second ? AssemblyItems() : m.toVector(); }});
// jump to next instruction // jump to next instruction
rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }}); rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }});
copt << *this;
unsigned total = 0; unsigned total = 0;
for (unsigned count = 1; count > 0; total += count) for (unsigned count = 1; count > 0; total += count)
{ {
copt << *this;
count = 0; count = 0;
copt << "Performing common subexpression elimination..."; copt << "Performing common subexpression elimination...";
@ -353,11 +335,22 @@ Assembly& Assembly::optimise(bool _enable)
CommonSubexpressionEliminator eliminator; CommonSubexpressionEliminator eliminator;
auto orig = iter; auto orig = iter;
iter = eliminator.feedItems(iter, m_items.end()); iter = eliminator.feedItems(iter, m_items.end());
AssemblyItems optItems = eliminator.getOptimizedItems(); AssemblyItems optItems;
copt << "Old size: " << (iter - orig) << ", new size: " << optItems.size(); bool shouldReplace = false;
if (optItems.size() < size_t(iter - orig)) try
{
optItems = eliminator.getOptimizedItems();
shouldReplace = (optItems.size() < size_t(iter - orig));
}
catch (StackTooDeepException const&)
{
// This might happen if the opcode reconstruction is not as efficient
// as the hand-crafted code.
}
if (shouldReplace)
{ {
// replace items copt << "Old size: " << (iter - orig) << ", new size: " << optItems.size();
count++; count++;
for (auto moveIter = optItems.begin(); moveIter != optItems.end(); ++orig, ++moveIter) for (auto moveIter = optItems.begin(); moveIter != optItems.end(); ++orig, ++moveIter)
*orig = move(*moveIter); *orig = move(*moveIter);

12
libevmcore/CommonSubexpressionEliminator.cpp

@ -157,10 +157,8 @@ ExpressionClasses::Id CommonSubexpressionEliminator::getStackElement(int _stackH
return m_stackElements[make_pair(_stackHeight, nextSequence - 1)]; return m_stackElements[make_pair(_stackHeight, nextSequence - 1)];
// Stack element not found (not assigned yet), create new equivalence class. // Stack element not found (not assigned yet), create new equivalence class.
if (_stackHeight > 0) assertThrow(_stackHeight <= 0, OptimizerException, "Stack element accessed before assignment.");
BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Stack element accessed before assignment.")); assertThrow(_stackHeight > -16, StackTooDeepException, "");
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. // This is a special assembly item that refers to elements pre-existing on the initial stack.
return m_stackElements[make_pair(_stackHeight, nextSequence)] = return m_stackElements[make_pair(_stackHeight, nextSequence)] =
m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight))); m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight)));
@ -423,7 +421,8 @@ bool CSECodeGenerator::removeStackTopIfPossible()
void CSECodeGenerator::appendDup(int _fromPosition) void CSECodeGenerator::appendDup(int _fromPosition)
{ {
int nr = 1 + m_stackHeight - _fromPosition; int nr = 1 + m_stackHeight - _fromPosition;
assertThrow(1 <= nr && nr <= 16, OptimizerException, "Stack too deep."); assertThrow(nr <= 16, StackTooDeepException, "Stack too deep.");
assertThrow(1 <= nr, OptimizerException, "Invalid stack access.");
m_generatedItems.push_back(AssemblyItem(dupInstruction(nr))); m_generatedItems.push_back(AssemblyItem(dupInstruction(nr)));
m_stackHeight++; m_stackHeight++;
m_stack[m_stackHeight] = m_stack[_fromPosition]; m_stack[m_stackHeight] = m_stack[_fromPosition];
@ -434,7 +433,8 @@ void CSECodeGenerator::appendSwapOrRemove(int _fromPosition)
if (_fromPosition == m_stackHeight) if (_fromPosition == m_stackHeight)
return; return;
int nr = m_stackHeight - _fromPosition; int nr = m_stackHeight - _fromPosition;
assertThrow(1 <= nr && nr <= 16, OptimizerException, "Stack too deep."); assertThrow(nr <= 16, StackTooDeepException, "Stack too deep.");
assertThrow(1 <= nr, OptimizerException, "Invalid stack access.");
m_generatedItems.push_back(AssemblyItem(swapInstruction(nr))); m_generatedItems.push_back(AssemblyItem(swapInstruction(nr)));
// The value of a class can be present in multiple locations on the stack. We only update the // The value of a class can be present in multiple locations on the stack. We only update the
// "canonical" one that is tracked by m_classPositions // "canonical" one that is tracked by m_classPositions

1
libevmcore/Exceptions.h

@ -32,6 +32,7 @@ struct AssemblyException: virtual Exception {};
struct InvalidDeposit: virtual AssemblyException {}; struct InvalidDeposit: virtual AssemblyException {};
struct InvalidOpcode: virtual AssemblyException {}; struct InvalidOpcode: virtual AssemblyException {};
struct OptimizerException: virtual AssemblyException {}; struct OptimizerException: virtual AssemblyException {};
struct StackTooDeepException: virtual OptimizerException {};
} }
} }

226
libevmcore/ExpressionClasses.cpp

@ -22,6 +22,9 @@
*/ */
#include <libevmcore/ExpressionClasses.h> #include <libevmcore/ExpressionClasses.h>
#include <utility>
#include <tuple>
#include <functional>
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
#include <libevmcore/Assembly.h> #include <libevmcore/Assembly.h>
#include <libevmcore/CommonSubexpressionEliminator.h> #include <libevmcore/CommonSubexpressionEliminator.h>
@ -31,22 +34,26 @@ using namespace dev;
using namespace dev::eth; 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. auto type = item->type();
// - check for the presence of constants in the argument classes and do arithmetic auto otherType = _other.item->type();
// - check whether the two items are equal for a SUB instruction return std::tie(type, item->data(), arguments) <
// - check whether 0 or 1 is in one of the classes for a MUL std::tie(otherType, _other.item->data(), _other.arguments);
}
ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids const& _arguments)
{
Expression exp; Expression exp;
exp.item = &_item; exp.item = &_item;
exp.arguments = _arguments; exp.arguments = _arguments;
if (SemanticInformation::isCommutativeOperation(_item)) if (SemanticInformation::isCommutativeOperation(_item))
sort(exp.arguments.begin(), exp.arguments.end()); 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) 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; return e.id;
if (SemanticInformation::isDupInstruction(_item)) 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)); m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(_item));
exp.item = m_spareAssemblyItem.back().get(); 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 default:
// an equivalence class 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); }; * Simplification rule. If _strict is false, Push or a constant matches any constant,
if (exp.arguments.size() == 2 && all_of(exp.arguments.begin(), exp.arguments.end(), isConstant)) * 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 (!_expr.item->match(pattern.front()))
{ return false;
if (a >= 31) assertThrow(_expr.arguments.size() == pattern.size() - 1, OptimizerException, "");
return b; Id argRequiredToBeEqual(-1);
unsigned testBit = unsigned(a) * 8 + 7; for (size_t i = 1; i < pattern.size(); ++i)
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; } }, Id arg = _expr.arguments[i - 1];
{ Instruction::DIV, [](u256 a, u256 b) -> u256 {return b == 0 ? 0 : a / b; } }, if (pattern[i].type() == UndefinedItem)
{ 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; } }, if (argRequiredToBeEqual == Id(-1))
{ Instruction::SMOD, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : s2u(u2s(a) % u2s(b)); } }, argRequiredToBeEqual = arg;
{ Instruction::EXP, [](u256 a, u256 b) -> u256 { return (u256)boost::multiprecision::powm(bigint(a), bigint(b), bigint(1) << 256); } }, else if (argRequiredToBeEqual != arg)
{ Instruction::SIGNEXTEND, signextend }, return false;
{ Instruction::LT, [](u256 a, u256 b) -> u256 { return a < b ? 1 : 0; } }, }
{ Instruction::GT, [](u256 a, u256 b) -> u256 { return a > b ? 1 : 0; } }, else
{ 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; } }, AssemblyItem const& argItem = *_classes.representative(arg).item;
{ Instruction::EQ, [](u256 a, u256 b) -> u256 { return a == b ? 1 : 0; } }, if (strict && argItem != pattern[i])
{ Instruction::ADD, [](u256 a, u256 b) -> u256 { return a + b; } }, return false;
{ Instruction::MUL, [](u256 a, u256 b) -> u256 { return a * b; } }, else if (!strict && !argItem.match(pattern[i]))
{ Instruction::AND, [](u256 a, u256 b) -> u256 { return a & b; } }, return false;
{ Instruction::OR, [](u256 a, u256 b) -> u256 { return a | b; } }, }
{ Instruction::XOR, [](u256 a, u256 b) -> u256 { return a ^ b; } }, }
}; return true;
if (arithmetics.count(_item.instruction())) }
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())( m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(rule.assemblyItemAction()));
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()); 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;
}

7
libevmcore/ExpressionClasses.h

@ -24,7 +24,7 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <set> #include <map>
#include <memory> #include <memory>
namespace dev namespace dev
@ -49,6 +49,7 @@ public:
Id id; Id id;
AssemblyItem const* item; AssemblyItem const* item;
Ids arguments; Ids arguments;
bool operator<(Expression const& _other) const;
}; };
/// Retrieves the id of the expression equivalence class resulting from the given item applied to the /// Retrieves the id of the expression equivalence class resulting from the given item applied to the
@ -60,6 +61,10 @@ public:
Id size() const { return m_representatives.size(); } Id size() const { return m_representatives.size(); }
private: private:
/// Tries to simplify the given expression.
/// @returns its class if it possible or Id(-1) otherwise.
/// @param _secondRun is set to true for the second run where arguments of commutative expressions are reversed
Id tryToSimplify(Expression const& _expr, bool _secondRun = false);
/// Expression equivalence class representatives - we only store one item of an equivalence. /// Expression equivalence class representatives - we only store one item of an equivalence.
std::vector<Expression> m_representatives; std::vector<Expression> m_representatives;

78
test/SolidityOptimizer.cpp

@ -74,6 +74,14 @@ public:
"\nOptimized: " + toHex(optimizedOutput)); "\nOptimized: " + toHex(optimizedOutput));
} }
void checkCSE(AssemblyItems const& _input, AssemblyItems const& _expectation)
{
eth::CommonSubexpressionEliminator cse;
BOOST_REQUIRE(cse.feedItems(_input.begin(), _input.end()) == _input.end());
AssemblyItems output = cse.getOptimizedItems();
BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end());
}
protected: protected:
Address m_optimizedContract; Address m_optimizedContract;
Address m_nonOptimizedContract; Address m_nonOptimizedContract;
@ -199,61 +207,59 @@ BOOST_AUTO_TEST_CASE(cse_intermediate_swap)
BOOST_AUTO_TEST_CASE(cse_negative_stack_access) BOOST_AUTO_TEST_CASE(cse_negative_stack_access)
{ {
eth::CommonSubexpressionEliminator cse; AssemblyItems input{Instruction::DUP2, u256(0)};
AssemblyItems input{AssemblyItem(Instruction::DUP2), AssemblyItem(u256(0))}; checkCSE(input, input);
BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end());
AssemblyItems output = cse.getOptimizedItems();
BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end());
} }
BOOST_AUTO_TEST_CASE(cse_negative_stack_end) BOOST_AUTO_TEST_CASE(cse_negative_stack_end)
{ {
eth::CommonSubexpressionEliminator cse; AssemblyItems input{Instruction::ADD};
AssemblyItems input{ checkCSE(input, input);
AssemblyItem(Instruction::ADD)
};
BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end());
AssemblyItems output = cse.getOptimizedItems();
BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end());
} }
BOOST_AUTO_TEST_CASE(cse_intermediate_negative_stack) BOOST_AUTO_TEST_CASE(cse_intermediate_negative_stack)
{ {
eth::CommonSubexpressionEliminator cse; AssemblyItems input{Instruction::ADD, u256(1), Instruction::DUP1};
AssemblyItems input{ checkCSE(input, input);
AssemblyItem(Instruction::ADD),
AssemblyItem(u256(1)),
AssemblyItem(Instruction::DUP2)
};
BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end());
AssemblyItems output = cse.getOptimizedItems();
BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end());
} }
BOOST_AUTO_TEST_CASE(cse_pop) BOOST_AUTO_TEST_CASE(cse_pop)
{ {
eth::CommonSubexpressionEliminator cse; checkCSE({Instruction::POP}, {Instruction::POP});
}
BOOST_AUTO_TEST_CASE(cse_unneeded_items)
{
AssemblyItems input{ AssemblyItems input{
AssemblyItem(Instruction::POP) Instruction::ADD,
Instruction::SWAP1,
Instruction::POP,
u256(7),
u256(8),
}; };
BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); checkCSE(input, input);
AssemblyItems output = cse.getOptimizedItems();
BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end());
} }
BOOST_AUTO_TEST_CASE(cse_unneeded_items) BOOST_AUTO_TEST_CASE(cse_invariants)
{ {
eth::CommonSubexpressionEliminator cse;
AssemblyItems input{ AssemblyItems input{
AssemblyItem(Instruction::ADD), Instruction::DUP1,
AssemblyItem(Instruction::SWAP1), Instruction::DUP1,
AssemblyItem(Instruction::POP), u256(0),
AssemblyItem(u256(7)), Instruction::OR,
AssemblyItem(u256(8)), Instruction::OR
}; };
BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); checkCSE(input, {Instruction::DUP1});
AssemblyItems output = cse.getOptimizedItems(); }
BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end());
BOOST_AUTO_TEST_CASE(cse_subself)
{
checkCSE({Instruction::DUP1, Instruction::SUB}, {Instruction::POP, u256(0)});
}
BOOST_AUTO_TEST_CASE(cse_subother)
{
checkCSE({Instruction::SUB}, {Instruction::SUB});
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

Loading…
Cancel
Save