Skip to content

Commit 2b8c997

Browse files
ekpyronmatheusaaguiar
authored andcommitted
Special case code generation for for loops.
1 parent 1b5775a commit 2b8c997

File tree

61 files changed

+638
-220
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+638
-220
lines changed

Diff for: Changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Language Features:
55

66

77
Compiler Features:
8+
* Code Generator: Remove redundant overflow checks of certain ``for`` loops when the counter variable cannot overflow.
89
* Commandline Interface: Add ``--no-import-callback`` option that prevents the compiler from loading source files not given explicitly on the CLI or in Standard JSON input.
910
* Commandline Interface: Use proper severity and coloring also for error messages produced outside of the compilation pipeline.
1011
* EVM: Deprecate support for "homestead", "tangerineWhistle", "spuriousDragon" and "byzantium" EVM versions.

Diff for: docs/using-the-compiler.rst

+3
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,9 @@ Input Description
287287
"cse": false,
288288
// Optimize representation of literal numbers and strings in code.
289289
"constantOptimizer": false,
290+
// Use unchecked arithmetic when incrementing the counter of for loops
291+
// under certain circumstances. It is always on if no details are given.
292+
"simpleCounterForLoopUncheckedIncrement": true,
290293
// The new Yul optimizer. Mostly operates on the code of ABI coder v2
291294
// and inline assembly.
292295
// It is activated together with the global optimizer setting

Diff for: libsolidity/analysis/PostTypeChecker.cpp

+126
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
#include <liblangutil/SemVerHandler.h>
2525
#include <libsolutil/Algorithms.h>
2626
#include <libsolutil/FunctionSelector.h>
27+
#include <libyul/optimiser/ASTWalker.h>
28+
#include <libyul/AST.h>
29+
30+
#include <range/v3/algorithm/any_of.hpp>
2731

2832
#include <memory>
2933

@@ -129,6 +133,17 @@ void PostTypeChecker::endVisit(ModifierInvocation const& _modifierInvocation)
129133
callEndVisit(_modifierInvocation);
130134
}
131135

136+
137+
bool PostTypeChecker::visit(ForStatement const& _forStatement)
138+
{
139+
return callVisit(_forStatement);
140+
}
141+
142+
void PostTypeChecker::endVisit(ForStatement const& _forStatement)
143+
{
144+
callEndVisit(_forStatement);
145+
}
146+
132147
namespace
133148
{
134149
struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker
@@ -421,6 +436,116 @@ struct ReservedErrorSelector: public PostTypeChecker::Checker
421436
}
422437
};
423438

