/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 | | } |