diff --git a/libyul/backends/evm/StackHelpers.h b/libyul/backends/evm/StackHelpers.h index ff8887b24..c945c7762 100644 --- a/libyul/backends/evm/StackHelpers.h +++ b/libyul/backends/evm/StackHelpers.h @@ -69,6 +69,8 @@ template concept ShuffleOperationConcept = requires(ShuffleOperations ops, size_t sourceOffset, size_t targetOffset, size_t depth) { // Returns true, iff the current slot at sourceOffset in source layout is a suitable slot at targetOffset. { ops.isCompatible(sourceOffset, targetOffset) } -> std::convertible_to; + // Returns true, iff the slots at the given source offset is junk. + { ops.sourceIsJunk(sourceOffset) } -> std::convertible_to; // Returns true, iff the slots at the two given source offsets are identical. { ops.sourceIsSame(sourceOffset, sourceOffset) } -> std::convertible_to; // Returns a positive integer n, if the slot at the given source offset needs n more copies. @@ -187,6 +189,12 @@ private: std::list toVisit{_targetOffset}; std::set visited; + if (_ops.targetIsArbitrary(_targetOffset)) + { + _ops.pushOrDupTarget(_targetOffset); + return true; + } + while (!toVisit.empty()) { auto offset = *toVisit.begin(); @@ -314,6 +322,22 @@ private: // If we still need more slots, produce a suitable one. if (ops.sourceSize() < ops.targetSize()) { + // If we do not need the current top and pushing another slot would make a position we need to fix unreachable, + // pop the current top. + if ( + ops.sourceSize() >= 16 && + (ops.sourceMultiplicity(sourceTop) < 0 || ops.sourceIsJunk(sourceTop)) + ) + { + for (size_t sourceOffset = 0; sourceOffset < ops.sourceSize() - 16; ++sourceOffset) + if (!ops.isCompatible(sourceOffset, sourceOffset)) + { + ops.pop(); + return true; + } + } + + if (!dupDeepSlotIfRequired(ops)) yulAssert(bringUpTargetSlot(ops, ops.sourceSize()), ""); return true; @@ -430,6 +454,10 @@ void createStackLayout(Stack& _currentStack, Stack const& _targetStack, Swap _sw bool sourceIsSame(size_t _lhs, size_t _rhs) { return currentStack.at(_lhs) == currentStack.at(_rhs); } int sourceMultiplicity(size_t _offset) { return multiplicity.at(currentStack.at(_offset)); } int targetMultiplicity(size_t _offset) { return multiplicity.at(targetStack.at(_offset)); } + bool sourceIsJunk(size_t offset) + { + return std::holds_alternative(currentStack.at(offset)); + } bool targetIsArbitrary(size_t offset) { return offset < targetStack.size() && std::holds_alternative(targetStack.at(offset)); diff --git a/libyul/backends/evm/StackLayoutGenerator.cpp b/libyul/backends/evm/StackLayoutGenerator.cpp index 4f9a1e2a1..a6ce399d2 100644 --- a/libyul/backends/evm/StackLayoutGenerator.cpp +++ b/libyul/backends/evm/StackLayoutGenerator.cpp @@ -206,6 +206,13 @@ Stack createIdealLayout(Stack const& _operationOutput, Stack const& _post, Calla }, layout.at(_source)) ); } + bool sourceIsJunk(size_t offset) + { + return std::visit(util::GenericVisitor{ + [&](PreviousSlot const&) { return false; }, + [&](StackSlot const& _slot) { return std::holds_alternative(_slot); }, + }, layout.at(offset)); + } bool sourceIsSame(size_t _lhs, size_t _rhs) { return std::visit(util::GenericVisitor{