mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #10015 from ethereum/moveFunctionArgumentsToMemoryNew
Stack Limit Evader: Move function arguments and return values to memory (v2)
This commit is contained in:
commit
d91dc9953f
@ -4,6 +4,9 @@ Language Features:
|
||||
|
||||
|
||||
Compiler Features:
|
||||
* Yul EVM Code Transform: Do not reuse stack slots that immediately become unreachable.
|
||||
* Yul EVM Code Transform: Also pop unused argument slots for functions without return variables (under the same restrictions as for functions with return variables).
|
||||
* Yul Optimizer: Move function arguments and return variables to memory with the experimental Stack Limit Evader (which is not enabled by default).
|
||||
|
||||
|
||||
Bugfixes:
|
||||
|
@ -129,6 +129,8 @@ add_library(yul
|
||||
optimiser/FullInliner.h
|
||||
optimiser/FunctionCallFinder.cpp
|
||||
optimiser/FunctionCallFinder.h
|
||||
optimiser/FunctionDefinitionCollector.cpp
|
||||
optimiser/FunctionDefinitionCollector.h
|
||||
optimiser/FunctionGrouper.cpp
|
||||
optimiser/FunctionGrouper.h
|
||||
optimiser/FunctionHoister.cpp
|
||||
|
@ -182,16 +182,24 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
|
||||
else
|
||||
m_variablesScheduledForDeletion.insert(&var);
|
||||
}
|
||||
else if (m_unusedStackSlots.empty())
|
||||
atTopOfStack = false;
|
||||
else
|
||||
{
|
||||
auto slot = static_cast<size_t>(*m_unusedStackSlots.begin());
|
||||
m_unusedStackSlots.erase(m_unusedStackSlots.begin());
|
||||
bool foundUnusedSlot = false;
|
||||
for (auto it = m_unusedStackSlots.begin(); it != m_unusedStackSlots.end(); ++it)
|
||||
{
|
||||
if (m_assembly.stackHeight() - *it > 17)
|
||||
continue;
|
||||
foundUnusedSlot = true;
|
||||
auto slot = static_cast<size_t>(*it);
|
||||
m_unusedStackSlots.erase(it);
|
||||
m_context->variableStackHeights[&var] = slot;
|
||||
if (size_t heightDiff = variableHeightDiff(var, varName, true))
|
||||
m_assembly.appendInstruction(evmasm::swapInstruction(static_cast<unsigned>(heightDiff - 1)));
|
||||
m_assembly.appendInstruction(evmasm::Instruction::POP);
|
||||
break;
|
||||
}
|
||||
if (!foundUnusedSlot)
|
||||
atTopOfStack = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -404,7 +412,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
||||
subTransform.deleteVariable(var);
|
||||
}
|
||||
|
||||
if (!m_allowStackOpt || _function.returnVariables.empty())
|
||||
if (!m_allowStackOpt)
|
||||
subTransform.setupReturnVariablesAndFunctionExit();
|
||||
|
||||
subTransform(_function.body);
|
||||
@ -594,6 +602,7 @@ void CodeTransform::visitExpression(Expression const& _expression)
|
||||
|
||||
void CodeTransform::setupReturnVariablesAndFunctionExit()
|
||||
{
|
||||
yulAssert(isInsideFunction(), "");
|
||||
yulAssert(!returnVariablesAndFunctionExitAreSetup(), "");
|
||||
yulAssert(m_scope, "");
|
||||
|
||||
@ -656,7 +665,8 @@ void CodeTransform::visitStatements(vector<Statement> const& _statements)
|
||||
{
|
||||
freeUnusedVariables();
|
||||
if (
|
||||
!m_delayedReturnVariables.empty() &&
|
||||
isInsideFunction() &&
|
||||
!returnVariablesAndFunctionExitAreSetup() &&
|
||||
statementNeedsReturnVariableSetup(statement, m_delayedReturnVariables)
|
||||
)
|
||||
setupReturnVariablesAndFunctionExit();
|
||||
|
@ -181,6 +181,10 @@ private:
|
||||
{
|
||||
return m_functionExitStackHeight.has_value();
|
||||
}
|
||||
bool isInsideFunction() const
|
||||
{
|
||||
return m_functionExitLabel.has_value();
|
||||
}
|
||||
|
||||
AbstractAssembly& m_assembly;
|
||||
AsmAnalysisInfo& m_info;
|
||||
|
36
libyul/optimiser/FunctionDefinitionCollector.cpp
Normal file
36
libyul/optimiser/FunctionDefinitionCollector.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libyul/optimiser/FunctionDefinitionCollector.h>
|
||||
#include <libyul/AST.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::yul;
|
||||
|
||||
map<YulString, FunctionDefinition const*> FunctionDefinitionCollector::run(Block& _block)
|
||||
{
|
||||
FunctionDefinitionCollector functionDefinitionCollector;
|
||||
functionDefinitionCollector(_block);
|
||||
return functionDefinitionCollector.m_functionDefinitions;
|
||||
}
|
||||
|
||||
void FunctionDefinitionCollector::operator()(FunctionDefinition const& _functionDefinition)
|
||||
{
|
||||
m_functionDefinitions[_functionDefinition.name] = &_functionDefinition;
|
||||
ASTWalker::operator()(_functionDefinition);
|
||||
}
|
44
libyul/optimiser/FunctionDefinitionCollector.h
Normal file
44
libyul/optimiser/FunctionDefinitionCollector.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* AST walker that finds all function definitions and stores them into a map indexed by the function names.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <libyul/optimiser/ASTWalker.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace solidity::yul
|
||||
{
|
||||
|
||||
/**
|
||||
* AST walker that finds all function definitions and stores them into a map indexed by the function names.
|
||||
*
|
||||
* Prerequisite: Disambiguator
|
||||
*/
|
||||
class FunctionDefinitionCollector: ASTWalker
|
||||
{
|
||||
public:
|
||||
static std::map<YulString, FunctionDefinition const*> run(Block& _block);
|
||||
private:
|
||||
using ASTWalker::operator();
|
||||
void operator()(FunctionDefinition const& _functionDefinition) override;
|
||||
std::map<YulString, FunctionDefinition const*> m_functionDefinitions;
|
||||
};
|
||||
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
#include <libyul/optimiser/StackLimitEvader.h>
|
||||
#include <libyul/optimiser/CallGraphGenerator.h>
|
||||
#include <libyul/optimiser/FunctionCallFinder.h>
|
||||
#include <libyul/optimiser/FunctionDefinitionCollector.h>
|
||||
#include <libyul/optimiser/NameDispenser.h>
|
||||
#include <libyul/optimiser/StackToMemoryMover.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
@ -28,6 +29,9 @@
|
||||
#include <libsolutil/Algorithms.h>
|
||||
#include <libsolutil/CommonData.h>
|
||||
|
||||
#include <range/v3/view/concat.hpp>
|
||||
#include <range/v3/view/take.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::yul;
|
||||
@ -62,25 +66,42 @@ struct MemoryOffsetAllocator
|
||||
for (YulString child: callGraph.at(_function))
|
||||
requiredSlots = std::max(run(child), requiredSlots);
|
||||
|
||||
if (unreachableVariables.count(_function))
|
||||
if (auto const* unreachables = util::valueOrNullptr(unreachableVariables, _function))
|
||||
{
|
||||
yulAssert(!slotAllocations.count(_function), "");
|
||||
for (YulString variable: unreachableVariables.at(_function))
|
||||
if (variable.empty())
|
||||
{
|
||||
// TODO: Too many function arguments or return parameters.
|
||||
}
|
||||
else
|
||||
if (FunctionDefinition const* functionDefinition = util::valueOrDefault(functionDefinitions, _function, nullptr, util::allow_copy))
|
||||
if (
|
||||
size_t totalArgCount = functionDefinition->returnVariables.size() + functionDefinition->parameters.size();
|
||||
totalArgCount > 16
|
||||
)
|
||||
for (TypedName const& var: ranges::concat_view(
|
||||
functionDefinition->parameters,
|
||||
functionDefinition->returnVariables
|
||||
) | ranges::views::take(totalArgCount - 16))
|
||||
slotAllocations[var.name] = requiredSlots++;
|
||||
|
||||
// Assign slots for all variables that become unreachable in the function body, if the above did not
|
||||
// assign a slot for them already.
|
||||
for (YulString variable: *unreachables)
|
||||
// The empty case is a function with too many arguments or return values,
|
||||
// which was already handled above.
|
||||
if (!variable.empty() && !slotAllocations.count(variable))
|
||||
slotAllocations[variable] = requiredSlots++;
|
||||
}
|
||||
|
||||
return slotsRequiredForFunction[_function] = requiredSlots;
|
||||
}
|
||||
|
||||
/// Maps function names to the set of unreachable variables in that function.
|
||||
/// An empty variable name means that the function has too many arguments or return variables.
|
||||
map<YulString, set<YulString>> const& unreachableVariables;
|
||||
/// The graph of immediate function calls of all functions.
|
||||
map<YulString, set<YulString>> const& callGraph;
|
||||
/// Maps the name of each user-defined function to its definition.
|
||||
map<YulString, FunctionDefinition const*> const& functionDefinitions;
|
||||
|
||||
/// Maps variable names to the memory slot the respective variable is assigned.
|
||||
map<YulString, uint64_t> slotAllocations{};
|
||||
/// Maps function names to the number of memory slots the respective function requires.
|
||||
map<YulString, uint64_t> slotsRequiredForFunction{};
|
||||
};
|
||||
|
||||
@ -116,6 +137,8 @@ void StackLimitEvader::run(
|
||||
|
||||
// Make sure all calls to ``memoryguard`` we found have the same value as argument (otherwise, abort).
|
||||
u256 reservedMemory = literalArgumentValue(*memoryGuardCalls.front());
|
||||
yulAssert(reservedMemory < u256(1) << 32 - 1, "");
|
||||
|
||||
for (FunctionCall const* memoryGuardCall: memoryGuardCalls)
|
||||
if (reservedMemory != literalArgumentValue(*memoryGuardCall))
|
||||
return;
|
||||
@ -127,12 +150,14 @@ void StackLimitEvader::run(
|
||||
if (_unreachableVariables.count(function))
|
||||
return;
|
||||
|
||||
MemoryOffsetAllocator memoryOffsetAllocator{_unreachableVariables, callGraph.functionCalls};
|
||||
map<YulString, FunctionDefinition const*> functionDefinitions = FunctionDefinitionCollector::run(*_object.code);
|
||||
|
||||
MemoryOffsetAllocator memoryOffsetAllocator{_unreachableVariables, callGraph.functionCalls, functionDefinitions};
|
||||
uint64_t requiredSlots = memoryOffsetAllocator.run();
|
||||
yulAssert(requiredSlots < (uint64_t(1) << 32) - 1, "");
|
||||
|
||||
StackToMemoryMover::run(_context, reservedMemory, memoryOffsetAllocator.slotAllocations, requiredSlots, *_object.code);
|
||||
|
||||
yulAssert(requiredSlots < std::numeric_limits<uint64_t>::max() / 32, "");
|
||||
reservedMemory += 32 * requiredSlots;
|
||||
for (FunctionCall* memoryGuardCall: FunctionCallFinder::run(*_object.code, "memoryguard"_yulstring))
|
||||
{
|
||||
|
@ -15,13 +15,19 @@
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <libyul/optimiser/StackToMemoryMover.h>
|
||||
#include <libyul/optimiser/FunctionDefinitionCollector.h>
|
||||
#include <libyul/optimiser/NameDispenser.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
|
||||
#include <libyul/AST.h>
|
||||
|
||||
#include <libsolutil/CommonData.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <range/v3/algorithm/none_of.hpp>
|
||||
#include <range/v3/view/filter.hpp>
|
||||
#include <range/v3/view/transform.hpp>
|
||||
#include <range/v3/view/zip.hpp>
|
||||
#include <range/v3/range/conversion.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
@ -44,7 +50,7 @@ vector<Statement> generateMemoryStore(
|
||||
Identifier{_debugData, memoryStoreFunction->name},
|
||||
{
|
||||
Literal{_debugData, LiteralKind::Number, _mpos, {}},
|
||||
std::move(_value)
|
||||
move(_value)
|
||||
}
|
||||
}});
|
||||
return result;
|
||||
@ -77,17 +83,30 @@ void StackToMemoryMover::run(
|
||||
)
|
||||
{
|
||||
VariableMemoryOffsetTracker memoryOffsetTracker(_reservedMemory, _memorySlots, _numRequiredSlots);
|
||||
StackToMemoryMover stackToMemoryMover(_context, memoryOffsetTracker);
|
||||
StackToMemoryMover stackToMemoryMover(
|
||||
_context,
|
||||
memoryOffsetTracker,
|
||||
util::applyMap(
|
||||
FunctionDefinitionCollector::run(_block),
|
||||
util::mapTuple([](YulString _name, FunctionDefinition const* _funDef) {
|
||||
return make_pair(_name, _funDef->returnVariables);
|
||||
}),
|
||||
map<YulString, TypedNameList>{}
|
||||
)
|
||||
);
|
||||
stackToMemoryMover(_block);
|
||||
_block.statements += move(stackToMemoryMover.m_newFunctionDefinitions);
|
||||
}
|
||||
|
||||
StackToMemoryMover::StackToMemoryMover(
|
||||
OptimiserStepContext& _context,
|
||||
VariableMemoryOffsetTracker const& _memoryOffsetTracker
|
||||
VariableMemoryOffsetTracker const& _memoryOffsetTracker,
|
||||
map<YulString, TypedNameList> _functionReturnVariables
|
||||
):
|
||||
m_context(_context),
|
||||
m_memoryOffsetTracker(_memoryOffsetTracker),
|
||||
m_nameDispenser(_context.dispenser)
|
||||
m_nameDispenser(_context.dispenser),
|
||||
m_functionReturnVariables(move(_functionReturnVariables))
|
||||
{
|
||||
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_context.dialect);
|
||||
yulAssert(
|
||||
@ -98,89 +117,186 @@ m_nameDispenser(_context.dispenser)
|
||||
|
||||
void StackToMemoryMover::operator()(FunctionDefinition& _functionDefinition)
|
||||
{
|
||||
for (TypedName const& param: _functionDefinition.parameters + _functionDefinition.returnVariables)
|
||||
if (m_memoryOffsetTracker(param.name))
|
||||
// It is important to first visit the function body, so that it doesn't replace the memory inits for
|
||||
// variable arguments we might generate below.
|
||||
ASTModifier::operator()(_functionDefinition);
|
||||
|
||||
vector<Statement> memoryVariableInits;
|
||||
|
||||
// All function parameters with a memory slot are moved at the beginning of the function body.
|
||||
for (TypedName const& param: _functionDefinition.parameters)
|
||||
if (auto slot = m_memoryOffsetTracker(param.name))
|
||||
memoryVariableInits += generateMemoryStore(
|
||||
m_context.dialect,
|
||||
param.debugData,
|
||||
*slot,
|
||||
Identifier{param.debugData, param.name}
|
||||
);
|
||||
|
||||
// All memory return variables have to be initialized to zero in memory.
|
||||
for (TypedName const& returnVariable: _functionDefinition.returnVariables)
|
||||
if (auto slot = m_memoryOffsetTracker(returnVariable.name))
|
||||
memoryVariableInits += generateMemoryStore(
|
||||
m_context.dialect,
|
||||
returnVariable.debugData,
|
||||
*slot,
|
||||
Literal{returnVariable.debugData, LiteralKind::Number, "0"_yulstring, {}}
|
||||
);
|
||||
|
||||
// Special case of a function with a single return argument that needs to move to memory.
|
||||
if (_functionDefinition.returnVariables.size() == 1 && m_memoryOffsetTracker(_functionDefinition.returnVariables.front().name))
|
||||
{
|
||||
// TODO: we cannot handle function parameters yet.
|
||||
TypedNameList stackParameters = _functionDefinition.parameters | ranges::views::filter(
|
||||
not_fn(m_memoryOffsetTracker)
|
||||
) | ranges::to<TypedNameList>;
|
||||
// Generate new function without return variable and with only the non-moved parameters.
|
||||
YulString newFunctionName = m_context.dispenser.newName(_functionDefinition.name);
|
||||
m_newFunctionDefinitions.emplace_back(FunctionDefinition{
|
||||
_functionDefinition.debugData,
|
||||
newFunctionName,
|
||||
stackParameters,
|
||||
{},
|
||||
move(_functionDefinition.body)
|
||||
});
|
||||
// Generate new names for the arguments to maintain disambiguation.
|
||||
std::map<YulString, YulString> newArgumentNames;
|
||||
for (TypedName const& _var: stackParameters)
|
||||
newArgumentNames[_var.name] = m_context.dispenser.newName(_var.name);
|
||||
for (auto& parameter: _functionDefinition.parameters)
|
||||
parameter.name = util::valueOrDefault(newArgumentNames, parameter.name, parameter.name);
|
||||
// Replace original function by a call to the new function and an assignment to the return variable from memory.
|
||||
_functionDefinition.body = Block{_functionDefinition.debugData, move(memoryVariableInits)};
|
||||
_functionDefinition.body.statements.emplace_back(ExpressionStatement{
|
||||
_functionDefinition.debugData,
|
||||
FunctionCall{
|
||||
_functionDefinition.debugData,
|
||||
Identifier{_functionDefinition.debugData, newFunctionName},
|
||||
stackParameters | ranges::views::transform([&](TypedName const& _arg) {
|
||||
return Expression{Identifier{_arg.debugData, newArgumentNames.at(_arg.name)}};
|
||||
}) | ranges::to<vector<Expression>>
|
||||
}
|
||||
});
|
||||
_functionDefinition.body.statements.emplace_back(Assignment{
|
||||
_functionDefinition.debugData,
|
||||
{Identifier{_functionDefinition.debugData, _functionDefinition.returnVariables.front().name}},
|
||||
make_unique<Expression>(generateMemoryLoad(
|
||||
m_context.dialect,
|
||||
_functionDefinition.debugData,
|
||||
*m_memoryOffsetTracker(_functionDefinition.returnVariables.front().name)
|
||||
))
|
||||
});
|
||||
return;
|
||||
}
|
||||
ASTModifier::operator()(_functionDefinition);
|
||||
|
||||
if (!memoryVariableInits.empty())
|
||||
_functionDefinition.body.statements = move(memoryVariableInits) + move(_functionDefinition.body.statements);
|
||||
|
||||
_functionDefinition.returnVariables = _functionDefinition.returnVariables | ranges::views::filter(
|
||||
not_fn(m_memoryOffsetTracker)
|
||||
) | ranges::to<TypedNameList>;
|
||||
}
|
||||
|
||||
void StackToMemoryMover::operator()(Block& _block)
|
||||
{
|
||||
using OptionalStatements = std::optional<vector<Statement>>;
|
||||
auto rewriteAssignmentOrVariableDeclaration = [&](
|
||||
using OptionalStatements = optional<vector<Statement>>;
|
||||
|
||||
auto rewriteAssignmentOrVariableDeclarationLeftHandSide = [this](
|
||||
auto& _stmt,
|
||||
auto const& _variables
|
||||
auto& _lhsVars
|
||||
) -> OptionalStatements {
|
||||
using StatementType = decay_t<decltype(_stmt)>;
|
||||
if (_stmt.value)
|
||||
visit(*_stmt.value);
|
||||
bool leftHandSideNeedsMoving = util::contains_if(_variables, [&](auto const& var) {
|
||||
return m_memoryOffsetTracker(var.name);
|
||||
});
|
||||
if (!leftHandSideNeedsMoving)
|
||||
return {};
|
||||
|
||||
if (_variables.size() == 1)
|
||||
auto debugData = _stmt.debugData;
|
||||
if (_lhsVars.size() == 1)
|
||||
{
|
||||
optional<YulString> offset = m_memoryOffsetTracker(_variables.front().name);
|
||||
yulAssert(offset, "");
|
||||
if (optional<YulString> offset = m_memoryOffsetTracker(_lhsVars.front().name))
|
||||
return generateMemoryStore(
|
||||
m_context.dialect,
|
||||
_stmt.debugData,
|
||||
debugData,
|
||||
*offset,
|
||||
_stmt.value ? *std::move(_stmt.value) : Literal{_stmt.debugData, LiteralKind::Number, "0"_yulstring, {}}
|
||||
_stmt.value ? *move(_stmt.value) : Literal{debugData, LiteralKind::Number, "0"_yulstring, {}}
|
||||
);
|
||||
else
|
||||
return {};
|
||||
}
|
||||
vector<optional<YulString>> rhsMemorySlots;
|
||||
if (_stmt.value)
|
||||
{
|
||||
FunctionCall const* functionCall = get_if<FunctionCall>(_stmt.value.get());
|
||||
yulAssert(functionCall, "");
|
||||
if (m_context.dialect.builtin(functionCall->functionName.name))
|
||||
rhsMemorySlots = vector<optional<YulString>>(_lhsVars.size(), nullopt);
|
||||
else
|
||||
rhsMemorySlots =
|
||||
m_functionReturnVariables.at(functionCall->functionName.name) |
|
||||
ranges::views::transform(m_memoryOffsetTracker) |
|
||||
ranges::to<vector<optional<YulString>>>;
|
||||
}
|
||||
else
|
||||
rhsMemorySlots = vector<optional<YulString>>(_lhsVars.size(), nullopt);
|
||||
|
||||
// Nothing to do, if the right-hand-side remains entirely on the stack and
|
||||
// none of the variables in the left-hand-side are moved.
|
||||
if (
|
||||
ranges::none_of(rhsMemorySlots, [](optional<YulString> const& _slot) { return _slot.has_value(); }) &&
|
||||
!util::contains_if(_lhsVars, m_memoryOffsetTracker)
|
||||
)
|
||||
return {};
|
||||
|
||||
VariableDeclaration tempDecl{_stmt.debugData, {}, std::move(_stmt.value)};
|
||||
vector<Statement> memoryAssignments;
|
||||
vector<Statement> variableAssignments;
|
||||
for (auto& var: _variables)
|
||||
{
|
||||
YulString tempVarName = m_nameDispenser.newName(var.name);
|
||||
tempDecl.variables.emplace_back(TypedName{var.debugData, tempVarName, {}});
|
||||
VariableDeclaration tempDecl{debugData, {}, move(_stmt.value)};
|
||||
|
||||
if (optional<YulString> offset = m_memoryOffsetTracker(var.name))
|
||||
yulAssert(rhsMemorySlots.size() == _lhsVars.size(), "");
|
||||
for (auto&& [lhsVar, rhsSlot]: ranges::views::zip(_lhsVars, rhsMemorySlots))
|
||||
{
|
||||
unique_ptr<Expression> rhs;
|
||||
if (rhsSlot)
|
||||
rhs = make_unique<Expression>(generateMemoryLoad(m_context.dialect, debugData, *rhsSlot));
|
||||
else
|
||||
{
|
||||
YulString tempVarName = m_nameDispenser.newName(lhsVar.name);
|
||||
tempDecl.variables.emplace_back(TypedName{lhsVar.debugData, tempVarName, {}});
|
||||
rhs = make_unique<Expression>(Identifier{debugData, tempVarName});
|
||||
}
|
||||
|
||||
if (optional<YulString> offset = m_memoryOffsetTracker(lhsVar.name))
|
||||
memoryAssignments += generateMemoryStore(
|
||||
m_context.dialect,
|
||||
_stmt.debugData,
|
||||
*offset,
|
||||
Identifier{_stmt.debugData, tempVarName}
|
||||
move(*rhs)
|
||||
);
|
||||
else
|
||||
variableAssignments.emplace_back(StatementType{
|
||||
_stmt.debugData,
|
||||
{move(var)},
|
||||
make_unique<Expression>(Identifier{_stmt.debugData, tempVarName})
|
||||
debugData,
|
||||
{ move(lhsVar) },
|
||||
move(rhs)
|
||||
});
|
||||
}
|
||||
std::vector<Statement> result;
|
||||
result.emplace_back(std::move(tempDecl));
|
||||
std::reverse(memoryAssignments.begin(), memoryAssignments.end());
|
||||
result += std::move(memoryAssignments);
|
||||
std::reverse(variableAssignments.begin(), variableAssignments.end());
|
||||
result += std::move(variableAssignments);
|
||||
|
||||
vector<Statement> result;
|
||||
if (tempDecl.variables.empty())
|
||||
result.emplace_back(ExpressionStatement{debugData, *move(tempDecl.value)});
|
||||
else
|
||||
result.emplace_back(move(tempDecl));
|
||||
reverse(memoryAssignments.begin(), memoryAssignments.end());
|
||||
result += move(memoryAssignments);
|
||||
reverse(variableAssignments.begin(), variableAssignments.end());
|
||||
result += move(variableAssignments);
|
||||
return OptionalStatements{move(result)};
|
||||
};
|
||||
|
||||
util::iterateReplacing(
|
||||
_block.statements,
|
||||
[&](Statement& _statement)
|
||||
[&](Statement& _statement) -> OptionalStatements
|
||||
{
|
||||
return std::visit(util::GenericVisitor{
|
||||
[&](Assignment& _assignment) -> OptionalStatements
|
||||
{
|
||||
return rewriteAssignmentOrVariableDeclaration(_assignment, _assignment.variableNames);
|
||||
},
|
||||
[&](VariableDeclaration& _varDecl) -> OptionalStatements
|
||||
{
|
||||
return rewriteAssignmentOrVariableDeclaration(_varDecl, _varDecl.variables);
|
||||
},
|
||||
[&](auto& _stmt) -> OptionalStatements { (*this)(_stmt); return {}; }
|
||||
}, _statement);
|
||||
visit(_statement);
|
||||
if (auto* assignment = get_if<Assignment>(&_statement))
|
||||
return rewriteAssignmentOrVariableDeclarationLeftHandSide(*assignment, assignment->variableNames);
|
||||
else if (auto* varDecl = get_if<VariableDeclaration>(&_statement))
|
||||
return rewriteAssignmentOrVariableDeclarationLeftHandSide(*varDecl, varDecl->variables);
|
||||
return {};
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -188,7 +304,7 @@ void StackToMemoryMover::operator()(Block& _block)
|
||||
void StackToMemoryMover::visit(Expression& _expression)
|
||||
{
|
||||
ASTModifier::visit(_expression);
|
||||
if (Identifier* identifier = std::get_if<Identifier>(&_expression))
|
||||
if (Identifier* identifier = get_if<Identifier>(&_expression))
|
||||
if (optional<YulString> offset = m_memoryOffsetTracker(identifier->name))
|
||||
_expression = generateMemoryLoad(m_context.dialect, identifier->debugData, *offset);
|
||||
}
|
||||
@ -202,5 +318,15 @@ optional<YulString> StackToMemoryMover::VariableMemoryOffsetTracker::operator()(
|
||||
return YulString{util::toCompactHexWithPrefix(m_reservedMemory + 32 * (m_numRequiredSlots - slot - 1))};
|
||||
}
|
||||
else
|
||||
return std::nullopt;
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
optional<YulString> StackToMemoryMover::VariableMemoryOffsetTracker::operator()(TypedName const& _variable) const
|
||||
{
|
||||
return (*this)(_variable.name);
|
||||
}
|
||||
|
||||
optional<YulString> StackToMemoryMover::VariableMemoryOffsetTracker::operator()(Identifier const& _variable) const
|
||||
{
|
||||
return (*this)(_variable.name);
|
||||
}
|
||||
|
@ -22,6 +22,9 @@
|
||||
|
||||
#include <libyul/optimiser/ASTWalker.h>
|
||||
#include <libyul/optimiser/OptimiserStep.h>
|
||||
#include <libyul/ASTForward.h>
|
||||
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
#include <libsolutil/Common.h>
|
||||
|
||||
namespace solidity::yul
|
||||
@ -52,6 +55,16 @@ namespace solidity::yul
|
||||
* let c := _3
|
||||
* let a := _1
|
||||
*
|
||||
* In case f has return parameters that are moved to memory, fewer variables are returned and the return values read
|
||||
* from memory instead. Assume the third return parameter of f (i.e. c) has to be moved to memory:
|
||||
* let a, b, c, d := f()
|
||||
* then it is replaced by
|
||||
* let _1, _2, _4 := f()
|
||||
* mstore(<memory offset for d>, _4)
|
||||
* mstore(<memory offset for b>, _2)
|
||||
* let c := mload(<memory offset of third return parameter of f>)
|
||||
* let a := _1
|
||||
*
|
||||
* Assignments to single variables are replaced by mstore's:
|
||||
* If a is in the map, replace
|
||||
* a := expr
|
||||
@ -70,8 +83,44 @@ namespace solidity::yul
|
||||
*
|
||||
* Replace all references to a variable ``a`` in the map by ``mload(<memory offset for a>)``.
|
||||
*
|
||||
* If a visited function has arguments or return parameters that are contained in the map,
|
||||
* the entire function is skipped (no local variables in the function will be moved at all).
|
||||
* Function arguments are moved at the beginning of a function body:
|
||||
* If a1 is in the map, replace
|
||||
* function f(a1, a2, ..., a17)
|
||||
* {
|
||||
* ...
|
||||
* sstore(a1, a17)
|
||||
* }
|
||||
* by
|
||||
* function f(a1, a2, ..., a17)
|
||||
* {
|
||||
* mstore(<memory offset for a1>, a1)
|
||||
* ...
|
||||
* sstore(mload(<memory offset for a1>, a17)
|
||||
* }
|
||||
* This relies on the code transform popping arguments that are no longer used, if they are on the stack top.
|
||||
*
|
||||
* Functions with only one return argument that has to be moved are encapsulated in a wrapper function as follows:
|
||||
* Suppose b and r need to be moved in:
|
||||
* function f(a, b) -> r
|
||||
* {
|
||||
* ...body of f...
|
||||
* r := b
|
||||
* ...body of f continued...
|
||||
* }
|
||||
* then replace by:
|
||||
* function f(a, b) -> r
|
||||
* {
|
||||
* mstore(<memory offset of b>, b)
|
||||
* mstore(<memory offset of r>, 0)
|
||||
* f_1(a)
|
||||
* r := mload(<memory offset of r>)
|
||||
* }
|
||||
* function f_1(a)
|
||||
* {
|
||||
* ...body of f...
|
||||
* mstore(<memory offset of r>, mload(<memory offset of b>))
|
||||
* ...body of f continued...
|
||||
* }
|
||||
*
|
||||
* Prerequisite: Disambiguator, ForLoopInitRewriter, FunctionHoister.
|
||||
*/
|
||||
@ -98,6 +147,7 @@ public:
|
||||
|
||||
void operator()(FunctionDefinition& _functionDefinition) override;
|
||||
void operator()(Block& _block) override;
|
||||
using ASTModifier::visit;
|
||||
void visit(Expression& _expression) override;
|
||||
private:
|
||||
class VariableMemoryOffsetTracker
|
||||
@ -113,20 +163,36 @@ private:
|
||||
/// @returns a YulString containing the memory offset to be assigned to @a _variable as number literal
|
||||
/// or std::nullopt if the variable should not be moved.
|
||||
std::optional<YulString> operator()(YulString _variable) const;
|
||||
/// @returns a YulString containing the memory offset to be assigned to @a _variable as number literal
|
||||
/// or std::nullopt if the variable should not be moved.
|
||||
std::optional<YulString> operator()(TypedName const& _variable) const;
|
||||
/// @returns a YulString containing the memory offset to be assigned to @a _variable as number literal
|
||||
/// or std::nullopt if the variable should not be moved.
|
||||
std::optional<YulString> operator()(Identifier const& _variable) const;
|
||||
|
||||
private:
|
||||
u256 m_reservedMemory;
|
||||
std::map<YulString, uint64_t> const& m_memorySlots;
|
||||
uint64_t m_numRequiredSlots = 0;
|
||||
};
|
||||
struct FunctionMoveInfo
|
||||
{
|
||||
std::vector<std::optional<YulString>> returnVariableSlots;
|
||||
};
|
||||
|
||||
StackToMemoryMover(
|
||||
OptimiserStepContext& _context,
|
||||
VariableMemoryOffsetTracker const& _memoryOffsetTracker
|
||||
VariableMemoryOffsetTracker const& _memoryOffsetTracker,
|
||||
std::map<YulString, std::vector<TypedName>> _functionReturnVariables
|
||||
);
|
||||
|
||||
OptimiserStepContext& m_context;
|
||||
VariableMemoryOffsetTracker const& m_memoryOffsetTracker;
|
||||
NameDispenser& m_nameDispenser;
|
||||
/// Map from function names to the return variables of the function with that name.
|
||||
std::map<YulString, std::vector<TypedName>> m_functionReturnVariables;
|
||||
/// List of functions generated while running this step that are to be appended to the code in the end.
|
||||
std::list<Statement> m_newFunctionDefinitions;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -345,6 +345,10 @@ YulOptimizerTestCommon::YulOptimizerTestCommon(
|
||||
{
|
||||
YulString originalFunctionName = m_currentFunction;
|
||||
m_currentFunction = _function.name;
|
||||
for (TypedName const& _argument: _function.parameters)
|
||||
visitVariableName(_argument.name);
|
||||
for (TypedName const& _argument: _function.returnVariables)
|
||||
visitVariableName(_argument.name);
|
||||
ASTWalker::operator()(_function);
|
||||
m_currentFunction = originalFunctionName;
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
{
|
||||
function f(x, y) {
|
||||
mstore(0x80, x)
|
||||
if calldataload(0) { sstore(y, y) }
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// stackOptimization: true
|
||||
// ----
|
||||
// PUSH1 0x17
|
||||
// JUMP
|
||||
// JUMPDEST
|
||||
// DUP1
|
||||
// PUSH1 0x80
|
||||
// MSTORE
|
||||
// POP
|
||||
// PUSH1 0x0
|
||||
// CALLDATALOAD
|
||||
// ISZERO
|
||||
// PUSH1 0x13
|
||||
// JUMPI
|
||||
// DUP1
|
||||
// DUP2
|
||||
// SSTORE
|
||||
// JUMPDEST
|
||||
// POP
|
||||
// JUMPDEST
|
||||
// JUMP
|
||||
// JUMPDEST
|
110
test/libyul/evmCodeTransform/stackReuse/reuse_too_deep_slot.yul
Normal file
110
test/libyul/evmCodeTransform/stackReuse/reuse_too_deep_slot.yul
Normal file
@ -0,0 +1,110 @@
|
||||
{
|
||||
let x := 7
|
||||
|
||||
let y1, y2, y3, y4, y5, y6, y7, y8, y9, y10, y11, y12, y13, y14, y15 := verbatim_0i_15o("\x60\x42") // the verbatim will show up as PUSH1 42
|
||||
|
||||
// last use of x - the slot of x will be marked as unused, but not popped, since it is not at the stack top
|
||||
sstore(0,x)
|
||||
|
||||
// If the slot of x is blindly reused, this will fail.
|
||||
let z1, z2 := verbatim_0i_2o("\x60\x43") // will show up as PUSH1 43
|
||||
|
||||
// prevent the z's from being popped immediately after their declaration above.
|
||||
mstore(1, z1)
|
||||
mstore(1, z2)
|
||||
|
||||
// use all y's to prevent them from being popped immediately after their declaration above
|
||||
sstore(1, y1)
|
||||
sstore(1, y2)
|
||||
sstore(1, y3)
|
||||
sstore(1, y4)
|
||||
sstore(1, y5)
|
||||
sstore(1, y6)
|
||||
sstore(1, y7)
|
||||
sstore(1, y8)
|
||||
sstore(1, y9)
|
||||
sstore(1, y10)
|
||||
sstore(1, y11)
|
||||
sstore(1, y12)
|
||||
sstore(1, y13)
|
||||
sstore(1, y14)
|
||||
sstore(1, y15)
|
||||
}
|
||||
// ====
|
||||
// stackOptimization: true
|
||||
// ----
|
||||
// PUSH1 0x7
|
||||
// PUSH1 0x42
|
||||
// DUP16
|
||||
// PUSH1 0x0
|
||||
// SSTORE
|
||||
// PUSH1 0x43
|
||||
// DUP2
|
||||
// PUSH1 0x1
|
||||
// MSTORE
|
||||
// DUP1
|
||||
// PUSH1 0x1
|
||||
// MSTORE
|
||||
// POP
|
||||
// POP
|
||||
// DUP15
|
||||
// PUSH1 0x1
|
||||
// SSTORE
|
||||
// DUP14
|
||||
// PUSH1 0x1
|
||||
// SSTORE
|
||||
// DUP13
|
||||
// PUSH1 0x1
|
||||
// SSTORE
|
||||
// DUP12
|
||||
// PUSH1 0x1
|
||||
// SSTORE
|
||||
// DUP11
|
||||
// PUSH1 0x1
|
||||
// SSTORE
|
||||
// DUP10
|
||||
// PUSH1 0x1
|
||||
// SSTORE
|
||||
// DUP9
|
||||
// PUSH1 0x1
|
||||
// SSTORE
|
||||
// DUP8
|
||||
// PUSH1 0x1
|
||||
// SSTORE
|
||||
// DUP7
|
||||
// PUSH1 0x1
|
||||
// SSTORE
|
||||
// DUP6
|
||||
// PUSH1 0x1
|
||||
// SSTORE
|
||||
// DUP5
|
||||
// PUSH1 0x1
|
||||
// SSTORE
|
||||
// DUP4
|
||||
// PUSH1 0x1
|
||||
// SSTORE
|
||||
// DUP3
|
||||
// PUSH1 0x1
|
||||
// SSTORE
|
||||
// DUP2
|
||||
// PUSH1 0x1
|
||||
// SSTORE
|
||||
// DUP1
|
||||
// PUSH1 0x1
|
||||
// SSTORE
|
||||
// POP
|
||||
// POP
|
||||
// POP
|
||||
// POP
|
||||
// POP
|
||||
// POP
|
||||
// POP
|
||||
// POP
|
||||
// POP
|
||||
// POP
|
||||
// POP
|
||||
// POP
|
||||
// POP
|
||||
// POP
|
||||
// POP
|
||||
// POP
|
@ -15,6 +15,9 @@
|
||||
// mstore(0x00, 0)
|
||||
// sstore(0, mload(0x00))
|
||||
// function h($hx) -> y
|
||||
// { y := $hx }
|
||||
// {
|
||||
// mstore(0x20, $hx)
|
||||
// y := mload(0x20)
|
||||
// }
|
||||
// sstore(1, h(32))
|
||||
// }
|
||||
|
@ -0,0 +1,21 @@
|
||||
{
|
||||
mstore(0x40, memoryguard(0x60))
|
||||
{
|
||||
let x, y
|
||||
}
|
||||
{
|
||||
let z, $w
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: fakeStackLimitEvader
|
||||
//
|
||||
// {
|
||||
// mstore(0x40, memoryguard(0x80))
|
||||
// { let x, y }
|
||||
// {
|
||||
// let z_1, $w_2
|
||||
// mstore(0x60, $w_2)
|
||||
// let z := z_1
|
||||
// }
|
||||
// }
|
@ -0,0 +1,48 @@
|
||||
{
|
||||
{
|
||||
mstore(0x40, memoryguard(0x80))
|
||||
let a1, a2 := f()
|
||||
sstore(a1, a2)
|
||||
}
|
||||
function g(x) -> a, b { a := x b := 2 }
|
||||
function f() -> $b1, $b2 {
|
||||
if calldataload(0) {
|
||||
$b1, $b2 := g(1)
|
||||
leave
|
||||
}
|
||||
$b1, $b2 := g(2)
|
||||
}
|
||||
|
||||
}
|
||||
// ----
|
||||
// step: fakeStackLimitEvader
|
||||
//
|
||||
// {
|
||||
// {
|
||||
// mstore(0x40, memoryguard(0xc0))
|
||||
// f()
|
||||
// let a2 := mload(0x80)
|
||||
// let a1 := mload(0xa0)
|
||||
// sstore(a1, a2)
|
||||
// }
|
||||
// function g(x) -> a, b
|
||||
// {
|
||||
// a := x
|
||||
// b := 2
|
||||
// }
|
||||
// function f()
|
||||
// {
|
||||
// mstore(0xa0, 0)
|
||||
// mstore(0x80, 0)
|
||||
// if calldataload(0)
|
||||
// {
|
||||
// let $b1_1, $b2_2 := g(1)
|
||||
// mstore(0x80, $b2_2)
|
||||
// mstore(0xa0, $b1_1)
|
||||
// leave
|
||||
// }
|
||||
// let $b1_3, $b2_4 := g(2)
|
||||
// mstore(0x80, $b2_4)
|
||||
// mstore(0xa0, $b1_3)
|
||||
// }
|
||||
// }
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
{
|
||||
mstore(0x40, memoryguard(0x80))
|
||||
sstore(0, f())
|
||||
}
|
||||
function f() -> $b1 {
|
||||
if calldataload(0) {
|
||||
$b1 := 0
|
||||
leave
|
||||
}
|
||||
$b1 := 1
|
||||
}
|
||||
|
||||
}
|
||||
// ----
|
||||
// step: fakeStackLimitEvader
|
||||
//
|
||||
// {
|
||||
// {
|
||||
// mstore(0x40, memoryguard(0xa0))
|
||||
// sstore(0, f())
|
||||
// }
|
||||
// function f() -> $b1
|
||||
// {
|
||||
// mstore(0x80, 0)
|
||||
// f_1()
|
||||
// $b1 := mload(0x80)
|
||||
// }
|
||||
// function f_1()
|
||||
// {
|
||||
// if calldataload(0)
|
||||
// {
|
||||
// mstore(0x80, 0)
|
||||
// leave
|
||||
// }
|
||||
// mstore(0x80, 1)
|
||||
// }
|
||||
// }
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
{
|
||||
mstore(0x40, memoryguard(0x80))
|
||||
sstore(0, f(1,2,3))
|
||||
}
|
||||
function f(a, b, c) -> $b1 {
|
||||
if calldataload(add(sub(a,b),c)) {
|
||||
$b1 := 0
|
||||
leave
|
||||
}
|
||||
$b1 := 1
|
||||
}
|
||||
|
||||
}
|
||||
// ----
|
||||
// step: fakeStackLimitEvader
|
||||
//
|
||||
// {
|
||||
// {
|
||||
// mstore(0x40, memoryguard(0xa0))
|
||||
// sstore(0, f(1, 2, 3))
|
||||
// }
|
||||
// function f(a_2, b_3, c_4) -> $b1
|
||||
// {
|
||||
// mstore(0x80, 0)
|
||||
// f_1(a_2, b_3, c_4)
|
||||
// $b1 := mload(0x80)
|
||||
// }
|
||||
// function f_1(a, b, c)
|
||||
// {
|
||||
// if calldataload(add(sub(a, b), c))
|
||||
// {
|
||||
// mstore(0x80, 0)
|
||||
// leave
|
||||
// }
|
||||
// mstore(0x80, 1)
|
||||
// }
|
||||
// }
|
@ -0,0 +1,17 @@
|
||||
{
|
||||
function f(x) -> y { y := x }
|
||||
mstore(0x40, memoryguard(0))
|
||||
|
||||
let $z := 42
|
||||
$z := f($z)
|
||||
}
|
||||
// ----
|
||||
// step: fakeStackLimitEvader
|
||||
//
|
||||
// {
|
||||
// function f(x) -> y
|
||||
// { y := x }
|
||||
// mstore(0x40, memoryguard(0x20))
|
||||
// mstore(0x00, 42)
|
||||
// mstore(0x00, f(mload(0x00)))
|
||||
// }
|
@ -50,6 +50,7 @@
|
||||
// }
|
||||
// function f(a1) -> v
|
||||
// {
|
||||
// mstore(0x80, a1)
|
||||
// let a2 := calldataload(mul(2, 4))
|
||||
// let a3 := calldataload(mul(3, 4))
|
||||
// let a4 := calldataload(mul(4, 4))
|
||||
@ -66,7 +67,7 @@
|
||||
// let a15 := calldataload(mul(15, 4))
|
||||
// let a16 := calldataload(mul(16, 4))
|
||||
// let a17 := calldataload(mul(17, 4))
|
||||
// sstore(0, a1)
|
||||
// sstore(0, mload(0x80))
|
||||
// sstore(mul(17, 4), a17)
|
||||
// sstore(mul(16, 4), a16)
|
||||
// sstore(mul(15, 4), a15)
|
||||
@ -83,6 +84,6 @@
|
||||
// sstore(mul(4, 4), a4)
|
||||
// sstore(mul(3, 4), a3)
|
||||
// sstore(mul(2, 4), a2)
|
||||
// sstore(mul(1, 4), a1)
|
||||
// sstore(mul(1, 4), mload(0x80))
|
||||
// }
|
||||
// }
|
||||
|
@ -1,13 +1,12 @@
|
||||
{
|
||||
{
|
||||
mstore(0x40, memoryguard(128))
|
||||
sstore(g(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16), f())
|
||||
sstore(g(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17), f())
|
||||
}
|
||||
function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16) -> v {
|
||||
// Should be, but cannot yet be escalated.
|
||||
v := b16
|
||||
function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17) -> v {
|
||||
v := add(b16, b17)
|
||||
}
|
||||
function f() -> v{
|
||||
function f() -> v {
|
||||
let a1 := calldataload(mul(1,4))
|
||||
let a2 := calldataload(mul(2,4))
|
||||
let a3 := calldataload(mul(3,4))
|
||||
@ -52,10 +51,10 @@
|
||||
// {
|
||||
// {
|
||||
// mstore(0x40, memoryguard(0xa0))
|
||||
// sstore(g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16), f())
|
||||
// sstore(g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17), f())
|
||||
// }
|
||||
// function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16) -> v
|
||||
// { v := b16 }
|
||||
// function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17) -> v
|
||||
// { v := add(b16, b17) }
|
||||
// function f() -> v_1
|
||||
// {
|
||||
// mstore(0x80, calldataload(mul(1, 4)))
|
||||
|
@ -0,0 +1,55 @@
|
||||
{
|
||||
{
|
||||
mstore(0x40, memoryguard(128))
|
||||
sstore(g(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29), 0)
|
||||
}
|
||||
function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29) -> v {
|
||||
sstore(0, b14)
|
||||
sstore(1, b15)
|
||||
sstore(2, b16)
|
||||
sstore(3, b17)
|
||||
sstore(4, b18)
|
||||
sstore(5, b19)
|
||||
sstore(6, b29)
|
||||
v := add(b1,b29)
|
||||
}
|
||||
|
||||
}
|
||||
// ----
|
||||
// step: stackLimitEvader
|
||||
//
|
||||
// {
|
||||
// {
|
||||
// mstore(0x40, memoryguard(0x02c0))
|
||||
// sstore(g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29), 0)
|
||||
// }
|
||||
// function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29) -> v
|
||||
// {
|
||||
// mstore(0x02a0, b1)
|
||||
// mstore(0x0280, b2)
|
||||
// mstore(0x0260, b3)
|
||||
// mstore(0x0240, b4)
|
||||
// mstore(0x0220, b5)
|
||||
// mstore(0x0200, b6)
|
||||
// mstore(0x01e0, b7)
|
||||
// mstore(0x01c0, b8)
|
||||
// mstore(0x01a0, b9)
|
||||
// mstore(0x0180, b10)
|
||||
// mstore(0x0160, b11)
|
||||
// mstore(0x0140, b12)
|
||||
// mstore(0x0120, b13)
|
||||
// mstore(0x0100, b14)
|
||||
// mstore(0xc0, b17)
|
||||
// mstore(0xa0, b18)
|
||||
// mstore(0x80, b19)
|
||||
// mstore(0xe0, b29)
|
||||
// sstore(0, mload(0x0100))
|
||||
// sstore(1, b15)
|
||||
// sstore(2, b16)
|
||||
// sstore(3, mload(0xc0))
|
||||
// sstore(4, mload(0xa0))
|
||||
// sstore(5, mload(0x80))
|
||||
// sstore(6, mload(0xe0))
|
||||
// v := add(mload(0x02a0), mload(0xe0))
|
||||
// }
|
||||
// }
|
@ -0,0 +1,61 @@
|
||||
{
|
||||
{
|
||||
mstore(0x40, memoryguard(128))
|
||||
sstore(g(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29, 30), 0)
|
||||
}
|
||||
function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30) -> v {
|
||||
sstore(0, b14)
|
||||
sstore(1, b15)
|
||||
sstore(2, b16)
|
||||
sstore(3, b17)
|
||||
sstore(4, b18)
|
||||
sstore(5, b19)
|
||||
sstore(6, b29)
|
||||
sstore(7, b30)
|
||||
v := b30
|
||||
sstore(b1, b30)
|
||||
}
|
||||
|
||||
}
|
||||
// ----
|
||||
// step: stackLimitEvader
|
||||
//
|
||||
// {
|
||||
// {
|
||||
// mstore(0x40, memoryguard(0x0300))
|
||||
// sstore(g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30), 0)
|
||||
// }
|
||||
// function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30) -> v
|
||||
// {
|
||||
// mstore(0x02e0, b1)
|
||||
// mstore(0x02c0, b2)
|
||||
// mstore(0x02a0, b3)
|
||||
// mstore(0x0280, b4)
|
||||
// mstore(0x0260, b5)
|
||||
// mstore(0x0240, b6)
|
||||
// mstore(0x0220, b7)
|
||||
// mstore(0x0200, b8)
|
||||
// mstore(0x01e0, b9)
|
||||
// mstore(0x01c0, b10)
|
||||
// mstore(0x01a0, b11)
|
||||
// mstore(0x0180, b12)
|
||||
// mstore(0x0160, b13)
|
||||
// mstore(0x0140, b14)
|
||||
// mstore(0x0120, b15)
|
||||
// mstore(0xc0, b17)
|
||||
// mstore(0xa0, b18)
|
||||
// mstore(0x80, b19)
|
||||
// mstore(0xe0, b29)
|
||||
// mstore(0x0100, b30)
|
||||
// sstore(0, mload(0x0140))
|
||||
// sstore(1, mload(0x0120))
|
||||
// sstore(2, b16)
|
||||
// sstore(3, mload(0xc0))
|
||||
// sstore(4, mload(0xa0))
|
||||
// sstore(5, mload(0x80))
|
||||
// sstore(6, mload(0xe0))
|
||||
// sstore(7, mload(0x0100))
|
||||
// v := mload(0x0100)
|
||||
// sstore(mload(0x02e0), mload(0x0100))
|
||||
// }
|
||||
// }
|
@ -0,0 +1,63 @@
|
||||
{
|
||||
{
|
||||
mstore(0x40, memoryguard(128))
|
||||
sstore(g(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31), 0)
|
||||
}
|
||||
function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31) -> v {
|
||||
sstore(0, b14)
|
||||
sstore(1, b15)
|
||||
sstore(2, b16)
|
||||
sstore(3, b17)
|
||||
sstore(4, b18)
|
||||
sstore(5, b19)
|
||||
sstore(6, b29)
|
||||
sstore(7, b30)
|
||||
sstore(8, b31)
|
||||
v := add(b1,b31)
|
||||
}
|
||||
|
||||
}
|
||||
// ----
|
||||
// step: stackLimitEvader
|
||||
//
|
||||
// {
|
||||
// {
|
||||
// mstore(0x40, memoryguard(0x0340))
|
||||
// sstore(g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31), 0)
|
||||
// }
|
||||
// function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31) -> v
|
||||
// {
|
||||
// mstore(0x0320, b1)
|
||||
// mstore(0x0300, b2)
|
||||
// mstore(0x02e0, b3)
|
||||
// mstore(0x02c0, b4)
|
||||
// mstore(0x02a0, b5)
|
||||
// mstore(0x0280, b6)
|
||||
// mstore(0x0260, b7)
|
||||
// mstore(0x0240, b8)
|
||||
// mstore(0x0220, b9)
|
||||
// mstore(0x0200, b10)
|
||||
// mstore(0x01e0, b11)
|
||||
// mstore(0x01c0, b12)
|
||||
// mstore(0x01a0, b13)
|
||||
// mstore(0x0180, b14)
|
||||
// mstore(0x0160, b15)
|
||||
// mstore(0x0140, b16)
|
||||
// mstore(0xc0, b17)
|
||||
// mstore(0xa0, b18)
|
||||
// mstore(0x80, b19)
|
||||
// mstore(0xe0, b29)
|
||||
// mstore(0x0120, b30)
|
||||
// mstore(0x0100, b31)
|
||||
// sstore(0, mload(0x0180))
|
||||
// sstore(1, mload(0x0160))
|
||||
// sstore(2, mload(0x0140))
|
||||
// sstore(3, mload(0xc0))
|
||||
// sstore(4, mload(0xa0))
|
||||
// sstore(5, mload(0x80))
|
||||
// sstore(6, mload(0xe0))
|
||||
// sstore(7, mload(0x0120))
|
||||
// sstore(8, mload(0x0100))
|
||||
// v := add(mload(0x0320), mload(0x0100))
|
||||
// }
|
||||
// }
|
@ -0,0 +1,48 @@
|
||||
{
|
||||
{
|
||||
mstore(0x40, memoryguard(128))
|
||||
let a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30)
|
||||
sstore(0, 1)
|
||||
a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30)
|
||||
}
|
||||
function g(b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30) -> b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 {
|
||||
b1 := 1
|
||||
b2 := 2
|
||||
b15 := 15
|
||||
sstore(b16, b30)
|
||||
}
|
||||
|
||||
}
|
||||
// ----
|
||||
// step: stackLimitEvader
|
||||
//
|
||||
// {
|
||||
// {
|
||||
// mstore(0x40, memoryguard(0x0260))
|
||||
// let a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30)
|
||||
// sstore(0, 1)
|
||||
// a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30)
|
||||
// }
|
||||
// function g(b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30) -> b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15
|
||||
// {
|
||||
// mstore(0x0240, b16)
|
||||
// mstore(0x0220, b17)
|
||||
// mstore(0x0200, b18)
|
||||
// mstore(0x01e0, b19)
|
||||
// mstore(0x01c0, b20)
|
||||
// mstore(0x01a0, b21)
|
||||
// mstore(0x0180, b22)
|
||||
// mstore(0x0160, b23)
|
||||
// mstore(0x0140, b24)
|
||||
// mstore(0x0120, b25)
|
||||
// mstore(0x0100, b26)
|
||||
// mstore(0xe0, b27)
|
||||
// mstore(0xc0, b28)
|
||||
// mstore(0xa0, b29)
|
||||
// mstore(0x80, b30)
|
||||
// b1 := 1
|
||||
// b2 := 2
|
||||
// b15 := 15
|
||||
// sstore(mload(0x0240), mload(0x80))
|
||||
// }
|
||||
// }
|
@ -0,0 +1,49 @@
|
||||
{
|
||||
{
|
||||
mstore(0x40, memoryguard(128))
|
||||
let a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31)
|
||||
sstore(0, 1)
|
||||
a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31)
|
||||
}
|
||||
function g(b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31) -> b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 {
|
||||
b1 := 1
|
||||
b2 := 2
|
||||
b15 := 15
|
||||
sstore(b16, b31)
|
||||
}
|
||||
|
||||
}
|
||||
// ----
|
||||
// step: stackLimitEvader
|
||||
//
|
||||
// {
|
||||
// {
|
||||
// mstore(0x40, memoryguard(0x0280))
|
||||
// let a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31)
|
||||
// sstore(0, 1)
|
||||
// a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31)
|
||||
// }
|
||||
// function g(b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31) -> b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15
|
||||
// {
|
||||
// mstore(0x0260, b16)
|
||||
// mstore(0x0240, b17)
|
||||
// mstore(0x0220, b18)
|
||||
// mstore(0x0200, b19)
|
||||
// mstore(0x01e0, b20)
|
||||
// mstore(0x01c0, b21)
|
||||
// mstore(0x01a0, b22)
|
||||
// mstore(0x0180, b23)
|
||||
// mstore(0x0160, b24)
|
||||
// mstore(0x0140, b25)
|
||||
// mstore(0x0120, b26)
|
||||
// mstore(0x0100, b27)
|
||||
// mstore(0xe0, b28)
|
||||
// mstore(0xc0, b29)
|
||||
// mstore(0xa0, b30)
|
||||
// mstore(0x80, b31)
|
||||
// b1 := 1
|
||||
// b2 := 2
|
||||
// b15 := 15
|
||||
// sstore(mload(0x0260), mload(0x80))
|
||||
// }
|
||||
// }
|
@ -0,0 +1,55 @@
|
||||
{
|
||||
{
|
||||
mstore(0x40, memoryguard(128))
|
||||
let a_1 := 1
|
||||
let a_2 := 2
|
||||
let a_3 := 3
|
||||
let a_4 := 4
|
||||
let a_5 := 5
|
||||
let a_6 := 6
|
||||
let a_7 := 7
|
||||
let a_8 := 8
|
||||
let a_9 := 9
|
||||
let a_10 := 10
|
||||
let a_11 := 11
|
||||
let a_12 := 12
|
||||
let a_13 := 13
|
||||
let a_14 := 14
|
||||
let a_15 := 15
|
||||
let a_16 := 16
|
||||
let a_17 := 17
|
||||
let a_18 := 18
|
||||
let a_19 := 19
|
||||
let a_20 := 20
|
||||
verbatim_20i_0o("test", a_1, a_2, a_3, a_4, a_5, a_6, a_7, a_8, a_9, a_10, a_11, a_12, a_13, a_14, a_15, a_16, a_17, a_18, a_19, a_20)
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: stackLimitEvader
|
||||
//
|
||||
// {
|
||||
// {
|
||||
// mstore(0x40, memoryguard(0x0200))
|
||||
// mstore(0x80, 1)
|
||||
// mstore(0xa0, 2)
|
||||
// mstore(0xc0, 3)
|
||||
// mstore(0xe0, 4)
|
||||
// mstore(0x0100, 5)
|
||||
// mstore(0x0120, 6)
|
||||
// mstore(0x0140, 7)
|
||||
// mstore(0x0160, 8)
|
||||
// mstore(0x0180, 9)
|
||||
// mstore(0x01c0, 10)
|
||||
// mstore(0x01a0, 11)
|
||||
// mstore(0x01e0, 12)
|
||||
// let a_13 := 13
|
||||
// let a_14 := 14
|
||||
// let a_15 := 15
|
||||
// let a_16 := 16
|
||||
// let a_17 := 17
|
||||
// let a_18 := 18
|
||||
// let a_19 := 19
|
||||
// let a_20 := 20
|
||||
// verbatim_20i_0o("test", mload(0x80), mload(0xa0), mload(0xc0), mload(0xe0), mload(0x0100), mload(0x0120), mload(0x0140), mload(0x0160), mload(0x0180), mload(0x01c0), mload(0x01a0), mload(0x01e0), a_13, a_14, a_15, a_16, a_17, a_18, a_19, a_20)
|
||||
// }
|
||||
// }
|
@ -0,0 +1,116 @@
|
||||
{
|
||||
{
|
||||
mstore(0x40, memoryguard(128))
|
||||
let a_1 := 1
|
||||
let a_2 := 2
|
||||
let a_3 := 3
|
||||
let a_4 := 4
|
||||
let a_5 := 5
|
||||
let a_6 := 6
|
||||
let a_7 := 7
|
||||
let a_8 := 8
|
||||
let a_9 := 9
|
||||
let a_10 := 10
|
||||
let a_11 := 11
|
||||
let a_12 := 12
|
||||
let a_13 := 13
|
||||
let a_14 := 14
|
||||
let a_15 := 15
|
||||
let a_16 := 16
|
||||
let a_17 := 17
|
||||
let a_18 := 18
|
||||
let a_19 := 19
|
||||
let a_20 := 20
|
||||
let b_1, b_2, b_3, b_4, b_5, b_6, b_7, b_8, b_9, b_10, b_11, b_12, b_13, b_14, b_15, b_16, b_17, b_18, b_19, b_20 := verbatim_20i_20o("test", a_1, a_2, a_3, a_4, a_5, a_6, a_7, a_8, a_9, a_10, a_11, a_12, a_13, a_14, a_15, a_16, a_17, a_18, a_19, a_20)
|
||||
sstore(1, b_1)
|
||||
sstore(2, b_2)
|
||||
sstore(3, b_3)
|
||||
sstore(4, b_4)
|
||||
sstore(5, b_5)
|
||||
sstore(6, b_6)
|
||||
sstore(7, b_7)
|
||||
sstore(8, b_8)
|
||||
sstore(9, b_9)
|
||||
sstore(10, b_10)
|
||||
sstore(11, b_11)
|
||||
sstore(12, b_12)
|
||||
sstore(13, b_13)
|
||||
sstore(14, b_14)
|
||||
sstore(15, b_15)
|
||||
sstore(16, b_16)
|
||||
sstore(17, b_17)
|
||||
sstore(18, b_18)
|
||||
sstore(19, b_19)
|
||||
sstore(20, b_20)
|
||||
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: stackLimitEvader
|
||||
//
|
||||
// {
|
||||
// {
|
||||
// mstore(0x40, memoryguard(0x0280))
|
||||
// mstore(0x80, 1)
|
||||
// mstore(0xa0, 2)
|
||||
// mstore(0xc0, 3)
|
||||
// mstore(0xe0, 4)
|
||||
// mstore(0x0100, 5)
|
||||
// mstore(0x0120, 6)
|
||||
// mstore(0x0140, 7)
|
||||
// mstore(0x0160, 8)
|
||||
// mstore(0x0180, 9)
|
||||
// mstore(0x0240, 10)
|
||||
// mstore(0x0220, 11)
|
||||
// mstore(0x0260, 12)
|
||||
// let a_13 := 13
|
||||
// let a_14 := 14
|
||||
// let a_15 := 15
|
||||
// let a_16 := 16
|
||||
// let a_17 := 17
|
||||
// let a_18 := 18
|
||||
// let a_19 := 19
|
||||
// let a_20 := 20
|
||||
// let b_1_1, b_2_2, b_3_3, b_4_4, b_5_5, b_6_6, b_7_7, b_8_8, b_9_9, b_10_10, b_11_11, b_12_12, b_13_13, b_14_14, b_15_15, b_16_16, b_17_17, b_18_18, b_19_19, b_20_20 := verbatim_20i_20o("test", mload(0x80), mload(0xa0), mload(0xc0), mload(0xe0), mload(0x0100), mload(0x0120), mload(0x0140), mload(0x0160), mload(0x0180), mload(0x0240), mload(0x0220), mload(0x0260), a_13, a_14, a_15, a_16, a_17, a_18, a_19, a_20)
|
||||
// mstore(0x01a0, b_4_4)
|
||||
// mstore(0x01c0, b_3_3)
|
||||
// mstore(0x01e0, b_2_2)
|
||||
// mstore(0x0200, b_1_1)
|
||||
// let b_20 := b_20_20
|
||||
// let b_19 := b_19_19
|
||||
// let b_18 := b_18_18
|
||||
// let b_17 := b_17_17
|
||||
// let b_16 := b_16_16
|
||||
// let b_15 := b_15_15
|
||||
// let b_14 := b_14_14
|
||||
// let b_13 := b_13_13
|
||||
// let b_12 := b_12_12
|
||||
// let b_11 := b_11_11
|
||||
// let b_10 := b_10_10
|
||||
// let b_9 := b_9_9
|
||||
// let b_8 := b_8_8
|
||||
// let b_7 := b_7_7
|
||||
// let b_6 := b_6_6
|
||||
// let b_5 := b_5_5
|
||||
// sstore(1, mload(0x0200))
|
||||
// sstore(2, mload(0x01e0))
|
||||
// sstore(3, mload(0x01c0))
|
||||
// sstore(4, mload(0x01a0))
|
||||
// sstore(5, b_5)
|
||||
// sstore(6, b_6)
|
||||
// sstore(7, b_7)
|
||||
// sstore(8, b_8)
|
||||
// sstore(9, b_9)
|
||||
// sstore(10, b_10)
|
||||
// sstore(11, b_11)
|
||||
// sstore(12, b_12)
|
||||
// sstore(13, b_13)
|
||||
// sstore(14, b_14)
|
||||
// sstore(15, b_15)
|
||||
// sstore(16, b_16)
|
||||
// sstore(17, b_17)
|
||||
// sstore(18, b_18)
|
||||
// sstore(19, b_19)
|
||||
// sstore(20, b_20)
|
||||
// }
|
||||
// }
|
@ -0,0 +1,53 @@
|
||||
{
|
||||
{
|
||||
mstore(0x40, memoryguard(128))
|
||||
let a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a13, a14, a15, a16, a17, a18 := verbatim_0i_16o("test")
|
||||
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a13, a14, a15, a16, a17, a18 := verbatim_0i_16o("test")
|
||||
sstore(a1, 10)
|
||||
sstore(a18, 20)
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: stackLimitEvader
|
||||
//
|
||||
// {
|
||||
// {
|
||||
// mstore(0x40, memoryguard(0xa0))
|
||||
// let a1_1, a2_2, a3_3, a4_4, a5_5, a6_6, a7_7, a8_8, a9_9, a10_10, a13_11, a14_12, a15_13, a16_14, a17_15, a18_16 := verbatim_0i_16o("test")
|
||||
// mstore(0x80, a1_1)
|
||||
// let a18 := a18_16
|
||||
// let a17 := a17_15
|
||||
// let a16 := a16_14
|
||||
// let a15 := a15_13
|
||||
// let a14 := a14_12
|
||||
// let a13 := a13_11
|
||||
// let a10 := a10_10
|
||||
// let a9 := a9_9
|
||||
// let a8 := a8_8
|
||||
// let a7 := a7_7
|
||||
// let a6 := a6_6
|
||||
// let a5 := a5_5
|
||||
// let a4 := a4_4
|
||||
// let a3 := a3_3
|
||||
// let a2 := a2_2
|
||||
// let a1_17, a2_18, a3_19, a4_20, a5_21, a6_22, a7_23, a8_24, a9_25, a10_26, a13_27, a14_28, a15_29, a16_30, a17_31, a18_32 := verbatim_0i_16o("test")
|
||||
// mstore(0x80, a1_17)
|
||||
// a18 := a18_32
|
||||
// a17 := a17_31
|
||||
// a16 := a16_30
|
||||
// a15 := a15_29
|
||||
// a14 := a14_28
|
||||
// a13 := a13_27
|
||||
// a10 := a10_26
|
||||
// a9 := a9_25
|
||||
// a8 := a8_24
|
||||
// a7 := a7_23
|
||||
// a6 := a6_22
|
||||
// a5 := a5_21
|
||||
// a4 := a4_20
|
||||
// a3 := a3_19
|
||||
// a2 := a2_18
|
||||
// sstore(mload(0x80), 10)
|
||||
// sstore(a18, 20)
|
||||
// }
|
||||
// }
|
Loading…
Reference in New Issue
Block a user