From 6ce4ff262b83a5eacd8543d0c9c4fd653cc8411c Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 6 Apr 2022 18:33:42 +0200 Subject: [PATCH] Yul Optimizer: Simplify start offset of zero-length operations. --- Changelog.md | 2 ++ libyul/optimiser/DataFlowAnalyzer.cpp | 2 +- libyul/optimiser/DataFlowAnalyzer.h | 2 +- libyul/optimiser/ExpressionSimplifier.cpp | 29 +++++++++++++++++++ libyul/optimiser/ExpressionSimplifier.h | 1 + .../side_effects_in_for_condition.yul | 4 +-- .../expressionSimplifier/zero_length_read.yul | 21 ++++++++++++++ 7 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 test/libyul/yulOptimizerTests/expressionSimplifier/zero_length_read.yul diff --git a/Changelog.md b/Changelog.md index a86643987..df185b8bc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,9 +8,11 @@ Compiler Features: * Yul IR Code Generation: Improved copy routines for arrays with packed storage layout. * Yul Optimizer: Add rule to convert `mod(mul(X, Y), A)` into `mulmod(X, Y, A)`, if `A` is a power of two. * Yul Optimizer: Add rule to convert `mod(add(X, Y), A)` into `addmod(X, Y, A)`, if `A` is a power of two. + * Yul Optimizer: Simplify the starting offset of zero-length operations to zero. * Code Generator: More efficient code for checked addition and subtraction. * Error Reporter: More readable and informative error/warning messages. + Bugfixes: * Commandline Interface: Disallow the following options outside of the compiler mode: ``--via-ir``,``--metadata-literal``, ``--metadata-hash``, ``--model-checker-show-unproved``, ``--model-checker-div-mod-no-slacks``, ``--model-checker-engine``, ``--model-checker-invariants``, ``--model-checker-solvers``, ``--model-checker-timeout``, ``--model-checker-contracts``, ``--model-checker-targets``. diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index 65cdf31c3..b6b5bc67d 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -398,7 +398,7 @@ bool DataFlowAnalyzer::inScope(YulString _variableName) const return false; } -optional DataFlowAnalyzer::valueOfIdentifier(YulString const& _name) +optional DataFlowAnalyzer::valueOfIdentifier(YulString const& _name) const { if (AssignedValue const* value = variableValue(_name)) if (Literal const* literal = get_if(value->value)) diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h index a8463360b..7589db859 100644 --- a/libyul/optimiser/DataFlowAnalyzer.h +++ b/libyul/optimiser/DataFlowAnalyzer.h @@ -146,7 +146,7 @@ protected: bool inScope(YulString _variableName) const; /// Returns the literal value of the identifier, if it exists. - std::optional valueOfIdentifier(YulString const& _name); + std::optional valueOfIdentifier(YulString const& _name) const; enum class StoreLoadLocation { Memory = 0, diff --git a/libyul/optimiser/ExpressionSimplifier.cpp b/libyul/optimiser/ExpressionSimplifier.cpp index 8c3a038f1..caf74cfb7 100644 --- a/libyul/optimiser/ExpressionSimplifier.cpp +++ b/libyul/optimiser/ExpressionSimplifier.cpp @@ -23,7 +23,11 @@ #include #include +#include #include +#include + +#include using namespace std; using namespace solidity; @@ -44,4 +48,29 @@ void ExpressionSimplifier::visit(Expression& _expression) [this](YulString _var) { return variableValue(_var); } )) _expression = match->action().toExpression(debugDataOf(_expression)); + + if (auto* functionCall = get_if(&_expression)) + if (optional instruction = toEVMInstruction(m_dialect, functionCall->functionName.name)) + for (auto op: evmasm::SemanticInformation::readWriteOperations(*instruction)) + if (op.startParameter && op.lengthParameter) + { + Expression& startArgument = functionCall->arguments.at(*op.startParameter); + Expression const& lengthArgument = functionCall->arguments.at(*op.lengthParameter); + if ( + knownToBeZero(lengthArgument) && + !knownToBeZero(startArgument) && + !holds_alternative(startArgument) + ) + startArgument = Literal{debugDataOf(startArgument), LiteralKind::Number, "0"_yulstring, {}}; + } +} + +bool ExpressionSimplifier::knownToBeZero(Expression const& _expression) const +{ + if (auto const* literal = get_if(&_expression)) + return valueOfLiteral(*literal) == 0; + else if (auto const* identifier = get_if(&_expression)) + return valueOfIdentifier(identifier->name) == 0; + else + return false; } diff --git a/libyul/optimiser/ExpressionSimplifier.h b/libyul/optimiser/ExpressionSimplifier.h index f9d4e8da5..4bef2dbce 100644 --- a/libyul/optimiser/ExpressionSimplifier.h +++ b/libyul/optimiser/ExpressionSimplifier.h @@ -52,6 +52,7 @@ public: private: explicit ExpressionSimplifier(Dialect const& _dialect): DataFlowAnalyzer(_dialect) {} + bool knownToBeZero(Expression const& _expression) const; }; } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/side_effects_in_for_condition.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/side_effects_in_for_condition.yul index bd567a69a..9c3a5c8ba 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/side_effects_in_for_condition.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/side_effects_in_for_condition.yul @@ -1,5 +1,5 @@ { - for {} div(create(0, 1, 0), shl(msize(), 1)) {} + for {} div(create(0, 1, 1), shl(msize(), 1)) {} { } } @@ -10,7 +10,7 @@ // // { // { -// for { } div(create(0, 1, 0), shl(msize(), 1)) { } +// for { } div(create(0, 1, 1), shl(msize(), 1)) { } // { } // } // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/zero_length_read.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/zero_length_read.yul new file mode 100644 index 000000000..ee7464932 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/zero_length_read.yul @@ -0,0 +1,21 @@ +{ + revert(calldataload(0), 0) + revert(call(0,0,0,0,0,0,0), 0) + calldatacopy(calldataload(1), calldataload(2), 0) + return(calldataload(3), 0) + codecopy(calldataload(4), calldataload(5), sub(42,42)) +} +// ---- +// step: expressionSimplifier +// +// { +// { +// let _1 := 0 +// revert(0, _1) +// pop(call(_1, _1, _1, _1, _1, _1, _1)) +// revert(0, _1) +// calldatacopy(0, calldataload(2), _1) +// return(0, _1) +// codecopy(0, calldataload(5), 0) +// } +// }