Browse Source

Try to keep source location during optimization.

cl-refactor
chriseth 10 years ago
parent
commit
a30da8b41b
  1. 13
      libevmcore/AssemblyItem.h
  2. 176
      libevmcore/CommonSubexpressionEliminator.cpp
  3. 20
      libevmcore/CommonSubexpressionEliminator.h
  4. 24
      libevmcore/ExpressionClasses.cpp
  5. 10
      libevmcore/ExpressionClasses.h
  6. 16
      test/libsolidity/SolidityOptimizer.cpp

13
libevmcore/AssemblyItem.h

@ -43,9 +43,16 @@ class AssemblyItem
public: public:
enum class JumpType { Ordinary, IntoFunction, OutOfFunction }; enum class JumpType { Ordinary, IntoFunction, OutOfFunction };
AssemblyItem(u256 _push): m_type(Push), m_data(_push) {} AssemblyItem(u256 _push, SourceLocation const& _location = SourceLocation()):
AssemblyItem(Instruction _i): m_type(Operation), m_data((byte)_i) {} AssemblyItem(Push, _push, _location) { }
AssemblyItem(AssemblyItemType _type, u256 _data = 0): m_type(_type), m_data(_data) {} AssemblyItem(Instruction _i, SourceLocation const& _location = SourceLocation()):
AssemblyItem(Operation, byte(_i), _location) { }
AssemblyItem(AssemblyItemType _type, u256 _data = 0, SourceLocation const& _location = SourceLocation()):
m_type(_type),
m_data(_data),
m_location(_location)
{
}
AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(Tag, m_data); } AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(Tag, m_data); }
AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(PushTag, m_data); } AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(PushTag, m_data); }

176
libevmcore/CommonSubexpressionEliminator.cpp

