From d233c66795db85e37a967ec38de3b31be2b5105f Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 10 Aug 2022 15:48:06 +0200 Subject: [PATCH] Special case code generation for for loops. --- Changelog.md | 1 + libsolidity/analysis/PostTypeChecker.cpp | 78 +++++++++++++++++-- libsolidity/analysis/PostTypeChecker.h | 3 + libsolidity/ast/ASTAnnotations.h | 1 + libsolidity/ast/ASTJsonExporter.cpp | 18 +++-- libsolidity/codegen/ContractCompiler.cpp | 9 +++ .../codegen/ir/IRGeneratorForStatements.cpp | 13 +++- .../codegen/ir/IRGeneratorForStatements.h | 3 +- 8 files changed, 111 insertions(+), 15 deletions(-) diff --git a/Changelog.md b/Changelog.md index 5fdd6b78e..aa80aaede 100644 --- a/Changelog.md +++ b/Changelog.md @@ -30,6 +30,7 @@ Language Features: Compiler Features: + * Code Generator: Remove redundant overflow checks in specific for loops. * Commandline Interface: Add ``--ast-compact-json`` output in assembler mode. * Commandline Interface: Add ``--ir-ast-json`` and ``--ir-optimized-ast-json`` outputs for Solidity input, providing AST in compact JSON format for IR and optimized IR. * Commandline Interface: Respect ``--optimize-yul`` and ``--no-optimize-yul`` in compiler mode and accept them in assembler mode as well. ``--optimize --no-optimize-yul`` combination now allows enabling EVM assembly optimizer without enabling Yul optimizer. diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp index bc9df3dc4..b68167c1d 100644 --- a/libsolidity/analysis/PostTypeChecker.cpp +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -129,6 +129,17 @@ void PostTypeChecker::endVisit(ModifierInvocation const& _modifierInvocation) callEndVisit(_modifierInvocation); } + +bool PostTypeChecker::visit(ForStatement const& _forStatement) +{ + return callVisit(_forStatement); +} + +void PostTypeChecker::endVisit(ForStatement const& _forStatement) +{ + callEndVisit(_forStatement); +} + namespace { struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker @@ -421,15 +432,70 @@ struct ReservedErrorSelector: public PostTypeChecker::Checker } }; +class LValueChecker: public ASTConstVisitor +{ +public: + LValueChecker(Identifier const& _identifier): m_declaration(_identifier.annotation().referencedDeclaration) {} + bool willBeWrittenTo() const { return m_willBeWrittenTo; } + void endVisit(Identifier const& _identifier) override + { + if ( + _identifier.annotation().referencedDeclaration == m_declaration && + _identifier.annotation().willBeWrittenTo + ) + m_willBeWrittenTo = true; + } +private: + Declaration const* m_declaration; + bool m_willBeWrittenTo = false; +}; + +struct SimpleCounterForLoopChecker: public PostTypeChecker::Checker +{ + SimpleCounterForLoopChecker(ErrorReporter& _errorReporter): Checker(_errorReporter) {} + bool visit(ForStatement const& _forStatement) override + { + _forStatement.annotation().isSimpleCounterLoop = isSimpleCounterLoop(_forStatement); + return true; + } + bool isSimpleCounterLoop(ForStatement const& _forStatement) const + { + auto const* cond = dynamic_cast(_forStatement.condition()); + if (!cond || cond->getOperator() != Token::LessThan || cond->userDefinedFunctionType()) + return false; + if (!_forStatement.loopExpression()) + return false; + + auto const* post = dynamic_cast(&_forStatement.loopExpression()->expression()); + // This matches both operators ++i and i++ + if (!post || post->getOperator() != Token::Inc || post->userDefinedFunctionType()) + return false; + + auto const* lhsIdentifier = dynamic_cast(&cond->leftExpression()); + auto const* lhsType = dynamic_cast(cond->leftExpression().annotation().type); + auto const *commonType = dynamic_cast(cond->annotation().commonType); + + if (lhsIdentifier && lhsType && commonType && *lhsType == *commonType) + { + LValueChecker lValueChecker{*lhsIdentifier}; + _forStatement.body().accept(lValueChecker); + if (!lValueChecker.willBeWrittenTo()) + return true; + } + return false; + } +}; + } PostTypeChecker::PostTypeChecker(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) { - m_checkers.push_back(std::make_shared(_errorReporter)); - m_checkers.push_back(std::make_shared(_errorReporter)); - m_checkers.push_back(std::make_shared(_errorReporter)); - m_checkers.push_back(std::make_shared(_errorReporter)); - m_checkers.push_back(std::make_shared(_errorReporter)); - m_checkers.push_back(std::make_shared(_errorReporter)); + m_checkers.push_back(make_shared(_errorReporter)); + m_checkers.push_back(make_shared(_errorReporter)); + m_checkers.push_back(make_shared(_errorReporter)); + m_checkers.push_back(make_shared(_errorReporter)); + m_checkers.push_back(make_shared(_errorReporter)); + m_checkers.push_back(make_shared(_errorReporter)); + m_checkers.push_back(make_shared(_errorReporter)); } diff --git a/libsolidity/analysis/PostTypeChecker.h b/libsolidity/analysis/PostTypeChecker.h index 04b4fbe7a..178130a35 100644 --- a/libsolidity/analysis/PostTypeChecker.h +++ b/libsolidity/analysis/PostTypeChecker.h @@ -97,6 +97,9 @@ private: bool visit(ModifierInvocation const& _modifierInvocation) override; void endVisit(ModifierInvocation const& _modifierInvocation) override; + bool visit(ForStatement const& _forStatement) override; + void endVisit(ForStatement const& _forStatement) override; + template bool callVisit(T const& _node) { diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 2573015c2..9a1990711 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -238,6 +238,7 @@ struct TryCatchClauseAnnotation: ASTAnnotation, ScopableAnnotation struct ForStatementAnnotation: StatementAnnotation, ScopableAnnotation { + util::SetOnce isSimpleCounterLoop; }; struct ReturnAnnotation: StatementAnnotation diff --git a/libsolidity/ast/ASTJsonExporter.cpp b/libsolidity/ast/ASTJsonExporter.cpp index b15917809..251622195 100644 --- a/libsolidity/ast/ASTJsonExporter.cpp +++ b/libsolidity/ast/ASTJsonExporter.cpp @@ -742,12 +742,18 @@ bool ASTJsonExporter::visit(WhileStatement const& _node) bool ASTJsonExporter::visit(ForStatement const& _node) { - setJsonNode(_node, "ForStatement", { - std::make_pair("initializationExpression", toJsonOrNull(_node.initializationExpression())), - std::make_pair("condition", toJsonOrNull(_node.condition())), - std::make_pair("loopExpression", toJsonOrNull(_node.loopExpression())), - std::make_pair("body", toJson(_node.body())) - }); + + std::vector> attributes = { + make_pair("initializationExpression", toJsonOrNull(_node.initializationExpression())), + make_pair("condition", toJsonOrNull(_node.condition())), + make_pair("loopExpression", toJsonOrNull(_node.loopExpression())), + make_pair("body", toJson(_node.body())) + }; + + if (_node.annotation().isSimpleCounterLoop.set()) + attributes.emplace_back(make_pair("isSimpleCounterLoop", *_node.annotation().isSimpleCounterLoop)); + + setJsonNode(_node, "ForStatement", std::move(attributes)); return false; } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 3dfdf76d8..465c284a6 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -1245,7 +1245,16 @@ bool ContractCompiler::visit(ForStatement const& _forStatement) // for's loop expression if existing if (_forStatement.loopExpression()) + { + Arithmetic previousArithmetic = m_context.arithmetic(); + if ( + *_forStatement.annotation().isSimpleCounterLoop && + m_optimiserSettings == OptimiserSettings::standard() + ) + m_context.setArithmetic(Arithmetic::Wrapping); _forStatement.loopExpression()->accept(*this); + m_context.setArithmetic(previousArithmetic); + } m_context.appendJumpTo(loopStart); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index c05fffbff..ffcb1409b 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -617,7 +617,9 @@ bool IRGeneratorForStatements::visit(ForStatement const& _forStatement) _forStatement.body(), _forStatement.condition(), _forStatement.initializationExpression(), - _forStatement.loopExpression() + _forStatement.loopExpression(), + false, + *_forStatement.annotation().isSimpleCounterLoop ); return false; @@ -3192,7 +3194,8 @@ void IRGeneratorForStatements::generateLoop( Expression const* _conditionExpression, Statement const* _initExpression, ExpressionStatement const* _loopExpression, - bool _isDoWhile + bool _isDoWhile, + bool _isSimpleCounterLoop ) { std::string firstRun; @@ -3209,7 +3212,13 @@ void IRGeneratorForStatements::generateLoop( _initExpression->accept(*this); appendCode() << "} 1 {\n"; if (_loopExpression) + { + Arithmetic previousArithmetic = m_context.arithmetic(); + if (_isSimpleCounterLoop) + m_context.setArithmetic(Arithmetic::Wrapping); _loopExpression->accept(*this); + m_context.setArithmetic(previousArithmetic); + } appendCode() << "}\n"; appendCode() << "{\n"; diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h index 11f8b3c84..e7a478f2d 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.h +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h @@ -235,7 +235,8 @@ private: Expression const* _conditionExpression, Statement const* _initExpression = nullptr, ExpressionStatement const* _loopExpression = nullptr, - bool _isDoWhile = false + bool _isDoWhile = false, + bool _isSimpleCounterLoop = false ); static Type const& type(Expression const& _expression);