From 6c77a54f187eb2806d0fe8aa9c9e7236a26323ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 11 May 2024 14:04:16 +0200 Subject: [PATCH 1/5] Optimizer docs: add missing prerequisites, missing bits of extra info from step docstrings and remove outdated remarks about steps being EVM-specific --- docs/internals/optimizer.rst | 124 +++++++++++++++++++---- libyul/optimiser/ControlFlowSimplifier.h | 2 - libyul/optimiser/StructuralSimplifier.h | 2 - 3 files changed, 106 insertions(+), 22 deletions(-) diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index 604560554ad5..d7bd8b1ba795 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -411,6 +411,8 @@ its visibility and it is impossible to reference variables defined in a differen The benefit of this stage is that function definitions can be looked up more easily and functions can be optimized in isolation without having to traverse the AST completely. +Prerequisites: Disambiguator. + .. _function-grouper: FunctionGrouper @@ -431,6 +433,8 @@ and ``F`` is a list of function definitions such that no function contains a fun The benefit of this stage is that we always know where the list of functions begins. +Prerequisites: Disambiguator, FunctionHoister. + .. _for-loop-condition-into-body: ForLoopConditionIntoBody @@ -458,6 +462,12 @@ is transformed to This transformation can also be useful when paired with LoopInvariantCodeMotion, since invariants in the loop-invariant conditions can then be taken outside the loop. +Loops that already have a literal constant as iteration condition are not transformed. + +To avoid unnecessary rewriting, it is recommended to run this step after StructuralSimplifier. + +Prerequisites: Disambiguator. + .. _for-loop-init-rewriter: ForLoopInitRewriter @@ -484,6 +494,8 @@ is transformed to This eases the rest of the optimization process because we can ignore the complicated scoping rules of the ``for`` loop initialization block. +Prerequisites: Disambiguator. + .. _var-decl-initializer: VarDeclInitializer @@ -660,6 +672,8 @@ are run right before it, because then it does not generate excessive amounts of On the other hand, the CommonSubexpressionEliminator could be more efficient if run after the SSA transform. +Prerequisites: Disambiguator, ForLoopInitRewriter. + .. _unused-assign-eliminator: UnusedAssignEliminator @@ -774,6 +788,8 @@ In the second traversal, all assignments that are in the "unused" state are remo This step is usually run right after the SSA transform to complete the generation of the pseudo-SSA. +Prerequisites: Disambiguator, ForLoopInitRewriter. + Tools ----- @@ -807,6 +823,8 @@ in any of the control-flow paths. For instance, upon entering a ``for`` loop, all variables are cleared that will be assigned during the body or the post block. +Prerequisites: Disambiguator, ForLoopInitRewriter. + Expression-Scale Simplifications -------------------------------- @@ -832,14 +850,19 @@ value, one of them will always be unused. The UnusedPruner or the UnusedAssignEliminator will then be able to fully eliminate such variables. -This step is especially efficient if the ExpressionSplitter is run -before. If the code is in pseudo-SSA form, -the values of variables are available for a longer time and thus we -have a higher chance of expressions to be replaceable. +The longer the values of variables are available, the higher the chance of an expression being replaceable. +For this reason the step could potentially be more effective when used on code in pseudo-SSA form. +This property, however, is not easily destroyed once established, and in most cases it is enough +if SSATransform was used once at any point before running the step. +The difference may also not be very significant in practice. + +This step is especially efficient if the ExpressionSplitter is run before. The ExpressionSimplifier will be able to perform better replacements if the CommonSubexpressionEliminator was run right before it. +Prerequisites: Disambiguator, ForLoopInitRewriter. + .. _expression-simplifier: ExpressionSimplifier @@ -853,12 +876,15 @@ It tries to match patterns like ``X + 0`` on each subexpression. During the matching procedure, it resolves variables to their currently assigned expressions to be able to match more deeply nested patterns even when the code is in pseudo-SSA form. +In fact, running CommonSubexpressionEliminator before helps this component track equivalent subexpressions. Some of the patterns like ``X - X -> 0`` can only be applied as long as the expression ``X`` is movable, because otherwise it would remove its potential side-effects. Since variable references are always movable, even if their current value might not be, the ExpressionSimplifier is again more powerful -in split or pseudo-SSA form. +in :ref:`expression-split ` or :ref:`pseudo-SSA ` form. + +Prerequisites: Disambiguator, ForLoopInitRewriter. .. _literal-rematerialiser: @@ -875,6 +901,9 @@ LoadResolver Optimisation stage that replaces expressions of type ``sload(x)`` and ``mload(x)`` by the value currently stored in storage resp. memory, if known. +Also evaluates simple ``keccak256(a, c)`` when the value at memory location ``a`` is known and ``c`` +is a constant ``<= 32``. + Works best if the code is in SSA form. Prerequisites: Disambiguator, ForLoopInitRewriter. @@ -890,6 +919,8 @@ CircularReferencesPruner This stage removes functions that call each other but are neither externally referenced nor referenced from the outermost context. +Prerequisites: Disambiguator, FunctionHoister. + .. _conditional-simplifier: ConditionalSimplifier @@ -914,9 +945,9 @@ Future features: - allow replacements by ``1`` - take termination of user-defined functions into account -Works best with SSA form and if dead code removal has run before. +Works best with SSA form and if DeadCodeEliminator has run before. -Prerequisite: Disambiguator. +Prerequisites: Disambiguator. .. _conditional-unsimplifier: @@ -948,9 +979,7 @@ performs similar tasks that do depend on data flow. The ControlFlowSimplifier does record the presence or absence of ``break`` and ``continue`` statements during its traversal. -Prerequisite: Disambiguator, FunctionHoister, ForLoopInitRewriter. - -Important: Introduces EVM opcodes and thus can only be used on EVM code for now. +Prerequisites: Disambiguator, FunctionHoister, ForLoopInitRewriter. .. _dead-code-eliminator: @@ -985,6 +1014,9 @@ CommonSubexpressionEliminator, because SSA will make sure that the variables will not change and the CommonSubexpressionEliminator re-uses exactly the same variable if the value is known to be the same. +Works best on code without literal arguments, which is the case when the code is in the +:ref:`expression-split ` form. + Prerequisites: Disambiguator, ForLoopInitRewriter. .. _unused-pruner: @@ -1000,6 +1032,10 @@ but its value is discarded. All movable expression statements (expressions that are not assigned) are removed. +Does not remove circular references. + +Prerequisites: Disambiguator. + .. _structural-simplifier: StructuralSimplifier @@ -1018,6 +1054,10 @@ a structural level: This component uses the DataflowAnalyzer. +LiteralRematerialiser should be run before this step. + +Prerequisites: Disambiguator. + .. _block-flattener: BlockFlattener @@ -1055,6 +1095,8 @@ is transformed to As long as the code is disambiguated, this does not cause a problem because the scopes of variables can only grow. +Prerequisites: Disambiguator, FunctionGrouper. + .. _loop-invariant-code-motion: LoopInvariantCodeMotion @@ -1066,6 +1108,9 @@ declarations inside conditional branches will not be moved out of the loop. ExpressionSplitter and SSATransform should be run upfront to obtain better results. +The transformation will not move loop-invariant condition out of the condition block of the loop. +This can be addressed by running ForLoopConditionIntoBody beforehand. + Prerequisites: Disambiguator, ForLoopInitRewriter, FunctionHoister. @@ -1095,7 +1140,7 @@ optimization step is mainly useful for functions that would not be inlined. Prerequisites: Disambiguator, FunctionHoister. -LiteralRematerialiser is recommended as a prerequisite, even though it's not required for +LiteralRematerialiser is recommended before, even though it's not required for correctness. .. _unused-function-parameter-pruner: @@ -1103,7 +1148,7 @@ correctness. UnusedFunctionParameterPruner ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -This step removes unused parameters in a function. +This step removes unused parameters from function definitions. If a parameter is unused, like ``c`` and ``y`` in, ``function f(a,b,c) -> x, y { x := div(a,b) }``, we remove the parameter and create a new "linking" function as follows: @@ -1114,12 +1159,12 @@ remove the parameter and create a new "linking" function as follows: function f2(a,b,c) -> x, y { x := f(a,b) } and replace all references to ``f`` by ``f2``. -The inliner should be run afterwards to make sure that all references to ``f2`` are replaced by +FullInliner should be run afterwards to make sure that all references to ``f2`` are replaced by ``f``. -Prerequisites: Disambiguator, FunctionHoister, LiteralRematerialiser. +Prerequisites: Disambiguator, FunctionHoister. -The step LiteralRematerialiser is not required for correctness. It helps deal with cases such as: +The step LiteralRematerialiser is recommended but not required for correctness. It helps deal with cases such as: ``function f(x) -> y { revert(y, y} }`` where the literal ``y`` will be replaced by its value ``0``, allowing us to rewrite the function. @@ -1178,9 +1223,10 @@ If two functions are syntactically equivalent, while allowing variable renaming but not any re-ordering, then any reference to one of the functions is replaced by the other. - The actual removal of the function is performed by the UnusedPruner. +Prerequisites: Disambiguator, FunctionHoister. + Function Inlining ----------------- @@ -1207,7 +1253,7 @@ Example: The function to be inlined has the form of ``function f(...) -> r { r : The result of this inlining is always a single expression. -This component can only be used on sources with unique names. +Prerequisites: Disambiguator. .. _full-inliner: @@ -1222,6 +1268,24 @@ code than more efficient code. In some cases, though, inlining a function can have positive effects on subsequent optimizer steps. This is the case if one of the function arguments is a constant, for example. +The transform changes code of the form + +.. code-block:: none + + function f(a, b) -> c { /* ... */ } + let z := f(x, y) + +into + +.. code-block:: none + + function f(a, b) -> c { /* ... */ } + let f_b := y + let f_a := x + let f_c + /* ... code of f, with replacements: a -> f_a, b -> f_b, c -> f_c */ + let z := f_c + During inlining, a heuristic is used to tell if the function call should be inlined or not. The current heuristic does not inline into "large" functions unless @@ -1238,11 +1302,13 @@ we can run the optimizer on this specialized function. If it results in heavy gains, the specialized function is kept, otherwise the original function is used instead. -FunctionHoister and ExpressionSplitter are recommended as prerequisites since they make the step +FunctionHoister and ExpressionSplitter are recommended before this step since they make it more efficient, but are not required for correctness. In particular, function calls with other function calls as arguments are not inlined, but running ExpressionSplitter beforehand ensures that there are no such calls in the input. +Prerequisites: Disambiguator. + Cleanup ------- @@ -1263,8 +1329,24 @@ It does not make use of any information concerning the commutativity of the opco if moving the value of a variable to its place of use would change the order of any function call or opcode execution, the transformation is not performed. +Code of the form + +.. code-block:: none + + let a1 := mload(y) + let a2 := mul(x, 4) + sstore(a2, a1) + +is transformed into + +.. code-block:: none + + sstore(mul(x, 4), mload(y)) + Note that the component will not move the assigned value of a variable assignment or a variable that is referenced more than once. +The transformation is also not applied to loop conditions, because those are +evaluated with each loop. The snippet ``let x := add(0, 2) let y := mul(x, mload(2))`` is not transformed, because it would cause the order of the call to the opcodes ``add`` and @@ -1276,6 +1358,8 @@ Because of that, the snippet ``let x := add(0, 2) let y := mul(x, 3)`` is transformed to ``let y := mul(add(0, 2), 3)``, even though the ``add`` opcode would be executed after the evaluation of the literal ``3``. +Prerequisites: Disambiguator. + .. _ssa-reverser: SSAReverser @@ -1326,6 +1410,8 @@ by ``a`` (until ``a`` is re-assigned). The UnusedPruner will then eliminate the variable ``a_1`` altogether and thus fully reverse the SSATransform. +Prerequisites: Disambiguator. + .. _stack-compressor: StackCompressor @@ -1362,6 +1448,8 @@ which are always movable. If the value is very cheap or the variable was explicitly requested to be eliminated, the variable reference is replaced by its current value. +Prerequisites: Disambiguator, ForLoopInitRewriter. + .. _for-loop-condition-out-of-body: ForLoopConditionOutOfBody diff --git a/libyul/optimiser/ControlFlowSimplifier.h b/libyul/optimiser/ControlFlowSimplifier.h index 3ebbe7c9aeee..77953a10cca7 100644 --- a/libyul/optimiser/ControlFlowSimplifier.h +++ b/libyul/optimiser/ControlFlowSimplifier.h @@ -45,8 +45,6 @@ class TypeInfo; * and ``continue`` statements during its traversal. * * Prerequisite: Disambiguator, FunctionHoister, ForLoopInitRewriter. - * - * Important: Introduces EVM opcodes and thus can only be used on EVM code for now. */ class ControlFlowSimplifier: public ASTModifier { diff --git a/libyul/optimiser/StructuralSimplifier.h b/libyul/optimiser/StructuralSimplifier.h index 734b2e53af8e..295c92101b93 100644 --- a/libyul/optimiser/StructuralSimplifier.h +++ b/libyul/optimiser/StructuralSimplifier.h @@ -34,8 +34,6 @@ namespace solidity::yul * The LiteralRematerialiser should be run before this. * * Prerequisite: Disambiguator. - * - * Important: Can only be used on EVM code. */ class StructuralSimplifier: public ASTModifier { From a52085b05835aee866705ba9be88349d9c19b6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 25 Apr 2024 18:53:40 +0200 Subject: [PATCH 2/5] cheatsheet: List optimizer step dependencies --- docs/cheatsheet.rst | 41 ++++++++++++++++++++++++++++++++++++ docs/internals/optimizer.rst | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 6b69b81be34a..d56d09e46ba2 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -168,3 +168,44 @@ Modifiers - ``override``: States that this function, modifier or public state variable changes the behavior of a function or modifier in a base contract. +Optimizer Step Dependencies +=========================== + +=========================================== ===================================================== =============================================================== =========================================== +Step Hard prerequisites Steps that should run before Steps that should run after +=========================================== ===================================================== =============================================================== =========================================== +:ref:`a ` Disambiguator, ForLoopInitRewriter ExpressionSplitter, CommonSubexpressionEliminator UnusedAssignEliminator +:ref:`C ` Disambiguator SSATransform, DeadCodeEliminator +:ref:`c ` Disambiguator, ForLoopInitRewriter ExpressionSplitter, SSATransform [#]_ UnusedPruner, UnusedAssignEliminator +:ref:`D ` ForLoopInitRewriter, FunctionHoister, FunctionGrouper +:ref:`d ` +:ref:`E ` Disambiguator, ForLoopInitRewriter ExpressionSplitter, SSATransform, CommonSubexpressionEliminator +:ref:`e ` Disambiguator +:ref:`F ` Disambiguator, FunctionHoister LiteralRematerialiser +:ref:`f ` Disambiguator, FunctionGrouper +:ref:`g ` Disambiguator, FunctionHoister +:ref:`h ` Disambiguator +:ref:`I ` Disambiguator StructuralSimplifier +:ref:`i ` Disambiguator FunctionHoister, ExpressionSplitter +:ref:`j ` Disambiguator +:ref:`L ` Disambiguator, ForLoopInitRewriter SSATransform +:ref:`l ` Disambiguator, FunctionHoister +:ref:`M ` Disambiguator, ForLoopInitRewriter, FunctionHoister ExpressionSplitter, SSATransform, ForLoopConditionIntoBody +:ref:`m ` Disambiguator, ForLoopInitRewriter +:ref:`n ` Disambiguator, ForLoopInitRewriter, FunctionHoister +:ref:`O ` LiteralRematerialiser +:ref:`o ` Disambiguator +:ref:`p ` Disambiguator, FunctionHoister LiteralRematerialiser FullInliner, UnusedPruner +:ref:`r ` Disambiguator, ForLoopInitRewriter +:ref:`S ` Disambiguator, ForLoopInitRewriter SSATransform +:ref:`s ` Disambiguator, ForLoopInitRewriter ExpressionSplitter, SSATransform, CommonSubexpressionEliminator +:ref:`T ` Disambiguator, ForLoopInitRewriter +:ref:`t ` Disambiguator LiteralRematerialiser +:ref:`U ` +:ref:`u ` Disambiguator +:ref:`V ` Disambiguator CommonSubexpressionEliminator, UnusedPruner +:ref:`v ` Disambiguator, FunctionHoister UnusedPruner +:ref:`x ` ForLoopConditionIntoBody +=========================================== ===================================================== =============================================================== =========================================== + +.. [#] It is enough if SSATransform was used once at any point before running the step. diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index d7bd8b1ba795..20fa6ee9f4a5 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -1,4 +1,4 @@ -.. index:: optimizer, optimiser, common subexpression elimination, constant propagation +.. index:: ! optimizer, ! optimiser, ! common subexpression elimination, ! constant propagation .. _optimizer: ************* From 5e9af646c271f24be07c30efe354b30f4ff68428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 12 Feb 2024 13:48:13 +0100 Subject: [PATCH 3/5] Optimizer docs: Document LiteralRematerialiser and details of FullInliner's heuristic --- docs/internals/optimizer.rst | 40 +++++++++++++++++++++++++++++------- docs/yul.rst | 2 ++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index 20fa6ee9f4a5..c1a2751eb202 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -891,7 +891,12 @@ Prerequisites: Disambiguator, ForLoopInitRewriter. LiteralRematerialiser ^^^^^^^^^^^^^^^^^^^^^ -To be documented. +If a variable referenced in an expression is known to have a literal value, replaces that use of +the variable with the literal. + +This is mostly used so that other components do not have to rely on the DataflowAnalyzer. + +Prerequisites: Disambiguator, ForLoopInitRewriter. .. _load-resolver: @@ -1286,13 +1291,34 @@ into /* ... code of f, with replacements: a -> f_a, b -> f_b, c -> f_c */ let z := f_c -During inlining, a heuristic is used to tell if the function call -should be inlined or not. -The current heuristic does not inline into "large" functions unless -the called function is tiny. Functions that are only used once -are inlined, as well as medium-sized functions, while function -calls with constant arguments allow slightly larger functions. +During inlining, a heuristic is used to tell if the function call should be inlined or not. + +A function call will never be inlined if: + +- The function definition contains the ``leave`` opcode. +- The function is large and inlining is not *aggressive* (details below). +- The function definition contains any direct, recursive calls to itself or the call being inlined is recursive. + Note that indirect recursive calls (going through other functions) do not disqualify the function from inlining. +- Argument expressions passed into the call can potentially have side effects. + This check is very broad, rejecting anything that is not simply a literal or an identifier, since + the condition is easy to satisfy by running ExpressionSplitter beforehand. + +Otherwise, if any of the following conditions is met, the call is inlined unconditionally: + +- The function is tiny. +- The function is only ever called once. + +In the remaining cases the decision to inline or not depends on the size of the function. +The size used here is not the actual bytecode size (which is not known to the optimizer), but rather +a score calculated at the Yul level, which approximates that size. +Inlining is performed if the size does not exceed a predefined threshold, which is a little higher +if the inlining is aggressive and yet higher when the call has some constant arguments. + +The aggressive inlining is performed when: +- The :ref:`yul-memoryguard` is present. +- The function is not recursive (not even indirectly). +- The legacy Yul->EVM transform is not used (i.e. the target is not the ``homestead`` :ref:`EVM version `). In the future, we may include a backtracking component that, instead of inlining a function right away, only specializes it, diff --git a/docs/yul.rst b/docs/yul.rst index 58cfc992fd99..0d7de11ceae2 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -1023,6 +1023,8 @@ option. See :ref:`Using the Commandline Compiler ` for details about the Solidity linker. +.. _yul-memoryguard: + memoryguard ^^^^^^^^^^^ From 5c7f7141aee0e54f504244093ac1dbd753d62bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 27 Apr 2024 21:51:37 +0200 Subject: [PATCH 4/5] Optimizer docs: Add missing info about steps that destroy the SSA and expression-split forms --- docs/internals/optimizer.rst | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index c1a2751eb202..6f84f6676fc5 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -466,6 +466,8 @@ Loops that already have a literal constant as iteration condition are not transf To avoid unnecessary rewriting, it is recommended to run this step after StructuralSimplifier. +May destroy the :ref:`expression-split ` form. + Prerequisites: Disambiguator. .. _for-loop-init-rewriter: @@ -884,6 +886,8 @@ Since variable references are always movable, even if their current value might not be, the ExpressionSimplifier is again more powerful in :ref:`expression-split ` or :ref:`pseudo-SSA ` form. +Some of the simplifications destroy the :ref:`expression-split ` form. + Prerequisites: Disambiguator, ForLoopInitRewriter. .. _literal-rematerialiser: @@ -896,6 +900,10 @@ the variable with the literal. This is mostly used so that other components do not have to rely on the DataflowAnalyzer. +While the step destroys the :ref:`expression-split ` form in the strict sense, +it does not introduce any nested function calls, which may be good enough for many steps that require +this property. + Prerequisites: Disambiguator, ForLoopInitRewriter. .. _load-resolver: @@ -934,7 +942,7 @@ ConditionalSimplifier The ConditionalSimplifier inserts assignments to condition variables if the value can be determined from the control-flow. -Destroys SSA form. +Destroys the :ref:`SSA ` form. Currently, this tool is very limited, mostly because we do not yet have support for boolean types. Since conditions only check for expressions being nonzero, @@ -1384,6 +1392,8 @@ Because of that, the snippet ``let x := add(0, 2) let y := mul(x, 3)`` is transformed to ``let y := mul(add(0, 2), 3)``, even though the ``add`` opcode would be executed after the evaluation of the literal ``3``. +Destroys the :ref:`expression-split ` form. + Prerequisites: Disambiguator. .. _ssa-reverser: @@ -1436,6 +1446,8 @@ by ``a`` (until ``a`` is re-assigned). The UnusedPruner will then eliminate the variable ``a_1`` altogether and thus fully reverse the SSATransform. +Destroys the :ref:`SSA ` form. + Prerequisites: Disambiguator. .. _stack-compressor: @@ -1474,6 +1486,8 @@ which are always movable. If the value is very cheap or the variable was explicitly requested to be eliminated, the variable reference is replaced by its current value. +Destroys the :ref:`expression-split ` form. + Prerequisites: Disambiguator, ForLoopInitRewriter. .. _for-loop-condition-out-of-body: From 53b1c0b3878225cbc60927573936a065c9c5850f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 11 May 2024 19:22:25 +0200 Subject: [PATCH 5/5] Optimizer docs: Remove detailed info about hard prerequisites for each step - From user's perspective it's more useful to assume that all other steps implicitly depend on these prerequisites. - For development the info is still available in docstrings of steps. --- docs/cheatsheet.rst | 68 +++++++++++++++++----------------- docs/internals/optimizer.rst | 72 +++++++----------------------------- 2 files changed, 48 insertions(+), 92 deletions(-) diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index d56d09e46ba2..7af444fc4579 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -171,41 +171,41 @@ Modifiers Optimizer Step Dependencies =========================== -=========================================== ===================================================== =============================================================== =========================================== -Step Hard prerequisites Steps that should run before Steps that should run after -=========================================== ===================================================== =============================================================== =========================================== -:ref:`a ` Disambiguator, ForLoopInitRewriter ExpressionSplitter, CommonSubexpressionEliminator UnusedAssignEliminator -:ref:`C ` Disambiguator SSATransform, DeadCodeEliminator -:ref:`c ` Disambiguator, ForLoopInitRewriter ExpressionSplitter, SSATransform [#]_ UnusedPruner, UnusedAssignEliminator -:ref:`D ` ForLoopInitRewriter, FunctionHoister, FunctionGrouper +=========================================== =============================================================== =========================================== +Step Steps that should run before Steps that should run after +=========================================== =============================================================== =========================================== +:ref:`a ` ExpressionSplitter, CommonSubexpressionEliminator UnusedAssignEliminator +:ref:`C ` SSATransform, DeadCodeEliminator +:ref:`c ` ExpressionSplitter, SSATransform [#]_ UnusedPruner, UnusedAssignEliminator +:ref:`D ` :ref:`d ` -:ref:`E ` Disambiguator, ForLoopInitRewriter ExpressionSplitter, SSATransform, CommonSubexpressionEliminator -:ref:`e ` Disambiguator -:ref:`F ` Disambiguator, FunctionHoister LiteralRematerialiser -:ref:`f ` Disambiguator, FunctionGrouper -:ref:`g ` Disambiguator, FunctionHoister -:ref:`h ` Disambiguator -:ref:`I ` Disambiguator StructuralSimplifier -:ref:`i ` Disambiguator FunctionHoister, ExpressionSplitter -:ref:`j ` Disambiguator -:ref:`L ` Disambiguator, ForLoopInitRewriter SSATransform -:ref:`l ` Disambiguator, FunctionHoister -:ref:`M ` Disambiguator, ForLoopInitRewriter, FunctionHoister ExpressionSplitter, SSATransform, ForLoopConditionIntoBody -:ref:`m ` Disambiguator, ForLoopInitRewriter -:ref:`n ` Disambiguator, ForLoopInitRewriter, FunctionHoister -:ref:`O ` LiteralRematerialiser -:ref:`o ` Disambiguator -:ref:`p ` Disambiguator, FunctionHoister LiteralRematerialiser FullInliner, UnusedPruner -:ref:`r ` Disambiguator, ForLoopInitRewriter -:ref:`S ` Disambiguator, ForLoopInitRewriter SSATransform -:ref:`s ` Disambiguator, ForLoopInitRewriter ExpressionSplitter, SSATransform, CommonSubexpressionEliminator -:ref:`T ` Disambiguator, ForLoopInitRewriter -:ref:`t ` Disambiguator LiteralRematerialiser +:ref:`E ` ExpressionSplitter, SSATransform, CommonSubexpressionEliminator +:ref:`e ` +:ref:`F ` LiteralRematerialiser +:ref:`f ` +:ref:`g ` +:ref:`h ` +:ref:`I ` StructuralSimplifier +:ref:`i ` FunctionHoister, ExpressionSplitter +:ref:`j ` +:ref:`L ` SSATransform +:ref:`l ` +:ref:`M ` ExpressionSplitter, SSATransform, ForLoopConditionIntoBody +:ref:`m ` +:ref:`n ` +:ref:`O ` LiteralRematerialiser +:ref:`o ` +:ref:`p ` LiteralRematerialiser FullInliner, UnusedPruner +:ref:`r ` +:ref:`S ` SSATransform +:ref:`s ` ExpressionSplitter, SSATransform, CommonSubexpressionEliminator +:ref:`T ` +:ref:`t ` LiteralRematerialiser :ref:`U ` -:ref:`u ` Disambiguator -:ref:`V ` Disambiguator CommonSubexpressionEliminator, UnusedPruner -:ref:`v ` Disambiguator, FunctionHoister UnusedPruner -:ref:`x ` ForLoopConditionIntoBody -=========================================== ===================================================== =============================================================== =========================================== +:ref:`u ` +:ref:`V ` CommonSubexpressionEliminator, UnusedPruner +:ref:`v ` UnusedPruner +:ref:`x ` ForLoopConditionIntoBody +=========================================== =============================================================== =========================================== .. [#] It is enough if SSATransform was used once at any point before running the step. diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index 6f84f6676fc5..2c1be0ba9692 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -385,6 +385,20 @@ The preprocessing components perform transformations to get the program into a certain normal form that is easier to work with. This normal form is kept during the rest of the optimization process. +Some of the steps in this group should be considered prerequisites for all other components. +Currently these are: + +- :ref:`disambiguator` +- :ref:`function-hoister` +- :ref:`function-grouper` +- :ref:`for-loop-init-rewriter` + +Each one establishes a certain property that all other steps preserve and implicitly assume +to be always satisfied by the code. +Not satisfying the property will not only make optimization less effective but in some cases may also +lead to the produced code being wrong. +For this reason the optimizer always runs these prerequisites before applying the main sequence. + .. _disambiguator: Disambiguator @@ -411,8 +425,6 @@ its visibility and it is impossible to reference variables defined in a differen The benefit of this stage is that function definitions can be looked up more easily and functions can be optimized in isolation without having to traverse the AST completely. -Prerequisites: Disambiguator. - .. _function-grouper: FunctionGrouper @@ -433,8 +445,6 @@ and ``F`` is a list of function definitions such that no function contains a fun The benefit of this stage is that we always know where the list of functions begins. -Prerequisites: Disambiguator, FunctionHoister. - .. _for-loop-condition-into-body: ForLoopConditionIntoBody @@ -468,8 +478,6 @@ To avoid unnecessary rewriting, it is recommended to run this step after Structu May destroy the :ref:`expression-split ` form. -Prerequisites: Disambiguator. - .. _for-loop-init-rewriter: ForLoopInitRewriter @@ -496,8 +504,6 @@ is transformed to This eases the rest of the optimization process because we can ignore the complicated scoping rules of the ``for`` loop initialization block. -Prerequisites: Disambiguator. - .. _var-decl-initializer: VarDeclInitializer @@ -674,8 +680,6 @@ are run right before it, because then it does not generate excessive amounts of On the other hand, the CommonSubexpressionEliminator could be more efficient if run after the SSA transform. -Prerequisites: Disambiguator, ForLoopInitRewriter. - .. _unused-assign-eliminator: UnusedAssignEliminator @@ -790,8 +794,6 @@ In the second traversal, all assignments that are in the "unused" state are remo This step is usually run right after the SSA transform to complete the generation of the pseudo-SSA. -Prerequisites: Disambiguator, ForLoopInitRewriter. - Tools ----- @@ -825,8 +827,6 @@ in any of the control-flow paths. For instance, upon entering a ``for`` loop, all variables are cleared that will be assigned during the body or the post block. -Prerequisites: Disambiguator, ForLoopInitRewriter. - Expression-Scale Simplifications -------------------------------- @@ -863,8 +863,6 @@ This step is especially efficient if the ExpressionSplitter is run before. The ExpressionSimplifier will be able to perform better replacements if the CommonSubexpressionEliminator was run right before it. -Prerequisites: Disambiguator, ForLoopInitRewriter. - .. _expression-simplifier: ExpressionSimplifier @@ -888,8 +886,6 @@ in :ref:`expression-split ` or :ref:`pseudo-SSA ` form. -Prerequisites: Disambiguator, ForLoopInitRewriter. - .. _literal-rematerialiser: LiteralRematerialiser @@ -904,8 +900,6 @@ While the step destroys the :ref:`expression-split ` form i it does not introduce any nested function calls, which may be good enough for many steps that require this property. -Prerequisites: Disambiguator, ForLoopInitRewriter. - .. _load-resolver: LoadResolver @@ -919,8 +913,6 @@ is a constant ``<= 32``. Works best if the code is in SSA form. -Prerequisites: Disambiguator, ForLoopInitRewriter. - Statement-Scale Simplifications ------------------------------- @@ -932,8 +924,6 @@ CircularReferencesPruner This stage removes functions that call each other but are neither externally referenced nor referenced from the outermost context. -Prerequisites: Disambiguator, FunctionHoister. - .. _conditional-simplifier: ConditionalSimplifier @@ -960,8 +950,6 @@ Future features: Works best with SSA form and if DeadCodeEliminator has run before. -Prerequisites: Disambiguator. - .. _conditional-unsimplifier: ConditionalUnsimplifier @@ -992,8 +980,6 @@ performs similar tasks that do depend on data flow. The ControlFlowSimplifier does record the presence or absence of ``break`` and ``continue`` statements during its traversal. -Prerequisites: Disambiguator, FunctionHoister, ForLoopInitRewriter. - .. _dead-code-eliminator: DeadCodeEliminator @@ -1011,8 +997,6 @@ code and thus are considered reachable. Because variables declared in a ``for`` loop's init block have their scope extended to the loop body, we require ForLoopInitRewriter to run before this step. -Prerequisites: ForLoopInitRewriter, FunctionHoister, FunctionGrouper. - .. _equal-store-eliminator: EqualStoreEliminator @@ -1030,8 +1014,6 @@ variable if the value is known to be the same. Works best on code without literal arguments, which is the case when the code is in the :ref:`expression-split ` form. -Prerequisites: Disambiguator, ForLoopInitRewriter. - .. _unused-pruner: UnusedPruner @@ -1047,8 +1029,6 @@ All movable expression statements (expressions that are not assigned) are remove Does not remove circular references. -Prerequisites: Disambiguator. - .. _structural-simplifier: StructuralSimplifier @@ -1069,8 +1049,6 @@ This component uses the DataflowAnalyzer. LiteralRematerialiser should be run before this step. -Prerequisites: Disambiguator. - .. _block-flattener: BlockFlattener @@ -1108,8 +1086,6 @@ is transformed to As long as the code is disambiguated, this does not cause a problem because the scopes of variables can only grow. -Prerequisites: Disambiguator, FunctionGrouper. - .. _loop-invariant-code-motion: LoopInvariantCodeMotion @@ -1124,8 +1100,6 @@ ExpressionSplitter and SSATransform should be run upfront to obtain better resul The transformation will not move loop-invariant condition out of the condition block of the loop. This can be addressed by running ForLoopConditionIntoBody beforehand. -Prerequisites: Disambiguator, ForLoopInitRewriter, FunctionHoister. - Function-Level Optimizations ---------------------------- @@ -1151,8 +1125,6 @@ function ``f_1`` that takes only one argument, i.e., Other optimization steps will be able to make more simplifications to the function. The optimization step is mainly useful for functions that would not be inlined. -Prerequisites: Disambiguator, FunctionHoister. - LiteralRematerialiser is recommended before, even though it's not required for correctness. @@ -1175,8 +1147,6 @@ and replace all references to ``f`` by ``f2``. FullInliner should be run afterwards to make sure that all references to ``f2`` are replaced by ``f``. -Prerequisites: Disambiguator, FunctionHoister. - The step LiteralRematerialiser is recommended but not required for correctness. It helps deal with cases such as: ``function f(x) -> y { revert(y, y} }`` where the literal ``y`` will be replaced by its value ``0``, allowing us to rewrite the function. @@ -1225,8 +1195,6 @@ be read once we leave the function's scope, so the statement will be removed onl Best run in SSA form. -Prerequisites: Disambiguator, ForLoopInitRewriter. - .. _equivalent-function-combiner: EquivalentFunctionCombiner @@ -1238,8 +1206,6 @@ functions is replaced by the other. The actual removal of the function is performed by the UnusedPruner. -Prerequisites: Disambiguator, FunctionHoister. - Function Inlining ----------------- @@ -1266,8 +1232,6 @@ Example: The function to be inlined has the form of ``function f(...) -> r { r : The result of this inlining is always a single expression. -Prerequisites: Disambiguator. - .. _full-inliner: FullInliner @@ -1341,8 +1305,6 @@ more efficient, but are not required for correctness. In particular, function calls with other function calls as arguments are not inlined, but running ExpressionSplitter beforehand ensures that there are no such calls in the input. -Prerequisites: Disambiguator. - Cleanup ------- @@ -1394,8 +1356,6 @@ would be executed after the evaluation of the literal ``3``. Destroys the :ref:`expression-split ` form. -Prerequisites: Disambiguator. - .. _ssa-reverser: SSAReverser @@ -1448,8 +1408,6 @@ SSATransform. Destroys the :ref:`SSA ` form. -Prerequisites: Disambiguator. - .. _stack-compressor: StackCompressor @@ -1488,8 +1446,6 @@ the variable reference is replaced by its current value. Destroys the :ref:`expression-split ` form. -Prerequisites: Disambiguator, ForLoopInitRewriter. - .. _for-loop-condition-out-of-body: ForLoopConditionOutOfBody