mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Compatibility with StackCompressor and StackLimitEvader.
This commit is contained in:
parent
b8bf8da514
commit
bdddb845b0
@ -1033,7 +1033,7 @@ string IRGenerator::memoryInit(bool _useMemoryGuard)
|
|||||||
{
|
{
|
||||||
// TODO: Remove once we have made sure it is safe, i.e. after "Yul memory objects lite".
|
// TODO: Remove once we have made sure it is safe, i.e. after "Yul memory objects lite".
|
||||||
// Also restore the tests removed in the commit that adds this comment.
|
// Also restore the tests removed in the commit that adds this comment.
|
||||||
_useMemoryGuard = false;
|
// _useMemoryGuard = false;
|
||||||
// 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).
|
||||||
|
@ -27,6 +27,13 @@
|
|||||||
#include <libyul/optimiser/Metrics.h>
|
#include <libyul/optimiser/Metrics.h>
|
||||||
#include <libyul/optimiser/Semantics.h>
|
#include <libyul/optimiser/Semantics.h>
|
||||||
|
|
||||||
|
#include <libyul/backends/evm/ControlFlowGraphBuilder.h>
|
||||||
|
#include <libyul/backends/evm/StackHelpers.h>
|
||||||
|
#include <libyul/backends/evm/StackLayoutGenerator.h>
|
||||||
|
|
||||||
|
#include <libyul/AsmAnalysis.h>
|
||||||
|
#include <libyul/AsmAnalysisInfo.h>
|
||||||
|
|
||||||
#include <libyul/CompilabilityChecker.h>
|
#include <libyul/CompilabilityChecker.h>
|
||||||
|
|
||||||
#include <libyul/AST.h>
|
#include <libyul/AST.h>
|
||||||
@ -150,6 +157,40 @@ void eliminateVariables(
|
|||||||
UnusedPruner::runUntilStabilised(_dialect, _node, _allowMSizeOptimization);
|
UnusedPruner::runUntilStabilised(_dialect, _node, _allowMSizeOptimization);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void eliminateVariables(
|
||||||
|
Dialect const& _dialect,
|
||||||
|
Block& _block,
|
||||||
|
vector<StackLayoutGenerator::StackTooDeep> const& _unreachables,
|
||||||
|
bool _allowMSizeOptimization
|
||||||
|
)
|
||||||
|
{
|
||||||
|
RematCandidateSelector selector{_dialect};
|
||||||
|
selector(_block);
|
||||||
|
std::map<YulString, size_t> candidates;
|
||||||
|
for (auto [cost, name, dependencies]: selector.candidates())
|
||||||
|
candidates[name] = cost;
|
||||||
|
|
||||||
|
set<YulString> varsToEliminate;
|
||||||
|
|
||||||
|
for (auto const& unreachable: _unreachables)
|
||||||
|
{
|
||||||
|
set<tuple<size_t, YulString>> suitableCandidates;
|
||||||
|
size_t neededSlots = unreachable.deficit;
|
||||||
|
for (auto varName: unreachable.variableChoices)
|
||||||
|
{
|
||||||
|
if (varsToEliminate.count(varName))
|
||||||
|
--neededSlots;
|
||||||
|
else if (size_t* cost = util::valueOrNullptr(candidates, varName))
|
||||||
|
suitableCandidates.insert(std::make_tuple(*cost, varName));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto candidate: suitableCandidates | ranges::views::take(neededSlots))
|
||||||
|
varsToEliminate.emplace(get<1>(candidate));
|
||||||
|
}
|
||||||
|
Rematerialiser::run(_dialect, _block, std::move(varsToEliminate), true);
|
||||||
|
UnusedPruner::runUntilStabilised(_dialect, _block, _allowMSizeOptimization);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StackCompressor::run(
|
bool StackCompressor::run(
|
||||||
@ -164,7 +205,31 @@ bool StackCompressor::run(
|
|||||||
_object.code->statements.size() > 0 && holds_alternative<Block>(_object.code->statements.at(0)),
|
_object.code->statements.size() > 0 && holds_alternative<Block>(_object.code->statements.at(0)),
|
||||||
"Need to run the function grouper before the stack compressor."
|
"Need to run the function grouper before the stack compressor."
|
||||||
);
|
);
|
||||||
|
bool usesOptimizedCodeGenerator = false;
|
||||||
|
if (auto evmDialect = dynamic_cast<EVMDialect const*>(&_dialect))
|
||||||
|
usesOptimizedCodeGenerator = _optimizeStackAllocation && evmDialect->evmVersion() > langutil::EVMVersion::homestead();
|
||||||
bool allowMSizeOptimzation = !MSizeFinder::containsMSize(_dialect, *_object.code);
|
bool allowMSizeOptimzation = !MSizeFinder::containsMSize(_dialect, *_object.code);
|
||||||
|
if (usesOptimizedCodeGenerator)
|
||||||
|
{
|
||||||
|
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, _object);
|
||||||
|
unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(analysisInfo, _dialect, *_object.code);
|
||||||
|
Block& mainBlock = std::get<Block>(_object.code->statements.at(0));
|
||||||
|
if (
|
||||||
|
auto stackTooDeepErrors = StackLayoutGenerator::reportStackTooDeep(*cfg, YulString{});
|
||||||
|
!stackTooDeepErrors.empty()
|
||||||
|
)
|
||||||
|
eliminateVariables(_dialect, mainBlock, stackTooDeepErrors, allowMSizeOptimzation);
|
||||||
|
for (size_t i = 1; i < _object.code->statements.size(); ++i)
|
||||||
|
{
|
||||||
|
auto& fun = std::get<FunctionDefinition>(_object.code->statements[i]);
|
||||||
|
if (
|
||||||
|
auto stackTooDeepErrors = StackLayoutGenerator::reportStackTooDeep(*cfg, fun.name);
|
||||||
|
!stackTooDeepErrors.empty()
|
||||||
|
)
|
||||||
|
eliminateVariables(_dialect, fun.body, stackTooDeepErrors, allowMSizeOptimzation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
for (size_t iterations = 0; iterations < _maxIterations; iterations++)
|
for (size_t iterations = 0; iterations < _maxIterations; iterations++)
|
||||||
{
|
{
|
||||||
map<YulString, int> stackSurplus = CompilabilityChecker(_dialect, _object, _optimizeStackAllocation).stackDeficit;
|
map<YulString, int> stackSurplus = CompilabilityChecker(_dialect, _object, _optimizeStackAllocation).stackDeficit;
|
||||||
|
@ -21,14 +21,18 @@
|
|||||||
#include <libyul/optimiser/FunctionDefinitionCollector.h>
|
#include <libyul/optimiser/FunctionDefinitionCollector.h>
|
||||||
#include <libyul/optimiser/NameDispenser.h>
|
#include <libyul/optimiser/NameDispenser.h>
|
||||||
#include <libyul/optimiser/StackToMemoryMover.h>
|
#include <libyul/optimiser/StackToMemoryMover.h>
|
||||||
|
#include <libyul/backends/evm/ControlFlowGraphBuilder.h>
|
||||||
#include <libyul/backends/evm/EVMDialect.h>
|
#include <libyul/backends/evm/EVMDialect.h>
|
||||||
|
#include <libyul/AsmAnalysis.h>
|
||||||
#include <libyul/AST.h>
|
#include <libyul/AST.h>
|
||||||
|
#include <libyul/CompilabilityChecker.h>
|
||||||
#include <libyul/Exceptions.h>
|
#include <libyul/Exceptions.h>
|
||||||
#include <libyul/Object.h>
|
#include <libyul/Object.h>
|
||||||
#include <libyul/Utilities.h>
|
#include <libyul/Utilities.h>
|
||||||
#include <libsolutil/Algorithms.h>
|
#include <libsolutil/Algorithms.h>
|
||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
|
#include <range/v3/range/conversion.hpp>
|
||||||
#include <range/v3/view/concat.hpp>
|
#include <range/v3/view/concat.hpp>
|
||||||
#include <range/v3/view/take.hpp>
|
#include <range/v3/view/take.hpp>
|
||||||
|
|
||||||
@ -114,6 +118,45 @@ u256 literalArgumentValue(FunctionCall const& _call)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StackLimitEvader::run(
|
||||||
|
OptimiserStepContext& _context,
|
||||||
|
Object& _object
|
||||||
|
)
|
||||||
|
{
|
||||||
|
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."
|
||||||
|
);
|
||||||
|
if (evmDialect && evmDialect->evmVersion() > langutil::EVMVersion::homestead())
|
||||||
|
{
|
||||||
|
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(*evmDialect, _object);
|
||||||
|
unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(analysisInfo, *evmDialect, *_object.code);
|
||||||
|
run(_context, _object, StackLayoutGenerator::reportStackTooDeep(*cfg));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
run(_context, _object, CompilabilityChecker{
|
||||||
|
_context.dialect,
|
||||||
|
_object,
|
||||||
|
true
|
||||||
|
}.unreachableVariables);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackLimitEvader::run(
|
||||||
|
OptimiserStepContext& _context,
|
||||||
|
Object& _object,
|
||||||
|
map<YulString, vector<StackLayoutGenerator::StackTooDeep>> const& _stackTooDeepErrors
|
||||||
|
)
|
||||||
|
{
|
||||||
|
map<YulString, set<YulString>> unreachableVariables;
|
||||||
|
for (auto&& [function, stackTooDeepErrors]: _stackTooDeepErrors)
|
||||||
|
// TODO: choose wisely.
|
||||||
|
for (auto const& stackTooDeepError: stackTooDeepErrors)
|
||||||
|
unreachableVariables[function] += stackTooDeepError.variableChoices | ranges::views::take(stackTooDeepError.deficit) | ranges::to<set<YulString>>;
|
||||||
|
run(_context, _object, unreachableVariables);
|
||||||
|
}
|
||||||
|
|
||||||
void StackLimitEvader::run(
|
void StackLimitEvader::run(
|
||||||
OptimiserStepContext& _context,
|
OptimiserStepContext& _context,
|
||||||
Object& _object,
|
Object& _object,
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libyul/optimiser/OptimiserStep.h>
|
#include <libyul/optimiser/OptimiserStep.h>
|
||||||
|
#include <libyul/backends/evm/StackLayoutGenerator.h>
|
||||||
|
|
||||||
namespace solidity::yul
|
namespace solidity::yul
|
||||||
{
|
{
|
||||||
@ -61,6 +62,25 @@ public:
|
|||||||
Object& _object,
|
Object& _object,
|
||||||
std::map<YulString, std::set<YulString>> const& _unreachableVariables
|
std::map<YulString, std::set<YulString>> const& _unreachableVariables
|
||||||
);
|
);
|
||||||
|
/// @a _stackTooDeepErrors can be determined by the StackLayoutGenerator.
|
||||||
|
/// 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 _stackTooDeepErrors
|
||||||
|
/// are contained in a recursive function.
|
||||||
|
static void run(
|
||||||
|
OptimiserStepContext& _context,
|
||||||
|
Object& _object,
|
||||||
|
std::map<YulString, std::vector<StackLayoutGenerator::StackTooDeep>> const& _stackTooDeepErrors
|
||||||
|
);
|
||||||
|
/// Determines stack too deep errors using the appropriate code generation backend.
|
||||||
|
/// 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 unreachable variables
|
||||||
|
/// are contained in a recursive function.
|
||||||
|
static void run(
|
||||||
|
OptimiserStepContext& _context,
|
||||||
|
Object& _object
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,11 @@ void OptimiserSuite::run(
|
|||||||
set<YulString> const& _externallyUsedIdentifiers
|
set<YulString> const& _externallyUsedIdentifiers
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&_dialect);
|
||||||
|
bool usesOptimizedCodeGenerator =
|
||||||
|
_optimizeStackAllocation &&
|
||||||
|
evmDialect &&
|
||||||
|
evmDialect->evmVersion() > langutil::EVMVersion::homestead();
|
||||||
set<YulString> reservedIdentifiers = _externallyUsedIdentifiers;
|
set<YulString> reservedIdentifiers = _externallyUsedIdentifiers;
|
||||||
reservedIdentifiers += _dialect.fixedFunctionNames();
|
reservedIdentifiers += _dialect.fixedFunctionNames();
|
||||||
|
|
||||||
@ -118,6 +123,7 @@ void OptimiserSuite::run(
|
|||||||
|
|
||||||
// We ignore the return value because we will get a much better error
|
// We ignore the return value because we will get a much better error
|
||||||
// message once we perform code generation.
|
// message once we perform code generation.
|
||||||
|
if (!usesOptimizedCodeGenerator)
|
||||||
StackCompressor::run(
|
StackCompressor::run(
|
||||||
_dialect,
|
_dialect,
|
||||||
_object,
|
_object,
|
||||||
@ -126,16 +132,23 @@ void OptimiserSuite::run(
|
|||||||
);
|
);
|
||||||
suite.runSequence("fDnTOc g", ast);
|
suite.runSequence("fDnTOc g", ast);
|
||||||
|
|
||||||
if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&_dialect))
|
if (evmDialect)
|
||||||
{
|
{
|
||||||
yulAssert(_meter, "");
|
yulAssert(_meter, "");
|
||||||
ConstantOptimiser{*dialect, *_meter}(ast);
|
ConstantOptimiser{*evmDialect, *_meter}(ast);
|
||||||
if (dialect->providesObjectAccess() && _optimizeStackAllocation)
|
if (usesOptimizedCodeGenerator)
|
||||||
StackLimitEvader::run(suite.m_context, _object, CompilabilityChecker{
|
{
|
||||||
|
StackCompressor::run(
|
||||||
_dialect,
|
_dialect,
|
||||||
_object,
|
_object,
|
||||||
_optimizeStackAllocation
|
_optimizeStackAllocation,
|
||||||
}.unreachableVariables);
|
stackCompressorMaxIterations
|
||||||
|
);
|
||||||
|
if (evmDialect->providesObjectAccess())
|
||||||
|
StackLimitEvader::run(suite.m_context, _object);
|
||||||
|
}
|
||||||
|
else if (evmDialect->providesObjectAccess() && _optimizeStackAllocation)
|
||||||
|
StackLimitEvader::run(suite.m_context, _object);
|
||||||
}
|
}
|
||||||
else if (dynamic_cast<WasmDialect const*>(&_dialect))
|
else if (dynamic_cast<WasmDialect const*>(&_dialect))
|
||||||
{
|
{
|
||||||
|
@ -29,6 +29,25 @@ using namespace std;
|
|||||||
namespace solidity::yul::test
|
namespace solidity::yul::test
|
||||||
{
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool triggersStackTooDeep(Stack _source, Stack const& _target)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
createStackLayout(_source, _target, [&](unsigned _i) {
|
||||||
|
if (_i > 16)
|
||||||
|
result = true;
|
||||||
|
}, [&](StackSlot const& _slot) {
|
||||||
|
if (canBeFreelyGenerated(_slot))
|
||||||
|
return;
|
||||||
|
if (auto depth = util::findOffset(_source | ranges::views::reverse, _slot); depth && *depth >= 16)
|
||||||
|
if (*depth >= 16)
|
||||||
|
result = true;
|
||||||
|
}, [&]() {});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE(StackHelpers)
|
BOOST_AUTO_TEST_SUITE(StackHelpers)
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(avoid_deep_dup)
|
BOOST_AUTO_TEST_CASE(avoid_deep_dup)
|
||||||
@ -74,8 +93,7 @@ BOOST_AUTO_TEST_CASE(avoid_deep_dup)
|
|||||||
VariableSlot{variableContainer[14]}, // While "optimal", bringing this slot up first will make the next unreachable.
|
VariableSlot{variableContainer[14]}, // While "optimal", bringing this slot up first will make the next unreachable.
|
||||||
VariableSlot{variableContainer[0]}
|
VariableSlot{variableContainer[0]}
|
||||||
}};
|
}};
|
||||||
auto unreachable = OptimizedEVMCodeTransform::tryCreateStackLayout(from, to, {});
|
BOOST_CHECK(!triggersStackTooDeep(from, to));
|
||||||
BOOST_CHECK(unreachable.empty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
Loading…
Reference in New Issue
Block a user