From 90a147d3710c8c689aa6be6dc01352f8477811fd Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 29 Nov 2022 11:56:58 +0100 Subject: [PATCH] Update documentation. --- libyul/optimiser/UnusedAssignEliminator.h | 47 ++++++++++++----------- libyul/optimiser/UnusedStoreBase.h | 7 +++- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/libyul/optimiser/UnusedAssignEliminator.h b/libyul/optimiser/UnusedAssignEliminator.h index 75d9659c4..afcc6f00e 100644 --- a/libyul/optimiser/UnusedAssignEliminator.h +++ b/libyul/optimiser/UnusedAssignEliminator.h @@ -62,28 +62,34 @@ struct Dialect; * Detailed rules: * * The AST is traversed twice: in an information gathering step and in the - * actual removal step. During information gathering, we maintain a - * mapping from assignment statements to the three states - * "unused", "undecided" and "used". - * When an assignment is visited, it is added to the mapping in the "undecided" state - * (see remark about for loops below) and every other assignment to the same variable - * that is still in the "undecided" state is changed to "unused". - * When a variable is referenced, the state of any assignment to that variable still - * in the "undecided" state is changed to "used". - * At points where control flow splits, a copy - * of the mapping is handed over to each branch. At points where control flow - * joins, the two mappings coming from the two branches are combined in the following way: - * Statements that are only in one mapping or have the same state are used unchanged. - * Conflicting values are resolved in the following way: - * "unused", "undecided" -> "undecided" - * "unused", "used" -> "used" - * "undecided, "used" -> "used". + * actual removal step. During information gathering, assignment statements + * can be marked as "potentially unused" or as "used". + * + * When an assignment is visited, it is stored in the "set of all stores" and + * added to the branch-dependent "active" sets for the assigned variables. This active + * set for a variable contains all statements where that variable was last assigned to, i.e. + * where a read from that variable could read from. + * Furthermore, all other active sets for the assigned variables are cleared. + * + * When a reference to a variable is visited, the active assignments to that variable + * in the current branch are marked as "used". This mark is permanent. + * Also, the active set for this variable in the current branch is cleared. + * + * At points where control-flow splits, we maintain a copy of the active set + * (all other data structures are shared across branches). + * + * At control-flow joins, we combine the sets of active stores for each variable. + * + * In the example above, the active set right after the assignment "b := mload(a)" (but before + * the control-flow join) is "b := mload(a)"; the assignment "b := 2" was removed. + * After the control-flow join it will contain both "b := mload(a)" and "b := 2", coming from + * the two branches. * * For for-loops, the condition, body and post-part are visited twice, taking * the joining control-flow at the condition into account. * In other words, we create three control flow paths: Zero runs of the loop, * one run and two runs and then combine them at the end. - * Running at most twice is enough because there are only three different states. + * Running at most twice is enough because this takes into account all possible control-flow connections. * * Since this algorithm has exponential runtime in the nesting depth of for loops, * a shortcut is taken at a certain nesting level: We only use the zero- and @@ -95,12 +101,7 @@ struct Dialect; * * At ``leave`` statements, all return variables are set to "used". * - * When a variable goes out of scope, all statements still in the "undecided" - * state are changed to "unused", unless the variable is the return - * parameter of a function - there, the state changes to "used". - * - * In the second traversal, all assignments that are in the "unused" state are removed. - * + * In the second traversal, all assignments that are not marked as "used" are removed. * * This step is usually run right after the SSA transform to complete * the generation of the pseudo-SSA. diff --git a/libyul/optimiser/UnusedStoreBase.h b/libyul/optimiser/UnusedStoreBase.h index 7568620f7..0d618d8d2 100644 --- a/libyul/optimiser/UnusedStoreBase.h +++ b/libyul/optimiser/UnusedStoreBase.h @@ -38,8 +38,11 @@ struct Dialect; * * The class tracks the state of abstract "stores" (assignments or mstore/sstore * statements) across the control-flow. It is the job of the derived class to create - * the stores and track references, but the base class adjusts their "used state" at - * control-flow splits and joins. + * the stores and track references, but the base class manages control-flow splits and joins. + * + * In general, active stores are those where it has not yet been determined if they are used + * or not. Those are split and joined at control-flow forks. Once a store has been deemed + * used, it is removed from the active set and marked as used and this will never change. * * Prerequisite: Disambiguator, ForLoopInitRewriter. */