Deterministically choose memory slots for variables independently of names that may depend on AST IDs.

This commit is contained in:
Daniel Kirchner 2023-06-07 15:55:22 +02:00 committed by r0qs
parent 6c4e40bcef
commit b7abd9bba4
No known key found for this signature in database
GPG Key ID: 61503DBA6667276C
16 changed files with 97 additions and 82 deletions

View File

@ -18,6 +18,7 @@ Bugfixes:
* Commandline Interface: It is no longer possible to specify both ``--optimize-yul`` and ``--no-optimize-yul`` at the same time. * Commandline Interface: It is no longer possible to specify both ``--optimize-yul`` and ``--no-optimize-yul`` at the same time.
* SMTChecker: Fix encoding of side-effects inside ``if`` and ``ternary conditional``statements in the BMC engine. * SMTChecker: Fix encoding of side-effects inside ``if`` and ``ternary conditional``statements in the BMC engine.
* SMTChecker: Fix false negative when a verification target can be violated only by trusted external call from another public function. * SMTChecker: Fix false negative when a verification target can be violated only by trusted external call from another public function.
* Yul Optimizer: Ensure that the assignment of memory slots for variables moved to memory does not depend on AST IDs that may depend on whether additional files are included during compilation.
* Yul Optimizer: Fix optimized IR being unnecessarily passed through the Yul optimizer again before bytecode generation. * Yul Optimizer: Fix optimized IR being unnecessarily passed through the Yul optimizer again before bytecode generation.
AST Changes: AST Changes:

View File

