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  * Unit tests for the code metrics.
19  */
20 
21 #include <test/Common.h>
22 
23 #include <test/libyul/Common.h>
24 
25 #include <libyul/optimiser/Metrics.h>
26 #include <libyul/AST.h>
27 
28 #include <boost/test/unit_test.hpp>
29 
30 using namespace std;
31 using namespace solidity::langutil;
32 
33 namespace solidity::yul::test
34 {
35 
36 namespace
37 {
38 
codeSize(string const & _source,CodeWeights const _weights={})39 size_t codeSize(string const& _source, CodeWeights const _weights = {})
40 {
41 	shared_ptr<Block> ast = parse(_source, false).first;
42 	BOOST_REQUIRE(ast);
43 	return CodeSize::codeSize(*ast, _weights);
44 }
45 
46 }
47 
48 class CustomWeightFixture
49 {
50 protected:
51 	CodeWeights m_weights{
52 		/* expressionStatementCost = */ 1,
53 		/* assignmentCost = */ 2,
54 		/* variableDeclarationCost = */ 3,
55 		/* functionDefinitionCost = */ 4,
56 		/* ifCost = */ 5,
57 		/* switchCost = */ 6,
58 		/* caseCost = */ 7,
59 		/* forLoopCost = */ 8,
60 		/* breakCost = */ 9,
61 		/* continueCost = */ 10,
62 		/* leaveCost = */ 11,
63 		/* blockCost = */ 12,
64 
65 		/* functionCallCost = */ 13,
66 		/* identifierCost = */ 14,
67 		/* literalCost = */ 15,
68 	};
69 };
70 
71 BOOST_AUTO_TEST_SUITE(YulCodeSize)
72 
BOOST_AUTO_TEST_CASE(empty_code)73 BOOST_AUTO_TEST_CASE(empty_code)
74 {
75 	BOOST_CHECK_EQUAL(codeSize("{}"), 0);
76 }
77 
BOOST_FIXTURE_TEST_CASE(empty_code_custom_weights,CustomWeightFixture)78 BOOST_FIXTURE_TEST_CASE(empty_code_custom_weights, CustomWeightFixture)
79 {
80 	BOOST_CHECK_EQUAL(codeSize("{}", m_weights), 0);
81 }
82 
BOOST_AUTO_TEST_CASE(nested_blocks)83 BOOST_AUTO_TEST_CASE(nested_blocks)
84 {
85 	BOOST_CHECK_EQUAL(codeSize("{ {} {} {{ }} }"), 0);
86 }
87 
BOOST_FIXTURE_TEST_CASE(nested_blocks_custom_weights,CustomWeightFixture)88 BOOST_FIXTURE_TEST_CASE(nested_blocks_custom_weights, CustomWeightFixture)
89 {
90 	BOOST_CHECK_EQUAL(codeSize("{ {} {} {{ }} }", m_weights), 4 * m_weights.blockCost);
91 }
92 
BOOST_AUTO_TEST_CASE(instruction)93 BOOST_AUTO_TEST_CASE(instruction)
94 {
95 	BOOST_CHECK_EQUAL(codeSize("{ pop(calldatasize()) }"), 2);
96 }
97 
BOOST_FIXTURE_TEST_CASE(instruction_custom_weights,CustomWeightFixture)98 BOOST_FIXTURE_TEST_CASE(instruction_custom_weights, CustomWeightFixture)
99 {
100 	BOOST_CHECK_EQUAL(
101 		codeSize("{ pop(calldatasize()) }", m_weights),
102 		2 * m_weights.functionCallCost +
103 		1 * m_weights.expressionStatementCost
104 	);
105 }
106 
BOOST_AUTO_TEST_CASE(variables_are_free)107 BOOST_AUTO_TEST_CASE(variables_are_free)
108 {
109 	BOOST_CHECK_EQUAL(codeSize("{ let x let y let a, b, c }"), 0);
110 }
111 
BOOST_FIXTURE_TEST_CASE(variables_custom_weights,CustomWeightFixture)112 BOOST_FIXTURE_TEST_CASE(variables_custom_weights, CustomWeightFixture)
113 {
114 	BOOST_CHECK_EQUAL(
115 		codeSize("{ let x let y let a, b, c }", m_weights),
116 		3 * m_weights.variableDeclarationCost
117 	);
118 }
119 
BOOST_AUTO_TEST_CASE(constants_cost_one)120 BOOST_AUTO_TEST_CASE(constants_cost_one)
121 {
122 	BOOST_CHECK_EQUAL(codeSize("{ let x := 3 }"), 1);
123 }
124 
BOOST_FIXTURE_TEST_CASE(constants_custom_weights,CustomWeightFixture)125 BOOST_FIXTURE_TEST_CASE(constants_custom_weights, CustomWeightFixture)
126 {
127 	BOOST_CHECK_EQUAL(
128 		codeSize("{ let x := 3 }", m_weights),
129 		1 * m_weights.variableDeclarationCost +
130 		1 * m_weights.literalCost
131 	);
132 }
133 
BOOST_AUTO_TEST_CASE(functions_are_skipped)134 BOOST_AUTO_TEST_CASE(functions_are_skipped)
135 {
136 	BOOST_CHECK_EQUAL(codeSize("{ function f(x) -> r { r := mload(x) } }"), 0);
137 }
138 
BOOST_FIXTURE_TEST_CASE(functions_are_skipped_custom_weights,CustomWeightFixture)139 BOOST_FIXTURE_TEST_CASE(functions_are_skipped_custom_weights, CustomWeightFixture)
140 {
141 	BOOST_CHECK_EQUAL(codeSize("{ function f(x) -> r { r := mload(x) } }", m_weights), 0);
142 }
143 
BOOST_AUTO_TEST_CASE(function_with_arguments)144 BOOST_AUTO_TEST_CASE(function_with_arguments)
145 {
146 	BOOST_CHECK_EQUAL(codeSize("{ function f(x) { sstore(x, 2) } f(2) }"), 2);
147 }
148 
BOOST_FIXTURE_TEST_CASE(function_with_arguments_custom_weights,CustomWeightFixture)149 BOOST_FIXTURE_TEST_CASE(function_with_arguments_custom_weights, CustomWeightFixture)
150 {
151 	BOOST_CHECK_EQUAL(
152 		codeSize("{ function f(x) { sstore(x, 2) } f(2) }", m_weights),
153 		1 * m_weights.expressionStatementCost +
154 		1 * m_weights.functionCallCost +
155 		1 * m_weights.literalCost
156 	);
157 }
158 
BOOST_AUTO_TEST_CASE(function_with_variables_as_arguments)159 BOOST_AUTO_TEST_CASE(function_with_variables_as_arguments)
160 {
161 	BOOST_CHECK_EQUAL(codeSize("{ function f(x) { sstore(x, 2) } let y f(y) }"), 1);
162 }
163 
BOOST_FIXTURE_TEST_CASE(function_with_variables_as_arguments_custom_weights,CustomWeightFixture)164 BOOST_FIXTURE_TEST_CASE(function_with_variables_as_arguments_custom_weights, CustomWeightFixture)
165 {
166 	BOOST_CHECK_EQUAL(
167 		codeSize("{ function f(x) { sstore(x, 2) } let y f(y) }", m_weights),
168 		1 * m_weights.variableDeclarationCost +
169 		1 * m_weights.expressionStatementCost +
170 		1 * m_weights.functionCallCost +
171 		1 * m_weights.identifierCost
172 	);
173 }
174 
BOOST_AUTO_TEST_CASE(function_with_variables_and_constants_as_arguments)175 BOOST_AUTO_TEST_CASE(function_with_variables_and_constants_as_arguments)
176 {
177 	BOOST_CHECK_EQUAL(codeSize(
178 		"{ function f(x, r) -> z { sstore(x, r) z := r } let y let t := f(y, 2) }"
179 	), 2);
180 }
181 
BOOST_FIXTURE_TEST_CASE(function_with_variables_and_constants_as_arguments_custom_weights,CustomWeightFixture)182 BOOST_FIXTURE_TEST_CASE(
183 	function_with_variables_and_constants_as_arguments_custom_weights,
184 	CustomWeightFixture
185 )
186 {
187 	BOOST_CHECK_EQUAL(
188 		codeSize(
189 			"{ function f(x, r) -> z { sstore(x, r) z := r } let y let t := f(y, 2) }",
190 			m_weights
191 		),
192 		2 * m_weights.variableDeclarationCost +
193 		1 * m_weights.functionCallCost +
194 		1 * m_weights.identifierCost +
195 		1 * m_weights.literalCost
196 	);
197 }
198 
BOOST_AUTO_TEST_CASE(assignment)199 BOOST_AUTO_TEST_CASE(assignment)
200 {
201 	BOOST_CHECK_EQUAL(codeSize("{ let a a := 3 }"), 1);
202 }
203 
BOOST_FIXTURE_TEST_CASE(assignment_custom_weights,CustomWeightFixture)204 BOOST_FIXTURE_TEST_CASE(assignment_custom_weights, CustomWeightFixture)
205 {
206 	BOOST_CHECK_EQUAL(
207 		codeSize("{ let a a := 3 }", m_weights),
208 		1 * m_weights.variableDeclarationCost +
209 		1 * m_weights.assignmentCost +
210 		1 * m_weights.literalCost
211 	);
212 }
213 
BOOST_AUTO_TEST_CASE(assignments_between_vars_are_free)214 BOOST_AUTO_TEST_CASE(assignments_between_vars_are_free)
215 {
216 	BOOST_CHECK_EQUAL(codeSize("{ let a let b := a a := b }"), 0);
217 }
218 
BOOST_FIXTURE_TEST_CASE(assignments_between_vars_are_free_custom_weights,CustomWeightFixture)219 BOOST_FIXTURE_TEST_CASE(assignments_between_vars_are_free_custom_weights, CustomWeightFixture)
220 {
221 	BOOST_CHECK_EQUAL(
222 		codeSize("{ let a let b := a a := b }", m_weights),
223 		2 * m_weights.variableDeclarationCost +
224 		1 * m_weights.assignmentCost +
225 		2 * m_weights.identifierCost
226 	);
227 }
228 
BOOST_AUTO_TEST_CASE(assignment_complex)229 BOOST_AUTO_TEST_CASE(assignment_complex)
230 {
231 	BOOST_CHECK_EQUAL(codeSize("{ let a let x := mload(a) a := sload(x) }"), 2);
232 }
233 
BOOST_FIXTURE_TEST_CASE(assignment_complex_custom_weights,CustomWeightFixture)234 BOOST_FIXTURE_TEST_CASE(assignment_complex_custom_weights, CustomWeightFixture)
235 {
236 	BOOST_CHECK_EQUAL(
237 		codeSize("{ let a let x := mload(a) a := sload(x) }", m_weights),
238 		2 * m_weights.variableDeclarationCost +
239 		1 * m_weights.assignmentCost +
240 		2 * m_weights.identifierCost +
241 		2 * m_weights.functionCallCost
242 	);
243 }
244 
BOOST_AUTO_TEST_CASE(empty_for_loop)245 BOOST_AUTO_TEST_CASE(empty_for_loop)
246 {
247 	BOOST_CHECK_EQUAL(codeSize(
248 		"{ for {} 1 {} {} }"
249 	), 4);
250 }
251 
BOOST_FIXTURE_TEST_CASE(empty_for_loop_custom_weights,CustomWeightFixture)252 BOOST_FIXTURE_TEST_CASE(empty_for_loop_custom_weights, CustomWeightFixture)
253 {
254 	BOOST_CHECK_EQUAL(
255 		codeSize("{ for {} 1 {} {} }", m_weights),
256 		1 * m_weights.forLoopCost +
257 		1 * m_weights.literalCost
258 	);
259 }
260 
BOOST_AUTO_TEST_CASE(break_statement)261 BOOST_AUTO_TEST_CASE(break_statement)
262 {
263 	BOOST_CHECK_EQUAL(codeSize(
264 		"{ for {} 1 {} { break } }"
265 	), 6);
266 }
267 
BOOST_FIXTURE_TEST_CASE(break_statement_custom_weights,CustomWeightFixture)268 BOOST_FIXTURE_TEST_CASE(break_statement_custom_weights, CustomWeightFixture)
269 {
270 	BOOST_CHECK_EQUAL(
271 		codeSize("{ for {} 1 {} { break } }", m_weights),
272 		1 * m_weights.forLoopCost +
273 		1 * m_weights.literalCost +
274 		1 * m_weights.breakCost
275 	);
276 }
277 
BOOST_AUTO_TEST_CASE(continue_statement)278 BOOST_AUTO_TEST_CASE(continue_statement)
279 {
280 	BOOST_CHECK_EQUAL(codeSize(
281 		"{ for {} 1 {} { continue } }"
282 	), 6);
283 }
284 
BOOST_FIXTURE_TEST_CASE(continue_statement_custom_weights,CustomWeightFixture)285 BOOST_FIXTURE_TEST_CASE(continue_statement_custom_weights, CustomWeightFixture)
286 {
287 	BOOST_CHECK_EQUAL(
288 		codeSize("{ for {} 1 {} { continue } }", m_weights),
289 		1 * m_weights.forLoopCost +
290 		1 * m_weights.literalCost +
291 		1 * m_weights.continueCost
292 	);
293 }
294 
BOOST_AUTO_TEST_CASE(regular_for_loop)295 BOOST_AUTO_TEST_CASE(regular_for_loop)
296 {
297 	BOOST_CHECK_EQUAL(codeSize(
298 		"{ for { let x := 0 } lt(x, 10) { x := add(x, 1) } { mstore(x, 1) } }"
299 	), 9);
300 }
301 
BOOST_FIXTURE_TEST_CASE(regular_for_loop_custom_weights,CustomWeightFixture)302 BOOST_FIXTURE_TEST_CASE(regular_for_loop_custom_weights, CustomWeightFixture)
303 {
304 	BOOST_CHECK_EQUAL(
305 		codeSize("{ for { let x := 0 } lt(x, 10) { x := add(x, 1) } { mstore(x, 1) } }", m_weights),
306 		1 * m_weights.forLoopCost +
307 		1 * m_weights.variableDeclarationCost +
308 		1 * m_weights.assignmentCost +
309 		3 * m_weights.functionCallCost +
310 		3 * m_weights.literalCost +
311 		1 * m_weights.literalZeroCost +
312 		3 * m_weights.identifierCost +
313 		1 * m_weights.expressionStatementCost
314 	);
315 }
316 
BOOST_AUTO_TEST_CASE(if_statement)317 BOOST_AUTO_TEST_CASE(if_statement)
318 {
319 	BOOST_CHECK_EQUAL(codeSize(
320 		"{ if 1 {} }"
321 	), 3);
322 }
323 
BOOST_FIXTURE_TEST_CASE(if_statement_custom_weights,CustomWeightFixture)324 BOOST_FIXTURE_TEST_CASE(if_statement_custom_weights, CustomWeightFixture)
325 {
326 	BOOST_CHECK_EQUAL(
327 		codeSize("{ if 1 {} }", m_weights),
328 		1 * m_weights.ifCost +
329 		1 * m_weights.literalCost
330 	);
331 }
332 
BOOST_AUTO_TEST_CASE(switch_statement_tiny)333 BOOST_AUTO_TEST_CASE(switch_statement_tiny)
334 {
335 	BOOST_CHECK_EQUAL(codeSize(
336 		"{ switch calldatasize() case 0 {} }"
337 	), 4);
338 }
339 
BOOST_AUTO_TEST_CASE(switch_statement_small)340 BOOST_AUTO_TEST_CASE(switch_statement_small)
341 {
342 	BOOST_CHECK_EQUAL(codeSize(
343 		"{ switch calldatasize() case 0 {} default {} }"
344 	), 6);
345 }
346 
BOOST_FIXTURE_TEST_CASE(switch_statement_small_custom_weights,CustomWeightFixture)347 BOOST_FIXTURE_TEST_CASE(switch_statement_small_custom_weights, CustomWeightFixture)
348 {
349 	BOOST_CHECK_EQUAL(
350 		codeSize("{ switch calldatasize() case 0 {} default {} }", m_weights),
351 		1 * m_weights.functionCallCost +
352 		1 * m_weights.switchCost +
353 		2 * m_weights.caseCost
354 	);
355 }
356 
BOOST_AUTO_TEST_CASE(switch_statement_medium)357 BOOST_AUTO_TEST_CASE(switch_statement_medium)
358 {
359 	BOOST_CHECK_EQUAL(codeSize(
360 		"{ switch calldatasize() case 0 {} case 1 {} case 2 {} }"
361 	), 8);
362 }
363 
BOOST_AUTO_TEST_CASE(switch_statement_large)364 BOOST_AUTO_TEST_CASE(switch_statement_large)
365 {
366 	BOOST_CHECK_EQUAL(codeSize(
367 		"{ switch calldatasize() case 0 {} case 1 {} case 2 {} default {} }"
368 	), 10);
369 }
370 
BOOST_FIXTURE_TEST_CASE(switch_statement_large_custom_weights,CustomWeightFixture)371 BOOST_FIXTURE_TEST_CASE(switch_statement_large_custom_weights, CustomWeightFixture)
372 {
373 	BOOST_CHECK_EQUAL(
374 		codeSize("{ switch calldatasize() case 0 {} case 1 {} case 2 {} default {} }", m_weights),
375 		1 * m_weights.functionCallCost +
376 		1 * m_weights.switchCost +
377 		4 * m_weights.caseCost
378 	);
379 }
380 
381 BOOST_AUTO_TEST_SUITE_END()
382 
383 }
384