[Yul] Directly jump over a series of function definitions

Implement a AbstractAssembly::setStackHeight function
Update the tests
Update Changelog
This commit is contained in:
Vedant Agarwala 2019-05-25 18:37:51 +08:00 committed by Vedant Agarwala
parent b66950711e
commit 5d6cbd97df
9 changed files with 69 additions and 15 deletions

View File

@ -85,6 +85,7 @@ Compiler Features:
* Yul: Adds break and continue keywords to for-loop syntax.
* Yul: Support ``.`` as part of identifiers.
* Yul Optimizer: Adds steps for detecting and removing of dead code.
* Yul Code Generator: Directly jump over a series of function definitions (instead of jumping over each one)
Bugfixes:

View File

@ -62,6 +62,7 @@ public:
/// Retrieve the current height of the stack. This does not have to be zero
/// at the beginning.
virtual int stackHeight() const = 0;
virtual void setStackHeight(int height) = 0;
/// Append an EVM instruction.
virtual void appendInstruction(dev::eth::Instruction _instruction) = 0;
/// Append a constant.

View File

@ -57,6 +57,11 @@ int EthAssemblyAdapter::stackHeight() const
return m_assembly.deposit();
}
void EthAssemblyAdapter::setStackHeight(int height)
{
m_assembly.setDeposit(height);
}
void EthAssemblyAdapter::appendInstruction(dev::eth::Instruction _instruction)
{
m_assembly.append(_instruction);

View File

@ -44,6 +44,7 @@ public:
explicit EthAssemblyAdapter(dev::eth::Assembly& _assembly);
void setSourceLocation(langutil::SourceLocation const& _location) override;
int stackHeight() const override;
void setStackHeight(int height) override;
void appendInstruction(dev::eth::Instruction _instruction) override;
void appendConstant(dev::u256 const& _constant) override;
void appendLabel(LabelID _labelId) override;

View File

@ -45,6 +45,7 @@ public:
/// Retrieve the current height of the stack. This does not have to be zero
/// at the beginning.
int stackHeight() const override { return m_stackHeight; }
void setStackHeight(int height) override { m_stackHeight = height; }
/// Append an EVM instruction.
void appendInstruction(dev::eth::Instruction _instruction) override;
/// Append a constant.

View File

@ -490,19 +490,15 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
}
m_assembly.setSourceLocation(_function.location);
int stackHeightBefore = m_assembly.stackHeight();
AbstractAssembly::LabelID afterFunction = m_assembly.newLabelId();
int const stackHeightBefore = m_assembly.stackHeight();
if (m_evm15)
{
m_assembly.appendJumpTo(afterFunction, -stackHeightBefore);
m_assembly.appendBeginsub(functionEntryID(_function.name, function), _function.parameters.size());
}
else
{
m_assembly.appendJumpTo(afterFunction, -stackHeightBefore + height);
m_assembly.appendLabel(functionEntryID(_function.name, function));
}
m_assembly.setStackHeight(height);
m_stackAdjustment += localStackAdjustment;
for (auto const& v: _function.returnVariables)
@ -592,8 +588,8 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
else
m_assembly.appendJump(stackHeightBefore - _function.returnVariables.size());
m_stackAdjustment -= localStackAdjustment;
m_assembly.appendLabel(afterFunction);
checkStackHeight(&_function);
m_assembly.setStackHeight(stackHeightBefore);
}
void CodeTransform::operator()(ForLoop const& _forLoop)
@ -727,11 +723,32 @@ void CodeTransform::visitExpression(Expression const& _expression)
void CodeTransform::visitStatements(vector<Statement> const& _statements)
{
// Workaround boost bug:
// https://www.boost.org/doc/libs/1_63_0/libs/optional/doc/html/boost_optional/tutorial/gotchas/false_positive_with__wmaybe_uninitialized.html
boost::optional<AbstractAssembly::LabelID> jumpTarget = boost::make_optional(false, AbstractAssembly::LabelID());
for (auto const& statement: _statements)
{
freeUnusedVariables();
auto const* functionDefinition = boost::get<FunctionDefinition>(&statement);
if (functionDefinition && !jumpTarget)
{
m_assembly.setSourceLocation(locationOf(statement));
jumpTarget = m_assembly.newLabelId();
m_assembly.appendJumpTo(*jumpTarget, 0);
}
else if (!functionDefinition && jumpTarget)
{
m_assembly.appendLabel(*jumpTarget);
jumpTarget = boost::none;
}
boost::apply_visitor(*this, statement);
}
// we may have a leftover jumpTarget
if (jumpTarget)
m_assembly.appendLabel(*jumpTarget);
freeUnusedVariables();
}

View File

@ -49,6 +49,7 @@ public:
void setSourceLocation(langutil::SourceLocation const&) override {}
int stackHeight() const override { return m_stackHeight; }
void setStackHeight(int height) override { m_stackHeight = height; }
void appendInstruction(dev::eth::Instruction _instruction) override;
void appendConstant(dev::u256 const& _constant) override;
void appendLabel(LabelID _labelId) override;

View File

@ -277,16 +277,15 @@ BOOST_AUTO_TEST_CASE(functions_multi_return)
let unused := 7
})";
BOOST_CHECK_EQUAL(assemble(in),
"PUSH1 0xB JUMP "
"PUSH1 0x13 JUMP "
"JUMPDEST PUSH1 0x0 SWAP3 SWAP2 POP POP JUMP " // f
"JUMPDEST PUSH1 0x17 JUMP "
"JUMPDEST PUSH1 0x0 PUSH1 0x0 SWAP1 SWAP2 JUMP " // g
"JUMPDEST PUSH1 0x21 PUSH1 0x2 PUSH1 0x1 PUSH1 0x3 JUMP " // f(1, 2)
"JUMPDEST PUSH1 0x2B PUSH1 0x4 PUSH1 0x3 PUSH1 0x3 JUMP " // f(3, 4)
"JUMPDEST PUSH1 0x1D PUSH1 0x2 PUSH1 0x1 PUSH1 0x3 JUMP " // f(1, 2)
"JUMPDEST PUSH1 0x27 PUSH1 0x4 PUSH1 0x3 PUSH1 0x3 JUMP " // f(3, 4)
"JUMPDEST SWAP1 POP " // assignment to x
"POP " // remove x
"PUSH1 0x34 PUSH1 0xF JUMP " // g()
"JUMPDEST PUSH1 0x3A PUSH1 0xF JUMP " // g()
"PUSH1 0x30 PUSH1 0xB JUMP " // g()
"JUMPDEST PUSH1 0x36 PUSH1 0xB JUMP " // g()
"JUMPDEST SWAP2 POP SWAP2 POP " // assignments
"POP POP " // removal of y and z
"PUSH1 0x7 POP "

View File

@ -0,0 +1,28 @@
object "Contract" {
code {
function f() {}
function g() {}
sstore(0, 1)
}
}
// ----
// Assembly:
// /* "source":33:48 */
// jump(tag_1)
// tag_2:
// /* "source":46:48 */
// jump
// /* "source":53:68 */
// tag_3:
// /* "source":66:68 */
// jump
// tag_1:
// /* "source":83:84 */
// 0x01
// /* "source":80:81 */
// 0x00
// /* "source":73:85 */
// sstore
// Bytecode: 6007565b565b565b6001600055
// Opcodes: PUSH1 0x7 JUMP JUMPDEST JUMP JUMPDEST JUMP JUMPDEST PUSH1 0x1 PUSH1 0x0 SSTORE