Browse Source

Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop

cl-refactor
Gav Wood 10 years ago
parent
commit
fc569d8dab
  1. 2
      CodingStandards.txt
  2. 1
      alethzero/MainWin.cpp
  3. 5
      libdevcore/CommonJS.h
  4. 15
      libevmcore/Assembly.cpp
  5. 299
      libevmcore/CommonSubexpressionEliminator.cpp
  6. 84
      libevmcore/CommonSubexpressionEliminator.h
  7. 96
      libevmcore/ExpressionClasses.cpp
  8. 20
      libevmcore/ExpressionClasses.h
  9. 49
      libp2p/Host.cpp
  10. 2
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  11. 19
      mix/style.xml
  12. 265
      test/SolidityOptimizer.cpp
  13. 9
      test/peer.cpp

2
CodingStandards.txt

@ -13,7 +13,7 @@ c. Don't use braces for condition-body one-liners.
d. Never place condition bodies on same line as condition. d. Never place condition bodies on same line as condition.
e. Space between first paren and keyword, but *not* following first paren or preceeding final paren. e. Space between first paren and keyword, but *not* following first paren or preceeding final paren.
f. No spaces when fewer than intra-expression three parens together; when three or more, space according to clarity. f. No spaces when fewer than intra-expression three parens together; when three or more, space according to clarity.
g. No spaces for subscripting. g. No spaces for subscripting or unary operators.
h. No space before ':' but one after it, except in the ternary operator: one on both sides. h. No space before ':' but one after it, except in the ternary operator: one on both sides.
i. Space all other operators. i. Space all other operators.
j. Braces, when used, always have their own lines and are at same indentation level as "parent" scope. j. Braces, when used, always have their own lines and are at same indentation level as "parent" scope.

1
alethzero/MainWin.cpp

@ -1774,6 +1774,7 @@ void Main::on_net_triggered()
else else
{ {
ui->downloadView->setDownloadMan(nullptr); ui->downloadView->setDownloadMan(nullptr);
writeSettings();
web3()->stopNetwork(); web3()->stopNetwork();
} }
} }

5
libdevcore/CommonJS.h

@ -38,7 +38,10 @@ template <unsigned S> std::string toJS(FixedHash<S> const& _h)
template <unsigned N> std::string toJS(boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N, N, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>> const& _n) template <unsigned N> std::string toJS(boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N, N, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>> const& _n)
{ {
return "0x" + toHex(toCompactBigEndian(_n, 1)); std::string h = toHex(toCompactBigEndian(_n, 1));
// remove first 0, if it is necessary;
std::string res = h[0] != '0' ? h : h.substr(1);
return "0x" + res;
} }
inline std::string toJS(bytes const& _n, std::size_t _padding = 0) inline std::string toJS(bytes const& _n, std::size_t _padding = 0)

15
libevmcore/Assembly.cpp

@ -187,18 +187,7 @@ Assembly& Assembly::optimise(bool _enable)
{ {
if (!_enable) if (!_enable)
return *this; return *this;
std::vector<pair<AssemblyItems, function<AssemblyItems(AssemblyItemsConstRef)>>> rules = std::vector<pair<AssemblyItems, function<AssemblyItems(AssemblyItemsConstRef)>>> rules;
{
{ { Push, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushTag, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushString, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushSub, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushSubSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushProgramSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { Push, PushTag, Instruction::JUMPI }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data()) return { m[1], Instruction::JUMP }; else return {}; } },
{ { Instruction::ISZERO, Instruction::ISZERO }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
};
// 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(); }});
@ -235,8 +224,6 @@ Assembly& Assembly::optimise(bool _enable)
*orig = move(*moveIter); *orig = move(*moveIter);
iter = m_items.erase(orig, iter); iter = m_items.erase(orig, iter);
} }
if (iter != m_items.end())
++iter;
} }
for (unsigned i = 0; i < m_items.size(); ++i) for (unsigned i = 0; i < m_items.size(); ++i)

299
libevmcore/CommonSubexpressionEliminator.cpp

