Move function arguments and return values.

This commit is contained in:
Daniel Kirchner
2021-06-28 18:11:57 +02:00
committed by hrkrshnn
parent cc0169ad68
commit dfce7b667d
20 changed files with 1037 additions and 85 deletions
+35 -10
View File
@@ -18,6 +18,7 @@
#include <libyul/optimiser/StackLimitEvader.h>
#include <libyul/optimiser/CallGraphGenerator.h>
#include <libyul/optimiser/FunctionCallFinder.h>
#include <libyul/optimiser/FunctionDefinitionCollector.h>
#include <libyul/optimiser/NameDispenser.h>
#include <libyul/optimiser/StackToMemoryMover.h>
#include <libyul/backends/evm/EVMDialect.h>
@@ -28,6 +29,9 @@
#include <libsolutil/Algorithms.h>
#include <libsolutil/CommonData.h>
#include <range/v3/view/concat.hpp>
#include <range/v3/view/take.hpp>
using namespace std;
using namespace solidity;
using namespace solidity::yul;
@@ -62,25 +66,42 @@ struct MemoryOffsetAllocator
for (YulString child: callGraph.at(_function))
requiredSlots = std::max(run(child), requiredSlots);
if (unreachableVariables.count(_function))
if (auto const* unreachables = util::valueOrNullptr(unreachableVariables, _function))
{
yulAssert(!slotAllocations.count(_function), "");
for (YulString variable: unreachableVariables.at(_function))
if (variable.empty())
{
// TODO: Too many function arguments or return parameters.
}
else
if (FunctionDefinition const* functionDefinition = util::valueOrDefault(functionDefinitions, _function, nullptr, util::allow_copy))
if (
size_t totalArgCount = functionDefinition->returnVariables.size() + functionDefinition->parameters.size();
totalArgCount > 16
)
for (TypedName const& var: ranges::concat_view(
functionDefinition->parameters,
functionDefinition->returnVariables
) | ranges::views::take(totalArgCount - 16))
slotAllocations[var.name] = requiredSlots++;
// Assign slots for all variables that become unreachable in the function body, if the above did not
// assign a slot for them already.
for (YulString variable: *unreachables)
// The empty case is a function with too many arguments or return values,
// which was already handled above.
if (!variable.empty() && !slotAllocations.count(variable))
slotAllocations[variable] = requiredSlots++;
}
return slotsRequiredForFunction[_function] = requiredSlots;
}
/// Maps function names to the set of unreachable variables in that function.
/// An empty variable name means that the function has too many arguments or return variables.
map<YulString, set<YulString>> const& unreachableVariables;
/// The graph of immediate function calls of all functions.
map<YulString, set<YulString>> const& callGraph;
/// Maps the name of each user-defined function to its definition.
map<YulString, FunctionDefinition const*> const& functionDefinitions;
/// Maps variable names to the memory slot the respective variable is assigned.
map<YulString, uint64_t> slotAllocations{};
/// Maps function names to the number of memory slots the respective function requires.
map<YulString, uint64_t> slotsRequiredForFunction{};
};
@@ -116,6 +137,8 @@ void StackLimitEvader::run(
// Make sure all calls to ``memoryguard`` we found have the same value as argument (otherwise, abort).
u256 reservedMemory = literalArgumentValue(*memoryGuardCalls.front());
yulAssert(reservedMemory < u256(1) << 32 - 1, "");
for (FunctionCall const* memoryGuardCall: memoryGuardCalls)
if (reservedMemory != literalArgumentValue(*memoryGuardCall))
return;
@@ -127,12 +150,14 @@ void StackLimitEvader::run(
if (_unreachableVariables.count(function))
return;
MemoryOffsetAllocator memoryOffsetAllocator{_unreachableVariables, callGraph.functionCalls};
map<YulString, FunctionDefinition const*> functionDefinitions = FunctionDefinitionCollector::run(*_object.code);
MemoryOffsetAllocator memoryOffsetAllocator{_unreachableVariables, callGraph.functionCalls, functionDefinitions};
uint64_t requiredSlots = memoryOffsetAllocator.run();
yulAssert(requiredSlots < (uint64_t(1) << 32) - 1, "");
StackToMemoryMover::run(_context, reservedMemory, memoryOffsetAllocator.slotAllocations, requiredSlots, *_object.code);
yulAssert(requiredSlots < std::numeric_limits<uint64_t>::max() / 32, "");
reservedMemory += 32 * requiredSlots;
for (FunctionCall* memoryGuardCall: FunctionCallFinder::run(*_object.code, "memoryguard"_yulstring))
{