Coverage Report

Created: 2026-10-26 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libyul/optimiser/UnusedAssignEliminator.cpp
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
#include <libyul/optimiser/UnusedAssignEliminator.h>
24
25
#include <libyul/optimiser/Semantics.h>
26
#include <libyul/optimiser/OptimizerUtilities.h>
27
#include <libyul/ControlFlowSideEffectsCollector.h>
28
#include <libyul/Utilities.h>
29
#include <libyul/AST.h>
30
#include <libyul/AsmPrinter.h>
31
32
#include <libsolutil/CommonData.h>
33
34
#include <range/v3/action/remove_if.hpp>
35
36
#include <iostream>
37
38
using namespace solidity;
39
using namespace solidity::yul;
40
41
void UnusedAssignEliminator::run(OptimiserStepContext& _context, Block& _ast)
42
582k
{
43
582k
  UnusedAssignEliminator uae{
44
582k
    _context.dialect,
45
582k
    ControlFlowSideEffectsCollector{_context.dialect, _ast}.functionSideEffectsNamed()
46
582k
  };
47
582k
  uae(_ast);
48
49
582k
  uae.m_storesToRemove += uae.m_allStores - uae.m_usedStores;
50
51
582k
  std::set<Statement const*> toRemove{uae.m_storesToRemove.begin(), uae.m_storesToRemove.end()};
52
582k
  StatementRemover remover{toRemove};
53
582k
  remover(_ast);
54
582k
}
55
56
void UnusedAssignEliminator::operator()(Identifier const& _identifier)
57
103M
{
58
103M
  markUsed(_identifier.name);
59
103M
}
60
61
void UnusedAssignEliminator::operator()(Assignment const& _assignment)
62
3.84M
{
63
3.84M
  visit(*_assignment.value);
64
  // Do not visit the variables because they are Identifiers
65
3.84M
}
66
67
68
void UnusedAssignEliminator::operator()(FunctionDefinition const& _functionDefinition)
69
1.50M
{
70
1.50M
  ScopedSaveAndRestore outerReturnVariables(m_returnVariables, {});
71
72
1.50M
  for (auto const& retParam: _functionDefinition.returnVariables)
73
1.37M
    m_returnVariables.insert(retParam.name);
74
75
1.50M
  UnusedStoreBase::operator()(_functionDefinition);
76
1.50M
}
77
78
void UnusedAssignEliminator::operator()(FunctionCall const& _functionCall)
79
26.3M
{
80
26.3M
  UnusedStoreBase::operator()(_functionCall);
81
82
26.3M
  ControlFlowSideEffects sideEffects;
83
26.3M
  if (BuiltinFunction const* builtin = resolveBuiltinFunction(_functionCall.functionName, m_dialect))
84
22.2M
    sideEffects = builtin->controlFlowSideEffects;
85
4.01M
  else
86
4.01M
  {
87
4.01M
    yulAssert(std::holds_alternative<Identifier>(_functionCall.functionName));
88
4.01M
    sideEffects = m_controlFlowSideEffects.at(std::get<Identifier>(_functionCall.functionName).name);
89
4.01M
  }
90
91
26.3M
  if (!sideEffects.canContinue)
92
    // We do not return from the current function, so it is OK to also
93
    // clear the return variables.
94
619k
    m_activeStores.clear();
95
26.3M
}
96
97
void UnusedAssignEliminator::operator()(Leave const&)
98
216k
{
99
216k
  for (YulName name: m_returnVariables)
100
79.5k
    markUsed(name);
101
216k
  m_activeStores.clear();
102
216k
}
103
104
void UnusedAssignEliminator::operator()(Block const& _block)
105
11.4M
{
106
11.4M
  UnusedStoreBase::operator()(_block);
107
108
11.4M
  for (auto const& statement: _block.statements)
109
103M
    if (auto const* varDecl = std::get_if<VariableDeclaration>(&statement))
110
81.5M
      for (auto const& var: varDecl->variables)
111
82.2M
        m_activeStores.erase(var.name);
112
11.4M
}
113
114
void UnusedAssignEliminator::visit(Statement const& _statement)
115
103M
{
116
103M
  UnusedStoreBase::visit(_statement);
117
118
103M
  if (auto const* assignment = std::get_if<Assignment>(&_statement))
119
3.84M
  {
120
    // We do not remove assignments whose values might have side-effects,
121
    // but clear the active stores to the assigned variables in any case.
122
3.84M
    if (SideEffectsCollector{m_dialect, *assignment->value}.movable())
123
3.81M
    {
124
3.81M
      m_allStores.insert(&_statement);
125
3.81M
      for (auto const& var: assignment->variableNames)
126
3.81M
        m_activeStores[var.name] = {&_statement};
127
3.81M
    }
128
38.6k
    else
129
38.6k
      for (auto const& var: assignment->variableNames)
130
38.6k
        m_activeStores[var.name].clear();
131
3.84M
  }
132
103M
}
133
134
void UnusedAssignEliminator::shortcutNestedLoop(ActiveStores const& _zeroRuns)
135
805k
{
136
  // Shortcut to avoid horrible runtime:
137
  // Change all assignments that were newly introduced in the for loop to "used".
138
  // We do not have to do that with the "break" or "continue" paths, because
139
  // they will be joined later anyway.
140
141
805k
  for (auto& [variable, stores]: m_activeStores)
142
713k
  {
143
713k
    auto zeroIt = _zeroRuns.find(variable);
144
713k
    for (auto& assignment: stores)
145
751k
    {
146
751k
      if (zeroIt != _zeroRuns.end() && zeroIt->second.count(assignment))
147
194k
        continue;
148
557k
      m_usedStores.insert(assignment);
149
557k
    }
150
713k
  }
151
805k
}
152
153
void UnusedAssignEliminator::finalizeFunctionDefinition(FunctionDefinition const& _functionDefinition)
154
1.50M
{
155
1.50M
  for (auto const& retParam: _functionDefinition.returnVariables)
156
1.37M
    markUsed(retParam.name);
157
1.50M
}
158
159
void UnusedAssignEliminator::markUsed(YulName _variable)
160
105M
{
161
105M
  for (auto& assignment: m_activeStores[_variable])
162
3.38M
    m_usedStores.insert(assignment);
163
105M
  m_activeStores.erase(_variable);
164
105M
}