@ -41,9 +41,9 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
if (!m_stackElements.empty()) if (!m_stackElements.empty())
minHeight = min(minHeight, m_stackElements.begin()->first); minHeight = min(minHeight, m_stackElements.begin()->first);
for (int height = minHeight; height <= 0; ++height) for (int height = minHeight; height <= 0; ++height)
initialStackContents[height] = initialStackElement(height); initialStackContents[height] = initialStackElement(height, SourceLocation());
for (int height = minHeight; height <= m_stackHeight; ++height) for (int height = minHeight; height <= m_stackHeight; ++height)
targetStackContents[height] = stackElement(height); targetStackContents[height] = stackElement(height, SourceLocation());
// Debug info: // Debug info:
//stream(cout, initialStackContents, targetStackContents); //stream(cout, initialStackContents, targetStackContents);
@ -111,30 +111,46 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _co
if (SemanticInformation::isDupInstruction(_item)) if (SemanticInformation::isDupInstruction(_item))
setStackElement( setStackElement(
m_stackHeight + 1, m_stackHeight + 1,
stackElement(m_stackHeight - int(instruction) + int(Instruction::DUP1)) stackElement(
m_stackHeight - int(instruction) + int(Instruction::DUP1),
_item.getLocation()
)
); );
else if (SemanticInformation::isSwapInstruction(_item)) else if (SemanticInformation::isSwapInstruction(_item))
swapStackElements( swapStackElements(
m_stackHeight, m_stackHeight,
m_stackHeight - 1 - int(instruction) + int(Instruction::SWAP1) m_stackHeight - 1 - int(instruction) + int(Instruction::SWAP1),
_item.getLocation()
); );
else if (instruction != Instruction::POP) else if (instruction != Instruction::POP)
{ {
vector<Id> arguments(info.args); vector<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, _item.getLocation());
if (_item.instruction() == Instruction::SSTORE) if (_item.instruction() == Instruction::SSTORE)
storeInStorage(arguments[0], arguments[1]); storeInStorage(arguments[0], arguments[1], _item.getLocation());
else if (_item.instruction() == Instruction::SLOAD) else if (_item.instruction() == Instruction::SLOAD)
setStackElement(m_stackHeight + _item.deposit(), loadFromStorage(arguments[0])); setStackElement(
m_stackHeight + _item.deposit(),
loadFromStorage(arguments[0], _item.getLocation())
);
else if (_item.instruction() == Instruction::MSTORE) else if (_item.instruction() == Instruction::MSTORE)
storeInMemory(arguments[0], arguments[1]); storeInMemory(arguments[0], arguments[1], _item.getLocation());
else if (_item.instruction() == Instruction::MLOAD) else if (_item.instruction() == Instruction::MLOAD)
setStackElement(m_stackHeight + _item.deposit(), loadFromMemory(arguments[0])); setStackElement(
m_stackHeight + _item.deposit(),
loadFromMemory(arguments[0], _item.getLocation())
);
else if (_item.instruction() == Instruction::SHA3) else if (_item.instruction() == Instruction::SHA3)
setStackElement(m_stackHeight + _item.deposit(), applySha3(arguments.at(0), arguments.at(1))); setStackElement(
m_stackHeight + _item.deposit(),
applySha3(arguments.at(0), arguments.at(1), _item.getLocation())
);
else else
setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments, _copyItem)); setStackElement(
m_stackHeight + _item.deposit(),
m_expressionClasses.find(_item, arguments, _copyItem)
);
} }
m_stackHeight += _item.deposit(); m_stackHeight += _item.deposit();
} }
@ -145,22 +161,27 @@ void CommonSubexpressionEliminator::optimizeBreakingItem()
if (!m_breakingItem || *m_breakingItem != AssemblyItem(Instruction::JUMPI)) if (!m_breakingItem || *m_breakingItem != AssemblyItem(Instruction::JUMPI))
return; return;
static AssemblyItem s_jump = Instruction::JUMP; SourceLocation const& location = m_breakingItem->getLocation();
AssemblyItem::JumpType jumpType = m_breakingItem->getJumpType();
Id condition = stackElement(m_stackHeight - 1); Id condition = stackElement(m_stackHeight - 1, location);
Id zero = m_expressionClasses.find(u256(0)); Id zero = m_expressionClasses.find(u256(0));
if (m_expressionClasses.knownToBeDifferent(condition, zero)) if (m_expressionClasses.knownToBeDifferent(condition, zero))
{ {
feedItem(Instruction::SWAP1, true); feedItem(AssemblyItem(Instruction::SWAP1, location), true);
feedItem(Instruction::POP, true); feedItem(AssemblyItem(Instruction::POP, location), true);
m_breakingItem = &s_jump;
AssemblyItem item(Instruction::JUMP, location);
item.setJumpType(jumpType);
m_breakingItem = m_expressionClasses.storeItem(item);
return; return;
} }
Id negatedCondition = m_expressionClasses.find(Instruction::ISZERO, {condition}); Id negatedCondition = m_expressionClasses.find(Instruction::ISZERO, {condition});
if (m_expressionClasses.knownToBeDifferent(negatedCondition, zero)) if (m_expressionClasses.knownToBeDifferent(negatedCondition, zero))
{ {
feedItem(Instruction::POP, true); AssemblyItem it(Instruction::POP, location);
feedItem(Instruction::POP, true); feedItem(it, true);
feedItem(it, true);
m_breakingItem = nullptr; m_breakingItem = nullptr;
} }
} }
@ -170,33 +191,43 @@ void CommonSubexpressionEliminator::setStackElement(int _stackHeight, Id _class)
m_stackElements[_stackHeight] = _class; m_stackElements[_stackHeight] = _class;
} }
void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _stackHeightB) void CommonSubexpressionEliminator::swapStackElements(
int _stackHeightA,
int _stackHeightB,
SourceLocation const& _location
)
{ {
assertThrow(_stackHeightA != _stackHeightB, OptimizerException, "Swap on same stack elements."); assertThrow(_stackHeightA != _stackHeightB, OptimizerException, "Swap on same stack elements.");
// ensure they are created // ensure they are created
stackElement(_stackHeightA); stackElement(_stackHeightA, _location);
stackElement(_stackHeightB); stackElement(_stackHeightB, _location);
swap(m_stackElements[_stackHeightA], m_stackElements[_stackHeightB]); swap(m_stackElements[_stackHeightA], m_stackElements[_stackHeightB]);
} }
ExpressionClasses::Id CommonSubexpressionEliminator::stackElement(int _stackHeight) ExpressionClasses::Id CommonSubexpressionEliminator::stackElement(
int _stackHeight,
SourceLocation const& _location
)
{ {
if (m_stackElements.count(_stackHeight)) if (m_stackElements.count(_stackHeight))
return m_stackElements.at(_stackHeight); return m_stackElements.at(_stackHeight);
// Stack element not found (not assigned yet), create new equivalence class. // Stack element not found (not assigned yet), create new equivalence class.
return m_stackElements[_stackHeight] = initialStackElement(_stackHeight); return m_stackElements[_stackHeight] = initialStackElement(_stackHeight, _location);
} }
ExpressionClasses::Id CommonSubexpressionEliminator::initialStackElement(int _stackHeight) ExpressionClasses::Id CommonSubexpressionEliminator::initialStackElement(
int _stackHeight,
SourceLocation const& _location
)
{ {
assertThrow(_stackHeight <= 0, OptimizerException, "Initial stack element of positive height requested."); assertThrow(_stackHeight <= 0, OptimizerException, "Initial stack element of positive height requested.");
assertThrow(_stackHeight > -16, StackTooDeepException, ""); assertThrow(_stackHeight > -16, StackTooDeepException, "");
// 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_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight))); return m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight), _location));
} }
void CommonSubexpressionEliminator::storeInStorage(Id _slot, Id _value) void CommonSubexpressionEliminator::storeInStorage(Id _slot, Id _value, SourceLocation const& _location)
{ {
if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value) if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value)
// do not execute the storage if we know that the value is already there // do not execute the storage if we know that the value is already there
@ -210,22 +241,25 @@ void CommonSubexpressionEliminator::storeInStorage(Id _slot, Id _value)
if (m_expressionClasses.knownToBeDifferent(storageItem.first, _slot) || storageItem.second == _value) if (m_expressionClasses.knownToBeDifferent(storageItem.first, _slot) || storageItem.second == _value)
storageContents.insert(storageItem); storageContents.insert(storageItem);
m_storageContent = move(storageContents); m_storageContent = move(storageContents);
Id id = m_expressionClasses.find(Instruction::SSTORE, {_slot, _value}, true, m_sequenceNumber);
AssemblyItem item(Instruction::SSTORE, _location);
Id id = m_expressionClasses.find(item, {_slot, _value}, true, m_sequenceNumber);
m_storeOperations.push_back(StoreOperation(StoreOperation::Storage, _slot, m_sequenceNumber, id)); m_storeOperations.push_back(StoreOperation(StoreOperation::Storage, _slot, m_sequenceNumber, id));
m_storageContent[_slot] = _value; m_storageContent[_slot] = _value;
// increment a second time so that we get unique sequence numbers for writes // increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++; m_sequenceNumber++;
} }
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(Id _slot) ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(Id _slot, SourceLocation const& _location)
{ {
if (m_storageContent.count(_slot)) if (m_storageContent.count(_slot))
return m_storageContent.at(_slot); return m_storageContent.at(_slot);
else
return m_storageContent[_slot] = m_expressionClasses.find(Instruction::SLOAD, {_slot}, true, m_sequenceNumber); AssemblyItem item(Instruction::SLOAD, _location);
return m_storageContent[_slot] = m_expressionClasses.find(item, {_slot}, true, m_sequenceNumber);
} }
void CommonSubexpressionEliminator::storeInMemory(Id _slot, Id _value) void CommonSubexpressionEliminator::storeInMemory(Id _slot, Id _value, SourceLocation const& _location)
{ {
if (m_memoryContent.count(_slot) && m_memoryContent[_slot] == _value) if (m_memoryContent.count(_slot) && m_memoryContent[_slot] == _value)
// do not execute the store if we know that the value is already there // do not execute the store if we know that the value is already there
@ -237,34 +271,45 @@ void CommonSubexpressionEliminator::storeInMemory(Id _slot, Id _value)
if (m_expressionClasses.knownToBeDifferentBy32(memoryItem.first, _slot)) if (m_expressionClasses.knownToBeDifferentBy32(memoryItem.first, _slot))
memoryContents.insert(memoryItem); memoryContents.insert(memoryItem);
m_memoryContent = move(memoryContents); m_memoryContent = move(memoryContents);
Id id = m_expressionClasses.find(Instruction::MSTORE, {_slot, _value}, true, m_sequenceNumber);
AssemblyItem item(Instruction::MSTORE, _location);
Id id = m_expressionClasses.find(item, {_slot, _value}, true, m_sequenceNumber);
m_storeOperations.push_back(StoreOperation(StoreOperation::Memory, _slot, m_sequenceNumber, id)); m_storeOperations.push_back(StoreOperation(StoreOperation::Memory, _slot, m_sequenceNumber, id));
m_memoryContent[_slot] = _value; m_memoryContent[_slot] = _value;
// increment a second time so that we get unique sequence numbers for writes // increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++; m_sequenceNumber++;
} }
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(Id _slot) ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(Id _slot, SourceLocation const& _location)
{ {
if (m_memoryContent.count(_slot)) if (m_memoryContent.count(_slot))
return m_memoryContent.at(_slot); return m_memoryContent.at(_slot);
else
return m_memoryContent[_slot] = m_expressionClasses.find(Instruction::MLOAD, {_slot}, true, m_sequenceNumber); AssemblyItem item(Instruction::MLOAD, _location);
return m_memoryContent[_slot] = m_expressionClasses.find(item, {_slot}, true, m_sequenceNumber);
} }
CommonSubexpressionEliminator::Id CommonSubexpressionEliminator::applySha3(Id _start, Id _length) CommonSubexpressionEliminator::Id CommonSubexpressionEliminator::applySha3(
Id _start,
Id _length,
SourceLocation const& _location
)
{ {
AssemblyItem sha3Item(Instruction::SHA3, _location);
// Special logic if length is a short constant, otherwise we cannot tell. // Special logic if length is a short constant, otherwise we cannot tell.
u256 const* l = m_expressionClasses.knownConstant(_length); u256 const* l = m_expressionClasses.knownConstant(_length);
// unknown or too large length // unknown or too large length
if (!l || *l > 128) if (!l || *l > 128)
return m_expressionClasses.find(Instruction::SHA3, {_start, _length}, true, m_sequenceNumber); return m_expressionClasses.find(sha3Item, {_start, _length}, true, m_sequenceNumber);
vector<Id> arguments; vector<Id> arguments;
for (u256 i = 0; i < *l; i += 32) for (u256 i = 0; i < *l; i += 32)
{ {
Id slot = m_expressionClasses.find(Instruction::ADD, {_start, m_expressionClasses.find(i)}); Id slot = m_expressionClasses.find(
arguments.push_back(loadFromMemory(slot)); AssemblyItem(Instruction::ADD, _location),
{_start, m_expressionClasses.find(i)}
);
arguments.push_back(loadFromMemory(slot, _location));
} }
if (m_knownSha3Hashes.count(arguments)) if (m_knownSha3Hashes.count(arguments))
return m_knownSha3Hashes.at(arguments); return m_knownSha3Hashes.at(arguments);
@ -276,10 +321,10 @@ CommonSubexpressionEliminator::Id CommonSubexpressionEliminator::applySha3(Id _s
for (Id a: arguments) for (Id a: arguments)
data += toBigEndian(*m_expressionClasses.knownConstant(a)); data += toBigEndian(*m_expressionClasses.knownConstant(a));
data.resize(size_t(*l)); data.resize(size_t(*l));
v = m_expressionClasses.find(u256(sha3(data))); v = m_expressionClasses.find(AssemblyItem(u256(sha3(data)), _location));
} }
else else
v = m_expressionClasses.find(Instruction::SHA3, {_start, _length}, true, m_sequenceNumber); v = m_expressionClasses.find(sha3Item, {_start, _length}, true, m_sequenceNumber);
return m_knownSha3Hashes[arguments] = v; return m_knownSha3Hashes[arguments] = v;
} }
@ -333,12 +378,13 @@ AssemblyItems CSECodeGenerator::generateCode(
assertThrow(position != c_invalidPosition, OptimizerException, ""); assertThrow(position != c_invalidPosition, OptimizerException, "");
if (position == targetItem.first) if (position == targetItem.first)
continue; continue;
SourceLocation const& location = m_expressionClasses.representative(targetItem.second).item->getLocation();
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, location);
else else
appendOrRemoveSwap(position); appendOrRemoveSwap(position, location);
appendOrRemoveSwap(targetItem.first); appendOrRemoveSwap(targetItem.first, location);
} }
// remove surplus elements // remove surplus elements
@ -404,7 +450,8 @@ void CSECodeGenerator::addDependencies(Id _c)
case Instruction::SHA3: case Instruction::SHA3:
{ {
Id length = expr.arguments.at(1); Id length = expr.arguments.at(1);
Id offsetToStart = m_expressionClasses.find(Instruction::SUB, {slot, slotToLoadFrom}); AssemblyItem offsetInstr(Instruction::SUB, expr.item->getLocation());
Id offsetToStart = m_expressionClasses.find(offsetInstr, {slot, slotToLoadFrom});
u256 const* o = m_expressionClasses.knownConstant(offsetToStart); u256 const* o = m_expressionClasses.knownConstant(offsetToStart);
u256 const* l = m_expressionClasses.knownConstant(length); u256 const* l = m_expressionClasses.knownConstant(length);
if (l && *l == 0) if (l && *l == 0)
@ -462,6 +509,7 @@ int CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
for (Id arg: boost::adaptors::reverse(arguments)) for (Id arg: boost::adaptors::reverse(arguments))
generateClassElement(arg); generateClassElement(arg);
SourceLocation const& location = expr.item->getLocation();
// The arguments are somewhere on the stack now, so it remains to move them at the correct place. // The arguments are somewhere on the stack now, so it remains to move them at the correct place.
// This is quite difficult as sometimes, the values also have to removed in this process // This is quite difficult as sometimes, the values also have to removed in this process
// (if canBeRemoved() returns true) and the two arguments can be equal. For now, this is // (if canBeRemoved() returns true) and the two arguments can be equal. For now, this is
@ -469,42 +517,42 @@ int CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
if (arguments.size() == 1) if (arguments.size() == 1)
{ {
if (canBeRemoved(arguments[0], _c)) if (canBeRemoved(arguments[0], _c))
appendOrRemoveSwap(classElementPosition(arguments[0])); appendOrRemoveSwap(classElementPosition(arguments[0]), location);
else else
appendDup(classElementPosition(arguments[0])); appendDup(classElementPosition(arguments[0]), location);
} }
else if (arguments.size() == 2) else if (arguments.size() == 2)
{ {
if (canBeRemoved(arguments[1], _c)) if (canBeRemoved(arguments[1], _c))
{ {
appendOrRemoveSwap(classElementPosition(arguments[1])); appendOrRemoveSwap(classElementPosition(arguments[1]), location);
if (arguments[0] == arguments[1]) if (arguments[0] == arguments[1])
appendDup(m_stackHeight); appendDup(m_stackHeight, location);
else if (canBeRemoved(arguments[0], _c)) else if (canBeRemoved(arguments[0], _c))
{ {
appendOrRemoveSwap(m_stackHeight - 1); appendOrRemoveSwap(m_stackHeight - 1, location);
appendOrRemoveSwap(classElementPosition(arguments[0])); appendOrRemoveSwap(classElementPosition(arguments[0]), location);
} }
else else
appendDup(classElementPosition(arguments[0])); appendDup(classElementPosition(arguments[0]), location);
} }
else else
{ {
if (arguments[0] == arguments[1]) if (arguments[0] == arguments[1])
{ {
appendDup(classElementPosition(arguments[0])); appendDup(classElementPosition(arguments[0]), location);
appendDup(m_stackHeight); appendDup(m_stackHeight, location);
} }
else if (canBeRemoved(arguments[0], _c)) else if (canBeRemoved(arguments[0], _c))
{ {
appendOrRemoveSwap(classElementPosition(arguments[0])); appendOrRemoveSwap(classElementPosition(arguments[0]), location);
appendDup(classElementPosition(arguments[1])); appendDup(classElementPosition(arguments[1]), location);
appendOrRemoveSwap(m_stackHeight - 1); appendOrRemoveSwap(m_stackHeight - 1, location);
} }
else else
{ {
appendDup(classElementPosition(arguments[1])); appendDup(classElementPosition(arguments[1]), location);
appendDup(classElementPosition(arguments[0])); appendDup(classElementPosition(arguments[0]), location);
} }
} }
} }
@ -521,7 +569,7 @@ int CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
!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
appendOrRemoveSwap(m_stackHeight - 1); appendOrRemoveSwap(m_stackHeight - 1, location);
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;
@ -582,17 +630,17 @@ bool CSECodeGenerator::removeStackTopIfPossible()
return true; return true;
} }
void CSECodeGenerator::appendDup(int _fromPosition) void CSECodeGenerator::appendDup(int _fromPosition, SourceLocation const& _location)
{ {
assertThrow(_fromPosition != c_invalidPosition, OptimizerException, ""); assertThrow(_fromPosition != c_invalidPosition, OptimizerException, "");
int instructionNum = 1 + m_stackHeight - _fromPosition; int instructionNum = 1 + m_stackHeight - _fromPosition;
assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep."); assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep.");
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
appendItem(AssemblyItem(dupInstruction(instructionNum))); appendItem(AssemblyItem(dupInstruction(instructionNum), _location));
m_stack[m_stackHeight] = m_stack[_fromPosition]; m_stack[m_stackHeight] = m_stack[_fromPosition];
} }
void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition) void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition, SourceLocation const& _location)
{ {
assertThrow(_fromPosition != c_invalidPosition, OptimizerException, ""); assertThrow(_fromPosition != c_invalidPosition, OptimizerException, "");
if (_fromPosition == m_stackHeight) if (_fromPosition == m_stackHeight)
@ -600,7 +648,7 @@ void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition)
int instructionNum = m_stackHeight - _fromPosition; int instructionNum = m_stackHeight - _fromPosition;
assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep."); assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep.");
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
appendItem(AssemblyItem(swapInstruction(instructionNum))); appendItem(AssemblyItem(swapInstruction(instructionNum), _location));
// 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)

