Coverage Report

Created: 2025-09-04 07:34

/src/solidity/libyul/optimiser/ExpressionJoiner.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
/**
19
 * Optimiser component that undoes what the ExpressionSplitter did, i.e.
20
 * it more or less inlines variable declarations.
21
 */
22
23
#include <libyul/optimiser/ExpressionJoiner.h>
24
25
#include <libyul/optimiser/FunctionGrouper.h>
26
#include <libyul/optimiser/NameCollector.h>
27
#include <libyul/optimiser/OptimizerUtilities.h>
28
#include <libyul/Exceptions.h>
29
#include <libyul/AST.h>
30
31
#include <libsolutil/CommonData.h>
32
33
#include <range/v3/view/reverse.hpp>
34
35
#include <limits>
36
37
using namespace solidity;
38
using namespace solidity::yul;
39
40
void ExpressionJoiner::run(OptimiserStepContext& _context, Block& _ast)
41
257k
{
42
257k
  ExpressionJoiner{_ast}(_ast);
43
257k
  FunctionGrouper::run(_context, _ast);
44
257k
}
45
46
47
void ExpressionJoiner::operator()(FunctionCall& _funCall)
48
6.93M
{
49
6.93M
  handleArguments(_funCall.arguments);
50
6.93M
}
51
52
void ExpressionJoiner::operator()(Block& _block)
53
3.02M
{
54
3.02M
  resetLatestStatementPointer();
55
12.5M
  for (size_t i = 0; i < _block.statements.size(); ++i)
56
9.56M
  {
57
9.56M
    visit(_block.statements[i]);
58
9.56M
    m_currentBlock = &_block;
59
9.56M
    m_latestStatementInBlock = i;
60
9.56M
  }
61
62
3.02M
  removeEmptyBlocks(_block);
63
3.02M
  resetLatestStatementPointer();
64
3.02M
}
65
66
void ExpressionJoiner::visit(Expression& _e)
67
20.0M
{
68
20.0M
  if (std::holds_alternative<Identifier>(_e))
69
5.54M
  {
70
5.54M
    Identifier const& identifier = std::get<Identifier>(_e);
71
5.54M
    if (isLatestStatementVarDeclJoinable(identifier))
72
1.77M
    {
73
1.77M
      VariableDeclaration& varDecl = std::get<VariableDeclaration>(*latestStatement());
74
1.77M
      _e = std::move(*varDecl.value);
75
76
      // Delete the variable declaration (also get the moved-from structure back into a sane state)
77
1.77M
      *latestStatement() = Block();
78
79
1.77M
      decrementLatestStatementPointer();
80
1.77M
    }
81
5.54M
  }
82
14.5M
  else
83
14.5M
    ASTModifier::visit(_e);
84
20.0M
}
85
86
ExpressionJoiner::ExpressionJoiner(Block& _ast)
87
257k
{
88
257k
  m_references = VariableReferencesCounter::countReferences(_ast);
89
257k
}
90
91
void ExpressionJoiner::handleArguments(std::vector<Expression>& _arguments)
92
6.93M
{
93
  // We have to fill from left to right, but we can only
94
  // fill if everything to the right is just an identifier
95
  // or a literal.
96
  // Also we only descend into function calls if everything
97
  // on the right is an identifier or literal.
98
99
6.93M
  size_t i = _arguments.size();
100
6.93M
  for (Expression const& arg: _arguments | ranges::views::reverse)
101
11.4M
  {
102
11.4M
    --i;
103
11.4M
    if (!std::holds_alternative<Identifier>(arg) && !std::holds_alternative<Literal>(arg))
104
1.06M
      break;
105
11.4M
  }
106
  // i points to the last element that is neither an identifier nor a literal,
107
  // or to the first element if all of them are identifiers or literals.
108
109
18.3M
  for (; i < _arguments.size(); ++i)
110
11.4M
    visit(_arguments.at(i));
111
6.93M
}
112
113
void ExpressionJoiner::decrementLatestStatementPointer()
114
1.77M
{
115
1.77M
  if (!m_currentBlock)
116
0
    return;
117
1.77M
  if (m_latestStatementInBlock > 0)
118
1.47M
    --m_latestStatementInBlock;
119
293k
  else
120
293k
    resetLatestStatementPointer();
121
1.77M
}
122
123
void ExpressionJoiner::resetLatestStatementPointer()
124
6.34M
{
125
6.34M
  m_currentBlock = nullptr;
126
6.34M
  m_latestStatementInBlock = std::numeric_limits<size_t>::max();
127
6.34M
}
128
129
Statement* ExpressionJoiner::latestStatement()
130
9.09M
{
131
9.09M
  if (!m_currentBlock)
132
912k
    return nullptr;
133
8.17M
  else
134
8.17M
    return &m_currentBlock->statements.at(m_latestStatementInBlock);
135
9.09M
}
136
137
bool ExpressionJoiner::isLatestStatementVarDeclJoinable(Identifier const& _identifier)
138
5.54M
{
139
5.54M
  Statement const* statement = latestStatement();
140
5.54M
  if (!statement || !std::holds_alternative<VariableDeclaration>(*statement))
141
2.75M
    return false;
142
2.79M
  VariableDeclaration const& varDecl = std::get<VariableDeclaration>(*statement);
143
2.79M
  if (varDecl.variables.size() != 1 || !varDecl.value)
144
48.9k
    return false;
145
2.74M
  assertThrow(varDecl.variables.size() == 1, OptimizerException, "");
146
2.74M
  assertThrow(varDecl.value, OptimizerException, "");
147
2.74M
  return varDecl.variables.at(0).name == _identifier.name && m_references[_identifier.name] == 1;
148
2.74M
}