Merge pull request #9378 from ethereum/variableDeclStackSlotReuse

Free variables directly after visiting RHS of VarDecls EVMCodeTransform.
This commit is contained in:
chriseth 2020-07-13 18:11:00 +02:00 committed by GitHub
commit 3376dbab47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 8 deletions

View File

@ -6,6 +6,7 @@ Language Features:
Compiler Features: Compiler Features:
* Code Generator: Evaluate ``keccak256`` of string literals at compile-time. * Code Generator: Evaluate ``keccak256`` of string literals at compile-time.
* Peephole Optimizer: Remove unnecessary masking of tags. * Peephole Optimizer: Remove unnecessary masking of tags.
* Yul EVM Code Transform: Free stack slots directly after visiting the right-hand-side of variable declarations instead of at the end of the statement only.
Bugfixes: Bugfixes:
* Type Checker: Fix overload resolution in combination with ``{value: ...}``. * Type Checker: Fix overload resolution in combination with ``{value: ...}``.

View File

@ -141,7 +141,7 @@ bool CodeTransform::unreferenced(Scope::Variable const& _var) const
return !m_context->variableReferences.count(&_var) || m_context->variableReferences[&_var] == 0; return !m_context->variableReferences.count(&_var) || m_context->variableReferences[&_var] == 0;
} }
void CodeTransform::freeUnusedVariables() void CodeTransform::freeUnusedVariables(bool _popUnusedSlotsAtStackTop)
{ {
if (!m_allowStackOpt) if (!m_allowStackOpt)
return; return;
@ -154,6 +154,7 @@ void CodeTransform::freeUnusedVariables()
deleteVariable(var); deleteVariable(var);
} }
if (_popUnusedSlotsAtStackTop)
while (m_unusedStackSlots.count(m_assembly.stackHeight() - 1)) while (m_unusedStackSlots.count(m_assembly.stackHeight() - 1))
{ {
yulAssert(m_unusedStackSlots.erase(m_assembly.stackHeight() - 1), ""); yulAssert(m_unusedStackSlots.erase(m_assembly.stackHeight() - 1), "");
@ -181,6 +182,7 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
{ {
std::visit(*this, *_varDecl.value); std::visit(*this, *_varDecl.value);
expectDeposit(static_cast<int>(numVariables), static_cast<int>(heightAtStart)); expectDeposit(static_cast<int>(numVariables), static_cast<int>(heightAtStart));
freeUnusedVariables(false);
} }
else else
{ {

View File

@ -161,8 +161,9 @@ protected:
bool unreferenced(Scope::Variable const& _var) const; bool unreferenced(Scope::Variable const& _var) const;
/// Marks slots of variables that are not used anymore /// Marks slots of variables that are not used anymore
/// and were defined in the current scope for reuse. /// and were defined in the current scope for reuse.
/// Also POPs unused topmost stack slots. /// Also POPs unused topmost stack slots,
void freeUnusedVariables(); /// unless @a _popUnusedSlotsAtStackTop is set to false.
void freeUnusedVariables(bool _popUnusedSlotsAtStackTop = true);
/// Marks the stack slot of @a _var to be reused. /// Marks the stack slot of @a _var to be reused.
void deleteVariable(Scope::Variable const& _var); void deleteVariable(Scope::Variable const& _var);

View File

@ -345,6 +345,69 @@ BOOST_AUTO_TEST_CASE(reuse_slots_function_with_gaps)
); );
} }
BOOST_AUTO_TEST_CASE(reuse_on_decl_assign_to_last_used)
{
string in = R"({
let x := 5
let y := x // y should reuse the stack slot of x
sstore(y, y)
})";
BOOST_CHECK_EQUAL(assemble(in),
"PUSH1 0x5 "
"DUP1 SWAP1 POP "
"DUP1 DUP2 SSTORE "
"POP "
);
}
BOOST_AUTO_TEST_CASE(reuse_on_decl_assign_to_last_used_expr)
{
string in = R"({
let x := 5
let y := add(x, 2) // y should reuse the stack slot of x
sstore(y, y)
})";
BOOST_CHECK_EQUAL(assemble(in),
"PUSH1 0x5 "
"PUSH1 0x2 DUP2 ADD "
"SWAP1 POP "
"DUP1 DUP2 SSTORE "
"POP "
);
}
BOOST_AUTO_TEST_CASE(reuse_on_decl_assign_to_not_last_used)
{
string in = R"({
let x := 5
let y := x // y should not reuse the stack slot of x, since x is still used below
sstore(y, x)
})";
BOOST_CHECK_EQUAL(assemble(in),
"PUSH1 0x5 "
"DUP1 "
"DUP2 DUP2 SSTORE "
"POP POP "
);
}
BOOST_AUTO_TEST_CASE(reuse_on_decl_assign_not_same_scope)
{
string in = R"({
let x := 5
{
let y := x // y should not reuse the stack slot of x, since x is not in the same scope
sstore(y, y)
}
})";
BOOST_CHECK_EQUAL(assemble(in),
"PUSH1 0x5 "
"DUP1 "
"DUP1 DUP2 SSTORE "
"POP POP "
);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()