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