Coverage Report

Created: 2026-09-14 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libyul/optimiser/Metrics.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
/**
18
* Module providing metrics for the optimizer.
19
*/
20
21
#include <libyul/optimiser/Metrics.h>
22
#include <libyul/optimiser/OptimizerUtilities.h>
23
#include <libyul/backends/evm/EVMDialect.h>
24
25
#include <libyul/AST.h>
26
#include <libyul/Exceptions.h>
27
#include <libyul/Utilities.h>
28
29
#include <libevmasm/Instruction.h>
30
31
#include <libsolutil/CommonData.h>
32
33
using namespace solidity;
34
using namespace solidity::yul;
35
using namespace solidity::util;
36
37
size_t CodeWeights::costOf(Statement const& _statement) const
38
240M
{
39
240M
  if (std::holds_alternative<ExpressionStatement>(_statement))
40
29.6M
    return expressionStatementCost;
41
210M
  else if (std::holds_alternative<Assignment>(_statement))
42
7.02M
    return assignmentCost;
43
203M
  else if (std::holds_alternative<VariableDeclaration>(_statement))
44
185M
    return variableDeclarationCost;
45
17.6M
  else if (std::holds_alternative<FunctionDefinition>(_statement))
46
3.83M
    return functionDefinitionCost;
47
13.8M
  else if (std::holds_alternative<If>(_statement))
48
3.64M
    return ifCost;
49
10.1M
  else if (std::holds_alternative<Switch>(_statement))
50
1.16M
    return switchCost + caseCost * std::get<Switch>(_statement).cases.size();
51
9.02M
  else if (std::holds_alternative<ForLoop>(_statement))
52
3.99M
    return forLoopCost;
53
5.02M
  else if (std::holds_alternative<Break>(_statement))
54
2.16M
    return breakCost;
55
2.85M
  else if (std::holds_alternative<Continue>(_statement))
56
315k
    return continueCost;
57
2.54M
  else if (std::holds_alternative<Leave>(_statement))
58
679k
    return leaveCost;
59
1.86M
  else if (std::holds_alternative<Block>(_statement))
60
1.86M
    return blockCost;
61
0
  else
62
1.86M
    yulAssert(false, "If you add a new statement type, you must update CodeWeights.");
63
240M
}
64
65
size_t CodeWeights::costOf(Expression const& _expression) const
66
367M
{
67
367M
  if (std::holds_alternative<FunctionCall>(_expression))
68
76.1M
    return functionCallCost;
69
291M
  else if (std::holds_alternative<Identifier>(_expression))
70
187M
    return identifierCost;
71
104M
  else if (Literal const* literal = std::get_if<Literal>(&_expression))
72
104M
  {
73
    // Avoid strings because they could be longer than 32 bytes.
74
104M
    if (literal->kind != LiteralKind::String && literal->value.value() == 0)
75
21.5M
      return literalZeroCost;
76
83.1M
    else
77
83.1M
      return literalCost;
78
104M
  }
79
0
  else
80
104M
    yulAssert(false, "If you add a new expression type, you must update CodeWeights.");
81
367M
}
82
83
84
size_t CodeSize::codeSize(Statement const& _statement, CodeWeights const& _weights)
85
0
{
86
0
  CodeSize cs(true, _weights);
87
0
  cs.visit(_statement);
88
0
  return cs.m_size;
89
0
}
90
91
size_t CodeSize::codeSize(Expression const& _expression, CodeWeights const& _weights)
92
0
{
93
0
  CodeSize cs(true, _weights);
94
0
  cs.visit(_expression);
95
0
  return cs.m_size;
96
0
}
97
98
size_t CodeSize::codeSize(Block const& _block, CodeWeights const& _weights)
99
2.22M
{
100
2.22M
  CodeSize cs(true, _weights);
101
2.22M
  cs(_block);
102
2.22M
  return cs.m_size;
103
2.22M
}
104
105
size_t CodeSize::codeSizeIncludingFunctions(Block const& _block, CodeWeights const& _weights)
106
1.51M
{
107
1.51M
  CodeSize cs(false, _weights);
108
1.51M
  cs(_block);
109
1.51M
  return cs.m_size;
110
1.51M
}
111
112
void CodeSize::visit(Statement const& _statement)
113
240M
{
114
240M
  if (std::holds_alternative<FunctionDefinition>(_statement) && m_ignoreFunctions)
115
613k
    return;
116
117
240M
  m_size += m_weights.costOf(_statement);
118
240M
  ASTWalker::visit(_statement);
119
240M
}
120
121
void CodeSize::visit(Expression const& _expression)
122
367M
{
123
367M
  m_size += m_weights.costOf(_expression);
124
367M
  ASTWalker::visit(_expression);
125
367M
}
126
127
128
size_t CodeCost::codeCost(Dialect const& _dialect, Expression const& _expr)
129
1.93M
{
130
1.93M
  CodeCost cc(_dialect);
131
1.93M
  cc.visit(_expr);
132
1.93M
  return cc.m_cost;
133
1.93M
}
134
135
136
void CodeCost::operator()(FunctionCall const& _funCall)
137
1.57M
{
138
1.57M
  ASTWalker::operator()(_funCall);
139
140
1.57M
  if (auto instruction = toEVMInstruction(m_dialect, _funCall.functionName))
141
1.41M
  {
142
1.41M
    addInstructionCost(*instruction);
143
1.41M
    return;
144
1.41M
  }
145
146
152k
  m_cost += 49;
147
152k
}
148
149
void CodeCost::operator()(Literal const& _literal)
150
2.39M
{
151
2.39M
  yulAssert(m_cost >= 1, "Should assign cost one in visit(Expression).");
152
2.39M
  size_t cost = 0;
153
2.39M
  switch (_literal.kind)
154
2.39M
  {
155
7.14k
  case LiteralKind::Boolean:
156
7.14k
    break;
157
2.22M
  case LiteralKind::Number:
158
8.58M
    for (u256 n = _literal.value.value(); n >= 0x100; n >>= 8)
159
6.36M
      cost++;
160
2.22M
    if (_literal.value.value() == 0)
161
605k
      if (auto evmDialect = dynamic_cast<EVMDialect const*>(&m_dialect))
162
605k
        if (evmDialect->evmVersion().hasPush0())
163
421k
          --m_cost;
164
2.22M
    break;
165
158k
  case LiteralKind::String:
166
158k
    cost = formatLiteral(_literal).size();
167
158k
    break;
168
2.39M
  }
169
170
2.39M
  m_cost += cost;
171
2.39M
}
172
173
void CodeCost::visit(Statement const& _statement)
174
0
{
175
0
  ++m_cost;
176
0
  ASTWalker::visit(_statement);
177
0
}
178
179
void CodeCost::visit(Expression const& _expression)
180
4.27M
{
181
4.27M
  ++m_cost;
182
4.27M
  ASTWalker::visit(_expression);
183
4.27M
}
184
185
void CodeCost::addInstructionCost(evmasm::Instruction _instruction)
186
1.41M
{
187
1.41M
  evmasm::Tier gasPriceTier = evmasm::instructionInfo(_instruction, evmVersionFromDialect(m_dialect)).gasPriceTier;
188
1.41M
  if (gasPriceTier < evmasm::Tier::VeryLow)
189
226k
    m_cost -= 1;
190
1.19M
  else if (gasPriceTier < evmasm::Tier::High)
191
1.10M
    m_cost += 1;
192
92.0k
  else
193
92.0k
    m_cost += 49;
194
1.41M
}
195
196
void AssignmentCounter::operator()(Assignment const& _assignment)
197
432k
{
198
432k
  for (auto const& variable: _assignment.variableNames)
199
432k
    ++m_assignmentCounters[variable.name];
200
432k
}
201
202
size_t AssignmentCounter::assignmentCount(YulName _name) const
203
169k
{
204
169k
  auto it = m_assignmentCounters.find(_name);
205
169k
  return (it == m_assignmentCounters.end()) ? 0 : it->second;
206
169k
}