439+
class YulLValueChecker : public solidity::yul::ASTWalker
440+
{
441+
public:
442+
YulLValueChecker(ASTString const& _identifierName): m_identifierName(_identifierName) {}
443+
bool willBeWrittenTo() const { return m_willBeWrittenTo; }
444+
using solidity::yul::ASTWalker::operator();
445+
void operator()(solidity::yul::Assignment const& _assignment) override
446+
{
447+
if (m_willBeWrittenTo)
448+
return;
449+
450+
if (ranges::any_of(
451+
_assignment.variableNames,
452+
[&](auto const& yulIdentifier) { return yulIdentifier.name.str() == m_identifierName; }
453+
))
454+
m_willBeWrittenTo = true;
455+
}
456+
private:
457+
ASTString const& m_identifierName;
458+
bool m_willBeWrittenTo = false;
459+
};
460+
461+
class LValueChecker: public ASTConstVisitor
462+
{
463+
public:
464+
LValueChecker(Identifier const& _identifier):
465+
m_declaration(_identifier.annotation().referencedDeclaration)
466+
{}
467+
bool willBeWrittenTo() const { return m_willBeWrittenTo; }
468+
void endVisit(Identifier const& _identifier) override
469+
{
470+
if (m_willBeWrittenTo)
471+
return;
472+
473+
solAssert(_identifier.annotation().referencedDeclaration);
474+
if (
475+
*_identifier.annotation().referencedDeclaration == *m_declaration &&
476+
_identifier.annotation().willBeWrittenTo
477+
)
478+
m_willBeWrittenTo = true;
479+
}
480+
void endVisit(InlineAssembly const& _inlineAssembly) override
481+
{
482+
if (m_willBeWrittenTo)
483+
return;
484+
485+
YulLValueChecker yulChecker{m_declaration->name()};
486+
yulChecker(_inlineAssembly.operations());
487+
m_willBeWrittenTo = yulChecker.willBeWrittenTo();
488+
}
489+
private:
490+
Declaration const* m_declaration{};
491+
bool m_willBeWrittenTo = false;
492+
};
493+
494+
struct SimpleCounterForLoopChecker: public PostTypeChecker::Checker
495+
{
496+
SimpleCounterForLoopChecker(ErrorReporter& _errorReporter): Checker(_errorReporter) {}
497+
bool visit(ForStatement const& _forStatement) override
498+
{
499+
_forStatement.annotation().isSimpleCounterLoop = isSimpleCounterLoop(_forStatement);
500+
return true;
501+
}
502+
bool isSimpleCounterLoop(ForStatement const& _forStatement) const
503+
{
504+
auto const* simpleCondition = dynamic_cast<BinaryOperation const*>(_forStatement.condition());
505+
if (!simpleCondition || simpleCondition->getOperator() != Token::LessThan || simpleCondition->userDefinedFunctionType())
506+
return false;
507+
if (!_forStatement.loopExpression())
508+
return false;
509+
510+
auto const* simplePostExpression = dynamic_cast<UnaryOperation const*>(&_forStatement.loopExpression()->expression());
511+
// This matches both operators ++i and i++
512+
if (!simplePostExpression || simplePostExpression->getOperator() != Token::Inc || simplePostExpression->userDefinedFunctionType())
513+
return false;
514+
515+
auto const* lhsIdentifier = dynamic_cast<Identifier const*>(&simpleCondition->leftExpression());
516+
auto const* lhsIntegerType = dynamic_cast<IntegerType const*>(simpleCondition->leftExpression().annotation().type);
517+
auto const* commonIntegerType = dynamic_cast<IntegerType const*>(simpleCondition->annotation().commonType);
518+
519+
if (!lhsIdentifier || !lhsIntegerType || !commonIntegerType || *lhsIntegerType != *commonIntegerType)
520+
return false;
521+
522+
auto const* incExpressionIdentifier = dynamic_cast<Identifier const*>(&simplePostExpression->subExpression());
523+
if (
524+
!incExpressionIdentifier ||
525+
incExpressionIdentifier->annotation().referencedDeclaration != lhsIdentifier->annotation().referencedDeclaration
526+
)
527+
return false;
528+
529+
solAssert(incExpressionIdentifier->annotation().referencedDeclaration);
530+
if (
531+
auto const* incVariableDeclaration = dynamic_cast<VariableDeclaration const*>(
532+
incExpressionIdentifier->annotation().referencedDeclaration
533+
);
534+
incVariableDeclaration &&
535+
!incVariableDeclaration->isLocalVariable()
536+
)
537+
return false;
538+
539+
solAssert(lhsIdentifier);
540+
LValueChecker lValueChecker{*lhsIdentifier};
541+
simpleCondition->rightExpression().accept(lValueChecker);
542+
if (!lValueChecker.willBeWrittenTo())
543+
_forStatement.body().accept(lValueChecker);
544+
545+
return !lValueChecker.willBeWrittenTo();
546+
}
547+
};
548+
424549
}
425550

426551

@@ -432,4 +557,5 @@ PostTypeChecker::PostTypeChecker(langutil::ErrorReporter& _errorReporter): m_err
432557
m_checkers.push_back(std::make_shared<EventOutsideEmitErrorOutsideRevertChecker>(_errorReporter));
433558
m_checkers.push_back(std::make_shared<NoVariablesInInterfaceChecker>(_errorReporter));
434559
m_checkers.push_back(std::make_shared<ReservedErrorSelector>(_errorReporter));
560+
m_checkers.push_back(std::make_shared<SimpleCounterForLoopChecker>(_errorReporter));
435561
}

Diff for: libsolidity/analysis/PostTypeChecker.h

+3
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ class PostTypeChecker: private ASTConstVisitor
9797
bool visit(ModifierInvocation const& _modifierInvocation) override;
9898
void endVisit(ModifierInvocation const& _modifierInvocation) override;
9999

