Coverage Report

Created: 2022-08-24 06:52

/src/solidity/libyul/optimiser/Metrics.h
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
 * Module providing metrics for the optimizer.
20
 */
21
22
#pragma once
23
24
#include <libyul/optimiser/ASTWalker.h>
25
#include <liblangutil/EVMVersion.h>
26
27
namespace solidity::yul
28
{
29
30
struct Dialect;
31
struct EVMDialect;
32
33
/**
34
 * Weights to be assigned to specific yul statements and expressions by a metric.
35
 *
36
 * The default values are meant to reflect specifically the number of AST nodes.
37
 *
38
 * The following AST elements have a default cost of zero (because the cleanup phase would
39
 * remove them anyway or they are just wrappers around something else that will be counted instead):
40
 *  - expression statement (only the expression inside has a cost)
41
 *  - block (only the statements inside have a cost)
42
 *  - variable references
43
 *  - variable declarations (only the right hand side has a cost)
44
 *  - assignments (only the value has a cost)
45
 *  - literal zeros (we optimistically assume they can be copied from somewhere else)
46
 *
47
 * Each statement incurs and additional cost of one
48
 * per jump/branch. This means if, break and continue statements have a cost of 2,
49
 * switch statements have a cost of 1 plus the number of cases times two,
50
 * and for loops cost 3.
51
*/
52
struct CodeWeights
53
{
54
  // Statements
55
  size_t expressionStatementCost = 0;
56
  size_t assignmentCost = 0;
57
  size_t variableDeclarationCost = 0;
58
  size_t functionDefinitionCost = 1;
59
  size_t ifCost = 2;
60
  size_t switchCost = 1;
61
  size_t caseCost = 2;
62
  size_t forLoopCost = 3;
63
  size_t breakCost = 2;
64
  size_t continueCost = 2;
65
  size_t leaveCost = 2;
66
  size_t blockCost = 0;
67
68
  // Expressions
69
  size_t functionCallCost = 1;
70
  size_t identifierCost = 0;
71
  size_t literalCost = 1;
72
  size_t literalZeroCost = 0;
73
74
  size_t costOf(Statement const& _statement) const;
75
  size_t costOf(Expression const& _expression) const;
76
};
77
78
/**
79
 * Metric for the size of code.
80
 * Ignores function definitions while traversing the AST by default.
81
 * If you want to know the size of a function, you have to invoke this on its body.
82
 *
83
 * The cost of each statement and expression type is configurable via CodeWeights.
84
 */
85
class CodeSize: public ASTWalker
86
{
87
public:
88
  static size_t codeSize(Statement const& _statement, CodeWeights const& _weights = {});
89
  static size_t codeSize(Expression const& _expression, CodeWeights const& _weights = {});
90
  static size_t codeSize(Block const& _block, CodeWeights const& _weights = {});
91
  static size_t codeSizeIncludingFunctions(Block const& _block, CodeWeights const& _weights = {});
92
93
private:
94
  CodeSize(bool _ignoreFunctions = true, CodeWeights const& _weights = {}):
95
    m_ignoreFunctions(_ignoreFunctions),
96
0
    m_weights(_weights) {}
97
98
  void visit(Statement const& _statement) override;
99
  void visit(Expression const& _expression) override;
100
101
private:
102
  bool m_ignoreFunctions;
103
  size_t m_size = 0;
104
  CodeWeights m_weights;
105
};
106
107
/**
108
 * Very rough cost that takes the size and execution cost of code into account.
109
 * The cost per AST element is one, except for literals where it is the byte size.
110
 * Function calls cost 50. Instructions cost 0 for 3 or less gas (same as DUP),
111
 * 2 for up to 10 and 50 otherwise.
112
 */
113
class CodeCost: public ASTWalker
114
{
115
public:
116
  static size_t codeCost(Dialect const& _dialect, Expression const& _expression);
117
118
private:
119
0
  CodeCost(Dialect const& _dialect): m_dialect(_dialect) {}
120
121
  void operator()(FunctionCall const& _funCall) override;
122
  void operator()(Literal const& _literal) override;
123
  void visit(Statement const& _statement) override;
124
  void visit(Expression const& _expression) override;
125
126
private:
127
  void addInstructionCost(evmasm::Instruction _instruction);
128
129
  Dialect const& m_dialect;
130
  size_t m_cost = 0;
131
};
132
133
/**
134
 * Counts the number of assignments to every variable.
135
 * Only works after running the Disambiguator.
136
 */
137
class AssignmentCounter: public ASTWalker
138
{
139
public:
140
  using ASTWalker::operator();
141
  void operator()(Assignment const& _assignment) override;
142
  std::size_t assignmentCount(YulString _name) const;
143
private:
144
  std::map<YulString, size_t> m_assignmentCounters;
145
};
146
147
}