mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Further customize max swap/dup.
This commit is contained in:
parent
a467bdcb3c
commit
3ff457c312
@ -433,7 +433,7 @@ map<u256, u256> const& Assembly::optimiseInternal(
|
|||||||
while (iter != m_items.end())
|
while (iter != m_items.end())
|
||||||
{
|
{
|
||||||
KnownState emptyState;
|
KnownState emptyState;
|
||||||
CommonSubexpressionEliminator eliminator{emptyState};
|
CommonSubexpressionEliminator eliminator{emptyState, maxSwap(), maxDup()};
|
||||||
auto orig = iter;
|
auto orig = iter;
|
||||||
iter = eliminator.feedItems(iter, m_items.end(), usesMSize);
|
iter = eliminator.feedItems(iter, m_items.end(), usesMSize);
|
||||||
bool shouldReplace = false;
|
bool shouldReplace = false;
|
||||||
|
@ -62,7 +62,7 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
|
|||||||
for (int height = minHeight; height <= m_state.stackHeight(); ++height)
|
for (int height = minHeight; height <= m_state.stackHeight(); ++height)
|
||||||
targetStackContents[height] = m_state.stackElement(height, SourceLocation());
|
targetStackContents[height] = m_state.stackElement(height, SourceLocation());
|
||||||
|
|
||||||
AssemblyItems items = CSECodeGenerator(m_state.expressionClasses(), m_storeOperations).generateCode(
|
AssemblyItems items = CSECodeGenerator(m_state.expressionClasses(), m_storeOperations, m_maxSwap, m_maxDup).generateCode(
|
||||||
m_initialState.sequenceNumber(),
|
m_initialState.sequenceNumber(),
|
||||||
m_initialState.stackHeight(),
|
m_initialState.stackHeight(),
|
||||||
initialStackContents,
|
initialStackContents,
|
||||||
@ -125,9 +125,11 @@ void CommonSubexpressionEliminator::optimizeBreakingItem()
|
|||||||
|
|
||||||
CSECodeGenerator::CSECodeGenerator(
|
CSECodeGenerator::CSECodeGenerator(
|
||||||
ExpressionClasses& _expressionClasses,
|
ExpressionClasses& _expressionClasses,
|
||||||
vector<CSECodeGenerator::StoreOperation> const& _storeOperations
|
vector<CSECodeGenerator::StoreOperation> const& _storeOperations,
|
||||||
|
unsigned _maxSwap,
|
||||||
|
unsigned _maxDup
|
||||||
):
|
):
|
||||||
m_expressionClasses(_expressionClasses)
|
m_expressionClasses(_expressionClasses), m_maxSwap(_maxSwap), m_maxDup(_maxDup)
|
||||||
{
|
{
|
||||||
for (auto const& store: _storeOperations)
|
for (auto const& store: _storeOperations)
|
||||||
m_storeOperations[make_pair(store.target, store.slot)].push_back(store);
|
m_storeOperations[make_pair(store.target, store.slot)].push_back(store);
|
||||||
@ -472,7 +474,7 @@ void CSECodeGenerator::appendDup(int _fromPosition, SourceLocation const& _locat
|
|||||||
{
|
{
|
||||||
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, util::stackTooDeepString);
|
assertThrow(instructionNum <= static_cast<int>(m_maxDup), StackTooDeepException, util::stackTooDeepString);
|
||||||
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
|
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
|
||||||
appendItem(AssemblyItem(AssemblyItemType::Dup, instructionNum, _location));
|
appendItem(AssemblyItem(AssemblyItemType::Dup, instructionNum, _location));
|
||||||
m_stack[m_stackHeight] = m_stack[_fromPosition];
|
m_stack[m_stackHeight] = m_stack[_fromPosition];
|
||||||
@ -485,7 +487,7 @@ void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition, SourceLocation cons
|
|||||||
if (_fromPosition == m_stackHeight)
|
if (_fromPosition == m_stackHeight)
|
||||||
return;
|
return;
|
||||||
int instructionNum = m_stackHeight - _fromPosition;
|
int instructionNum = m_stackHeight - _fromPosition;
|
||||||
assertThrow(instructionNum <= 16, StackTooDeepException, util::stackTooDeepString);
|
assertThrow(instructionNum <= static_cast<int>(m_maxSwap), StackTooDeepException, util::stackTooDeepString);
|
||||||
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
|
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
|
||||||
appendItem(AssemblyItem(AssemblyItemType::Swap, instructionNum, _location));
|
appendItem(AssemblyItem(AssemblyItemType::Swap, instructionNum, _location));
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ public:
|
|||||||
using Id = ExpressionClasses::Id;
|
using Id = ExpressionClasses::Id;
|
||||||
using StoreOperation = KnownState::StoreOperation;
|
using StoreOperation = KnownState::StoreOperation;
|
||||||
|
|
||||||
explicit CommonSubexpressionEliminator(KnownState const& _state): m_initialState(_state), m_state(_state) {}
|
explicit CommonSubexpressionEliminator(KnownState const& _state, unsigned _maxSwap, unsigned _maxDup): m_initialState(_state), m_state(_state), m_maxSwap(_maxSwap), m_maxDup(_maxDup) {}
|
||||||
|
|
||||||
/// 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.
|
||||||
@ -93,6 +93,8 @@ private:
|
|||||||
/// The item that breaks the basic block, can be nullptr.
|
/// The item that breaks the basic block, can be nullptr.
|
||||||
/// It is usually appended to the block but can be optimized in some cases.
|
/// It is usually appended to the block but can be optimized in some cases.
|
||||||
AssemblyItem const* m_breakingItem = nullptr;
|
AssemblyItem const* m_breakingItem = nullptr;
|
||||||
|
unsigned m_maxSwap = 16;
|
||||||
|
unsigned m_maxDup = 16;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -108,7 +110,7 @@ public:
|
|||||||
|
|
||||||
/// Initializes the code generator with the given classes and store operations.
|
/// Initializes the code generator with the given classes and store operations.
|
||||||
/// The store operations have to be sorted by sequence number in ascending order.
|
/// The store operations have to be sorted by sequence number in ascending order.
|
||||||
CSECodeGenerator(ExpressionClasses& _expressionClasses, StoreOperations const& _storeOperations);
|
CSECodeGenerator(ExpressionClasses& _expressionClasses, StoreOperations const& _storeOperations, unsigned _maxSwap, unsigned _maxDup);
|
||||||
|
|
||||||
/// @returns the assembly items generated from the given requirements
|
/// @returns the assembly items generated from the given requirements
|
||||||
/// @param _initialSequenceNumber starting sequence number, do not generate sequenced operations
|
/// @param _initialSequenceNumber starting sequence number, do not generate sequenced operations
|
||||||
@ -169,6 +171,8 @@ private:
|
|||||||
/// 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<Id> m_finalClasses;
|
std::set<Id> m_finalClasses;
|
||||||
std::map<int, Id> m_targetStack;
|
std::map<int, Id> m_targetStack;
|
||||||
|
unsigned m_maxSwap = 16;
|
||||||
|
unsigned m_maxDup = 16;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class AssemblyItemIterator>
|
template <class AssemblyItemIterator>
|
||||||
|
@ -475,7 +475,7 @@ void CompilerUtils::encodeToMemory(
|
|||||||
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
|
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
|
||||||
dynPointers++;
|
dynPointers++;
|
||||||
assertThrow(
|
assertThrow(
|
||||||
(argSize + dynPointers) < 16,
|
(argSize + dynPointers) < m_context.assembly().maxSwap(),
|
||||||
StackTooDeepError,
|
StackTooDeepError,
|
||||||
util::stackTooDeepString
|
util::stackTooDeepString
|
||||||
);
|
);
|
||||||
@ -536,7 +536,7 @@ void CompilerUtils::encodeToMemory(
|
|||||||
{
|
{
|
||||||
// copy tail pointer (=mem_end - mem_start) to memory
|
// copy tail pointer (=mem_end - mem_start) to memory
|
||||||
assertThrow(
|
assertThrow(
|
||||||
(2 + dynPointers) <= 16,
|
(2 + dynPointers) <= m_context.assembly().maxDup(),
|
||||||
StackTooDeepError,
|
StackTooDeepError,
|
||||||
util::stackTooDeepString
|
util::stackTooDeepString
|
||||||
);
|
);
|
||||||
@ -1415,7 +1415,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
|||||||
unsigned const size = _variable.annotation().type->sizeOnStack();
|
unsigned const size = _variable.annotation().type->sizeOnStack();
|
||||||
solAssert(stackPosition >= size, "Variable size and position mismatch.");
|
solAssert(stackPosition >= size, "Variable size and position mismatch.");
|
||||||
// move variable starting from its top end in the stack
|
// move variable starting from its top end in the stack
|
||||||
if (stackPosition - size + 1 > 16)
|
if (stackPosition - size + 1 > m_context.assembly().maxSwap())
|
||||||
BOOST_THROW_EXCEPTION(
|
BOOST_THROW_EXCEPTION(
|
||||||
StackTooDeepError() <<
|
StackTooDeepError() <<
|
||||||
errinfo_sourceLocation(_variable.location()) <<
|
errinfo_sourceLocation(_variable.location()) <<
|
||||||
@ -1428,7 +1428,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
|||||||
void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
|
void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
|
||||||
{
|
{
|
||||||
assertThrow(
|
assertThrow(
|
||||||
_stackDepth <= 16,
|
_stackDepth <= m_context.assembly().maxDup(),
|
||||||
StackTooDeepError,
|
StackTooDeepError,
|
||||||
util::stackTooDeepString
|
util::stackTooDeepString
|
||||||
);
|
);
|
||||||
@ -1454,7 +1454,7 @@ void CompilerUtils::moveIntoStack(unsigned _stackDepth, unsigned _itemSize)
|
|||||||
void CompilerUtils::rotateStackUp(unsigned _items)
|
void CompilerUtils::rotateStackUp(unsigned _items)
|
||||||
{
|
{
|
||||||
assertThrow(
|
assertThrow(
|
||||||
_items - 1 <= 16,
|
_items - 1 <= m_context.assembly().maxSwap(),
|
||||||
StackTooDeepError,
|
StackTooDeepError,
|
||||||
util::stackTooDeepString
|
util::stackTooDeepString
|
||||||
);
|
);
|
||||||
@ -1465,7 +1465,7 @@ void CompilerUtils::rotateStackUp(unsigned _items)
|
|||||||
void CompilerUtils::rotateStackDown(unsigned _items)
|
void CompilerUtils::rotateStackDown(unsigned _items)
|
||||||
{
|
{
|
||||||
assertThrow(
|
assertThrow(
|
||||||
_items - 1 <= 16,
|
_items - 1 <= m_context.assembly().maxSwap(),
|
||||||
StackTooDeepError,
|
StackTooDeepError,
|
||||||
util::stackTooDeepString
|
util::stackTooDeepString
|
||||||
);
|
);
|
||||||
|
@ -48,7 +48,7 @@ StackVariable::StackVariable(CompilerContext& _compilerContext, VariableDeclarat
|
|||||||
void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
|
void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
|
||||||
{
|
{
|
||||||
unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset);
|
unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset);
|
||||||
if (stackPos + 1 > 16) //@todo correct this by fetching earlier or moving to memory
|
if (stackPos + 1 > m_context.assembly().maxDup()) //@todo correct this by fetching earlier or moving to memory
|
||||||
BOOST_THROW_EXCEPTION(
|
BOOST_THROW_EXCEPTION(
|
||||||
StackTooDeepError() <<
|
StackTooDeepError() <<
|
||||||
errinfo_sourceLocation(_location) <<
|
errinfo_sourceLocation(_location) <<
|
||||||
@ -62,15 +62,17 @@ void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
|
|||||||
void StackVariable::storeValue(Type const&, SourceLocation const& _location, bool _move) const
|
void StackVariable::storeValue(Type const&, SourceLocation const& _location, bool _move) const
|
||||||
{
|
{
|
||||||
unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1;
|
unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1;
|
||||||
if (stackDiff > 16)
|
if (stackDiff > 0)
|
||||||
BOOST_THROW_EXCEPTION(
|
{
|
||||||
StackTooDeepError() <<
|
if (stackDiff > m_context.assembly().maxSwap())
|
||||||
errinfo_sourceLocation(_location) <<
|
BOOST_THROW_EXCEPTION(
|
||||||
util::errinfo_comment(util::stackTooDeepString)
|
StackTooDeepError() <<
|
||||||
);
|
errinfo_sourceLocation(_location) <<
|
||||||
else if (stackDiff > 0)
|
util::errinfo_comment(util::stackTooDeepString)
|
||||||
|
);
|
||||||
for (unsigned i = 0; i < m_size; ++i)
|
for (unsigned i = 0; i < m_size; ++i)
|
||||||
m_context << AssemblyItem(AssemblyItemType::Swap, stackDiff) << Instruction::POP;
|
m_context << AssemblyItem(AssemblyItemType::Swap, stackDiff) << Instruction::POP;
|
||||||
|
}
|
||||||
if (!_move)
|
if (!_move)
|
||||||
retrieveValue(_location);
|
retrieveValue(_location);
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,7 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
|
|||||||
bool foundUnusedSlot = false;
|
bool foundUnusedSlot = false;
|
||||||
for (auto it = m_unusedStackSlots.begin(); it != m_unusedStackSlots.end(); ++it)
|
for (auto it = m_unusedStackSlots.begin(); it != m_unusedStackSlots.end(); ++it)
|
||||||
{
|
{
|
||||||
if (m_assembly.stackHeight() - *it > 17)
|
if (m_assembly.stackHeight() - *it > static_cast<int>(m_assembly.maxSwap() + 1))
|
||||||
continue;
|
continue;
|
||||||
foundUnusedSlot = true;
|
foundUnusedSlot = true;
|
||||||
auto slot = static_cast<size_t>(*it);
|
auto slot = static_cast<size_t>(*it);
|
||||||
@ -454,16 +454,16 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
|||||||
&std::get<Scope::Variable>(virtualFunctionScope->identifiers.at(returnVariable.name))
|
&std::get<Scope::Variable>(virtualFunctionScope->identifiers.at(returnVariable.name))
|
||||||
)) = static_cast<int>(n);
|
)) = static_cast<int>(n);
|
||||||
|
|
||||||
if (stackLayout.size() > 17)
|
if (stackLayout.size() > m_assembly.maxSwap() + 1)
|
||||||
{
|
{
|
||||||
StackTooDeepError error(
|
StackTooDeepError error(
|
||||||
_function.name,
|
_function.name,
|
||||||
YulString{},
|
YulString{},
|
||||||
static_cast<int>(stackLayout.size()) - 17,
|
static_cast<int>(stackLayout.size()) - static_cast<int>(m_assembly.maxSwap() + 1),
|
||||||
"The function " +
|
"The function " +
|
||||||
_function.name.str() +
|
_function.name.str() +
|
||||||
" has " +
|
" has " +
|
||||||
to_string(stackLayout.size() - 17) +
|
to_string(stackLayout.size() - (m_assembly.maxSwap() + 1)) +
|
||||||
" parameters or return variables too many to fit the stack size."
|
" parameters or return variables too many to fit the stack size."
|
||||||
);
|
);
|
||||||
stackError(std::move(error), m_assembly.stackHeight() - static_cast<int>(_function.parameters.size()));
|
stackError(std::move(error), m_assembly.stackHeight() - static_cast<int>(_function.parameters.size()));
|
||||||
@ -777,7 +777,7 @@ size_t CodeTransform::variableHeightDiff(Scope::Variable const& _var, YulString
|
|||||||
yulAssert(m_context->variableStackHeights.count(&_var), "");
|
yulAssert(m_context->variableStackHeights.count(&_var), "");
|
||||||
size_t heightDiff = static_cast<size_t>(m_assembly.stackHeight()) - m_context->variableStackHeights[&_var];
|
size_t heightDiff = static_cast<size_t>(m_assembly.stackHeight()) - m_context->variableStackHeights[&_var];
|
||||||
yulAssert(heightDiff > (_forSwap ? 1 : 0), "Negative stack difference for variable.");
|
yulAssert(heightDiff > (_forSwap ? 1 : 0), "Negative stack difference for variable.");
|
||||||
size_t limit = _forSwap ? 17 : 16;
|
size_t limit = _forSwap ? m_assembly.maxSwap() + 1 : m_assembly.maxDup();
|
||||||
if (heightDiff > limit)
|
if (heightDiff > limit)
|
||||||
{
|
{
|
||||||
m_stackErrors.emplace_back(
|
m_stackErrors.emplace_back(
|
||||||
|
@ -359,7 +359,9 @@ void OptimizedEVMCodeTransform::createStackLayout(std::shared_ptr<DebugData cons
|
|||||||
[&]()
|
[&]()
|
||||||
{
|
{
|
||||||
m_assembly.appendInstruction(evmasm::Instruction::POP);
|
m_assembly.appendInstruction(evmasm::Instruction::POP);
|
||||||
}
|
},
|
||||||
|
m_assembly.maxSwap(),
|
||||||
|
m_assembly.maxDup()
|
||||||
);
|
);
|
||||||
yulAssert(m_assembly.stackHeight() == static_cast<int>(m_stack.size()), "");
|
yulAssert(m_assembly.stackHeight() == static_cast<int>(m_stack.size()), "");
|
||||||
}
|
}
|
||||||
|
@ -127,10 +127,10 @@ private:
|
|||||||
static bool dupDeepSlotIfRequired(ShuffleOperations& _ops)
|
static bool dupDeepSlotIfRequired(ShuffleOperations& _ops)
|
||||||
{
|
{
|
||||||
// Check if the stack is large enough for anything to potentially become unreachable.
|
// Check if the stack is large enough for anything to potentially become unreachable.
|
||||||
if (_ops.sourceSize() < 15)
|
if (_ops.sourceSize() < (_ops.maxDup() - 1))
|
||||||
return false;
|
return false;
|
||||||
// Check whether any deep slot might still be needed later (i.e. we still need to reach it with a DUP or SWAP).
|
// Check whether any deep slot might still be needed later (i.e. we still need to reach it with a DUP or SWAP).
|
||||||
for (size_t sourceOffset: ranges::views::iota(0u, _ops.sourceSize() - 15))
|
for (size_t sourceOffset: ranges::views::iota(0u, _ops.sourceSize() - (_ops.maxDup() - 1)))
|
||||||
{
|
{
|
||||||
// This slot needs to be moved.
|
// This slot needs to be moved.
|
||||||
if (!_ops.isCompatible(sourceOffset, sourceOffset))
|
if (!_ops.isCompatible(sourceOffset, sourceOffset))
|
||||||
@ -255,10 +255,10 @@ private:
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
// We cannot swap that deep.
|
// We cannot swap that deep.
|
||||||
if (ops.sourceSize() - offset - 1 > 16)
|
if (ops.sourceSize() - offset - 1 > ops.maxSwap())
|
||||||
{
|
{
|
||||||
// If there is a reachable slot to be removed, park the current top there.
|
// If there is a reachable slot to be removed, park the current top there.
|
||||||
for (size_t swapDepth: ranges::views::iota(1u, 17u) | ranges::views::reverse)
|
for (size_t swapDepth: ranges::views::iota(1u, ops.maxSwap() + 1) | ranges::views::reverse)
|
||||||
if (ops.sourceMultiplicity(ops.sourceSize() - 1 - swapDepth) < 0)
|
if (ops.sourceMultiplicity(ops.sourceSize() - 1 - swapDepth) < 0)
|
||||||
{
|
{
|
||||||
ops.swap(swapDepth);
|
ops.swap(swapDepth);
|
||||||
@ -326,7 +326,7 @@ private:
|
|||||||
yulAssert(ops.sourceMultiplicity(i) == 0 && (ops.targetIsArbitrary(i) || ops.targetMultiplicity(i) == 0), "");
|
yulAssert(ops.sourceMultiplicity(i) == 0 && (ops.targetIsArbitrary(i) || ops.targetMultiplicity(i) == 0), "");
|
||||||
yulAssert(ops.isCompatible(sourceTop, sourceTop), "");
|
yulAssert(ops.isCompatible(sourceTop, sourceTop), "");
|
||||||
|
|
||||||
auto swappableOffsets = ranges::views::iota(size > 17 ? size - 17 : 0u, size);
|
auto swappableOffsets = ranges::views::iota(size > (ops.maxSwap() + 1) ? size - (ops.maxSwap() + 1) : 0u, size);
|
||||||
|
|
||||||
// If we find a lower slot that is out of position, but also compatible with the top, swap that up.
|
// If we find a lower slot that is out of position, but also compatible with the top, swap that up.
|
||||||
for (size_t offset: swappableOffsets)
|
for (size_t offset: swappableOffsets)
|
||||||
@ -386,7 +386,7 @@ private:
|
|||||||
/// its argument to the stack top.
|
/// its argument to the stack top.
|
||||||
/// @a _pop is a function with signature void() that is called when the top most slot is popped.
|
/// @a _pop is a function with signature void() that is called when the top most slot is popped.
|
||||||
template<typename Swap, typename PushOrDup, typename Pop>
|
template<typename Swap, typename PushOrDup, typename Pop>
|
||||||
void createStackLayout(Stack& _currentStack, Stack const& _targetStack, Swap _swap, PushOrDup _pushOrDup, Pop _pop)
|
void createStackLayout(Stack& _currentStack, Stack const& _targetStack, Swap _swap, PushOrDup _pushOrDup, Pop _pop, size_t _maxSwap, size_t _maxDup)
|
||||||
{
|
{
|
||||||
struct ShuffleOperations
|
struct ShuffleOperations
|
||||||
{
|
{
|
||||||
@ -396,18 +396,24 @@ void createStackLayout(Stack& _currentStack, Stack const& _targetStack, Swap _sw
|
|||||||
PushOrDup pushOrDupCallback;
|
PushOrDup pushOrDupCallback;
|
||||||
Pop popCallback;
|
Pop popCallback;
|
||||||
std::map<StackSlot, int> multiplicity;
|
std::map<StackSlot, int> multiplicity;
|
||||||
|
size_t const m_maxSwap = 16;
|
||||||
|
size_t const m_maxDup = 16;
|
||||||
ShuffleOperations(
|
ShuffleOperations(
|
||||||
Stack& _currentStack,
|
Stack& _currentStack,
|
||||||
Stack const& _targetStack,
|
Stack const& _targetStack,
|
||||||
Swap _swap,
|
Swap _swap,
|
||||||
PushOrDup _pushOrDup,
|
PushOrDup _pushOrDup,
|
||||||
Pop _pop
|
Pop _pop,
|
||||||
|
size_t _maxSwap,
|
||||||
|
size_t _maxDup
|
||||||
):
|
):
|
||||||
currentStack(_currentStack),
|
currentStack(_currentStack),
|
||||||
targetStack(_targetStack),
|
targetStack(_targetStack),
|
||||||
swapCallback(_swap),
|
swapCallback(_swap),
|
||||||
pushOrDupCallback(_pushOrDup),
|
pushOrDupCallback(_pushOrDup),
|
||||||
popCallback(_pop)
|
popCallback(_pop),
|
||||||
|
m_maxSwap(_maxSwap),
|
||||||
|
m_maxDup(_maxDup)
|
||||||
{
|
{
|
||||||
for (auto const& slot: currentStack)
|
for (auto const& slot: currentStack)
|
||||||
--multiplicity[slot];
|
--multiplicity[slot];
|
||||||
@ -417,6 +423,8 @@ void createStackLayout(Stack& _currentStack, Stack const& _targetStack, Swap _sw
|
|||||||
else
|
else
|
||||||
++multiplicity[slot];
|
++multiplicity[slot];
|
||||||
}
|
}
|
||||||
|
size_t maxSwap() const { return m_maxSwap; }
|
||||||
|
size_t maxDup() const { return m_maxDup; }
|
||||||
bool isCompatible(size_t _source, size_t _target)
|
bool isCompatible(size_t _source, size_t _target)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
@ -454,7 +462,7 @@ void createStackLayout(Stack& _currentStack, Stack const& _targetStack, Swap _sw
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Shuffler<ShuffleOperations>::shuffle(_currentStack, _targetStack, _swap, _pushOrDup, _pop);
|
Shuffler<ShuffleOperations>::shuffle(_currentStack, _targetStack, _swap, _pushOrDup, _pop, _maxSwap, _maxDup);
|
||||||
|
|
||||||
yulAssert(_currentStack.size() == _targetStack.size(), "");
|
yulAssert(_currentStack.size() == _targetStack.size(), "");
|
||||||
for (auto&& [current, target]: ranges::zip_view(_currentStack, _targetStack))
|
for (auto&& [current, target]: ranges::zip_view(_currentStack, _targetStack))
|
||||||
|
@ -96,7 +96,7 @@ StackLayoutGenerator::StackLayoutGenerator(StackLayout& _layout): m_layout(_layo
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
/// @returns all stack too deep errors that would occur when shuffling @a _source to @a _target.
|
/// @returns all stack too deep errors that would occur when shuffling @a _source to @a _target.
|
||||||
vector<StackLayoutGenerator::StackTooDeep> findStackTooDeep(Stack const& _source, Stack const& _target)
|
vector<StackLayoutGenerator::StackTooDeep> findStackTooDeep(Stack const& _source, Stack const& _target, size_t _maxSwap, size_t _maxDup)
|
||||||
{
|
{
|
||||||
Stack currentStack = _source;
|
Stack currentStack = _source;
|
||||||
vector<StackLayoutGenerator::StackTooDeep> stackTooDeepErrors;
|
vector<StackLayoutGenerator::StackTooDeep> stackTooDeepErrors;
|
||||||
@ -113,9 +113,9 @@ vector<StackLayoutGenerator::StackTooDeep> findStackTooDeep(Stack const& _source
|
|||||||
_target,
|
_target,
|
||||||
[&](unsigned _i)
|
[&](unsigned _i)
|
||||||
{
|
{
|
||||||
if (_i > 16)
|
if (_i > _maxSwap)
|
||||||
stackTooDeepErrors.emplace_back(StackLayoutGenerator::StackTooDeep{
|
stackTooDeepErrors.emplace_back(StackLayoutGenerator::StackTooDeep{
|
||||||
_i - 16,
|
_i - _maxSwap,
|
||||||
getVariableChoices(currentStack | ranges::views::take_last(_i + 1))
|
getVariableChoices(currentStack | ranges::views::take_last(_i + 1))
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -125,14 +125,16 @@ vector<StackLayoutGenerator::StackTooDeep> findStackTooDeep(Stack const& _source
|
|||||||
return;
|
return;
|
||||||
if (
|
if (
|
||||||
auto depth = util::findOffset(currentStack | ranges::views::reverse, _slot);
|
auto depth = util::findOffset(currentStack | ranges::views::reverse, _slot);
|
||||||
depth && *depth >= 16
|
depth && *depth >= _maxDup
|
||||||
)
|
)
|
||||||
stackTooDeepErrors.emplace_back(StackLayoutGenerator::StackTooDeep{
|
stackTooDeepErrors.emplace_back(StackLayoutGenerator::StackTooDeep{
|
||||||
*depth - 15,
|
*depth - (_maxDup - 1),
|
||||||
getVariableChoices(currentStack | ranges::views::take_last(*depth + 1))
|
getVariableChoices(currentStack | ranges::views::take_last(*depth + 1))
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[&]() {}
|
[&]() {},
|
||||||
|
_maxSwap,
|
||||||
|
_maxDup
|
||||||
);
|
);
|
||||||
return stackTooDeepErrors;
|
return stackTooDeepErrors;
|
||||||
}
|
}
|
||||||
@ -142,7 +144,7 @@ vector<StackLayoutGenerator::StackTooDeep> findStackTooDeep(Stack const& _source
|
|||||||
/// If @a _generateSlotOnTheFly returns true for a slot, this slot should not occur in the ideal stack, but
|
/// If @a _generateSlotOnTheFly returns true for a slot, this slot should not occur in the ideal stack, but
|
||||||
/// rather be generated on the fly during shuffling.
|
/// rather be generated on the fly during shuffling.
|
||||||
template<typename Callable>
|
template<typename Callable>
|
||||||
Stack createIdealLayout(Stack const& _operationOutput, Stack const& _post, Callable _generateSlotOnTheFly)
|
Stack createIdealLayout(Stack const& _operationOutput, Stack const& _post, Callable _generateSlotOnTheFly, size_t _maxSwap, size_t _maxDup)
|
||||||
{
|
{
|
||||||
struct PreviousSlot { size_t slot; };
|
struct PreviousSlot { size_t slot; };
|
||||||
|
|
||||||
@ -174,11 +176,15 @@ Stack createIdealLayout(Stack const& _operationOutput, Stack const& _post, Calla
|
|||||||
std::set<StackSlot> outputs;
|
std::set<StackSlot> outputs;
|
||||||
std::map<StackSlot, int> multiplicity;
|
std::map<StackSlot, int> multiplicity;
|
||||||
Callable generateSlotOnTheFly;
|
Callable generateSlotOnTheFly;
|
||||||
|
size_t m_maxSwap = 16;
|
||||||
|
size_t m_maxDup = 16;
|
||||||
ShuffleOperations(
|
ShuffleOperations(
|
||||||
vector<variant<PreviousSlot, StackSlot>>& _layout,
|
vector<variant<PreviousSlot, StackSlot>>& _layout,
|
||||||
Stack const& _post,
|
Stack const& _post,
|
||||||
Callable _generateSlotOnTheFly
|
Callable _generateSlotOnTheFly,
|
||||||
): layout(_layout), post(_post), generateSlotOnTheFly(_generateSlotOnTheFly)
|
size_t _maxSwap,
|
||||||
|
size_t _maxDup
|
||||||
|
): layout(_layout), post(_post), generateSlotOnTheFly(_generateSlotOnTheFly), m_maxSwap(_maxSwap), m_maxDup(_maxDup)
|
||||||
{
|
{
|
||||||
for (auto const& layoutSlot: layout)
|
for (auto const& layoutSlot: layout)
|
||||||
if (StackSlot const* slot = get_if<StackSlot>(&layoutSlot))
|
if (StackSlot const* slot = get_if<StackSlot>(&layoutSlot))
|
||||||
@ -191,6 +197,8 @@ Stack createIdealLayout(Stack const& _operationOutput, Stack const& _post, Calla
|
|||||||
if (outputs.count(slot) || generateSlotOnTheFly(slot))
|
if (outputs.count(slot) || generateSlotOnTheFly(slot))
|
||||||
++multiplicity[slot];
|
++multiplicity[slot];
|
||||||
}
|
}
|
||||||
|
size_t maxSwap() const { return m_maxSwap; }
|
||||||
|
size_t maxDup() const { return m_maxDup; }
|
||||||
bool isCompatible(size_t _source, size_t _target)
|
bool isCompatible(size_t _source, size_t _target)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
@ -241,7 +249,7 @@ Stack createIdealLayout(Stack const& _operationOutput, Stack const& _post, Calla
|
|||||||
void pop() { layout.pop_back(); }
|
void pop() { layout.pop_back(); }
|
||||||
void pushOrDupTarget(size_t _offset) { layout.push_back(post.at(_offset)); }
|
void pushOrDupTarget(size_t _offset) { layout.push_back(post.at(_offset)); }
|
||||||
};
|
};
|
||||||
Shuffler<ShuffleOperations>::shuffle(layout, _post, _generateSlotOnTheFly);
|
Shuffler<ShuffleOperations>::shuffle(layout, _post, _generateSlotOnTheFly, _maxSwap, _maxDup);
|
||||||
|
|
||||||
// Now we can construct the ideal layout before the operation.
|
// Now we can construct the ideal layout before the operation.
|
||||||
// "layout" has shuffled the PreviousSlot{x} to new places using minimal operations to move the operation
|
// "layout" has shuffled the PreviousSlot{x} to new places using minimal operations to move the operation
|
||||||
@ -280,7 +288,7 @@ Stack StackLayoutGenerator::propagateStackThroughOperation(Stack _exitStack, CFG
|
|||||||
|
|
||||||
// Determine the ideal permutation of the slots in _exitLayout that are not operation outputs (and not to be
|
// Determine the ideal permutation of the slots in _exitLayout that are not operation outputs (and not to be
|
||||||
// generated on the fly), s.t. shuffling the `stack + _operation.output` to _exitLayout is cheap.
|
// generated on the fly), s.t. shuffling the `stack + _operation.output` to _exitLayout is cheap.
|
||||||
Stack stack = createIdealLayout(_operation.output, _exitStack, generateSlotOnTheFly);
|
Stack stack = createIdealLayout(_operation.output, _exitStack, generateSlotOnTheFly, maxSwap(), maxDup());
|
||||||
|
|
||||||
// Make sure the resulting previous slots do not overlap with any assignmed variables.
|
// Make sure the resulting previous slots do not overlap with any assignmed variables.
|
||||||
if (auto const* assignment = get_if<CFG::Assignment>(&_operation.operation))
|
if (auto const* assignment = get_if<CFG::Assignment>(&_operation.operation))
|
||||||
@ -304,7 +312,7 @@ Stack StackLayoutGenerator::propagateStackThroughOperation(Stack _exitStack, CFG
|
|||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
else if (auto offset = util::findOffset(stack | ranges::views::reverse | ranges::views::drop(1), stack.back()))
|
else if (auto offset = util::findOffset(stack | ranges::views::reverse | ranges::views::drop(1), stack.back()))
|
||||||
{
|
{
|
||||||
if (*offset + 2 < 16)
|
if (*offset + 2 < maxDup())
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
@ -322,7 +330,7 @@ Stack StackLayoutGenerator::propagateStackThroughBlock(Stack _exitStack, CFG::Ba
|
|||||||
for (auto&& [idx, operation]: _block.operations | ranges::views::enumerate | ranges::views::reverse)
|
for (auto&& [idx, operation]: _block.operations | ranges::views::enumerate | ranges::views::reverse)
|
||||||
{
|
{
|
||||||
Stack newStack = propagateStackThroughOperation(stack, operation, _aggressiveStackCompression);
|
Stack newStack = propagateStackThroughOperation(stack, operation, _aggressiveStackCompression);
|
||||||
if (!_aggressiveStackCompression && !findStackTooDeep(newStack, stack).empty())
|
if (!_aggressiveStackCompression && !findStackTooDeep(newStack, stack, maxSwap(), maxDup()).empty())
|
||||||
// If we had stack errors, run again with aggressive stack compression.
|
// If we had stack errors, run again with aggressive stack compression.
|
||||||
return propagateStackThroughBlock(std::move(_exitStack), _block, true);
|
return propagateStackThroughBlock(std::move(_exitStack), _block, true);
|
||||||
stack = std::move(newStack);
|
stack = std::move(newStack);
|
||||||
@ -544,7 +552,7 @@ void StackLayoutGenerator::stitchConditionalJumps(CFG::BasicBlock const& _block)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Stack StackLayoutGenerator::combineStack(Stack const& _stack1, Stack const& _stack2)
|
Stack StackLayoutGenerator::combineStack(Stack const& _stack1, Stack const& _stack2) const
|
||||||
{
|
{
|
||||||
// TODO: it would be nicer to replace this by a constructive algorithm.
|
// TODO: it would be nicer to replace this by a constructive algorithm.
|
||||||
// Currently it uses a reduced version of the Heap Algorithm to partly brute-force, which seems
|
// Currently it uses a reduced version of the Heap Algorithm to partly brute-force, which seems
|
||||||
@ -580,18 +588,18 @@ Stack StackLayoutGenerator::combineStack(Stack const& _stack1, Stack const& _sta
|
|||||||
auto evaluate = [&](Stack const& _candidate) -> size_t {
|
auto evaluate = [&](Stack const& _candidate) -> size_t {
|
||||||
size_t numOps = 0;
|
size_t numOps = 0;
|
||||||
Stack testStack = _candidate;
|
Stack testStack = _candidate;
|
||||||
auto swap = [&](unsigned _swapDepth) { ++numOps; if (_swapDepth > 16) numOps += 1000; };
|
auto swap = [&](unsigned _swapDepth) { ++numOps; if (_swapDepth > maxSwap()) numOps += 1000; };
|
||||||
auto dupOrPush = [&](StackSlot const& _slot)
|
auto dupOrPush = [&](StackSlot const& _slot)
|
||||||
{
|
{
|
||||||
if (canBeFreelyGenerated(_slot))
|
if (canBeFreelyGenerated(_slot))
|
||||||
return;
|
return;
|
||||||
auto depth = util::findOffset(ranges::concat_view(commonPrefix, testStack) | ranges::views::reverse, _slot);
|
auto depth = util::findOffset(ranges::concat_view(commonPrefix, testStack) | ranges::views::reverse, _slot);
|
||||||
if (depth && *depth >= 16)
|
if (depth && *depth >= maxDup())
|
||||||
numOps += 1000;
|
numOps += 1000;
|
||||||
};
|
};
|
||||||
createStackLayout(testStack, stack1Tail, swap, dupOrPush, [&](){});
|
createStackLayout(testStack, stack1Tail, swap, dupOrPush, [&](){}, maxSwap(), maxDup());
|
||||||
testStack = _candidate;
|
testStack = _candidate;
|
||||||
createStackLayout(testStack, stack2Tail, swap, dupOrPush, [&](){});
|
createStackLayout(testStack, stack2Tail, swap, dupOrPush, [&](){}, maxSwap(), maxDup());
|
||||||
return numOps;
|
return numOps;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -642,7 +650,7 @@ vector<StackLayoutGenerator::StackTooDeep> StackLayoutGenerator::reportStackTooD
|
|||||||
{
|
{
|
||||||
Stack& operationEntry = m_layout.operationEntryLayout.at(&operation);
|
Stack& operationEntry = m_layout.operationEntryLayout.at(&operation);
|
||||||
|
|
||||||
stackTooDeepErrors += findStackTooDeep(currentStack, operationEntry);
|
stackTooDeepErrors += findStackTooDeep(currentStack, operationEntry, maxSwap(), maxDup());
|
||||||
currentStack = operationEntry;
|
currentStack = operationEntry;
|
||||||
for (size_t i = 0; i < operation.input.size(); i++)
|
for (size_t i = 0; i < operation.input.size(); i++)
|
||||||
currentStack.pop_back();
|
currentStack.pop_back();
|
||||||
@ -656,7 +664,7 @@ vector<StackLayoutGenerator::StackTooDeep> StackLayoutGenerator::reportStackTooD
|
|||||||
[&](CFG::BasicBlock::Jump const& _jump)
|
[&](CFG::BasicBlock::Jump const& _jump)
|
||||||
{
|
{
|
||||||
Stack const& targetLayout = m_layout.blockInfos.at(_jump.target).entryLayout;
|
Stack const& targetLayout = m_layout.blockInfos.at(_jump.target).entryLayout;
|
||||||
stackTooDeepErrors += findStackTooDeep(currentStack, targetLayout);
|
stackTooDeepErrors += findStackTooDeep(currentStack, targetLayout, maxSwap(), maxDup());
|
||||||
|
|
||||||
if (!_jump.backwards)
|
if (!_jump.backwards)
|
||||||
_addChild(_jump.target);
|
_addChild(_jump.target);
|
||||||
@ -667,7 +675,7 @@ vector<StackLayoutGenerator::StackTooDeep> StackLayoutGenerator::reportStackTooD
|
|||||||
m_layout.blockInfos.at(_conditionalJump.zero).entryLayout,
|
m_layout.blockInfos.at(_conditionalJump.zero).entryLayout,
|
||||||
m_layout.blockInfos.at(_conditionalJump.nonZero).entryLayout
|
m_layout.blockInfos.at(_conditionalJump.nonZero).entryLayout
|
||||||
})
|
})
|
||||||
stackTooDeepErrors += findStackTooDeep(currentStack, targetLayout);
|
stackTooDeepErrors += findStackTooDeep(currentStack, targetLayout, maxSwap(), maxDup());
|
||||||
|
|
||||||
_addChild(_conditionalJump.zero);
|
_addChild(_conditionalJump.zero);
|
||||||
_addChild(_conditionalJump.nonZero);
|
_addChild(_conditionalJump.nonZero);
|
||||||
@ -679,7 +687,7 @@ vector<StackLayoutGenerator::StackTooDeep> StackLayoutGenerator::reportStackTooD
|
|||||||
return stackTooDeepErrors;
|
return stackTooDeepErrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stack StackLayoutGenerator::compressStack(Stack _stack)
|
Stack StackLayoutGenerator::compressStack(Stack _stack) const
|
||||||
{
|
{
|
||||||
optional<size_t> firstDupOffset;
|
optional<size_t> firstDupOffset;
|
||||||
do
|
do
|
||||||
@ -697,7 +705,7 @@ Stack StackLayoutGenerator::compressStack(Stack _stack)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (auto dupDepth = util::findOffset(_stack | ranges::views::reverse | ranges::views::drop(depth + 1), slot))
|
else if (auto dupDepth = util::findOffset(_stack | ranges::views::reverse | ranges::views::drop(depth + 1), slot))
|
||||||
if (depth + *dupDepth <= 16)
|
if (depth + *dupDepth <= maxDup())
|
||||||
{
|
{
|
||||||
firstDupOffset = _stack.size() - depth - 1;
|
firstDupOffset = _stack.size() - depth - 1;
|
||||||
break;
|
break;
|
||||||
@ -766,7 +774,7 @@ void StackLayoutGenerator::fillInJunk(CFG::BasicBlock const& _block, CFG::Functi
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
auto pop = [&]() { opGas += evmasm::GasMeter::runGas(evmasm::Instruction::POP); };
|
auto pop = [&]() { opGas += evmasm::GasMeter::runGas(evmasm::Instruction::POP); };
|
||||||
createStackLayout(_source, _target, swap, dupOrPush, pop);
|
createStackLayout(_source, _target, swap, dupOrPush, pop, maxSwap(), maxDup());
|
||||||
return opGas;
|
return opGas;
|
||||||
};
|
};
|
||||||
/// @returns the number of junk slots to be prepended to @a _targetLayout for an optimal transition from
|
/// @returns the number of junk slots to be prepended to @a _targetLayout for an optimal transition from
|
||||||
|
@ -100,7 +100,7 @@ private:
|
|||||||
|
|
||||||
/// Calculates the ideal stack layout, s.t. both @a _stack1 and @a _stack2 can be achieved with minimal
|
/// Calculates the ideal stack layout, s.t. both @a _stack1 and @a _stack2 can be achieved with minimal
|
||||||
/// stack shuffling when starting from the returned layout.
|
/// stack shuffling when starting from the returned layout.
|
||||||
static Stack combineStack(Stack const& _stack1, Stack const& _stack2);
|
Stack combineStack(Stack const& _stack1, Stack const& _stack2) const;
|
||||||
|
|
||||||
/// Walks through the CFG and reports any stack too deep errors that would occur when generating code for it
|
/// Walks through the CFG and reports any stack too deep errors that would occur when generating code for it
|
||||||
/// without countermeasures.
|
/// without countermeasures.
|
||||||
@ -109,7 +109,7 @@ private:
|
|||||||
/// @returns a copy of @a _stack stripped of all duplicates and slots that can be freely generated.
|
/// @returns a copy of @a _stack stripped of all duplicates and slots that can be freely generated.
|
||||||
/// Attempts to create a layout that requires a minimal amount of operations to reconstruct the original
|
/// Attempts to create a layout that requires a minimal amount of operations to reconstruct the original
|
||||||
/// stack @a _stack.
|
/// stack @a _stack.
|
||||||
static Stack compressStack(Stack _stack);
|
Stack compressStack(Stack _stack) const;
|
||||||
|
|
||||||
//// Fills in junk when entering branches that do not need a clean stack in case the result is cheaper.
|
//// Fills in junk when entering branches that do not need a clean stack in case the result is cheaper.
|
||||||
void fillInJunk(CFG::BasicBlock const& _block, CFG::FunctionInfo const* _functionInfo = nullptr);
|
void fillInJunk(CFG::BasicBlock const& _block, CFG::FunctionInfo const* _functionInfo = nullptr);
|
||||||
|
@ -72,7 +72,7 @@ namespace
|
|||||||
bool usesMsize = ranges::any_of(_input, [](AssemblyItem const& _i) {
|
bool usesMsize = ranges::any_of(_input, [](AssemblyItem const& _i) {
|
||||||
return _i == AssemblyItem{Instruction::MSIZE} || _i.type() == VerbatimBytecode;
|
return _i == AssemblyItem{Instruction::MSIZE} || _i.type() == VerbatimBytecode;
|
||||||
});
|
});
|
||||||
evmasm::CommonSubexpressionEliminator cse(_state);
|
evmasm::CommonSubexpressionEliminator cse(_state, 16u, 16u);
|
||||||
BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), usesMsize) == input.end());
|
BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), usesMsize) == input.end());
|
||||||
AssemblyItems output = cse.getOptimizedItems();
|
AssemblyItems output = cse.getOptimizedItems();
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ namespace
|
|||||||
while (iter != _input.end())
|
while (iter != _input.end())
|
||||||
{
|
{
|
||||||
KnownState emptyState;
|
KnownState emptyState;
|
||||||
CommonSubexpressionEliminator eliminator{emptyState};
|
CommonSubexpressionEliminator eliminator{emptyState, 16u, 16u};
|
||||||
auto orig = iter;
|
auto orig = iter;
|
||||||
iter = eliminator.feedItems(iter, _input.end(), usesMSize);
|
iter = eliminator.feedItems(iter, _input.end(), usesMSize);
|
||||||
bool shouldReplace = false;
|
bool shouldReplace = false;
|
||||||
@ -190,7 +190,7 @@ BOOST_AUTO_TEST_CASE(cse_assign_immutable_breaks)
|
|||||||
Instruction::ORIGIN
|
Instruction::ORIGIN
|
||||||
});
|
});
|
||||||
|
|
||||||
evmasm::CommonSubexpressionEliminator cse{evmasm::KnownState()};
|
evmasm::CommonSubexpressionEliminator cse{evmasm::KnownState(), 16u, 16u};
|
||||||
// Make sure CSE breaks after AssignImmutable.
|
// Make sure CSE breaks after AssignImmutable.
|
||||||
BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), false) == input.begin() + 2);
|
BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), false) == input.begin() + 2);
|
||||||
}
|
}
|
||||||
@ -198,7 +198,7 @@ BOOST_AUTO_TEST_CASE(cse_assign_immutable_breaks)
|
|||||||
BOOST_AUTO_TEST_CASE(cse_intermediate_swap)
|
BOOST_AUTO_TEST_CASE(cse_intermediate_swap)
|
||||||
{
|
{
|
||||||
evmasm::KnownState state;
|
evmasm::KnownState state;
|
||||||
evmasm::CommonSubexpressionEliminator cse(state);
|
evmasm::CommonSubexpressionEliminator cse(state, 16u, 16u);
|
||||||
AssemblyItems input{
|
AssemblyItems input{
|
||||||
Instruction::SWAP1, Instruction::POP, Instruction::ADD, u256(0), Instruction::SWAP1,
|
Instruction::SWAP1, Instruction::POP, Instruction::ADD, u256(0), Instruction::SWAP1,
|
||||||
Instruction::SLOAD, Instruction::SWAP1, u256(100), Instruction::EXP, Instruction::SWAP1,
|
Instruction::SLOAD, Instruction::SWAP1, u256(100), Instruction::EXP, Instruction::SWAP1,
|
||||||
|
@ -46,7 +46,7 @@ BOOST_AUTO_TEST_CASE(swap_cycle)
|
|||||||
FunctionReturnLabelSlot{function}, JunkSlot{}, JunkSlot{}
|
FunctionReturnLabelSlot{function}, JunkSlot{}, JunkSlot{}
|
||||||
};
|
};
|
||||||
// Used to hit a swapping cycle.
|
// Used to hit a swapping cycle.
|
||||||
createStackLayout(sourceStack, targetStack, [](auto){}, [](auto){}, [](){});
|
createStackLayout(sourceStack, targetStack, [](auto){}, [](auto){}, [](){}, 16u, 16u);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
24
test/libyul/evmCodeTransform/test.yul
Normal file
24
test/libyul/evmCodeTransform/test.yul
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
pop(addmod(addmod(0x80000000000000000000000000000000000, 0xCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC, 0x800000000000000000000000000000000000), 0xCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC, 0x8000000000000000000000000000000000000))
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// stackOptimization: true
|
||||||
|
// ----
|
||||||
|
// /* "":240:279 */
|
||||||
|
// 0x08000000000000000000000000000000000000
|
||||||
|
// /* "":172:238 */
|
||||||
|
// 0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
// /* "":131:169 */
|
||||||
|
// 0x800000000000000000000000000000000000
|
||||||
|
// /* "":17:170 */
|
||||||
|
// dup2
|
||||||
|
// /* "":24:61 */
|
||||||
|
// 0x080000000000000000000000000000000000
|
||||||
|
// /* "":17:170 */
|
||||||
|
// addmod
|
||||||
|
// /* "":10:280 */
|
||||||
|
// addmod
|
||||||
|
// /* "":6:281 */
|
||||||
|
// pop
|
||||||
|
// /* "":0:283 */
|
||||||
|
// stop
|
105
test/libyul/evmCodeTransform/test2.yul
Normal file
105
test/libyul/evmCodeTransform/test2.yul
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
{
|
||||||
|
for { let i_0 := 0 } lt(i_0, 0x60) { i_0 := add(i_0, 0x20) }
|
||||||
|
{
|
||||||
|
switch mload(0x1fffffffffffffffffffffffff)
|
||||||
|
case 0x1ffffffffffffffffffffffffff { }
|
||||||
|
default {
|
||||||
|
for { let i_1 := 0 } lt(i_1, 0x60) { i_1 := add(i_1, 0x20) }
|
||||||
|
{
|
||||||
|
switch 0x1fffffffffffffffffffffffffff
|
||||||
|
case 0x1ffffffffffffffffffffffffffff { }
|
||||||
|
default { break }
|
||||||
|
continue
|
||||||
|
let x_4, x_5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let x_6, x_7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// stackOptimization: true
|
||||||
|
// ----
|
||||||
|
// /* "":23:24 */
|
||||||
|
// 0x00
|
||||||
|
// /* "":27:40 */
|
||||||
|
// tag_1:
|
||||||
|
// /* "":35:39 */
|
||||||
|
// 0x60
|
||||||
|
// /* "":27:40 */
|
||||||
|
// dup2
|
||||||
|
// lt
|
||||||
|
// tag_2
|
||||||
|
// jumpi
|
||||||
|
// /* "":6:527 */
|
||||||
|
// tag_3:
|
||||||
|
// stop
|
||||||
|
// /* "":71:527 */
|
||||||
|
// tag_2:
|
||||||
|
// /* "":59:63 */
|
||||||
|
// 0x20
|
||||||
|
// /* "":88:123 */
|
||||||
|
// swap1
|
||||||
|
// /* "":94:122 */
|
||||||
|
// 0x1fffffffffffffffffffffffff
|
||||||
|
// /* "":88:123 */
|
||||||
|
// mload
|
||||||
|
// /* "":137:166 */
|
||||||
|
// 0x01ffffffffffffffffffffffffff
|
||||||
|
// /* "":132:170 */
|
||||||
|
// eq
|
||||||
|
// tag_4
|
||||||
|
// jumpi
|
||||||
|
// /* "":81:500 */
|
||||||
|
// tag_5:
|
||||||
|
// /* "":218:219 */
|
||||||
|
// 0x00
|
||||||
|
// /* "":222:235 */
|
||||||
|
// tag_6:
|
||||||
|
// /* "":230:234 */
|
||||||
|
// 0x60
|
||||||
|
// /* "":222:235 */
|
||||||
|
// dup2
|
||||||
|
// lt
|
||||||
|
// tag_7
|
||||||
|
// jumpi
|
||||||
|
// /* "":201:490 */
|
||||||
|
// tag_8:
|
||||||
|
// /* "":187:500 */
|
||||||
|
// pop
|
||||||
|
// /* "":81:500 */
|
||||||
|
// tag_9:
|
||||||
|
// /* "":509:521 */
|
||||||
|
// 0x00
|
||||||
|
// dup1
|
||||||
|
// /* "":71:527 */
|
||||||
|
// pop
|
||||||
|
// pop
|
||||||
|
// /* "":50:64 */
|
||||||
|
// add
|
||||||
|
// /* "":41:66 */
|
||||||
|
// jump(tag_1)
|
||||||
|
// /* "":274:490 */
|
||||||
|
// tag_7:
|
||||||
|
// /* "":299:329 */
|
||||||
|
// 0x1fffffffffffffffffffffffffff
|
||||||
|
// /* "":353:384 */
|
||||||
|
// 0x01ffffffffffffffffffffffffffff
|
||||||
|
// /* "":346:388 */
|
||||||
|
// eq
|
||||||
|
// tag_10
|
||||||
|
// jumpi
|
||||||
|
// /* "":292:422 */
|
||||||
|
// tag_11:
|
||||||
|
// /* "":415:420 */
|
||||||
|
// jump(tag_8)
|
||||||
|
// /* "":385:388 */
|
||||||
|
// tag_10:
|
||||||
|
// dup3
|
||||||
|
// swap1
|
||||||
|
// /* "":245:259 */
|
||||||
|
// add
|
||||||
|
// /* "":236:261 */
|
||||||
|
// jump(tag_6)
|
||||||
|
// /* "":167:170 */
|
||||||
|
// tag_4:
|
||||||
|
// jump(tag_9)
|
132
test/libyul/evmCodeTransform/test3.yul
Normal file
132
test/libyul/evmCodeTransform/test3.yul
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
{
|
||||||
|
for { let i_0 := 0 } lt(i_0, 0x60) { i_0 := add(i_0, 0x20) }
|
||||||
|
{
|
||||||
|
switch mload(0x1fffffffffffffffffffffffff)
|
||||||
|
case 0x1ffffffffffffffffffffffffff { }
|
||||||
|
default {
|
||||||
|
for { let i_1 := 0 } lt(i_1, 0x60) { i_1 := add(i_1, 0x20) }
|
||||||
|
{
|
||||||
|
switch 0x1fffffffffffffffffffffffffff
|
||||||
|
case 0x1ffffffffffffffffffffffffffff { }
|
||||||
|
default { break }
|
||||||
|
continue
|
||||||
|
let x_4, x_5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let x_6, x_7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// stackOptimization: false
|
||||||
|
// ----
|
||||||
|
// /* "":23:24 */
|
||||||
|
// 0x00
|
||||||
|
// /* "":6:527 */
|
||||||
|
// tag_1:
|
||||||
|
// /* "":35:39 */
|
||||||
|
// 0x60
|
||||||
|
// /* "":30:33 */
|
||||||
|
// dup2
|
||||||
|
// /* "":27:40 */
|
||||||
|
// lt
|
||||||
|
// /* "":6:527 */
|
||||||
|
// iszero
|
||||||
|
// tag_3
|
||||||
|
// jumpi
|
||||||
|
// /* "":94:122 */
|
||||||
|
// 0x1fffffffffffffffffffffffff
|
||||||
|
// /* "":88:123 */
|
||||||
|
// mload
|
||||||
|
// /* "":137:166 */
|
||||||
|
// 0x01ffffffffffffffffffffffffff
|
||||||
|
// /* "":132:170 */
|
||||||
|
// dup2
|
||||||
|
// eq
|
||||||
|
// tag_5
|
||||||
|
// jumpi
|
||||||
|
// /* "":218:219 */
|
||||||
|
// 0x00
|
||||||
|
// /* "":201:490 */
|
||||||
|
// tag_6:
|
||||||
|
// /* "":230:234 */
|
||||||
|
// 0x60
|
||||||
|
// /* "":225:228 */
|
||||||
|
// dup2
|
||||||
|
// /* "":222:235 */
|
||||||
|
// lt
|
||||||
|
// /* "":201:490 */
|
||||||
|
// iszero
|
||||||
|
// tag_8
|
||||||
|
// jumpi
|
||||||
|
// /* "":299:329 */
|
||||||
|
// 0x1fffffffffffffffffffffffffff
|
||||||
|
// /* "":353:384 */
|
||||||
|
// 0x01ffffffffffffffffffffffffffff
|
||||||
|
// /* "":346:388 */
|
||||||
|
// dup2
|
||||||
|
// eq
|
||||||
|
// tag_10
|
||||||
|
// jumpi
|
||||||
|
// /* "":415:420 */
|
||||||
|
// pop
|
||||||
|
// jump(tag_8)
|
||||||
|
// /* "":292:422 */
|
||||||
|
// jump(tag_9)
|
||||||
|
// /* "":346:388 */
|
||||||
|
// tag_10:
|
||||||
|
// /* "":292:422 */
|
||||||
|
// tag_9:
|
||||||
|
// pop
|
||||||
|
// /* "":439:447 */
|
||||||
|
// jump(tag_7)
|
||||||
|
// /* "":464:476 */
|
||||||
|
// 0x00
|
||||||
|
// 0x00
|
||||||
|
// /* "":274:490 */
|
||||||
|
// pop
|
||||||
|
// pop
|
||||||
|
// /* "":201:490 */
|
||||||
|
// tag_7:
|
||||||
|
// /* "":254:258 */
|
||||||
|
// 0x20
|
||||||
|
// /* "":249:252 */
|
||||||
|
// dup2
|
||||||
|
// /* "":245:259 */
|
||||||
|
// add
|
||||||
|
// /* "":238:259 */
|
||||||
|
// swap1
|
||||||
|
// pop
|
||||||
|
// /* "":201:490 */
|
||||||
|
// jump(tag_6)
|
||||||
|
// tag_8:
|
||||||
|
// /* "":205:221 */
|
||||||
|
// pop
|
||||||
|
// /* "":81:500 */
|
||||||
|
// jump(tag_4)
|
||||||
|
// /* "":132:170 */
|
||||||
|
// tag_5:
|
||||||
|
// /* "":81:500 */
|
||||||
|
// tag_4:
|
||||||
|
// pop
|
||||||
|
// /* "":509:521 */
|
||||||
|
// 0x00
|
||||||
|
// 0x00
|
||||||
|
// /* "":71:527 */
|
||||||
|
// pop
|
||||||
|
// pop
|
||||||
|
// /* "":6:527 */
|
||||||
|
// tag_2:
|
||||||
|
// /* "":59:63 */
|
||||||
|
// 0x20
|
||||||
|
// /* "":54:57 */
|
||||||
|
// dup2
|
||||||
|
// /* "":50:64 */
|
||||||
|
// add
|
||||||
|
// /* "":43:64 */
|
||||||
|
// swap1
|
||||||
|
// pop
|
||||||
|
// /* "":6:527 */
|
||||||
|
// jump(tag_1)
|
||||||
|
// tag_3:
|
||||||
|
// /* "":10:26 */
|
||||||
|
// pop
|
105
test/libyul/evmCodeTransform/test4.yul
Normal file
105
test/libyul/evmCodeTransform/test4.yul
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
{
|
||||||
|
{
|
||||||
|
let i := 0
|
||||||
|
let i_1 := i
|
||||||
|
let _1 := mload(0x1fffffffffffffffffffffffff)
|
||||||
|
for { } true { i_1 := add(i_1, 0x20) }
|
||||||
|
{
|
||||||
|
let _2 := 0x60
|
||||||
|
if iszero(lt(i_1, _2)) { break }
|
||||||
|
switch _1
|
||||||
|
case 0x1ffffffffffffffffffffffffff { }
|
||||||
|
default {
|
||||||
|
let i_2 := i
|
||||||
|
if lt(i, _2) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// /* "":25:26 */
|
||||||
|
// 0x00
|
||||||
|
// /* "":46:47 */
|
||||||
|
// dup1
|
||||||
|
// /* "":72:100 */
|
||||||
|
// 0x1fffffffffffffffffffffffff
|
||||||
|
// /* "":66:101 */
|
||||||
|
// mload
|
||||||
|
// /* "":110:411 */
|
||||||
|
// tag_1:
|
||||||
|
// /* "":118:122 */
|
||||||
|
// 0x01
|
||||||
|
// /* "":110:411 */
|
||||||
|
// iszero
|
||||||
|
// tag_3
|
||||||
|
// jumpi
|
||||||
|
// /* "":181:185 */
|
||||||
|
// 0x60
|
||||||
|
// /* "":216:218 */
|
||||||
|
// dup1
|
||||||
|
// /* "":211:214 */
|
||||||
|
// dup4
|
||||||
|
// /* "":208:219 */
|
||||||
|
// lt
|
||||||
|
// /* "":201:220 */
|
||||||
|
// iszero
|
||||||
|
// /* "":198:230 */
|
||||||
|
// iszero
|
||||||
|
// tag_4
|
||||||
|
// jumpi
|
||||||
|
// /* "":223:228 */
|
||||||
|
// pop
|
||||||
|
// jump(tag_3)
|
||||||
|
// /* "":198:230 */
|
||||||
|
// tag_4:
|
||||||
|
// /* "":250:252 */
|
||||||
|
// dup2
|
||||||
|
// /* "":270:299 */
|
||||||
|
// 0x01ffffffffffffffffffffffffff
|
||||||
|
// /* "":265:303 */
|
||||||
|
// dup2
|
||||||
|
// eq
|
||||||
|
// tag_6
|
||||||
|
// jumpi
|
||||||
|
// /* "":353:354 */
|
||||||
|
// dup5
|
||||||
|
// /* "":380:382 */
|
||||||
|
// dup3
|
||||||
|
// /* "":377:378 */
|
||||||
|
// dup7
|
||||||
|
// /* "":374:383 */
|
||||||
|
// lt
|
||||||
|
// /* "":371:387 */
|
||||||
|
// iszero
|
||||||
|
// tag_7
|
||||||
|
// jumpi
|
||||||
|
// tag_7:
|
||||||
|
// /* "":324:401 */
|
||||||
|
// pop
|
||||||
|
// /* "":243:401 */
|
||||||
|
// jump(tag_5)
|
||||||
|
// /* "":265:303 */
|
||||||
|
// tag_6:
|
||||||
|
// /* "":243:401 */
|
||||||
|
// tag_5:
|
||||||
|
// pop
|
||||||
|
// /* "":157:411 */
|
||||||
|
// pop
|
||||||
|
// /* "":110:411 */
|
||||||
|
// tag_2:
|
||||||
|
// /* "":141:145 */
|
||||||
|
// 0x20
|
||||||
|
// /* "":136:139 */
|
||||||
|
// dup3
|
||||||
|
// /* "":132:146 */
|
||||||
|
// add
|
||||||
|
// /* "":125:146 */
|
||||||
|
// swap2
|
||||||
|
// pop
|
||||||
|
// /* "":110:411 */
|
||||||
|
// jump(tag_1)
|
||||||
|
// tag_3:
|
||||||
|
// /* "":6:417 */
|
||||||
|
// pop
|
||||||
|
// pop
|
||||||
|
// pop
|
89
test/libyul/evmCodeTransform/test5.yul
Normal file
89
test/libyul/evmCodeTransform/test5.yul
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
{
|
||||||
|
let i := 0
|
||||||
|
let i_1 := i
|
||||||
|
let _1 := mload(0x1fffffffffffffffffffffffff)
|
||||||
|
for { } true { i_1 := add(i_1, 0x20) }
|
||||||
|
{
|
||||||
|
let _2 := 0x60
|
||||||
|
if iszero(lt(i_1, _2)) { break }
|
||||||
|
switch _1
|
||||||
|
case 0x1ffffffffffffffffffffffffff { }
|
||||||
|
default {
|
||||||
|
let i_2 := i
|
||||||
|
if lt(i, _2) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// stackOptimization: true
|
||||||
|
// ----
|
||||||
|
// /* "":25:26 */
|
||||||
|
// 0x00
|
||||||
|
// /* "":35:47 */
|
||||||
|
// dup1
|
||||||
|
// /* "":66:101 */
|
||||||
|
// swap1
|
||||||
|
// /* "":72:100 */
|
||||||
|
// 0x1fffffffffffffffffffffffff
|
||||||
|
// /* "":66:101 */
|
||||||
|
// mload
|
||||||
|
// /* "":114:117 */
|
||||||
|
// swap2
|
||||||
|
// /* "":157:411 */
|
||||||
|
// tag_1:
|
||||||
|
// /* "":181:185 */
|
||||||
|
// 0x60
|
||||||
|
// /* "":208:219 */
|
||||||
|
// swap1
|
||||||
|
// dup2
|
||||||
|
// dup2
|
||||||
|
// lt
|
||||||
|
// /* "":201:220 */
|
||||||
|
// iszero
|
||||||
|
// /* "":198:230 */
|
||||||
|
// tag_2
|
||||||
|
// jumpi
|
||||||
|
// /* "":157:411 */
|
||||||
|
// tag_3:
|
||||||
|
// /* "":141:145 */
|
||||||
|
// 0x20
|
||||||
|
// /* "":243:401 */
|
||||||
|
// swap2
|
||||||
|
// dup5
|
||||||
|
// /* "":270:299 */
|
||||||
|
// 0x01ffffffffffffffffffffffffff
|
||||||
|
// /* "":265:303 */
|
||||||
|
// eq
|
||||||
|
// tag_4
|
||||||
|
// jumpi
|
||||||
|
// /* "":243:401 */
|
||||||
|
// tag_5:
|
||||||
|
// /* "":342:354 */
|
||||||
|
// dup4
|
||||||
|
// /* "":374:383 */
|
||||||
|
// pop
|
||||||
|
// dup4
|
||||||
|
// lt
|
||||||
|
// /* "":371:387 */
|
||||||
|
// tag_6
|
||||||
|
// jumpi
|
||||||
|
// /* "":243:401 */
|
||||||
|
// tag_7:
|
||||||
|
// tag_8:
|
||||||
|
// /* "":132:146 */
|
||||||
|
// add
|
||||||
|
// /* "":123:148 */
|
||||||
|
// jump(tag_1)
|
||||||
|
// /* "":384:387 */
|
||||||
|
// tag_6:
|
||||||
|
// jump(tag_7)
|
||||||
|
// /* "":300:303 */
|
||||||
|
// tag_4:
|
||||||
|
// pop
|
||||||
|
// jump(tag_8)
|
||||||
|
// /* "":221:230 */
|
||||||
|
// tag_2:
|
||||||
|
// /* "":110:411 */
|
||||||
|
// stop
|
Loading…
Reference in New Issue
Block a user