20
libevmcore/CommonSubexpressionEliminator.h

@ -99,26 +99,26 @@ private:
/// Assigns a new equivalence class to the next sequence number of the given stack element. /// Assigns a new equivalence class to the next sequence number of the given stack element.
void setStackElement(int _stackHeight, Id _class); void setStackElement(int _stackHeight, Id _class);
/// Swaps the given stack elements in their next sequence number. /// Swaps the given stack elements in their next sequence number.
void swapStackElements(int _stackHeightA, int _stackHeightB); void swapStackElements(int _stackHeightA, int _stackHeightB, SourceLocation const& _location);
/// Retrieves the current equivalence class fo the given stack element (or generates a new /// Retrieves the current equivalence class fo the given stack element (or generates a new
/// one if it does not exist yet). /// one if it does not exist yet).
Id stackElement(int _stackHeight); Id stackElement(int _stackHeight, SourceLocation const& _location);
/// @returns the equivalence class id of the special initial stack element at the given height /// @returns the equivalence class id of the special initial stack element at the given height
/// (must not be positive). /// (must not be positive).
Id initialStackElement(int _stackHeight); Id initialStackElement(int _stackHeight, SourceLocation const& _location);
/// Increments the sequence number, deletes all storage information that might be overwritten /// Increments the sequence number, deletes all storage information that might be overwritten
/// and stores the new value at the given slot. /// and stores the new value at the given slot.
void storeInStorage(Id _slot, Id _value); void storeInStorage(Id _slot, Id _value, SourceLocation const& _location);
/// Retrieves the current value at the given slot in storage or creates a new special sload class. /// Retrieves the current value at the given slot in storage or creates a new special sload class.
Id loadFromStorage(Id _slot); Id loadFromStorage(Id _slot, SourceLocation const& _location);
/// Increments the sequence number, deletes all memory information that might be overwritten /// Increments the sequence number, deletes all memory information that might be overwritten
/// and stores the new value at the given slot. /// and stores the new value at the given slot.
void storeInMemory(Id _slot, Id _value); void storeInMemory(Id _slot, Id _value, SourceLocation const& _location);
/// Retrieves the current value at the given slot in memory or creates a new special mload class. /// Retrieves the current value at the given slot in memory or creates a new special mload class.
Id loadFromMemory(Id _slot); Id loadFromMemory(Id _slot, SourceLocation const& _location);
/// Finds or creates a new expression that applies the sha3 hash function to the contents in memory. /// Finds or creates a new expression that applies the sha3 hash function to the contents in memory.
Id applySha3(Id _start, Id _length); Id applySha3(Id _start, Id _length, SourceLocation const& _location);
/// Current stack height, can be negative. /// Current stack height, can be negative.
int m_stackHeight = 0; int m_stackHeight = 0;
@ -188,10 +188,10 @@ private:
bool removeStackTopIfPossible(); bool removeStackTopIfPossible();
/// Appends a dup instruction to m_generatedItems to retrieve the element at the given stack position. /// Appends a dup instruction to m_generatedItems to retrieve the element at the given stack position.
void appendDup(int _fromPosition); void appendDup(int _fromPosition, SourceLocation const& _location);
/// 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 appendOrRemoveSwap(int _fromPosition); void appendOrRemoveSwap(int _fromPosition, SourceLocation const& _location);
/// Appends the given assembly item. /// Appends the given assembly item.
void appendItem(AssemblyItem const& _item); void appendItem(AssemblyItem const& _item);

