diff --git a/Changelog.md b/Changelog.md index d1e221080..5d46ee6f6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Language Features: Compiler Features: + * Yul: Adds break and continue keywords to for-loop syntax. Bugfixes: diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index 728162b4c..4afafa6af 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -633,14 +633,34 @@ void CodeTransform::operator()(ForLoop const& _forLoop) checkStackHeight(&_forLoop); } -void CodeTransform::operator()(Break const&) +int CodeTransform::appendPopUntil(int _targetDepth) { - yulAssert(false, "Code generation for break statement in Yul is not implemented yet."); + int const stackDiffAfter = m_assembly.stackHeight() - _targetDepth; + for (int i = 0; i < stackDiffAfter; ++i) + m_assembly.appendInstruction(solidity::Instruction::POP); + return stackDiffAfter; } -void CodeTransform::operator()(Continue const&) +void CodeTransform::operator()(Break const& _break) { - yulAssert(false, "Code generation for continue statement in Yul is not implemented yet."); + yulAssert(!m_context->forLoopStack.empty(), "Invalid break-statement. Requires surrounding for-loop in code generation."); + m_assembly.setSourceLocation(_break.location); + + Context::JumpInfo const& jump = m_context->forLoopStack.top().done; + m_assembly.appendJumpTo(jump.label, appendPopUntil(jump.targetStackHeight)); + + checkStackHeight(&_break); +} + +void CodeTransform::operator()(Continue const& _continue) +{ + yulAssert(!m_context->forLoopStack.empty(), "Invalid continue-statement. Requires surrounding for-loop in code generation."); + m_assembly.setSourceLocation(_continue.location); + + Context::JumpInfo const& jump = m_context->forLoopStack.top().post; + m_assembly.appendJumpTo(jump.label, appendPopUntil(jump.targetStackHeight)); + + checkStackHeight(&_continue); } void CodeTransform::operator()(Block const& _block) diff --git a/libyul/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h index 141dd91ef..001616c34 100644 --- a/libyul/backends/evm/EVMCodeTransform.h +++ b/libyul/backends/evm/EVMCodeTransform.h @@ -217,6 +217,10 @@ private: /// and corrects the stack height to the target stack height. void stackError(StackTooDeepError _error, int _targetStackSize); + /// Ensures stack height is down to @p _targetDepth by appending POP instructions to the output assembly. + /// Returns the number of POP statements that have been appended. + int appendPopUntil(int _targetDepth); + AbstractAssembly& m_assembly; AsmAnalysisInfo& m_info; Scope* m_scope = nullptr; diff --git a/test/libsolidity/semanticTests/asmForLoop/for_loop_break.sol b/test/libsolidity/semanticTests/asmForLoop/for_loop_break.sol new file mode 100644 index 000000000..d74db942c --- /dev/null +++ b/test/libsolidity/semanticTests/asmForLoop/for_loop_break.sol @@ -0,0 +1,13 @@ +contract C { + function f() public returns (uint i) { + assembly { + for {} lt(i, 10) { i := add(i, 1) } + { + if eq(i, 6) { break } + i := add(i, 1) + } + } + } +} +// ---- +// f() -> 6 diff --git a/test/libsolidity/semanticTests/asmForLoop/for_loop_continue.sol b/test/libsolidity/semanticTests/asmForLoop/for_loop_continue.sol new file mode 100644 index 000000000..f25a3c06a --- /dev/null +++ b/test/libsolidity/semanticTests/asmForLoop/for_loop_continue.sol @@ -0,0 +1,13 @@ +contract C { + function f() public returns (uint k) { + assembly { + for {let i := 0} lt(i, 10) { i := add(i, 1) } + { + if eq(mod(i, 2), 0) { continue } + k := add(k, 1) + } + } + } +} +// ---- +// f() -> 5 diff --git a/test/libsolidity/semanticTests/asmForLoop/for_loop_nested.sol b/test/libsolidity/semanticTests/asmForLoop/for_loop_nested.sol new file mode 100644 index 000000000..3a13e9f0e --- /dev/null +++ b/test/libsolidity/semanticTests/asmForLoop/for_loop_nested.sol @@ -0,0 +1,20 @@ +contract C { + function f(uint x) public returns (uint i) { + assembly { + for {} lt(i, 10) { i := add(i, 1) } + { + if eq(x, 0) { i := 2 break } + for {} lt(x, 3) { i := 17 x := 9 } { + if eq(x, 1) { continue } + if eq(x, 2) { break } + } + if eq(x, 4) { i := 90 } + } + } + } +} +// ---- +// f(uint256): 0 -> 2 +// f(uint256): 1 -> 18 +// f(uint256): 2 -> 10 +// f(uint256): 4 -> 91