@ -63,7 +63,9 @@ CompilabilityChecker::CompilabilityChecker(
for (StackTooDeepError const& error: transform.stackErrors()) for (StackTooDeepError const& error: transform.stackErrors())
{ {
unreachableVariables[error.functionName].emplace(error.variable); auto& unreachables = unreachableVariables[error.functionName];
if (!util::contains(unreachables, error.variable))
unreachables.emplace_back(error.variable);
int& deficit = stackDeficit[error.functionName]; int& deficit = stackDeficit[error.functionName];
deficit = std::max(error.depth, deficit); deficit = std::max(error.depth, deficit);
} }

View File

@ -45,7 +45,7 @@ namespace solidity::yul
struct CompilabilityChecker struct CompilabilityChecker
{ {
CompilabilityChecker(Dialect const& _dialect, Object const& _object, bool _optimizeStackAllocation); CompilabilityChecker(Dialect const& _dialect, Object const& _object, bool _optimizeStackAllocation);
std::map<YulString, std::set<YulString>> unreachableVariables; std::map<YulString, std::vector<YulString>> unreachableVariables;
std::map<YulString, int> stackDeficit; std::map<YulString, int> stackDeficit;
}; };

View File

@ -22,6 +22,7 @@
#include <libyul/AST.h> #include <libyul/AST.h>
#include <libyul/optimiser/CallGraphGenerator.h> #include <libyul/optimiser/CallGraphGenerator.h>
#include <libsolutil/CommonData.h>
#include <stack> #include <stack>
using namespace std; using namespace std;
@ -79,7 +80,9 @@ CallGraph CallGraphGenerator::callGraph(Block const& _ast)
void CallGraphGenerator::operator()(FunctionCall const& _functionCall) void CallGraphGenerator::operator()(FunctionCall const& _functionCall)
{ {
m_callGraph.functionCalls[m_currentFunction].insert(_functionCall.functionName.name); auto& functionCalls = m_callGraph.functionCalls[m_currentFunction];
if (!util::contains(functionCalls, _functionCall.functionName.name))
functionCalls.emplace_back(_functionCall.functionName.name);
ASTWalker::operator()(_functionCall); ASTWalker::operator()(_functionCall);
} }

View File

@ -32,7 +32,7 @@ namespace solidity::yul
struct CallGraph struct CallGraph
{ {
std::map<YulString, std::set<YulString>> functionCalls; std::map<YulString, std::vector<YulString>> functionCalls;
std::set<YulString> functionsWithLoops; std::set<YulString> functionsWithLoops;
/// @returns the set of functions contained in cycles in the call graph, i.e. /// @returns the set of functions contained in cycles in the call graph, i.e.
/// functions that are part of a (mutual) recursion. /// functions that are part of a (mutual) recursion.

View File

@ -37,6 +37,8 @@
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
#include <libsolutil/Visitor.h> #include <libsolutil/Visitor.h>
#include <range/v3/action/remove.hpp>
using namespace std; using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::yul; using namespace solidity::yul;
@ -156,7 +158,8 @@ map<YulString, size_t> FullInliner::callDepths() const
} }
for (auto& call: cg.functionCalls) for (auto& call: cg.functionCalls)
call.second -= removed; for (YulString toBeRemoved: removed)
ranges::actions::remove(call.second, toBeRemoved);
currentDepth++; currentDepth++;

View File

@ -97,9 +97,9 @@ struct MemoryOffsetAllocator
/// Maps function names to the set of unreachable variables in that function. /// 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. /// An empty variable name means that the function has too many arguments or return variables.
map<YulString, set<YulString>> const& unreachableVariables; map<YulString, vector<YulString>> const& unreachableVariables;
/// The graph of immediate function calls of all functions. /// The graph of immediate function calls of all functions.
map<YulString, set<YulString>> const& callGraph; map<YulString, vector<YulString>> const& callGraph;
/// Maps the name of each user-defined function to its definition. /// Maps the name of each user-defined function to its definition.
map<YulString, FunctionDefinition const*> const& functionDefinitions; map<YulString, FunctionDefinition const*> const& functionDefinitions;
@ -149,18 +149,23 @@ void StackLimitEvader::run(
map<YulString, vector<StackLayoutGenerator::StackTooDeep>> const& _stackTooDeepErrors map<YulString, vector<StackLayoutGenerator::StackTooDeep>> const& _stackTooDeepErrors
) )
{ {
map<YulString, set<YulString>> unreachableVariables; map<YulString, vector<YulString>> unreachableVariables;
for (auto&& [function, stackTooDeepErrors]: _stackTooDeepErrors) for (auto&& [function, stackTooDeepErrors]: _stackTooDeepErrors)
{
auto& unreachables = unreachableVariables[function];
// TODO: choose wisely. // TODO: choose wisely.
for (auto const& stackTooDeepError: stackTooDeepErrors) for (auto const& stackTooDeepError: stackTooDeepErrors)
unreachableVariables[function] += stackTooDeepError.variableChoices | ranges::views::take(stackTooDeepError.deficit) | ranges::to<set<YulString>>; for (auto variable: stackTooDeepError.variableChoices | ranges::views::take(stackTooDeepError.deficit))
if (!util::contains(unreachables, variable))
unreachables.emplace_back(variable);
}
run(_context, _object, unreachableVariables); run(_context, _object, unreachableVariables);
} }
void StackLimitEvader::run( void StackLimitEvader::run(
OptimiserStepContext& _context, OptimiserStepContext& _context,
Object& _object, Object& _object,
map<YulString, set<YulString>> const& _unreachableVariables map<YulString, vector<YulString>> const& _unreachableVariables
) )
{ {
yulAssert(_object.code, ""); yulAssert(_object.code, "");

View File

@ -60,7 +60,7 @@ public:
static void run( static void run(
OptimiserStepContext& _context, OptimiserStepContext& _context,
Object& _object, Object& _object,
std::map<YulString, std::set<YulString>> const& _unreachableVariables std::map<YulString, std::vector<YulString>> const& _unreachableVariables
); );
/// @a _stackTooDeepErrors can be determined by the StackLayoutGenerator. /// @a _stackTooDeepErrors can be determined by the StackLayoutGenerator.
/// Can only be run on the EVM dialect with objects. /// Can only be run on the EVM dialect with objects.

View File

@ -349,7 +349,7 @@ YulOptimizerTestCommon::YulOptimizerTestCommon(
// Mark all variables with a name starting with "$" for escalation to memory. // Mark all variables with a name starting with "$" for escalation to memory.
struct FakeUnreachableGenerator: ASTWalker struct FakeUnreachableGenerator: ASTWalker
{ {
map<YulString, set<YulString>> fakeUnreachables; map<YulString, vector<YulString>> fakeUnreachables;
using ASTWalker::operator(); using ASTWalker::operator();
void operator()(FunctionDefinition const& _function) override void operator()(FunctionDefinition const& _function) override
{ {
@ -365,7 +365,8 @@ YulOptimizerTestCommon::YulOptimizerTestCommon(
void visitVariableName(YulString _var) void visitVariableName(YulString _var)
{ {
if (!_var.empty() && _var.str().front() == '$') if (!_var.empty() && _var.str().front() == '$')
fakeUnreachables[m_currentFunction].insert(_var); if (!util::contains(fakeUnreachables[m_currentFunction], _var))
fakeUnreachables[m_currentFunction].emplace_back(_var);
} }
void operator()(VariableDeclaration const& _varDecl) override void operator()(VariableDeclaration const& _varDecl) override
{ {

View File

@ -36,13 +36,13 @@
// } // }
// function f() -> x // function f() -> x
// { // {
// mstore(0x20, 0) // mstore(0x40, 0)
// mstore(0x20, 42) // mstore(0x40, 42)
// let $x3_4, $x4_5 := g() // let $x3_4, $x4_5 := g()
// mstore(0x00, $x4_5) // mstore(0x00, $x4_5)
// mstore(0x40, $x3_4) // mstore(0x20, $x3_4)
// x := mul(add(mload(0x20), mload(0x40)), h(mload(0x00))) // x := mul(add(mload(0x40), mload(0x20)), h(mload(0x00)))
// sstore(mload(0x40), mload(0x00)) // sstore(mload(0x20), mload(0x00))
// } // }
// function h(v) -> a_1 // function h(v) -> a_1
// { // {

View File

@ -30,10 +30,10 @@
// mstore(0x40, memoryguard(0x80)) // mstore(0x40, memoryguard(0x80))
// function f() // function f()
// { // {
// mstore(0x40, 0) // mstore(0x60, 0)
// mstore(0x60, 42) // mstore(0x40, 42)
// sstore(mload(0x40), mload(0x60)) // sstore(mload(0x60), mload(0x40))
// mstore(0x40, 21) // mstore(0x60, 21)
// } // }
// function g(gx) // function g(gx)
// { // {
@ -60,9 +60,9 @@
// { // {
// let $hx_9, $hy_10, $hz_11, $hw_12 := tuple4() // let $hx_9, $hy_10, $hz_11, $hw_12 := tuple4()
// mstore(0x00, $hw_12) // mstore(0x00, $hw_12)
// mstore(0x60, $hz_11) // mstore(0x20, $hz_11)
// mstore(0x40, $hy_10) // mstore(0x40, $hy_10)
// mstore(0x20, $hx_9) // mstore(0x60, $hx_9)
// { // {
// let hx_13, $hy_14, hz_15, $hw_16 := tuple4() // let hx_13, $hy_14, hz_15, $hw_16 := tuple4()
// mstore(0x00, $hw_16) // mstore(0x00, $hw_16)
@ -73,7 +73,7 @@
// { // {
// let $hx_17, $hy_18, hz_19, hw_20 := tuple4() // let $hx_17, $hy_18, hz_19, hw_20 := tuple4()
// mstore(0x40, $hy_18) // mstore(0x40, $hy_18)
// mstore(0x20, $hx_17) // mstore(0x60, $hx_17)
// hw := hw_20 // hw := hw_20
// hz := hz_19 // hz := hz_19
// } // }

View File

@ -39,17 +39,17 @@
// mstore(0x0140, b12) // mstore(0x0140, b12)
// mstore(0x0120, b13) // mstore(0x0120, b13)
// mstore(0x0100, b14) // mstore(0x0100, b14)
// mstore(0xc0, b17) // mstore(0xe0, b17)
// mstore(0xa0, b18) // mstore(0xc0, b18)
// mstore(0x80, b19) // mstore(0xa0, b19)
// mstore(0xe0, b29) // mstore(0x80, b29)
// sstore(0, mload(0x0100)) // sstore(0, mload(0x0100))
// sstore(1, b15) // sstore(1, b15)
// sstore(2, b16) // sstore(2, b16)
// sstore(3, mload(0xc0)) // sstore(3, mload(0xe0))
// sstore(4, mload(0xa0)) // sstore(4, mload(0xc0))
// sstore(5, mload(0x80)) // sstore(5, mload(0xa0))
// sstore(6, mload(0xe0)) // sstore(6, mload(0x80))
// v := add(mload(0x02a0), mload(0xe0)) // v := add(mload(0x02a0), mload(0x80))
// } // }
// } // }

View File

@ -42,20 +42,20 @@
// mstore(0x0160, b13) // mstore(0x0160, b13)
// mstore(0x0140, b14) // mstore(0x0140, b14)
// mstore(0x0120, b15) // mstore(0x0120, b15)
// mstore(0xc0, b17) // mstore(0x0100, b17)
// mstore(0xa0, b18) // mstore(0xe0, b18)
// mstore(0x80, b19) // mstore(0xc0, b19)
// mstore(0xe0, b29) // mstore(0xa0, b29)
// mstore(0x0100, b30) // mstore(0x80, b30)
// sstore(0, mload(0x0140)) // sstore(0, mload(0x0140))
// sstore(1, mload(0x0120)) // sstore(1, mload(0x0120))
// sstore(2, b16) // sstore(2, b16)
// sstore(3, mload(0xc0)) // sstore(3, mload(0x0100))
// sstore(4, mload(0xa0)) // sstore(4, mload(0xe0))
// sstore(5, mload(0x80)) // sstore(5, mload(0xc0))
// sstore(6, mload(0xe0)) // sstore(6, mload(0xa0))
// sstore(7, mload(0x0100)) // sstore(7, mload(0x80))
// v := mload(0x0100) // v := mload(0x80)
// sstore(mload(0x02e0), mload(0x0100)) // sstore(mload(0x02e0), mload(0x80))
// } // }
// } // }

View File

@ -43,21 +43,21 @@
// mstore(0x0180, b14) // mstore(0x0180, b14)
// mstore(0x0160, b15) // mstore(0x0160, b15)
// mstore(0x0140, b16) // mstore(0x0140, b16)
// mstore(0xc0, b17) // mstore(0x0120, b17)
// mstore(0xa0, b18) // mstore(0x0100, b18)
// mstore(0x80, b19) // mstore(0xe0, b19)
// mstore(0xe0, b29) // mstore(0xc0, b29)
// mstore(0x0120, b30) // mstore(0xa0, b30)
// mstore(0x0100, b31) // mstore(0x80, b31)
// sstore(0, mload(0x0180)) // sstore(0, mload(0x0180))
// sstore(1, mload(0x0160)) // sstore(1, mload(0x0160))
// sstore(2, mload(0x0140)) // sstore(2, mload(0x0140))
// sstore(3, mload(0xc0)) // sstore(3, mload(0x0120))
// sstore(4, mload(0xa0)) // sstore(4, mload(0x0100))
// sstore(5, mload(0x80)) // sstore(5, mload(0xe0))
// sstore(6, mload(0xe0)) // sstore(6, mload(0xc0))
// sstore(7, mload(0x0120)) // sstore(7, mload(0xa0))
// sstore(8, mload(0x0100)) // sstore(8, mload(0x80))
// v := add(mload(0x0320), mload(0x0100)) // v := add(mload(0x0320), mload(0x80))
// } // }
// } // }

View File

@ -39,8 +39,8 @@
// mstore(0x0140, 7) // mstore(0x0140, 7)
// mstore(0x0160, 8) // mstore(0x0160, 8)
// mstore(0x0180, 9) // mstore(0x0180, 9)
// mstore(0x01c0, 10) // mstore(0x01a0, 10)
// mstore(0x01a0, 11) // mstore(0x01c0, 11)
// mstore(0x01e0, 12) // mstore(0x01e0, 12)
// let a_13 := 13 // let a_13 := 13
// let a_14 := 14 // let a_14 := 14
@ -50,6 +50,6 @@
// let a_18 := 18 // let a_18 := 18
// let a_19 := 19 // let a_19 := 19
// let a_20 := 20 // let a_20 := 20
// verbatim_20i_0o("test", mload(0x80), mload(0xa0), mload(0xc0), mload(0xe0), mload(0x0100), mload(0x0120), mload(0x0140), mload(0x0160), mload(0x0180), mload(0x01c0), mload(0x01a0), mload(0x01e0), a_13, a_14, a_15, a_16, a_17, a_18, a_19, a_20) // verbatim_20i_0o("test", mload(0x80), mload(0xa0), mload(0xc0), mload(0xe0), mload(0x0100), mload(0x0120), mload(0x0140), mload(0x0160), mload(0x0180), mload(0x01a0), mload(0x01c0), mload(0x01e0), a_13, a_14, a_15, a_16, a_17, a_18, a_19, a_20)
// } // }
// } // }

View File

@ -51,17 +51,17 @@
// { // {
// { // {
// mstore(0x40, memoryguard(0x0280)) // mstore(0x40, memoryguard(0x0280))
// mstore(0x80, 1) // mstore(0x0100, 1)
// mstore(0xa0, 2) // mstore(0x0120, 2)
// mstore(0xc0, 3) // mstore(0x0140, 3)
// mstore(0xe0, 4) // mstore(0x0160, 4)
// mstore(0x0100, 5) // mstore(0x0180, 5)
// mstore(0x0120, 6) // mstore(0x01a0, 6)
// mstore(0x0140, 7) // mstore(0x01c0, 7)
// mstore(0x0160, 8) // mstore(0x01e0, 8)
// mstore(0x0180, 9) // mstore(0x0200, 9)
// mstore(0x0240, 10) // mstore(0x0220, 10)
// mstore(0x0220, 11) // mstore(0x0240, 11)
// mstore(0x0260, 12) // mstore(0x0260, 12)
// let a_13 := 13 // let a_13 := 13
// let a_14 := 14 // let a_14 := 14
@ -71,11 +71,11 @@
// let a_18 := 18 // let a_18 := 18
// let a_19 := 19 // let a_19 := 19
// let a_20 := 20 // let a_20 := 20
// let b_1_1, b_2_2, b_3_3, b_4_4, b_5_5, b_6_6, b_7_7, b_8_8, b_9_9, b_10_10, b_11_11, b_12_12, b_13_13, b_14_14, b_15_15, b_16_16, b_17_17, b_18_18, b_19_19, b_20_20 := verbatim_20i_20o("test", mload(0x80), mload(0xa0), mload(0xc0), mload(0xe0), mload(0x0100), mload(0x0120), mload(0x0140), mload(0x0160), mload(0x0180), mload(0x0240), mload(0x0220), mload(0x0260), a_13, a_14, a_15, a_16, a_17, a_18, a_19, a_20) // let b_1_1, b_2_2, b_3_3, b_4_4, b_5_5, b_6_6, b_7_7, b_8_8, b_9_9, b_10_10, b_11_11, b_12_12, b_13_13, b_14_14, b_15_15, b_16_16, b_17_17, b_18_18, b_19_19, b_20_20 := verbatim_20i_20o("test", mload(0x0100), mload(0x0120), mload(0x0140), mload(0x0160), mload(0x0180), mload(0x01a0), mload(0x01c0), mload(0x01e0), mload(0x0200), mload(0x0220), mload(0x0240), mload(0x0260), a_13, a_14, a_15, a_16, a_17, a_18, a_19, a_20)
// mstore(0x01a0, b_4_4) // mstore(0x80, b_4_4)
// mstore(0x01c0, b_3_3) // mstore(0xa0, b_3_3)
// mstore(0x01e0, b_2_2) // mstore(0xc0, b_2_2)
// mstore(0x0200, b_1_1) // mstore(0xe0, b_1_1)
// let b_20 := b_20_20 // let b_20 := b_20_20
// let b_19 := b_19_19 // let b_19 := b_19_19
// let b_18 := b_18_18 // let b_18 := b_18_18
@ -92,10 +92,10 @@
// let b_7 := b_7_7 // let b_7 := b_7_7
// let b_6 := b_6_6 // let b_6 := b_6_6
// let b_5 := b_5_5 // let b_5 := b_5_5
// sstore(1, mload(0x0200)) // sstore(1, mload(0xe0))
// sstore(2, mload(0x01e0)) // sstore(2, mload(0xc0))
// sstore(3, mload(0x01c0)) // sstore(3, mload(0xa0))
// sstore(4, mload(0x01a0)) // sstore(4, mload(0x80))
// sstore(5, b_5) // sstore(5, b_5)
// sstore(6, b_6) // sstore(6, b_6)
// sstore(7, b_7) // sstore(7, b_7)