Coverage Report

Created: 2022-08-24 06:38

/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 std;
38
using namespace solidity;
39
using namespace solidity::yul;
40
41
void ExpressionJoiner::run(OptimiserStepContext& _context, Block& _ast)
42
103k
{
43
103k
  ExpressionJoiner{_ast}(_ast);
44
103k
  FunctionGrouper::run(_context, _ast);
45
103k
}
46
47
48
void ExpressionJoiner::operator()(FunctionCall& _funCall)
49
3.42M
{
50
3.42M
  handleArguments(_funCall.arguments);
51
3.42M
}
52
53
void ExpressionJoiner::operator()(Block& _block)
54
1.43M
{
55
1.43M
  resetLatestStatementPointer();
56
6.37M
  for (size_t i = 0; i < _block.statements.size(); ++i)
57
4.93M
  {
58
4.93M
    visit(_block.statements[i]);
59
4.93M
    m_currentBlock = &_block;
60
4.93M
    m_latestStatementInBlock = i;
61
4.93M
  }
62
63
1.43M
  removeEmptyBlocks(_block);
64
1.43M
  resetLatestStatementPointer();
65
1.43M
}
66
67
void ExpressionJoiner::visit(Expression& _e)
68
9.37M
{
69
9.37M
  if (holds_alternative<Identifier>(_e))
70
2.81M
  {
71
2.81M
    Identifier const& identifier = std::get<Identifier>(_e);
72
2.81M
    if (isLatestStatementVarDeclJoinable(identifier))
73
247k
    {
74
247k
      VariableDeclaration& varDecl = std::get<VariableDeclaration>(*latestStatement());
75
247k
      _e = std::move(*varDecl.value);
76
77
      // Delete the variable declaration (also get the moved-from structure back into a sane state)
78
247k
      *latestStatement() = Block();
79
80
247k
      decrementLatestStatementPointer();
81
247k
    }
82
2.81M
  }
83
6.56M
  else
84
6.56M
    ASTModifier::visit(_e);
85
9.37M
}
86
87
ExpressionJoiner::ExpressionJoiner(Block& _ast)
88
103k
{
89
103k
  m_references = ReferencesCounter::countReferences(_ast);
90
103k
}
91
92
void ExpressionJoiner::handleArguments(vector<Expression>& _arguments)
93
3.42M
{
94
  // We have to fill from left to right, but we can only
95
  // fill if everything to the right is just an identifier
96
  // or a literal.
97
  // Also we only descend into function calls if everything
98
  // on the right is an identifier or literal.
99
100
3.42M
  size_t i = _arguments.size();
101
3.42M
  for (Expression const& arg: _arguments | ranges::views::reverse)
102
4.92M
  {
103
4.92M
    --i;
104
4.92M
    if (!holds_alternative<Identifier>(arg) && !holds_alternative<Literal>(arg))
105
248k
      break;
106
4.92M
  }
107
  // i points to the last element that is neither an identifier nor a literal,
108
  // or to the first element if all of them are identifiers or literals.
109
110
8.35M
  for (; i < _arguments.size(); ++i)
111
4.92M
    visit(_arguments.at(i));
112
3.42M
}
113
114
void ExpressionJoiner::decrementLatestStatementPointer()
115
247k
{
116
247k
  if (!m_currentBlock)
117
0
    return;
118
247k
  if (m_latestStatementInBlock > 0)
119
172k
    --m_latestStatementInBlock;
120
75.3k
  else
121
75.3k
    resetLatestStatementPointer();
122
247k
}
123
124
void ExpressionJoiner::resetLatestStatementPointer()
125
2.95M
{
126
2.95M
  m_currentBlock = nullptr;
127
2.95M
  m_latestStatementInBlock = numeric_limits<size_t>::max();
128
2.95M
}
129
130
Statement* ExpressionJoiner::latestStatement()
131
3.30M
{
132
3.30M
  if (!m_currentBlock)
133
361k
    return nullptr;
134
2.94M
  else
135
2.94M
    return &m_currentBlock->statements.at(m_latestStatementInBlock);
136
3.30M
}
137
138
bool ExpressionJoiner::isLatestStatementVarDeclJoinable(Identifier const& _identifier)
139
2.81M
{
140
2.81M
  Statement const* statement = latestStatement();
141
2.81M
  if (!statement || !holds_alternative<VariableDeclaration>(*statement))
142
1.82M
    return false;
143
986k
  VariableDeclaration const& varDecl = std::get<VariableDeclaration>(*statement);
144
986k
  if (varDecl.variables.size() != 1 || !varDecl.value)
145
1.44k
    return false;
146
985k
  assertThrow(varDecl.variables.size() == 1, OptimizerException, "");
147
985k
  assertThrow(varDecl.value, OptimizerException, "");
148
985k
  return varDecl.variables.at(0).name == _identifier.name && m_references[_identifier.name] == 1;
149
985k
}