24
libevmcore/ExpressionClasses.cpp

@ -64,10 +64,7 @@ ExpressionClasses::Id ExpressionClasses::find(
return it->id; return it->id;
if (_copyItem) if (_copyItem)
{ exp.item = storeItem(_item);
m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(_item));
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())
@ -115,6 +112,12 @@ u256 const* ExpressionClasses::knownConstant(Id _c)
return &constant.d(); return &constant.d();
} }
AssemblyItem const* ExpressionClasses::storeItem(AssemblyItem const& _item)
{
m_spareAssemblyItems.push_back(make_shared<AssemblyItem>(_item));
return m_spareAssemblyItems.back().get();
}
string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const
{ {
Expression const& expr = representative(_id); Expression const& expr = representative(_id);
@ -292,7 +295,7 @@ ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr,
//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(), _expr.item->getLocation()));
} }
} }
@ -350,6 +353,11 @@ bool Pattern::matches(Expression const& _expr, ExpressionClasses const& _classes
return true; return true;
} }
AssemblyItem Pattern::toAssemblyItem(SourceLocation const& _location) const
{
return AssemblyItem(m_type, m_data, _location);
}
string Pattern::toString() const string Pattern::toString() const
{ {
stringstream s; stringstream s;
@ -399,7 +407,7 @@ Pattern::Expression const& Pattern::matchGroupValue() const
} }
ExpressionTemplate::ExpressionTemplate(Pattern const& _pattern) ExpressionTemplate::ExpressionTemplate(Pattern const& _pattern, SourceLocation const& _location)
{ {
if (_pattern.matchGroup()) if (_pattern.matchGroup())
{ {
@ -409,10 +417,10 @@ ExpressionTemplate::ExpressionTemplate(Pattern const& _pattern)
else else
{ {
hasId = false; hasId = false;
item = _pattern.toAssemblyItem(); item = _pattern.toAssemblyItem(_location);
} }
for (auto const& arg: _pattern.arguments()) for (auto const& arg: _pattern.arguments())
arguments.push_back(ExpressionTemplate(arg)); arguments.push_back(ExpressionTemplate(arg, _location));
} }
string ExpressionTemplate::toString() const string ExpressionTemplate::toString() const

10
libevmcore/ExpressionClasses.h

@ -88,6 +88,10 @@ public:
/// and a nullptr otherwise. /// and a nullptr otherwise.
u256 const* knownConstant(Id _c); u256 const* knownConstant(Id _c);
/// Stores a copy of the given AssemblyItem and returns a pointer to the copy that is valid for
/// the lifetime of the ExpressionClasses object.
AssemblyItem const* storeItem(AssemblyItem const& _item);
std::string fullDAGToString(Id _id) const; std::string fullDAGToString(Id _id) const;
private: private:
@ -105,7 +109,7 @@ private:
std::vector<Expression> m_representatives; std::vector<Expression> m_representatives;
/// All expression ever encountered. /// All expression ever encountered.
std::set<Expression> m_expressions; std::set<Expression> m_expressions;
std::vector<std::shared_ptr<AssemblyItem>> m_spareAssemblyItem; std::vector<std::shared_ptr<AssemblyItem>> m_spareAssemblyItems;
}; };
/** /**
@ -134,7 +138,7 @@ public:
unsigned matchGroup() const { return m_matchGroup; } unsigned matchGroup() const { return m_matchGroup; }
bool matches(Expression const& _expr, ExpressionClasses const& _classes) const; bool matches(Expression const& _expr, ExpressionClasses const& _classes) const;
AssemblyItem toAssemblyItem() const { return AssemblyItem(m_type, m_data); } AssemblyItem toAssemblyItem(SourceLocation const& _location) const;
std::vector<Pattern> arguments() const { return m_arguments; } std::vector<Pattern> arguments() const { return m_arguments; }
/// @returns the id of the matched expression if this pattern is part of a match group. /// @returns the id of the matched expression if this pattern is part of a match group.
@ -163,7 +167,7 @@ struct ExpressionTemplate
{ {
using Expression = ExpressionClasses::Expression; using Expression = ExpressionClasses::Expression;
using Id = ExpressionClasses::Id; using Id = ExpressionClasses::Id;
explicit ExpressionTemplate(Pattern const& _pattern); explicit ExpressionTemplate(Pattern const& _pattern, SourceLocation const& _location);
std::string toString() const; std::string toString() const;
bool hasId = false; bool hasId = false;
/// Id of the matched expression, if available. /// Id of the matched expression, if available.

16
test/libsolidity/SolidityOptimizer.cpp

@ -22,6 +22,7 @@
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <memory>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <test/libsolidity/solidityExecutionFramework.h> #include <test/libsolidity/solidityExecutionFramework.h>
@ -84,9 +85,20 @@ public:
AssemblyItems getCSE(AssemblyItems const& _input) AssemblyItems getCSE(AssemblyItems const& _input)
{ {
// add dummy locations to each item so that we can check that they are not deleted
AssemblyItems input = _input;
for (AssemblyItem& item: input)
item.setLocation(SourceLocation(1, 3, make_shared<string>("")));
eth::CommonSubexpressionEliminator cse; eth::CommonSubexpressionEliminator cse;
BOOST_REQUIRE(cse.feedItems(_input.begin(), _input.end()) == _input.end()); BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end());
return cse.getOptimizedItems(); AssemblyItems output = cse.getOptimizedItems();
for (AssemblyItem const& item: output)
{
BOOST_CHECK(item == Instruction::POP || !item.getLocation().isEmpty());
}
return output;
} }
void checkCSE(AssemblyItems const& _input, AssemblyItems const& _expectation) void checkCSE(AssemblyItems const& _input, AssemblyItems const& _expectation)

Loading…
Cancel
Save