Coverage Report

Created: 2026-08-14 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libyul/optimiser/ExpressionSplitter.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 turns complex expressions into multiple variable
20
 * declarations.
21
 */
22
23
#include <libyul/optimiser/ExpressionSplitter.h>
24
25
#include <libyul/optimiser/OptimiserStep.h>
26
27
#include <libyul/AST.h>
28
#include <libyul/Dialect.h>
29
#include <libyul/Utilities.h>
30
31
#include <libsolutil/CommonData.h>
32
33
using namespace solidity;
34
using namespace solidity::yul;
35
using namespace solidity::util;
36
using namespace solidity::langutil;
37
38
ExpressionSplitter::ExpressionSplitter(Dialect const& _dialect, NameDispenser& _nameDispenser):
39
325k
  m_dialect(_dialect),
40
325k
  m_nameDispenser(_nameDispenser)
41
325k
{}
42
43
325k
ExpressionSplitter::~ExpressionSplitter() = default;
44
45
void ExpressionSplitter::run(OptimiserStepContext& _context, Block& _ast)
46
325k
{
47
325k
  ExpressionSplitter{_context.dialect, _context.dispenser}(_ast);
48
325k
}
49
50
void ExpressionSplitter::operator()(FunctionCall& _funCall)
51
7.92M
{
52
7.92M
  BuiltinFunction const* builtin = resolveBuiltinFunction(_funCall.functionName, m_dialect);
53
54
21.8M
  for (size_t i = _funCall.arguments.size(); i > 0; i--)
55
13.9M
    if (!builtin || !builtin->literalArgument(i - 1))
56
13.7M
      outlineExpression(_funCall.arguments[i - 1]);
57
7.92M
}
58
59
void ExpressionSplitter::operator()(If& _if)
60
356k
{
61
356k
  outlineExpression(*_if.condition);
62
356k
  (*this)(_if.body);
63
356k
}
64
65
void ExpressionSplitter::operator()(Switch& _switch)
66
94.5k
{
67
94.5k
  outlineExpression(*_switch.expression);
68
94.5k
  for (auto& _case: _switch.cases)
69
    // Do not visit the case expression, nothing to split there.
70
297k
    (*this)(_case.body);
71
94.5k
}
72
73
void ExpressionSplitter::operator()(ForLoop& _loop)
74
412k
{
75
412k
  (*this)(_loop.pre);
76
  // Do not visit the condition because we cannot split expressions there.
77
412k
  (*this)(_loop.post);
78
412k
  (*this)(_loop.body);
79
412k
}
80
81
void ExpressionSplitter::operator()(Block& _block)
82
3.22M
{
83
3.22M
  std::vector<Statement> saved;
84
3.22M
  swap(saved, m_statementsToPrefix);
85
86
3.22M
  std::function<std::optional<std::vector<Statement>>(Statement&)> f =
87
12.8M
      [&](Statement& _statement) -> std::optional<std::vector<Statement>> {
88
12.8M
    m_statementsToPrefix.clear();
89
12.8M
    visit(_statement);
90
12.8M
    if (m_statementsToPrefix.empty())
91
8.09M
      return {};
92
4.76M
    m_statementsToPrefix.emplace_back(std::move(_statement));
93
4.76M
    return std::move(m_statementsToPrefix);
94
12.8M
  };
95
3.22M
  iterateReplacing(_block.statements, f);
96
97
3.22M
  swap(saved, m_statementsToPrefix);
98
3.22M
}
99
100
void ExpressionSplitter::outlineExpression(Expression& _expr)
101
14.2M
{
102
14.2M
  if (std::holds_alternative<Identifier>(_expr))
103
4.06M
    return;
104
105
10.1M
  visit(_expr);
106
107
10.1M
  langutil::DebugData::ConstPtr debugData = debugDataOf(_expr);
108
10.1M
  YulName var = m_nameDispenser.newName({});
109
10.1M
  m_statementsToPrefix.emplace_back(VariableDeclaration{
110
10.1M
    debugData,
111
10.1M
    {{NameWithDebugData{debugData, var}}},
112
10.1M
    std::make_unique<Expression>(std::move(_expr))
113
10.1M
  });
114
10.1M
  _expr = Identifier{debugData, var};
115
10.1M
}
116