Coverage Report

Created: 2025-06-24 07:59

/src/solidity/libyul/optimiser/ControlFlowSimplifier.cpp
Line
Count
Source (jump to first uncovered line)
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
#include <libyul/optimiser/ControlFlowSimplifier.h>
19
#include <libyul/optimiser/Semantics.h>
20
#include <libyul/optimiser/OptimiserStep.h>
21
#include <libyul/AST.h>
22
#include <libyul/Utilities.h>
23
#include <libyul/Dialect.h>
24
#include <libsolutil/CommonData.h>
25
#include <libsolutil/Visitor.h>
26
27
#include <range/v3/action/remove_if.hpp>
28
29
using namespace solidity;
30
using namespace solidity::util;
31
using namespace solidity::yul;
32
33
using OptionalStatements = std::optional<std::vector<Statement>>;
34
35
namespace
36
{
37
38
ExpressionStatement makeDiscardCall(
39
  langutil::DebugData::ConstPtr const& _debugData,
40
  BuiltinHandle const& _discardFunction,
41
  Expression&& _expression
42
)
43
47.4k
{
44
47.4k
  return {_debugData, FunctionCall{
45
47.4k
    _debugData,
46
47.4k
    BuiltinName{_debugData, _discardFunction},
47
47.4k
    {std::move(_expression)}
48
47.4k
  }};
49
47.4k
}
50
51
void removeEmptyDefaultFromSwitch(Switch& _switchStmt)
52
193k
{
53
193k
  ranges::actions::remove_if(
54
193k
    _switchStmt.cases,
55
601k
    [](Case const& _case) { return !_case.value && _case.body.statements.empty(); }
56
193k
  );
57
193k
}
58
59
void removeEmptyCasesFromSwitch(Switch& _switchStmt)
60
193k
{
61
193k
  if (hasDefaultCase(_switchStmt))
62
120k
    return;
63
64
72.9k
  ranges::actions::remove_if(
65
72.9k
    _switchStmt.cases,
66
144k
    [](Case const& _case) { return _case.body.statements.empty(); }
67
72.9k
  );
68
72.9k
}
69
70
}
71
72
void ControlFlowSimplifier::run(OptimiserStepContext& _context, Block& _ast)
73
464k
{
74
464k
  ControlFlowSimplifier{_context.dialect}(_ast);
75
464k
}
76
77
void ControlFlowSimplifier::operator()(Block& _block)
78
7.09M
{
79
7.09M
  simplify(_block.statements);
80
7.09M
}
81
82
void ControlFlowSimplifier::operator()(FunctionDefinition& _funDef)
83
1.33M
{
84
1.33M
  ASTModifier::operator()(_funDef);
85
1.33M
  if (!_funDef.body.statements.empty() && std::holds_alternative<Leave>(_funDef.body.statements.back()))
86
5.90k
    _funDef.body.statements.pop_back();
87
1.33M
}
88
89
void ControlFlowSimplifier::visit(Statement& _st)
90
51.4M
{
91
51.4M
  if (std::holds_alternative<ForLoop>(_st))
92
1.16M
  {
93
1.16M
    ForLoop& forLoop = std::get<ForLoop>(_st);
94
1.16M
    yulAssert(forLoop.pre.statements.empty(), "");
95
96
1.16M
    size_t outerBreak = m_numBreakStatements;
97
1.16M
    size_t outerContinue = m_numContinueStatements;
98
1.16M
    m_numBreakStatements = 0;
99
1.16M
    m_numContinueStatements = 0;
100
101
1.16M
    ASTModifier::visit(_st);
102
103
1.16M
    if (!forLoop.body.statements.empty())
104
973k
    {
105
973k
      bool isTerminating = false;
106
973k
      TerminationFinder::ControlFlow controlFlow = TerminationFinder{m_dialect}.controlFlowKind(forLoop.body.statements.back());
107
973k
      if (controlFlow == TerminationFinder::ControlFlow::Break)
108
9.59k
      {
109
9.59k
        isTerminating = true;
110
9.59k
        --m_numBreakStatements;
111
9.59k
      }
112
964k
      else if (
113
964k
        controlFlow == TerminationFinder::ControlFlow::Terminate ||
114
964k
        controlFlow == TerminationFinder::ControlFlow::Leave
115
964k
      )
116
19.6k
        isTerminating = true;
117
118
973k
      if (isTerminating && m_numContinueStatements == 0 && m_numBreakStatements == 0)
119
8.19k
      {
120
8.19k
        If replacement{forLoop.debugData, std::move(forLoop.condition), std::move(forLoop.body)};
121
8.19k
        if (controlFlow == TerminationFinder::ControlFlow::Break)
122
3.79k
          replacement.body.statements.resize(replacement.body.statements.size() - 1);
123
8.19k
        _st = std::move(replacement);
124
8.19k
      }
125
973k
    }
126
127
1.16M
    m_numBreakStatements = outerBreak;
128
1.16M
    m_numContinueStatements = outerContinue;
129
1.16M
  }
130
50.2M
  else
131
50.2M
    ASTModifier::visit(_st);
132
51.4M
}
133
134
void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements)
135
7.14M
{
136
7.14M
  GenericVisitor visitor{
137
7.14M
    VisitorFallback<OptionalStatements>{},
138
7.14M
    [&](If& _ifStmt) -> OptionalStatements {
139
788k
      if (_ifStmt.body.statements.empty() && m_dialect.discardFunctionHandle())
140
23.3k
      {
141
23.3k
        OptionalStatements s = std::vector<Statement>{};
142
23.3k
        s->emplace_back(makeDiscardCall(
143
23.3k
          _ifStmt.debugData,
144
23.3k
          *m_dialect.discardFunctionHandle(),
145
23.3k
          std::move(*_ifStmt.condition)
146
23.3k
        ));
147
23.3k
        return s;
148
23.3k
      }
149
765k
      return {};
150
788k
    },
151
7.14M
    [&](Switch& _switchStmt) -> OptionalStatements {
152
193k
      removeEmptyDefaultFromSwitch(_switchStmt);
153
193k
      removeEmptyCasesFromSwitch(_switchStmt);
154
155
193k
      if (_switchStmt.cases.empty())
156
13.2k
        return reduceNoCaseSwitch(_switchStmt);
157
180k
      else if (_switchStmt.cases.size() == 1)
158
17.4k
        return reduceSingleCaseSwitch(_switchStmt);
159
160
162k
      return {};
161
193k
    }
162
7.14M
  };
163
7.14M
  iterateReplacing(
164
7.14M
    _statements,
165
7.14M
    [&](Statement& _stmt) -> OptionalStatements
166
51.4M
    {
167
51.4M
      OptionalStatements result = std::visit(visitor, _stmt);
168
51.4M
      if (result)
169
54.0k
        simplify(*result);
170
51.4M
      else
171
51.4M
        visit(_stmt);
172
51.4M
      return result;
173
51.4M
    }
174
7.14M
  );
175
7.14M
}
176
177
OptionalStatements ControlFlowSimplifier::reduceNoCaseSwitch(Switch& _switchStmt) const
178
13.2k
{
179
13.2k
  yulAssert(_switchStmt.cases.empty(), "Expected no case!");
180
13.2k
  std::optional<BuiltinHandle> discardFunctionHandle =
181
13.2k
    m_dialect.discardFunctionHandle();
182
13.2k
  if (!discardFunctionHandle)
183
0
    return {};
184
185
13.2k
  return make_vector<Statement>(makeDiscardCall(
186
13.2k
    debugDataOf(*_switchStmt.expression),
187
13.2k
    *discardFunctionHandle,
188
13.2k
    std::move(*_switchStmt.expression)
189
13.2k
  ));
190
13.2k
}
191
192
OptionalStatements ControlFlowSimplifier::reduceSingleCaseSwitch(Switch& _switchStmt) const
193
17.4k
{
194
17.4k
  yulAssert(_switchStmt.cases.size() == 1, "Expected only one case!");
195
196
17.4k
  auto& switchCase = _switchStmt.cases.front();
197
17.4k
  langutil::DebugData::ConstPtr debugData = debugDataOf(*_switchStmt.expression);
198
17.4k
  if (switchCase.value)
199
6.62k
  {
200
6.62k
    if (!m_dialect.equalityFunctionHandle())
201
0
      return {};
202
6.62k
    BuiltinName const builtinName{debugData, *m_dialect.equalityFunctionHandle()};
203
6.62k
    return make_vector<Statement>(If{
204
6.62k
      std::move(_switchStmt.debugData),
205
6.62k
      std::make_unique<Expression>(FunctionCall{
206
6.62k
        debugData,
207
6.62k
        builtinName,
208
6.62k
        {std::move(*switchCase.value), std::move(*_switchStmt.expression)}
209
6.62k
      }),
210
6.62k
      std::move(switchCase.body)
211
6.62k
    });
212
6.62k
  }
213
10.7k
  else
214
10.7k
  {
215
10.7k
    if (!m_dialect.discardFunctionHandle())
216
0
      return {};
217
218
10.7k
    return make_vector<Statement>(
219
10.7k
      makeDiscardCall(
220
10.7k
        debugData,
221
10.7k
        *m_dialect.discardFunctionHandle(),
222
10.7k
        std::move(*_switchStmt.expression)
223
10.7k
      ),
224
10.7k
      std::move(switchCase.body)
225
10.7k
    );
226
10.7k
  }
227
17.4k
}
228