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: 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,11 +154,12 @@ void CodeTransform::freeUnusedVariables()
deleteVariable(var); deleteVariable(var);
} }
while (m_unusedStackSlots.count(m_assembly.stackHeight() - 1)) if (_popUnusedSlotsAtStackTop)
{ while (m_unusedStackSlots.count(m_assembly.stackHeight() - 1))
yulAssert(m_unusedStackSlots.erase(m_assembly.stackHeight() - 1), ""); {
m_assembly.appendInstruction(evmasm::Instruction::POP); yulAssert(m_unusedStackSlots.erase(m_assembly.stackHeight() - 1), "");
} m_assembly.appendInstruction(evmasm::Instruction::POP);
}
} }
void CodeTransform::deleteVariable(Scope::Variable const& _var) void CodeTransform::deleteVariable(Scope::Variable const& _var)
@ -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()