@ -32,6 +32,8 @@ using namespace dev::eth;
vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems() vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
{ {
optimizeBreakingItem();
map<int, ExpressionClasses::Id> initialStackContents; map<int, ExpressionClasses::Id> initialStackContents;
map<int, ExpressionClasses::Id> targetStackContents; map<int, ExpressionClasses::Id> targetStackContents;
int minHeight = m_stackHeight + 1; int minHeight = m_stackHeight + 1;
@ -45,19 +47,27 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
// Debug info: // Debug info:
//stream(cout, initialStackContents, targetStackContents); //stream(cout, initialStackContents, targetStackContents);
return CSECodeGenerator(m_expressionClasses).generateCode(initialStackContents, targetStackContents); AssemblyItems items = CSECodeGenerator(m_expressionClasses, m_storeOperations).generateCode(
initialStackContents,
targetStackContents
);
if (m_breakingItem)
items.push_back(*m_breakingItem);
return items;
} }
ostream& CommonSubexpressionEliminator::stream( ostream& CommonSubexpressionEliminator::stream(
ostream& _out, ostream& _out,
map<int, ExpressionClasses::Id> _currentStack, map<int, ExpressionClasses::Id> _initialStack,
map<int, ExpressionClasses::Id> _targetStack map<int, ExpressionClasses::Id> _targetStack
) const ) const
{ {
auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id) auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id)
{ {
auto const& expr = m_expressionClasses.representative(_id); auto const& expr = m_expressionClasses.representative(_id);
_out << " " << _id << ": " << *expr.item; _out << " " << dec << _id << ": " << *expr.item;
if (expr.sequenceNumber)
_out << "@" << dec << expr.sequenceNumber;
_out << "("; _out << "(";
for (ExpressionClasses::Id arg: expr.arguments) for (ExpressionClasses::Id arg: expr.arguments)
_out << dec << arg << ","; _out << dec << arg << ",";
@ -66,18 +76,12 @@ ostream& CommonSubexpressionEliminator::stream(
_out << "Optimizer analysis:" << endl; _out << "Optimizer analysis:" << endl;
_out << "Final stack height: " << dec << m_stackHeight << endl; _out << "Final stack height: " << dec << m_stackHeight << endl;
_out << "Stack elements: " << endl;
for (auto const& it: m_stackElements)
{
_out << " " << dec << it.first << " = ";
streamExpressionClass(_out, it.second);
}
_out << "Equivalence classes: " << endl; _out << "Equivalence classes: " << endl;
for (ExpressionClasses::Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass) for (ExpressionClasses::Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass)
streamExpressionClass(_out, eqClass); streamExpressionClass(_out, eqClass);
_out << "Current stack: " << endl; _out << "Initial stack: " << endl;
for (auto const& it: _currentStack) for (auto const& it: _initialStack)
{ {
_out << " " << dec << it.first << ": "; _out << " " << dec << it.first << ": ";
streamExpressionClass(_out, it.second); streamExpressionClass(_out, it.second);
@ -92,13 +96,12 @@ ostream& CommonSubexpressionEliminator::stream(
return _out; return _out;
} }
void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _copyItem)
{ {
if (_item.type() != Operation) if (_item.type() != Operation)
{ {
if (_item.deposit() != 1) assertThrow(_item.deposit() == 1, InvalidDeposit, "");
BOOST_THROW_EXCEPTION(InvalidDeposit()); setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}, _copyItem));
setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}));
} }
else else
{ {
@ -119,12 +122,47 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item)
vector<ExpressionClasses::Id> 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] = stackElement(m_stackHeight - i); arguments[i] = stackElement(m_stackHeight - i);
setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments)); if (_item.instruction() == Instruction::SSTORE)
storeInStorage(arguments[0], arguments[1]);
else if (_item.instruction() == Instruction::SLOAD)
setStackElement(m_stackHeight + _item.deposit(), loadFromStorage(arguments[0]));
else if (_item.instruction() == Instruction::MSTORE)
storeInMemory(arguments[0], arguments[1]);
else if (_item.instruction() == Instruction::MLOAD)
setStackElement(m_stackHeight + _item.deposit(), loadFromMemory(arguments[0]));
else
setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments, _copyItem));
} }
m_stackHeight += _item.deposit(); m_stackHeight += _item.deposit();
} }
} }
void CommonSubexpressionEliminator::optimizeBreakingItem()
{
if (!m_breakingItem || *m_breakingItem != AssemblyItem(Instruction::JUMPI))
return;
using Id = ExpressionClasses::Id;
static AssemblyItem s_jump = Instruction::JUMP;
Id condition = stackElement(m_stackHeight - 1);
Id zero = m_expressionClasses.find(u256(0));
if (m_expressionClasses.knownToBeDifferent(condition, zero))
{
feedItem(Instruction::SWAP1, true);
feedItem(Instruction::POP, true);
m_breakingItem = &s_jump;
return;
}
Id negatedCondition = m_expressionClasses.find(Instruction::ISZERO, {condition});
if (m_expressionClasses.knownToBeDifferent(negatedCondition, zero))
{
feedItem(Instruction::POP, true);
feedItem(Instruction::POP, true);
m_breakingItem = nullptr;
}
}
void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class) void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class)
{ {
m_stackElements[_stackHeight] = _class; m_stackElements[_stackHeight] = _class;
@ -132,8 +170,7 @@ void CommonSubexpressionEliminator::setStackElement(int _stackHeight, Expression
void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _stackHeightB) void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _stackHeightB)
{ {
if (_stackHeightA == _stackHeightB) assertThrow(_stackHeightA != _stackHeightB, OptimizerException, "Swap on same stack elements.");
BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Swap on same stack elements."));
// ensure they are created // ensure they are created
stackElement(_stackHeightA); stackElement(_stackHeightA);
stackElement(_stackHeightB); stackElement(_stackHeightB);
@ -157,6 +194,60 @@ ExpressionClasses::Id CommonSubexpressionEliminator::initialStackElement(int _st
return m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight))); return m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight)));
} }
void CommonSubexpressionEliminator::storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value)
{
if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value)
// do not execute the storage if we know that the value is already there
return;
m_sequenceNumber++;
decltype(m_storageContent) storageContents;
// copy over values at points where we know that they are different from _slot
for (auto const& storageItem: m_storageContent)
if (m_expressionClasses.knownToBeDifferent(storageItem.first, _slot))
storageContents.insert(storageItem);
m_storageContent = move(storageContents);
ExpressionClasses::Id id = m_expressionClasses.find(Instruction::SSTORE, {_slot, _value}, true, m_sequenceNumber);
m_storeOperations.push_back(StoreOperation(StoreOperation::Storage, _slot, m_sequenceNumber, id));
m_storageContent[_slot] = _value;
// increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++;
}
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(ExpressionClasses::Id _slot)
{
if (m_storageContent.count(_slot))
return m_storageContent.at(_slot);
else
return m_storageContent[_slot] = m_expressionClasses.find(Instruction::SLOAD, {_slot}, true, m_sequenceNumber);
}
void CommonSubexpressionEliminator::storeInMemory(ExpressionClasses::Id _slot, ExpressionClasses::Id _value)
{
if (m_memoryContent.count(_slot) && m_memoryContent[_slot] == _value)
// do not execute the store if we know that the value is already there
return;
m_sequenceNumber++;
decltype(m_memoryContent) memoryContents;
// copy over values at points where we know that they are different from _slot by at least 32
for (auto const& memoryItem: m_memoryContent)
if (m_expressionClasses.knownToBeDifferentBy32(memoryItem.first, _slot))
memoryContents.insert(memoryItem);
m_memoryContent = move(memoryContents);
ExpressionClasses::Id id = m_expressionClasses.find(Instruction::MSTORE, {_slot, _value}, true, m_sequenceNumber);
m_storeOperations.push_back(StoreOperation(StoreOperation::Memory, _slot, m_sequenceNumber, id));
m_memoryContent[_slot] = _value;
// increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++;
}
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(ExpressionClasses::Id _slot)
{
if (m_memoryContent.count(_slot))
return m_memoryContent.at(_slot);
else
return m_memoryContent[_slot] = m_expressionClasses.find(Instruction::MLOAD, {_slot}, true, m_sequenceNumber);
}
bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item) bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item)
{ {
switch (_item.type()) switch (_item.type())
@ -179,7 +270,19 @@ bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item)
return false; return false;
if (_item.instruction() == Instruction::GAS || _item.instruction() == Instruction::PC) if (_item.instruction() == Instruction::GAS || _item.instruction() == Instruction::PC)
return true; // GAS and PC assume a specific order of opcodes return true; // GAS and PC assume a specific order of opcodes
if (_item.instruction() == Instruction::MSIZE)
return true; // msize is modified already by memory access, avoid that for now
if (_item.instruction() == Instruction::SHA3)
return true; //@todo: we have to compare sha3's not based on their memory addresses but on the memory content.
InstructionInfo info = instructionInfo(_item.instruction()); InstructionInfo info = instructionInfo(_item.instruction());
if (_item.instruction() == Instruction::SSTORE)
return false;
if (_item.instruction() == Instruction::MSTORE)
return false;
//@todo: We do not handle the following memory instructions for now:
// calldatacopy, codecopy, extcodecopy, mstore8,
// msize (note that msize also depends on memory read access)
// the second requirement will be lifted once it is implemented // the second requirement will be lifted once it is implemented
return info.sideEffects || info.args > 2; return info.sideEffects || info.args > 2;
} }
@ -218,6 +321,16 @@ bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16; return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16;
} }
CSECodeGenerator::CSECodeGenerator(
ExpressionClasses& _expressionClasses,
vector<CSECodeGenerator::StoreOperation> const& _storeOperations
):
m_expressionClasses(_expressionClasses)
{
for (auto const& store: _storeOperations)
m_storeOperations[make_pair(store.target, store.slot)].push_back(store);
}
AssemblyItems CSECodeGenerator::generateCode( AssemblyItems CSECodeGenerator::generateCode(
map<int, ExpressionClasses::Id> const& _initialStack, map<int, ExpressionClasses::Id> const& _initialStack,
map<int, ExpressionClasses::Id> const& _targetStackContents map<int, ExpressionClasses::Id> const& _targetStackContents
@ -230,26 +343,40 @@ AssemblyItems CSECodeGenerator::generateCode(
// @todo: provide information about the positions of copies of class elements // @todo: provide information about the positions of copies of class elements
// generate the dependency graph // generate the dependency graph starting from final storage and memory writes and target stack contents
for (auto const& p: m_storeOperations)
addDependencies(p.second.back().expression);
for (auto const& targetItem: _targetStackContents) for (auto const& targetItem: _targetStackContents)
{ {
m_finalClasses.insert(targetItem.second); m_finalClasses.insert(targetItem.second);
addDependencies(targetItem.second); addDependencies(targetItem.second);
} }
// generate the actual elements // store all needed sequenced expressions
set<pair<unsigned, ExpressionClasses::Id>> sequencedExpressions;
for (auto const& p: m_neededBy)
for (auto id: {p.first, p.second})
if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber)
sequencedExpressions.insert(make_pair(seqNr, id));
// Perform all operations on storage and memory in order, if they are needed.
for (auto const& seqAndId: sequencedExpressions)
if (!m_classPositions.count(seqAndId.second))
generateClassElement(seqAndId.second, true);
// generate the target stack elements
for (auto const& targetItem: _targetStackContents) for (auto const& targetItem: _targetStackContents)
{ {
removeStackTopIfPossible();
int position = generateClassElement(targetItem.second); int position = generateClassElement(targetItem.second);
assertThrow(position != c_invalidPosition, OptimizerException, "");
if (position == targetItem.first) if (position == targetItem.first)
continue; continue;
if (position < targetItem.first) if (position < targetItem.first)
// it is already at its target, we need another copy // it is already at its target, we need another copy
appendDup(position); appendDup(position);
else else
appendSwapOrRemove(position); appendOrRemoveSwap(position);
appendSwapOrRemove(targetItem.first); appendOrRemoveSwap(targetItem.first);
} }
// remove surplus elements // remove surplus elements
@ -270,23 +397,59 @@ AssemblyItems CSECodeGenerator::generateCode(
// neither initial no target stack, no change in height // neither initial no target stack, no change in height
finalHeight = 0; finalHeight = 0;
assertThrow(finalHeight == m_stackHeight, OptimizerException, "Incorrect final stack height."); assertThrow(finalHeight == m_stackHeight, OptimizerException, "Incorrect final stack height.");
return m_generatedItems; return m_generatedItems;
} }
void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c) void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c)
{ {
if (m_neededBy.count(_c)) if (m_neededBy.count(_c))
return; return; // we already computed the dependencies for _c
for (ExpressionClasses::Id argument: m_expressionClasses.representative(_c).arguments) ExpressionClasses::Expression expr = m_expressionClasses.representative(_c);
for (ExpressionClasses::Id argument: expr.arguments)
{ {
addDependencies(argument); addDependencies(argument);
m_neededBy.insert(make_pair(argument, _c)); m_neededBy.insert(make_pair(argument, _c));
} }
if (expr.item->type() == Operation && (
expr.item->instruction() == Instruction::SLOAD ||
expr.item->instruction() == Instruction::MLOAD
))
{
// this loads an unknown value from storage or memory and thus, in addition to its
// arguments, depends on all store operations to addresses where we do not know that
// they are different that occur before this load
StoreOperation::Target target = expr.item->instruction() == Instruction::SLOAD ?
StoreOperation::Storage : StoreOperation::Memory;
ExpressionClasses::Id slotToLoadFrom = expr.arguments.at(0);
for (auto const& p: m_storeOperations)
{
if (p.first.first != target)
continue;
ExpressionClasses::Id slot = p.first.second;
StoreOperations const& storeOps = p.second;
if (storeOps.front().sequenceNumber > expr.sequenceNumber)
continue;
if (
(target == StoreOperation::Memory && m_expressionClasses.knownToBeDifferentBy32(slot, slotToLoadFrom)) ||
(target == StoreOperation::Storage && m_expressionClasses.knownToBeDifferent(slot, slotToLoadFrom))
)
continue;
// note that store and load never have the same sequence number
ExpressionClasses::Id latestStore = storeOps.front().expression;
for (auto it = ++storeOps.begin(); it != storeOps.end(); ++it)
if (it->sequenceNumber < expr.sequenceNumber)
latestStore = it->expression;
addDependencies(latestStore);
m_neededBy.insert(make_pair(latestStore, _c));
}
}
} }
int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c) int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allowSequenced)
{ {
// do some cleanup
removeStackTopIfPossible();
if (m_classPositions.count(_c)) if (m_classPositions.count(_c))
{ {
assertThrow( assertThrow(
@ -296,7 +459,13 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
); );
return m_classPositions[_c]; return m_classPositions[_c];
} }
ExpressionClasses::Ids const& arguments = m_expressionClasses.representative(_c).arguments; ExpressionClasses::Expression const& expr = m_expressionClasses.representative(_c);
assertThrow(
_allowSequenced || expr.sequenceNumber == 0,
OptimizerException,
"Sequence constrained operation requested out of sequence."
);
ExpressionClasses::Ids const& arguments = expr.arguments;
for (ExpressionClasses::Id arg: boost::adaptors::reverse(arguments)) for (ExpressionClasses::Id arg: boost::adaptors::reverse(arguments))
generateClassElement(arg); generateClassElement(arg);
@ -307,42 +476,42 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
if (arguments.size() == 1) if (arguments.size() == 1)
{ {
if (canBeRemoved(arguments[0], _c)) if (canBeRemoved(arguments[0], _c))
appendSwapOrRemove(generateClassElement(arguments[0])); appendOrRemoveSwap(classElementPosition(arguments[0]));
else else
appendDup(generateClassElement(arguments[0])); appendDup(classElementPosition(arguments[0]));
} }
else if (arguments.size() == 2) else if (arguments.size() == 2)
{ {
if (canBeRemoved(arguments[1], _c)) if (canBeRemoved(arguments[1], _c))
{ {
appendSwapOrRemove(generateClassElement(arguments[1])); appendOrRemoveSwap(classElementPosition(arguments[1]));
if (arguments[0] == arguments[1]) if (arguments[0] == arguments[1])
appendDup(m_stackHeight); appendDup(m_stackHeight);
else if (canBeRemoved(arguments[0], _c)) else if (canBeRemoved(arguments[0], _c))
{ {
appendSwapOrRemove(m_stackHeight - 1); appendOrRemoveSwap(m_stackHeight - 1);
appendSwapOrRemove(generateClassElement(arguments[0])); appendOrRemoveSwap(classElementPosition(arguments[0]));
} }
else else
appendDup(generateClassElement(arguments[0])); appendDup(classElementPosition(arguments[0]));
} }
else else
{ {
if (arguments[0] == arguments[1]) if (arguments[0] == arguments[1])
{ {
appendDup(generateClassElement(arguments[0])); appendDup(classElementPosition(arguments[0]));
appendDup(m_stackHeight); appendDup(m_stackHeight);
} }
else if (canBeRemoved(arguments[0], _c)) else if (canBeRemoved(arguments[0], _c))
{ {
appendSwapOrRemove(generateClassElement(arguments[0])); appendOrRemoveSwap(classElementPosition(arguments[0]));
appendDup(generateClassElement(arguments[1])); appendDup(classElementPosition(arguments[1]));
appendSwapOrRemove(m_stackHeight - 1); appendOrRemoveSwap(m_stackHeight - 1);
} }
else else
{ {
appendDup(generateClassElement(arguments[1])); appendDup(classElementPosition(arguments[1]));
appendDup(generateClassElement(arguments[0])); appendDup(classElementPosition(arguments[0]));
} }
} }
} }
@ -355,20 +524,41 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _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_expressionClasses.representative(_c).item; while (SemanticInformation::isCommutativeOperation(*expr.item) &&
while (SemanticInformation::isCommutativeOperation(item) &&
!m_generatedItems.empty() && !m_generatedItems.empty() &&
m_generatedItems.back() == AssemblyItem(Instruction::SWAP1)) m_generatedItems.back() == AssemblyItem(Instruction::SWAP1))
// this will not append a swap but remove the one that is already there // this will not append a swap but remove the one that is already there
appendSwapOrRemove(m_stackHeight - 1); appendOrRemoveSwap(m_stackHeight - 1);
for (auto arg: arguments) for (auto arg: arguments)
if (canBeRemoved(arg, _c)) if (canBeRemoved(arg, _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_expressionClasses.representative(_c).item); appendItem(*expr.item);
if (expr.item->type() != Operation || instructionInfo(expr.item->instruction()).ret == 1)
{
m_stack[m_stackHeight] = _c; m_stack[m_stackHeight] = _c;
return m_classPositions[_c] = m_stackHeight; return m_classPositions[_c] = m_stackHeight;
}
else
{
assertThrow(
instructionInfo(expr.item->instruction()).ret == 0,
OptimizerException,
"Invalid number of return values."
);
return m_classPositions[_c] = c_invalidPosition;
}
}
int CSECodeGenerator::classElementPosition(ExpressionClasses::Id _id) const
{
assertThrow(
m_classPositions.count(_id) && m_classPositions.at(_id) != c_invalidPosition,
OptimizerException,
"Element requested but is not present."
);
return m_classPositions.at(_id);
} }
bool CSECodeGenerator::canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result) bool CSECodeGenerator::canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result)
@ -401,22 +591,23 @@ bool CSECodeGenerator::removeStackTopIfPossible()
void CSECodeGenerator::appendDup(int _fromPosition) void CSECodeGenerator::appendDup(int _fromPosition)
{ {
int nr = 1 + m_stackHeight - _fromPosition; assertThrow(_fromPosition != c_invalidPosition, OptimizerException, "");
assertThrow(nr <= 16, StackTooDeepException, "Stack too deep."); int instructionNum = 1 + m_stackHeight - _fromPosition;
assertThrow(1 <= nr, OptimizerException, "Invalid stack access."); assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep.");
m_generatedItems.push_back(AssemblyItem(dupInstruction(nr))); assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
m_stackHeight++; appendItem(AssemblyItem(dupInstruction(instructionNum)));
m_stack[m_stackHeight] = m_stack[_fromPosition]; m_stack[m_stackHeight] = m_stack[_fromPosition];
} }
void CSECodeGenerator::appendSwapOrRemove(int _fromPosition) void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition)
{ {
assertThrow(_fromPosition != c_invalidPosition, OptimizerException, "");
if (_fromPosition == m_stackHeight) if (_fromPosition == m_stackHeight)
return; return;
int nr = m_stackHeight - _fromPosition; int instructionNum = m_stackHeight - _fromPosition;
assertThrow(nr <= 16, StackTooDeepException, "Stack too deep."); assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep.");
assertThrow(1 <= nr, OptimizerException, "Invalid stack access."); assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
m_generatedItems.push_back(AssemblyItem(swapInstruction(nr))); appendItem(AssemblyItem(swapInstruction(instructionNum)));
// 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
if (m_classPositions[m_stack[m_stackHeight]] == m_stackHeight) if (m_classPositions[m_stack[m_stackHeight]] == m_stackHeight)

84
libevmcore/CommonSubexpressionEliminator.h

@ -25,6 +25,8 @@
#include <vector> #include <vector>
#include <map> #include <map>
#include <set>
#include <tuple>
#include <ostream> #include <ostream>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
@ -44,9 +46,9 @@ using AssemblyItems = std::vector<AssemblyItem>;
* known to be equal only once. * known to be equal only once.
* *
* The general workings are that for each assembly item that is fed into the eliminator, an * 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 * equivalence class is derived from the operation and the equivalence class of its arguments.
* it is assigned to the next sequence number of a stack item. DUPi, SWAPi and some arithmetic * DUPi, SWAPi and some arithmetic instructions are used to infer equivalences while these
* instructions are used to infer equivalences while these classes are determined. * classes are determined.
* *
* When the list of optimized items is requested, they are generated in a bottom-up fashion, * 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. * adding code for equivalence classes that were not yet computed.
@ -54,6 +56,21 @@ using AssemblyItems = std::vector<AssemblyItem>;
class CommonSubexpressionEliminator class CommonSubexpressionEliminator
{ {
public: public:
struct StoreOperation
{
enum Target { Memory, Storage };
StoreOperation(
Target _target,
ExpressionClasses::Id _slot,
unsigned _sequenceNumber,
ExpressionClasses::Id _expression
): target(_target), slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {}
Target target;
ExpressionClasses::Id slot;
unsigned sequenceNumber;
ExpressionClasses::Id expression;
};
/// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first /// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first
/// item that must be fed into a new instance of the eliminator. /// item that must be fed into a new instance of the eliminator.
template <class _AssemblyItemIterator> template <class _AssemblyItemIterator>
@ -65,13 +82,16 @@ 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, ExpressionClasses::Id> _currentStack = std::map<int, ExpressionClasses::Id>(), std::map<int, ExpressionClasses::Id> _initialStack = std::map<int, ExpressionClasses::Id>(),
std::map<int, ExpressionClasses::Id> _targetStack = std::map<int, ExpressionClasses::Id>() 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, bool _copyItem = false);
/// Tries to optimize the item that breaks the basic block at the end.
void optimizeBreakingItem();
/// Simplifies the given item using /// 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.
@ -85,12 +105,38 @@ private:
/// (must not be positive). /// (must not be positive).
ExpressionClasses::Id initialStackElement(int _stackHeight); ExpressionClasses::Id initialStackElement(int _stackHeight);
/// Increments the sequence number, deletes all storage information that might be overwritten
/// and stores the new value at the given slot.
void storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value);
/// Retrieves the current value at the given slot in storage or creates a new special sload class.
ExpressionClasses::Id loadFromStorage(ExpressionClasses::Id _slot);
/// Increments the sequence number, deletes all memory information that might be overwritten
/// and stores the new value at the given slot.
void storeInMemory(ExpressionClasses::Id _slot, ExpressionClasses::Id _value);
/// Retrieves the current value at the given slot in memory or creates a new special mload class.
ExpressionClasses::Id loadFromMemory(ExpressionClasses::Id _slot);
/// Current stack height, can be negative. /// Current stack height, can be negative.
int m_stackHeight = 0; int m_stackHeight = 0;
/// Current stack layout, mapping stack height -> equivalence class /// Current stack layout, mapping stack height -> equivalence class
std::map<int, ExpressionClasses::Id> m_stackElements; std::map<int, ExpressionClasses::Id> m_stackElements;
/// Current sequence number, this is incremented with each modification to storage or memory.
unsigned m_sequenceNumber = 1;
/// Knowledge about storage content.
std::map<ExpressionClasses::Id, ExpressionClasses::Id> m_storageContent;
/// Knowledge about memory content. Keys are memory addresses, note that the values overlap
/// and are not contained here if they are not completely known.
std::map<ExpressionClasses::Id, ExpressionClasses::Id> m_memoryContent;
/// Keeps information about which storage or memory slots were written to at which sequence
/// number with what instruction.
std::vector<StoreOperation> m_storeOperations;
/// Structure containing the classes of equivalent expressions. /// Structure containing the classes of equivalent expressions.
ExpressionClasses m_expressionClasses; ExpressionClasses m_expressionClasses;
/// The item that breaks the basic block, can be nullptr.
/// It is usually appended to the block but can be optimized in some cases.
AssemblyItem const* m_breakingItem = nullptr;
}; };
/** /**
@ -114,14 +160,16 @@ struct SemanticInformation
class CSECodeGenerator class CSECodeGenerator
{ {
public: public:
CSECodeGenerator(ExpressionClasses const& _expressionClasses): using StoreOperation = CommonSubexpressionEliminator::StoreOperation;
m_expressionClasses(_expressionClasses) using StoreOperations = std::vector<StoreOperation>;
{}
/// Initializes the code generator with the given classes and store operations.
/// The store operations have to be sorted by sequence number in ascending order.
CSECodeGenerator(ExpressionClasses& _expressionClasses, StoreOperations const& _storeOperations);
/// @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
/// @note should only be called once on each object. /// @note should only be called once on each object.
AssemblyItems generateCode( AssemblyItems generateCode(
std::map<int, ExpressionClasses::Id> const& _initialStack, std::map<int, ExpressionClasses::Id> const& _initialStack,
@ -133,8 +181,13 @@ private:
void addDependencies(ExpressionClasses::Id _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 or c_invalidPosition if it does not actually
int generateClassElement(ExpressionClasses::Id _c); /// generate a value on the stack.
/// @param _allowSequenced indicates that sequence-constrained operations are allowed
int generateClassElement(ExpressionClasses::Id _c, bool _allowSequenced = false);
/// @returns the position of the representative of the given id on the stack.
/// @note throws an exception if it is not on the stack.
int classElementPosition(ExpressionClasses::Id _id) const;
/// @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(ExpressionClasses::Id _element, ExpressionClasses::Id _result = ExpressionClasses::Id(-1)); bool canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result = ExpressionClasses::Id(-1));
@ -146,7 +199,7 @@ private:
void appendDup(int _fromPosition); void appendDup(int _fromPosition);
/// Appends a swap instruction to m_generatedItems to retrieve the element at the given stack position. /// Appends a swap instruction to m_generatedItems to retrieve the element at the given stack position.
/// @note this might also remove the last item if it exactly the same swap instruction. /// @note this might also remove the last item if it exactly the same swap instruction.
void appendSwapOrRemove(int _fromPosition); void appendOrRemoveSwap(int _fromPosition);
/// Appends the given assembly item. /// Appends the given assembly item.
void appendItem(AssemblyItem const& _item); void appendItem(AssemblyItem const& _item);
@ -163,7 +216,10 @@ private:
std::map<ExpressionClasses::Id, 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.
ExpressionClasses const& m_expressionClasses; ExpressionClasses& m_expressionClasses;
/// Keeps information about which storage or memory slots were written to by which operations.
/// The operations are sorted ascendingly by sequence number.
std::map<std::pair<StoreOperation::Target, ExpressionClasses::Id>, StoreOperations> m_storeOperations;
/// 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<ExpressionClasses::Id> m_finalClasses; std::set<ExpressionClasses::Id> m_finalClasses;
}; };
@ -176,6 +232,8 @@ _AssemblyItemIterator CommonSubexpressionEliminator::feedItems(
{ {
for (; _iterator != _end && !SemanticInformation::breaksBasicBlock(*_iterator); ++_iterator) for (; _iterator != _end && !SemanticInformation::breaksBasicBlock(*_iterator); ++_iterator)
feedItem(*_iterator); feedItem(*_iterator);
if (_iterator != _end)
m_breakingItem = &(*_iterator++);
return _iterator; return _iterator;
} }

96
libevmcore/ExpressionClasses.cpp

@ -39,41 +39,71 @@ bool ExpressionClasses::Expression::operator<(ExpressionClasses::Expression cons
{ {
auto type = item->type(); auto type = item->type();
auto otherType = _other.item->type(); auto otherType = _other.item->type();
return std::tie(type, item->data(), arguments) < return std::tie(type, item->data(), arguments, sequenceNumber) <
std::tie(otherType, _other.item->data(), _other.arguments); std::tie(otherType, _other.item->data(), _other.arguments, _other.sequenceNumber);
} }
ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids const& _arguments) ExpressionClasses::Id ExpressionClasses::find(
AssemblyItem const& _item,
Ids const& _arguments,
bool _copyItem,
unsigned _sequenceNumber
)
{ {
Expression exp; Expression exp;
exp.id = Id(-1); exp.id = Id(-1);
exp.item = &_item; exp.item = &_item;
exp.arguments = _arguments; exp.arguments = _arguments;
exp.sequenceNumber = _sequenceNumber;
if (SemanticInformation::isCommutativeOperation(_item)) if (SemanticInformation::isCommutativeOperation(_item))
sort(exp.arguments.begin(), exp.arguments.end()); sort(exp.arguments.begin(), exp.arguments.end());
//@todo store all class members (not only the representatives) in an efficient data structure to search here auto it = m_expressions.find(exp);
for (Expression const& e: m_representatives) if (it != m_expressions.end())
if (!(e < exp || exp < e)) return it->id;
return e.id;
if (SemanticInformation::isDupInstruction(_item)) if (_copyItem)
{ {
// Special item that refers to values pre-existing on the stack
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();
} }
ExpressionClasses::Id id = tryToSimplify(exp); ExpressionClasses::Id id = tryToSimplify(exp);
if (id < m_representatives.size()) if (id < m_representatives.size())
return id; exp.id = id;
else
{
exp.id = m_representatives.size(); exp.id = m_representatives.size();
m_representatives.push_back(exp); m_representatives.push_back(exp);
}
m_expressions.insert(exp);
return exp.id; return exp.id;
} }
bool ExpressionClasses::knownToBeDifferent(ExpressionClasses::Id _a, ExpressionClasses::Id _b)
{
// Try to simplify "_a - _b" and return true iff the value is a non-zero constant.
map<unsigned, Expression const*> matchGroups;
Pattern constant(Push);
constant.setMatchGroup(1, matchGroups);
Id difference = find(Instruction::SUB, {_a, _b});
return constant.matches(representative(difference), *this) && constant.d() != u256(0);
}
bool ExpressionClasses::knownToBeDifferentBy32(ExpressionClasses::Id _a, ExpressionClasses::Id _b)
{
// Try to simplify "_a - _b" and return true iff the value is at least 32 away from zero.
map<unsigned, Expression const*> matchGroups;
Pattern constant(Push);
constant.setMatchGroup(1, matchGroups);
Id difference = find(Instruction::SUB, {_a, _b});
if (!constant.matches(representative(difference), *this))
return false;
// forbidden interval is ["-31", 31]
return constant.d() + 31 > u256(62);
}
string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const
{ {
Expression const& expr = representative(_id); Expression const& expr = representative(_id);
@ -189,27 +219,46 @@ Rules::Rules()
// Moving constants to the outside, order matters here! // Moving constants to the outside, order matters here!
// we need actions that return expressions (or patterns?) here, and we need also reversed rules // we need actions that return expressions (or patterns?) here, and we need also reversed rules
// (X+A)+B -> X+(A+B) // (X+A)+B -> X+(A+B)
m_rules.push_back({ m_rules += vector<pair<Pattern, function<Pattern()>>>{{
{op, {{op, {X, A}}, B}}, {op, {{op, {X, A}}, B}},
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; } [=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }
}); }, {
// X+(Y+A) -> (X+Y)+A // X+(Y+A) -> (X+Y)+A
m_rules.push_back({
{op, {{op, {X, A}}, Y}}, {op, {{op, {X, A}}, Y}},
[=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; } [=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; }
}); }, {
// For now, we still need explicit commutativity for the inner pattern // For now, we still need explicit commutativity for the inner pattern
m_rules.push_back({
{op, {{op, {A, X}}, B}}, {op, {{op, {A, X}}, B}},
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; } [=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }
}); }, {
m_rules.push_back({
{op, {{op, {A, X}}, Y}}, {op, {{op, {A, X}}, Y}},
[=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; } [=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; }
}); }};
}
// move constants across subtractions
m_rules += vector<pair<Pattern, function<Pattern()>>>{
{
// X - A -> X + (-A)
{Instruction::SUB, {X, A}},
[=]() -> Pattern { return {Instruction::ADD, {X, 0 - A.d()}}; }
}, {
// (X + A) - Y -> (X - Y) + A
{Instruction::SUB, {{Instruction::ADD, {X, A}}, Y}},
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; }
}, {
// (A + X) - Y -> (X - Y) + A
{Instruction::SUB, {{Instruction::ADD, {A, X}}, Y}},
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; }
}, {
// X - (Y + A) -> (X - Y) + (-A)
{Instruction::SUB, {X, {Instruction::ADD, {Y, A}}}},
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; }
}, {
// X - (A + Y) -> (X - Y) + (-A)
{Instruction::SUB, {X, {Instruction::ADD, {A, Y}}}},
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; }
}
}; };
//@todo: (x+8)-3 and other things
} }
ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr, bool _secondRun) ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr, bool _secondRun)
@ -231,7 +280,7 @@ ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr,
//cout << ")" << endl; //cout << ")" << endl;
//cout << "with rule " << rule.first.toString() << endl; //cout << "with rule " << rule.first.toString() << endl;
//ExpressionTemplate t(rule.second()); //ExpressionTemplate t(rule.second());
//cout << "to" << rule.second().toString() << endl; //cout << "to " << rule.second().toString() << endl;
return rebuildExpression(ExpressionTemplate(rule.second())); return rebuildExpression(ExpressionTemplate(rule.second()));
} }
} }
@ -254,8 +303,7 @@ ExpressionClasses::Id ExpressionClasses::rebuildExpression(ExpressionTemplate co
Ids arguments; Ids arguments;
for (ExpressionTemplate const& t: _template.arguments) for (ExpressionTemplate const& t: _template.arguments)
arguments.push_back(rebuildExpression(t)); arguments.push_back(rebuildExpression(t));
m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(_template.item)); return find(_template.item, arguments);
return find(*m_spareAssemblyItem.back(), arguments);
} }

20
libevmcore/ExpressionClasses.h

@ -52,17 +52,33 @@ public:
Id id; Id id;
AssemblyItem const* item; AssemblyItem const* item;
Ids arguments; Ids arguments;
unsigned sequenceNumber; ///< Storage modification sequence, only used for SLOAD/SSTORE instructions.
/// Behaves as if this was a tuple of (item->type(), item->data(), arguments, sequenceNumber).
bool operator<(Expression const& _other) const; 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
/// given classes, might also create a new one. /// given classes, might also create a new one.
Id find(AssemblyItem const& _item, Ids const& _arguments = {}); /// @param _copyItem if true, copies the assembly item to an internal storage instead of just
/// keeping a pointer.
/// The @a _sequenceNumber indicates the current storage or memory access sequence.
Id find(
AssemblyItem const& _item,
Ids const& _arguments = {},
bool _copyItem = true,
unsigned _sequenceNumber = 0
);
/// @returns the canonical representative of an expression class. /// @returns the canonical representative of an expression class.
Expression const& representative(Id _id) const { return m_representatives.at(_id); } Expression const& representative(Id _id) const { return m_representatives.at(_id); }
/// @returns the number of classes. /// @returns the number of classes.
Id size() const { return m_representatives.size(); } Id size() const { return m_representatives.size(); }
/// @returns true if the values of the given classes are known to be different (on every input).
/// @note that this function might still return false for some different inputs.
bool knownToBeDifferent(Id _a, Id _b);
/// Similar to @a knownToBeDifferent but require that abs(_a - b) >= 32.
bool knownToBeDifferentBy32(Id _a, Id _b);
std::string fullDAGToString(Id _id) const; std::string fullDAGToString(Id _id) const;
private: private:
@ -78,6 +94,8 @@ private:
/// 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;
/// All expression ever encountered.
std::set<Expression> m_expressions;
std::vector<std::shared_ptr<AssemblyItem>> m_spareAssemblyItem; std::vector<std::shared_ptr<AssemblyItem>> m_spareAssemblyItem;
}; };

49
libp2p/Host.cpp

@ -678,9 +678,6 @@ void Host::disconnectLatePeers()
bytes Host::saveNetwork() const bytes Host::saveNetwork() const
{ {
if (!m_nodeTable)
return bytes();
std::list<Peer> peers; std::list<Peer> peers;
{ {
RecursiveGuard l(x_sessions); RecursiveGuard l(x_sessions);
@ -692,29 +689,24 @@ bytes Host::saveNetwork() const
RLPStream network; RLPStream network;
int count = 0; int count = 0;
{
RecursiveGuard l(x_sessions);
for (auto const& p: peers) for (auto const& p: peers)
{ {
// TODO: alpha: Figure out why it ever shares these ports.//p.address.port() >= 30300 && p.address.port() <= 30305 &&
// TODO: alpha: if/how to save private addresses
// Only save peers which have connected within 2 days, with properly-advertised port and public IP address // Only save peers which have connected within 2 days, with properly-advertised port and public IP address
if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && p.peerEndpoint().port() > 0 && p.peerEndpoint().port() < /*49152*/32768 && p.id != id() && !isPrivateAddress(p.endpoint.udp.address()) && !isPrivateAddress(p.endpoint.tcp.address())) // todo: e2e ipv6 support
bi::tcp::endpoint endpoint(p.peerEndpoint());
if (!endpoint.address().is_v4())
continue;
if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && endpoint.port() > 0 && endpoint.port() < /*49152*/32768 && p.id != id() && !isPrivateAddress(p.endpoint.udp.address()) && !isPrivateAddress(endpoint.address()))
{ {
network.appendList(10); network.appendList(10);
if (p.peerEndpoint().address().is_v4()) network << endpoint.port() << p.id << p.required
network << p.peerEndpoint().address().to_v4().to_bytes();
else
network << p.peerEndpoint().address().to_v6().to_bytes();
// TODO: alpha: replace 0 with trust-state of node
network << p.peerEndpoint().port() << p.id << 0
<< chrono::duration_cast<chrono::seconds>(p.m_lastConnected.time_since_epoch()).count() << chrono::duration_cast<chrono::seconds>(p.m_lastConnected.time_since_epoch()).count()
<< chrono::duration_cast<chrono::seconds>(p.m_lastAttempted.time_since_epoch()).count() << chrono::duration_cast<chrono::seconds>(p.m_lastAttempted.time_since_epoch()).count()
<< p.m_failedAttempts << (unsigned)p.m_lastDisconnect << p.m_score << p.m_rating; << p.m_failedAttempts << (unsigned)p.m_lastDisconnect << p.m_score << p.m_rating;
count++; count++;
} }
} }
}
if (!!m_nodeTable) if (!!m_nodeTable)
{ {
@ -731,10 +723,13 @@ bytes Host::saveNetwork() const
count++; count++;
} }
} }
// else: TODO: use previous configuration if available
RLPStream ret(3); RLPStream ret(3);
ret << dev::p2p::c_protocolVersion << m_alias.secret(); ret << dev::p2p::c_protocolVersion << m_alias.secret();
ret.appendList(count).appendRaw(network.out(), count); ret.appendList(count);
if (!!count)
ret.appendRaw(network.out(), count);
return ret.out(); return ret.out();
} }
@ -757,22 +752,14 @@ void Host::restoreNetwork(bytesConstRef _b)
for (auto i: r[2]) for (auto i: r[2])
{ {
// todo: e2e ipv6 support
// bi::tcp::endpoint(bi::address_v6(i[0].toArray<byte, 16>()), i[1].toInt<short>());
if (i[0].itemCount() != 4)
continue;
bi::tcp::endpoint tcp; bi::tcp::endpoint tcp;
bi::udp::endpoint udp; bi::udp::endpoint udp;
if (i[0].itemCount() == 4)
{
tcp = bi::tcp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>()); tcp = bi::tcp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>());
udp = bi::udp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>()); udp = bi::udp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>());
}
else
{
tcp = bi::tcp::endpoint(bi::address_v6(i[0].toArray<byte, 16>()), i[1].toInt<short>());
udp = bi::udp::endpoint(bi::address_v6(i[0].toArray<byte, 16>()), i[1].toInt<short>());
}
// skip private addresses
// todo: to support private addresseses entries must be stored
// and managed externally by host rather than nodetable.
if (isPrivateAddress(tcp.address()) || isPrivateAddress(udp.address())) if (isPrivateAddress(tcp.address()) || isPrivateAddress(udp.address()))
continue; continue;
@ -783,6 +770,7 @@ void Host::restoreNetwork(bytesConstRef _b)
{ {
shared_ptr<Peer> p = make_shared<Peer>(); shared_ptr<Peer> p = make_shared<Peer>();
p->id = id; p->id = id;
p->required = i[3].toInt<bool>();
p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[4].toInt<unsigned>())); p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[4].toInt<unsigned>()));
p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt<unsigned>())); p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt<unsigned>()));
p->m_failedAttempts = i[6].toInt<unsigned>(); p->m_failedAttempts = i[6].toInt<unsigned>();
@ -792,6 +780,9 @@ void Host::restoreNetwork(bytesConstRef _b)
p->endpoint.tcp = tcp; p->endpoint.tcp = tcp;
p->endpoint.udp = udp; p->endpoint.udp = udp;
m_peers[p->id] = p; m_peers[p->id] = p;
if (p->required)
requirePeer(p->id, p->endpoint.udp.address(), p->endpoint.udp.port());
else
m_nodeTable->addNode(*p.get()); m_nodeTable->addNode(*p.get());
} }
} }
@ -801,7 +792,7 @@ void Host::restoreNetwork(bytesConstRef _b)
KeyPair Host::networkAlias(bytesConstRef _b) KeyPair Host::networkAlias(bytesConstRef _b)
{ {
RLP r(_b); RLP r(_b);
if (r.itemCount() == 3 && r[0].isInt() && r[0].toInt<int>() == 1) if (r.itemCount() == 3 && r[0].isInt() && r[0].toInt<unsigned>() == dev::p2p::c_protocolVersion)
return move(KeyPair(move(Secret(r[1].toBytes())))); return move(KeyPair(move(Secret(r[1].toBytes()))));
else else
return move(KeyPair::create()); return move(KeyPair::create());

2
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -402,7 +402,7 @@ string WebThreeStubServerBase::eth_getCode(string const& _address, string const&
{ {
try try
{ {
return toJS(client()->codeAt(jsToAddress(_address), toBlockNumber(_blockNumber)), 1); return toJS(client()->codeAt(jsToAddress(_address), toBlockNumber(_blockNumber)));
} }
catch (...) catch (...)
{ {

19
mix/style.xml

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorCodeStyle>
<!-- Written by QtCreator 3.3.2, 2015-03-31T14:55:07. -->
<qtcreator>
<data>
<variable>CodeStyleData</variable>
<valuemap type="QVariantMap">
<value type="bool" key="AutoSpacesForTabs">false</value>
<value type="int" key="IndentSize">4</value>
<value type="int" key="PaddingMode">2</value>
<value type="bool" key="SpacesForTabs">false</value>
<value type="int" key="TabSize">4</value>
</valuemap>
</data>
<data>
<variable>DisplayName</variable>
<value type="QString">Eth</value>
</data>
</qtcreator>

265
test/SolidityOptimizer.cpp

@ -303,6 +303,271 @@ BOOST_AUTO_TEST_CASE(cse_associativity2)
checkCSE(input, {Instruction::DUP2, Instruction::DUP2, Instruction::ADD, u256(5), Instruction::ADD}); checkCSE(input, {Instruction::DUP2, Instruction::DUP2, Instruction::ADD, u256(5), Instruction::ADD});
} }
BOOST_AUTO_TEST_CASE(cse_storage)
{
AssemblyItems input{
u256(0),
Instruction::SLOAD,
u256(0),
Instruction::SLOAD,
Instruction::ADD,
u256(0),
Instruction::SSTORE
};
checkCSE(input, {
u256(0),
Instruction::DUP1,
Instruction::SLOAD,
Instruction::DUP1,
Instruction::ADD,
Instruction::SWAP1,
Instruction::SSTORE
});
}
BOOST_AUTO_TEST_CASE(cse_noninterleaved_storage)
{
// two stores to the same location should be replaced by only one store, even if we
// read in the meantime
AssemblyItems input{
u256(7),
Instruction::DUP2,
Instruction::SSTORE,
Instruction::DUP1,
Instruction::SLOAD,
u256(8),
Instruction::DUP3,
Instruction::SSTORE
};
checkCSE(input, {
u256(8),
Instruction::DUP2,
Instruction::SSTORE,
u256(7)
});
}
BOOST_AUTO_TEST_CASE(cse_interleaved_storage)
{
// stores and reads to/from two unknown locations, should not optimize away the first store
AssemblyItems input{
u256(7),
Instruction::DUP2,
Instruction::SSTORE, // store to "DUP1"
Instruction::DUP2,
Instruction::SLOAD, // read from "DUP2", might be equal to "DUP1"
u256(0),
Instruction::DUP3,
Instruction::SSTORE // store different value to "DUP1"
};
checkCSE(input, input);
}
BOOST_AUTO_TEST_CASE(cse_interleaved_storage_same_value)
{
// stores and reads to/from two unknown locations, should not optimize away the first store
// but it should optimize away the second, since we already know the value will be the same
AssemblyItems input{
u256(7),
Instruction::DUP2,
Instruction::SSTORE, // store to "DUP1"
Instruction::DUP2,
Instruction::SLOAD, // read from "DUP2", might be equal to "DUP1"
u256(6),
u256(1),
Instruction::ADD,
Instruction::DUP3,
Instruction::SSTORE // store same value to "DUP1"
};
checkCSE(input, {
u256(7),
Instruction::DUP2,
Instruction::SSTORE,
Instruction::DUP2,
Instruction::SLOAD
});
}
BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location)
{
// stores and reads to/from two known locations, should optimize away the first store,
// because we know that the location is different
AssemblyItems input{
u256(0x70),
u256(1),
Instruction::SSTORE, // store to 1
u256(2),
Instruction::SLOAD, // read from 2, is different from 1
u256(0x90),
u256(1),
Instruction::SSTORE // store different value at 1
};
checkCSE(input, {
u256(2),
Instruction::SLOAD,
u256(0x90),
u256(1),
Instruction::SSTORE
});
}
BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location_offset)
{
// stores and reads to/from two locations which are known to be different,
// should optimize away the first store, because we know that the location is different
AssemblyItems input{
u256(0x70),
Instruction::DUP2,
u256(1),
Instruction::ADD,
Instruction::SSTORE, // store to "DUP1"+1
Instruction::DUP1,
u256(2),
Instruction::ADD,
Instruction::SLOAD, // read from "DUP1"+2, is different from "DUP1"+1
u256(0x90),
Instruction::DUP3,
u256(1),
Instruction::ADD,
Instruction::SSTORE // store different value at "DUP1"+1
};
checkCSE(input, {
u256(2),
Instruction::DUP2,
Instruction::ADD,
Instruction::SLOAD,
u256(0x90),
u256(1),
Instruction::DUP4,
Instruction::ADD,
Instruction::SSTORE
});
}
BOOST_AUTO_TEST_CASE(cse_interleaved_memory_at_known_location_offset)
{
// stores and reads to/from two locations which are known to be different,
// should not optimize away the first store, because the location overlaps with the load,
// but it should optimize away the second, because we know that the location is different by 32
AssemblyItems input{
u256(0x50),
Instruction::DUP2,
u256(2),
Instruction::ADD,
Instruction::MSTORE, // ["DUP1"+2] = 0x50
u256(0x60),
Instruction::DUP2,
u256(32),
Instruction::ADD,
Instruction::MSTORE, // ["DUP1"+32] = 0x60
Instruction::DUP1,
Instruction::MLOAD, // read from "DUP1"
u256(0x70),
Instruction::DUP3,
u256(32),
Instruction::ADD,
Instruction::MSTORE, // ["DUP1"+32] = 0x70
u256(0x80),
Instruction::DUP3,
u256(2),
Instruction::ADD,
Instruction::MSTORE, // ["DUP1"+2] = 0x80
};
// If the actual code changes too much, we could also simply check that the output contains
// exactly 3 MSTORE and exactly 1 MLOAD instruction.
checkCSE(input, {
u256(0x50),
u256(2),
Instruction::DUP3,
Instruction::ADD,
Instruction::SWAP1,
Instruction::DUP2,
Instruction::MSTORE, // ["DUP1"+2] = 0x50
Instruction::DUP2,
Instruction::MLOAD, // read from "DUP1"
u256(0x70),
u256(32),
Instruction::DUP5,
Instruction::ADD,
Instruction::MSTORE, // ["DUP1"+32] = 0x70
u256(0x80),
Instruction::SWAP1,
Instruction::SWAP2,
Instruction::MSTORE // ["DUP1"+2] = 0x80
});
}
BOOST_AUTO_TEST_CASE(cse_deep_stack)
{
AssemblyItems input{
Instruction::ADD,
Instruction::SWAP1,
Instruction::POP,
Instruction::SWAP8,
Instruction::POP,
Instruction::SWAP8,
Instruction::POP,
Instruction::SWAP8,
Instruction::SWAP5,
Instruction::POP,
Instruction::POP,
Instruction::POP,
Instruction::POP,
Instruction::POP,
};
checkCSE(input, {
Instruction::SWAP4,
Instruction::SWAP12,
Instruction::SWAP3,
Instruction::SWAP11,
Instruction::POP,
Instruction::SWAP1,
Instruction::SWAP3,
Instruction::ADD,
Instruction::SWAP8,
Instruction::POP,
Instruction::SWAP6,
Instruction::POP,
Instruction::POP,
Instruction::POP,
Instruction::POP,
Instruction::POP,
Instruction::POP,
});
}
BOOST_AUTO_TEST_CASE(cse_jumpi_no_jump)
{
AssemblyItems input{
u256(0),
u256(1),
Instruction::DUP2,
AssemblyItem(PushTag, 1),
Instruction::JUMPI
};
checkCSE(input, {
u256(0),
u256(1)
});
}
BOOST_AUTO_TEST_CASE(cse_jumpi_jump)
{
AssemblyItems input{
u256(1),
u256(1),
Instruction::DUP2,
AssemblyItem(PushTag, 1),
Instruction::JUMPI
};
checkCSE(input, {
u256(1),
Instruction::DUP1,
AssemblyItem(PushTag, 1),
Instruction::JUMP
});
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

9
test/peer.cpp

@ -57,6 +57,15 @@ BOOST_AUTO_TEST_CASE(host)
g_logVerbosity = oldLogVerbosity; g_logVerbosity = oldLogVerbosity;
} }
BOOST_AUTO_TEST_CASE(networkConfig)
{
Host save("Test", NetworkPreferences(false));
bytes store(save.saveNetwork());
Host restore("Test", NetworkPreferences(false), bytesConstRef(&store));
BOOST_REQUIRE(save.id() == restore.id());
}
BOOST_AUTO_TEST_CASE(save_nodes) BOOST_AUTO_TEST_CASE(save_nodes)
{ {
std::list<Host*> hosts; std::list<Host*> hosts;

Loading…
Cancel
Save