Skip to content

Commit

Permalink
Update optimizer docs to include codegen-based optimizer section
Browse files Browse the repository at this point in the history
  • Loading branch information
matheusaaguiar committed Nov 2, 2023
1 parent 7df949e commit c5abb46
Showing 1 changed file with 72 additions and 4 deletions.
76 changes: 72 additions & 4 deletions docs/internals/optimizer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://github.com/ethereum/solidity/blob/develop/libevmasm/RuleList.h>`_
to opcodes. It also combines equal code sets and removes unused code.
Expand All @@ -20,17 +23,23 @@ 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
optimized Yul IR for a Solidity source. Similarly, one can use ``solc --strict-assembly --optimize``
for a stand-alone Yul mode.

.. note::
The `peephole optimizer <https://en.wikipedia.org/wiki/Peephole_optimization>`_ is always
Some optimizer steps, such as, for example, the `peephole optimizer <https://en.wikipedia.org/wiki/Peephole_optimization>`_
and the :ref:`unchecked loop increment optimizer <unchecked-loop-optimizer>` are always
enabled by default and can only be turned off via the :ref:`Standard JSON <compiler-api>`.

You can find more details on both optimizer modules and their optimization steps below.
You can find more details on the optimizer modules and their optimization steps below.

Benefits of Optimizing Solidity Code
====================================
Expand Down Expand Up @@ -1382,3 +1391,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.

0 comments on commit c5abb46

Please sign in to comment.