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