From 439a225ceec5dce16ee1778993e3958aeacbbb5d Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 9 May 2019 22:34:09 +0200 Subject: [PATCH] Simplify single-run for loops to if statements. --- Changelog.md | 1 + libyul/optimiser/ControlFlowSimplifier.cpp | 47 ++++++++++++++++++++-- libyul/optimiser/ControlFlowSimplifier.h | 10 ++++- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index d182257f1..a10da4252 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Compiler Features: * SMTChecker: Support ``delete``. * SMTChecker: Inline external function calls to ``this``. * Assembler: Encode the compiler version in the deployed bytecode. + * Yul Optimizer: Simplify single-run ``for`` loops to ``if`` statements. Bugfixes: diff --git a/libyul/optimiser/ControlFlowSimplifier.cpp b/libyul/optimiser/ControlFlowSimplifier.cpp index 7040891df..a3b2f4f37 100644 --- a/libyul/optimiser/ControlFlowSimplifier.cpp +++ b/libyul/optimiser/ControlFlowSimplifier.cpp @@ -115,9 +115,51 @@ void ControlFlowSimplifier::operator()(Block& _block) simplify(_block.statements); } +void ControlFlowSimplifier::visit(Statement& _st) +{ + if (_st.type() == typeid(ForLoop)) + { + ForLoop& forLoop = boost::get(_st); + yulAssert(forLoop.pre.statements.empty(), ""); + + size_t outerBreak = m_numBreakStatements; + size_t outerContinue = m_numContinueStatements; + m_numBreakStatements = 0; + m_numContinueStatements = 0; + + ASTModifier::visit(_st); + + if (!forLoop.body.statements.empty()) + { + bool isTerminating = false; + TerminationFinder::ControlFlow controlFlow = TerminationFinder::controlFlowKind(forLoop.body.statements.back()); + if (controlFlow == TerminationFinder::ControlFlow::Break) + { + isTerminating = true; + --m_numBreakStatements; + } + else if (controlFlow == TerminationFinder::ControlFlow::Terminate) + isTerminating = true; + + if (isTerminating && m_numContinueStatements == 0 && m_numBreakStatements == 0) + { + If replacement{forLoop.location, std::move(forLoop.condition), std::move(forLoop.body)}; + if (controlFlow == TerminationFinder::ControlFlow::Break) + replacement.body.statements.resize(replacement.body.statements.size() - 1); + _st = std::move(replacement); + } + } + + m_numBreakStatements = outerBreak; + m_numContinueStatements = outerContinue; + } + else + ASTModifier::visit(_st); +} + void ControlFlowSimplifier::simplify(std::vector& _statements) { - GenericFallbackReturnsVisitor const visitor( + GenericFallbackReturnsVisitor const visitor( [&](If& _ifStmt) -> OptionalStatements { if (_ifStmt.body.statements.empty()) { @@ -136,9 +178,6 @@ void ControlFlowSimplifier::simplify(std::vector& _statements) else if (_switchStmt.cases.size() == 1) return reduceSingleCaseSwitch(_switchStmt); - return {}; - }, - [&](ForLoop&) -> OptionalStatements { return {}; } ); diff --git a/libyul/optimiser/ControlFlowSimplifier.h b/libyul/optimiser/ControlFlowSimplifier.h index 4b0b83873..a2975e5a0 100644 --- a/libyul/optimiser/ControlFlowSimplifier.h +++ b/libyul/optimiser/ControlFlowSimplifier.h @@ -38,7 +38,7 @@ namespace yul * The ControlFlowSimplifier does record the presence or absence of ``break`` * and ``continue`` statements during its traversal. * - * Prerequisite: Disambiguator, ForLoopInitRewriter. + * Prerequisite: Disambiguator, FunctionHoister, ForLoopInitRewriter. * * Important: Introduces EVM opcodes and thus can only be used on EVM code for now. */ @@ -46,9 +46,17 @@ class ControlFlowSimplifier: public ASTModifier { public: using ASTModifier::operator(); + void operator()(Break&) override { ++m_numBreakStatements; } + void operator()(Continue&) override { ++m_numContinueStatements; } void operator()(Block& _block) override; + + void visit(Statement& _st) override; + private: void simplify(std::vector& _statements); + + size_t m_numBreakStatements = 0; + size_t m_numContinueStatements = 0; }; }