From 6f54f1046ab3fe00acefe2e9d4ee683be1b569cb Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 18 Mar 2015 20:04:30 +0100 Subject: [PATCH] Code generation part 1. --- libevmcore/CommonSubexpressionEliminator.cpp | 167 ++++++++++++++++++- libevmcore/CommonSubexpressionEliminator.h | 39 ++++- 2 files changed, 194 insertions(+), 12 deletions(-) diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index c442425a4..711b6d016 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -22,6 +22,7 @@ */ #include +#include #include #include @@ -56,16 +57,19 @@ vector CommonSubexpressionEliminator::getOptimizedItems() streamEquivalenceClass(cout, eqClass); cout << "----------------------------" << endl; + map currentStackContents; map targetStackContents; int minStackHeight = m_stackHeight; if (m_stackElements.size() > 0) minStackHeight = min(minStackHeight, m_stackElements.begin()->first.first); for (int stackHeight = minStackHeight; stackHeight <= m_stackHeight; ++stackHeight) + { + if (stackHeight <= 0) + currentStackContents[stackHeight] = getClass(AssemblyItem(dupInstruction(1 - stackHeight))); targetStackContents[stackHeight] = getStackElement(stackHeight); + } - CSECodeGenerator generator; - - return generator.generateCode(m_stackHeight, targetStackContents, m_equivalenceClasses); + return CSECodeGenerator().generateCode(currentStackContents, targetStackContents, m_equivalenceClasses); } bool CommonSubexpressionEliminator::breaksBasicBlock(AssemblyItem const& _item) @@ -234,13 +238,160 @@ unsigned CommonSubexpressionEliminator::getNextStackElementSequence(int _stackHe return 0; } - AssemblyItems CSECodeGenerator::generateCode( - int _targetStackHeight, + map const& _currentStack, map const& _targetStackContents, - vector> const& _equivalenceClasses) + vector> const& _equivalenceClasses +) { - m_generatedItems.clear(); - m_classRequestCount.clear(); + // reset + *this = move(CSECodeGenerator()); + m_stack = _currentStack; + m_equivalenceClasses = _equivalenceClasses; + for (auto const& item: m_stack) + m_classPositions[item.second] = item.first; + + // generate the dependency graph + for (auto const& stackContent: _targetStackContents) + { + m_finalClasses.insert(stackContent.second); + addDependencies(stackContent.second); + } + + for (auto const& cid: m_finalClasses) + generateClassElement(cid); + + // @TODO shuffle and copy the elements + + cout << "--------------- generated code: ---------------" << endl; + for (auto const& it: m_generatedItems) + cout << it << endl; + cout << "-----------------------------" << endl; + return m_generatedItems; } + +void CSECodeGenerator::addDependencies(EquivalenceClassId _c) +{ + if (m_neededBy.count(_c)) + return; + for (EquivalenceClassId argument: m_equivalenceClasses[_c].second) + { + addDependencies(argument); + m_neededBy.insert(make_pair(argument, _c)); + } +} + +int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) +{ + if (m_classPositions.count(_c)) + return m_classPositions[_c]; + assertThrow( + m_classPositions[_c] != c_invalidPosition, + OptimizerException, + "Element already removed but still needed." + ); + EquivalenceClassIds const& arguments = m_equivalenceClasses[_c].second; + for (EquivalenceClassId arg: boost::adaptors::reverse(arguments)) + generateClassElement(arg); + + if (arguments.size() == 1) + { + if (canBeRemoved(arguments[0], _c)) + appendSwap(generateClassElement(arguments[0])); + else + appendDup(generateClassElement(arguments[0])); + } + else if (arguments.size() == 2) + { + if (canBeRemoved(arguments[1], _c)) + { + appendSwap(generateClassElement(arguments[1])); + if (arguments[0] == arguments[1]) + appendDup(m_stackHeight); + else if (canBeRemoved(arguments[0], _c)) + { + appendSwap(m_stackHeight - 1); + appendSwap(generateClassElement(arguments[1])); + } + else + appendDup(generateClassElement(arguments[1])); + } + else + { + if (arguments[0] == arguments[1]) + { + appendDup(generateClassElement(arguments[0])); + appendDup(m_stackHeight); + } + else if (canBeRemoved(arguments[0], _c)) + { + appendSwap(generateClassElement(arguments[0])); + appendDup(generateClassElement(arguments[1])); + appendSwap(m_stackHeight - 1); + } + else + { + appendDup(generateClassElement(arguments[1])); + appendDup(generateClassElement(arguments[0])); + } + } + } + else + assertThrow( + arguments.size() <= 2, + OptimizerException, + "Opcodes with more than two arguments not implemented yet." + ); + for (auto arg: arguments) + if (canBeRemoved(arg, _c)) + m_classPositions[arguments[1]] = c_invalidPosition; + appendItem(*m_equivalenceClasses[_c].first); + m_stack[m_stackHeight] = _c; + return m_classPositions[_c] = m_stackHeight; +} + +bool CSECodeGenerator::canBeRemoved(EquivalenceClassId _element, EquivalenceClassId _result) +{ + // Returns false if _element is finally needed or is needed by a class that has not been + // computed yet. Note that m_classPositions also includes classes that were deleted in the meantime. + if (m_finalClasses.count(_element)) + return false; + + auto range = m_neededBy.equal_range(_element); + for (auto it = range.first; it != range.second; ++it) + if (it->second != _result && !m_classPositions.count(it->second)) + return false; + return true; +} + +void CSECodeGenerator::appendDup(int _fromPosition) +{ + m_generatedItems.push_back(AssemblyItem(swapInstruction(1 + m_stackHeight - _fromPosition))); + int nr = 1 + m_stackHeight - _fromPosition; + assertThrow(1 <= nr && nr <= 16, OptimizerException, "Stack too deep."); + m_generatedItems.push_back(AssemblyItem(dupInstruction(nr))); + m_stackHeight++; + m_stack[m_stackHeight] = m_stack[_fromPosition]; +} + +void CSECodeGenerator::appendSwap(int _fromPosition) +{ + if (_fromPosition == m_stackHeight) + return; + int nr = m_stackHeight - _fromPosition; + assertThrow(1 <= nr && nr <= 16, OptimizerException, "Stack too deep."); + m_generatedItems.push_back(AssemblyItem(swapInstruction(nr))); + // only update if they are the "canonical" positions + if (m_classPositions[m_stack[m_stackHeight]] == m_stackHeight) + m_classPositions[m_stack[m_stackHeight]] = _fromPosition; + if (m_classPositions[m_stack[_fromPosition]] == _fromPosition) + m_classPositions[m_stack[_fromPosition]] = m_stackHeight; + swap(m_stack[m_stackHeight], m_stack[_fromPosition]); +} + +void CSECodeGenerator::appendItem(AssemblyItem const& _item) +{ + m_generatedItems.push_back(_item); + m_stackHeight += _item.deposit(); +} diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index c15b3efad..e93ccfaad 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -26,6 +26,7 @@ #include #include #include +#include namespace dev { @@ -98,19 +99,49 @@ class CSECodeGenerator { public: /// @returns the assembly items generated from the given requirements - /// @param _targetStackHeight target stack height starting from an assumed initial stack height of zero + /// @param _currentStack 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 _equivalenceClasses equivalence classes as expressions of how to compute them AssemblyItems generateCode( - int _targetStackHeight, + std::map const& _currentStack, std::map const& _targetStackContents, std::vector> const& _equivalenceClasses ); private: + /// Recursively discovers all dependencies to @a m_requests. + void addDependencies(EquivalenceClassId _c); + + /// Produce code that generates the given element if it is not yet present. + /// @returns the stack position of the element. + int generateClassElement(EquivalenceClassId _c); + + /// @returns true if @a _element can be removed while computing @a _result. + bool canBeRemoved(EquivalenceClassId _element, EquivalenceClassId _result); + + /// Appends a dup instruction to m_generatedItems to retrieve the element at the given stack position. + void appendDup(int _fromPosition); + /// Appends a swap instruction to m_generatedItems to retrieve the element at the given stack position. + void appendSwap(int _fromPosition); + /// Appends the given assembly item. + void appendItem(AssemblyItem const& _item); + + static const int c_invalidPosition = std::numeric_limits::min(); + AssemblyItems m_generatedItems; - /// Number of requests for an equivalence class, used as a reference counter. - std::map m_classRequestCount; + /// Current height of the stack relative to the start. + int m_stackHeight = 0; + /// If (b, a) is in m_requests then b is needed to compute a. + std::multimap m_neededBy; + /// Current content of the stack. + std::map m_stack; + /// Current positions of equivalence classes, equal to c_invalidPosition if already deleted. + std::map m_classPositions; + + /// The actual eqivalence class items and how to compute them. + std::vector> m_equivalenceClasses; + /// The set of equivalence classes that should be present on the stack at the end. + std::set m_finalClasses; }; template