Coverage Report

Created: 2022-08-24 06:43

/src/solidity/libyul/optimiser/UnusedAssignEliminator.h
Line
Count
Source
1
/*
2
  This file is part of solidity.
3
4
  solidity is free software: you can redistribute it and/or modify
5
  it under the terms of the GNU General Public License as published by
6
  the Free Software Foundation, either version 3 of the License, or
7
  (at your option) any later version.
8
9
  solidity is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  GNU General Public License for more details.
13
14
  You should have received a copy of the GNU General Public License
15
  along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
// SPDX-License-Identifier: GPL-3.0
18
/**
19
 * Optimiser component that removes assignments to variables that are not used
20
 * until they go out of scope or are re-assigned.
21
 */
22
23
#pragma once
24
25
#include <libyul/ASTForward.h>
26
#include <libyul/optimiser/ASTWalker.h>
27
#include <libyul/optimiser/OptimiserStep.h>
28
#include <libyul/optimiser/UnusedStoreBase.h>
29
30
#include <map>
31
#include <vector>
32
33
namespace solidity::yul
34
{
35
struct Dialect;
36
37
/**
38
 * Optimiser component that removes assignments to variables that are not used
39
 * until they go out of scope or are re-assigned. This component
40
 * respects the control-flow and takes it into account for removal.
41
 *
42
 * Example:
43
 *
44
 * {
45
 *   let a
46
 *   a := 1
47
 *   a := 2
48
 *   b := 2
49
 *   if calldataload(0)
50
 *   {
51
 *     b := mload(a)
52
 *   }
53
 *   a := b
54
 * }
55
 *
56
 * In the example, "a := 1" can be removed because the value from this assignment
57
 * is not used in any control-flow branch (it is replaced right away).
58
 * The assignment "a := 2" is also overwritten by "a := b" at the end,
59
 * but there is a control-flow path (through the condition body) which uses
60
 * the value from "a := 2" and thus, this assignment cannot be removed.
61
 *
62
 * Detailed rules:
63
 *
64
 * The AST is traversed twice: in an information gathering step and in the
65
 * actual removal step. During information gathering, we maintain a
66
 * mapping from assignment statements to the three states
67
 * "unused", "undecided" and "used".
68
 * When an assignment is visited, it is added to the mapping in the "undecided" state
69
 * (see remark about for loops below) and every other assignment to the same variable
70
 * that is still in the "undecided" state is changed to "unused".
71
 * When a variable is referenced, the state of any assignment to that variable still
72
 * in the "undecided" state is changed to "used".
73
 * At points where control flow splits, a copy
74
 * of the mapping is handed over to each branch. At points where control flow
75
 * joins, the two mappings coming from the two branches are combined in the following way:
76
 * Statements that are only in one mapping or have the same state are used unchanged.
77
 * Conflicting values are resolved in the following way:
78
 * "unused", "undecided" -> "undecided"
79
 * "unused", "used" -> "used"
80
 * "undecided, "used" -> "used".
81
 *
82
 * For for-loops, the condition, body and post-part are visited twice, taking
83
 * the joining control-flow at the condition into account.
84
 * In other words, we create three control flow paths: Zero runs of the loop,
85
 * one run and two runs and then combine them at the end.
86
 * Running at most twice is enough because there are only three different states.
87
 *
88
 * Since this algorithm has exponential runtime in the nesting depth of for loops,
89
 * a shortcut is taken at a certain nesting level: We only use the zero- and
90
 * once-run of the for loop and change any assignment that was newly introduced
91
 * in the for loop from to "used".
92
 *
93
 * For switch statements that have a "default"-case, there is no control-flow
94
 * part that skips the switch.
95
 *
96
 * At ``leave`` statements, all return variables are set to "used".
97
 *
98
 * When a variable goes out of scope, all statements still in the "undecided"
99
 * state are changed to "unused", unless the variable is the return
100
 * parameter of a function - there, the state changes to "used".
101
 *
102
 * In the second traversal, all assignments that are in the "unused" state are removed.
103
 *
104
 *
105
 * This step is usually run right after the SSA transform to complete
106
 * the generation of the pseudo-SSA.
107
 *
108
 * Prerequisite: Disambiguator, ForLoopInitRewriter.
109
 */
110
class UnusedAssignEliminator: public UnusedStoreBase
111
{
112
public:
113
  static constexpr char const* name{"UnusedAssignEliminator"};
114
  static void run(OptimiserStepContext&, Block& _ast);
115
116
128k
  explicit UnusedAssignEliminator(Dialect const& _dialect): UnusedStoreBase(_dialect) {}
117
118
  void operator()(Identifier const& _identifier) override;
119
  void operator()(VariableDeclaration const& _variableDeclaration) override;
120
  void operator()(Assignment const& _assignment) override;
121
  void operator()(FunctionDefinition const&) override;
122
  void operator()(Leave const&) override;
123
  void operator()(Block const& _block) override;
124
125
  using UnusedStoreBase::visit;
126
  void visit(Statement const& _statement) override;
127
128
private:
129
  void shortcutNestedLoop(TrackedStores const& _beforeLoop) override;
130
  void finalizeFunctionDefinition(FunctionDefinition const& _functionDefinition) override;
131
132
  void changeUndecidedTo(YulString _variable, State _newState);
133
  /// Called when a variable goes out of scope. Sets the state of all still undecided
134
  /// assignments to the final state. In this case, this also applies to pending
135
  /// break and continue TrackedStores.
136
  void finalize(YulString _variable, State _finalState);
137
138
139
  std::set<YulString> m_declaredVariables;
140
  std::set<YulString> m_returnVariables;
141
};
142
143
}