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