Free variables directly after visiting RHS of Variable Declarations during EVMCodeTransform.

This commit is contained in:
Daniel Kirchner 2020-07-10 17:39:23 +02:00
parent a783449195
commit def0ebbb3e
4 changed files with 75 additions and 8 deletions

View File

@ -6,6 +6,7 @@ Language Features:
Compiler Features:
* Code Generator: Evaluate ``keccak256`` of string literals at compile-time.
* 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:
* 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;
}
void CodeTransform::freeUnusedVariables()
void CodeTransform::freeUnusedVariables(bool _popUnusedSlotsAtStackTop)
{
if (!m_allowStackOpt)
return;
@ -154,11 +154,12 @@ void CodeTransform::freeUnusedVariables()
deleteVariable(var);
}
while (m_unusedStackSlots.count(m_assembly.stackHeight() - 1))
{
yulAssert(m_unusedStackSlots.erase(m_assembly.stackHeight() - 1), "");
m_assembly.appendInstruction(evmasm::Instruction::POP);
}
if (_popUnusedSlotsAtStackTop)
while (m_unusedStackSlots.count(m_assembly.stackHeight() - 1))
{
yulAssert(m_unusedStackSlots.erase(m_assembly.stackHeight() - 1), "");
m_assembly.appendInstruction(evmasm::Instruction::POP);
}
}
void CodeTransform::deleteVariable(Scope::Variable const& _var)
@ -181,6 +182,7 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
{
std::visit(*this, *_varDecl.value);
expectDeposit(static_cast<int>(numVariables), static_cast<int>(heightAtStart));
freeUnusedVariables(false);
}
else
{

View File

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