Coverage Report

Created: 2026-09-14 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libevmasm/SimplificationRules.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
 * @file ExpressionClasses.cpp
20
 * @author Christian <c@ethdev.com>
21
 * @date 2015
22
 * Container for equivalence classes of expressions for use in common subexpression elimination.
23
 */
24
25
#include <libevmasm/SimplificationRules.h>
26
27
#include <libevmasm/ExpressionClasses.h>
28
#include <libevmasm/Assembly.h>
29
#include <libevmasm/RuleList.h>
30
#include <libsolutil/Assertions.h>
31
32
#include <utility>
33
#include <functional>
34
35
using namespace solidity;
36
using namespace solidity::evmasm;
37
using namespace solidity::langutil;
38
39
SimplificationRule<Pattern> const* Rules::findFirstMatch(
40
  Expression const& _expr,
41
  ExpressionClasses const& _classes
42
)
43
10.2M
{
44
10.2M
  resetMatchGroups();
45
46
10.2M
  assertThrow(_expr.item, OptimizerException, "");
47
10.2M
  for (auto const& rule: m_rules[uint8_t(_expr.item->instruction())])
48
53.9M
  {
49
53.9M
    if (rule.pattern.matches(_expr, _classes))
50
4.91M
      if (!rule.feasible || rule.feasible())
51
4.84M
        return &rule;
52
53
49.1M
    resetMatchGroups();
54
49.1M
  }
55
5.37M
  return nullptr;
56
10.2M
}
57
58
bool Rules::isInitialized() const
59
23.2M
{
60
23.2M
  return !m_rules[uint8_t(Instruction::ADD)].empty();
61
23.2M
}
62
63
void Rules::addRules(std::vector<SimplificationRule<Pattern>> const& _rules)
64
3
{
65
3
  for (auto const& r: _rules)
66
1.37k
    addRule(r);
67
3
}
68
69
void Rules::addRule(SimplificationRule<Pattern> const& _rule)
70
1.37k
{
71
1.37k
  m_rules[uint8_t(_rule.pattern.instruction())].push_back(_rule);
72
1.37k
}
73
74
Rules::Rules()
75
3
{
76
  // Multiple occurrences of one of these inside one rule must match the same equivalence class.
77
  // Constants.
78
3
  Pattern A(Push);
79
3
  Pattern B(Push);
80
3
  Pattern C(Push);
81
  // Anything.
82
3
  Pattern W;
83
3
  Pattern X;
84
3
  Pattern Y;
85
3
  Pattern Z;
86
3
  A.setMatchGroup(1, m_matchGroups);
87
3
  B.setMatchGroup(2, m_matchGroups);
88
3
  C.setMatchGroup(3, m_matchGroups);
89
3
  W.setMatchGroup(4, m_matchGroups);
90
3
  X.setMatchGroup(5, m_matchGroups);
91
3
  Y.setMatchGroup(6, m_matchGroups);
92
3
  Z.setMatchGroup(7, m_matchGroups);
93
94
3
  addRules(simplificationRuleList(std::nullopt, A, B, C, W, X, Y, Z));
95
3
  assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized.");
96
3
}
97
98
Pattern::Pattern(Instruction _instruction, std::initializer_list<Pattern> _arguments):
99
630k
  m_type(Operation),
100
630k
  m_instruction(_instruction),
101
630k
  m_arguments(_arguments)
