Special case code generation for for loops.

This commit is contained in:
Daniel Kirchner 2022-08-10 15:48:06 +02:00 committed by Matheus Aguiar
parent cc7a14a61d
commit d233c66795
8 changed files with 111 additions and 15 deletions

View File

@ -30,6 +30,7 @@ Language Features:
Compiler 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 ``--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: 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. * 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.

View File

@ -129,6 +129,17 @@ void PostTypeChecker::endVisit(ModifierInvocation const& _modifierInvocation)
callEndVisit(_modifierInvocation); callEndVisit(_modifierInvocation);
} }
bool PostTypeChecker::visit(ForStatement const& _forStatement)
{
return callVisit(_forStatement);
}
void PostTypeChecker::endVisit(ForStatement const& _forStatement)
{
callEndVisit(_forStatement);
}
namespace namespace
{ {
struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker 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<BinaryOperation const*>(_forStatement.condition());
if (!cond || cond->getOperator() != Token::LessThan || cond->userDefinedFunctionType())
return false;
if (!_forStatement.loopExpression())
return false;
auto const* post = dynamic_cast<UnaryOperation const*>(&_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<Identifier const*>(&cond->leftExpression());
auto const* lhsType = dynamic_cast<IntegerType const*>(cond->leftExpression().annotation().type);
auto const *commonType = dynamic_cast<IntegerType const*>(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) PostTypeChecker::PostTypeChecker(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter)
{ {
m_checkers.push_back(std::make_shared<ConstStateVarCircularReferenceChecker>(_errorReporter)); m_checkers.push_back(make_shared<ConstStateVarCircularReferenceChecker>(_errorReporter));
m_checkers.push_back(std::make_shared<OverrideSpecifierChecker>(_errorReporter)); m_checkers.push_back(make_shared<OverrideSpecifierChecker>(_errorReporter));
m_checkers.push_back(std::make_shared<ModifierContextChecker>(_errorReporter)); m_checkers.push_back(make_shared<ModifierContextChecker>(_errorReporter));
m_checkers.push_back(std::make_shared<EventOutsideEmitErrorOutsideRevertChecker>(_errorReporter)); m_checkers.push_back(make_shared<EventOutsideEmitErrorOutsideRevertChecker>(_errorReporter));
m_checkers.push_back(std::make_shared<NoVariablesInInterfaceChecker>(_errorReporter)); m_checkers.push_back(make_shared<NoVariablesInInterfaceChecker>(_errorReporter));
m_checkers.push_back(std::make_shared<ReservedErrorSelector>(_errorReporter)); m_checkers.push_back(make_shared<ReservedErrorSelector>(_errorReporter));
m_checkers.push_back(make_shared<SimpleCounterForLoopChecker>(_errorReporter));
} }

View File

@ -97,6 +97,9 @@ private:
bool visit(ModifierInvocation const& _modifierInvocation) override; bool visit(ModifierInvocation const& _modifierInvocation) override;
void endVisit(ModifierInvocation const& _modifierInvocation) override; void endVisit(ModifierInvocation const& _modifierInvocation) override;
bool visit(ForStatement const& _forStatement) override;
void endVisit(ForStatement const& _forStatement) override;
template <class T> template <class T>
bool callVisit(T const& _node) bool callVisit(T const& _node)
{ {

View File

@ -238,6 +238,7 @@ struct TryCatchClauseAnnotation: ASTAnnotation, ScopableAnnotation
struct ForStatementAnnotation: StatementAnnotation, ScopableAnnotation struct ForStatementAnnotation: StatementAnnotation, ScopableAnnotation
{ {
util::SetOnce<bool> isSimpleCounterLoop;
}; };
struct ReturnAnnotation: StatementAnnotation struct ReturnAnnotation: StatementAnnotation

View File

@ -742,12 +742,18 @@ bool ASTJsonExporter::visit(WhileStatement const& _node)
bool ASTJsonExporter::visit(ForStatement const& _node) bool ASTJsonExporter::visit(ForStatement const& _node)
{ {
setJsonNode(_node, "ForStatement", {
std::make_pair("initializationExpression", toJsonOrNull(_node.initializationExpression())), std::vector<pair<string, Json::Value>> attributes = {
std::make_pair("condition", toJsonOrNull(_node.condition())), make_pair("initializationExpression", toJsonOrNull(_node.initializationExpression())),
std::make_pair("loopExpression", toJsonOrNull(_node.loopExpression())), make_pair("condition", toJsonOrNull(_node.condition())),
std::make_pair("body", toJson(_node.body())) 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; return false;
} }

View File

@ -1245,7 +1245,16 @@ bool ContractCompiler::visit(ForStatement const& _forStatement)
// for's loop expression if existing // for's loop expression if existing
if (_forStatement.loopExpression()) 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); _forStatement.loopExpression()->accept(*this);
m_context.setArithmetic(previousArithmetic);
}
m_context.appendJumpTo(loopStart); m_context.appendJumpTo(loopStart);

View File

@ -617,7 +617,9 @@ bool IRGeneratorForStatements::visit(ForStatement const& _forStatement)
_forStatement.body(), _forStatement.body(),
_forStatement.condition(), _forStatement.condition(),
_forStatement.initializationExpression(), _forStatement.initializationExpression(),
_forStatement.loopExpression() _forStatement.loopExpression(),
false,
*_forStatement.annotation().isSimpleCounterLoop
); );
return false; return false;
@ -3192,7 +3194,8 @@ void IRGeneratorForStatements::generateLoop(
Expression const* _conditionExpression, Expression const* _conditionExpression,
Statement const* _initExpression, Statement const* _initExpression,
ExpressionStatement const* _loopExpression, ExpressionStatement const* _loopExpression,
bool _isDoWhile bool _isDoWhile,
bool _isSimpleCounterLoop
) )
{ {
std::string firstRun; std::string firstRun;
@ -3209,7 +3212,13 @@ void IRGeneratorForStatements::generateLoop(
_initExpression->accept(*this); _initExpression->accept(*this);
appendCode() << "} 1 {\n"; appendCode() << "} 1 {\n";
if (_loopExpression) if (_loopExpression)
{
Arithmetic previousArithmetic = m_context.arithmetic();
if (_isSimpleCounterLoop)
m_context.setArithmetic(Arithmetic::Wrapping);
_loopExpression->accept(*this); _loopExpression->accept(*this);
m_context.setArithmetic(previousArithmetic);
}
appendCode() << "}\n"; appendCode() << "}\n";
appendCode() << "{\n"; appendCode() << "{\n";

View File

@ -235,7 +235,8 @@ private:
Expression const* _conditionExpression, Expression const* _conditionExpression,
Statement const* _initExpression = nullptr, Statement const* _initExpression = nullptr,
ExpressionStatement const* _loopExpression = nullptr, ExpressionStatement const* _loopExpression = nullptr,
bool _isDoWhile = false bool _isDoWhile = false,
bool _isSimpleCounterLoop = false
); );
static Type const& type(Expression const& _expression); static Type const& type(Expression const& _expression);