mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add stack limit evader.
This commit is contained in:
parent
b571fd05b0
commit
f4b42d1c72
14
docs/yul.rst
14
docs/yul.rst
@ -952,6 +952,20 @@ option.
|
|||||||
|
|
||||||
See :ref:`Using the Commandline Compiler <commandline-compiler>` for details about the Solidity linker.
|
See :ref:`Using the Commandline Compiler <commandline-compiler>` for details about the Solidity linker.
|
||||||
|
|
||||||
|
memoryguard
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
This function is available in the EVM dialect with objects. The caller of
|
||||||
|
``let ptr := memoryguard(size)`` promises that they only use memory in either
|
||||||
|
the range ``[0, size)`` or the unbounded range above ``ptr``. The Yul optimizer
|
||||||
|
promises to only use the memory range ``[size, ptr)`` for its purposes.
|
||||||
|
If the optimizer does not need to reserve any memory, it holds that ``ptr := size``.
|
||||||
|
|
||||||
|
``memoryguard`` can be called multiple times, but needs to have the same literal as argument
|
||||||
|
within one Yul subobject. If at least one ``memoryguard`` call is found in a subobject,
|
||||||
|
the Yul optimiser will try to perform experimental steps like the stack limit evader,
|
||||||
|
which attempts to move stack variables that would otherwise be unreachable
|
||||||
|
to memory.
|
||||||
|
|
||||||
.. _yul-object:
|
.. _yul-object:
|
||||||
|
|
||||||
|
@ -142,6 +142,9 @@ public:
|
|||||||
|
|
||||||
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
|
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
|
||||||
|
|
||||||
|
bool inlineAssemblySeen() const { return m_inlineAssemblySeen; }
|
||||||
|
void setInlineAssemblySeen() { m_inlineAssemblySeen = true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
RevertStrings m_revertStrings;
|
RevertStrings m_revertStrings;
|
||||||
@ -159,6 +162,9 @@ private:
|
|||||||
MultiUseYulFunctionCollector m_functions;
|
MultiUseYulFunctionCollector m_functions;
|
||||||
size_t m_varCounter = 0;
|
size_t m_varCounter = 0;
|
||||||
|
|
||||||
|
/// Flag indicating whether any inline assembly block was seen.
|
||||||
|
bool m_inlineAssemblySeen = false;
|
||||||
|
|
||||||
/// Function definitions queued for code generation. They're the Solidity functions whose calls
|
/// Function definitions queued for code generation. They're the Solidity functions whose calls
|
||||||
/// were discovered by the IR generator during AST traversal.
|
/// were discovered by the IR generator during AST traversal.
|
||||||
/// Note that the queue gets filled in a lazy way - new definitions can be added while the
|
/// Note that the queue gets filled in a lazy way - new definitions can be added while the
|
||||||
|
@ -92,7 +92,7 @@ string IRGenerator::generate(
|
|||||||
Whiskers t(R"(
|
Whiskers t(R"(
|
||||||
object "<CreationObject>" {
|
object "<CreationObject>" {
|
||||||
code {
|
code {
|
||||||
<memoryInit>
|
<memoryInitCreation>
|
||||||
<callValueCheck>
|
<callValueCheck>
|
||||||
<?notLibrary>
|
<?notLibrary>
|
||||||
<?constructorHasParams> let <constructorParams> := <copyConstructorArguments>() </constructorHasParams>
|
<?constructorHasParams> let <constructorParams> := <copyConstructorArguments>() </constructorHasParams>
|
||||||
@ -103,7 +103,7 @@ string IRGenerator::generate(
|
|||||||
}
|
}
|
||||||
object "<RuntimeObject>" {
|
object "<RuntimeObject>" {
|
||||||
code {
|
code {
|
||||||
<memoryInit>
|
<memoryInitRuntime>
|
||||||
<dispatch>
|
<dispatch>
|
||||||
<runtimeFunctions>
|
<runtimeFunctions>
|
||||||
}
|
}
|
||||||
@ -118,7 +118,6 @@ string IRGenerator::generate(
|
|||||||
m_context.registerImmutableVariable(*var);
|
m_context.registerImmutableVariable(*var);
|
||||||
|
|
||||||
t("CreationObject", IRNames::creationObject(_contract));
|
t("CreationObject", IRNames::creationObject(_contract));
|
||||||
t("memoryInit", memoryInit());
|
|
||||||
t("notLibrary", !_contract.isLibrary());
|
t("notLibrary", !_contract.isLibrary());
|
||||||
|
|
||||||
FunctionDefinition const* constructor = _contract.constructor();
|
FunctionDefinition const* constructor = _contract.constructor();
|
||||||
@ -143,6 +142,7 @@ string IRGenerator::generate(
|
|||||||
InternalDispatchMap internalDispatchMap = generateInternalDispatchFunctions();
|
InternalDispatchMap internalDispatchMap = generateInternalDispatchFunctions();
|
||||||
t("functions", m_context.functionCollector().requestedFunctions());
|
t("functions", m_context.functionCollector().requestedFunctions());
|
||||||
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
|
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
|
||||||
|
t("memoryInitCreation", memoryInit(!m_context.inlineAssemblySeen()));
|
||||||
|
|
||||||
resetContext(_contract);
|
resetContext(_contract);
|
||||||
|
|
||||||
@ -158,6 +158,7 @@ string IRGenerator::generate(
|
|||||||
generateInternalDispatchFunctions();
|
generateInternalDispatchFunctions();
|
||||||
t("runtimeFunctions", m_context.functionCollector().requestedFunctions());
|
t("runtimeFunctions", m_context.functionCollector().requestedFunctions());
|
||||||
t("runtimeSubObjects", subObjectSources(m_context.subObjectsCreated()));
|
t("runtimeSubObjects", subObjectSources(m_context.subObjectsCreated()));
|
||||||
|
t("memoryInitRuntime", memoryInit(!m_context.inlineAssemblySeen()));
|
||||||
return t.render();
|
return t.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -651,16 +652,22 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
|||||||
return t.render();
|
return t.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRGenerator::memoryInit()
|
string IRGenerator::memoryInit(bool _useMemoryGuard)
|
||||||
{
|
{
|
||||||
// This function should be called at the beginning of the EVM call frame
|
// This function should be called at the beginning of the EVM call frame
|
||||||
// and thus can assume all memory to be zero, including the contents of
|
// and thus can assume all memory to be zero, including the contents of
|
||||||
// the "zero memory area" (the position CompilerUtils::zeroPointer points to).
|
// the "zero memory area" (the position CompilerUtils::zeroPointer points to).
|
||||||
return
|
return
|
||||||
Whiskers{"mstore(<memPtr>, <freeMemoryStart>)"}
|
Whiskers{
|
||||||
|
_useMemoryGuard ?
|
||||||
|
"mstore(<memPtr>, memoryguard(<freeMemoryStart>))" :
|
||||||
|
"mstore(<memPtr>, <freeMemoryStart>)"
|
||||||
|
}
|
||||||
("memPtr", to_string(CompilerUtils::freeMemoryPointer))
|
("memPtr", to_string(CompilerUtils::freeMemoryPointer))
|
||||||
("freeMemoryStart", to_string(CompilerUtils::generalPurposeMemoryStart + m_context.reservedMemory()))
|
(
|
||||||
.render();
|
"freeMemoryStart",
|
||||||
|
to_string(CompilerUtils::generalPurposeMemoryStart + m_context.reservedMemory())
|
||||||
|
).render();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRGenerator::resetContext(ContractDefinition const& _contract)
|
void IRGenerator::resetContext(ContractDefinition const& _contract)
|
||||||
|
@ -100,7 +100,9 @@ private:
|
|||||||
|
|
||||||
std::string dispatchRoutine(ContractDefinition const& _contract);
|
std::string dispatchRoutine(ContractDefinition const& _contract);
|
||||||
|
|
||||||
std::string memoryInit();
|
/// @a _useMemoryGuard If true, use a memory guard, allowing the optimiser
|
||||||
|
/// to perform memory optimizations.
|
||||||
|
std::string memoryInit(bool _useMemoryGuard);
|
||||||
|
|
||||||
void resetContext(ContractDefinition const& _contract);
|
void resetContext(ContractDefinition const& _contract);
|
||||||
|
|
||||||
|
@ -1858,6 +1858,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
|
bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
|
||||||
{
|
{
|
||||||
setLocation(_inlineAsm);
|
setLocation(_inlineAsm);
|
||||||
|
m_context.setInlineAssemblySeen();
|
||||||
CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences};
|
CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences};
|
||||||
|
|
||||||
yul::Statement modified = bodyCopier(_inlineAsm.operations());
|
yul::Statement modified = bodyCopier(_inlineAsm.operations());
|
||||||
|
@ -105,6 +105,8 @@ add_library(yul
|
|||||||
optimiser/ForLoopInitRewriter.h
|
optimiser/ForLoopInitRewriter.h
|
||||||
optimiser/FullInliner.cpp
|
optimiser/FullInliner.cpp
|
||||||
optimiser/FullInliner.h
|
optimiser/FullInliner.h
|
||||||
|
optimiser/FunctionCallFinder.cpp
|
||||||
|
optimiser/FunctionCallFinder.h
|
||||||
optimiser/FunctionGrouper.cpp
|
optimiser/FunctionGrouper.cpp
|
||||||
optimiser/FunctionGrouper.h
|
optimiser/FunctionGrouper.h
|
||||||
optimiser/FunctionHoister.cpp
|
optimiser/FunctionHoister.cpp
|
||||||
@ -150,6 +152,10 @@ add_library(yul
|
|||||||
optimiser/SimplificationRules.h
|
optimiser/SimplificationRules.h
|
||||||
optimiser/StackCompressor.cpp
|
optimiser/StackCompressor.cpp
|
||||||
optimiser/StackCompressor.h
|
optimiser/StackCompressor.h
|
||||||
|
optimiser/StackLimitEvader.cpp
|
||||||
|
optimiser/StackLimitEvader.h
|
||||||
|
optimiser/StackToMemoryMover.cpp
|
||||||
|
optimiser/StackToMemoryMover.h
|
||||||
optimiser/StructuralSimplifier.cpp
|
optimiser/StructuralSimplifier.cpp
|
||||||
optimiser/StructuralSimplifier.h
|
optimiser/StructuralSimplifier.h
|
||||||
optimiser/Substitution.cpp
|
optimiser/Substitution.cpp
|
||||||
|
@ -33,7 +33,7 @@ using namespace solidity;
|
|||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
using namespace solidity::util;
|
using namespace solidity::util;
|
||||||
|
|
||||||
map<YulString, int> CompilabilityChecker::run(
|
CompilabilityChecker::CompilabilityChecker(
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
Object const& _object,
|
Object const& _object,
|
||||||
bool _optimizeStackAllocation
|
bool _optimizeStackAllocation
|
||||||
@ -63,12 +63,11 @@ map<YulString, int> CompilabilityChecker::run(
|
|||||||
);
|
);
|
||||||
transform(*_object.code);
|
transform(*_object.code);
|
||||||
|
|
||||||
std::map<YulString, int> functions;
|
|
||||||
for (StackTooDeepError const& error: transform.stackErrors())
|
for (StackTooDeepError const& error: transform.stackErrors())
|
||||||
functions[error.functionName] = max(error.depth, functions[error.functionName]);
|
{
|
||||||
|
unreachableVariables[error.functionName].emplace(error.variable);
|
||||||
return functions;
|
int& deficit = stackDeficit[error.functionName];
|
||||||
|
deficit = std::max(error.depth, deficit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
@ -33,22 +33,20 @@ namespace solidity::yul
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that checks whether all variables are reachable on the stack and
|
* Component that checks whether all variables are reachable on the stack and
|
||||||
* returns a mapping from function name to the largest stack difference found
|
* provides a mapping from function name to the largest stack difference found
|
||||||
* in that function (no entry present if that function is compilable).
|
* in that function (no entry present if that function is compilable), as well
|
||||||
|
* as the set of unreachable variables for each function.
|
||||||
*
|
*
|
||||||
* This only works properly if the outermost block is compilable and
|
* This only works properly if the outermost block is compilable and
|
||||||
* functions are not nested. Otherwise, it might miss reporting some functions.
|
* functions are not nested. Otherwise, it might miss reporting some functions.
|
||||||
*
|
*
|
||||||
* Only checks the code of the object itself, does not descend into sub-objects.
|
* Only checks the code of the object itself, does not descend into sub-objects.
|
||||||
*/
|
*/
|
||||||
class CompilabilityChecker
|
struct CompilabilityChecker
|
||||||
{
|
{
|
||||||
public:
|
CompilabilityChecker(Dialect const& _dialect, Object const& _object, bool _optimizeStackAllocation);
|
||||||
static std::map<YulString, int> run(
|
std::map<YulString, std::set<YulString>> unreachableVariables;
|
||||||
Dialect const& _dialect,
|
std::map<YulString, int> stackDeficit;
|
||||||
Object const& _object,
|
|
||||||
bool _optimizeStackAllocation
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -142,6 +142,23 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
|||||||
Expression const& arg = _call.arguments.front();
|
Expression const& arg = _call.arguments.front();
|
||||||
_assembly.appendLinkerSymbol(std::get<Literal>(arg).value.str());
|
_assembly.appendLinkerSymbol(std::get<Literal>(arg).value.str());
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
builtins.emplace(createFunction(
|
||||||
|
"memoryguard",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
SideEffects{},
|
||||||
|
{LiteralKind::Number},
|
||||||
|
[](
|
||||||
|
FunctionCall const& _call,
|
||||||
|
AbstractAssembly& _assembly,
|
||||||
|
BuiltinContext&,
|
||||||
|
function<void(Expression const&)> _visitExpression
|
||||||
|
) {
|
||||||
|
visitArguments(_assembly, _call, _visitExpression);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {LiteralKind::String}, [](
|
builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {LiteralKind::String}, [](
|
||||||
FunctionCall const& _call,
|
FunctionCall const& _call,
|
||||||
AbstractAssembly& _assembly,
|
AbstractAssembly& _assembly,
|
||||||
|
@ -1211,6 +1211,9 @@ function revert(x1, x2, x3, x4, y1, y2, y3, y4) {
|
|||||||
function invalid() {
|
function invalid() {
|
||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
|
function memoryguard(x:i64) -> y1, y2, y3, y4 {
|
||||||
|
y4 := x
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)"};
|
)"};
|
||||||
|
|
||||||
|
39
libyul/optimiser/FunctionCallFinder.cpp
Normal file
39
libyul/optimiser/FunctionCallFinder.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
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/FunctionCallFinder.h>
|
||||||
|
#include <libyul/AsmData.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity;
|
||||||
|
using namespace solidity::yul;
|
||||||
|
|
||||||
|
vector<FunctionCall*> FunctionCallFinder::run(Block& _block, YulString _functionName)
|
||||||
|
{
|
||||||
|
FunctionCallFinder functionCallFinder(_functionName);
|
||||||
|
functionCallFinder(_block);
|
||||||
|
return functionCallFinder.m_calls;
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionCallFinder::FunctionCallFinder(YulString _functionName): m_functionName(_functionName) {}
|
||||||
|
|
||||||
|
void FunctionCallFinder::operator()(FunctionCall& _functionCall)
|
||||||
|
{
|
||||||
|
ASTModifier::operator()(_functionCall);
|
||||||
|
if (_functionCall.functionName.name == m_functionName)
|
||||||
|
m_calls.emplace_back(&_functionCall);
|
||||||
|
}
|
47
libyul/optimiser/FunctionCallFinder.h
Normal file
47
libyul/optimiser/FunctionCallFinder.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
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 calls to a function of a given name.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libyul/optimiser/ASTWalker.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace solidity::yul
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AST walker that finds all calls to a function of a given name.
|
||||||
|
*
|
||||||
|
* Prerequisite: Disambiguator
|
||||||
|
*/
|
||||||
|
class FunctionCallFinder: ASTModifier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::vector<FunctionCall*> run(Block& _block, YulString _functionName);
|
||||||
|
private:
|
||||||
|
FunctionCallFinder(YulString _functionName);
|
||||||
|
using ASTModifier::operator();
|
||||||
|
void operator()(FunctionCall& _functionCall) override;
|
||||||
|
YulString m_functionName;
|
||||||
|
std::vector<FunctionCall*> m_calls;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -168,7 +168,7 @@ bool StackCompressor::run(
|
|||||||
bool allowMSizeOptimzation = !MSizeFinder::containsMSize(_dialect, *_object.code);
|
bool allowMSizeOptimzation = !MSizeFinder::containsMSize(_dialect, *_object.code);
|
||||||
for (size_t iterations = 0; iterations < _maxIterations; iterations++)
|
for (size_t iterations = 0; iterations < _maxIterations; iterations++)
|
||||||
{
|
{
|
||||||
map<YulString, int> stackSurplus = CompilabilityChecker::run(_dialect, _object, _optimizeStackAllocation);
|
map<YulString, int> stackSurplus = CompilabilityChecker(_dialect, _object, _optimizeStackAllocation).stackDeficit;
|
||||||
if (stackSurplus.empty())
|
if (stackSurplus.empty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
142
libyul/optimiser/StackLimitEvader.cpp
Normal file
142
libyul/optimiser/StackLimitEvader.cpp
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
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/StackLimitEvader.h>
|
||||||
|
#include <libyul/optimiser/CallGraphGenerator.h>
|
||||||
|
#include <libyul/optimiser/FunctionCallFinder.h>
|
||||||
|
#include <libyul/optimiser/NameDispenser.h>
|
||||||
|
#include <libyul/optimiser/StackToMemoryMover.h>
|
||||||
|
#include <libyul/backends/evm/EVMDialect.h>
|
||||||
|
#include <libyul/AsmData.h>
|
||||||
|
#include <libyul/Dialect.h>
|
||||||
|
#include <libyul/Exceptions.h>
|
||||||
|
#include <libyul/Object.h>
|
||||||
|
#include <libyul/Utilities.h>
|
||||||
|
#include <libsolutil/Algorithms.h>
|
||||||
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity;
|
||||||
|
using namespace solidity::yul;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Walks the call graph using a Depth-First-Search assigning memory offsets to variables.
|
||||||
|
// - The leaves of the call graph will get the lowest offsets, increasing towards the root.
|
||||||
|
// - ``nextAvailableSlot`` maps a function to the next available slot that can be used by another
|
||||||
|
// function that calls it.
|
||||||
|
// - For each function starting from the root of the call graph:
|
||||||
|
// - Visit all children that are not already visited.
|
||||||
|
// - Determine the maximum value ``n`` of the values of ``nextAvailableSlot`` among the children.
|
||||||
|
// - If the function itself contains variables that need memory slots, but is contained in a cycle,
|
||||||
|
// abort the process as failure.
|
||||||
|
// - If not, assign each variable its slot starting from ``n`` (incrementing it).
|
||||||
|
// - Assign ``n`` to ``nextAvailableSlot`` of the function.
|
||||||
|
struct MemoryOffsetAllocator
|
||||||
|
{
|
||||||
|
uint64_t run(YulString _function = YulString{})
|
||||||
|
{
|
||||||
|
if (nextAvailableSlot.count(_function))
|
||||||
|
return nextAvailableSlot[_function];
|
||||||
|
|
||||||
|
// Assign to zero early to guard against recursive calls.
|
||||||
|
nextAvailableSlot[_function] = 0;
|
||||||
|
|
||||||
|
uint64_t nextSlot = 0;
|
||||||
|
if (callGraph.count(_function))
|
||||||
|
for (YulString child: callGraph.at(_function))
|
||||||
|
nextSlot = std::max(run(child), nextSlot);
|
||||||
|
|
||||||
|
if (unreachableVariables.count(_function))
|
||||||
|
{
|
||||||
|
yulAssert(!slotAllocations.count(_function), "");
|
||||||
|
auto& assignedSlots = slotAllocations[_function];
|
||||||
|
for (YulString variable: unreachableVariables.at(_function))
|
||||||
|
if (variable.empty())
|
||||||
|
{
|
||||||
|
// TODO: Too many function arguments or return parameters.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
assignedSlots[variable] = nextSlot++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextAvailableSlot[_function] = nextSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
map<YulString, set<YulString>> const& unreachableVariables;
|
||||||
|
map<YulString, set<YulString>> const& callGraph;
|
||||||
|
|
||||||
|
map<YulString, map<YulString, uint64_t>> slotAllocations{};
|
||||||
|
map<YulString, uint64_t> nextAvailableSlot{};
|
||||||
|
};
|
||||||
|
|
||||||
|
u256 literalArgumentValue(FunctionCall const& _call)
|
||||||
|
{
|
||||||
|
yulAssert(_call.arguments.size() == 1, "");
|
||||||
|
Literal const* literal = std::get_if<Literal>(&_call.arguments.front());
|
||||||
|
yulAssert(literal && literal->kind == LiteralKind::Number, "");
|
||||||
|
return valueOfLiteral(*literal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackLimitEvader::run(
|
||||||
|
OptimiserStepContext& _context,
|
||||||
|
Object& _object,
|
||||||
|
map<YulString, set<YulString>> const& _unreachableVariables
|
||||||
|
)
|
||||||
|
{
|
||||||
|
yulAssert(_object.code, "");
|
||||||
|
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_context.dialect);
|
||||||
|
yulAssert(
|
||||||
|
evmDialect && evmDialect->providesObjectAccess(),
|
||||||
|
"StackLimitEvader can only be run on objects using the EVMDialect with object access."
|
||||||
|
);
|
||||||
|
|
||||||
|
vector<FunctionCall*> memoryGuardCalls = FunctionCallFinder::run(
|
||||||
|
*_object.code,
|
||||||
|
"memoryguard"_yulstring
|
||||||
|
);
|
||||||
|
// Do not optimise, if no ``memoryguard`` call is found.
|
||||||
|
if (memoryGuardCalls.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Make sure all calls to ``memoryguard`` we found have the same value as argument (otherwise, abort).
|
||||||
|
u256 reservedMemory = literalArgumentValue(*memoryGuardCalls.front());
|
||||||
|
for (FunctionCall const* getFreeMemoryStartCall: memoryGuardCalls)
|
||||||
|
if (reservedMemory != literalArgumentValue(*getFreeMemoryStartCall))
|
||||||
|
return;
|
||||||
|
|
||||||
|
CallGraph callGraph = CallGraphGenerator::callGraph(*_object.code);
|
||||||
|
|
||||||
|
// We cannot move variables in recursive functions to fixed memory offsets.
|
||||||
|
for (YulString function: callGraph.recursiveFunctions())
|
||||||
|
if (_unreachableVariables.count(function))
|
||||||
|
return;
|
||||||
|
|
||||||
|
MemoryOffsetAllocator memoryOffsetAllocator{_unreachableVariables, callGraph.functionCalls};
|
||||||
|
uint64_t requiredSlots = memoryOffsetAllocator.run();
|
||||||
|
|
||||||
|
StackToMemoryMover{_context, reservedMemory, memoryOffsetAllocator.slotAllocations}(*_object.code);
|
||||||
|
reservedMemory += 32 * requiredSlots;
|
||||||
|
YulString reservedMemoryString{util::toCompactHexWithPrefix(reservedMemory)};
|
||||||
|
for (FunctionCall* memoryGuardCall: memoryGuardCalls)
|
||||||
|
{
|
||||||
|
Literal* literal = std::get_if<Literal>(&memoryGuardCall->arguments.front());
|
||||||
|
yulAssert(literal && literal->kind == LiteralKind::Number, "");
|
||||||
|
literal->value = reservedMemoryString;
|
||||||
|
}
|
||||||
|
}
|
66
libyul/optimiser/StackLimitEvader.h
Normal file
66
libyul/optimiser/StackLimitEvader.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Optimisation stage that assigns memory offsets to variables that would become unreachable if
|
||||||
|
* assigned a stack slot as usual and replaces references and assignments to them by mload and mstore calls.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libyul/optimiser/OptimiserStep.h>
|
||||||
|
|
||||||
|
namespace solidity::yul
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optimisation stage that assigns memory offsets to variables that would become unreachable if
|
||||||
|
* assigned a stack slot as usual.
|
||||||
|
*
|
||||||
|
* Uses CompilabilityChecker to determine which variables in which functions are unreachable.
|
||||||
|
*
|
||||||
|
* Only variables outside of functions contained in cycles in the call graph are considered. Thereby it is possible
|
||||||
|
* to assign globally fixed memory offsets to the variable. If a variable in a function contained in a cycle in the
|
||||||
|
* call graph is reported as unreachable, the process is aborted.
|
||||||
|
*
|
||||||
|
* Offsets are assigned to the variables, s.t. on every path through the call graph each variable gets a unique offset
|
||||||
|
* in memory. However, distinct paths through the call graph can use the same memory offsets for their variables.
|
||||||
|
*
|
||||||
|
* The current arguments to the ``memoryguard`` calls are used as base memory offset and then replaced by the offset past
|
||||||
|
* the last memory offset used for a variable on any path through the call graph.
|
||||||
|
*
|
||||||
|
* Finally, the StackToMemoryMover is called to actually move the variables to their offsets in memory.
|
||||||
|
*
|
||||||
|
* Prerequisite: Disambiguator
|
||||||
|
*/
|
||||||
|
class StackLimitEvader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// @a _unreachableVariables can be determined by the CompilabilityChecker.
|
||||||
|
/// Can only be run on the EVM dialect with objects.
|
||||||
|
/// Abort and do nothing, if no ``memoryguard`` call or several ``memoryguard`` calls
|
||||||
|
/// with non-matching arguments are found, or if any of the @a _unreachableVariables
|
||||||
|
/// are contained in a recursive function.
|
||||||
|
static void run(
|
||||||
|
OptimiserStepContext& _context,
|
||||||
|
Object& _object,
|
||||||
|
std::map<YulString, std::set<YulString>> const& _unreachableVariables
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
210
libyul/optimiser/StackToMemoryMover.cpp
Normal file
210
libyul/optimiser/StackToMemoryMover.cpp
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
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/StackToMemoryMover.h>
|
||||||
|
#include <libyul/optimiser/NameDispenser.h>
|
||||||
|
#include <libyul/backends/evm/EVMDialect.h>
|
||||||
|
|
||||||
|
#include <libyul/AsmData.h>
|
||||||
|
|
||||||
|
#include <libsolutil/CommonData.h>
|
||||||
|
#include <libsolutil/Visitor.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity;
|
||||||
|
using namespace solidity::yul;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void appendMemoryStore(
|
||||||
|
vector<Statement>& _statements,
|
||||||
|
langutil::SourceLocation const& _loc,
|
||||||
|
YulString _mpos,
|
||||||
|
Expression _value
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_statements.emplace_back(ExpressionStatement{_loc, FunctionCall{
|
||||||
|
_loc,
|
||||||
|
Identifier{_loc, "mstore"_yulstring},
|
||||||
|
{
|
||||||
|
Literal{_loc, LiteralKind::Number, _mpos, {}},
|
||||||
|
std::move(_value)
|
||||||
|
}
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StackToMemoryMover::StackToMemoryMover(
|
||||||
|
OptimiserStepContext& _context,
|
||||||
|
u256 _reservedMemory,
|
||||||
|
map<YulString, map<YulString, uint64_t>> const& _memorySlots
|
||||||
|
): m_reservedMemory(std::move(_reservedMemory)), m_memorySlots(_memorySlots), m_nameDispenser(_context.dispenser)
|
||||||
|
{
|
||||||
|
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_context.dialect);
|
||||||
|
yulAssert(
|
||||||
|
evmDialect && evmDialect->providesObjectAccess(),
|
||||||
|
"StackToMemoryMover can only be run on objects using the EVMDialect with object access."
|
||||||
|
);
|
||||||
|
|
||||||
|
if (m_memorySlots.count(YulString{}))
|
||||||
|
// If the global scope contains variables to be moved, start with those as if it were a function.
|
||||||
|
m_currentFunctionMemorySlots = &m_memorySlots.at(YulString{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackToMemoryMover::operator()(FunctionDefinition& _functionDefinition)
|
||||||
|
{
|
||||||
|
map<YulString, uint64_t> const* saved = m_currentFunctionMemorySlots;
|
||||||
|
if (m_memorySlots.count(_functionDefinition.name))
|
||||||
|
{
|
||||||
|
m_currentFunctionMemorySlots = &m_memorySlots.at(_functionDefinition.name);
|
||||||
|
for (TypedName const& param: _functionDefinition.parameters + _functionDefinition.returnVariables)
|
||||||
|
if (m_currentFunctionMemorySlots->count(param.name))
|
||||||
|
{
|
||||||
|
// TODO: we cannot handle function parameters yet.
|
||||||
|
m_currentFunctionMemorySlots = nullptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_currentFunctionMemorySlots = nullptr;
|
||||||
|
ASTModifier::operator()(_functionDefinition);
|
||||||
|
m_currentFunctionMemorySlots = saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackToMemoryMover::operator()(Block& _block)
|
||||||
|
{
|
||||||
|
using OptionalStatements = std::optional<vector<Statement>>;
|
||||||
|
if (!m_currentFunctionMemorySlots)
|
||||||
|
{
|
||||||
|
ASTModifier::operator()(_block);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto containsVariableNeedingEscalation = [&](auto const& _variables) {
|
||||||
|
return util::contains_if(_variables, [&](auto const& var) {
|
||||||
|
return m_currentFunctionMemorySlots->count(var.name);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
auto rewriteAssignmentOrVariableDeclaration = [&](
|
||||||
|
langutil::SourceLocation const& _loc,
|
||||||
|
auto const& _variables,
|
||||||
|
std::unique_ptr<Expression> _value
|
||||||
|
) -> std::vector<Statement> {
|
||||||
|
if (_variables.size() == 1)
|
||||||
|
{
|
||||||
|
std::vector<Statement> result;
|
||||||
|
appendMemoryStore(
|
||||||
|
result,
|
||||||
|
_loc,
|
||||||
|
memoryOffset(_variables.front().name),
|
||||||
|
_value ? *std::move(_value) : Literal{_loc, LiteralKind::Number, "0"_yulstring, {}}
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
VariableDeclaration tempDecl{_loc, {}, std::move(_value)};
|
||||||
|
vector<Statement> memoryAssignments;
|
||||||
|
vector<Statement> variableAssignments;
|
||||||
|
for (auto& var: _variables)
|
||||||
|
{
|
||||||
|
YulString tempVarName = m_nameDispenser.newName(var.name);
|
||||||
|
tempDecl.variables.emplace_back(TypedName{var.location, tempVarName, {}});
|
||||||
|
|
||||||
|
if (m_currentFunctionMemorySlots->count(var.name))
|
||||||
|
appendMemoryStore(memoryAssignments, _loc, memoryOffset(var.name), Identifier{_loc, tempVarName});
|
||||||
|
else if constexpr (std::is_same_v<std::decay_t<decltype(var)>, Identifier>)
|
||||||
|
variableAssignments.emplace_back(Assignment{
|
||||||
|
_loc, { Identifier{var.location, var.name} },
|
||||||
|
make_unique<Expression>(Identifier{_loc, tempVarName})
|
||||||
|
});
|
||||||
|
else
|
||||||
|
variableAssignments.emplace_back(VariableDeclaration{
|
||||||
|
_loc, {std::move(var)},
|
||||||
|
make_unique<Expression>(Identifier{_loc, tempVarName})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
util::iterateReplacing(
|
||||||
|
_block.statements,
|
||||||
|
[&](Statement& _statement)
|
||||||
|
{
|
||||||
|
auto defaultVisit = [&]() { ASTModifier::visit(_statement); return OptionalStatements{}; };
|
||||||
|
return std::visit(util::GenericVisitor{
|
||||||
|
[&](Assignment& _assignment) -> OptionalStatements
|
||||||
|
{
|
||||||
|
if (!containsVariableNeedingEscalation(_assignment.variableNames))
|
||||||
|
return defaultVisit();
|
||||||
|
visit(*_assignment.value);
|
||||||
|
return {rewriteAssignmentOrVariableDeclaration(
|
||||||
|
_assignment.location,
|
||||||
|
_assignment.variableNames,
|
||||||
|
std::move(_assignment.value)
|
||||||
|
)};
|
||||||
|
},
|
||||||
|
[&](VariableDeclaration& _varDecl) -> OptionalStatements
|
||||||
|
{
|
||||||
|
if (!containsVariableNeedingEscalation(_varDecl.variables))
|
||||||
|
return defaultVisit();
|
||||||
|
if (_varDecl.value)
|
||||||
|
visit(*_varDecl.value);
|
||||||
|
return {rewriteAssignmentOrVariableDeclaration(
|
||||||
|
_varDecl.location,
|
||||||
|
_varDecl.variables,
|
||||||
|
std::move(_varDecl.value)
|
||||||
|
)};
|
||||||
|
},
|
||||||
|
[&](auto&) { return defaultVisit(); }
|
||||||
|
}, _statement);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackToMemoryMover::visit(Expression& _expression)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
Identifier* identifier = std::get_if<Identifier>(&_expression);
|
||||||
|
identifier && m_currentFunctionMemorySlots && m_currentFunctionMemorySlots->count(identifier->name)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
langutil::SourceLocation loc = identifier->location;
|
||||||
|
_expression = FunctionCall {
|
||||||
|
loc,
|
||||||
|
Identifier{loc, "mload"_yulstring}, {
|
||||||
|
Literal {
|
||||||
|
loc,
|
||||||
|
LiteralKind::Number,
|
||||||
|
memoryOffset(identifier->name),
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ASTModifier::visit(_expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
YulString StackToMemoryMover::memoryOffset(YulString _variable)
|
||||||
|
{
|
||||||
|
yulAssert(m_currentFunctionMemorySlots, "");
|
||||||
|
return YulString{util::toCompactHexWithPrefix(m_reservedMemory + 32 * m_currentFunctionMemorySlots->at(_variable))};
|
||||||
|
}
|
||||||
|
|
100
libyul/optimiser/StackToMemoryMover.h
Normal file
100
libyul/optimiser/StackToMemoryMover.h
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Optimisation stage that moves Yul variables from stack to memory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libyul/optimiser/ASTWalker.h>
|
||||||
|
#include <libyul/optimiser/OptimiserStep.h>
|
||||||
|
#include <libsolutil/Common.h>
|
||||||
|
|
||||||
|
namespace solidity::yul
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optimisation stage that moves Yul variables from stack to memory.
|
||||||
|
* It takes a map from functions names and variable names to memory offsets.
|
||||||
|
* It then transforms the AST as follows:
|
||||||
|
*
|
||||||
|
* Single variable declarations are replaced by mstore's as follows:
|
||||||
|
* If a is in the map, replace
|
||||||
|
* let a
|
||||||
|
* by
|
||||||
|
* mstore(<memory offset for a>, 0)
|
||||||
|
* respectively, replace
|
||||||
|
* let a := expr
|
||||||
|
* by
|
||||||
|
* mstore(<memory offset for a>, expr)
|
||||||
|
*
|
||||||
|
* In a multi-variable declaration, variables to be moved are replaced by fresh variables and then moved to memory:
|
||||||
|
* If b and d are in the map, replace
|
||||||
|
* let a, b, c, d := f()
|
||||||
|
* by
|
||||||
|
* let _1, _2, _3, _4 := f()
|
||||||
|
* mstore(<memory offset for d>, _4)
|
||||||
|
* mstore(<memory offset for b>, _2)
|
||||||
|
* let c := _3
|
||||||
|
* let a := _1
|
||||||
|
*
|
||||||
|
* Assignments to single variables are replaced by mstore's:
|
||||||
|
* If a is in the map, replace
|
||||||
|
* a := expr
|
||||||
|
* by
|
||||||
|
* mstore(<memory offset for a>, expr)
|
||||||
|
*
|
||||||
|
* Assignments to multiple variables are split up similarly to multi-variable declarations:
|
||||||
|
* If b and d are in the map, replace
|
||||||
|
* a, b, c, d := f()
|
||||||
|
* by
|
||||||
|
* let _1, _2, _3, _4 := f()
|
||||||
|
* mstore(<memory offset for d>, _4)
|
||||||
|
* mstore(<memory offset for b>, _2)
|
||||||
|
* c := _3
|
||||||
|
* a := _1
|
||||||
|
*
|
||||||
|
* 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).
|
||||||
|
*
|
||||||
|
* Prerequisite: Disambiguator, ForLoopInitRewriter.
|
||||||
|
*/
|
||||||
|
class StackToMemoryMover: ASTModifier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StackToMemoryMover(
|
||||||
|
OptimiserStepContext& _context,
|
||||||
|
u256 _reservedMemory,
|
||||||
|
std::map<YulString, std::map<YulString, uint64_t>> const& _memoryOffsets
|
||||||
|
);
|
||||||
|
|
||||||
|
using ASTModifier::operator();
|
||||||
|
|
||||||
|
void operator()(FunctionDefinition& _functionDefinition) override;
|
||||||
|
void operator()(Block& _block) override;
|
||||||
|
void visit(Expression& _expression) override;
|
||||||
|
private:
|
||||||
|
YulString memoryOffset(YulString _variable);
|
||||||
|
u256 m_reservedMemory;
|
||||||
|
std::map<YulString, std::map<YulString, uint64_t>> const& m_memorySlots;
|
||||||
|
NameDispenser& m_nameDispenser;
|
||||||
|
std::map<YulString, uint64_t> const* m_currentFunctionMemorySlots = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -51,6 +51,7 @@
|
|||||||
#include <libyul/optimiser/SSAReverser.h>
|
#include <libyul/optimiser/SSAReverser.h>
|
||||||
#include <libyul/optimiser/SSATransform.h>
|
#include <libyul/optimiser/SSATransform.h>
|
||||||
#include <libyul/optimiser/StackCompressor.h>
|
#include <libyul/optimiser/StackCompressor.h>
|
||||||
|
#include <libyul/optimiser/StackLimitEvader.h>
|
||||||
#include <libyul/optimiser/StructuralSimplifier.h>
|
#include <libyul/optimiser/StructuralSimplifier.h>
|
||||||
#include <libyul/optimiser/SyntacticalEquality.h>
|
#include <libyul/optimiser/SyntacticalEquality.h>
|
||||||
#include <libyul/optimiser/RedundantAssignEliminator.h>
|
#include <libyul/optimiser/RedundantAssignEliminator.h>
|
||||||
@ -73,6 +74,7 @@
|
|||||||
|
|
||||||
#include <boost/range/adaptor/map.hpp>
|
#include <boost/range/adaptor/map.hpp>
|
||||||
#include <boost/range/algorithm_ext/erase.hpp>
|
#include <boost/range/algorithm_ext/erase.hpp>
|
||||||
|
#include <libyul/CompilabilityChecker.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
@ -124,6 +126,12 @@ void OptimiserSuite::run(
|
|||||||
{
|
{
|
||||||
yulAssert(_meter, "");
|
yulAssert(_meter, "");
|
||||||
ConstantOptimiser{*dialect, *_meter}(ast);
|
ConstantOptimiser{*dialect, *_meter}(ast);
|
||||||
|
if (dialect->providesObjectAccess())
|
||||||
|
StackLimitEvader::run(suite.m_context, _object, CompilabilityChecker{
|
||||||
|
_dialect,
|
||||||
|
_object,
|
||||||
|
_optimizeStackAllocation
|
||||||
|
}.unreachableVariables);
|
||||||
}
|
}
|
||||||
else if (dynamic_cast<WasmDialect const*>(&_dialect))
|
else if (dynamic_cast<WasmDialect const*>(&_dialect))
|
||||||
{
|
{
|
||||||
|
@ -9,7 +9,7 @@ Optimized IR:
|
|||||||
object "C_6" {
|
object "C_6" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(0x80))
|
||||||
if callvalue() { revert(0, 0) }
|
if callvalue() { revert(0, 0) }
|
||||||
let _1 := datasize("C_6_deployed")
|
let _1 := datasize("C_6_deployed")
|
||||||
codecopy(0, dataoffset("C_6_deployed"), _1)
|
codecopy(0, dataoffset("C_6_deployed"), _1)
|
||||||
@ -19,7 +19,7 @@ object "C_6" {
|
|||||||
object "C_6_deployed" {
|
object "C_6_deployed" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(0x80))
|
||||||
revert(0, 0)
|
revert(0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ Optimized IR:
|
|||||||
object "D_9" {
|
object "D_9" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(0x80))
|
||||||
if callvalue() { revert(0, 0) }
|
if callvalue() { revert(0, 0) }
|
||||||
let _1 := datasize("D_9_deployed")
|
let _1 := datasize("D_9_deployed")
|
||||||
codecopy(0, dataoffset("D_9_deployed"), _1)
|
codecopy(0, dataoffset("D_9_deployed"), _1)
|
||||||
@ -47,7 +47,7 @@ object "D_9" {
|
|||||||
object "D_9_deployed" {
|
object "D_9_deployed" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(0x80))
|
||||||
revert(0, 0)
|
revert(0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ Optimized IR:
|
|||||||
object "C_2" {
|
object "C_2" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(0x80))
|
||||||
if callvalue() { revert(0, 0) }
|
if callvalue() { revert(0, 0) }
|
||||||
let _1 := datasize("C_2_deployed")
|
let _1 := datasize("C_2_deployed")
|
||||||
codecopy(0, dataoffset("C_2_deployed"), _1)
|
codecopy(0, dataoffset("C_2_deployed"), _1)
|
||||||
@ -19,7 +19,7 @@ object "C_2" {
|
|||||||
object "C_2_deployed" {
|
object "C_2_deployed" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(0x80))
|
||||||
revert(0, 0)
|
revert(0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ Optimized IR:
|
|||||||
object "D_13" {
|
object "D_13" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(0x80))
|
||||||
if callvalue() { revert(0, 0) }
|
if callvalue() { revert(0, 0) }
|
||||||
let _1 := datasize("D_13_deployed")
|
let _1 := datasize("D_13_deployed")
|
||||||
codecopy(0, dataoffset("D_13_deployed"), _1)
|
codecopy(0, dataoffset("D_13_deployed"), _1)
|
||||||
@ -47,20 +47,21 @@ object "D_13" {
|
|||||||
object "D_13_deployed" {
|
object "D_13_deployed" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
let _1 := memoryguard(0x80)
|
||||||
|
mstore(64, _1)
|
||||||
if iszero(lt(calldatasize(), 4))
|
if iszero(lt(calldatasize(), 4))
|
||||||
{
|
{
|
||||||
let _1 := 0
|
let _2 := 0
|
||||||
if eq(0x26121ff0, shr(224, calldataload(_1)))
|
if eq(0x26121ff0, shr(224, calldataload(_2)))
|
||||||
{
|
{
|
||||||
if callvalue() { revert(_1, _1) }
|
if callvalue() { revert(_2, _2) }
|
||||||
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
|
if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }
|
||||||
let _2 := datasize("C_2")
|
let _3 := datasize("C_2")
|
||||||
let _3 := add(128, _2)
|
let _4 := add(_1, _3)
|
||||||
if or(gt(_3, 0xffffffffffffffff), lt(_3, 128)) { revert(_1, _1) }
|
if or(gt(_4, 0xffffffffffffffff), lt(_4, _1)) { revert(_2, _2) }
|
||||||
datacopy(128, dataoffset("C_2"), _2)
|
datacopy(_1, dataoffset("C_2"), _3)
|
||||||
pop(create(_1, 128, _2))
|
pop(create(_2, _1, sub(_4, _1)))
|
||||||
return(allocateMemory(_1), _1)
|
return(allocateMemory(_2), _2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
revert(0, 0)
|
revert(0, 0)
|
||||||
@ -76,7 +77,7 @@ object "D_13" {
|
|||||||
object "C_2" {
|
object "C_2" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(0x80))
|
||||||
if callvalue() { revert(0, 0) }
|
if callvalue() { revert(0, 0) }
|
||||||
let _1 := datasize("C_2_deployed")
|
let _1 := datasize("C_2_deployed")
|
||||||
codecopy(0, dataoffset("C_2_deployed"), _1)
|
codecopy(0, dataoffset("C_2_deployed"), _1)
|
||||||
@ -86,7 +87,7 @@ object "D_13" {
|
|||||||
object "C_2_deployed" {
|
object "C_2_deployed" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(0x80))
|
||||||
revert(0, 0)
|
revert(0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
--ir-optimized --optimize
|
@ -0,0 +1,7 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >=0.0.0;
|
||||||
|
|
||||||
|
contract D {
|
||||||
|
constructor() { assembly {}}
|
||||||
|
function f() public pure {}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
Optimized IR:
|
||||||
|
/*******************************************************
|
||||||
|
* WARNING *
|
||||||
|
* Solidity to Yul compilation is still EXPERIMENTAL *
|
||||||
|
* It can result in LOSS OF FUNDS or worse *
|
||||||
|
* !USE AT YOUR OWN RISK! *
|
||||||
|
*******************************************************/
|
||||||
|
|
||||||
|
object "D_11" {
|
||||||
|
code {
|
||||||
|
{
|
||||||
|
mstore(64, 128)
|
||||||
|
if callvalue() { revert(0, 0) }
|
||||||
|
let _1 := datasize("D_11_deployed")
|
||||||
|
codecopy(0, dataoffset("D_11_deployed"), _1)
|
||||||
|
return(0, _1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object "D_11_deployed" {
|
||||||
|
code {
|
||||||
|
{
|
||||||
|
let _1 := memoryguard(0x80)
|
||||||
|
mstore(64, _1)
|
||||||
|
if iszero(lt(calldatasize(), 4))
|
||||||
|
{
|
||||||
|
let _2 := 0
|
||||||
|
if eq(0x26121ff0, shr(224, calldataload(_2)))
|
||||||
|
{
|
||||||
|
if callvalue() { revert(_2, _2) }
|
||||||
|
if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }
|
||||||
|
if gt(_1, 0xffffffffffffffff) { revert(_2, _2) }
|
||||||
|
mstore(64, _1)
|
||||||
|
return(_1, _2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
revert(0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
--ir-optimized --optimize
|
@ -0,0 +1,8 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >=0.0.0;
|
||||||
|
|
||||||
|
contract D {
|
||||||
|
function f() public pure {
|
||||||
|
assembly {}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
Optimized IR:
|
||||||
|
/*******************************************************
|
||||||
|
* WARNING *
|
||||||
|
* Solidity to Yul compilation is still EXPERIMENTAL *
|
||||||
|
* It can result in LOSS OF FUNDS or worse *
|
||||||
|
* !USE AT YOUR OWN RISK! *
|
||||||
|
*******************************************************/
|
||||||
|
|
||||||
|
object "D_7" {
|
||||||
|
code {
|
||||||
|
{
|
||||||
|
mstore(64, memoryguard(0x80))
|
||||||
|
if callvalue() { revert(0, 0) }
|
||||||
|
let _1 := datasize("D_7_deployed")
|
||||||
|
codecopy(0, dataoffset("D_7_deployed"), _1)
|
||||||
|
return(0, _1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object "D_7_deployed" {
|
||||||
|
code {
|
||||||
|
{
|
||||||
|
mstore(64, 128)
|
||||||
|
if iszero(lt(calldatasize(), 4))
|
||||||
|
{
|
||||||
|
let _1 := 0
|
||||||
|
if eq(0x26121ff0, shr(224, calldataload(_1)))
|
||||||
|
{
|
||||||
|
if callvalue() { revert(_1, _1) }
|
||||||
|
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
|
||||||
|
mstore(64, 128)
|
||||||
|
return(128, _1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
revert(0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@ Optimized IR:
|
|||||||
object "C_56" {
|
object "C_56" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(0x80))
|
||||||
if callvalue() { revert(0, 0) }
|
if callvalue() { revert(0, 0) }
|
||||||
let _1 := datasize("C_56_deployed")
|
let _1 := datasize("C_56_deployed")
|
||||||
codecopy(0, dataoffset("C_56_deployed"), _1)
|
codecopy(0, dataoffset("C_56_deployed"), _1)
|
||||||
@ -19,7 +19,7 @@ object "C_56" {
|
|||||||
object "C_56_deployed" {
|
object "C_56_deployed" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(0x80))
|
||||||
if iszero(lt(calldatasize(), 4))
|
if iszero(lt(calldatasize(), 4))
|
||||||
{
|
{
|
||||||
let _1 := 0
|
let _1 := 0
|
||||||
|
@ -9,7 +9,7 @@ Optimized IR:
|
|||||||
object "Arraysum_33" {
|
object "Arraysum_33" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(0x80))
|
||||||
if callvalue() { revert(0, 0) }
|
if callvalue() { revert(0, 0) }
|
||||||
let _1 := datasize("Arraysum_33_deployed")
|
let _1 := datasize("Arraysum_33_deployed")
|
||||||
codecopy(0, dataoffset("Arraysum_33_deployed"), _1)
|
codecopy(0, dataoffset("Arraysum_33_deployed"), _1)
|
||||||
@ -19,7 +19,7 @@ object "Arraysum_33" {
|
|||||||
object "Arraysum_33_deployed" {
|
object "Arraysum_33_deployed" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(0x80))
|
||||||
if iszero(lt(calldatasize(), 4))
|
if iszero(lt(calldatasize(), 4))
|
||||||
{
|
{
|
||||||
let _1 := 0
|
let _1 := 0
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
object \"C_6\" {
|
object \"C_6\" {
|
||||||
code {
|
code {
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(128))
|
||||||
if callvalue() { revert(0, 0) }
|
if callvalue() { revert(0, 0) }
|
||||||
constructor_C_6()
|
constructor_C_6()
|
||||||
codecopy(0, dataoffset(\"C_6_deployed\"), datasize(\"C_6_deployed\"))
|
codecopy(0, dataoffset(\"C_6_deployed\"), datasize(\"C_6_deployed\"))
|
||||||
@ -17,7 +17,7 @@ object \"C_6\" {
|
|||||||
}
|
}
|
||||||
object \"C_6_deployed\" {
|
object \"C_6_deployed\" {
|
||||||
code {
|
code {
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(128))
|
||||||
if iszero(lt(calldatasize(), 4))
|
if iszero(lt(calldatasize(), 4))
|
||||||
{
|
{
|
||||||
let selector := shift_right_224_unsigned(calldataload(0))
|
let selector := shift_right_224_unsigned(calldataload(0))
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
object \"C_6\" {
|
object \"C_6\" {
|
||||||
code {
|
code {
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(128))
|
||||||
if callvalue() { revert(0, 0) }
|
if callvalue() { revert(0, 0) }
|
||||||
|
|
||||||
constructor_C_6()
|
constructor_C_6()
|
||||||
@ -24,7 +24,7 @@ object \"C_6\" {
|
|||||||
}
|
}
|
||||||
object \"C_6_deployed\" {
|
object \"C_6_deployed\" {
|
||||||
code {
|
code {
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(128))
|
||||||
|
|
||||||
if iszero(lt(calldatasize(), 4))
|
if iszero(lt(calldatasize(), 4))
|
||||||
{
|
{
|
||||||
|
@ -9,7 +9,7 @@ Optimized IR:
|
|||||||
object "C_6" {
|
object "C_6" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(0x80))
|
||||||
if callvalue() { revert(0, 0) }
|
if callvalue() { revert(0, 0) }
|
||||||
codecopy(0, dataoffset("C_6_deployed"), datasize("C_6_deployed"))
|
codecopy(0, dataoffset("C_6_deployed"), datasize("C_6_deployed"))
|
||||||
return(0, datasize("C_6_deployed"))
|
return(0, datasize("C_6_deployed"))
|
||||||
@ -18,7 +18,7 @@ object "C_6" {
|
|||||||
object "C_6_deployed" {
|
object "C_6_deployed" {
|
||||||
code {
|
code {
|
||||||
{
|
{
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(0x80))
|
||||||
if iszero(lt(calldatasize(), 4))
|
if iszero(lt(calldatasize(), 4))
|
||||||
{
|
{
|
||||||
let selector := shift_right_224_unsigned(calldataload(0))
|
let selector := shift_right_224_unsigned(calldataload(0))
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
object \"C_10\" {
|
object \"C_10\" {
|
||||||
code {
|
code {
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(128))
|
||||||
if callvalue() { revert(0, 0) }
|
if callvalue() { revert(0, 0) }
|
||||||
|
|
||||||
constructor_C_10()
|
constructor_C_10()
|
||||||
@ -24,7 +24,7 @@ object \"C_10\" {
|
|||||||
}
|
}
|
||||||
object \"C_10_deployed\" {
|
object \"C_10_deployed\" {
|
||||||
code {
|
code {
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(128))
|
||||||
|
|
||||||
if iszero(lt(calldatasize(), 4))
|
if iszero(lt(calldatasize(), 4))
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
object \"C_10\" {
|
object \"C_10\" {
|
||||||
code {
|
code {
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(128))
|
||||||
if callvalue() { revert(0, 0) }
|
if callvalue() { revert(0, 0) }
|
||||||
|
|
||||||
constructor_C_10()
|
constructor_C_10()
|
||||||
@ -24,7 +24,7 @@ object \"C_10\" {
|
|||||||
}
|
}
|
||||||
object \"C_10_deployed\" {
|
object \"C_10_deployed\" {
|
||||||
code {
|
code {
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(128))
|
||||||
|
|
||||||
if iszero(lt(calldatasize(), 4))
|
if iszero(lt(calldatasize(), 4))
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
object \"C_10\" {
|
object \"C_10\" {
|
||||||
code {
|
code {
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(128))
|
||||||
if callvalue() { revert(0, 0) }
|
if callvalue() { revert(0, 0) }
|
||||||
|
|
||||||
constructor_C_10()
|
constructor_C_10()
|
||||||
@ -24,7 +24,7 @@ object \"C_10\" {
|
|||||||
}
|
}
|
||||||
object \"C_10_deployed\" {
|
object \"C_10_deployed\" {
|
||||||
code {
|
code {
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(128))
|
||||||
|
|
||||||
if iszero(lt(calldatasize(), 4))
|
if iszero(lt(calldatasize(), 4))
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
object \"C_10\" {
|
object \"C_10\" {
|
||||||
code {
|
code {
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(128))
|
||||||
if callvalue() { revert(0, 0) }
|
if callvalue() { revert(0, 0) }
|
||||||
|
|
||||||
constructor_C_10()
|
constructor_C_10()
|
||||||
@ -24,7 +24,7 @@ object \"C_10\" {
|
|||||||
}
|
}
|
||||||
object \"C_10_deployed\" {
|
object \"C_10_deployed\" {
|
||||||
code {
|
code {
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(128))
|
||||||
|
|
||||||
if iszero(lt(calldatasize(), 4))
|
if iszero(lt(calldatasize(), 4))
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
object \"C_10\" {
|
object \"C_10\" {
|
||||||
code {
|
code {
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(128))
|
||||||
if callvalue() { revert(0, 0) }
|
if callvalue() { revert(0, 0) }
|
||||||
|
|
||||||
constructor_C_10()
|
constructor_C_10()
|
||||||
@ -24,7 +24,7 @@ object \"C_10\" {
|
|||||||
}
|
}
|
||||||
object \"C_10_deployed\" {
|
object \"C_10_deployed\" {
|
||||||
code {
|
code {
|
||||||
mstore(64, 128)
|
mstore(64, memoryguard(128))
|
||||||
|
|
||||||
if iszero(lt(calldatasize(), 4))
|
if iszero(lt(calldatasize(), 4))
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
contract C {
|
||||||
|
uint256[1024] s;
|
||||||
|
function f() public returns (uint256 x) {
|
||||||
|
x = 42;
|
||||||
|
uint256 x0 = s[0];
|
||||||
|
uint256 x1 = s[1];
|
||||||
|
uint256 x2 = s[2];
|
||||||
|
uint256 x3 = s[3];
|
||||||
|
uint256 x4 = s[4];
|
||||||
|
uint256 x5 = s[5];
|
||||||
|
uint256 x6 = s[6];
|
||||||
|
uint256 x7 = s[7];
|
||||||
|
uint256 x8 = s[8];
|
||||||
|
uint256 x9 = s[9];
|
||||||
|
uint256 x10 = s[10];
|
||||||
|
uint256 x11 = s[11];
|
||||||
|
uint256 x12 = s[12];
|
||||||
|
uint256 x13 = s[13];
|
||||||
|
uint256 x14 = s[14];
|
||||||
|
uint256 x15 = s[15];
|
||||||
|
uint256 x16 = s[16];
|
||||||
|
uint256 x17 = s[17];
|
||||||
|
uint256 x18 = s[18];
|
||||||
|
s[1000] = x0 + 2;
|
||||||
|
s[118] = x18;
|
||||||
|
s[117] = x17;
|
||||||
|
s[116] = x16;
|
||||||
|
s[115] = x15;
|
||||||
|
s[114] = x14;
|
||||||
|
s[113] = x13;
|
||||||
|
s[112] = x12;
|
||||||
|
s[111] = x11;
|
||||||
|
s[110] = x10;
|
||||||
|
s[109] = x9;
|
||||||
|
s[108] = x8;
|
||||||
|
s[107] = x7;
|
||||||
|
s[106] = x6;
|
||||||
|
s[105] = x5;
|
||||||
|
s[104] = x4;
|
||||||
|
s[103] = x3;
|
||||||
|
s[102] = x2;
|
||||||
|
s[101] = x1;
|
||||||
|
s[100] = x0;
|
||||||
|
}
|
||||||
|
function test() public view returns(uint256) {
|
||||||
|
return s[1000];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// f() -> 0x2a
|
||||||
|
// test() -> 2
|
@ -0,0 +1,57 @@
|
|||||||
|
contract C {
|
||||||
|
uint256[1024] s;
|
||||||
|
function g() public returns (uint256) {
|
||||||
|
// try to prevent inlining
|
||||||
|
return f() + f() + f() + f() + f();
|
||||||
|
}
|
||||||
|
function f() public returns (uint256 x) {
|
||||||
|
x = 42;
|
||||||
|
uint256 x0 = s[0];
|
||||||
|
uint256 x1 = s[1];
|
||||||
|
uint256 x2 = s[2];
|
||||||
|
uint256 x3 = s[3];
|
||||||
|
uint256 x4 = s[4];
|
||||||
|
uint256 x5 = s[5];
|
||||||
|
uint256 x6 = s[6];
|
||||||
|
uint256 x7 = s[7];
|
||||||
|
uint256 x8 = s[8];
|
||||||
|
uint256 x9 = s[9];
|
||||||
|
uint256 x10 = s[10];
|
||||||
|
uint256 x11 = s[11];
|
||||||
|
uint256 x12 = s[12];
|
||||||
|
uint256 x13 = s[13];
|
||||||
|
uint256 x14 = s[14];
|
||||||
|
uint256 x15 = s[15];
|
||||||
|
uint256 x16 = s[16];
|
||||||
|
uint256 x17 = s[17];
|
||||||
|
uint256 x18 = s[18];
|
||||||
|
s[1000] = x0 + 2;
|
||||||
|
s[118] = x18;
|
||||||
|
s[117] = x17;
|
||||||
|
s[116] = x16;
|
||||||
|
s[115] = x15;
|
||||||
|
s[114] = x14;
|
||||||
|
s[113] = x13;
|
||||||
|
s[112] = x12;
|
||||||
|
s[111] = x11;
|
||||||
|
s[110] = x10;
|
||||||
|
s[109] = x9;
|
||||||
|
s[108] = x8;
|
||||||
|
s[107] = x7;
|
||||||
|
s[106] = x6;
|
||||||
|
s[105] = x5;
|
||||||
|
s[104] = x4;
|
||||||
|
s[103] = x3;
|
||||||
|
s[102] = x2;
|
||||||
|
s[101] = x1;
|
||||||
|
s[100] = x0;
|
||||||
|
}
|
||||||
|
function test() public view returns(uint256) {
|
||||||
|
return s[1000];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// f() -> 0x2a
|
||||||
|
// test() -> 2
|
@ -39,7 +39,7 @@ string check(string const& _input)
|
|||||||
Object obj;
|
Object obj;
|
||||||
std::tie(obj.code, obj.analysisInfo) = yul::test::parse(_input, false);
|
std::tie(obj.code, obj.analysisInfo) = yul::test::parse(_input, false);
|
||||||
BOOST_REQUIRE(obj.code);
|
BOOST_REQUIRE(obj.code);
|
||||||
map<YulString, int> functions = CompilabilityChecker::run(EVMDialect::strictAssemblyForEVM(solidity::test::CommonOptions::get().evmVersion()), obj, true);
|
auto functions = CompilabilityChecker(EVMDialect::strictAssemblyForEVM(solidity::test::CommonOptions::get().evmVersion()), obj, true).stackDeficit;
|
||||||
string out;
|
string out;
|
||||||
for (auto const& function: functions)
|
for (auto const& function: functions)
|
||||||
out += function.first.str() + ": " + to_string(function.second) + " ";
|
out += function.first.str() + ": " + to_string(function.second) + " ";
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include <libyul/optimiser/LoadResolver.h>
|
#include <libyul/optimiser/LoadResolver.h>
|
||||||
#include <libyul/optimiser/LoopInvariantCodeMotion.h>
|
#include <libyul/optimiser/LoopInvariantCodeMotion.h>
|
||||||
#include <libyul/optimiser/MainFunction.h>
|
#include <libyul/optimiser/MainFunction.h>
|
||||||
|
#include <libyul/optimiser/StackLimitEvader.h>
|
||||||
#include <libyul/optimiser/NameDisplacer.h>
|
#include <libyul/optimiser/NameDisplacer.h>
|
||||||
#include <libyul/optimiser/Rematerialiser.h>
|
#include <libyul/optimiser/Rematerialiser.h>
|
||||||
#include <libyul/optimiser/ExpressionSimplifier.h>
|
#include <libyul/optimiser/ExpressionSimplifier.h>
|
||||||
@ -60,6 +61,7 @@
|
|||||||
#include <libyul/optimiser/RedundantAssignEliminator.h>
|
#include <libyul/optimiser/RedundantAssignEliminator.h>
|
||||||
#include <libyul/optimiser/StructuralSimplifier.h>
|
#include <libyul/optimiser/StructuralSimplifier.h>
|
||||||
#include <libyul/optimiser/StackCompressor.h>
|
#include <libyul/optimiser/StackCompressor.h>
|
||||||
|
#include <libyul/optimiser/StackToMemoryMover.h>
|
||||||
#include <libyul/optimiser/Suite.h>
|
#include <libyul/optimiser/Suite.h>
|
||||||
#include <libyul/backends/evm/ConstantOptimiser.h>
|
#include <libyul/backends/evm/ConstantOptimiser.h>
|
||||||
#include <libyul/backends/evm/EVMDialect.h>
|
#include <libyul/backends/evm/EVMDialect.h>
|
||||||
@ -70,6 +72,7 @@
|
|||||||
#include <libyul/AsmParser.h>
|
#include <libyul/AsmParser.h>
|
||||||
#include <libyul/AsmAnalysis.h>
|
#include <libyul/AsmAnalysis.h>
|
||||||
#include <libyul/AssemblyStack.h>
|
#include <libyul/AssemblyStack.h>
|
||||||
|
#include <libyul/CompilabilityChecker.h>
|
||||||
#include <liblangutil/SourceReferenceFormatter.h>
|
#include <liblangutil/SourceReferenceFormatter.h>
|
||||||
|
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
@ -378,6 +381,58 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
|
|||||||
obj.analysisInfo = m_analysisInfo;
|
obj.analysisInfo = m_analysisInfo;
|
||||||
OptimiserSuite::run(*m_dialect, &meter, obj, true, solidity::frontend::OptimiserSettings::DefaultYulOptimiserSteps);
|
OptimiserSuite::run(*m_dialect, &meter, obj, true, solidity::frontend::OptimiserSettings::DefaultYulOptimiserSteps);
|
||||||
}
|
}
|
||||||
|
else if (m_optimizerStep == "stackLimitEvader")
|
||||||
|
{
|
||||||
|
yul::Object obj;
|
||||||
|
obj.code = m_object->code;
|
||||||
|
obj.analysisInfo = m_analysisInfo;
|
||||||
|
disambiguate();
|
||||||
|
StackLimitEvader::run(*m_context, obj, CompilabilityChecker{
|
||||||
|
*m_dialect,
|
||||||
|
obj,
|
||||||
|
true
|
||||||
|
}.unreachableVariables);
|
||||||
|
}
|
||||||
|
else if (m_optimizerStep == "fakeStackLimitEvader")
|
||||||
|
{
|
||||||
|
yul::Object obj;
|
||||||
|
obj.code = m_object->code;
|
||||||
|
obj.analysisInfo = m_analysisInfo;
|
||||||
|
disambiguate();
|
||||||
|
// Mark all variables with a name starting with "$" for escalation to memory.
|
||||||
|
struct FakeUnreachableGenerator: ASTWalker
|
||||||
|
{
|
||||||
|
map<YulString, set<YulString>> fakeUnreachables;
|
||||||
|
using ASTWalker::operator();
|
||||||
|
void operator()(FunctionDefinition const& _function) override
|
||||||
|
{
|
||||||
|
YulString originalFunctionName = m_currentFunction;
|
||||||
|
m_currentFunction = _function.name;
|
||||||
|
ASTWalker::operator()(_function);
|
||||||
|
m_currentFunction = originalFunctionName;
|
||||||
|
}
|
||||||
|
void visitVariableName(YulString _var)
|
||||||
|
{
|
||||||
|
if (!_var.empty() && _var.str().front() == '$')
|
||||||
|
fakeUnreachables[m_currentFunction].insert(_var);
|
||||||
|
}
|
||||||
|
void operator()(VariableDeclaration const& _varDecl) override
|
||||||
|
{
|
||||||
|
for (auto const& var: _varDecl.variables)
|
||||||
|
visitVariableName(var.name);
|
||||||
|
ASTWalker::operator()(_varDecl);
|
||||||
|
}
|
||||||
|
void operator()(Identifier const& _identifier) override
|
||||||
|
{
|
||||||
|
visitVariableName(_identifier.name);
|
||||||
|
ASTWalker::operator()(_identifier);
|
||||||
|
}
|
||||||
|
YulString m_currentFunction = YulString{};
|
||||||
|
};
|
||||||
|
FakeUnreachableGenerator fakeUnreachableGenerator;
|
||||||
|
fakeUnreachableGenerator(*obj.code);
|
||||||
|
StackLimitEvader::run(*m_context, obj, fakeUnreachableGenerator.fakeUnreachables);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Invalid optimizer step: " << m_optimizerStep << endl;
|
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Invalid optimizer step: " << m_optimizerStep << endl;
|
||||||
|
12
test/libyul/ewasmTranslationTests/memoryguard.yul
Normal file
12
test/libyul/ewasmTranslationTests/memoryguard.yul
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
mstore(0x40, memoryguard(0x0102030405060708))
|
||||||
|
sstore(1, mload(0x40))
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Trace:
|
||||||
|
// Memory dump:
|
||||||
|
// 0: 0000000000000000000000000000000000000000000000000000000000000001
|
||||||
|
// 20: 0000000000000000000000000000000000000000000000000102030405060708
|
||||||
|
// 80: 0000000000000000000000000000000000000000000000000102030405060708
|
||||||
|
// Storage dump:
|
||||||
|
// 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000102030405060708
|
@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
mstore(0x40, memoryguard(0))
|
||||||
|
function g() -> a, b {
|
||||||
|
a := 21
|
||||||
|
let $c := 1
|
||||||
|
b,a,$c := z()
|
||||||
|
}
|
||||||
|
function f() -> x {
|
||||||
|
let $x2
|
||||||
|
$x2 := 42
|
||||||
|
let $x3, $x4 := g()
|
||||||
|
x := mul(add($x2, $x3), h($x4))
|
||||||
|
sstore($x3, $x4)
|
||||||
|
}
|
||||||
|
function h(v) -> a {
|
||||||
|
let x, $z, y := z()
|
||||||
|
a, $z, v := z()
|
||||||
|
}
|
||||||
|
function z() -> a,b,c { let $x := 0 }
|
||||||
|
sstore(0, f())
|
||||||
|
let x, y := g()
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fakeStackLimitEvader
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// mstore(0x40, memoryguard(0xa0))
|
||||||
|
// function g() -> a, b
|
||||||
|
// {
|
||||||
|
// a := 21
|
||||||
|
// mstore(0x20, 1)
|
||||||
|
// let b_1, a_2, $c_3 := z()
|
||||||
|
// mstore(0x20, $c_3)
|
||||||
|
// a := a_2
|
||||||
|
// b := b_1
|
||||||
|
// }
|
||||||
|
// function f() -> x
|
||||||
|
// {
|
||||||
|
// mstore(0x60, 0)
|
||||||
|
// mstore(0x60, 42)
|
||||||
|
// let $x3_4, $x4_5 := g()
|
||||||
|
// mstore(0x80, $x4_5)
|
||||||
|
// mstore(0x40, $x3_4)
|
||||||
|
// x := mul(add(mload(0x60), mload(0x40)), h(mload(0x80)))
|
||||||
|
// sstore(mload(0x40), mload(0x80))
|
||||||
|
// }
|
||||||
|
// function h(v) -> a_1
|
||||||
|
// {
|
||||||
|
// let x_2_6, $z_7, y_8 := z()
|
||||||
|
// mstore(0x20, $z_7)
|
||||||
|
// let y := y_8
|
||||||
|
// let x_2 := x_2_6
|
||||||
|
// let a_1_9, $z_10, v_11 := z()
|
||||||
|
// mstore(0x20, $z_10)
|
||||||
|
// v := v_11
|
||||||
|
// a_1 := a_1_9
|
||||||
|
// }
|
||||||
|
// function z() -> a_3, b_4, c
|
||||||
|
// { mstore(0x00, 0) }
|
||||||
|
// sstore(0, f())
|
||||||
|
// let x_5, y_6 := g()
|
||||||
|
// }
|
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
mstore(0x40, memoryguard(0))
|
||||||
|
let $x := 0
|
||||||
|
sstore(0, $x)
|
||||||
|
function h($hx) -> y {
|
||||||
|
y := $hx
|
||||||
|
}
|
||||||
|
sstore(1, h(32))
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fakeStackLimitEvader
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// mstore(0x40, memoryguard(0x40))
|
||||||
|
// mstore(0x20, 0)
|
||||||
|
// sstore(0, mload(0x20))
|
||||||
|
// function h($hx) -> y
|
||||||
|
// { y := $hx }
|
||||||
|
// sstore(1, h(32))
|
||||||
|
// }
|
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
mstore(0x40, memoryguard(0x80))
|
||||||
|
let $x := 42
|
||||||
|
sstore(42, $x)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fakeStackLimitEvader
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// mstore(0x40, memoryguard(0xa0))
|
||||||
|
// mstore(0x80, 42)
|
||||||
|
// sstore(42, mload(0x80))
|
||||||
|
// }
|
88
test/libyul/yulOptimizerTests/fakeStackLimitEvader/stub.yul
Normal file
88
test/libyul/yulOptimizerTests/fakeStackLimitEvader/stub.yul
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
mstore(0x40, memoryguard(0))
|
||||||
|
function f() {
|
||||||
|
let $fx
|
||||||
|
let $fy := 42
|
||||||
|
sstore($fx, $fy)
|
||||||
|
$fx := 21
|
||||||
|
}
|
||||||
|
function g(gx) {
|
||||||
|
let $gx, $gy := tuple2()
|
||||||
|
{ $gx, $gy := tuple2() }
|
||||||
|
{ $gx, gx := tuple2() }
|
||||||
|
{ gx, $gy := tuple2() }
|
||||||
|
}
|
||||||
|
function h(hx, hy, hz, hw) {
|
||||||
|
let $hx, $hy, $hz, $hw := tuple4()
|
||||||
|
{ hx, $hy, hz, $hw := tuple4() }
|
||||||
|
{ $hx, $hy, hz, hw := tuple4() }
|
||||||
|
}
|
||||||
|
function tuple2() -> a, b {}
|
||||||
|
function tuple4() -> a, b, c, d {}
|
||||||
|
f()
|
||||||
|
g(0)
|
||||||
|
h(1, 2, 3, 4)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fakeStackLimitEvader
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// mstore(0x40, memoryguard(0x80))
|
||||||
|
// function f()
|
||||||
|
// {
|
||||||
|
// mstore(0x20, 0)
|
||||||
|
// mstore(0x00, 42)
|
||||||
|
// sstore(mload(0x20), mload(0x00))
|
||||||
|
// mstore(0x20, 21)
|
||||||
|
// }
|
||||||
|
// function g(gx)
|
||||||
|
// {
|
||||||
|
// let $gx_1, $gy_2 := tuple2()
|
||||||
|
// mstore(0x20, $gy_2)
|
||||||
|
// mstore(0x00, $gx_1)
|
||||||
|
// {
|
||||||
|
// let $gx_3, $gy_4 := tuple2()
|
||||||
|
// mstore(0x20, $gy_4)
|
||||||
|
// mstore(0x00, $gx_3)
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// let $gx_5, gx_6 := tuple2()
|
||||||
|
// mstore(0x00, $gx_5)
|
||||||
|
// gx := gx_6
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// let gx_7, $gy_8 := tuple2()
|
||||||
|
// mstore(0x20, $gy_8)
|
||||||
|
// gx := gx_7
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// function h(hx, hy, hz, hw)
|
||||||
|
// {
|
||||||
|
// let $hx_9, $hy_10, $hz_11, $hw_12 := tuple4()
|
||||||
|
// mstore(0x60, $hw_12)
|
||||||
|
// mstore(0x00, $hz_11)
|
||||||
|
// mstore(0x20, $hy_10)
|
||||||
|
// mstore(0x40, $hx_9)
|
||||||
|
// {
|
||||||
|
// let hx_13, $hy_14, hz_15, $hw_16 := tuple4()
|
||||||
|
// mstore(0x60, $hw_16)
|
||||||
|
// mstore(0x20, $hy_14)
|
||||||
|
// hz := hz_15
|
||||||
|
// hx := hx_13
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// let $hx_17, $hy_18, hz_19, hw_20 := tuple4()
|
||||||
|
// mstore(0x20, $hy_18)
|
||||||
|
// mstore(0x40, $hx_17)
|
||||||
|
// hw := hw_20
|
||||||
|
// hz := hz_19
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// function tuple2() -> a, b
|
||||||
|
// { }
|
||||||
|
// function tuple4() -> a_1, b_2, c, d
|
||||||
|
// { }
|
||||||
|
// f()
|
||||||
|
// g(0)
|
||||||
|
// h(1, 2, 3, 4)
|
||||||
|
// }
|
95
test/libyul/yulOptimizerTests/stackLimitEvader/cycle.yul
Normal file
95
test/libyul/yulOptimizerTests/stackLimitEvader/cycle.yul
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
{
|
||||||
|
mstore(0x40, memoryguard(128))
|
||||||
|
sstore(0, g(sload(3)))
|
||||||
|
function g(x) -> v {
|
||||||
|
v := f()
|
||||||
|
}
|
||||||
|
function f() -> v {
|
||||||
|
let a1 := calldataload(mul(1,4))
|
||||||
|
let a2 := calldataload(mul(2,4))
|
||||||
|
let a3 := calldataload(mul(3,4))
|
||||||
|
let a4 := calldataload(mul(4,4))
|
||||||
|
let a5 := calldataload(mul(5,4))
|
||||||
|
let a6 := calldataload(mul(6,4))
|
||||||
|
let a7 := calldataload(mul(7,4))
|
||||||
|
let a8 := calldataload(mul(8,4))
|
||||||
|
let a9 := calldataload(mul(9,4))
|
||||||
|
a1 := calldataload(mul(0,4))
|
||||||
|
let a10 := calldataload(mul(10,4))
|
||||||
|
let a11 := calldataload(mul(11,4))
|
||||||
|
let a12 := calldataload(mul(12,4))
|
||||||
|
let a13 := calldataload(mul(13,4))
|
||||||
|
let a14 := calldataload(mul(14,4))
|
||||||
|
let a15 := calldataload(mul(15,4))
|
||||||
|
let a16 := calldataload(mul(16,4))
|
||||||
|
let a17 := calldataload(mul(17,4))
|
||||||
|
sstore(0, a1)
|
||||||
|
sstore(mul(17,4), a17)
|
||||||
|
sstore(mul(16,4), a16)
|
||||||
|
sstore(mul(15,4), a15)
|
||||||
|
sstore(mul(14,4), a14)
|
||||||
|
sstore(mul(13,4), a13)
|
||||||
|
sstore(mul(12,4), a12)
|
||||||
|
sstore(mul(11,4), a11)
|
||||||
|
sstore(mul(10,4), a10)
|
||||||
|
sstore(mul(9,4), a9)
|
||||||
|
sstore(mul(8,4), a8)
|
||||||
|
sstore(mul(7,4), a7)
|
||||||
|
sstore(mul(6,4), a6)
|
||||||
|
sstore(mul(5,4), a5)
|
||||||
|
sstore(mul(4,4), a4)
|
||||||
|
sstore(mul(3,4), a3)
|
||||||
|
sstore(mul(2,4), a2)
|
||||||
|
sstore(mul(1,4), a1)
|
||||||
|
sstore(23, g(sload(42)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: stackLimitEvader
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// mstore(0x40, memoryguard(128))
|
||||||
|
// sstore(0, g(sload(3)))
|
||||||
|
// function g(x) -> v
|
||||||
|
// { v := f() }
|
||||||
|
// function f() -> v_1
|
||||||
|
// {
|
||||||
|
// let a1 := calldataload(mul(1, 4))
|
||||||
|
// let a2 := calldataload(mul(2, 4))
|
||||||
|
// let a3 := calldataload(mul(3, 4))
|
||||||
|
// let a4 := calldataload(mul(4, 4))
|
||||||
|
// let a5 := calldataload(mul(5, 4))
|
||||||
|
// let a6 := calldataload(mul(6, 4))
|
||||||
|
// let a7 := calldataload(mul(7, 4))
|
||||||
|
// let a8 := calldataload(mul(8, 4))
|
||||||
|
// let a9 := calldataload(mul(9, 4))
|
||||||
|
// a1 := calldataload(mul(0, 4))
|
||||||
|
// let a10 := calldataload(mul(10, 4))
|
||||||
|
// let a11 := calldataload(mul(11, 4))
|
||||||
|
// let a12 := calldataload(mul(12, 4))
|
||||||
|
// let a13 := calldataload(mul(13, 4))
|
||||||
|
// let a14 := calldataload(mul(14, 4))
|
||||||
|
// let a15 := calldataload(mul(15, 4))
|
||||||
|
// let a16 := calldataload(mul(16, 4))
|
||||||
|
// let a17 := calldataload(mul(17, 4))
|
||||||
|
// sstore(0, a1)
|
||||||
|
// sstore(mul(17, 4), a17)
|
||||||
|
// sstore(mul(16, 4), a16)
|
||||||
|
// sstore(mul(15, 4), a15)
|
||||||
|
// sstore(mul(14, 4), a14)
|
||||||
|
// sstore(mul(13, 4), a13)
|
||||||
|
// sstore(mul(12, 4), a12)
|
||||||
|
// sstore(mul(11, 4), a11)
|
||||||
|
// sstore(mul(10, 4), a10)
|
||||||
|
// sstore(mul(9, 4), a9)
|
||||||
|
// sstore(mul(8, 4), a8)
|
||||||
|
// sstore(mul(7, 4), a7)
|
||||||
|
// sstore(mul(6, 4), a6)
|
||||||
|
// sstore(mul(5, 4), a5)
|
||||||
|
// sstore(mul(4, 4), a4)
|
||||||
|
// sstore(mul(3, 4), a3)
|
||||||
|
// sstore(mul(2, 4), a2)
|
||||||
|
// sstore(mul(1, 4), a1)
|
||||||
|
// sstore(23, g(sload(42)))
|
||||||
|
// }
|
||||||
|
// }
|
@ -0,0 +1,95 @@
|
|||||||
|
{
|
||||||
|
mstore(0x40, memoryguard(128))
|
||||||
|
sstore(0, f())
|
||||||
|
function f() -> v {
|
||||||
|
let a1 := calldataload(mul(1,4))
|
||||||
|
let a2 := calldataload(mul(2,4))
|
||||||
|
let a3 := calldataload(mul(3,4))
|
||||||
|
let a4 := calldataload(mul(4,4))
|
||||||
|
let a5 := calldataload(mul(5,4))
|
||||||
|
let a6 := calldataload(mul(6,4))
|
||||||
|
let a7 := calldataload(mul(7,4))
|
||||||
|
let a8 := calldataload(mul(8,4))
|
||||||
|
let a9 := calldataload(mul(9,4))
|
||||||
|
a1 := calldataload(mul(0,4))
|
||||||
|
let a10 := calldataload(mul(10,4))
|
||||||
|
let a11 := calldataload(mul(11,4))
|
||||||
|
let a12 := calldataload(mul(12,4))
|
||||||
|
let a13 := calldataload(mul(13,4))
|
||||||
|
let a14 := calldataload(mul(14,4))
|
||||||
|
let a15 := calldataload(mul(15,4))
|
||||||
|
let a16 := calldataload(mul(16,4))
|
||||||
|
let a17 := calldataload(mul(17,4))
|
||||||
|
sstore(0, a1)
|
||||||
|
sstore(mul(17,4), a17)
|
||||||
|
sstore(mul(16,4), a16)
|
||||||
|
sstore(mul(15,4), a15)
|
||||||
|
sstore(mul(14,4), a14)
|
||||||
|
sstore(mul(13,4), a13)
|
||||||
|
sstore(mul(12,4), a12)
|
||||||
|
sstore(mul(11,4), a11)
|
||||||
|
sstore(mul(10,4), a10)
|
||||||
|
sstore(mul(9,4), a9)
|
||||||
|
sstore(mul(8,4), a8)
|
||||||
|
sstore(mul(7,4), a7)
|
||||||
|
sstore(mul(6,4), a6)
|
||||||
|
sstore(mul(5,4), a5)
|
||||||
|
sstore(mul(4,4), a4)
|
||||||
|
sstore(mul(3,4), a3)
|
||||||
|
sstore(mul(2,4), a2)
|
||||||
|
sstore(mul(1,4), a1)
|
||||||
|
sstore(23, h())
|
||||||
|
}
|
||||||
|
function h() -> v {
|
||||||
|
v := h()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: stackLimitEvader
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// mstore(0x40, memoryguard(0xa0))
|
||||||
|
// sstore(0, f())
|
||||||
|
// function f() -> v
|
||||||
|
// {
|
||||||
|
// mstore(0x80, calldataload(mul(1, 4)))
|
||||||
|
// let a2 := calldataload(mul(2, 4))
|
||||||
|
// let a3 := calldataload(mul(3, 4))
|
||||||
|
// let a4 := calldataload(mul(4, 4))
|
||||||
|
// let a5 := calldataload(mul(5, 4))
|
||||||
|
// let a6 := calldataload(mul(6, 4))
|
||||||
|
// let a7 := calldataload(mul(7, 4))
|
||||||
|
// let a8 := calldataload(mul(8, 4))
|
||||||
|
// let a9 := calldataload(mul(9, 4))
|
||||||
|
// mstore(0x80, calldataload(mul(0, 4)))
|
||||||
|
// let a10 := calldataload(mul(10, 4))
|
||||||
|
// let a11 := calldataload(mul(11, 4))
|
||||||
|
// let a12 := calldataload(mul(12, 4))
|
||||||
|
// let a13 := calldataload(mul(13, 4))
|
||||||
|
// let a14 := calldataload(mul(14, 4))
|
||||||
|
// let a15 := calldataload(mul(15, 4))
|
||||||
|
// let a16 := calldataload(mul(16, 4))
|
||||||
|
// let a17 := calldataload(mul(17, 4))
|
||||||
|
// sstore(0, mload(0x80))
|
||||||
|
// sstore(mul(17, 4), a17)
|
||||||
|
// sstore(mul(16, 4), a16)
|
||||||
|
// sstore(mul(15, 4), a15)
|
||||||
|
// sstore(mul(14, 4), a14)
|
||||||
|
// sstore(mul(13, 4), a13)
|
||||||
|
// sstore(mul(12, 4), a12)
|
||||||
|
// sstore(mul(11, 4), a11)
|
||||||
|
// sstore(mul(10, 4), a10)
|
||||||
|
// sstore(mul(9, 4), a9)
|
||||||
|
// sstore(mul(8, 4), a8)
|
||||||
|
// sstore(mul(7, 4), a7)
|
||||||
|
// sstore(mul(6, 4), a6)
|
||||||
|
// sstore(mul(5, 4), a5)
|
||||||
|
// sstore(mul(4, 4), a4)
|
||||||
|
// sstore(mul(3, 4), a3)
|
||||||
|
// sstore(mul(2, 4), a2)
|
||||||
|
// sstore(mul(1, 4), mload(0x80))
|
||||||
|
// sstore(23, h())
|
||||||
|
// }
|
||||||
|
// function h() -> v_1
|
||||||
|
// { v_1 := h() }
|
||||||
|
// }
|
100
test/libyul/yulOptimizerTests/stackLimitEvader/cycle_after_2.yul
Normal file
100
test/libyul/yulOptimizerTests/stackLimitEvader/cycle_after_2.yul
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
{
|
||||||
|
mstore(0x40, memoryguard(128))
|
||||||
|
sstore(0, f())
|
||||||
|
function f() -> v {
|
||||||
|
let a1 := calldataload(mul(1,4))
|
||||||
|
let a2 := calldataload(mul(2,4))
|
||||||
|
let a3 := calldataload(mul(3,4))
|
||||||
|
let a4 := calldataload(mul(4,4))
|
||||||
|
let a5 := calldataload(mul(5,4))
|
||||||
|
let a6 := calldataload(mul(6,4))
|
||||||
|
let a7 := calldataload(mul(7,4))
|
||||||
|
let a8 := calldataload(mul(8,4))
|
||||||
|
let a9 := calldataload(mul(9,4))
|
||||||
|
a1 := calldataload(mul(0,4))
|
||||||
|
let a10 := calldataload(mul(10,4))
|
||||||
|
let a11 := calldataload(mul(11,4))
|
||||||
|
let a12 := calldataload(mul(12,4))
|
||||||
|
let a13 := calldataload(mul(13,4))
|
||||||
|
let a14 := calldataload(mul(14,4))
|
||||||
|
let a15 := calldataload(mul(15,4))
|
||||||
|
let a16 := calldataload(mul(16,4))
|
||||||
|
let a17 := calldataload(mul(17,4))
|
||||||
|
sstore(0, a1)
|
||||||
|
sstore(mul(17,4), a17)
|
||||||
|
sstore(mul(16,4), a16)
|
||||||
|
sstore(mul(15,4), a15)
|
||||||
|
sstore(mul(14,4), a14)
|
||||||
|
sstore(mul(13,4), a13)
|
||||||
|
sstore(mul(12,4), a12)
|
||||||
|
sstore(mul(11,4), a11)
|
||||||
|
sstore(mul(10,4), a10)
|
||||||
|
sstore(mul(9,4), a9)
|
||||||
|
sstore(mul(8,4), a8)
|
||||||
|
sstore(mul(7,4), a7)
|
||||||
|
sstore(mul(6,4), a6)
|
||||||
|
sstore(mul(5,4), a5)
|
||||||
|
sstore(mul(4,4), a4)
|
||||||
|
sstore(mul(3,4), a3)
|
||||||
|
sstore(mul(2,4), a2)
|
||||||
|
sstore(mul(1,4), a1)
|
||||||
|
sstore(23, h())
|
||||||
|
}
|
||||||
|
function h() -> v {
|
||||||
|
v := i()
|
||||||
|
}
|
||||||
|
function i() -> v {
|
||||||
|
v := h()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: stackLimitEvader
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// mstore(0x40, memoryguard(0xa0))
|
||||||
|
// sstore(0, f())
|
||||||
|
// function f() -> v
|
||||||
|
// {
|
||||||
|
// mstore(0x80, calldataload(mul(1, 4)))
|
||||||
|
// let a2 := calldataload(mul(2, 4))
|
||||||
|
// let a3 := calldataload(mul(3, 4))
|
||||||
|
// let a4 := calldataload(mul(4, 4))
|
||||||
|
// let a5 := calldataload(mul(5, 4))
|
||||||
|
// let a6 := calldataload(mul(6, 4))
|
||||||
|
// let a7 := calldataload(mul(7, 4))
|
||||||
|
// let a8 := calldataload(mul(8, 4))
|
||||||
|
// let a9 := calldataload(mul(9, 4))
|
||||||
|
// mstore(0x80, calldataload(mul(0, 4)))
|
||||||
|
// let a10 := calldataload(mul(10, 4))
|
||||||
|
// let a11 := calldataload(mul(11, 4))
|
||||||
|
// let a12 := calldataload(mul(12, 4))
|
||||||
|
// let a13 := calldataload(mul(13, 4))
|
||||||
|
// let a14 := calldataload(mul(14, 4))
|
||||||
|
// let a15 := calldataload(mul(15, 4))
|
||||||
|
// let a16 := calldataload(mul(16, 4))
|
||||||
|
// let a17 := calldataload(mul(17, 4))
|
||||||
|
// sstore(0, mload(0x80))
|
||||||
|
// sstore(mul(17, 4), a17)
|
||||||
|
// sstore(mul(16, 4), a16)
|
||||||
|
// sstore(mul(15, 4), a15)
|
||||||
|
// sstore(mul(14, 4), a14)
|
||||||
|
// sstore(mul(13, 4), a13)
|
||||||
|
// sstore(mul(12, 4), a12)
|
||||||
|
// sstore(mul(11, 4), a11)
|
||||||
|
// sstore(mul(10, 4), a10)
|
||||||
|
// sstore(mul(9, 4), a9)
|
||||||
|
// sstore(mul(8, 4), a8)
|
||||||
|
// sstore(mul(7, 4), a7)
|
||||||
|
// sstore(mul(6, 4), a6)
|
||||||
|
// sstore(mul(5, 4), a5)
|
||||||
|
// sstore(mul(4, 4), a4)
|
||||||
|
// sstore(mul(3, 4), a3)
|
||||||
|
// sstore(mul(2, 4), a2)
|
||||||
|
// sstore(mul(1, 4), mload(0x80))
|
||||||
|
// sstore(23, h())
|
||||||
|
// }
|
||||||
|
// function h() -> v_1
|
||||||
|
// { v_1 := i() }
|
||||||
|
// function i() -> v_2
|
||||||
|
// { v_2 := h() }
|
||||||
|
// }
|
103
test/libyul/yulOptimizerTests/stackLimitEvader/cycle_before.yul
Normal file
103
test/libyul/yulOptimizerTests/stackLimitEvader/cycle_before.yul
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
{
|
||||||
|
mstore(0x40, memoryguard(128))
|
||||||
|
sstore(0, g(sload(3)))
|
||||||
|
function g(x) -> v {
|
||||||
|
switch lt(x, 3)
|
||||||
|
case 0 {
|
||||||
|
v := f()
|
||||||
|
}
|
||||||
|
case 1 {
|
||||||
|
v := g(sub(x,1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function f() -> v {
|
||||||
|
let a1 := calldataload(mul(1,4))
|
||||||
|
let a2 := calldataload(mul(2,4))
|
||||||
|
let a3 := calldataload(mul(3,4))
|
||||||
|
let a4 := calldataload(mul(4,4))
|
||||||
|
let a5 := calldataload(mul(5,4))
|
||||||
|
let a6 := calldataload(mul(6,4))
|
||||||
|
let a7 := calldataload(mul(7,4))
|
||||||
|
let a8 := calldataload(mul(8,4))
|
||||||
|
let a9 := calldataload(mul(9,4))
|
||||||
|
a1 := calldataload(mul(0,4))
|
||||||
|
let a10 := calldataload(mul(10,4))
|
||||||
|
let a11 := calldataload(mul(11,4))
|
||||||
|
let a12 := calldataload(mul(12,4))
|
||||||
|
let a13 := calldataload(mul(13,4))
|
||||||
|
let a14 := calldataload(mul(14,4))
|
||||||
|
let a15 := calldataload(mul(15,4))
|
||||||
|
let a16 := calldataload(mul(16,4))
|
||||||
|
let a17 := calldataload(mul(17,4))
|
||||||
|
sstore(0, a1)
|
||||||
|
sstore(mul(17,4), a17)
|
||||||
|
sstore(mul(16,4), a16)
|
||||||
|
sstore(mul(15,4), a15)
|
||||||
|
sstore(mul(14,4), a14)
|
||||||
|
sstore(mul(13,4), a13)
|
||||||
|
sstore(mul(12,4), a12)
|
||||||
|
sstore(mul(11,4), a11)
|
||||||
|
sstore(mul(10,4), a10)
|
||||||
|
sstore(mul(9,4), a9)
|
||||||
|
sstore(mul(8,4), a8)
|
||||||
|
sstore(mul(7,4), a7)
|
||||||
|
sstore(mul(6,4), a6)
|
||||||
|
sstore(mul(5,4), a5)
|
||||||
|
sstore(mul(4,4), a4)
|
||||||
|
sstore(mul(3,4), a3)
|
||||||
|
sstore(mul(2,4), a2)
|
||||||
|
sstore(mul(1,4), a1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: stackLimitEvader
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// mstore(0x40, memoryguard(0xa0))
|
||||||
|
// sstore(0, g(sload(3)))
|
||||||
|
// function g(x) -> v
|
||||||
|
// {
|
||||||
|
// switch lt(x, 3)
|
||||||
|
// case 0 { v := f() }
|
||||||
|
// case 1 { v := g(sub(x, 1)) }
|
||||||
|
// }
|
||||||
|
// function f() -> v_1
|
||||||
|
// {
|
||||||
|
// mstore(0x80, calldataload(mul(1, 4)))
|
||||||
|
// let a2 := calldataload(mul(2, 4))
|
||||||
|
// let a3 := calldataload(mul(3, 4))
|
||||||
|
// let a4 := calldataload(mul(4, 4))
|
||||||
|
// let a5 := calldataload(mul(5, 4))
|
||||||
|
// let a6 := calldataload(mul(6, 4))
|
||||||
|
// let a7 := calldataload(mul(7, 4))
|
||||||
|
// let a8 := calldataload(mul(8, 4))
|
||||||
|
// let a9 := calldataload(mul(9, 4))
|
||||||
|
// mstore(0x80, calldataload(mul(0, 4)))
|
||||||
|
// let a10 := calldataload(mul(10, 4))
|
||||||
|
// let a11 := calldataload(mul(11, 4))
|
||||||
|
// let a12 := calldataload(mul(12, 4))
|
||||||
|
// let a13 := calldataload(mul(13, 4))
|
||||||
|
// let a14 := calldataload(mul(14, 4))
|
||||||
|
// let a15 := calldataload(mul(15, 4))
|
||||||
|
// let a16 := calldataload(mul(16, 4))
|
||||||
|
// let a17 := calldataload(mul(17, 4))
|
||||||
|
// sstore(0, mload(0x80))
|
||||||
|
// sstore(mul(17, 4), a17)
|
||||||
|
// sstore(mul(16, 4), a16)
|
||||||
|
// sstore(mul(15, 4), a15)
|
||||||
|
// sstore(mul(14, 4), a14)
|
||||||
|
// sstore(mul(13, 4), a13)
|
||||||
|
// sstore(mul(12, 4), a12)
|
||||||
|
// sstore(mul(11, 4), a11)
|
||||||
|
// sstore(mul(10, 4), a10)
|
||||||
|
// sstore(mul(9, 4), a9)
|
||||||
|
// sstore(mul(8, 4), a8)
|
||||||
|
// sstore(mul(7, 4), a7)
|
||||||
|
// sstore(mul(6, 4), a6)
|
||||||
|
// sstore(mul(5, 4), a5)
|
||||||
|
// sstore(mul(4, 4), a4)
|
||||||
|
// sstore(mul(3, 4), a3)
|
||||||
|
// sstore(mul(2, 4), a2)
|
||||||
|
// sstore(mul(1, 4), mload(0x80))
|
||||||
|
// }
|
||||||
|
// }
|
@ -0,0 +1,108 @@
|
|||||||
|
{
|
||||||
|
mstore(0x40, memoryguard(128))
|
||||||
|
sstore(0, g(sload(3)))
|
||||||
|
function g(x) -> v {
|
||||||
|
switch lt(x, 3)
|
||||||
|
case 0 {
|
||||||
|
v := h(x)
|
||||||
|
}
|
||||||
|
case 1 {
|
||||||
|
v := g(sub(x,f()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function h(x) -> v {
|
||||||
|
v := g(x)
|
||||||
|
}
|
||||||
|
function f() -> v {
|
||||||
|
let a1 := calldataload(mul(1,4))
|
||||||
|
let a2 := calldataload(mul(2,4))
|
||||||
|
let a3 := calldataload(mul(3,4))
|
||||||
|
let a4 := calldataload(mul(4,4))
|
||||||
|
let a5 := calldataload(mul(5,4))
|
||||||
|
let a6 := calldataload(mul(6,4))
|
||||||
|
let a7 := calldataload(mul(7,4))
|
||||||
|
let a8 := calldataload(mul(8,4))
|
||||||
|
let a9 := calldataload(mul(9,4))
|
||||||
|
a1 := calldataload(mul(0,4))
|
||||||
|
let a10 := calldataload(mul(10,4))
|
||||||
|
let a11 := calldataload(mul(11,4))
|
||||||
|
let a12 := calldataload(mul(12,4))
|
||||||
|
let a13 := calldataload(mul(13,4))
|
||||||
|
let a14 := calldataload(mul(14,4))
|
||||||
|
let a15 := calldataload(mul(15,4))
|
||||||
|
let a16 := calldataload(mul(16,4))
|
||||||
|
let a17 := calldataload(mul(17,4))
|
||||||
|
sstore(0, a1)
|
||||||
|
sstore(mul(17,4), a17)
|
||||||
|
sstore(mul(16,4), a16)
|
||||||
|
sstore(mul(15,4), a15)
|
||||||
|
sstore(mul(14,4), a14)
|
||||||
|
sstore(mul(13,4), a13)
|
||||||
|
sstore(mul(12,4), a12)
|
||||||
|
sstore(mul(11,4), a11)
|
||||||
|
sstore(mul(10,4), a10)
|
||||||
|
sstore(mul(9,4), a9)
|
||||||
|
sstore(mul(8,4), a8)
|
||||||
|
sstore(mul(7,4), a7)
|
||||||
|
sstore(mul(6,4), a6)
|
||||||
|
sstore(mul(5,4), a5)
|
||||||
|
sstore(mul(4,4), a4)
|
||||||
|
sstore(mul(3,4), a3)
|
||||||
|
sstore(mul(2,4), a2)
|
||||||
|
sstore(mul(1,4), a1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: stackLimitEvader
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// mstore(0x40, memoryguard(0xa0))
|
||||||
|
// sstore(0, g(sload(3)))
|
||||||
|
// function g(x) -> v
|
||||||
|
// {
|
||||||
|
// switch lt(x, 3)
|
||||||
|
// case 0 { v := h(x) }
|
||||||
|
// case 1 { v := g(sub(x, f())) }
|
||||||
|
// }
|
||||||
|
// function h(x_1) -> v_2
|
||||||
|
// { v_2 := g(x_1) }
|
||||||
|
// function f() -> v_3
|
||||||
|
// {
|
||||||
|
// mstore(0x80, calldataload(mul(1, 4)))
|
||||||
|
// let a2 := calldataload(mul(2, 4))
|
||||||
|
// let a3 := calldataload(mul(3, 4))
|
||||||
|
// let a4 := calldataload(mul(4, 4))
|
||||||
|
// let a5 := calldataload(mul(5, 4))
|
||||||
|
// let a6 := calldataload(mul(6, 4))
|
||||||
|
// let a7 := calldataload(mul(7, 4))
|
||||||
|
// let a8 := calldataload(mul(8, 4))
|
||||||
|
// let a9 := calldataload(mul(9, 4))
|
||||||
|
// mstore(0x80, calldataload(mul(0, 4)))
|
||||||
|
// let a10 := calldataload(mul(10, 4))
|
||||||
|
// let a11 := calldataload(mul(11, 4))
|
||||||
|
// let a12 := calldataload(mul(12, 4))
|
||||||
|
// let a13 := calldataload(mul(13, 4))
|
||||||
|
// let a14 := calldataload(mul(14, 4))
|
||||||
|
// let a15 := calldataload(mul(15, 4))
|
||||||
|
// let a16 := calldataload(mul(16, 4))
|
||||||
|
// let a17 := calldataload(mul(17, 4))
|
||||||
|
// sstore(0, mload(0x80))
|
||||||
|
// sstore(mul(17, 4), a17)
|
||||||
|
// sstore(mul(16, 4), a16)
|
||||||
|
// sstore(mul(15, 4), a15)
|
||||||
|
// sstore(mul(14, 4), a14)
|
||||||
|
// sstore(mul(13, 4), a13)
|
||||||
|
// sstore(mul(12, 4), a12)
|
||||||
|
// sstore(mul(11, 4), a11)
|
||||||
|
// sstore(mul(10, 4), a10)
|
||||||
|
// sstore(mul(9, 4), a9)
|
||||||
|
// sstore(mul(8, 4), a8)
|
||||||
|
// sstore(mul(7, 4), a7)
|
||||||
|
// sstore(mul(6, 4), a6)
|
||||||
|
// sstore(mul(5, 4), a5)
|
||||||
|
// sstore(mul(4, 4), a4)
|
||||||
|
// sstore(mul(3, 4), a3)
|
||||||
|
// sstore(mul(2, 4), a2)
|
||||||
|
// sstore(mul(1, 4), mload(0x80))
|
||||||
|
// }
|
||||||
|
// }
|
@ -0,0 +1,110 @@
|
|||||||
|
{
|
||||||
|
mstore(0x40, memoryguard(128))
|
||||||
|
sstore(0, g(sload(0)))
|
||||||
|
function g(x) -> v {
|
||||||
|
switch lt(x, 3)
|
||||||
|
case 0 {
|
||||||
|
v := f()
|
||||||
|
}
|
||||||
|
case 1 {
|
||||||
|
v := g(sub(x,1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function f() -> v {
|
||||||
|
let a1 := calldataload(mul(1,4))
|
||||||
|
let a2 := calldataload(mul(2,4))
|
||||||
|
let a3 := calldataload(mul(3,4))
|
||||||
|
let a4 := calldataload(mul(4,4))
|
||||||
|
let a5 := calldataload(mul(5,4))
|
||||||
|
let a6 := calldataload(mul(6,4))
|
||||||
|
let a7 := calldataload(mul(7,4))
|
||||||
|
let a8 := calldataload(mul(8,4))
|
||||||
|
let a9 := calldataload(mul(9,4))
|
||||||
|
a1 := calldataload(mul(0,4))
|
||||||
|
let a10 := calldataload(mul(10,4))
|
||||||
|
let a11 := calldataload(mul(11,4))
|
||||||
|
let a12 := calldataload(mul(12,4))
|
||||||
|
let a13 := calldataload(mul(13,4))
|
||||||
|
let a14 := calldataload(mul(14,4))
|
||||||
|
let a15 := calldataload(mul(15,4))
|
||||||
|
let a16 := calldataload(mul(16,4))
|
||||||
|
let a17 := calldataload(mul(17,4))
|
||||||
|
sstore(0, a1)
|
||||||
|
sstore(mul(17,4), a17)
|
||||||
|
sstore(mul(16,4), a16)
|
||||||
|
sstore(mul(15,4), a15)
|
||||||
|
sstore(mul(14,4), a14)
|
||||||
|
sstore(mul(13,4), a13)
|
||||||
|
sstore(mul(12,4), a12)
|
||||||
|
sstore(mul(11,4), a11)
|
||||||
|
sstore(mul(10,4), a10)
|
||||||
|
sstore(mul(9,4), a9)
|
||||||
|
sstore(mul(8,4), a8)
|
||||||
|
sstore(mul(7,4), a7)
|
||||||
|
sstore(mul(6,4), a6)
|
||||||
|
sstore(mul(5,4), a5)
|
||||||
|
sstore(mul(4,4), a4)
|
||||||
|
sstore(mul(3,4), a3)
|
||||||
|
sstore(mul(2,4), a2)
|
||||||
|
sstore(mul(1,4), a1)
|
||||||
|
sstore(23, h())
|
||||||
|
}
|
||||||
|
function h() -> v {
|
||||||
|
v := h()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: stackLimitEvader
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// mstore(0x40, memoryguard(0xa0))
|
||||||
|
// sstore(0, g(sload(0)))
|
||||||
|
// function g(x) -> v
|
||||||
|
// {
|
||||||
|
// switch lt(x, 3)
|
||||||
|
// case 0 { v := f() }
|
||||||
|
// case 1 { v := g(sub(x, 1)) }
|
||||||
|
// }
|
||||||
|
// function f() -> v_1
|
||||||
|
// {
|
||||||
|
// mstore(0x80, calldataload(mul(1, 4)))
|
||||||
|
// let a2 := calldataload(mul(2, 4))
|
||||||
|
// let a3 := calldataload(mul(3, 4))
|
||||||
|
// let a4 := calldataload(mul(4, 4))
|
||||||
|
// let a5 := calldataload(mul(5, 4))
|
||||||
|
// let a6 := calldataload(mul(6, 4))
|
||||||
|
// let a7 := calldataload(mul(7, 4))
|
||||||
|
// let a8 := calldataload(mul(8, 4))
|
||||||
|
// let a9 := calldataload(mul(9, 4))
|
||||||
|
// mstore(0x80, calldataload(mul(0, 4)))
|
||||||
|
// let a10 := calldataload(mul(10, 4))
|
||||||
|
// let a11 := calldataload(mul(11, 4))
|
||||||
|
// let a12 := calldataload(mul(12, 4))
|
||||||
|
// let a13 := calldataload(mul(13, 4))
|
||||||
|
// let a14 := calldataload(mul(14, 4))
|
||||||
|
// let a15 := calldataload(mul(15, 4))
|
||||||
|
// let a16 := calldataload(mul(16, 4))
|
||||||
|
// let a17 := calldataload(mul(17, 4))
|
||||||
|
// sstore(0, mload(0x80))
|
||||||
|
// sstore(mul(17, 4), a17)
|
||||||
|
// sstore(mul(16, 4), a16)
|
||||||
|
// sstore(mul(15, 4), a15)
|
||||||
|
// sstore(mul(14, 4), a14)
|
||||||
|
// sstore(mul(13, 4), a13)
|
||||||
|
// sstore(mul(12, 4), a12)
|
||||||
|
// sstore(mul(11, 4), a11)
|
||||||
|
// sstore(mul(10, 4), a10)
|
||||||
|
// sstore(mul(9, 4), a9)
|
||||||
|
// sstore(mul(8, 4), a8)
|
||||||
|
// sstore(mul(7, 4), a7)
|
||||||
|
// sstore(mul(6, 4), a6)
|
||||||
|
// sstore(mul(5, 4), a5)
|
||||||
|
// sstore(mul(4, 4), a4)
|
||||||
|
// sstore(mul(3, 4), a3)
|
||||||
|
// sstore(mul(2, 4), a2)
|
||||||
|
// sstore(mul(1, 4), mload(0x80))
|
||||||
|
// sstore(23, h())
|
||||||
|
// }
|
||||||
|
// function h() -> v_2
|
||||||
|
// { v_2 := h() }
|
||||||
|
// }
|
@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
{
|
||||||
|
mstore(0x40, memoryguard(128))
|
||||||
|
sstore(0, f(0))
|
||||||
|
}
|
||||||
|
function f(a1) -> v {
|
||||||
|
let a2 := calldataload(mul(2,4))
|
||||||
|
let a3 := calldataload(mul(3,4))
|
||||||
|
let a4 := calldataload(mul(4,4))
|
||||||
|
let a5 := calldataload(mul(5,4))
|
||||||
|
let a6 := calldataload(mul(6,4))
|
||||||
|
let a7 := calldataload(mul(7,4))
|
||||||
|
let a8 := calldataload(mul(8,4))
|
||||||
|
let a9 := calldataload(mul(9,4))
|
||||||
|
let a10 := calldataload(mul(10,4))
|
||||||
|
let a11 := calldataload(mul(11,4))
|
||||||
|
let a12 := calldataload(mul(12,4))
|
||||||
|
let a13 := calldataload(mul(13,4))
|
||||||
|
let a14 := calldataload(mul(14,4))
|
||||||
|
let a15 := calldataload(mul(15,4))
|
||||||
|
let a16 := calldataload(mul(16,4))
|
||||||
|
let a17 := calldataload(mul(17,4))
|
||||||
|
sstore(0, a1)
|
||||||
|
sstore(mul(17,4), a17)
|
||||||
|
sstore(mul(16,4), a16)
|
||||||
|
sstore(mul(15,4), a15)
|
||||||
|
sstore(mul(14,4), a14)
|
||||||
|
sstore(mul(13,4), a13)
|
||||||
|
sstore(mul(12,4), a12)
|
||||||
|
sstore(mul(11,4), a11)
|
||||||
|
sstore(mul(10,4), a10)
|
||||||
|
sstore(mul(9,4), a9)
|
||||||
|
sstore(mul(8,4), a8)
|
||||||
|
sstore(mul(7,4), a7)
|
||||||
|
sstore(mul(6,4), a6)
|
||||||
|
sstore(mul(5,4), a5)
|
||||||
|
sstore(mul(4,4), a4)
|
||||||
|
sstore(mul(3,4), a3)
|
||||||
|
sstore(mul(2,4), a2)
|
||||||
|
sstore(mul(1,4), a1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: stackLimitEvader
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// {
|
||||||
|
// mstore(0x40, memoryguard(0xa0))
|
||||||
|
// sstore(0, f(0))
|
||||||
|
// }
|
||||||
|
// function f(a1) -> v
|
||||||
|
// {
|
||||||
|
// let a2 := calldataload(mul(2, 4))
|
||||||
|
// let a3 := calldataload(mul(3, 4))
|
||||||
|
// let a4 := calldataload(mul(4, 4))
|
||||||
|
// let a5 := calldataload(mul(5, 4))
|
||||||
|
// let a6 := calldataload(mul(6, 4))
|
||||||
|
// let a7 := calldataload(mul(7, 4))
|
||||||
|
// let a8 := calldataload(mul(8, 4))
|
||||||
|
// let a9 := calldataload(mul(9, 4))
|
||||||
|
// let a10 := calldataload(mul(10, 4))
|
||||||
|
// let a11 := calldataload(mul(11, 4))
|
||||||
|
// let a12 := calldataload(mul(12, 4))
|
||||||
|
// let a13 := calldataload(mul(13, 4))
|
||||||
|
// let a14 := calldataload(mul(14, 4))
|
||||||
|
// let a15 := calldataload(mul(15, 4))
|
||||||
|
// let a16 := calldataload(mul(16, 4))
|
||||||
|
// let a17 := calldataload(mul(17, 4))
|
||||||
|
// sstore(0, a1)
|
||||||
|
// sstore(mul(17, 4), a17)
|
||||||
|
// sstore(mul(16, 4), a16)
|
||||||
|
// sstore(mul(15, 4), a15)
|
||||||
|
// sstore(mul(14, 4), a14)
|
||||||
|
// sstore(mul(13, 4), a13)
|
||||||
|
// sstore(mul(12, 4), a12)
|
||||||
|
// sstore(mul(11, 4), a11)
|
||||||
|
// sstore(mul(10, 4), a10)
|
||||||
|
// sstore(mul(9, 4), a9)
|
||||||
|
// sstore(mul(8, 4), a8)
|
||||||
|
// sstore(mul(7, 4), a7)
|
||||||
|
// sstore(mul(6, 4), a6)
|
||||||
|
// sstore(mul(5, 4), a5)
|
||||||
|
// sstore(mul(4, 4), a4)
|
||||||
|
// sstore(mul(3, 4), a3)
|
||||||
|
// sstore(mul(2, 4), a2)
|
||||||
|
// sstore(mul(1, 4), a1)
|
||||||
|
// }
|
||||||
|
// }
|
98
test/libyul/yulOptimizerTests/stackLimitEvader/stub.yul
Normal file
98
test/libyul/yulOptimizerTests/stackLimitEvader/stub.yul
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
{
|
||||||
|
mstore(0x40, memoryguard(128))
|
||||||
|
sstore(g(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16), 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 f() -> v{
|
||||||
|
let a1 := calldataload(mul(1,4))
|
||||||
|
let a2 := calldataload(mul(2,4))
|
||||||
|
let a3 := calldataload(mul(3,4))
|
||||||
|
let a4 := calldataload(mul(4,4))
|
||||||
|
let a5 := calldataload(mul(5,4))
|
||||||
|
let a6 := calldataload(mul(6,4))
|
||||||
|
let a7 := calldataload(mul(7,4))
|
||||||
|
let a8 := calldataload(mul(8,4))
|
||||||
|
let a9 := calldataload(mul(9,4))
|
||||||
|
a1 := calldataload(mul(0,4))
|
||||||
|
let a10 := calldataload(mul(10,4))
|
||||||
|
let a11 := calldataload(mul(11,4))
|
||||||
|
let a12 := calldataload(mul(12,4))
|
||||||
|
let a13 := calldataload(mul(13,4))
|
||||||
|
let a14 := calldataload(mul(14,4))
|
||||||
|
let a15 := calldataload(mul(15,4))
|
||||||
|
let a16 := calldataload(mul(16,4))
|
||||||
|
let a17 := calldataload(mul(17,4))
|
||||||
|
sstore(0, a1)
|
||||||
|
sstore(mul(17,4), a17)
|
||||||
|
sstore(mul(16,4), a16)
|
||||||
|
sstore(mul(15,4), a15)
|
||||||
|
sstore(mul(14,4), a14)
|
||||||
|
sstore(mul(13,4), a13)
|
||||||
|
sstore(mul(12,4), a12)
|
||||||
|
sstore(mul(11,4), a11)
|
||||||
|
sstore(mul(10,4), a10)
|
||||||
|
sstore(mul(9,4), a9)
|
||||||
|
sstore(mul(8,4), a8)
|
||||||
|
sstore(mul(7,4), a7)
|
||||||
|
sstore(mul(6,4), a6)
|
||||||
|
sstore(mul(5,4), a5)
|
||||||
|
sstore(mul(4,4), a4)
|
||||||
|
sstore(mul(3,4), a3)
|
||||||
|
sstore(mul(2,4), a2)
|
||||||
|
sstore(mul(1,4), a1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: stackLimitEvader
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// {
|
||||||
|
// mstore(0x40, memoryguard(0xa0))
|
||||||
|
// sstore(g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16), f())
|
||||||
|
// }
|
||||||
|
// function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16) -> v
|
||||||
|
// { v := b16 }
|
||||||
|
// function f() -> v_1
|
||||||
|
// {
|
||||||
|
// mstore(0x80, calldataload(mul(1, 4)))
|
||||||
|
// let a2 := calldataload(mul(2, 4))
|
||||||
|
// let a3 := calldataload(mul(3, 4))
|
||||||
|
// let a4 := calldataload(mul(4, 4))
|
||||||
|
// let a5 := calldataload(mul(5, 4))
|
||||||
|
// let a6 := calldataload(mul(6, 4))
|
||||||
|
// let a7 := calldataload(mul(7, 4))
|
||||||
|
// let a8 := calldataload(mul(8, 4))
|
||||||
|
// let a9 := calldataload(mul(9, 4))
|
||||||
|
// mstore(0x80, calldataload(mul(0, 4)))
|
||||||
|
// let a10 := calldataload(mul(10, 4))
|
||||||
|
// let a11 := calldataload(mul(11, 4))
|
||||||
|
// let a12 := calldataload(mul(12, 4))
|
||||||
|
// let a13 := calldataload(mul(13, 4))
|
||||||
|
// let a14 := calldataload(mul(14, 4))
|
||||||
|
// let a15 := calldataload(mul(15, 4))
|
||||||
|
// let a16 := calldataload(mul(16, 4))
|
||||||
|
// let a17 := calldataload(mul(17, 4))
|
||||||
|
// sstore(0, mload(0x80))
|
||||||
|
// sstore(mul(17, 4), a17)
|
||||||
|
// sstore(mul(16, 4), a16)
|
||||||
|
// sstore(mul(15, 4), a15)
|
||||||
|
// sstore(mul(14, 4), a14)
|
||||||
|
// sstore(mul(13, 4), a13)
|
||||||
|
// sstore(mul(12, 4), a12)
|
||||||
|
// sstore(mul(11, 4), a11)
|
||||||
|
// sstore(mul(10, 4), a10)
|
||||||
|
// sstore(mul(9, 4), a9)
|
||||||
|
// sstore(mul(8, 4), a8)
|
||||||
|
// sstore(mul(7, 4), a7)
|
||||||
|
// sstore(mul(6, 4), a6)
|
||||||
|
// sstore(mul(5, 4), a5)
|
||||||
|
// sstore(mul(4, 4), a4)
|
||||||
|
// sstore(mul(3, 4), a3)
|
||||||
|
// sstore(mul(2, 4), a2)
|
||||||
|
// sstore(mul(1, 4), mload(0x80))
|
||||||
|
// }
|
||||||
|
// }
|
339
test/libyul/yulOptimizerTests/stackLimitEvader/tree.yul
Normal file
339
test/libyul/yulOptimizerTests/stackLimitEvader/tree.yul
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
{
|
||||||
|
{
|
||||||
|
mstore(0x40, memoryguard(128))
|
||||||
|
sstore(23, f())
|
||||||
|
}
|
||||||
|
function f() -> v{
|
||||||
|
let a1 := calldataload(mul(1,4))
|
||||||
|
let a2 := calldataload(mul(2,4))
|
||||||
|
let a3 := calldataload(mul(3,4))
|
||||||
|
let a4 := calldataload(g())
|
||||||
|
let a5 := calldataload(mul(5,4))
|
||||||
|
let a6 := calldataload(mul(6,4))
|
||||||
|
let a7 := calldataload(mul(7,4))
|
||||||
|
let a8 := calldataload(mul(8,4))
|
||||||
|
let a9 := calldataload(mul(9,4))
|
||||||
|
a1 := calldataload(mul(0,4))
|
||||||
|
let a10 := calldataload(mul(10,4))
|
||||||
|
let a11 := calldataload(mul(11,4))
|
||||||
|
let a12 := calldataload(mul(12,4))
|
||||||
|
let a13 := calldataload(mul(13,4))
|
||||||
|
let a14 := calldataload(mul(14,4))
|
||||||
|
let a15 := calldataload(mul(15,4))
|
||||||
|
let a16 := calldataload(mul(16,4))
|
||||||
|
let a17 := calldataload(mul(17,4))
|
||||||
|
sstore(0, a1)
|
||||||
|
sstore(mul(17,4), a17)
|
||||||
|
sstore(mul(16,4), a16)
|
||||||
|
sstore(mul(15,4), a15)
|
||||||
|
sstore(mul(14,4), a14)
|
||||||
|
sstore(mul(13,4), a13)
|
||||||
|
sstore(mul(12,4), a12)
|
||||||
|
sstore(mul(11,4), a11)
|
||||||
|
sstore(mul(10,4), a10)
|
||||||
|
sstore(mul(9,4), a9)
|
||||||
|
sstore(mul(8,h()), a8)
|
||||||
|
sstore(mul(7,4), a7)
|
||||||
|
sstore(mul(6,4), a6)
|
||||||
|
sstore(mul(5,4), a5)
|
||||||
|
sstore(mul(4,4), a4)
|
||||||
|
sstore(mul(3,4), a3)
|
||||||
|
sstore(mul(2,4), a2)
|
||||||
|
sstore(mul(1,4), a1)
|
||||||
|
}
|
||||||
|
function g() -> v {
|
||||||
|
let a1 := calldataload(mul(1,4))
|
||||||
|
let a2 := calldataload(mul(2,4))
|
||||||
|
let a3 := calldataload(mul(3,4))
|
||||||
|
let a4 := calldataload(mul(4,4))
|
||||||
|
let a5 := calldataload(mul(5,4))
|
||||||
|
let a6 := calldataload(mul(6,4))
|
||||||
|
let a7 := calldataload(mul(7,4))
|
||||||
|
let a8 := calldataload(mul(8,4))
|
||||||
|
let a9 := calldataload(mul(9,4))
|
||||||
|
a1 := calldataload(mul(0,4))
|
||||||
|
let a10 := calldataload(mul(10,4))
|
||||||
|
let a11 := calldataload(mul(11,4))
|
||||||
|
let a12 := calldataload(mul(12,4))
|
||||||
|
let a13 := calldataload(mul(13,4))
|
||||||
|
let a14 := calldataload(mul(14,4))
|
||||||
|
let a15 := calldataload(mul(15,4))
|
||||||
|
let a16 := calldataload(mul(16,4))
|
||||||
|
let a17 := calldataload(mul(17,4))
|
||||||
|
sstore(0, a1)
|
||||||
|
sstore(mul(17,4), a17)
|
||||||
|
sstore(mul(16,4), a16)
|
||||||
|
sstore(mul(15,4), a15)
|
||||||
|
sstore(mul(14,4), a14)
|
||||||
|
sstore(mul(13,4), a13)
|
||||||
|
sstore(mul(12,4), a12)
|
||||||
|
sstore(mul(11,4), a11)
|
||||||
|
sstore(mul(10,4), a10)
|
||||||
|
sstore(mul(9,4), a9)
|
||||||
|
sstore(mul(8,4), a8)
|
||||||
|
sstore(mul(7,4), a7)
|
||||||
|
sstore(mul(6,4), a6)
|
||||||
|
sstore(mul(5,4), a5)
|
||||||
|
sstore(mul(4,4), a4)
|
||||||
|
sstore(mul(3,4), a3)
|
||||||
|
sstore(mul(2,4), a2)
|
||||||
|
sstore(mul(1,4), a1)
|
||||||
|
v := i()
|
||||||
|
}
|
||||||
|
function h() -> v {
|
||||||
|
let a1 := calldataload(mul(1,4))
|
||||||
|
let a2 := calldataload(mul(2,4))
|
||||||
|
let a3 := calldataload(mul(3,4))
|
||||||
|
let a4 := calldataload(mul(4,4))
|
||||||
|
let a5 := calldataload(mul(5,4))
|
||||||
|
let a6 := calldataload(mul(6,4))
|
||||||
|
let a7 := calldataload(mul(7,4))
|
||||||
|
let a8 := calldataload(mul(8,4))
|
||||||
|
let a9 := calldataload(mul(9,4))
|
||||||
|
let a10 := calldataload(mul(10,4))
|
||||||
|
let a11 := calldataload(mul(10,4))
|
||||||
|
a1 := calldataload(mul(0,4))
|
||||||
|
a2 := calldataload(mul(1,4))
|
||||||
|
let a12 := calldataload(mul(12,4))
|
||||||
|
let a13 := calldataload(mul(13,4))
|
||||||
|
let a14 := calldataload(mul(14,4))
|
||||||
|
let a15 := calldataload(mul(15,4))
|
||||||
|
let a16 := calldataload(mul(16,4))
|
||||||
|
let a17 := calldataload(mul(17,4))
|
||||||
|
let a18 := calldataload(mul(18,4))
|
||||||
|
let a19 := calldataload(mul(19,4))
|
||||||
|
sstore(0, add(a1, a2))
|
||||||
|
sstore(mul(17,4), a19)
|
||||||
|
sstore(mul(17,4), a18)
|
||||||
|
sstore(mul(17,4), a17)
|
||||||
|
sstore(mul(16,4), a16)
|
||||||
|
sstore(mul(15,4), a15)
|
||||||
|
sstore(mul(14,4), a14)
|
||||||
|
sstore(mul(13,4), a13)
|
||||||
|
sstore(mul(12,4), a12)
|
||||||
|
sstore(mul(11,4), a11)
|
||||||
|
sstore(mul(10,4), a10)
|
||||||
|
sstore(mul(9,4), a9)
|
||||||
|
sstore(mul(8,4), a8)
|
||||||
|
sstore(mul(7,4), a7)
|
||||||
|
sstore(mul(6,4), a6)
|
||||||
|
sstore(mul(5,4), a5)
|
||||||
|
sstore(mul(4,4), a4)
|
||||||
|
sstore(mul(3,4), a3)
|
||||||
|
sstore(mul(2,4), a2)
|
||||||
|
sstore(mul(1,4), a1)
|
||||||
|
v := i()
|
||||||
|
}
|
||||||
|
function i() -> v {
|
||||||
|
let a1 := calldataload(mul(1,4))
|
||||||
|
let a2 := calldataload(mul(2,4))
|
||||||
|
let a3 := calldataload(mul(3,4))
|
||||||
|
let a4 := calldataload(mul(4,4))
|
||||||
|
let a5 := calldataload(mul(5,4))
|
||||||
|
let a6 := calldataload(mul(6,4))
|
||||||
|
let a7 := calldataload(mul(7,4))
|
||||||
|
let a8 := calldataload(mul(8,4))
|
||||||
|
let a9 := calldataload(mul(9,4))
|
||||||
|
a1 := calldataload(mul(0,4))
|
||||||
|
let a10 := calldataload(mul(10,4))
|
||||||
|
let a11 := calldataload(mul(11,4))
|
||||||
|
let a12 := calldataload(mul(12,4))
|
||||||
|
let a13 := calldataload(mul(13,4))
|
||||||
|
let a14 := calldataload(mul(14,4))
|
||||||
|
let a15 := calldataload(mul(15,4))
|
||||||
|
let a16 := calldataload(mul(16,4))
|
||||||
|
let a17 := calldataload(mul(17,4))
|
||||||
|
sstore(0, a1)
|
||||||
|
sstore(mul(17,4), a17)
|
||||||
|
sstore(mul(16,4), a16)
|
||||||
|
sstore(mul(15,4), a15)
|
||||||
|
sstore(mul(14,4), a14)
|
||||||
|
sstore(mul(13,4), a13)
|
||||||
|
sstore(mul(12,4), a12)
|
||||||
|
sstore(mul(11,4), a11)
|
||||||
|
sstore(mul(10,4), a10)
|
||||||
|
sstore(mul(9,4), a9)
|
||||||
|
sstore(mul(8,4), a8)
|
||||||
|
sstore(mul(7,4), a7)
|
||||||
|
sstore(mul(6,4), a6)
|
||||||
|
sstore(mul(5,4), a5)
|
||||||
|
sstore(mul(4,4), a4)
|
||||||
|
sstore(mul(3,4), a3)
|
||||||
|
sstore(mul(2,4), a2)
|
||||||
|
sstore(mul(1,4), a1)
|
||||||
|
v := sload(mul(42,8))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: stackLimitEvader
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// {
|
||||||
|
// mstore(0x40, memoryguard(0x0100))
|
||||||
|
// sstore(23, f())
|
||||||
|
// }
|
||||||
|
// function f() -> v
|
||||||
|
// {
|
||||||
|
// mstore(0xe0, calldataload(mul(1, 4)))
|
||||||
|
// let a2 := calldataload(mul(2, 4))
|
||||||
|
// let a3 := calldataload(mul(3, 4))
|
||||||
|
// let a4 := calldataload(g())
|
||||||
|
// let a5 := calldataload(mul(5, 4))
|
||||||
|
// let a6 := calldataload(mul(6, 4))
|
||||||
|
// let a7 := calldataload(mul(7, 4))
|
||||||
|
// let a8 := calldataload(mul(8, 4))
|
||||||
|
// let a9 := calldataload(mul(9, 4))
|
||||||
|
// mstore(0xe0, calldataload(mul(0, 4)))
|
||||||
|
// let a10 := calldataload(mul(10, 4))
|
||||||
|
// let a11 := calldataload(mul(11, 4))
|
||||||
|
// let a12 := calldataload(mul(12, 4))
|
||||||
|
// let a13 := calldataload(mul(13, 4))
|
||||||
|
// let a14 := calldataload(mul(14, 4))
|
||||||
|
// let a15 := calldataload(mul(15, 4))
|
||||||
|
// let a16 := calldataload(mul(16, 4))
|
||||||
|
// let a17 := calldataload(mul(17, 4))
|
||||||
|
// sstore(0, mload(0xe0))
|
||||||
|
// sstore(mul(17, 4), a17)
|
||||||
|
// sstore(mul(16, 4), a16)
|
||||||
|
// sstore(mul(15, 4), a15)
|
||||||
|
// sstore(mul(14, 4), a14)
|
||||||
|
// sstore(mul(13, 4), a13)
|
||||||
|
// sstore(mul(12, 4), a12)
|
||||||
|
// sstore(mul(11, 4), a11)
|
||||||
|
// sstore(mul(10, 4), a10)
|
||||||
|
// sstore(mul(9, 4), a9)
|
||||||
|
// sstore(mul(8, h()), a8)
|
||||||
|
// sstore(mul(7, 4), a7)
|
||||||
|
// sstore(mul(6, 4), a6)
|
||||||
|
// sstore(mul(5, 4), a5)
|
||||||
|
// sstore(mul(4, 4), a4)
|
||||||
|
// sstore(mul(3, 4), a3)
|
||||||
|
// sstore(mul(2, 4), a2)
|
||||||
|
// sstore(mul(1, 4), mload(0xe0))
|
||||||
|
// }
|
||||||
|
// function g() -> v_1
|
||||||
|
// {
|
||||||
|
// mstore(0xa0, calldataload(mul(1, 4)))
|
||||||
|
// let a2_3 := calldataload(mul(2, 4))
|
||||||
|
// let a3_4 := calldataload(mul(3, 4))
|
||||||
|
// let a4_5 := calldataload(mul(4, 4))
|
||||||
|
// let a5_6 := calldataload(mul(5, 4))
|
||||||
|
// let a6_7 := calldataload(mul(6, 4))
|
||||||
|
// let a7_8 := calldataload(mul(7, 4))
|
||||||
|
// let a8_9 := calldataload(mul(8, 4))
|
||||||
|
// let a9_10 := calldataload(mul(9, 4))
|
||||||
|
// mstore(0xa0, calldataload(mul(0, 4)))
|
||||||
|
// let a10_11 := calldataload(mul(10, 4))
|
||||||
|
// let a11_12 := calldataload(mul(11, 4))
|
||||||
|
// let a12_13 := calldataload(mul(12, 4))
|
||||||
|
// let a13_14 := calldataload(mul(13, 4))
|
||||||
|
// let a14_15 := calldataload(mul(14, 4))
|
||||||
|
// let a15_16 := calldataload(mul(15, 4))
|
||||||
|
// let a16_17 := calldataload(mul(16, 4))
|
||||||
|
// let a17_18 := calldataload(mul(17, 4))
|
||||||
|
// sstore(0, mload(0xa0))
|
||||||
|
// sstore(mul(17, 4), a17_18)
|
||||||
|
// sstore(mul(16, 4), a16_17)
|
||||||
|
// sstore(mul(15, 4), a15_16)
|
||||||
|
// sstore(mul(14, 4), a14_15)
|
||||||
|
// sstore(mul(13, 4), a13_14)
|
||||||
|
// sstore(mul(12, 4), a12_13)
|
||||||
|
// sstore(mul(11, 4), a11_12)
|
||||||
|
// sstore(mul(10, 4), a10_11)
|
||||||
|
// sstore(mul(9, 4), a9_10)
|
||||||
|
// sstore(mul(8, 4), a8_9)
|
||||||
|
// sstore(mul(7, 4), a7_8)
|
||||||
|
// sstore(mul(6, 4), a6_7)
|
||||||
|
// sstore(mul(5, 4), a5_6)
|
||||||
|
// sstore(mul(4, 4), a4_5)
|
||||||
|
// sstore(mul(3, 4), a3_4)
|
||||||
|
// sstore(mul(2, 4), a2_3)
|
||||||
|
// sstore(mul(1, 4), mload(0xa0))
|
||||||
|
// v_1 := i()
|
||||||
|
// }
|
||||||
|
// function h() -> v_19
|
||||||
|
// {
|
||||||
|
// mstore(0xc0, calldataload(mul(1, 4)))
|
||||||
|
// mstore(0xa0, calldataload(mul(2, 4)))
|
||||||
|
// let a3_22 := calldataload(mul(3, 4))
|
||||||
|
// let a4_23 := calldataload(mul(4, 4))
|
||||||
|
// let a5_24 := calldataload(mul(5, 4))
|
||||||
|
// let a6_25 := calldataload(mul(6, 4))
|
||||||
|
// let a7_26 := calldataload(mul(7, 4))
|
||||||
|
// let a8_27 := calldataload(mul(8, 4))
|
||||||
|
// let a9_28 := calldataload(mul(9, 4))
|
||||||
|
// let a10_29 := calldataload(mul(10, 4))
|
||||||
|
// let a11_30 := calldataload(mul(10, 4))
|
||||||
|
// mstore(0xc0, calldataload(mul(0, 4)))
|
||||||
|
// mstore(0xa0, calldataload(mul(1, 4)))
|
||||||
|
// let a12_31 := calldataload(mul(12, 4))
|
||||||
|
// let a13_32 := calldataload(mul(13, 4))
|
||||||
|
// let a14_33 := calldataload(mul(14, 4))
|
||||||
|
// let a15_34 := calldataload(mul(15, 4))
|
||||||
|
// let a16_35 := calldataload(mul(16, 4))
|
||||||
|
// let a17_36 := calldataload(mul(17, 4))
|
||||||
|
// let a18 := calldataload(mul(18, 4))
|
||||||
|
// let a19 := calldataload(mul(19, 4))
|
||||||
|
// sstore(0, add(mload(0xc0), mload(0xa0)))
|
||||||
|
// sstore(mul(17, 4), a19)
|
||||||
|
// sstore(mul(17, 4), a18)
|
||||||
|
// sstore(mul(17, 4), a17_36)
|
||||||
|
// sstore(mul(16, 4), a16_35)
|
||||||
|
// sstore(mul(15, 4), a15_34)
|
||||||
|
// sstore(mul(14, 4), a14_33)
|
||||||
|
// sstore(mul(13, 4), a13_32)
|
||||||
|
// sstore(mul(12, 4), a12_31)
|
||||||
|
// sstore(mul(11, 4), a11_30)
|
||||||
|
// sstore(mul(10, 4), a10_29)
|
||||||
|
// sstore(mul(9, 4), a9_28)
|
||||||
|
// sstore(mul(8, 4), a8_27)
|
||||||
|
// sstore(mul(7, 4), a7_26)
|
||||||
|
// sstore(mul(6, 4), a6_25)
|
||||||
|
// sstore(mul(5, 4), a5_24)
|
||||||
|
// sstore(mul(4, 4), a4_23)
|
||||||
|
// sstore(mul(3, 4), a3_22)
|
||||||
|
// sstore(mul(2, 4), mload(0xa0))
|
||||||
|
// sstore(mul(1, 4), mload(0xc0))
|
||||||
|
// v_19 := i()
|
||||||
|
// }
|
||||||
|
// function i() -> v_37
|
||||||
|
// {
|
||||||
|
// mstore(0x80, calldataload(mul(1, 4)))
|
||||||
|
// let a2_39 := calldataload(mul(2, 4))
|
||||||
|
// let a3_40 := calldataload(mul(3, 4))
|
||||||
|
// let a4_41 := calldataload(mul(4, 4))
|
||||||
|
// let a5_42 := calldataload(mul(5, 4))
|
||||||
|
// let a6_43 := calldataload(mul(6, 4))
|
||||||
|
// let a7_44 := calldataload(mul(7, 4))
|
||||||
|
// let a8_45 := calldataload(mul(8, 4))
|
||||||
|
// let a9_46 := calldataload(mul(9, 4))
|
||||||
|
// mstore(0x80, calldataload(mul(0, 4)))
|
||||||
|
// let a10_47 := calldataload(mul(10, 4))
|
||||||
|
// let a11_48 := calldataload(mul(11, 4))
|
||||||
|
// let a12_49 := calldataload(mul(12, 4))
|
||||||
|
// let a13_50 := calldataload(mul(13, 4))
|
||||||
|
// let a14_51 := calldataload(mul(14, 4))
|
||||||
|
// let a15_52 := calldataload(mul(15, 4))
|
||||||
|
// let a16_53 := calldataload(mul(16, 4))
|
||||||
|
// let a17_54 := calldataload(mul(17, 4))
|
||||||
|
// sstore(0, mload(0x80))
|
||||||
|
// sstore(mul(17, 4), a17_54)
|
||||||
|
// sstore(mul(16, 4), a16_53)
|
||||||
|
// sstore(mul(15, 4), a15_52)
|
||||||
|
// sstore(mul(14, 4), a14_51)
|
||||||
|
// sstore(mul(13, 4), a13_50)
|
||||||
|
// sstore(mul(12, 4), a12_49)
|
||||||
|
// sstore(mul(11, 4), a11_48)
|
||||||
|
// sstore(mul(10, 4), a10_47)
|
||||||
|
// sstore(mul(9, 4), a9_46)
|
||||||
|
// sstore(mul(8, 4), a8_45)
|
||||||
|
// sstore(mul(7, 4), a7_44)
|
||||||
|
// sstore(mul(6, 4), a6_43)
|
||||||
|
// sstore(mul(5, 4), a5_42)
|
||||||
|
// sstore(mul(4, 4), a4_41)
|
||||||
|
// sstore(mul(3, 4), a3_40)
|
||||||
|
// sstore(mul(2, 4), a2_39)
|
||||||
|
// sstore(mul(1, 4), mload(0x80))
|
||||||
|
// v_37 := sload(mul(42, 8))
|
||||||
|
// }
|
||||||
|
// }
|
Loading…
Reference in New Issue
Block a user