102
630k
{
103
630k
}
104
105
void Pattern::setMatchGroup(unsigned _group, std::map<unsigned, Expression const*>& _matchGroups)
106
777k
{
107
777k
  m_matchGroup = _group;
108
777k
  m_matchGroups = &_matchGroups;
109
777k
}
110
111
bool Pattern::matches(Expression const& _expr, ExpressionClasses const& _classes) const
112
143M
{
113
143M
  if (!matchesBaseItem(_expr.item))
114
49.2M
    return false;
115
93.8M
  if (m_matchGroup)
116
36.5M
  {
117
36.5M
    if (!m_matchGroups->count(m_matchGroup))
118
34.8M
      (*m_matchGroups)[m_matchGroup] = &_expr;
119
1.71M
    else if ((*m_matchGroups)[m_matchGroup]->id != _expr.id)
120
1.68M
      return false;
121
36.5M
  }
122
92.1M
  assertThrow(m_arguments.size() == 0 || _expr.arguments.size() == m_arguments.size(), OptimizerException, "");
123
127M
  for (size_t i = 0; i < m_arguments.size(); ++i)
124
84.7M
    if (!m_arguments[i].matches(_classes.representative(_expr.arguments[i]), _classes))
125
49.8M
      return false;
126
42.2M
  return true;
127
92.1M
}
128
129
AssemblyItem Pattern::toAssemblyItem(langutil::DebugData::ConstPtr _debugData) const
130
5.15M
{
131
5.15M
  if (m_type == Operation)
132
629k
    return AssemblyItem(m_instruction, std::move(_debugData));
133
4.52M
  else
134
4.52M
    return AssemblyItem(m_type, data(), std::move(_debugData));
135
5.15M
}
136
137
std::string Pattern::toString() const
138
0
{
139
0
  std::stringstream s;
140
0
  switch (m_type)
141
0
  {
142
0
  case Operation:
143
    // Note: This function is exclusively used for debugging.
144
0
    s << instructionInfo(m_instruction, EVMVersion()).name;
145
0
    break;
146
0
  case Push:
147
0
    if (m_data)
148
0
      s << "PUSH " << std::hex << data();
149
0
    else
150
0
      s << "PUSH ";
151
0
    break;
152
0
  case UndefinedItem:
153
0
    s << "ANY";
154
0
    break;
155
0
  default:
156
0
    if (m_data)
157
0
      s << "t=" << std::dec << m_type << " d=" << std::hex << data();
158
0
    else
159
0
      s << "t=" << std::dec << m_type << " d: nullptr";
160
0
    break;
161
0
  }
162
0
  if (!m_requireDataMatch)
163
0
    s << " ~";
164
0
  if (m_matchGroup)
165
0
    s << "[" << std::dec << m_matchGroup << "]";
166
0
  s << "(";
167
0
  for (Pattern const& p: m_arguments)
168
0
    s << p.toString() << ", ";
169
0
  s << ")";
170
0
  return s.str();
171
0
}
172
173
bool Pattern::matchesBaseItem(AssemblyItem const* _item) const
174
143M
{
175
143M
  if (m_type == UndefinedItem)
176
26.5M
    return true;
177
116M
  if (!_item)
178
0
    return false;
179
116M
  if (m_type != _item->type())
180
27.4M
    return false;
181
89.1M
  else if (m_type == Operation)
182
67.6M
    return m_instruction == _item->instruction();
183
21.4M
  else if (m_requireDataMatch)
184
11.4M
    return data() == _item->data();
185
10.0M
  return true;
186
116M
}
187
188
Expression const& Pattern::matchGroupValue() const
189
8.97M
{
190
8.97M
  assertThrow(m_matchGroup > 0, OptimizerException, "");
191
8.97M
  assertThrow(!!m_matchGroups, OptimizerException, "");
192
8.97M
  assertThrow((*m_matchGroups)[m_matchGroup], OptimizerException, "");
193
8.97M
  return *(*m_matchGroups)[m_matchGroup];
194
8.97M
}
195
196
u256 const& Pattern::data() const
197
15.9M
{
198
15.9M
  assertThrow(m_data, OptimizerException, "");
199
15.9M
  return *m_data;
200
15.9M
}
201
202
ExpressionTemplate::ExpressionTemplate(Pattern const& _pattern, langutil::DebugData::ConstPtr const& _debugData)
203
6.08M
{
204
6.08M
  if (_pattern.matchGroup())
205
931k
  {
206
931k
    hasId = true;
207
931k
    id = _pattern.id();
208
931k
  }
209
5.15M
  else
210
5.15M
  {
211
5.15M
    hasId = false;
212
5.15M
    item = _pattern.toAssemblyItem(_debugData);
213
5.15M
  }
214
6.08M
  for (auto const& arg: _pattern.arguments())
215
1.24M
    arguments.emplace_back(arg, _debugData);
216
6.08M
}
217
218
std::string ExpressionTemplate::toString() const
219
0
{
220
0
  std::stringstream s;
221
0
  if (hasId)
222
0
    s << id;
223
0
  else
224
0
    s << item;
225
0
  s << "(";
226
0
  for (auto const& arg: arguments)
227
0
    s << arg.toString();
228
0
  s << ")";
229
0
  return s.str();
230
0
}