Coverage Report

Created: 2026-10-26 06:53

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
9.98M
{
44
9.98M
  resetMatchGroups();
45
46
9.98M
  assertThrow(_expr.item, OptimizerException, "");
47
9.98M
  for (auto const& rule: m_rules[uint8_t(_expr.item->instruction())])
48
51.8M
  {
49
51.8M
    if (rule.pattern.matches(_expr, _classes))
50
4.72M
      if (!rule.feasible || rule.feasible())
51
4.65M
        return &rule;
52
53
47.1M
    resetMatchGroups();
54
47.1M
  }
55
5.33M
  return nullptr;
56
9.98M
}
57
58
bool Rules::isInitialized() const
59
22.2M
{
60
22.2M
  return !m_rules[uint8_t(Instruction::ADD)].empty();
61
22.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
671k
  m_type(Operation),
100
671k
  m_instruction(_instruction),
101
671k
  m_arguments(_arguments)
102
671k
{
103
671k
}
104
105
void Pattern::setMatchGroup(unsigned _group, std::map<unsigned, Expression const*>& _matchGroups)
106
816k
{
107
816k
  m_matchGroup = _group;
108
816k
  m_matchGroups = &_matchGroups;
109
816k
}
110
111
bool Pattern::matches(Expression const& _expr, ExpressionClasses const& _classes) const
112
136M
{
113
136M
  if (!matchesBaseItem(_expr.item))
114
47.2M
    return false;
115
89.3M
  if (m_matchGroup)
116
34.3M
  {
117
34.3M
    if (!m_matchGroups->count(m_matchGroup))
118
32.6M
      (*m_matchGroups)[m_matchGroup] = &_expr;
119
1.74M
    else if ((*m_matchGroups)[m_matchGroup]->id != _expr.id)
120
1.71M
      return false;
121
34.3M
  }
122
87.6M
  assertThrow(m_arguments.size() == 0 || _expr.arguments.size() == m_arguments.size(), OptimizerException, "");
123
120M
  for (size_t i = 0; i < m_arguments.size(); ++i)
124
80.5M
    if (!m_arguments[i].matches(_classes.representative(_expr.arguments[i]), _classes))
125
47.8M
      return false;
126
39.7M
  return true;
127
87.6M
}
128
129
AssemblyItem Pattern::toAssemblyItem(langutil::DebugData::ConstPtr _debugData) const
130
4.98M
{
131
4.98M
  if (m_type == Operation)
132
669k
    return AssemblyItem(m_instruction, std::move(_debugData));
133
4.31M
  else
134
4.31M
    return AssemblyItem(m_type, data(), std::move(_debugData));
135
4.98M
}
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
136M
{
175
136M
  if (m_type == UndefinedItem)
176
24.6M
    return true;
177
111M
  if (!_item)
178
0
    return false;
179
111M
  if (m_type != _item->type())
180
25.3M
    return false;
181
86.6M
  else if (m_type == Operation)
182
65.8M
    return m_instruction == _item->instruction();
183
20.8M
  else if (m_requireDataMatch)
184
11.1M
    return data() == _item->data();
185
9.69M
  return true;
186
111M
}
187
188
Expression const& Pattern::matchGroupValue() const
189
8.74M
{
190
8.74M
  assertThrow(m_matchGroup > 0, OptimizerException, "");
191
8.74M
  assertThrow(!!m_matchGroups, OptimizerException, "");
192
8.74M
  assertThrow((*m_matchGroups)[m_matchGroup], OptimizerException, "");
193
8.74M
  return *(*m_matchGroups)[m_matchGroup];
194
8.74M
}
195
196
u256 const& Pattern::data() const
197
15.4M
{
198
15.4M
  assertThrow(m_data, OptimizerException, "");
199
15.4M
  return *m_data;
200
15.4M
}
201
202
ExpressionTemplate::ExpressionTemplate(Pattern const& _pattern, langutil::DebugData::ConstPtr const& _debugData)
203
5.98M
{
204
5.98M
  if (_pattern.matchGroup())
205
1.00M
  {
206
1.00M
    hasId = true;
207
1.00M
    id = _pattern.id();
208
1.00M
  }
209
4.98M
  else
210
4.98M
  {
211
4.98M
    hasId = false;
212
4.98M
    item = _pattern.toAssemblyItem(_debugData);
213
4.98M
  }
214
5.98M
  for (auto const& arg: _pattern.arguments())
215
1.32M
    arguments.emplace_back(arg, _debugData);
216
5.98M
}
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
}