From 15c432c11de7c399a18b8b7d52016bb8e0175ba1 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Thu, 26 Oct 2023 00:27:28 -0300 Subject: [PATCH] Update optimizer docs to include codegen-based optimizer section --- docs/internals/optimizer.rst | 74 ++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index 15a25f960003..64c44506a819 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -5,8 +5,11 @@ The Optimizer ************* -The Solidity compiler uses two different optimizer modules: The "old" optimizer -that operates at the opcode level and the "new" optimizer that operates on Yul IR code. +The Solidity compiler involves optimizations at three different levels (in order of execution): + +- Optimizations during code generation based on a direct analysis of Solidity code. +- Optimizing transformations on the Yul IR code. +- Optimizations at the opcode level. The opcode-based optimizer applies a set of `simplification rules `_ to opcodes. It also combines equal code sets and removes unused code. @@ -20,6 +23,11 @@ the function calls. Similarly, if a function is side-effect free and its result is multiplied by zero, you can remove the function call completely. +The codegen-based optimizer consists of two steps, the first of which changes the +order of literals in binary operators, moving them to the right. The second one +enables the compiler to utilize unchecked arithmetic when generating code for +incrementing the counter variable in certain ``for`` loops. + Currently, the parameter ``--optimize`` activates the opcode-based optimizer for the generated bytecode and the Yul optimizer for the Yul code generated internally, for example for ABI coder v2. One can use ``solc --ir-optimized --optimize`` to produce an @@ -27,7 +35,8 @@ optimized Yul IR for a Solidity source. Similarly, one can use ``solc --strict-a for a stand-alone Yul mode. .. note:: - The `peephole optimizer `_ is always + Some optimizer steps, such as, for example, the `peephole optimizer `_ + and the :ref:`unchecked loop increment optimizer ` are always enabled by default and can only be turned off via the :ref:`Standard JSON `. .. note:: @@ -1388,3 +1397,62 @@ into } The LiteralRematerialiser should be run before this step. + +Codegen-Based Optimizer Module +============================== + +Currently, the codegen-based optimizer module consists of two distinct steps. + +The first step moves literals to the right of commutative binary operators which +helps exploit associativity. + +The second step uses unchecked arithmetic when generating code for incrementing +the counter variable of specific ``for`` loops. This +avoids wasting gas by identifying some conditions that guarantee +that the counter variable cannot overflow. This eliminates the need to use a verbose +unchecked arithmetic block inside the loop body to increment the counter variable. + +.. _unchecked-loop-optimizer: + +Unchecked Loop Increment +------------------------ + +Introduced in Solidity ``0.8.22``, the overflow check optimization step is concerned with identifying +the conditions under which the ``for`` loop counter can be safely incremented +without overflow checks. + +This optimization is **only** applied to ``for`` loops of the general form: + +.. code-block:: solidity + + for (uint i = X; i < Y; ++i) { + // variable i is not modified in the loop body + } + +The condition and the fact that the counter variable is only ever incremented +guarantee that it never overflows. +The precise requirements for the loop to be eligible for the optimization are as follows: + +- The loop condition is a comparison of the form ``i < Y``, for a local counter variable ``i`` + (called the "loop counter" hereon) and an expression ``Y``. +- The built-in operator ``<`` is necessarily used in the loop condition and is the only operator + that triggers the optimization. ``<=`` and the like are intentionally excluded. Additionally, + user-defined operators are **not** eligible. +- The loop expression is a prefix or postfix increment of the counter variable, i.e, ``i++`` or ``++i``. +- The loop counter is a local variable of a built-in integer type. +- The loop counter is **not** modified by the loop body or by the expression used as the loop condition. +- The comparison is performed on the same type as the loop counter, meaning that the type of the + right-hand-side expression is implicitly convertible to the type of the counter, such that the latter + is not implicitly widened before the comparison. + +To clarify the last condition, consider the following example: + +.. code-block:: solidity + + for (uint8 i = 0; i < uint16(1000); i++) { + // ... + } + +In this case, the counter ``i`` has its type implicitly converted from ``uint8`` +to ``uint16`` before the comparison and the condition is in fact never false, so +the overflow check for the increment cannot be removed.