100+
bool visit(ForStatement const& _forStatement) override;
101+
void endVisit(ForStatement const& _forStatement) override;
102+
100103
template <class T>
101104
bool callVisit(T const& _node)
102105
{

Diff for: libsolidity/ast/ASTAnnotations.h

+1
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ struct TryCatchClauseAnnotation: ASTAnnotation, ScopableAnnotation
238238

239239
struct ForStatementAnnotation: StatementAnnotation, ScopableAnnotation
240240
{
241+
util::SetOnce<bool> isSimpleCounterLoop;
241242
};
242243

243244
struct ReturnAnnotation: StatementAnnotation

Diff for: libsolidity/ast/ASTJsonExporter.cpp

+8-2
Original file line numberDiff line numberDiff line change
@@ -742,12 +742,18 @@ bool ASTJsonExporter::visit(WhileStatement const& _node)
742742

743743
bool ASTJsonExporter::visit(ForStatement const& _node)
744744
{
745-
setJsonNode(_node, "ForStatement", {
745+
746+
std::vector<std::pair<std::string, Json::Value>> attributes = {
746747
std::make_pair("initializationExpression", toJsonOrNull(_node.initializationExpression())),
747748
std::make_pair("condition", toJsonOrNull(_node.condition())),
748749
std::make_pair("loopExpression", toJsonOrNull(_node.loopExpression())),
749750
std::make_pair("body", toJson(_node.body()))
750-
});
751+
};
752+
753+
if (_node.annotation().isSimpleCounterLoop.set())
754+
attributes.emplace_back("isSimpleCounterLoop", *_node.annotation().isSimpleCounterLoop);
755+
756+
setJsonNode(_node, "ForStatement", std::move(attributes));
751757
return false;
752758
}
753759

Diff for: libsolidity/codegen/ContractCompiler.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -1245,7 +1245,16 @@ bool ContractCompiler::visit(ForStatement const& _forStatement)
12451245

12461246
// for's loop expression if existing
12471247
if (_forStatement.loopExpression())
1248+
{
1249+
Arithmetic previousArithmetic = m_context.arithmetic();
1250+
if (
1251+
*_forStatement.annotation().isSimpleCounterLoop &&
1252+
m_optimiserSettings.simpleCounterForLoopUncheckedIncrement
1253+
)
1254+
m_context.setArithmetic(Arithmetic::Wrapping);
12481255
_forStatement.loopExpression()->accept(*this);
1256+
m_context.setArithmetic(previousArithmetic);
1257+
}
12491258

12501259
m_context.appendJumpTo(loopStart);
12511260

Diff for: libsolidity/codegen/ir/IRGenerator.cpp

+7-7
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ std::string IRGenerator::generate(
224224

225225
std::string IRGenerator::generate(Block const& _block)
226226
{
227-
IRGeneratorForStatements generator(m_context, m_utils);
227+
IRGeneratorForStatements generator(m_context, m_utils, m_optimiserSettings);
228228
generator.generate(_block);
229229
return generator.code();
230230
}
@@ -450,7 +450,7 @@ std::string IRGenerator::generateModifier(
450450
(!_modifierInvocation.arguments() || _modifierInvocation.arguments()->empty()),
451451
""
452452
);
453-
IRGeneratorForStatements expressionEvaluator(m_context, m_utils);
453+
IRGeneratorForStatements expressionEvaluator(m_context, m_utils, m_optimiserSettings);
454454
if (_modifierInvocation.arguments())
455455
for (size_t i = 0; i < _modifierInvocation.arguments()->size(); i++)
456456
{
@@ -465,7 +465,7 @@ std::string IRGenerator::generateModifier(
465465
}
466466

467467
t("evalArgs", expressionEvaluator.code());
468-
IRGeneratorForStatements generator(m_context, m_utils, [&]() {
468+
IRGeneratorForStatements generator(m_context, m_utils, m_optimiserSettings, [&]() {
469469
std::string ret = joinHumanReadable(retParams);
470470
return
471471
(ret.empty() ? "" : ret + " := ") +
@@ -575,7 +575,7 @@ std::string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
575575
dispenseLocationComment(m_context.mostDerivedContract())
576576
)
577577
("functionName", functionName)
578-
("constantValueFunction", IRGeneratorForStatements(m_context, m_utils).constantValueFunction(_varDecl))
578+
("constantValueFunction", IRGeneratorForStatements(m_context, m_utils, m_optimiserSettings).constantValueFunction(_varDecl))
579579
("ret", suffixedVariableNameList("ret_", 0, _varDecl.type()->sizeOnStack()))
580580
.render();
581581
}
@@ -750,7 +750,7 @@ std::string IRGenerator::generateExternalFunction(ContractDefinition const& _con
750750

751751
std::string IRGenerator::generateInitialAssignment(VariableDeclaration const& _varDecl)
752752
{
753-
IRGeneratorForStatements generator(m_context, m_utils);
753+
IRGeneratorForStatements generator(m_context, m_utils, m_optimiserSettings);
754754
generator.initializeLocalVar(_varDecl);
755755
return generator.code();
756756
}
@@ -799,7 +799,7 @@ std::pair<std::string, std::map<ContractDefinition const*, std::vector<std::stri
799799
modifier->arguments()
800800
).second, "");
801801

802-
IRGeneratorForStatements generator{m_context, m_utils};
802+
IRGeneratorForStatements generator{m_context, m_utils, m_optimiserSettings};
803803
for (auto&& [baseContract, arguments]: baseConstructorArguments)
804804
{
805805
solAssert(baseContract && arguments, "");
@@ -820,7 +820,7 @@ std::pair<std::string, std::map<ContractDefinition const*, std::vector<std::stri
820820

821821
std::string IRGenerator::initStateVariables(ContractDefinition const& _contract)
822822
{
823-
IRGeneratorForStatements generator{m_context, m_utils};
823+
IRGeneratorForStatements generator{m_context, m_utils, m_optimiserSettings};
824824
for (VariableDeclaration const* variable: _contract.stateVariables())
825825
if (!variable->isConstant())
826826
generator.initializeStateVar(*variable);

Diff for: libsolidity/codegen/ir/IRGenerator.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <libsolidity/ast/CallGraph.h>
2828
#include <libsolidity/codegen/ir/IRGenerationContext.h>
2929
#include <libsolidity/codegen/YulUtilFunctions.h>
30+
#include <libsolidity/interface/OptimiserSettings.h>
3031

3132
#include <liblangutil/CharStreamProvider.h>
3233
#include <liblangutil/EVMVersion.h>
@@ -51,7 +52,8 @@ class IRGenerator
5152
RevertStrings _revertStrings,
5253
std::map<std::string, unsigned> _sourceIndices,
5354
langutil::DebugInfoSelection const& _debugInfoSelection,
54-
langutil::CharStreamProvider const* _soliditySourceProvider
55+
langutil::CharStreamProvider const* _soliditySourceProvider,
56+
OptimiserSettings& _optimiserSettings
5557
):
5658
m_evmVersion(_evmVersion),
5759
m_eofVersion(_eofVersion),
@@ -63,7 +65,8 @@ class IRGenerator
6365
_debugInfoSelection,
6466
_soliditySourceProvider
6567
),
66-
m_utils(_evmVersion, m_context.revertStrings(), m_context.functionCollector())
68+
m_utils(_evmVersion, m_context.revertStrings(), m_context.functionCollector()),
69+
m_optimiserSettings(_optimiserSettings)
6770
{}
6871

6972
/// Generates and returns (unoptimized) IR code.
@@ -141,6 +144,7 @@ class IRGenerator
141144

142145
IRGenerationContext m_context;
143146
YulUtilFunctions m_utils;
147+
OptimiserSettings m_optimiserSettings;
144148
};
145149

146150
}

Diff for: libsolidity/codegen/ir/IRGeneratorForStatements.cpp

+12-3
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ std::string IRGeneratorForStatements::constantValueFunction(VariableDeclaration
352352
)");
353353
templ("sourceLocationComment", dispenseLocationComment(_constant, m_context));
354354
templ("functionName", functionName);
355-
IRGeneratorForStatements generator(m_context, m_utils);
355+
IRGeneratorForStatements generator(m_context, m_utils, m_optimiserSettings);
356356
solAssert(_constant.value());
357357
Type const& constantType = *_constant.type();
358358
templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList());
@@ -617,7 +617,9 @@ bool IRGeneratorForStatements::visit(ForStatement const& _forStatement)
617617
_forStatement.body(),
618618
_forStatement.condition(),
619619
_forStatement.initializationExpression(),
620-
_forStatement.loopExpression()
620+
_forStatement.loopExpression(),
621+
false, // _isDoWhile
622+
*_forStatement.annotation().isSimpleCounterLoop
621623
);
622624

623625
return false;
@@ -3192,7 +3194,8 @@ void IRGeneratorForStatements::generateLoop(
31923194
Expression const* _conditionExpression,
31933195
Statement const* _initExpression,
31943196
ExpressionStatement const* _loopExpression,
3195-
bool _isDoWhile
3197+
bool _isDoWhile,
3198+
bool _isSimpleCounterLoop
31963199
)
31973200
{
31983201
std::string firstRun;
@@ -3209,7 +3212,13 @@ void IRGeneratorForStatements::generateLoop(
32093212
_initExpression->accept(*this);
32103213
appendCode() << "} 1 {\n";
32113214
if (_loopExpression)
3215+
{
3216+
Arithmetic previousArithmetic = m_context.arithmetic();
3217+
if (m_optimiserSettings.simpleCounterForLoopUncheckedIncrement && _isSimpleCounterLoop)
3218+
m_context.setArithmetic(Arithmetic::Wrapping);
32123219
_loopExpression->accept(*this);
3220+
m_context.setArithmetic(previousArithmetic);
3221+
}
32133222
appendCode() << "}\n";
32143223
appendCode() << "{\n";
32153224

0 commit comments

Comments
 (0)