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  * Component that can generate various useful Yul functions.
20  */
21 
22 #include <libsolidity/codegen/YulUtilFunctions.h>
23 
24 #include <libsolidity/codegen/MultiUseYulFunctionCollector.h>
25 #include <libsolidity/ast/AST.h>
26 #include <libsolidity/codegen/CompilerUtils.h>
27 
28 #include <libsolutil/CommonData.h>
29 #include <libsolutil/FunctionSelector.h>
30 #include <libsolutil/Whiskers.h>
31 #include <libsolutil/StringUtils.h>
32 
33 using namespace std;
34 using namespace solidity;
35 using namespace solidity::util;
36 using namespace solidity::frontend;
37 
identityFunction()38 string YulUtilFunctions::identityFunction()
39 {
40 	string functionName = "identity";
41 	return m_functionCollector.createFunction("identity", [&](vector<string>& _args, vector<string>& _rets) {
42 		_args.push_back("value");
43 		_rets.push_back("ret");
44 		return "ret := value";
45 	});
46 }
47 
combineExternalFunctionIdFunction()48 string YulUtilFunctions::combineExternalFunctionIdFunction()
49 {
50 	string functionName = "combine_external_function_id";
51 	return m_functionCollector.createFunction(functionName, [&]() {
52 		return Whiskers(R"(
53 			function <functionName>(addr, selector) -> combined {
54 				combined := <shl64>(or(<shl32>(addr), and(selector, 0xffffffff)))
55 			}
56 		)")
57 		("functionName", functionName)
58 		("shl32", shiftLeftFunction(32))
59 		("shl64", shiftLeftFunction(64))
60 		.render();
61 	});
62 }
63 
splitExternalFunctionIdFunction()64 string YulUtilFunctions::splitExternalFunctionIdFunction()
65 {
66 	string functionName = "split_external_function_id";
67 	return m_functionCollector.createFunction(functionName, [&]() {
68 		return Whiskers(R"(
69 			function <functionName>(combined) -> addr, selector {
70 				combined := <shr64>(combined)
71 				selector := and(combined, 0xffffffff)
72 				addr := <shr32>(combined)
73 			}
74 		)")
75 		("functionName", functionName)
76 		("shr32", shiftRightFunction(32))
77 		("shr64", shiftRightFunction(64))
78 		.render();
79 	});
80 }
81 
copyToMemoryFunction(bool _fromCalldata)82 string YulUtilFunctions::copyToMemoryFunction(bool _fromCalldata)
83 {
84 	string functionName = "copy_" + string(_fromCalldata ? "calldata" : "memory") + "_to_memory";
85 	return m_functionCollector.createFunction(functionName, [&]() {
86 		if (_fromCalldata)
87 		{
88 			return Whiskers(R"(
89 				function <functionName>(src, dst, length) {
90 					calldatacopy(dst, src, length)
91 					// clear end
92 					mstore(add(dst, length), 0)
93 				}
94 			)")
95 			("functionName", functionName)
96 			.render();
97 		}
98 		else
99 		{
100 			return Whiskers(R"(
101 				function <functionName>(src, dst, length) {
102 					let i := 0
103 					for { } lt(i, length) { i := add(i, 32) }
104 					{
105 						mstore(add(dst, i), mload(add(src, i)))
106 					}
107 					if gt(i, length)
108 					{
109 						// clear end
110 						mstore(add(dst, length), 0)
111 					}
112 				}
113 			)")
114 			("functionName", functionName)
115 			.render();
116 		}
117 	});
118 }
119 
copyLiteralToMemoryFunction(string const & _literal)120 string YulUtilFunctions::copyLiteralToMemoryFunction(string const& _literal)
121 {
122 	string functionName = "copy_literal_to_memory_" + util::toHex(util::keccak256(_literal).asBytes());
123 
124 	return m_functionCollector.createFunction(functionName, [&]() {
125 		return Whiskers(R"(
126 			function <functionName>() -> memPtr {
127 				memPtr := <arrayAllocationFunction>(<size>)
128 				<storeLiteralInMem>(add(memPtr, 32))
129 			}
130 			)")
131 			("functionName", functionName)
132 			("arrayAllocationFunction", allocateMemoryArrayFunction(*TypeProvider::array(DataLocation::Memory, true)))
133 			("size", to_string(_literal.size()))
134 			("storeLiteralInMem", storeLiteralInMemoryFunction(_literal))
135 			.render();
136 	});
137 }
138 
storeLiteralInMemoryFunction(string const & _literal)139 string YulUtilFunctions::storeLiteralInMemoryFunction(string const& _literal)
140 {
141 	string functionName = "store_literal_in_memory_" + util::toHex(util::keccak256(_literal).asBytes());
142 
143 	return m_functionCollector.createFunction(functionName, [&]() {
144 		size_t words = (_literal.length() + 31) / 32;
145 		vector<map<string, string>> wordParams(words);
146 		for (size_t i = 0; i < words; ++i)
147 		{
148 			wordParams[i]["offset"] = to_string(i * 32);
149 			wordParams[i]["wordValue"] = formatAsStringOrNumber(_literal.substr(32 * i, 32));
150 		}
151 
152 		return Whiskers(R"(
153 			function <functionName>(memPtr) {
154 				<#word>
155 					mstore(add(memPtr, <offset>), <wordValue>)
156 				</word>
157 			}
158 			)")
159 			("functionName", functionName)
160 			("word", wordParams)
161 			.render();
162 	});
163 }
164 
copyLiteralToStorageFunction(string const & _literal)165 string YulUtilFunctions::copyLiteralToStorageFunction(string const& _literal)
166 {
167 	string functionName = "copy_literal_to_storage_" + util::toHex(util::keccak256(_literal).asBytes());
168 
169 	return m_functionCollector.createFunction(functionName, [&](vector<string>& _args, vector<string>&) {
170 		_args = {"slot"};
171 
172 		if (_literal.size() >= 32)
173 		{
174 			size_t words = (_literal.length() + 31) / 32;
175 			vector<map<string, string>> wordParams(words);
176 			for (size_t i = 0; i < words; ++i)
177 			{
178 				wordParams[i]["offset"] = to_string(i);
179 				wordParams[i]["wordValue"] = formatAsStringOrNumber(_literal.substr(32 * i, 32));
180 			}
181 			return Whiskers(R"(
182 				let oldLen := <byteArrayLength>(sload(slot))
183 				<cleanUpArrayEnd>(slot, oldLen, <length>)
184 				sstore(slot, <encodedLen>)
185 				let dstPtr := <dataArea>(slot)
186 				<#word>
187 					sstore(add(dstPtr, <offset>), <wordValue>)
188 				</word>
189 			)")
190 			("byteArrayLength", extractByteArrayLengthFunction())
191 			("cleanUpArrayEnd", cleanUpDynamicByteArrayEndSlotsFunction(*TypeProvider::bytesStorage()))
192 			("dataArea", arrayDataAreaFunction(*TypeProvider::bytesStorage()))
193 			("word", wordParams)
194 			("length", to_string(_literal.size()))
195 			("encodedLen", to_string(2 * _literal.size() + 1))
196 			.render();
197 		}
198 		else
199 			return Whiskers(R"(
200 				let oldLen := <byteArrayLength>(sload(slot))
201 				<cleanUpArrayEnd>(slot, oldLen, <length>)
202 				sstore(slot, add(<wordValue>, <encodedLen>))
203 			)")
204 			("byteArrayLength", extractByteArrayLengthFunction())
205 			("cleanUpArrayEnd", cleanUpDynamicByteArrayEndSlotsFunction(*TypeProvider::bytesStorage()))
206 			("wordValue", formatAsStringOrNumber(_literal))
207 			("length", to_string(_literal.size()))
208 			("encodedLen", to_string(2 * _literal.size()))
209 			.render();
210 	});
211 }
212 
requireOrAssertFunction(bool _assert,Type const * _messageType)213 string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const* _messageType)
214 {
215 	string functionName =
216 		string(_assert ? "assert_helper" : "require_helper") +
217 		(_messageType ? ("_" + _messageType->identifier()) : "");
218 
219 	solAssert(!_assert || !_messageType, "Asserts can't have messages!");
220 
221 	return m_functionCollector.createFunction(functionName, [&]() {
222 		if (!_messageType)
223 			return Whiskers(R"(
224 				function <functionName>(condition) {
225 					if iszero(condition) { <error> }
226 				}
227 			)")
228 			("error", _assert ? panicFunction(PanicCode::Assert) + "()" : "revert(0, 0)")
229 			("functionName", functionName)
230 			.render();
231 
232 		int const hashHeaderSize = 4;
233 		u256 const errorHash = util::selectorFromSignature("Error(string)");
234 
235 		string const encodeFunc = ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector)
236 			.tupleEncoder(
237 				{_messageType},
238 				{TypeProvider::stringMemory()}
239 			);
240 
241 		return Whiskers(R"(
242 			function <functionName>(condition <messageVars>) {
243 				if iszero(condition) {
244 					let memPtr := <allocateUnbounded>()
245 					mstore(memPtr, <errorHash>)
246 					let end := <abiEncodeFunc>(add(memPtr, <hashHeaderSize>) <messageVars>)
247 					revert(memPtr, sub(end, memPtr))
248 				}
249 			}
250 		)")
251 		("functionName", functionName)
252 		("allocateUnbounded", allocateUnboundedFunction())
253 		("errorHash", formatNumber(errorHash))
254 		("abiEncodeFunc", encodeFunc)
255 		("hashHeaderSize", to_string(hashHeaderSize))
256 		("messageVars",
257 			(_messageType->sizeOnStack() > 0 ? ", " : "") +
258 			suffixedVariableNameList("message_", 1, 1 + _messageType->sizeOnStack())
259 		)
260 		.render();
261 	});
262 }
263 
leftAlignFunction(Type const & _type)264 string YulUtilFunctions::leftAlignFunction(Type const& _type)
265 {
266 	string functionName = string("leftAlign_") + _type.identifier();
267 	return m_functionCollector.createFunction(functionName, [&]() {
268 		Whiskers templ(R"(
269 			function <functionName>(value) -> aligned {
270 				<body>
271 			}
272 		)");
273 		templ("functionName", functionName);
274 		switch (_type.category())
275 		{
276 		case Type::Category::Address:
277 			templ("body", "aligned := " + leftAlignFunction(IntegerType(160)) + "(value)");
278 			break;
279 		case Type::Category::Integer:
280 		{
281 			IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
282 			if (type.numBits() == 256)
283 				templ("body", "aligned := value");
284 			else
285 				templ("body", "aligned := " + shiftLeftFunction(256 - type.numBits()) + "(value)");
286 			break;
287 		}
288 		case Type::Category::RationalNumber:
289 			solAssert(false, "Left align requested for rational number.");
290 			break;
291 		case Type::Category::Bool:
292 			templ("body", "aligned := " + leftAlignFunction(IntegerType(8)) + "(value)");
293 			break;
294 		case Type::Category::FixedPoint:
295 			solUnimplemented("Fixed point types not implemented.");
296 			break;
297 		case Type::Category::Array:
298 		case Type::Category::Struct:
299 			solAssert(false, "Left align requested for non-value type.");
300 			break;
301 		case Type::Category::FixedBytes:
302 			templ("body", "aligned := value");
303 			break;
304 		case Type::Category::Contract:
305 			templ("body", "aligned := " + leftAlignFunction(*TypeProvider::address()) + "(value)");
306 			break;
307 		case Type::Category::Enum:
308 		{
309 			solAssert(dynamic_cast<EnumType const&>(_type).storageBytes() == 1, "");
310 			templ("body", "aligned := " + leftAlignFunction(IntegerType(8)) + "(value)");
311 			break;
312 		}
313 		case Type::Category::InaccessibleDynamic:
314 			solAssert(false, "Left align requested for inaccessible dynamic type.");
315 			break;
316 		default:
317 			solAssert(false, "Left align of type " + _type.identifier() + " requested.");
318 		}
319 
320 		return templ.render();
321 	});
322 }
323 
shiftLeftFunction(size_t _numBits)324 string YulUtilFunctions::shiftLeftFunction(size_t _numBits)
325 {
326 	solAssert(_numBits < 256, "");
327 
328 	string functionName = "shift_left_" + to_string(_numBits);
329 	return m_functionCollector.createFunction(functionName, [&]() {
330 		return
331 			Whiskers(R"(
332 			function <functionName>(value) -> newValue {
333 				newValue :=
334 				<?hasShifts>
335 					shl(<numBits>, value)
336 				<!hasShifts>
337 					mul(value, <multiplier>)
338 				</hasShifts>
339 			}
340 			)")
341 			("functionName", functionName)
342 			("numBits", to_string(_numBits))
343 			("hasShifts", m_evmVersion.hasBitwiseShifting())
344 			("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
345 			.render();
346 	});
347 }
348 
shiftLeftFunctionDynamic()349 string YulUtilFunctions::shiftLeftFunctionDynamic()
350 {
351 	string functionName = "shift_left_dynamic";
352 	return m_functionCollector.createFunction(functionName, [&]() {
353 		return
354 			Whiskers(R"(
355 			function <functionName>(bits, value) -> newValue {
356 				newValue :=
357 				<?hasShifts>
358 					shl(bits, value)
359 				<!hasShifts>
360 					mul(value, exp(2, bits))
361 				</hasShifts>
362 			}
363 			)")
364 			("functionName", functionName)
365 			("hasShifts", m_evmVersion.hasBitwiseShifting())
366 			.render();
367 	});
368 }
369 
shiftRightFunction(size_t _numBits)370 string YulUtilFunctions::shiftRightFunction(size_t _numBits)
371 {
372 	solAssert(_numBits < 256, "");
373 
374 	// Note that if this is extended with signed shifts,
375 	// the opcodes SAR and SDIV behave differently with regards to rounding!
376 
377 	string functionName = "shift_right_" + to_string(_numBits) + "_unsigned";
378 	return m_functionCollector.createFunction(functionName, [&]() {
379 		return
380 			Whiskers(R"(
381 			function <functionName>(value) -> newValue {
382 				newValue :=
383 				<?hasShifts>
384 					shr(<numBits>, value)
385 				<!hasShifts>
386 					div(value, <multiplier>)
387 				</hasShifts>
388 			}
389 			)")
390 			("functionName", functionName)
391 			("hasShifts", m_evmVersion.hasBitwiseShifting())
392 			("numBits", to_string(_numBits))
393 			("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
394 			.render();
395 	});
396 }
397 
shiftRightFunctionDynamic()398 string YulUtilFunctions::shiftRightFunctionDynamic()
399 {
400 	string const functionName = "shift_right_unsigned_dynamic";
401 	return m_functionCollector.createFunction(functionName, [&]() {
402 		return
403 			Whiskers(R"(
404 			function <functionName>(bits, value) -> newValue {
405 				newValue :=
406 				<?hasShifts>
407 					shr(bits, value)
408 				<!hasShifts>
409 					div(value, exp(2, bits))
410 				</hasShifts>
411 			}
412 			)")
413 			("functionName", functionName)
414 			("hasShifts", m_evmVersion.hasBitwiseShifting())
415 			.render();
416 	});
417 }
418 
shiftRightSignedFunctionDynamic()419 string YulUtilFunctions::shiftRightSignedFunctionDynamic()
420 {
421 	string const functionName = "shift_right_signed_dynamic";
422 	return m_functionCollector.createFunction(functionName, [&]() {
423 		return
424 			Whiskers(R"(
425 			function <functionName>(bits, value) -> result {
426 				<?hasShifts>
427 					result := sar(bits, value)
428 				<!hasShifts>
429 					let divisor := exp(2, bits)
430 					let xor_mask := sub(0, slt(value, 0))
431 					result := xor(div(xor(value, xor_mask), divisor), xor_mask)
432 					// combined version of
433 					//   switch slt(value, 0)
434 					//   case 0 { result := div(value, divisor) }
435 					//   default { result := not(div(not(value), divisor)) }
436 				</hasShifts>
437 			}
438 			)")
439 			("functionName", functionName)
440 			("hasShifts", m_evmVersion.hasBitwiseShifting())
441 			.render();
442 	});
443 }
444 
445 
typedShiftLeftFunction(Type const & _type,Type const & _amountType)446 string YulUtilFunctions::typedShiftLeftFunction(Type const& _type, Type const& _amountType)
447 {
448 	solUnimplementedAssert(_type.category() != Type::Category::FixedPoint, "Not yet implemented - FixedPointType.");
449 	solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, "");
450 	solAssert(_amountType.category() == Type::Category::Integer, "");
451 	solAssert(!dynamic_cast<IntegerType const&>(_amountType).isSigned(), "");
452 	string const functionName = "shift_left_" + _type.identifier() + "_" + _amountType.identifier();
453 	return m_functionCollector.createFunction(functionName, [&]() {
454 		return
455 			Whiskers(R"(
456 			function <functionName>(value, bits) -> result {
457 				bits := <cleanAmount>(bits)
458 				result := <cleanup>(<shift>(bits, <cleanup>(value)))
459 			}
460 			)")
461 			("functionName", functionName)
462 			("cleanAmount", cleanupFunction(_amountType))
463 			("shift", shiftLeftFunctionDynamic())
464 			("cleanup", cleanupFunction(_type))
465 			.render();
466 	});
467 }
468 
typedShiftRightFunction(Type const & _type,Type const & _amountType)469 string YulUtilFunctions::typedShiftRightFunction(Type const& _type, Type const& _amountType)
470 {
471 	solUnimplementedAssert(_type.category() != Type::Category::FixedPoint, "Not yet implemented - FixedPointType.");
472 	solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, "");
473 	solAssert(_amountType.category() == Type::Category::Integer, "");
474 	solAssert(!dynamic_cast<IntegerType const&>(_amountType).isSigned(), "");
475 	IntegerType const* integerType = dynamic_cast<IntegerType const*>(&_type);
476 	bool valueSigned = integerType && integerType->isSigned();
477 
478 	string const functionName = "shift_right_" + _type.identifier() + "_" + _amountType.identifier();
479 	return m_functionCollector.createFunction(functionName, [&]() {
480 		return
481 			Whiskers(R"(
482 			function <functionName>(value, bits) -> result {
483 				bits := <cleanAmount>(bits)
484 				result := <cleanup>(<shift>(bits, <cleanup>(value)))
485 			}
486 			)")
487 			("functionName", functionName)
488 			("cleanAmount", cleanupFunction(_amountType))
489 			("shift", valueSigned ? shiftRightSignedFunctionDynamic() : shiftRightFunctionDynamic())
490 			("cleanup", cleanupFunction(_type))
491 			.render();
492 	});
493 }
494 
updateByteSliceFunction(size_t _numBytes,size_t _shiftBytes)495 string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes)
496 {
497 	solAssert(_numBytes <= 32, "");
498 	solAssert(_shiftBytes <= 32, "");
499 	size_t numBits = _numBytes * 8;
500 	size_t shiftBits = _shiftBytes * 8;
501 	string functionName = "update_byte_slice_" + to_string(_numBytes) + "_shift_" + to_string(_shiftBytes);
502 	return m_functionCollector.createFunction(functionName, [&]() {
503 		return
504 			Whiskers(R"(
505 			function <functionName>(value, toInsert) -> result {
506 				let mask := <mask>
507 				toInsert := <shl>(toInsert)
508 				value := and(value, not(mask))
509 				result := or(value, and(toInsert, mask))
510 			}
511 			)")
512 			("functionName", functionName)
513 			("mask", formatNumber(((bigint(1) << numBits) - 1) << shiftBits))
514 			("shl", shiftLeftFunction(shiftBits))
515 			.render();
516 	});
517 }
518 
updateByteSliceFunctionDynamic(size_t _numBytes)519 string YulUtilFunctions::updateByteSliceFunctionDynamic(size_t _numBytes)
520 {
521 	solAssert(_numBytes <= 32, "");
522 	size_t numBits = _numBytes * 8;
523 	string functionName = "update_byte_slice_dynamic" + to_string(_numBytes);
524 	return m_functionCollector.createFunction(functionName, [&]() {
525 		return
526 			Whiskers(R"(
527 			function <functionName>(value, shiftBytes, toInsert) -> result {
528 				let shiftBits := mul(shiftBytes, 8)
529 				let mask := <shl>(shiftBits, <mask>)
530 				toInsert := <shl>(shiftBits, toInsert)
531 				value := and(value, not(mask))
532 				result := or(value, and(toInsert, mask))
533 			}
534 			)")
535 			("functionName", functionName)
536 			("mask", formatNumber((bigint(1) << numBits) - 1))
537 			("shl", shiftLeftFunctionDynamic())
538 			.render();
539 	});
540 }
541 
maskBytesFunctionDynamic()542 string YulUtilFunctions::maskBytesFunctionDynamic()
543 {
544 	string functionName = "mask_bytes_dynamic";
545 	return m_functionCollector.createFunction(functionName, [&]() {
546 		return Whiskers(R"(
547 			function <functionName>(data, bytes) -> result {
548 				let mask := not(<shr>(mul(8, bytes), not(0)))
549 				result := and(data, mask)
550 			})")
551 			("functionName", functionName)
552 			("shr", shiftRightFunctionDynamic())
553 			.render();
554 	});
555 }
556 
maskLowerOrderBytesFunction(size_t _bytes)557 string YulUtilFunctions::maskLowerOrderBytesFunction(size_t _bytes)
558 {
559 	string functionName = "mask_lower_order_bytes_" + to_string(_bytes);
560 	solAssert(_bytes <= 32, "");
561 	return m_functionCollector.createFunction(functionName, [&]() {
562 		return Whiskers(R"(
563 			function <functionName>(data) -> result {
564 				result := and(data, <mask>)
565 			})")
566 			("functionName", functionName)
567 			("mask", formatNumber((~u256(0)) >> (256 - 8 * _bytes)))
568 			.render();
569 	});
570 }
571 
maskLowerOrderBytesFunctionDynamic()572 string YulUtilFunctions::maskLowerOrderBytesFunctionDynamic()
573 {
574 	string functionName = "mask_lower_order_bytes_dynamic";
575 	return m_functionCollector.createFunction(functionName, [&]() {
576 		return Whiskers(R"(
577 			function <functionName>(data, bytes) -> result {
578 				let mask := not(<shl>(mul(8, bytes), not(0)))
579 				result := and(data, mask)
580 			})")
581 			("functionName", functionName)
582 			("shl", shiftLeftFunctionDynamic())
583 			.render();
584 	});
585 }
586 
roundUpFunction()587 string YulUtilFunctions::roundUpFunction()
588 {
589 	string functionName = "round_up_to_mul_of_32";
590 	return m_functionCollector.createFunction(functionName, [&]() {
591 		return
592 			Whiskers(R"(
593 			function <functionName>(value) -> result {
594 				result := and(add(value, 31), not(31))
595 			}
596 			)")
597 			("functionName", functionName)
598 			.render();
599 	});
600 }
601 
divide32CeilFunction()602 string YulUtilFunctions::divide32CeilFunction()
603 {
604 	return m_functionCollector.createFunction(
605 		"divide_by_32_ceil",
606 		[&](vector<string>& _args, vector<string>& _ret) {
607 			_args = {"value"};
608 			_ret = {"result"};
609 			return "result := div(add(value, 31), 32)";
610 		}
611 	);
612 }
613 
overflowCheckedIntAddFunction(IntegerType const & _type)614 string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
615 {
616 	string functionName = "checked_add_" + _type.identifier();
617 	// TODO: Consider to add a special case for unsigned 256-bit integers
618 	//       and use the following instead:
619 	//       sum := add(x, y) if lt(sum, x) { <panic>() }
620 	return m_functionCollector.createFunction(functionName, [&]() {
621 		return
622 			Whiskers(R"(
623 			function <functionName>(x, y) -> sum {
624 				x := <cleanupFunction>(x)
625 				y := <cleanupFunction>(y)
626 				<?signed>
627 					// overflow, if x >= 0 and y > (maxValue - x)
628 					if and(iszero(slt(x, 0)), sgt(y, sub(<maxValue>, x))) { <panic>() }
629 					// underflow, if x < 0 and y < (minValue - x)
630 					if and(slt(x, 0), slt(y, sub(<minValue>, x))) { <panic>() }
631 				<!signed>
632 					// overflow, if x > (maxValue - y)
633 					if gt(x, sub(<maxValue>, y)) { <panic>() }
634 				</signed>
635 				sum := add(x, y)
636 			}
637 			)")
638 			("functionName", functionName)
639 			("signed", _type.isSigned())
640 			("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
641 			("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
642 			("cleanupFunction", cleanupFunction(_type))
643 			("panic", panicFunction(PanicCode::UnderOverflow))
644 			.render();
645 	});
646 }
647 
wrappingIntAddFunction(IntegerType const & _type)648 string YulUtilFunctions::wrappingIntAddFunction(IntegerType const& _type)
649 {
650 	string functionName = "wrapping_add_" + _type.identifier();
651 	return m_functionCollector.createFunction(functionName, [&]() {
652 		return
653 			Whiskers(R"(
654 			function <functionName>(x, y) -> sum {
655 				sum := <cleanupFunction>(add(x, y))
656 			}
657 			)")
658 			("functionName", functionName)
659 			("cleanupFunction", cleanupFunction(_type))
660 			.render();
661 	});
662 }
663 
overflowCheckedIntMulFunction(IntegerType const & _type)664 string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
665 {
666 	string functionName = "checked_mul_" + _type.identifier();
667 	return m_functionCollector.createFunction(functionName, [&]() {
668 		return
669 			// Multiplication by zero could be treated separately and directly return zero.
670 			Whiskers(R"(
671 			function <functionName>(x, y) -> product {
672 				x := <cleanupFunction>(x)
673 				y := <cleanupFunction>(y)
674 				<?signed>
675 					// overflow, if x > 0, y > 0 and x > (maxValue / y)
676 					if and(and(sgt(x, 0), sgt(y, 0)), gt(x, div(<maxValue>, y))) { <panic>() }
677 					// underflow, if x > 0, y < 0 and y < (minValue / x)
678 					if and(and(sgt(x, 0), slt(y, 0)), slt(y, sdiv(<minValue>, x))) { <panic>() }
679 					// underflow, if x < 0, y > 0 and x < (minValue / y)
680 					if and(and(slt(x, 0), sgt(y, 0)), slt(x, sdiv(<minValue>, y))) { <panic>() }
681 					// overflow, if x < 0, y < 0 and x < (maxValue / y)
682 					if and(and(slt(x, 0), slt(y, 0)), slt(x, sdiv(<maxValue>, y))) { <panic>() }
683 				<!signed>
684 					// overflow, if x != 0 and y > (maxValue / x)
685 					if and(iszero(iszero(x)), gt(y, div(<maxValue>, x))) { <panic>() }
686 				</signed>
687 				product := mul(x, y)
688 			}
689 			)")
690 			("functionName", functionName)
691 			("signed", _type.isSigned())
692 			("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
693 			("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
694 			("cleanupFunction", cleanupFunction(_type))
695 			("panic", panicFunction(PanicCode::UnderOverflow))
696 			.render();
697 	});
698 }
699 
wrappingIntMulFunction(IntegerType const & _type)700 string YulUtilFunctions::wrappingIntMulFunction(IntegerType const& _type)
701 {
702 	string functionName = "wrapping_mul_" + _type.identifier();
703 	return m_functionCollector.createFunction(functionName, [&]() {
704 		return
705 			Whiskers(R"(
706 			function <functionName>(x, y) -> product {
707 				product := <cleanupFunction>(mul(x, y))
708 			}
709 			)")
710 			("functionName", functionName)
711 			("cleanupFunction", cleanupFunction(_type))
712 			.render();
713 	});
714 }
715 
overflowCheckedIntDivFunction(IntegerType const & _type)716 string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
717 {
718 	string functionName = "checked_div_" + _type.identifier();
719 	return m_functionCollector.createFunction(functionName, [&]() {
720 		return
721 			Whiskers(R"(
722 			function <functionName>(x, y) -> r {
723 				x := <cleanupFunction>(x)
724 				y := <cleanupFunction>(y)
725 				if iszero(y) { <panicDivZero>() }
726 				<?signed>
727 				// overflow for minVal / -1
728 				if and(
729 					eq(x, <minVal>),
730 					eq(y, sub(0, 1))
731 				) { <panicOverflow>() }
732 				</signed>
733 				r := <?signed>s</signed>div(x, y)
734 			}
735 			)")
736 			("functionName", functionName)
737 			("signed", _type.isSigned())
738 			("minVal", toCompactHexWithPrefix(u256(_type.minValue())))
739 			("cleanupFunction", cleanupFunction(_type))
740 			("panicDivZero", panicFunction(PanicCode::DivisionByZero))
741 			("panicOverflow", panicFunction(PanicCode::UnderOverflow))
742 			.render();
743 	});
744 }
745 
wrappingIntDivFunction(IntegerType const & _type)746 string YulUtilFunctions::wrappingIntDivFunction(IntegerType const& _type)
747 {
748 	string functionName = "wrapping_div_" + _type.identifier();
749 	return m_functionCollector.createFunction(functionName, [&]() {
750 		return
751 			Whiskers(R"(
752 			function <functionName>(x, y) -> r {
753 				x := <cleanupFunction>(x)
754 				y := <cleanupFunction>(y)
755 				if iszero(y) { <error>() }
756 				r := <?signed>s</signed>div(x, y)
757 			}
758 			)")
759 			("functionName", functionName)
760 			("cleanupFunction", cleanupFunction(_type))
761 			("signed", _type.isSigned())
762 			("error", panicFunction(PanicCode::DivisionByZero))
763 			.render();
764 	});
765 }
766 
intModFunction(IntegerType const & _type)767 string YulUtilFunctions::intModFunction(IntegerType const& _type)
768 {
769 	string functionName = "mod_" + _type.identifier();
770 	return m_functionCollector.createFunction(functionName, [&]() {
771 		return
772 			Whiskers(R"(
773 			function <functionName>(x, y) -> r {
774 				x := <cleanupFunction>(x)
775 				y := <cleanupFunction>(y)
776 				if iszero(y) { <panic>() }
777 				r := <?signed>s</signed>mod(x, y)
778 			}
779 			)")
780 			("functionName", functionName)
781 			("signed", _type.isSigned())
782 			("cleanupFunction", cleanupFunction(_type))
783 			("panic", panicFunction(PanicCode::DivisionByZero))
784 			.render();
785 	});
786 }
787 
overflowCheckedIntSubFunction(IntegerType const & _type)788 string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
789 {
790 	string functionName = "checked_sub_" + _type.identifier();
791 	return m_functionCollector.createFunction(functionName, [&] {
792 		return
793 			Whiskers(R"(
794 			function <functionName>(x, y) -> diff {
795 				x := <cleanupFunction>(x)
796 				y := <cleanupFunction>(y)
797 				<?signed>
798 					// underflow, if y >= 0 and x < (minValue + y)
799 					if and(iszero(slt(y, 0)), slt(x, add(<minValue>, y))) { <panic>() }
800 					// overflow, if y < 0 and x > (maxValue + y)
801 					if and(slt(y, 0), sgt(x, add(<maxValue>, y))) { <panic>() }
802 				<!signed>
803 					if lt(x, y) { <panic>() }
804 				</signed>
805 				diff := sub(x, y)
806 			}
807 			)")
808 			("functionName", functionName)
809 			("signed", _type.isSigned())
810 			("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
811 			("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
812 			("cleanupFunction", cleanupFunction(_type))
813 			("panic", panicFunction(PanicCode::UnderOverflow))
814 			.render();
815 	});
816 }
817 
wrappingIntSubFunction(IntegerType const & _type)818 string YulUtilFunctions::wrappingIntSubFunction(IntegerType const& _type)
819 {
820 	string functionName = "wrapping_sub_" + _type.identifier();
821 	return m_functionCollector.createFunction(functionName, [&] {
822 		return
823 			Whiskers(R"(
824 			function <functionName>(x, y) -> diff {
825 				diff := <cleanupFunction>(sub(x, y))
826 			}
827 			)")
828 			("functionName", functionName)
829 			("cleanupFunction", cleanupFunction(_type))
830 			.render();
831 	});
832 }
833 
overflowCheckedIntExpFunction(IntegerType const & _type,IntegerType const & _exponentType)834 string YulUtilFunctions::overflowCheckedIntExpFunction(
835 	IntegerType const& _type,
836 	IntegerType const& _exponentType
837 )
838 {
839 	solAssert(!_exponentType.isSigned(), "");
840 
841 	string functionName = "checked_exp_" + _type.identifier() + "_" + _exponentType.identifier();
842 	return m_functionCollector.createFunction(functionName, [&]() {
843 		return
844 			Whiskers(R"(
845 			function <functionName>(base, exponent) -> power {
846 				base := <baseCleanupFunction>(base)
847 				exponent := <exponentCleanupFunction>(exponent)
848 				<?signed>
849 					power := <exp>(base, exponent, <minValue>, <maxValue>)
850 				<!signed>
851 					power := <exp>(base, exponent, <maxValue>)
852 				</signed>
853 
854 			}
855 			)")
856 			("functionName", functionName)
857 			("signed", _type.isSigned())
858 			("exp", _type.isSigned() ? overflowCheckedSignedExpFunction() : overflowCheckedUnsignedExpFunction())
859 			("maxValue", toCompactHexWithPrefix(_type.max()))
860 			("minValue", toCompactHexWithPrefix(_type.min()))
861 			("baseCleanupFunction", cleanupFunction(_type))
862 			("exponentCleanupFunction", cleanupFunction(_exponentType))
863 			.render();
864 	});
865 }
866 
overflowCheckedIntLiteralExpFunction(RationalNumberType const & _baseType,IntegerType const & _exponentType,IntegerType const & _commonType)867 string YulUtilFunctions::overflowCheckedIntLiteralExpFunction(
868 	RationalNumberType const& _baseType,
869 	IntegerType const& _exponentType,
870 	IntegerType const& _commonType
871 )
872 {
873 	solAssert(!_exponentType.isSigned(), "");
874 	solAssert(_baseType.isNegative() == _commonType.isSigned(), "");
875 	solAssert(_commonType.numBits() == 256, "");
876 
877 	string functionName = "checked_exp_" + _baseType.richIdentifier() + "_" + _exponentType.identifier();
878 
879 	return m_functionCollector.createFunction(functionName, [&]()
880 	{
881 		// Converts a bigint number into u256 (negative numbers represented in two's complement form.)
882 		// We assume that `_v` fits in 256 bits.
883 		auto bigint2u = [&](bigint const& _v) -> u256
884 		{
885 			if (_v < 0)
886 				return s2u(s256(_v));
887 			return u256(_v);
888 		};
889 
890 		// Calculates the upperbound for exponentiation, that is, calculate `b`, such that
891 		// _base**b <= _maxValue and _base**(b + 1) > _maxValue
892 		auto findExponentUpperbound = [](bigint const _base, bigint const _maxValue) -> unsigned
893 		{
894 			// There is no overflow for these cases
895 			if (_base == 0 || _base == -1 || _base == 1)
896 				return 0;
897 
898 			unsigned first = 0;
899 			unsigned last = 255;
900 			unsigned middle;
901 
902 			while (first < last)
903 			{
904 				middle = (first + last) / 2;
905 
906 				if (
907 					// The condition on msb is a shortcut that avoids computing large powers in
908 					// arbitrary precision.
909 					boost::multiprecision::msb(_base) * middle <= boost::multiprecision::msb(_maxValue) &&
910 					boost::multiprecision::pow(_base, middle) <= _maxValue
911 				)
912 				{
913 					if (boost::multiprecision::pow(_base, middle + 1) > _maxValue)
914 						return middle;
915 					else
916 						first = middle + 1;
917 				}
918 				else
919 					last = middle;
920 			}
921 
922 			return last;
923 		};
924 
925 		bigint baseValue = _baseType.isNegative() ?
926 			u2s(_baseType.literalValue(nullptr)) :
927 			_baseType.literalValue(nullptr);
928 		bool needsOverflowCheck = !((baseValue == 0) || (baseValue == -1) || (baseValue == 1));
929 		unsigned exponentUpperbound;
930 
931 		if (_baseType.isNegative())
932 		{
933 			// Only checks for underflow. The only case where this can be a problem is when, for a
934 			// negative base, say `b`, and an even exponent, say `e`, `b**e = 2**255` (which is an
935 			// overflow.) But this never happens because, `255 = 3*5*17`, and therefore there is no even
936 			// number `e` such that `b**e = 2**255`.
937 			exponentUpperbound = findExponentUpperbound(abs(baseValue), abs(_commonType.minValue()));
938 
939 			bigint power = boost::multiprecision::pow(baseValue, exponentUpperbound);
940 			bigint overflowedPower = boost::multiprecision::pow(baseValue, exponentUpperbound + 1);
941 
942 			if (needsOverflowCheck)
943 				solAssert(
944 					(power <= _commonType.maxValue()) && (power >= _commonType.minValue()) &&
945 					!((overflowedPower <= _commonType.maxValue()) && (overflowedPower >= _commonType.minValue())),
946 					"Incorrect exponent upper bound calculated."
947 				);
948 		}
949 		else
950 		{
951 			exponentUpperbound = findExponentUpperbound(baseValue, _commonType.maxValue());
952 
953 			if (needsOverflowCheck)
954 				solAssert(
955 					boost::multiprecision::pow(baseValue, exponentUpperbound) <= _commonType.maxValue() &&
956 					boost::multiprecision::pow(baseValue, exponentUpperbound + 1) > _commonType.maxValue(),
957 					"Incorrect exponent upper bound calculated."
958 				);
959 		}
960 
961 		return Whiskers(R"(
962 			function <functionName>(exponent) -> power {
963 				exponent := <exponentCleanupFunction>(exponent)
964 				<?needsOverflowCheck>
965 				if gt(exponent, <exponentUpperbound>) { <panic>() }
966 				</needsOverflowCheck>
967 				power := exp(<base>, exponent)
968 			}
969 			)")
970 			("functionName", functionName)
971 			("exponentCleanupFunction", cleanupFunction(_exponentType))
972 			("needsOverflowCheck", needsOverflowCheck)
973 			("exponentUpperbound", to_string(exponentUpperbound))
974 			("panic", panicFunction(PanicCode::UnderOverflow))
975 			("base", bigint2u(baseValue).str())
976 			.render();
977 	});
978 }
979 
overflowCheckedUnsignedExpFunction()980 string YulUtilFunctions::overflowCheckedUnsignedExpFunction()
981 {
982 	// Checks for the "small number specialization" below.
983 	using namespace boost::multiprecision;
984 	solAssert(pow(bigint(10), 77) < pow(bigint(2), 256), "");
985 	solAssert(pow(bigint(11), 77) >= pow(bigint(2), 256), "");
986 	solAssert(pow(bigint(10), 78) >= pow(bigint(2), 256), "");
987 
988 	solAssert(pow(bigint(306), 31) < pow(bigint(2), 256), "");
989 	solAssert(pow(bigint(307), 31) >= pow(bigint(2), 256), "");
990 	solAssert(pow(bigint(306), 32) >= pow(bigint(2), 256), "");
991 
992 	string functionName = "checked_exp_unsigned";
993 	return m_functionCollector.createFunction(functionName, [&]() {
994 		return
995 			Whiskers(R"(
996 			function <functionName>(base, exponent, max) -> power {
997 				// This function currently cannot be inlined because of the
998 				// "leave" statements. We have to improve the optimizer.
999 
1000 				// Note that 0**0 == 1
1001 				if iszero(exponent) { power := 1 leave }
1002 				if iszero(base) { power := 0 leave }
1003 
1004 				// Specializations for small bases
1005 				switch base
1006 				// 0 is handled above
1007 				case 1 { power := 1 leave }
1008 				case 2
1009 				{
1010 					if gt(exponent, 255) { <panic>() }
1011 					power := exp(2, exponent)
1012 					if gt(power, max) { <panic>() }
1013 					leave
1014 				}
1015 				if or(
1016 					and(lt(base, 11), lt(exponent, 78)),
1017 					and(lt(base, 307), lt(exponent, 32))
1018 				)
1019 				{
1020 					power := exp(base, exponent)
1021 					if gt(power, max) { <panic>() }
1022 					leave
1023 				}
1024 
1025 				power, base := <expLoop>(1, base, exponent, max)
1026 
1027 				if gt(power, div(max, base)) { <panic>() }
1028 				power := mul(power, base)
1029 			}
1030 			)")
1031 			("functionName", functionName)
1032 			("panic", panicFunction(PanicCode::UnderOverflow))
1033 			("expLoop", overflowCheckedExpLoopFunction())
1034 			.render();
1035 	});
1036 }
1037 
overflowCheckedSignedExpFunction()1038 string YulUtilFunctions::overflowCheckedSignedExpFunction()
1039 {
1040 	string functionName = "checked_exp_signed";
1041 	return m_functionCollector.createFunction(functionName, [&]() {
1042 		return
1043 			Whiskers(R"(
1044 			function <functionName>(base, exponent, min, max) -> power {
1045 				// Currently, `leave` avoids this function being inlined.
1046 				// We have to improve the optimizer.
1047 
1048 				// Note that 0**0 == 1
1049 				switch exponent
1050 				case 0 { power := 1 leave }
1051 				case 1 { power := base leave }
1052 				if iszero(base) { power := 0 leave }
1053 
1054 				power := 1
1055 
1056 				// We pull out the first iteration because it is the only one in which
1057 				// base can be negative.
1058 				// Exponent is at least 2 here.
1059 
1060 				// overflow check for base * base
1061 				switch sgt(base, 0)
1062 				case 1 { if gt(base, div(max, base)) { <panic>() } }
1063 				case 0 { if slt(base, sdiv(max, base)) { <panic>() } }
1064 				if and(exponent, 1)
1065 				{
1066 					power := base
1067 				}
1068 				base := mul(base, base)
1069 				exponent := <shr_1>(exponent)
1070 
1071 				// Below this point, base is always positive.
1072 
1073 				power, base := <expLoop>(power, base, exponent, max)
1074 
1075 				if and(sgt(power, 0), gt(power, div(max, base))) { <panic>() }
1076 				if and(slt(power, 0), slt(power, sdiv(min, base))) { <panic>() }
1077 				power := mul(power, base)
1078 			}
1079 			)")
1080 			("functionName", functionName)
1081 			("panic", panicFunction(PanicCode::UnderOverflow))
1082 			("expLoop", overflowCheckedExpLoopFunction())
1083 			("shr_1", shiftRightFunction(1))
1084 			.render();
1085 	});
1086 }
1087 
overflowCheckedExpLoopFunction()1088 string YulUtilFunctions::overflowCheckedExpLoopFunction()
1089 {
1090 	// We use this loop for both signed and unsigned exponentiation
1091 	// because we pull out the first iteration in the signed case which
1092 	// results in the base always being positive.
1093 
1094 	// This function does not include the final multiplication.
1095 
1096 	string functionName = "checked_exp_helper";
1097 	return m_functionCollector.createFunction(functionName, [&]() {
1098 		return
1099 			Whiskers(R"(
1100 			function <functionName>(_power, _base, exponent, max) -> power, base {
1101 				power := _power
1102 				base  := _base
1103 				for { } gt(exponent, 1) {}
1104 				{
1105 					// overflow check for base * base
1106 					if gt(base, div(max, base)) { <panic>() }
1107 					if and(exponent, 1)
1108 					{
1109 						// No checks for power := mul(power, base) needed, because the check
1110 						// for base * base above is sufficient, since:
1111 						// |power| <= base (proof by induction) and thus:
1112 						// |power * base| <= base * base <= max <= |min| (for signed)
1113 						// (this is equally true for signed and unsigned exp)
1114 						power := mul(power, base)
1115 					}
1116 					base := mul(base, base)
1117 					exponent := <shr_1>(exponent)
1118 				}
1119 			}
1120 			)")
1121 			("functionName", functionName)
1122 			("panic", panicFunction(PanicCode::UnderOverflow))
1123 			("shr_1", shiftRightFunction(1))
1124 			.render();
1125 	});
1126 }
1127 
wrappingIntExpFunction(IntegerType const & _type,IntegerType const & _exponentType)1128 string YulUtilFunctions::wrappingIntExpFunction(
1129 	IntegerType const& _type,
1130 	IntegerType const& _exponentType
1131 )
1132 {
1133 	solAssert(!_exponentType.isSigned(), "");
1134 
1135 	string functionName = "wrapping_exp_" + _type.identifier() + "_" + _exponentType.identifier();
1136 	return m_functionCollector.createFunction(functionName, [&]() {
1137 		return
1138 			Whiskers(R"(
1139 			function <functionName>(base, exponent) -> power {
1140 				base := <baseCleanupFunction>(base)
1141 				exponent := <exponentCleanupFunction>(exponent)
1142 				power := <baseCleanupFunction>(exp(base, exponent))
1143 			}
1144 			)")
1145 			("functionName", functionName)
1146 			("baseCleanupFunction", cleanupFunction(_type))
1147 			("exponentCleanupFunction", cleanupFunction(_exponentType))
1148 			.render();
1149 	});
1150 }
1151 
arrayLengthFunction(ArrayType const & _type)1152 string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type)
1153 {
1154 	string functionName = "array_length_" + _type.identifier();
1155 	return m_functionCollector.createFunction(functionName, [&]() {
1156 		Whiskers w(R"(
1157 			function <functionName>(value<?dynamic><?calldata>, len</calldata></dynamic>) -> length {
1158 				<?dynamic>
1159 					<?memory>
1160 						length := mload(value)
1161 					</memory>
1162 					<?storage>
1163 						length := sload(value)
1164 						<?byteArray>
1165 							length := <extractByteArrayLength>(length)
1166 						</byteArray>
1167 					</storage>
1168 					<?calldata>
1169 						length := len
1170 					</calldata>
1171 				<!dynamic>
1172 					length := <length>
1173 				</dynamic>
1174 			}
1175 		)");
1176 		w("functionName", functionName);
1177 		w("dynamic", _type.isDynamicallySized());
1178 		if (!_type.isDynamicallySized())
1179 			w("length", toCompactHexWithPrefix(_type.length()));
1180 		w("memory", _type.location() == DataLocation::Memory);
1181 		w("storage", _type.location() == DataLocation::Storage);
1182 		w("calldata", _type.location() == DataLocation::CallData);
1183 		if (_type.location() == DataLocation::Storage)
1184 		{
1185 			w("byteArray", _type.isByteArray());
1186 			if (_type.isByteArray())
1187 				w("extractByteArrayLength", extractByteArrayLengthFunction());
1188 		}
1189 
1190 		return w.render();
1191 	});
1192 }
1193 
extractByteArrayLengthFunction()1194 string YulUtilFunctions::extractByteArrayLengthFunction()
1195 {
1196 	string functionName = "extract_byte_array_length";
1197 	return m_functionCollector.createFunction(functionName, [&]() {
1198 		Whiskers w(R"(
1199 			function <functionName>(data) -> length {
1200 				length := div(data, 2)
1201 				let outOfPlaceEncoding := and(data, 1)
1202 				if iszero(outOfPlaceEncoding) {
1203 					length := and(length, 0x7f)
1204 				}
1205 
1206 				if eq(outOfPlaceEncoding, lt(length, 32)) {
1207 					<panic>()
1208 				}
1209 			}
1210 		)");
1211 		w("functionName", functionName);
1212 		w("panic", panicFunction(PanicCode::StorageEncodingError));
1213 		return w.render();
1214 	});
1215 }
1216 
resizeArrayFunction(ArrayType const & _type)1217 std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type)
1218 {
1219 	solAssert(_type.location() == DataLocation::Storage, "");
1220 	solUnimplementedAssert(_type.baseType()->storageBytes() <= 32);
1221 
1222 	if (_type.isByteArray())
1223 		return resizeDynamicByteArrayFunction(_type);
1224 
1225 	string functionName = "resize_array_" + _type.identifier();
1226 	return m_functionCollector.createFunction(functionName, [&]() {
1227 		Whiskers templ(R"(
1228 			function <functionName>(array, newLen) {
1229 				if gt(newLen, <maxArrayLength>) {
1230 					<panic>()
1231 				}
1232 
1233 				let oldLen := <fetchLength>(array)
1234 
1235 				<?isDynamic>
1236 					// Store new length
1237 					sstore(array, newLen)
1238 				</isDynamic>
1239 
1240 				<?needsClearing>
1241 					<cleanUpArrayEnd>(array, oldLen, newLen)
1242 				</needsClearing>
1243 			})");
1244 			templ("functionName", functionName);
1245 			templ("maxArrayLength", (u256(1) << 64).str());
1246 			templ("panic", panicFunction(util::PanicCode::ResourceError));
1247 			templ("fetchLength", arrayLengthFunction(_type));
1248 			templ("isDynamic", _type.isDynamicallySized());
1249 			bool isMappingBase = _type.baseType()->category() == Type::Category::Mapping;
1250 			templ("needsClearing", !isMappingBase);
1251 			if (!isMappingBase)
1252 				templ("cleanUpArrayEnd", cleanUpStorageArrayEndFunction(_type));
1253 			return templ.render();
1254 	});
1255 }
1256 
cleanUpStorageArrayEndFunction(ArrayType const & _type)1257 string YulUtilFunctions::cleanUpStorageArrayEndFunction(ArrayType const& _type)
1258 {
1259 	solAssert(_type.location() == DataLocation::Storage, "");
1260 	solAssert(_type.baseType()->category() != Type::Category::Mapping, "");
1261 	solAssert(!_type.isByteArray(), "");
1262 	solUnimplementedAssert(_type.baseType()->storageBytes() <= 32);
1263 
1264 	string functionName = "cleanup_storage_array_end_" + _type.identifier();
1265 	return m_functionCollector.createFunction(functionName, [&](vector<string>& _args, vector<string>&) {
1266 		_args = {"array", "len", "startIndex"};
1267 		return Whiskers(R"(
1268 			if lt(startIndex, len) {
1269 				// Size was reduced, clear end of array
1270 				let oldSlotCount := <convertToSize>(len)
1271 				let newSlotCount := <convertToSize>(startIndex)
1272 				let arrayDataStart := <dataPosition>(array)
1273 				let deleteStart := add(arrayDataStart, newSlotCount)
1274 				let deleteEnd := add(arrayDataStart, oldSlotCount)
1275 				<?packed>
1276 					// if we are dealing with packed array and offset is greater than zero
1277 					// we have  to partially clear last slot that is still used, so decreasing start by one
1278 					let offset := mul(mod(startIndex, <itemsPerSlot>), <storageBytes>)
1279 					if gt(offset, 0) { <partialClearStorageSlot>(sub(deleteStart, 1), offset) }
1280 				</packed>
1281 				<clearStorageRange>(deleteStart, deleteEnd)
1282 			}
1283 		)")
1284 		("convertToSize", arrayConvertLengthToSize(_type))
1285 		("dataPosition", arrayDataAreaFunction(_type))
1286 		("clearStorageRange", clearStorageRangeFunction(*_type.baseType()))
1287 		("packed", _type.baseType()->storageBytes() <= 16)
1288 		("itemsPerSlot", to_string(32 / _type.baseType()->storageBytes()))
1289 		("storageBytes", to_string(_type.baseType()->storageBytes()))
1290 		("partialClearStorageSlot", partialClearStorageSlotFunction())
1291 		.render();
1292 	});
1293 }
1294 
resizeDynamicByteArrayFunction(ArrayType const & _type)1295 string YulUtilFunctions::resizeDynamicByteArrayFunction(ArrayType const& _type)
1296 {
1297 	string functionName = "resize_array_" + _type.identifier();
1298 	return m_functionCollector.createFunction(functionName, [&](vector<string>& _args, vector<string>&) {
1299 		_args = {"array", "newLen"};
1300 		return Whiskers(R"(
1301 			let data := sload(array)
1302 			let oldLen := <extractLength>(data)
1303 
1304 			if gt(newLen, oldLen) {
1305 				<increaseSize>(array, data, oldLen, newLen)
1306 			}
1307 
1308 			if lt(newLen, oldLen) {
1309 				<decreaseSize>(array, data, oldLen, newLen)
1310 			}
1311 		)")
1312 		("extractLength", extractByteArrayLengthFunction())
1313 		("decreaseSize", decreaseByteArraySizeFunction(_type))
1314 		("increaseSize", increaseByteArraySizeFunction(_type))
1315 		.render();
1316 	});
1317 }
1318 
cleanUpDynamicByteArrayEndSlotsFunction(ArrayType const & _type)1319 string YulUtilFunctions::cleanUpDynamicByteArrayEndSlotsFunction(ArrayType const& _type)
1320 {
1321 	solAssert(_type.isByteArray(), "");
1322 	solAssert(_type.isDynamicallySized(), "");
1323 
1324 	string functionName = "clean_up_bytearray_end_slots_" + _type.identifier();
1325 	return m_functionCollector.createFunction(functionName, [&](vector<string>& _args, vector<string>&) {
1326 		_args = {"array", "len", "startIndex"};
1327 		return Whiskers(R"(
1328 			if gt(len, 31) {
1329 				let dataArea := <dataLocation>(array)
1330 				let deleteStart := add(dataArea, <div32Ceil>(startIndex))
1331 				// If we are clearing array to be short byte array, we want to clear only data starting from array data area.
1332 				if lt(startIndex, 32) { deleteStart := dataArea }
1333 				<clearStorageRange>(deleteStart, add(dataArea, <div32Ceil>(len)))
1334 			}
1335 		)")
1336 		("dataLocation", arrayDataAreaFunction(_type))
1337 		("div32Ceil", divide32CeilFunction())
1338 		("clearStorageRange", clearStorageRangeFunction(*_type.baseType()))
1339 		.render();
1340 	});
1341 }
1342 
decreaseByteArraySizeFunction(ArrayType const & _type)1343 string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _type)
1344 {
1345 	string functionName = "byte_array_decrease_size_" + _type.identifier();
1346 	return m_functionCollector.createFunction(functionName, [&]() {
1347 		return Whiskers(R"(
1348 			function <functionName>(array, data, oldLen, newLen) {
1349 				switch lt(newLen, 32)
1350 				case  0 {
1351 					let arrayDataStart := <dataPosition>(array)
1352 					let deleteStart := add(arrayDataStart, <div32Ceil>(newLen))
1353 
1354 					// we have to partially clear last slot that is still used
1355 					let offset := and(newLen, 0x1f)
1356 					if offset { <partialClearStorageSlot>(sub(deleteStart, 1), offset) }
1357 
1358 					<clearStorageRange>(deleteStart, add(arrayDataStart, <div32Ceil>(oldLen)))
1359 
1360 					sstore(array, or(mul(2, newLen), 1))
1361 				}
1362 				default {
1363 					switch gt(oldLen, 31)
1364 					case 1 {
1365 						let arrayDataStart := <dataPosition>(array)
1366 						// clear whole old array, as we are transforming to short bytes array
1367 						<clearStorageRange>(add(arrayDataStart, 1), add(arrayDataStart, <div32Ceil>(oldLen)))
1368 						<transitLongToShort>(array, newLen)
1369 					}
1370 					default {
1371 						sstore(array, <encodeUsedSetLen>(data, newLen))
1372 					}
1373 				}
1374 			})")
1375 			("functionName", functionName)
1376 			("dataPosition", arrayDataAreaFunction(_type))
1377 			("partialClearStorageSlot", partialClearStorageSlotFunction())
1378 			("clearStorageRange", clearStorageRangeFunction(*_type.baseType()))
1379 			("transitLongToShort", byteArrayTransitLongToShortFunction(_type))
1380 			("div32Ceil", divide32CeilFunction())
1381 			("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction())
1382 			.render();
1383 	});
1384 }
1385 
increaseByteArraySizeFunction(ArrayType const & _type)1386 string YulUtilFunctions::increaseByteArraySizeFunction(ArrayType const& _type)
1387 {
1388 	string functionName = "byte_array_increase_size_" + _type.identifier();
1389 	return m_functionCollector.createFunction(functionName, [&](vector<string>& _args, vector<string>&) {
1390 		_args = {"array", "data", "oldLen", "newLen"};
1391 		return Whiskers(R"(
1392 			if gt(newLen, <maxArrayLength>) { <panic>() }
1393 
1394 			switch lt(oldLen, 32)
1395 			case 0 {
1396 				// in this case array stays unpacked, so we just set new length
1397 				sstore(array, add(mul(2, newLen), 1))
1398 			}
1399 			default {
1400 				switch lt(newLen, 32)
1401 				case 0 {
1402 					// we need to copy elements to data area as we changed array from packed to unpacked
1403 					data := and(not(0xff), data)
1404 					sstore(<dataPosition>(array), data)
1405 					sstore(array, add(mul(2, newLen), 1))
1406 				}
1407 				default {
1408 					// here array stays packed, we just need to increase length
1409 					sstore(array, <encodeUsedSetLen>(data, newLen))
1410 				}
1411 			}
1412 		)")
1413 		("panic", panicFunction(PanicCode::ResourceError))
1414 		("maxArrayLength", (u256(1) << 64).str())
1415 		("dataPosition", arrayDataAreaFunction(_type))
1416 		("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction())
1417 		.render();
1418 	});
1419 }
1420 
byteArrayTransitLongToShortFunction(ArrayType const & _type)1421 string YulUtilFunctions::byteArrayTransitLongToShortFunction(ArrayType const& _type)
1422 {
1423 	string functionName = "transit_byte_array_long_to_short_" + _type.identifier();
1424 	return m_functionCollector.createFunction(functionName, [&]() {
1425 		return Whiskers(R"(
1426 			function <functionName>(array, len) {
1427 				// we need to copy elements from old array to new
1428 				// we want to copy only elements that are part of the array after resizing
1429 				let dataPos := <dataPosition>(array)
1430 				let data := <extractUsedApplyLen>(sload(dataPos), len)
1431 				sstore(array, data)
1432 				sstore(dataPos, 0)
1433 			})")
1434 			("functionName", functionName)
1435 			("dataPosition", arrayDataAreaFunction(_type))
1436 			("extractUsedApplyLen", shortByteArrayEncodeUsedAreaSetLengthFunction())
1437 			.render();
1438 	});
1439 }
1440 
shortByteArrayEncodeUsedAreaSetLengthFunction()1441 string YulUtilFunctions::shortByteArrayEncodeUsedAreaSetLengthFunction()
1442 {
1443 	string functionName = "extract_used_part_and_set_length_of_short_byte_array";
1444 	return m_functionCollector.createFunction(functionName, [&]() {
1445 		return Whiskers(R"(
1446 			function <functionName>(data, len) -> used {
1447 				// we want to save only elements that are part of the array after resizing
1448 				// others should be set to zero
1449 				data := <maskBytes>(data, len)
1450 				used := or(data, mul(2, len))
1451 			})")
1452 			("functionName", functionName)
1453 			("maskBytes", maskBytesFunctionDynamic())
1454 			.render();
1455 	});
1456 }
1457 
longByteArrayStorageIndexAccessNoCheckFunction()1458 string YulUtilFunctions::longByteArrayStorageIndexAccessNoCheckFunction()
1459 {
1460 	return m_functionCollector.createFunction(
1461 		"long_byte_array_index_access_no_checks",
1462 		[&](vector<string>& _args, vector<string>& _returnParams) {
1463 			_args = {"array", "index"};
1464 			_returnParams = {"slot", "offset"};
1465 			return Whiskers(R"(
1466 				offset := sub(31, mod(index, 0x20))
1467 				let dataArea := <dataAreaFunc>(array)
1468 				slot := add(dataArea, div(index, 0x20))
1469 			)")
1470 			("dataAreaFunc", arrayDataAreaFunction(*TypeProvider::bytesStorage()))
1471 			.render();
1472 		}
1473 	);
1474 }
1475 
storageArrayPopFunction(ArrayType const & _type)1476 string YulUtilFunctions::storageArrayPopFunction(ArrayType const& _type)
1477 {
1478 	solAssert(_type.location() == DataLocation::Storage, "");
1479 	solAssert(_type.isDynamicallySized(), "");
1480 	solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented.");
1481 	if (_type.isByteArray())
1482 		return storageByteArrayPopFunction(_type);
1483 
1484 	string functionName = "array_pop_" + _type.identifier();
1485 	return m_functionCollector.createFunction(functionName, [&]() {
1486 		return Whiskers(R"(
1487 			function <functionName>(array) {
1488 				let oldLen := <fetchLength>(array)
1489 				if iszero(oldLen) { <panic>() }
1490 				let newLen := sub(oldLen, 1)
1491 				let slot, offset := <indexAccess>(array, newLen)
1492 				<?+setToZero><setToZero>(slot, offset)</+setToZero>
1493 				sstore(array, newLen)
1494 			})")
1495 			("functionName", functionName)
1496 			("panic", panicFunction(PanicCode::EmptyArrayPop))
1497 			("fetchLength", arrayLengthFunction(_type))
1498 			("indexAccess", storageArrayIndexAccessFunction(_type))
1499 			(
1500 				"setToZero",
1501 				_type.baseType()->category() != Type::Category::Mapping ? storageSetToZeroFunction(*_type.baseType()) : ""
1502 			)
1503 			.render();
1504 	});
1505 }
1506 
storageByteArrayPopFunction(ArrayType const & _type)1507 string YulUtilFunctions::storageByteArrayPopFunction(ArrayType const& _type)
1508 {
1509 	solAssert(_type.location() == DataLocation::Storage, "");
1510 	solAssert(_type.isDynamicallySized(), "");
1511 	solAssert(_type.isByteArray(), "");
1512 
1513 	string functionName = "byte_array_pop_" + _type.identifier();
1514 	return m_functionCollector.createFunction(functionName, [&]() {
1515 		return Whiskers(R"(
1516 			function <functionName>(array) {
1517 				let data := sload(array)
1518 				let oldLen := <extractByteArrayLength>(data)
1519 				if iszero(oldLen) { <panic>() }
1520 
1521 				switch oldLen
1522 				case 32 {
1523 					// Here we have a special case where array transitions to shorter than 32
1524 					// So we need to copy data
1525 					<transitLongToShort>(array, 31)
1526 				}
1527 				default {
1528 					let newLen := sub(oldLen, 1)
1529 					switch lt(oldLen, 32)
1530 					case 1 {
1531 						sstore(array, <encodeUsedSetLen>(data, newLen))
1532 					}
1533 					default {
1534 						let slot, offset := <indexAccessNoChecks>(array, newLen)
1535 						<setToZero>(slot, offset)
1536 						sstore(array, sub(data, 2))
1537 					}
1538 				}
1539 			})")
1540 			("functionName", functionName)
1541 			("panic", panicFunction(PanicCode::EmptyArrayPop))
1542 			("extractByteArrayLength", extractByteArrayLengthFunction())
1543 			("transitLongToShort", byteArrayTransitLongToShortFunction(_type))
1544 			("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction())
1545 			("indexAccessNoChecks", longByteArrayStorageIndexAccessNoCheckFunction())
1546 			("setToZero", storageSetToZeroFunction(*_type.baseType()))
1547 			.render();
1548 	});
1549 }
1550 
storageArrayPushFunction(ArrayType const & _type,Type const * _fromType)1551 string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type, Type const* _fromType)
1552 {
1553 	solAssert(_type.location() == DataLocation::Storage, "");
1554 	solAssert(_type.isDynamicallySized(), "");
1555 	if (!_fromType)
1556 		_fromType = _type.baseType();
1557 	else if (_fromType->isValueType())
1558 		solUnimplementedAssert(*_fromType == *_type.baseType());
1559 
1560 	string functionName =
1561 		string{"array_push_from_"} +
1562 		_fromType->identifier() +
1563 		"_to_" +
1564 		_type.identifier();
1565 	return m_functionCollector.createFunction(functionName, [&]() {
1566 		return Whiskers(R"(
1567 			function <functionName>(array <values>) {
1568 				<?isByteArray>
1569 					let data := sload(array)
1570 					let oldLen := <extractByteArrayLength>(data)
1571 					if iszero(lt(oldLen, <maxArrayLength>)) { <panic>() }
1572 
1573 					switch gt(oldLen, 31)
1574 					case 0 {
1575 						let value := byte(0 <values>)
1576 						switch oldLen
1577 						case 31 {
1578 							// Here we have special case when array switches from short array to long array
1579 							// We need to copy data
1580 							let dataArea := <dataAreaFunction>(array)
1581 							data := and(data, not(0xff))
1582 							sstore(dataArea, or(and(0xff, value), data))
1583 							// New length is 32, encoded as (32 * 2 + 1)
1584 							sstore(array, 65)
1585 						}
1586 						default {
1587 							data := add(data, 2)
1588 							let shiftBits := mul(8, sub(31, oldLen))
1589 							let valueShifted := <shl>(shiftBits, and(0xff, value))
1590 							let mask := <shl>(shiftBits, 0xff)
1591 							data := or(and(data, not(mask)), valueShifted)
1592 							sstore(array, data)
1593 						}
1594 					}
1595 					default {
1596 						sstore(array, add(data, 2))
1597 						let slot, offset := <indexAccess>(array, oldLen)
1598 						<storeValue>(slot, offset <values>)
1599 					}
1600 				<!isByteArray>
1601 					let oldLen := sload(array)
1602 					if iszero(lt(oldLen, <maxArrayLength>)) { <panic>() }
1603 					sstore(array, add(oldLen, 1))
1604 					let slot, offset := <indexAccess>(array, oldLen)
1605 					<storeValue>(slot, offset <values>)
1606 				</isByteArray>
1607 			})")
1608 			("functionName", functionName)
1609 			("values", _fromType->sizeOnStack() == 0 ? "" : ", " + suffixedVariableNameList("value", 0, _fromType->sizeOnStack()))
1610 			("panic", panicFunction(PanicCode::ResourceError))
1611 			("extractByteArrayLength", _type.isByteArray() ? extractByteArrayLengthFunction() : "")
1612 			("dataAreaFunction", arrayDataAreaFunction(_type))
1613 			("isByteArray", _type.isByteArray())
1614 			("indexAccess", storageArrayIndexAccessFunction(_type))
1615 			("storeValue", updateStorageValueFunction(*_fromType, *_type.baseType()))
1616 			("maxArrayLength", (u256(1) << 64).str())
1617 			("shl", shiftLeftFunctionDynamic())
1618 			.render();
1619 	});
1620 }
1621 
storageArrayPushZeroFunction(ArrayType const & _type)1622 string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type)
1623 {
1624 	solAssert(_type.location() == DataLocation::Storage, "");
1625 	solAssert(_type.isDynamicallySized(), "");
1626 	solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented.");
1627 
1628 	string functionName = "array_push_zero_" + _type.identifier();
1629 	return m_functionCollector.createFunction(functionName, [&]() {
1630 		return Whiskers(R"(
1631 			function <functionName>(array) -> slot, offset {
1632 				<?isBytes>
1633 					let data := sload(array)
1634 					let oldLen := <extractLength>(data)
1635 					<increaseBytesSize>(array, data, oldLen, add(oldLen, 1))
1636 				<!isBytes>
1637 					let oldLen := <fetchLength>(array)
1638 					if iszero(lt(oldLen, <maxArrayLength>)) { <panic>() }
1639 					sstore(array, add(oldLen, 1))
1640 				</isBytes>
1641 				slot, offset := <indexAccess>(array, oldLen)
1642 			})")
1643 			("functionName", functionName)
1644 			("isBytes", _type.isByteArray())
1645 			("increaseBytesSize", _type.isByteArray() ? increaseByteArraySizeFunction(_type) : "")
1646 			("extractLength", _type.isByteArray() ? extractByteArrayLengthFunction() : "")
1647 			("panic", panicFunction(PanicCode::ResourceError))
1648 			("fetchLength", arrayLengthFunction(_type))
1649 			("indexAccess", storageArrayIndexAccessFunction(_type))
1650 			("maxArrayLength", (u256(1) << 64).str())
1651 			.render();
1652 	});
1653 }
1654 
partialClearStorageSlotFunction()1655 string YulUtilFunctions::partialClearStorageSlotFunction()
1656 {
1657 	string functionName = "partial_clear_storage_slot";
1658 	return m_functionCollector.createFunction(functionName, [&]() {
1659 		return Whiskers(R"(
1660 		function <functionName>(slot, offset) {
1661 			let mask := <shr>(mul(8, sub(32, offset)), <ones>)
1662 			sstore(slot, and(mask, sload(slot)))
1663 		}
1664 		)")
1665 		("functionName", functionName)
1666 		("ones", formatNumber((bigint(1) << 256) - 1))
1667 		("shr", shiftRightFunctionDynamic())
1668 		.render();
1669 	});
1670 }
1671 
clearStorageRangeFunction(Type const & _type)1672 string YulUtilFunctions::clearStorageRangeFunction(Type const& _type)
1673 {
1674 	if (_type.storageBytes() < 32)
1675 		solAssert(_type.isValueType(), "");
1676 
1677 	string functionName = "clear_storage_range_" + _type.identifier();
1678 
1679 	return m_functionCollector.createFunction(functionName, [&]() {
1680 		return Whiskers(R"(
1681 			function <functionName>(start, end) {
1682 				for {} lt(start, end) { start := add(start, <increment>) }
1683 				{
1684 					<setToZero>(start, 0)
1685 				}
1686 			}
1687 		)")
1688 		("functionName", functionName)
1689 		("setToZero", storageSetToZeroFunction(_type.storageBytes() < 32 ? *TypeProvider::uint256() : _type))
1690 		("increment", _type.storageSize().str())
1691 		.render();
1692 	});
1693 }
1694 
clearStorageArrayFunction(ArrayType const & _type)1695 string YulUtilFunctions::clearStorageArrayFunction(ArrayType const& _type)
1696 {
1697 	solAssert(_type.location() == DataLocation::Storage, "");
1698 
1699 	if (_type.baseType()->storageBytes() < 32)
1700 	{
1701 		solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
1702 		solAssert(_type.baseType()->storageSize() <= 1, "Invalid storage size for type.");
1703 	}
1704 
1705 	if (_type.baseType()->isValueType())
1706 		solAssert(_type.baseType()->storageSize() <= 1, "Invalid size for value type.");
1707 
1708 	string functionName = "clear_storage_array_" + _type.identifier();
1709 
1710 	return m_functionCollector.createFunction(functionName, [&]() {
1711 		return Whiskers(R"(
1712 			function <functionName>(slot) {
1713 				<?dynamic>
1714 					<resizeArray>(slot, 0)
1715 				<!dynamic>
1716 					<?+clearRange><clearRange>(slot, add(slot, <lenToSize>(<len>)))</+clearRange>
1717 				</dynamic>
1718 			}
1719 		)")
1720 		("functionName", functionName)
1721 		("dynamic", _type.isDynamicallySized())
1722 		("resizeArray", _type.isDynamicallySized() ? resizeArrayFunction(_type) : "")
1723 		(
1724 			"clearRange",
1725 			_type.baseType()->category() != Type::Category::Mapping ?
1726 			clearStorageRangeFunction((_type.baseType()->storageBytes() < 32) ? *TypeProvider::uint256() : *_type.baseType()) :
1727 			""
1728 		)
1729 		("lenToSize", arrayConvertLengthToSize(_type))
1730 		("len", _type.length().str())
1731 		.render();
1732 	});
1733 }
1734 
clearStorageStructFunction(StructType const & _type)1735 string YulUtilFunctions::clearStorageStructFunction(StructType const& _type)
1736 {
1737 	solAssert(_type.location() == DataLocation::Storage, "");
1738 
1739 	string functionName = "clear_struct_storage_" + _type.identifier();
1740 
1741 	return m_functionCollector.createFunction(functionName, [&] {
1742 		MemberList::MemberMap structMembers = _type.nativeMembers(nullptr);
1743 		vector<map<string, string>> memberSetValues;
1744 
1745 		set<u256> slotsCleared;
1746 		for (auto const& member: structMembers)
1747 		{
1748 			if (member.type->category() == Type::Category::Mapping)
1749 				continue;
1750 			if (member.type->storageBytes() < 32)
1751 			{
1752 				auto const& slotDiff = _type.storageOffsetsOfMember(member.name).first;
1753 				if (!slotsCleared.count(slotDiff))
1754 				{
1755 					memberSetValues.emplace_back().emplace("clearMember", "sstore(add(slot, " + slotDiff.str() + "), 0)");
1756 					slotsCleared.emplace(slotDiff);
1757 				}
1758 			}
1759 			else
1760 			{
1761 				auto const& [memberSlotDiff, memberStorageOffset] = _type.storageOffsetsOfMember(member.name);
1762 				solAssert(memberStorageOffset == 0, "");
1763 
1764 				memberSetValues.emplace_back().emplace("clearMember", Whiskers(R"(
1765 						<setZero>(add(slot, <memberSlotDiff>), <memberStorageOffset>)
1766 					)")
1767 					("setZero", storageSetToZeroFunction(*member.type))
1768 					("memberSlotDiff",  memberSlotDiff.str())
1769 					("memberStorageOffset", to_string(memberStorageOffset))
1770 					.render()
1771 				);
1772 			}
1773 		}
1774 
1775 		return Whiskers(R"(
1776 			function <functionName>(slot) {
1777 				<#member>
1778 					<clearMember>
1779 				</member>
1780 			}
1781 		)")
1782 		("functionName", functionName)
1783 		("member", memberSetValues)
1784 		.render();
1785 	});
1786 }
1787 
copyArrayToStorageFunction(ArrayType const & _fromType,ArrayType const & _toType)1788 string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
1789 {
1790 	solAssert(
1791 		*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
1792 		""
1793 	);
1794 	if (!_toType.isDynamicallySized())
1795 		solAssert(!_fromType.isDynamicallySized() && _fromType.length() <= _toType.length(), "");
1796 
1797 	if (_fromType.isByteArray())
1798 		return copyByteArrayToStorageFunction(_fromType, _toType);
1799 	if (_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType())
1800 		return copyValueArrayStorageToStorageFunction(_fromType, _toType);
1801 
1802 	string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
1803 	return m_functionCollector.createFunction(functionName, [&](){
1804 		Whiskers templ(R"(
1805 			function <functionName>(slot, value<?isFromDynamicCalldata>, len</isFromDynamicCalldata>) {
1806 				<?fromStorage> if eq(slot, value) { leave } </fromStorage>
1807 				let length := <arrayLength>(value<?isFromDynamicCalldata>, len</isFromDynamicCalldata>)
1808 
1809 				<resizeArray>(slot, length)
1810 
1811 				let srcPtr := <srcDataLocation>(value)
1812 
1813 				let elementSlot := <dstDataLocation>(slot)
1814 				let elementOffset := 0
1815 
1816 				for { let i := 0 } lt(i, length) {i := add(i, 1)} {
1817 					<?fromCalldata>
1818 						let <elementValues> :=
1819 						<?dynamicallyEncodedBase>
1820 							<accessCalldataTail>(value, srcPtr)
1821 						<!dynamicallyEncodedBase>
1822 							srcPtr
1823 						</dynamicallyEncodedBase>
1824 
1825 						<?isValueType>
1826 							<elementValues> := <readFromCalldataOrMemory>(<elementValues>)
1827 						</isValueType>
1828 					</fromCalldata>
1829 
1830 					<?fromMemory>
1831 						let <elementValues> := <readFromCalldataOrMemory>(srcPtr)
1832 					</fromMemory>
1833 
1834 					<?fromStorage>
1835 						let <elementValues> := srcPtr
1836 					</fromStorage>
1837 
1838 					<updateStorageValue>(elementSlot, elementOffset, <elementValues>)
1839 
1840 					srcPtr := add(srcPtr, <srcStride>)
1841 
1842 					<?multipleItemsPerSlot>
1843 						elementOffset := add(elementOffset, <storageStride>)
1844 						if gt(elementOffset, sub(32, <storageStride>)) {
1845 							elementOffset := 0
1846 							elementSlot := add(elementSlot, 1)
1847 						}
1848 					<!multipleItemsPerSlot>
1849 						elementSlot := add(elementSlot, <storageSize>)
1850 					</multipleItemsPerSlot>
1851 				}
1852 			}
1853 		)");
1854 		if (_fromType.dataStoredIn(DataLocation::Storage))
1855 			solAssert(!_fromType.isValueType(), "");
1856 		templ("functionName", functionName);
1857 		bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData);
1858 		templ("isFromDynamicCalldata", _fromType.isDynamicallySized() && fromCalldata);
1859 		templ("fromStorage", _fromType.dataStoredIn(DataLocation::Storage));
1860 		bool fromMemory = _fromType.dataStoredIn(DataLocation::Memory);
1861 		templ("fromMemory", fromMemory);
1862 		templ("fromCalldata", fromCalldata);
1863 		templ("srcDataLocation", arrayDataAreaFunction(_fromType));
1864 		if (fromCalldata)
1865 		{
1866 			templ("dynamicallyEncodedBase", _fromType.baseType()->isDynamicallyEncoded());
1867 			if (_fromType.baseType()->isDynamicallyEncoded())
1868 				templ("accessCalldataTail", accessCalldataTailFunction(*_fromType.baseType()));
1869 		}
1870 		templ("resizeArray", resizeArrayFunction(_toType));
1871 		templ("arrayLength",arrayLengthFunction(_fromType));
1872 		templ("isValueType", _fromType.baseType()->isValueType());
1873 		templ("dstDataLocation", arrayDataAreaFunction(_toType));
1874 		if (fromMemory || (fromCalldata && _fromType.baseType()->isValueType()))
1875 			templ("readFromCalldataOrMemory", readFromMemoryOrCalldata(*_fromType.baseType(), fromCalldata));
1876 		templ("elementValues", suffixedVariableNameList(
1877 			"elementValue_",
1878 			0,
1879 			_fromType.baseType()->stackItems().size()
1880 		));
1881 		templ("updateStorageValue", updateStorageValueFunction(*_fromType.baseType(), *_toType.baseType()));
1882 		templ("srcStride",
1883 			fromCalldata ?
1884 			to_string(_fromType.calldataStride()) :
1885 				fromMemory ?
1886 				to_string(_fromType.memoryStride()) :
1887 				formatNumber(_fromType.baseType()->storageSize())
1888 		);
1889 		templ("multipleItemsPerSlot", _toType.storageStride() <= 16);
1890 		templ("storageStride", to_string(_toType.storageStride()));
1891 		templ("storageSize", _toType.baseType()->storageSize().str());
1892 
1893 		return templ.render();
1894 	});
1895 }
1896 
1897 
copyByteArrayToStorageFunction(ArrayType const & _fromType,ArrayType const & _toType)1898 string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
1899 {
1900 	solAssert(
1901 		*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
1902 		""
1903 	);
1904 	solAssert(_fromType.isByteArray(), "");
1905 	solAssert(_toType.isByteArray(), "");
1906 
1907 	string functionName = "copy_byte_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
1908 	return m_functionCollector.createFunction(functionName, [&](){
1909 		Whiskers templ(R"(
1910 			function <functionName>(slot, src<?fromCalldata>, len</fromCalldata>) {
1911 				<?fromStorage> if eq(slot, src) { leave } </fromStorage>
1912 
1913 				let newLen := <arrayLength>(src<?fromCalldata>, len</fromCalldata>)
1914 				// Make sure array length is sane
1915 				if gt(newLen, 0xffffffffffffffff) { <panic>() }
1916 
1917 				let oldLen := <byteArrayLength>(sload(slot))
1918 
1919 				// potentially truncate data
1920 				<cleanUpEndArray>(slot, oldLen, newLen)
1921 
1922 				let srcOffset := 0
1923 				<?fromMemory>
1924 					srcOffset := 0x20
1925 				</fromMemory>
1926 
1927 				switch gt(newLen, 31)
1928 				case 1 {
1929 					let loopEnd := and(newLen, not(0x1f))
1930 					<?fromStorage> src := <srcDataLocation>(src) </fromStorage>
1931 					let dstPtr := <dstDataLocation>(slot)
1932 					let i := 0
1933 					for { } lt(i, loopEnd) { i := add(i, 0x20) } {
1934 						sstore(dstPtr, <read>(add(src, srcOffset)))
1935 						dstPtr := add(dstPtr, 1)
1936 						srcOffset := add(srcOffset, <srcIncrement>)
1937 					}
1938 					if lt(loopEnd, newLen) {
1939 						let lastValue := <read>(add(src, srcOffset))
1940 						sstore(dstPtr, <maskBytes>(lastValue, and(newLen, 0x1f)))
1941 					}
1942 					sstore(slot, add(mul(newLen, 2), 1))
1943 				}
1944 				default {
1945 					let value := 0
1946 					if newLen {
1947 						value := <read>(add(src, srcOffset))
1948 					}
1949 					sstore(slot, <byteArrayCombineShort>(value, newLen))
1950 				}
1951 			}
1952 		)");
1953 		templ("functionName", functionName);
1954 		bool fromStorage = _fromType.dataStoredIn(DataLocation::Storage);
1955 		templ("fromStorage", fromStorage);
1956 		bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData);
1957 		templ("fromMemory", _fromType.dataStoredIn(DataLocation::Memory));
1958 		templ("fromCalldata", fromCalldata);
1959 		templ("arrayLength", arrayLengthFunction(_fromType));
1960 		templ("panic", panicFunction(PanicCode::ResourceError));
1961 		templ("byteArrayLength", extractByteArrayLengthFunction());
1962 		templ("dstDataLocation", arrayDataAreaFunction(_toType));
1963 		if (fromStorage)
1964 			templ("srcDataLocation", arrayDataAreaFunction(_fromType));
1965 		templ("cleanUpEndArray", cleanUpDynamicByteArrayEndSlotsFunction(_toType));
1966 		templ("srcIncrement", to_string(fromStorage ? 1 : 0x20));
1967 		templ("read", fromStorage ? "sload" : fromCalldata ? "calldataload" : "mload");
1968 		templ("maskBytes", maskBytesFunctionDynamic());
1969 		templ("byteArrayCombineShort", shortByteArrayEncodeUsedAreaSetLengthFunction());
1970 
1971 		return templ.render();
1972 	});
1973 }
1974 
1975 
copyValueArrayStorageToStorageFunction(ArrayType const & _fromType,ArrayType const & _toType)1976 string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
1977 {
1978 	solAssert(_fromType.baseType()->isValueType(), "");
1979 	solAssert(_toType.baseType()->isValueType(), "");
1980 	solAssert(_fromType.baseType()->isImplicitlyConvertibleTo(*_toType.baseType()), "");
1981 
1982 	solAssert(!_fromType.isByteArray(), "");
1983 	solAssert(!_toType.isByteArray(), "");
1984 	solAssert(_fromType.dataStoredIn(DataLocation::Storage), "");
1985 	solAssert(_toType.dataStoredIn(DataLocation::Storage), "");
1986 
1987 	solAssert(_fromType.storageStride() <= _toType.storageStride(), "");
1988 	solAssert(_toType.storageStride() <= 32, "");
1989 
1990 	string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
1991 	return m_functionCollector.createFunction(functionName, [&](){
1992 		Whiskers templ(R"(
1993 			function <functionName>(dst, src) {
1994 				if eq(dst, src) { leave }
1995 				let length := <arrayLength>(src)
1996 				// Make sure array length is sane
1997 				if gt(length, 0xffffffffffffffff) { <panic>() }
1998 				<resizeArray>(dst, length)
1999 
2000 				let srcSlot := <srcDataLocation>(src)
2001 				let dstSlot := <dstDataLocation>(dst)
2002 
2003 				let fullSlots := div(length, <itemsPerSlot>)
2004 
2005 				let srcSlotValue := sload(srcSlot)
2006 				let srcItemIndexInSlot := 0
2007 				for { let i := 0 } lt(i, fullSlots) { i := add(i, 1) } {
2008 					let dstSlotValue := 0
2009 					<?sameType>
2010 						dstSlotValue := <maskFull>(srcSlotValue)
2011 						<updateSrcSlotValue>
2012 					<!sameType>
2013 						<?multipleItemsPerSlotDst>for { let j := 0 } lt(j, <itemsPerSlot>) { j := add(j, 1) } </multipleItemsPerSlotDst>
2014 						{
2015 							let itemValue := <convert>(
2016 								<extractFromSlot>(srcSlotValue, mul(<srcStride>, srcItemIndexInSlot))
2017 							)
2018 							itemValue := <prepareStore>(itemValue)
2019 							dstSlotValue :=
2020 							<?multipleItemsPerSlotDst>
2021 								<updateByteSlice>(dstSlotValue, mul(<dstStride>, j), itemValue)
2022 							<!multipleItemsPerSlotDst>
2023 								itemValue
2024 							</multipleItemsPerSlotDst>
2025 
2026 							<updateSrcSlotValue>
2027 						}
2028 					</sameType>
2029 
2030 					sstore(add(dstSlot, i), dstSlotValue)
2031 				}
2032 
2033 				<?multipleItemsPerSlotDst>
2034 					let spill := sub(length, mul(fullSlots, <itemsPerSlot>))
2035 					if gt(spill, 0) {
2036 						let dstSlotValue := 0
2037 						<?sameType>
2038 							dstSlotValue := <maskBytes>(srcSlotValue, mul(spill, <srcStride>))
2039 							<updateSrcSlotValue>
2040 						<!sameType>
2041 							for { let j := 0 } lt(j, spill) { j := add(j, 1) } {
2042 								let itemValue := <convert>(
2043 									<extractFromSlot>(srcSlotValue, mul(<srcStride>, srcItemIndexInSlot))
2044 								)
2045 								itemValue := <prepareStore>(itemValue)
2046 								dstSlotValue := <updateByteSlice>(dstSlotValue, mul(<dstStride>, j), itemValue)
2047 
2048 								<updateSrcSlotValue>
2049 							}
2050 						</sameType>
2051 						sstore(add(dstSlot, fullSlots), dstSlotValue)
2052 					}
2053 				</multipleItemsPerSlotDst>
2054 			}
2055 		)");
2056 		if (_fromType.dataStoredIn(DataLocation::Storage))
2057 			solAssert(!_fromType.isValueType(), "");
2058 		templ("functionName", functionName);
2059 		templ("resizeArray", resizeArrayFunction(_toType));
2060 		templ("arrayLength", arrayLengthFunction(_fromType));
2061 		templ("panic", panicFunction(PanicCode::ResourceError));
2062 		templ("srcDataLocation", arrayDataAreaFunction(_fromType));
2063 		templ("dstDataLocation", arrayDataAreaFunction(_toType));
2064 		templ("srcStride", to_string(_fromType.storageStride()));
2065 		unsigned itemsPerSlot = 32 / _toType.storageStride();
2066 		templ("itemsPerSlot", to_string(itemsPerSlot));
2067 		templ("multipleItemsPerSlotDst", itemsPerSlot > 1);
2068 		bool sameType = *_fromType.baseType() == *_toType.baseType();
2069 		if (auto functionType = dynamic_cast<FunctionType const*>(_fromType.baseType()))
2070 		{
2071 			solAssert(functionType->equalExcludingStateMutability(
2072 				dynamic_cast<FunctionType const&>(*_toType.baseType())
2073 			));
2074 			sameType = true;
2075 		}
2076 		templ("sameType", sameType);
2077 		if (sameType)
2078 		{
2079 			templ("maskFull", maskLowerOrderBytesFunction(itemsPerSlot * _toType.storageStride()));
2080 			templ("maskBytes", maskLowerOrderBytesFunctionDynamic());
2081 		}
2082 		else
2083 		{
2084 			templ("dstStride", to_string(_toType.storageStride()));
2085 			templ("extractFromSlot", extractFromStorageValueDynamic(*_fromType.baseType()));
2086 			templ("updateByteSlice", updateByteSliceFunctionDynamic(_toType.storageStride()));
2087 			templ("convert", conversionFunction(*_fromType.baseType(), *_toType.baseType()));
2088 			templ("prepareStore", prepareStoreFunction(*_toType.baseType()));
2089 		}
2090 		templ("updateSrcSlotValue", Whiskers(R"(
2091 			<?srcReadMultiPerSlot>
2092 				srcItemIndexInSlot := add(srcItemIndexInSlot, 1)
2093 				if eq(srcItemIndexInSlot, <srcItemsPerSlot>) {
2094 					// here we are done with this slot, we need to read next one
2095 					srcSlot := add(srcSlot, 1)
2096 					srcSlotValue := sload(srcSlot)
2097 					srcItemIndexInSlot := 0
2098 				}
2099 			<!srcReadMultiPerSlot>
2100 				srcSlot := add(srcSlot, 1)
2101 				srcSlotValue := sload(srcSlot)
2102 			</srcReadMultiPerSlot>
2103 			)")
2104 			("srcReadMultiPerSlot", !sameType && _fromType.storageStride() <= 16)
2105 			("srcItemsPerSlot", to_string(32 / _fromType.storageStride()))
2106 			.render()
2107 		);
2108 
2109 		return templ.render();
2110 	});
2111 }
2112 
2113 
arrayConvertLengthToSize(ArrayType const & _type)2114 string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
2115 {
2116 	string functionName = "array_convert_length_to_size_" + _type.identifier();
2117 	return m_functionCollector.createFunction(functionName, [&]() {
2118 		Type const& baseType = *_type.baseType();
2119 
2120 		switch (_type.location())
2121 		{
2122 			case DataLocation::Storage:
2123 			{
2124 				unsigned const baseStorageBytes = baseType.storageBytes();
2125 				solAssert(baseStorageBytes > 0, "");
2126 				solAssert(32 / baseStorageBytes > 0, "");
2127 
2128 				return Whiskers(R"(
2129 					function <functionName>(length) -> size {
2130 						size := length
2131 						<?multiSlot>
2132 							size := <mul>(<storageSize>, length)
2133 						<!multiSlot>
2134 							// Number of slots rounded up
2135 							size := div(add(length, sub(<itemsPerSlot>, 1)), <itemsPerSlot>)
2136 						</multiSlot>
2137 					})")
2138 					("functionName", functionName)
2139 					("multiSlot", baseType.storageSize() > 1)
2140 					("itemsPerSlot", to_string(32 / baseStorageBytes))
2141 					("storageSize", baseType.storageSize().str())
2142 					("mul", overflowCheckedIntMulFunction(*TypeProvider::uint256()))
2143 					.render();
2144 			}
2145 			case DataLocation::CallData: // fallthrough
2146 			case DataLocation::Memory:
2147 				return Whiskers(R"(
2148 					function <functionName>(length) -> size {
2149 						<?byteArray>
2150 							size := length
2151 						<!byteArray>
2152 							size := <mul>(length, <stride>)
2153 						</byteArray>
2154 					})")
2155 					("functionName", functionName)
2156 					("stride", to_string(_type.location() == DataLocation::Memory ? _type.memoryStride() : _type.calldataStride()))
2157 					("byteArray", _type.isByteArray())
2158 					("mul", overflowCheckedIntMulFunction(*TypeProvider::uint256()))
2159 					.render();
2160 			default:
2161 				solAssert(false, "");
2162 		}
2163 
2164 	});
2165 }
2166 
arrayAllocationSizeFunction(ArrayType const & _type)2167 string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type)
2168 {
2169 	solAssert(_type.dataStoredIn(DataLocation::Memory), "");
2170 	string functionName = "array_allocation_size_" + _type.identifier();
2171 	return m_functionCollector.createFunction(functionName, [&]() {
2172 		Whiskers w(R"(
2173 			function <functionName>(length) -> size {
2174 				// Make sure we can allocate memory without overflow
2175 				if gt(length, 0xffffffffffffffff) { <panic>() }
2176 				<?byteArray>
2177 					size := <roundUp>(length)
2178 				<!byteArray>
2179 					size := mul(length, 0x20)
2180 				</byteArray>
2181 				<?dynamic>
2182 					// add length slot
2183 					size := add(size, 0x20)
2184 				</dynamic>
2185 			}
2186 		)");
2187 		w("functionName", functionName);
2188 		w("panic", panicFunction(PanicCode::ResourceError));
2189 		w("byteArray", _type.isByteArray());
2190 		w("roundUp", roundUpFunction());
2191 		w("dynamic", _type.isDynamicallySized());
2192 		return w.render();
2193 	});
2194 }
2195 
arrayDataAreaFunction(ArrayType const & _type)2196 string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type)
2197 {
2198 	string functionName = "array_dataslot_" + _type.identifier();
2199 	return m_functionCollector.createFunction(functionName, [&]() {
2200 		// No special processing for calldata arrays, because they are stored as
2201 		// offset of the data area and length on the stack, so the offset already
2202 		// points to the data area.
2203 		// This might change, if calldata arrays are stored in a single
2204 		// stack slot at some point.
2205 		return Whiskers(R"(
2206 			function <functionName>(ptr) -> data {
2207 				data := ptr
2208 				<?dynamic>
2209 					<?memory>
2210 						data := add(ptr, 0x20)
2211 					</memory>
2212 					<?storage>
2213 						mstore(0, ptr)
2214 						data := keccak256(0, 0x20)
2215 					</storage>
2216 				</dynamic>
2217 			}
2218 		)")
2219 		("functionName", functionName)
2220 		("dynamic", _type.isDynamicallySized())
2221 		("memory", _type.location() == DataLocation::Memory)
2222 		("storage", _type.location() == DataLocation::Storage)
2223 		.render();
2224 	});
2225 }
2226 
storageArrayIndexAccessFunction(ArrayType const & _type)2227 string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type)
2228 {
2229 	string functionName = "storage_array_index_access_" + _type.identifier();
2230 	return m_functionCollector.createFunction(functionName, [&]() {
2231 		return Whiskers(R"(
2232 			function <functionName>(array, index) -> slot, offset {
2233 				let arrayLength := <arrayLen>(array)
2234 				if iszero(lt(index, arrayLength)) { <panic>() }
2235 
2236 				<?multipleItemsPerSlot>
2237 					<?isBytesArray>
2238 						switch lt(arrayLength, 0x20)
2239 						case 0 {
2240 							slot, offset := <indexAccessNoChecks>(array, index)
2241 						}
2242 						default {
2243 							offset := sub(31, mod(index, 0x20))
2244 							slot := array
2245 						}
2246 					<!isBytesArray>
2247 						let dataArea := <dataAreaFunc>(array)
2248 						slot := add(dataArea, div(index, <itemsPerSlot>))
2249 						offset := mul(mod(index, <itemsPerSlot>), <storageBytes>)
2250 					</isBytesArray>
2251 				<!multipleItemsPerSlot>
2252 					let dataArea := <dataAreaFunc>(array)
2253 					slot := add(dataArea, mul(index, <storageSize>))
2254 					offset := 0
2255 				</multipleItemsPerSlot>
2256 			}
2257 		)")
2258 		("functionName", functionName)
2259 		("panic", panicFunction(PanicCode::ArrayOutOfBounds))
2260 		("arrayLen", arrayLengthFunction(_type))
2261 		("dataAreaFunc", arrayDataAreaFunction(_type))
2262 		("indexAccessNoChecks", longByteArrayStorageIndexAccessNoCheckFunction())
2263 		("multipleItemsPerSlot", _type.baseType()->storageBytes() <= 16)
2264 		("isBytesArray", _type.isByteArray())
2265 		("storageSize", _type.baseType()->storageSize().str())
2266 		("storageBytes", toString(_type.baseType()->storageBytes()))
2267 		("itemsPerSlot", to_string(32 / _type.baseType()->storageBytes()))
2268 		.render();
2269 	});
2270 }
2271 
memoryArrayIndexAccessFunction(ArrayType const & _type)2272 string YulUtilFunctions::memoryArrayIndexAccessFunction(ArrayType const& _type)
2273 {
2274 	string functionName = "memory_array_index_access_" + _type.identifier();
2275 	return m_functionCollector.createFunction(functionName, [&]() {
2276 		return Whiskers(R"(
2277 			function <functionName>(baseRef, index) -> addr {
2278 				if iszero(lt(index, <arrayLen>(baseRef))) {
2279 					<panic>()
2280 				}
2281 
2282 				let offset := mul(index, <stride>)
2283 				<?dynamicallySized>
2284 					offset := add(offset, 32)
2285 				</dynamicallySized>
2286 				addr := add(baseRef, offset)
2287 			}
2288 		)")
2289 		("functionName", functionName)
2290 		("panic", panicFunction(PanicCode::ArrayOutOfBounds))
2291 		("arrayLen", arrayLengthFunction(_type))
2292 		("stride", to_string(_type.memoryStride()))
2293 		("dynamicallySized", _type.isDynamicallySized())
2294 		.render();
2295 	});
2296 }
2297 
calldataArrayIndexAccessFunction(ArrayType const & _type)2298 string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type)
2299 {
2300 	solAssert(_type.dataStoredIn(DataLocation::CallData), "");
2301 	string functionName = "calldata_array_index_access_" + _type.identifier();
2302 	return m_functionCollector.createFunction(functionName, [&]() {
2303 		return Whiskers(R"(
2304 			function <functionName>(base_ref<?dynamicallySized>, length</dynamicallySized>, index) -> addr<?dynamicallySizedBase>, len</dynamicallySizedBase> {
2305 				if iszero(lt(index, <?dynamicallySized>length<!dynamicallySized><arrayLen></dynamicallySized>)) { <panic>() }
2306 				addr := add(base_ref, mul(index, <stride>))
2307 				<?dynamicallyEncodedBase>
2308 					addr<?dynamicallySizedBase>, len</dynamicallySizedBase> := <accessCalldataTail>(base_ref, addr)
2309 				</dynamicallyEncodedBase>
2310 			}
2311 		)")
2312 		("functionName", functionName)
2313 		("panic", panicFunction(PanicCode::ArrayOutOfBounds))
2314 		("stride", to_string(_type.calldataStride()))
2315 		("dynamicallySized", _type.isDynamicallySized())
2316 		("dynamicallyEncodedBase", _type.baseType()->isDynamicallyEncoded())
2317 		("dynamicallySizedBase", _type.baseType()->isDynamicallySized())
2318 		("arrayLen",  toCompactHexWithPrefix(_type.length()))
2319 		("accessCalldataTail", _type.baseType()->isDynamicallyEncoded() ? accessCalldataTailFunction(*_type.baseType()): "")
2320 		.render();
2321 	});
2322 }
2323 
calldataArrayIndexRangeAccess(ArrayType const & _type)2324 string YulUtilFunctions::calldataArrayIndexRangeAccess(ArrayType const& _type)
2325 {
2326 	solAssert(_type.dataStoredIn(DataLocation::CallData), "");
2327 	solAssert(_type.isDynamicallySized(), "");
2328 	string functionName = "calldata_array_index_range_access_" + _type.identifier();
2329 	return m_functionCollector.createFunction(functionName, [&]() {
2330 		return Whiskers(R"(
2331 			function <functionName>(offset, length, startIndex, endIndex) -> offsetOut, lengthOut {
2332 				if gt(startIndex, endIndex) { <revertSliceStartAfterEnd>() }
2333 				if gt(endIndex, length) { <revertSliceGreaterThanLength>() }
2334 				offsetOut := add(offset, mul(startIndex, <stride>))
2335 				lengthOut := sub(endIndex, startIndex)
2336 			}
2337 		)")
2338 		("functionName", functionName)
2339 		("stride", to_string(_type.calldataStride()))
2340 		("revertSliceStartAfterEnd", revertReasonIfDebugFunction("Slice starts after end"))
2341 		("revertSliceGreaterThanLength", revertReasonIfDebugFunction("Slice is greater than length"))
2342 		.render();
2343 	});
2344 }
2345 
accessCalldataTailFunction(Type const & _type)2346 string YulUtilFunctions::accessCalldataTailFunction(Type const& _type)
2347 {
2348 	solAssert(_type.isDynamicallyEncoded(), "");
2349 	solAssert(_type.dataStoredIn(DataLocation::CallData), "");
2350 	string functionName = "access_calldata_tail_" + _type.identifier();
2351 	return m_functionCollector.createFunction(functionName, [&]() {
2352 		return Whiskers(R"(
2353 			function <functionName>(base_ref, ptr_to_tail) -> addr<?dynamicallySized>, length</dynamicallySized> {
2354 				let rel_offset_of_tail := calldataload(ptr_to_tail)
2355 				if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { <invalidCalldataTailOffset>() }
2356 				addr := add(base_ref, rel_offset_of_tail)
2357 				<?dynamicallySized>
2358 					length := calldataload(addr)
2359 					if gt(length, 0xffffffffffffffff) { <invalidCalldataTailLength>() }
2360 					addr := add(addr, 32)
2361 					if sgt(addr, sub(calldatasize(), mul(length, <calldataStride>))) { <shortCalldataTail>() }
2362 				</dynamicallySized>
2363 			}
2364 		)")
2365 		("functionName", functionName)
2366 		("dynamicallySized", _type.isDynamicallySized())
2367 		("neededLength", toCompactHexWithPrefix(_type.calldataEncodedTailSize()))
2368 		("calldataStride", toCompactHexWithPrefix(_type.isDynamicallySized() ? dynamic_cast<ArrayType const&>(_type).calldataStride() : 0))
2369 		("invalidCalldataTailOffset", revertReasonIfDebugFunction("Invalid calldata tail offset"))
2370 		("invalidCalldataTailLength", revertReasonIfDebugFunction("Invalid calldata tail length"))
2371 		("shortCalldataTail", revertReasonIfDebugFunction("Calldata tail too short"))
2372 		.render();
2373 	});
2374 }
2375 
nextArrayElementFunction(ArrayType const & _type)2376 string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
2377 {
2378 	solAssert(!_type.isByteArray(), "");
2379 	if (_type.dataStoredIn(DataLocation::Storage))
2380 		solAssert(_type.baseType()->storageBytes() > 16, "");
2381 	string functionName = "array_nextElement_" + _type.identifier();
2382 	return m_functionCollector.createFunction(functionName, [&]() {
2383 		Whiskers templ(R"(
2384 			function <functionName>(ptr) -> next {
2385 				next := add(ptr, <advance>)
2386 			}
2387 		)");
2388 		templ("functionName", functionName);
2389 		switch (_type.location())
2390 		{
2391 		case DataLocation::Memory:
2392 			templ("advance", "0x20");
2393 			break;
2394 		case DataLocation::Storage:
2395 		{
2396 			u256 size = _type.baseType()->storageSize();
2397 			solAssert(size >= 1, "");
2398 			templ("advance", toCompactHexWithPrefix(size));
2399 			break;
2400 		}
2401 		case DataLocation::CallData:
2402 		{
2403 			u256 size = _type.calldataStride();
2404 			solAssert(size >= 32 && size % 32 == 0, "");
2405 			templ("advance", toCompactHexWithPrefix(size));
2406 			break;
2407 		}
2408 		}
2409 		return templ.render();
2410 	});
2411 }
2412 
copyArrayFromStorageToMemoryFunction(ArrayType const & _from,ArrayType const & _to)2413 string YulUtilFunctions::copyArrayFromStorageToMemoryFunction(ArrayType const& _from, ArrayType const& _to)
2414 {
2415 	solAssert(_from.dataStoredIn(DataLocation::Storage), "");
2416 	solAssert(_to.dataStoredIn(DataLocation::Memory), "");
2417 	solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
2418 	if (!_from.isDynamicallySized())
2419 		solAssert(_from.length() == _to.length(), "");
2420 
2421 	string functionName = "copy_array_from_storage_to_memory_" + _from.identifier();
2422 
2423 	return m_functionCollector.createFunction(functionName, [&]() {
2424 		if (_from.baseType()->isValueType())
2425 		{
2426 			solAssert(*_from.baseType() == *_to.baseType(), "");
2427 			ABIFunctions abi(m_evmVersion, m_revertStrings, m_functionCollector);
2428 			return Whiskers(R"(
2429 				function <functionName>(slot) -> memPtr {
2430 					memPtr := <allocateUnbounded>()
2431 					let end := <encode>(slot, memPtr)
2432 					<finalizeAllocation>(memPtr, sub(end, memPtr))
2433 				}
2434 			)")
2435 			("functionName", functionName)
2436 			("allocateUnbounded", allocateUnboundedFunction())
2437 			(
2438 				"encode",
2439 				abi.abiEncodeAndReturnUpdatedPosFunction(_from, _to, ABIFunctions::EncodingOptions{})
2440 			)
2441 			("finalizeAllocation", finalizeAllocationFunction())
2442 			.render();
2443 		}
2444 		else
2445 		{
2446 			solAssert(_to.memoryStride() == 32, "");
2447 			solAssert(_to.baseType()->dataStoredIn(DataLocation::Memory), "");
2448 			solAssert(_from.baseType()->dataStoredIn(DataLocation::Storage), "");
2449 			solAssert(!_from.isByteArray(), "");
2450 			solAssert(*_to.withLocation(DataLocation::Storage, _from.isPointer()) == _from, "");
2451 			return Whiskers(R"(
2452 				function <functionName>(slot) -> memPtr {
2453 					let length := <lengthFunction>(slot)
2454 					memPtr := <allocateArray>(length)
2455 					let mpos := memPtr
2456 					<?dynamic>mpos := add(mpos, 0x20)</dynamic>
2457 					let spos := <arrayDataArea>(slot)
2458 					for { let i := 0 } lt(i, length) { i := add(i, 1) } {
2459 						mstore(mpos, <convert>(spos))
2460 						mpos := add(mpos, 0x20)
2461 						spos := add(spos, <baseStorageSize>)
2462 					}
2463 				}
2464 			)")
2465 			("functionName", functionName)
2466 			("lengthFunction", arrayLengthFunction(_from))
2467 			("allocateArray", allocateMemoryArrayFunction(_to))
2468 			("arrayDataArea", arrayDataAreaFunction(_from))
2469 			("dynamic", _to.isDynamicallySized())
2470 			("convert", conversionFunction(*_from.baseType(), *_to.baseType()))
2471 			("baseStorageSize", _from.baseType()->storageSize().str())
2472 			.render();
2473 		}
2474 	});
2475 }
2476 
bytesConcatFunction(vector<Type const * > const & _argumentTypes)2477 string YulUtilFunctions::bytesConcatFunction(vector<Type const*> const& _argumentTypes)
2478 {
2479 	string functionName = "bytes_concat";
2480 	size_t totalParams = 0;
2481 	vector<Type const*> targetTypes;
2482 	for (Type const* argumentType: _argumentTypes)
2483 	{
2484 		solAssert(
2485 			argumentType->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()) ||
2486 			argumentType->isImplicitlyConvertibleTo(*TypeProvider::fixedBytes(32)),
2487 			""
2488 		);
2489 		if (argumentType->category() == Type::Category::FixedBytes)
2490 			targetTypes.emplace_back(argumentType);
2491 		else if (
2492 			auto const* literalType = dynamic_cast<StringLiteralType const*>(argumentType);
2493 			literalType && !literalType->value().empty() && literalType->value().size() <= 32
2494 		)
2495 			targetTypes.emplace_back(TypeProvider::fixedBytes(static_cast<unsigned>(literalType->value().size())));
2496 		else
2497 		{
2498 			solAssert(!dynamic_cast<RationalNumberType const*>(argumentType), "");
2499 			solAssert(argumentType->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()), "");
2500 			targetTypes.emplace_back(TypeProvider::bytesMemory());
2501 		}
2502 
2503 		totalParams += argumentType->sizeOnStack();
2504 		functionName += "_" + argumentType->identifier();
2505 	}
2506 
2507 	return m_functionCollector.createFunction(functionName, [&]() {
2508 		Whiskers templ(R"(
2509 			function <functionName>(<parameters>) -> outPtr {
2510 				outPtr := <allocateUnbounded>()
2511 				let dataStart := add(outPtr, 0x20)
2512 				let dataEnd := <encodePacked>(dataStart<?+parameters>, <parameters></+parameters>)
2513 				mstore(outPtr, sub(dataEnd, dataStart))
2514 				<finalizeAllocation>(outPtr, sub(dataEnd, outPtr))
2515 			}
2516 		)");
2517 		templ("functionName", functionName);
2518 		templ("parameters", suffixedVariableNameList("param_", 0, totalParams));
2519 		templ("allocateUnbounded", allocateUnboundedFunction());
2520 		templ("finalizeAllocation", finalizeAllocationFunction());
2521 		templ(
2522 			"encodePacked",
2523 			ABIFunctions{m_evmVersion, m_revertStrings, m_functionCollector}.tupleEncoderPacked(
2524 				_argumentTypes,
2525 				targetTypes
2526 			)
2527 		);
2528 		return templ.render();
2529 	});
2530 }
2531 
mappingIndexAccessFunction(MappingType const & _mappingType,Type const & _keyType)2532 string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType)
2533 {
2534 	string functionName = "mapping_index_access_" + _mappingType.identifier() + "_of_" + _keyType.identifier();
2535 	return m_functionCollector.createFunction(functionName, [&]() {
2536 		if (_mappingType.keyType()->isDynamicallySized())
2537 			return Whiskers(R"(
2538 				function <functionName>(slot <?+key>,</+key> <key>) -> dataSlot {
2539 					dataSlot := <hash>(<key> <?+key>,</+key> slot)
2540 				}
2541 			)")
2542 			("functionName", functionName)
2543 			("key", suffixedVariableNameList("key_", 0, _keyType.sizeOnStack()))
2544 			("hash", packedHashFunction(
2545 				{&_keyType, TypeProvider::uint256()},
2546 				{_mappingType.keyType(), TypeProvider::uint256()}
2547 			))
2548 			.render();
2549 		else
2550 		{
2551 			solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
2552 			solAssert(!_mappingType.keyType()->isDynamicallyEncoded(), "");
2553 			solAssert(_mappingType.keyType()->calldataEncodedSize(false) <= 0x20, "");
2554 			Whiskers templ(R"(
2555 				function <functionName>(slot <key>) -> dataSlot {
2556 					mstore(0, <convertedKey>)
2557 					mstore(0x20, slot)
2558 					dataSlot := keccak256(0, 0x40)
2559 				}
2560 			)");
2561 			templ("functionName", functionName);
2562 			templ("key", _keyType.sizeOnStack() == 1 ? ", key" : "");
2563 			if (_keyType.sizeOnStack() == 0)
2564 				templ("convertedKey", conversionFunction(_keyType, *_mappingType.keyType()) + "()");
2565 			else
2566 				templ("convertedKey", conversionFunction(_keyType, *_mappingType.keyType()) + "(key)");
2567 			return templ.render();
2568 		}
2569 	});
2570 }
2571 
readFromStorage(Type const & _type,size_t _offset,bool _splitFunctionTypes)2572 string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes)
2573 {
2574 	if (_type.isValueType())
2575 		return readFromStorageValueType(_type, _offset, _splitFunctionTypes);
2576 	else
2577 	{
2578 		solAssert(_offset == 0, "");
2579 		return readFromStorageReferenceType(_type);
2580 	}
2581 }
2582 
readFromStorageDynamic(Type const & _type,bool _splitFunctionTypes)2583 string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes)
2584 {
2585 	if (_type.isValueType())
2586 		return readFromStorageValueType(_type, {}, _splitFunctionTypes);
2587 	string functionName =
2588 		"read_from_storage__dynamic_" +
2589 		string(_splitFunctionTypes ? "split_" : "") +
2590 		_type.identifier();
2591 
2592 	return m_functionCollector.createFunction(functionName, [&] {
2593 		return Whiskers(R"(
2594 			function <functionName>(slot, offset) -> value {
2595 				if gt(offset, 0) { <panic>() }
2596 				value := <readFromStorage>(slot)
2597 			}
2598 		)")
2599 		("functionName", functionName)
2600 		("panic", panicFunction(util::PanicCode::Generic))
2601 		("readFromStorage", readFromStorageReferenceType(_type))
2602 		.render();
2603 	});
2604 }
2605 
readFromStorageValueType(Type const & _type,optional<size_t> _offset,bool _splitFunctionTypes)2606 string YulUtilFunctions::readFromStorageValueType(Type const& _type, optional<size_t> _offset, bool _splitFunctionTypes)
2607 {
2608 	solAssert(_type.isValueType(), "");
2609 
2610 	string functionName =
2611 			"read_from_storage_" +
2612 			string(_splitFunctionTypes ? "split_" : "") + (
2613 				_offset.has_value() ?
2614 				"offset_" + to_string(*_offset) :
2615 				"dynamic"
2616 			) +
2617 			"_" +
2618 			_type.identifier();
2619 
2620 	return m_functionCollector.createFunction(functionName, [&] {
2621 		Whiskers templ(R"(
2622 			function <functionName>(slot<?dynamic>, offset</dynamic>) -> <?split>addr, selector<!split>value</split> {
2623 				<?split>let</split> value := <extract>(sload(slot)<?dynamic>, offset</dynamic>)
2624 				<?split>
2625 					addr, selector := <splitFunction>(value)
2626 				</split>
2627 			}
2628 		)");
2629 		templ("functionName", functionName);
2630 		templ("dynamic", !_offset.has_value());
2631 		if (_offset.has_value())
2632 			templ("extract", extractFromStorageValue(_type, *_offset));
2633 		else
2634 			templ("extract", extractFromStorageValueDynamic(_type));
2635 		auto const* funType = dynamic_cast<FunctionType const*>(&_type);
2636 		bool split = _splitFunctionTypes && funType && funType->kind() == FunctionType::Kind::External;
2637 		templ("split", split);
2638 		if (split)
2639 			templ("splitFunction", splitExternalFunctionIdFunction());
2640 		return templ.render();
2641 	});
2642 }
2643 
readFromStorageReferenceType(Type const & _type)2644 string YulUtilFunctions::readFromStorageReferenceType(Type const& _type)
2645 {
2646 	if (auto const* arrayType = dynamic_cast<ArrayType const*>(&_type))
2647 	{
2648 		solAssert(arrayType->dataStoredIn(DataLocation::Memory), "");
2649 		return copyArrayFromStorageToMemoryFunction(
2650 			dynamic_cast<ArrayType const&>(*arrayType->copyForLocation(DataLocation::Storage, false)),
2651 			*arrayType
2652 		);
2653 	}
2654 	solAssert(_type.category() == Type::Category::Struct, "");
2655 
2656 	string functionName = "read_from_storage_reference_type_" + _type.identifier();
2657 
2658 	auto const& structType = dynamic_cast<StructType const&>(_type);
2659 	solAssert(structType.location() == DataLocation::Memory, "");
2660 	MemberList::MemberMap structMembers = structType.nativeMembers(nullptr);
2661 	vector<map<string, string>> memberSetValues(structMembers.size());
2662 	for (size_t i = 0; i < structMembers.size(); ++i)
2663 	{
2664 		auto const& [memberSlotDiff, memberStorageOffset] = structType.storageOffsetsOfMember(structMembers[i].name);
2665 		solAssert(structMembers[i].type->isValueType() || memberStorageOffset == 0, "");
2666 
2667 		memberSetValues[i]["setMember"] = Whiskers(R"(
2668 			{
2669 				let <memberValues> := <readFromStorage>(add(slot, <memberSlotDiff>))
2670 				<writeToMemory>(add(value, <memberMemoryOffset>), <memberValues>)
2671 			}
2672 		)")
2673 		("memberValues", suffixedVariableNameList("memberValue_", 0, structMembers[i].type->stackItems().size()))
2674 		("memberMemoryOffset", structType.memoryOffsetOfMember(structMembers[i].name).str())
2675 		("memberSlotDiff",  memberSlotDiff.str())
2676 		("readFromStorage", readFromStorage(*structMembers[i].type, memberStorageOffset, true))
2677 		("writeToMemory", writeToMemoryFunction(*structMembers[i].type))
2678 		.render();
2679 	}
2680 
2681 	return m_functionCollector.createFunction(functionName, [&] {
2682 		return Whiskers(R"(
2683 			function <functionName>(slot) -> value {
2684 				value := <allocStruct>()
2685 				<#member>
2686 					<setMember>
2687 				</member>
2688 			}
2689 		)")
2690 		("functionName", functionName)
2691 		("allocStruct", allocateMemoryStructFunction(structType))
2692 		("member", memberSetValues)
2693 		.render();
2694 	});
2695 }
2696 
readFromMemory(Type const & _type)2697 string YulUtilFunctions::readFromMemory(Type const& _type)
2698 {
2699 	return readFromMemoryOrCalldata(_type, false);
2700 }
2701 
readFromCalldata(Type const & _type)2702 string YulUtilFunctions::readFromCalldata(Type const& _type)
2703 {
2704 	return readFromMemoryOrCalldata(_type, true);
2705 }
2706 
updateStorageValueFunction(Type const & _fromType,Type const & _toType,std::optional<unsigned> const & _offset)2707 string YulUtilFunctions::updateStorageValueFunction(
2708 	Type const& _fromType,
2709 	Type const& _toType,
2710 	std::optional<unsigned> const& _offset
2711 )
2712 {
2713 	string const functionName =
2714 		"update_storage_value_" +
2715 		(_offset.has_value() ? ("offset_" + to_string(*_offset)) : "") +
2716 		_fromType.identifier() +
2717 		"_to_" +
2718 		_toType.identifier();
2719 
2720 	return m_functionCollector.createFunction(functionName, [&] {
2721 		if (_toType.isValueType())
2722 		{
2723 			solAssert(_fromType.isImplicitlyConvertibleTo(_toType), "");
2724 			solAssert(_toType.storageBytes() <= 32, "Invalid storage bytes size.");
2725 			solAssert(_toType.storageBytes() > 0, "Invalid storage bytes size.");
2726 
2727 			return Whiskers(R"(
2728 				function <functionName>(slot, <offset><fromValues>) {
2729 					let <toValues> := <convert>(<fromValues>)
2730 					sstore(slot, <update>(sload(slot), <offset><prepare>(<toValues>)))
2731 				}
2732 
2733 			)")
2734 			("functionName", functionName)
2735 			("update",
2736 				_offset.has_value() ?
2737 					updateByteSliceFunction(_toType.storageBytes(), *_offset) :
2738 					updateByteSliceFunctionDynamic(_toType.storageBytes())
2739 			)
2740 			("offset", _offset.has_value() ? "" : "offset, ")
2741 			("convert", conversionFunction(_fromType, _toType))
2742 			("fromValues", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack()))
2743 			("toValues", suffixedVariableNameList("convertedValue_", 0, _toType.sizeOnStack()))
2744 			("prepare", prepareStoreFunction(_toType))
2745 			.render();
2746 		}
2747 
2748 		auto const* toReferenceType = dynamic_cast<ReferenceType const*>(&_toType);
2749 		auto const* fromReferenceType = dynamic_cast<ReferenceType const*>(&_fromType);
2750 		solAssert(toReferenceType, "");
2751 
2752 		if (!fromReferenceType)
2753 		{
2754 			solAssert(_fromType.category() == Type::Category::StringLiteral, "");
2755 			solAssert(toReferenceType->category() == Type::Category::Array, "");
2756 			auto const& toArrayType = dynamic_cast<ArrayType const&>(*toReferenceType);
2757 			solAssert(toArrayType.isByteArray(), "");
2758 
2759 			return Whiskers(R"(
2760 				function <functionName>(slot<?dynamicOffset>, offset</dynamicOffset>) {
2761 					<?dynamicOffset>if offset { <panic>() }</dynamicOffset>
2762 					<copyToStorage>(slot)
2763 				}
2764 			)")
2765 			("functionName", functionName)
2766 			("dynamicOffset", !_offset.has_value())
2767 			("panic", panicFunction(PanicCode::Generic))
2768 			("copyToStorage", copyLiteralToStorageFunction(dynamic_cast<StringLiteralType const&>(_fromType).value()))
2769 			.render();
2770 		}
2771 
2772 		solAssert(*toReferenceType->copyForLocation(
2773 			fromReferenceType->location(),
2774 			fromReferenceType->isPointer()
2775 		).get() == *fromReferenceType, "");
2776 
2777 		if (fromReferenceType->category() == Type::Category::ArraySlice)
2778 			solAssert(toReferenceType->category() == Type::Category::Array, "");
2779 		else
2780 			solAssert(toReferenceType->category() == fromReferenceType->category(), "");
2781 		solAssert(_offset.value_or(0) == 0, "");
2782 
2783 		Whiskers templ(R"(
2784 			function <functionName>(slot, <?dynamicOffset>offset, </dynamicOffset><value>) {
2785 				<?dynamicOffset>if offset { <panic>() }</dynamicOffset>
2786 				<copyToStorage>(slot, <value>)
2787 			}
2788 		)");
2789 		templ("functionName", functionName);
2790 		templ("dynamicOffset", !_offset.has_value());
2791 		templ("panic", panicFunction(PanicCode::Generic));
2792 		templ("value", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack()));
2793 		if (_fromType.category() == Type::Category::Array)
2794 			templ("copyToStorage", copyArrayToStorageFunction(
2795 				dynamic_cast<ArrayType const&>(_fromType),
2796 				dynamic_cast<ArrayType const&>(_toType)
2797 			));
2798 		else if (_fromType.category() == Type::Category::ArraySlice)
2799 		{
2800 			solAssert(
2801 				_fromType.dataStoredIn(DataLocation::CallData),
2802 				"Currently only calldata array slices are supported!"
2803 			);
2804 			templ("copyToStorage", copyArrayToStorageFunction(
2805 				dynamic_cast<ArraySliceType const&>(_fromType).arrayType(),
2806 				dynamic_cast<ArrayType const&>(_toType)
2807 			));
2808 		}
2809 		else
2810 			templ("copyToStorage", copyStructToStorageFunction(
2811 				dynamic_cast<StructType const&>(_fromType),
2812 				dynamic_cast<StructType const&>(_toType)
2813 			));
2814 
2815 		return templ.render();
2816 	});
2817 }
2818 
writeToMemoryFunction(Type const & _type)2819 string YulUtilFunctions::writeToMemoryFunction(Type const& _type)
2820 {
2821 	string const functionName = "write_to_memory_" + _type.identifier();
2822 
2823 	return m_functionCollector.createFunction(functionName, [&] {
2824 		solAssert(!dynamic_cast<StringLiteralType const*>(&_type), "");
2825 		if (auto ref = dynamic_cast<ReferenceType const*>(&_type))
2826 		{
2827 			solAssert(
2828 				ref->location() == DataLocation::Memory,
2829 				"Can only update types with location memory."
2830 			);
2831 
2832 			return Whiskers(R"(
2833 				function <functionName>(memPtr, value) {
2834 					mstore(memPtr, value)
2835 				}
2836 			)")
2837 			("functionName", functionName)
2838 			.render();
2839 		}
2840 		else if (
2841 			_type.category() == Type::Category::Function &&
2842 			dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External
2843 		)
2844 		{
2845 			return Whiskers(R"(
2846 				function <functionName>(memPtr, addr, selector) {
2847 					mstore(memPtr, <combine>(addr, selector))
2848 				}
2849 			)")
2850 			("functionName", functionName)
2851 			("combine", combineExternalFunctionIdFunction())
2852 			.render();
2853 		}
2854 		else if (_type.isValueType())
2855 		{
2856 			return Whiskers(R"(
2857 				function <functionName>(memPtr, value) {
2858 					mstore(memPtr, <cleanup>(value))
2859 				}
2860 			)")
2861 			("functionName", functionName)
2862 			("cleanup", cleanupFunction(_type))
2863 			.render();
2864 		}
2865 		else // Should never happen
2866 		{
2867 			solAssert(
2868 				false,
2869 				"Memory store of type " + _type.toString(true) + " not allowed."
2870 			);
2871 		}
2872 	});
2873 }
2874 
extractFromStorageValueDynamic(Type const & _type)2875 string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type)
2876 {
2877 	string functionName =
2878 		"extract_from_storage_value_dynamic" +
2879 		_type.identifier();
2880 	return m_functionCollector.createFunction(functionName, [&] {
2881 		return Whiskers(R"(
2882 			function <functionName>(slot_value, offset) -> value {
2883 				value := <cleanupStorage>(<shr>(mul(offset, 8), slot_value))
2884 			}
2885 		)")
2886 		("functionName", functionName)
2887 		("shr", shiftRightFunctionDynamic())
2888 		("cleanupStorage", cleanupFromStorageFunction(_type))
2889 		.render();
2890 	});
2891 }
2892 
extractFromStorageValue(Type const & _type,size_t _offset)2893 string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offset)
2894 {
2895 	string functionName = "extract_from_storage_value_offset_" + to_string(_offset) + _type.identifier();
2896 	return m_functionCollector.createFunction(functionName, [&] {
2897 		return Whiskers(R"(
2898 			function <functionName>(slot_value) -> value {
2899 				value := <cleanupStorage>(<shr>(slot_value))
2900 			}
2901 		)")
2902 		("functionName", functionName)
2903 		("shr", shiftRightFunction(_offset * 8))
2904 		("cleanupStorage", cleanupFromStorageFunction(_type))
2905 		.render();
2906 	});
2907 }
2908 
cleanupFromStorageFunction(Type const & _type)2909 string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type)
2910 {
2911 	solAssert(_type.isValueType(), "");
2912 
2913 	string functionName = string("cleanup_from_storage_") + _type.identifier();
2914 	return m_functionCollector.createFunction(functionName, [&] {
2915 		Whiskers templ(R"(
2916 			function <functionName>(value) -> cleaned {
2917 				cleaned := <cleaned>
2918 			}
2919 		)");
2920 		templ("functionName", functionName);
2921 
2922 		Type const* encodingType = &_type;
2923 		if (_type.category() == Type::Category::UserDefinedValueType)
2924 			encodingType = _type.encodingType();
2925 		unsigned storageBytes = encodingType->storageBytes();
2926 		if (IntegerType const* intType = dynamic_cast<IntegerType const*>(encodingType))
2927 			if (intType->isSigned() && storageBytes != 32)
2928 			{
2929 				templ("cleaned", "signextend(" + to_string(storageBytes - 1) + ", value)");
2930 				return templ.render();
2931 			}
2932 
2933 		if (storageBytes == 32)
2934 			templ("cleaned", "value");
2935 		else if (encodingType->leftAligned())
2936 			templ("cleaned", shiftLeftFunction(256 - 8 * storageBytes) + "(value)");
2937 		else
2938 			templ("cleaned", "and(value, " + toCompactHexWithPrefix((u256(1) << (8 * storageBytes)) - 1) + ")");
2939 
2940 		return templ.render();
2941 	});
2942 }
2943 
prepareStoreFunction(Type const & _type)2944 string YulUtilFunctions::prepareStoreFunction(Type const& _type)
2945 {
2946 	string functionName = "prepare_store_" + _type.identifier();
2947 	return m_functionCollector.createFunction(functionName, [&]() {
2948 		solAssert(_type.isValueType(), "");
2949 		auto const* funType = dynamic_cast<FunctionType const*>(&_type);
2950 		if (funType && funType->kind() == FunctionType::Kind::External)
2951 		{
2952 			Whiskers templ(R"(
2953 				function <functionName>(addr, selector) -> ret {
2954 					ret := <prepareBytes>(<combine>(addr, selector))
2955 				}
2956 			)");
2957 			templ("functionName", functionName);
2958 			templ("prepareBytes", prepareStoreFunction(*TypeProvider::fixedBytes(24)));
2959 			templ("combine", combineExternalFunctionIdFunction());
2960 			return templ.render();
2961 		}
2962 		else
2963 		{
2964 			solAssert(_type.sizeOnStack() == 1, "");
2965 			Whiskers templ(R"(
2966 				function <functionName>(value) -> ret {
2967 					ret := <actualPrepare>
2968 				}
2969 			)");
2970 			templ("functionName", functionName);
2971 			if (_type.leftAligned())
2972 				templ("actualPrepare", shiftRightFunction(256 - 8 * _type.storageBytes()) + "(value)");
2973 			else
2974 				templ("actualPrepare", "value");
2975 			return templ.render();
2976 		}
2977 	});
2978 }
2979 
allocationFunction()2980 string YulUtilFunctions::allocationFunction()
2981 {
2982 	string functionName = "allocate_memory";
2983 	return m_functionCollector.createFunction(functionName, [&]() {
2984 		return Whiskers(R"(
2985 			function <functionName>(size) -> memPtr {
2986 				memPtr := <allocateUnbounded>()
2987 				<finalizeAllocation>(memPtr, size)
2988 			}
2989 		)")
2990 		("functionName", functionName)
2991 		("allocateUnbounded", allocateUnboundedFunction())
2992 		("finalizeAllocation", finalizeAllocationFunction())
2993 		.render();
2994 	});
2995 }
2996 
allocateUnboundedFunction()2997 string YulUtilFunctions::allocateUnboundedFunction()
2998 {
2999 	string functionName = "allocate_unbounded";
3000 	return m_functionCollector.createFunction(functionName, [&]() {
3001 		return Whiskers(R"(
3002 			function <functionName>() -> memPtr {
3003 				memPtr := mload(<freeMemoryPointer>)
3004 			}
3005 		)")
3006 		("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer))
3007 		("functionName", functionName)
3008 		.render();
3009 	});
3010 }
3011 
finalizeAllocationFunction()3012 string YulUtilFunctions::finalizeAllocationFunction()
3013 {
3014 	string functionName = "finalize_allocation";
3015 	return m_functionCollector.createFunction(functionName, [&]() {
3016 		return Whiskers(R"(
3017 			function <functionName>(memPtr, size) {
3018 				let newFreePtr := add(memPtr, <roundUp>(size))
3019 				// protect against overflow
3020 				if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { <panic>() }
3021 				mstore(<freeMemoryPointer>, newFreePtr)
3022 			}
3023 		)")
3024 		("functionName", functionName)
3025 		("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer))
3026 		("roundUp", roundUpFunction())
3027 		("panic", panicFunction(PanicCode::ResourceError))
3028 		.render();
3029 	});
3030 }
3031 
zeroMemoryArrayFunction(ArrayType const & _type)3032 string YulUtilFunctions::zeroMemoryArrayFunction(ArrayType const& _type)
3033 {
3034 	if (_type.baseType()->hasSimpleZeroValueInMemory())
3035 		return zeroMemoryFunction(*_type.baseType());
3036 	return zeroComplexMemoryArrayFunction(_type);
3037 }
3038 
zeroMemoryFunction(Type const & _type)3039 string YulUtilFunctions::zeroMemoryFunction(Type const& _type)
3040 {
3041 	solAssert(_type.hasSimpleZeroValueInMemory(), "");
3042 
3043 	string functionName = "zero_memory_chunk_" + _type.identifier();
3044 	return m_functionCollector.createFunction(functionName, [&]() {
3045 		return Whiskers(R"(
3046 			function <functionName>(dataStart, dataSizeInBytes) {
3047 				calldatacopy(dataStart, calldatasize(), dataSizeInBytes)
3048 			}
3049 		)")
3050 		("functionName", functionName)
3051 		.render();
3052 	});
3053 }
3054 
zeroComplexMemoryArrayFunction(ArrayType const & _type)3055 string YulUtilFunctions::zeroComplexMemoryArrayFunction(ArrayType const& _type)
3056 {
3057 	solAssert(!_type.baseType()->hasSimpleZeroValueInMemory(), "");
3058 
3059 	string functionName = "zero_complex_memory_array_" + _type.identifier();
3060 	return m_functionCollector.createFunction(functionName, [&]() {
3061 		solAssert(_type.memoryStride() == 32, "");
3062 		return Whiskers(R"(
3063 			function <functionName>(dataStart, dataSizeInBytes) {
3064 				for {let i := 0} lt(i, dataSizeInBytes) { i := add(i, <stride>) } {
3065 					mstore(add(dataStart, i), <zeroValue>())
3066 				}
3067 			}
3068 		)")
3069 		("functionName", functionName)
3070 		("stride", to_string(_type.memoryStride()))
3071 		("zeroValue", zeroValueFunction(*_type.baseType(), false))
3072 		.render();
3073 	});
3074 }
3075 
allocateMemoryArrayFunction(ArrayType const & _type)3076 string YulUtilFunctions::allocateMemoryArrayFunction(ArrayType const& _type)
3077 {
3078 	string functionName = "allocate_memory_array_" + _type.identifier();
3079 	return m_functionCollector.createFunction(functionName, [&]() {
3080 		return Whiskers(R"(
3081 				function <functionName>(length) -> memPtr {
3082 					let allocSize := <allocSize>(length)
3083 					memPtr := <alloc>(allocSize)
3084 					<?dynamic>
3085 					mstore(memPtr, length)
3086 					</dynamic>
3087 				}
3088 			)")
3089 			("functionName", functionName)
3090 			("alloc", allocationFunction())
3091 			("allocSize", arrayAllocationSizeFunction(_type))
3092 			("dynamic", _type.isDynamicallySized())
3093 			.render();
3094 	});
3095 }
3096 
allocateAndInitializeMemoryArrayFunction(ArrayType const & _type)3097 string YulUtilFunctions::allocateAndInitializeMemoryArrayFunction(ArrayType const& _type)
3098 {
3099 	string functionName = "allocate_and_zero_memory_array_" + _type.identifier();
3100 	return m_functionCollector.createFunction(functionName, [&]() {
3101 		return Whiskers(R"(
3102 				function <functionName>(length) -> memPtr {
3103 					memPtr := <allocArray>(length)
3104 					let dataStart := memPtr
3105 					let dataSize := <allocSize>(length)
3106 					<?dynamic>
3107 					dataStart := add(dataStart, 32)
3108 					dataSize := sub(dataSize, 32)
3109 					</dynamic>
3110 					<zeroArrayFunction>(dataStart, dataSize)
3111 				}
3112 			)")
3113 			("functionName", functionName)
3114 			("allocArray", allocateMemoryArrayFunction(_type))
3115 			("allocSize", arrayAllocationSizeFunction(_type))
3116 			("zeroArrayFunction", zeroMemoryArrayFunction(_type))
3117 			("dynamic", _type.isDynamicallySized())
3118 			.render();
3119 	});
3120 }
3121 
allocateMemoryStructFunction(StructType const & _type)3122 string YulUtilFunctions::allocateMemoryStructFunction(StructType const& _type)
3123 {
3124 	string functionName = "allocate_memory_struct_" + _type.identifier();
3125 	return m_functionCollector.createFunction(functionName, [&]() {
3126 		Whiskers templ(R"(
3127 		function <functionName>() -> memPtr {
3128 			memPtr := <alloc>(<allocSize>)
3129 		}
3130 		)");
3131 		templ("functionName", functionName);
3132 		templ("alloc", allocationFunction());
3133 		templ("allocSize", _type.memoryDataSize().str());
3134 
3135 		return templ.render();
3136 	});
3137 }
3138 
allocateAndInitializeMemoryStructFunction(StructType const & _type)3139 string YulUtilFunctions::allocateAndInitializeMemoryStructFunction(StructType const& _type)
3140 {
3141 	string functionName = "allocate_and_zero_memory_struct_" + _type.identifier();
3142 	return m_functionCollector.createFunction(functionName, [&]() {
3143 		Whiskers templ(R"(
3144 		function <functionName>() -> memPtr {
3145 			memPtr := <allocStruct>()
3146 			let offset := memPtr
3147 			<#member>
3148 				mstore(offset, <zeroValue>())
3149 				offset := add(offset, 32)
3150 			</member>
3151 		}
3152 		)");
3153 		templ("functionName", functionName);
3154 		templ("allocStruct", allocateMemoryStructFunction(_type));
3155 
3156 		TypePointers const& members = _type.memoryMemberTypes();
3157 
3158 		vector<map<string, string>> memberParams(members.size());
3159 		for (size_t i = 0; i < members.size(); ++i)
3160 		{
3161 			solAssert(members[i]->memoryHeadSize() == 32, "");
3162 			memberParams[i]["zeroValue"] = zeroValueFunction(
3163 				*TypeProvider::withLocationIfReference(DataLocation::Memory, members[i]),
3164 				false
3165 			);
3166 		}
3167 		templ("member", memberParams);
3168 		return templ.render();
3169 	});
3170 }
3171 
conversionFunction(Type const & _from,Type const & _to)3172 string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
3173 {
3174 	if (_from.category() == Type::Category::UserDefinedValueType)
3175 	{
3176 		solAssert(_from == _to || _to == dynamic_cast<UserDefinedValueType const&>(_from).underlyingType(), "");
3177 		return conversionFunction(dynamic_cast<UserDefinedValueType const&>(_from).underlyingType(), _to);
3178 	}
3179 	if (_to.category() == Type::Category::UserDefinedValueType)
3180 	{
3181 		solAssert(_from == _to || _from.isImplicitlyConvertibleTo(dynamic_cast<UserDefinedValueType const&>(_to).underlyingType()), "");
3182 		return conversionFunction(_from, dynamic_cast<UserDefinedValueType const&>(_to).underlyingType());
3183 	}
3184 	if (_from.category() == Type::Category::Function)
3185 	{
3186 		solAssert(_to.category() == Type::Category::Function, "");
3187 		FunctionType const& fromType = dynamic_cast<FunctionType const&>(_from);
3188 		FunctionType const& targetType = dynamic_cast<FunctionType const&>(_to);
3189 		solAssert(
3190 			fromType.isImplicitlyConvertibleTo(targetType) &&
3191 			fromType.sizeOnStack() == targetType.sizeOnStack() &&
3192 			(fromType.kind() == FunctionType::Kind::Internal || fromType.kind() == FunctionType::Kind::External) &&
3193 			fromType.kind() == targetType.kind(),
3194 			"Invalid function type conversion requested."
3195 		);
3196 		string const functionName =
3197 			"convert_" +
3198 			_from.identifier() +
3199 			"_to_" +
3200 			_to.identifier();
3201 		return m_functionCollector.createFunction(functionName, [&]() {
3202 			return Whiskers(R"(
3203 				function <functionName>(<?external>addr, </external>functionId) -> <?external>outAddr, </external>outFunctionId {
3204 					<?external>outAddr := addr</external>
3205 					outFunctionId := functionId
3206 				}
3207 			)")
3208 			("functionName", functionName)
3209 			("external", fromType.kind() == FunctionType::Kind::External)
3210 			.render();
3211 		});
3212 	}
3213 	else if (_from.category() == Type::Category::ArraySlice)
3214 	{
3215 		auto const& fromType = dynamic_cast<ArraySliceType const&>(_from);
3216 		if (_to.category() == Type::Category::FixedBytes)
3217 		{
3218 			solAssert(fromType.arrayType().isByteArray(), "Array types other than bytes not convertible to bytesNN.");
3219 			return bytesToFixedBytesConversionFunction(fromType.arrayType(), dynamic_cast<FixedBytesType const &>(_to));
3220 		}
3221 		solAssert(_to.category() == Type::Category::Array, "");
3222 		auto const& targetType = dynamic_cast<ArrayType const&>(_to);
3223 
3224 		solAssert(fromType.arrayType().isImplicitlyConvertibleTo(targetType), "");
3225 		solAssert(
3226 			fromType.arrayType().dataStoredIn(DataLocation::CallData) &&
3227 			fromType.arrayType().isDynamicallySized() &&
3228 			!fromType.arrayType().baseType()->isDynamicallyEncoded(),
3229 			""
3230 		);
3231 
3232 		if (!targetType.dataStoredIn(DataLocation::CallData))
3233 			return arrayConversionFunction(fromType.arrayType(), targetType);
3234 
3235 		string const functionName =
3236 			"convert_" +
3237 			_from.identifier() +
3238 			"_to_" +
3239 			_to.identifier();
3240 		return m_functionCollector.createFunction(functionName, [&]() {
3241 			return Whiskers(R"(
3242 				function <functionName>(offset, length) -> outOffset, outLength {
3243 					outOffset := offset
3244 					outLength := length
3245 				}
3246 			)")
3247 			("functionName", functionName)
3248 			.render();
3249 		});
3250 	}
3251 	else if (_from.category() == Type::Category::Array)
3252 	{
3253 		auto const& fromArrayType =  dynamic_cast<ArrayType const&>(_from);
3254 		if (_to.category() == Type::Category::FixedBytes)
3255 		{
3256 			solAssert(fromArrayType.isByteArray(), "Array types other than bytes not convertible to bytesNN.");
3257 			return bytesToFixedBytesConversionFunction(fromArrayType, dynamic_cast<FixedBytesType const &>(_to));
3258 		}
3259 		solAssert(_to.category() == Type::Category::Array, "");
3260 		return arrayConversionFunction(fromArrayType, dynamic_cast<ArrayType const&>(_to));
3261 	}
3262 
3263 	if (_from.sizeOnStack() != 1 || _to.sizeOnStack() != 1)
3264 		return conversionFunctionSpecial(_from, _to);
3265 
3266 	string functionName =
3267 		"convert_" +
3268 		_from.identifier() +
3269 		"_to_" +
3270 		_to.identifier();
3271 	return m_functionCollector.createFunction(functionName, [&]() {
3272 		Whiskers templ(R"(
3273 			function <functionName>(value) -> converted {
3274 				<body>
3275 			}
3276 		)");
3277 		templ("functionName", functionName);
3278 		string body;
3279 		auto toCategory = _to.category();
3280 		auto fromCategory = _from.category();
3281 		switch (fromCategory)
3282 		{
3283 		case Type::Category::Address:
3284 		case Type::Category::Contract:
3285 			body =
3286 				Whiskers("converted := <convert>(value)")
3287 					("convert", conversionFunction(IntegerType(160), _to))
3288 					.render();
3289 			break;
3290 		case Type::Category::Integer:
3291 		case Type::Category::RationalNumber:
3292 		{
3293 			solAssert(_from.mobileType(), "");
3294 			if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(&_from))
3295 				if (rational->isFractional())
3296 					solAssert(toCategory == Type::Category::FixedPoint, "");
3297 
3298 			if (toCategory == Type::Category::Address || toCategory == Type::Category::Contract)
3299 				body =
3300 					Whiskers("converted := <convert>(value)")
3301 					("convert", conversionFunction(_from, IntegerType(160)))
3302 					.render();
3303 			else
3304 			{
3305 				Whiskers bodyTemplate("converted := <cleanOutput>(<convert>(<cleanInput>(value)))");
3306 				bodyTemplate("cleanInput", cleanupFunction(_from));
3307 				bodyTemplate("cleanOutput", cleanupFunction(_to));
3308 				string convert;
3309 
3310 				solAssert(_to.category() != Type::Category::UserDefinedValueType, "");
3311 				if (auto const* toFixedBytes = dynamic_cast<FixedBytesType const*>(&_to))
3312 					convert = shiftLeftFunction(256 - toFixedBytes->numBytes() * 8);
3313 				else if (dynamic_cast<FixedPointType const*>(&_to))
3314 					solUnimplemented("");
3315 				else if (dynamic_cast<IntegerType const*>(&_to))
3316 				{
3317 					solUnimplementedAssert(fromCategory != Type::Category::FixedPoint);
3318 					convert = identityFunction();
3319 				}
3320 				else if (toCategory == Type::Category::Enum)
3321 				{
3322 					solAssert(fromCategory != Type::Category::FixedPoint, "");
3323 					convert = identityFunction();
3324 				}
3325 				else
3326 					solAssert(false, "");
3327 				solAssert(!convert.empty(), "");
3328 				bodyTemplate("convert", convert);
3329 				body = bodyTemplate.render();
3330 			}
3331 			break;
3332 		}
3333 		case Type::Category::Bool:
3334 		{
3335 			solAssert(_from == _to, "Invalid conversion for bool.");
3336 			body =
3337 				Whiskers("converted := <clean>(value)")
3338 				("clean", cleanupFunction(_from))
3339 				.render();
3340 			break;
3341 		}
3342 		case Type::Category::FixedPoint:
3343 			solUnimplemented("Fixed point types not implemented.");
3344 			break;
3345 		case Type::Category::Struct:
3346 		{
3347 			solAssert(toCategory == Type::Category::Struct, "");
3348 			auto const& fromStructType = dynamic_cast<StructType const &>(_from);
3349 			auto const& toStructType = dynamic_cast<StructType const &>(_to);
3350 			solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), "");
3351 
3352 			if (fromStructType.location() == toStructType.location() && toStructType.isPointer())
3353 				body = "converted := value";
3354 			else
3355 			{
3356 				solUnimplementedAssert(toStructType.location() == DataLocation::Memory);
3357 				solUnimplementedAssert(fromStructType.location() != DataLocation::Memory);
3358 
3359 				if (fromStructType.location() == DataLocation::CallData)
3360 					body = Whiskers(R"(
3361 						converted := <abiDecode>(value, calldatasize())
3362 					)")
3363 					(
3364 						"abiDecode",
3365 						ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).abiDecodingFunctionStruct(
3366 							toStructType,
3367 							false
3368 						)
3369 					).render();
3370 				else
3371 				{
3372 					solAssert(fromStructType.location() == DataLocation::Storage, "");
3373 
3374 					body = Whiskers(R"(
3375 						converted := <readFromStorage>(value)
3376 					)")
3377 					("readFromStorage", readFromStorage(toStructType, 0, true))
3378 					.render();
3379 				}
3380 			}
3381 
3382 			break;
3383 		}
3384 		case Type::Category::FixedBytes:
3385 		{
3386 			FixedBytesType const& from = dynamic_cast<FixedBytesType const&>(_from);
3387 			if (toCategory == Type::Category::Integer)
3388 				body =
3389 					Whiskers("converted := <convert>(<shift>(value))")
3390 					("shift", shiftRightFunction(256 - from.numBytes() * 8))
3391 					("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to))
3392 					.render();
3393 			else if (toCategory == Type::Category::Address)
3394 				body =
3395 					Whiskers("converted := <convert>(value)")
3396 						("convert", conversionFunction(_from, IntegerType(160)))
3397 						.render();
3398 			else
3399 			{
3400 				// clear for conversion to longer bytes
3401 				solAssert(toCategory == Type::Category::FixedBytes, "Invalid type conversion requested.");
3402 				body =
3403 					Whiskers("converted := <clean>(value)")
3404 					("clean", cleanupFunction(from))
3405 					.render();
3406 			}
3407 			break;
3408 		}
3409 		case Type::Category::Function:
3410 		{
3411 			solAssert(false, "Conversion should not be called for function types.");
3412 			break;
3413 		}
3414 		case Type::Category::Enum:
3415 		{
3416 			solAssert(toCategory == Type::Category::Integer || _from == _to, "");
3417 			EnumType const& enumType = dynamic_cast<decltype(enumType)>(_from);
3418 			body =
3419 				Whiskers("converted := <clean>(value)")
3420 				("clean", cleanupFunction(enumType))
3421 				.render();
3422 			break;
3423 		}
3424 		case Type::Category::Tuple:
3425 		{
3426 			solUnimplemented("Tuple conversion not implemented.");
3427 			break;
3428 		}
3429 		case Type::Category::TypeType:
3430 		{
3431 			TypeType const& typeType = dynamic_cast<decltype(typeType)>(_from);
3432 			if (
3433 				auto const* contractType = dynamic_cast<ContractType const*>(typeType.actualType());
3434 				contractType->contractDefinition().isLibrary() &&
3435 				_to == *TypeProvider::address()
3436 			)
3437 				body = "converted := value";
3438 			else
3439 				solAssert(false, "Invalid conversion from " + _from.canonicalName() + " to " + _to.canonicalName());
3440 			break;
3441 		}
3442 		case Type::Category::Mapping:
3443 		{
3444 			solAssert(_from == _to, "");
3445 			body = "converted := value";
3446 			break;
3447 		}
3448 		default:
3449 			solAssert(false, "Invalid conversion from " + _from.canonicalName() + " to " + _to.canonicalName());
3450 		}
3451 
3452 		solAssert(!body.empty(), _from.canonicalName() + " to " + _to.canonicalName());
3453 		templ("body", body);
3454 		return templ.render();
3455 	});
3456 }
3457 
bytesToFixedBytesConversionFunction(ArrayType const & _from,FixedBytesType const & _to)3458 string YulUtilFunctions::bytesToFixedBytesConversionFunction(ArrayType const& _from, FixedBytesType const& _to)
3459 {
3460 	solAssert(_from.isByteArray() && !_from.isString(), "");
3461 	solAssert(_from.isDynamicallySized(), "");
3462 	string functionName = "convert_bytes_to_fixedbytes_from_" + _from.identifier() + "_to_" + _to.identifier();
3463 	return m_functionCollector.createFunction(functionName, [&](auto& _args, auto& _returnParams) {
3464 		_args = { "array" };
3465 		bool fromCalldata = _from.dataStoredIn(DataLocation::CallData);
3466 		if (fromCalldata)
3467 			_args.emplace_back("len");
3468 		_returnParams = {"value"};
3469 		Whiskers templ(R"(
3470 			let length := <arrayLen>(array<?fromCalldata>, len</fromCalldata>)
3471 			let dataArea := array
3472 			<?fromMemory>
3473 				dataArea := <dataArea>(array)
3474 			</fromMemory>
3475 			<?fromStorage>
3476 				if gt(length, 31) { dataArea := <dataArea>(array) }
3477 			</fromStorage>
3478 
3479 			<?fromCalldata>
3480 				value := <cleanup>(calldataload(dataArea))
3481 			<!fromCalldata>
3482 				value := <extractValue>(dataArea)
3483 			</fromCalldata>
3484 
3485 			if lt(length, <fixedBytesLen>) {
3486 				value := and(
3487 					value,
3488 					<shl>(
3489 						mul(8, sub(<fixedBytesLen>, length)),
3490 						<mask>
3491 					)
3492 				)
3493 			}
3494 		)");
3495 		templ("fromCalldata", fromCalldata);
3496 		templ("arrayLen", arrayLengthFunction(_from));
3497 		templ("fixedBytesLen", to_string(_to.numBytes()));
3498 		templ("fromMemory", _from.dataStoredIn(DataLocation::Memory));
3499 		templ("fromStorage", _from.dataStoredIn(DataLocation::Storage));
3500 		templ("dataArea", arrayDataAreaFunction(_from));
3501 		if (fromCalldata)
3502 			templ("cleanup", cleanupFunction(_to));
3503 		else
3504 			templ(
3505 				"extractValue",
3506 				_from.dataStoredIn(DataLocation::Storage) ?
3507 				readFromStorage(_to, 32 - _to.numBytes(), false) :
3508 				readFromMemory(_to)
3509 			);
3510 		templ("shl", shiftLeftFunctionDynamic());
3511 		templ("mask", formatNumber(~((u256(1) << (256 - _to.numBytes() * 8)) - 1)));
3512 		return templ.render();
3513 	});
3514 }
3515 
copyStructToStorageFunction(StructType const & _from,StructType const & _to)3516 string YulUtilFunctions::copyStructToStorageFunction(StructType const& _from, StructType const& _to)
3517 {
3518 	solAssert(_to.dataStoredIn(DataLocation::Storage), "");
3519 	solAssert(_from.structDefinition() == _to.structDefinition(), "");
3520 
3521 	string functionName =
3522 		"copy_struct_to_storage_from_" +
3523 		_from.identifier() +
3524 		"_to_" +
3525 		_to.identifier();
3526 
3527 	return m_functionCollector.createFunction(functionName, [&](auto& _arguments, auto&) {
3528 		_arguments = {"slot", "value"};
3529 		Whiskers templ(R"(
3530 			<?fromStorage> if iszero(eq(slot, value)) { </fromStorage>
3531 			<#member>
3532 			{
3533 				<updateMemberCall>
3534 			}
3535 			</member>
3536 			<?fromStorage> } </fromStorage>
3537 		)");
3538 		templ("fromStorage", _from.dataStoredIn(DataLocation::Storage));
3539 
3540 		MemberList::MemberMap structMembers = _from.nativeMembers(nullptr);
3541 		MemberList::MemberMap toStructMembers = _to.nativeMembers(nullptr);
3542 
3543 		vector<map<string, string>> memberParams(structMembers.size());
3544 		for (size_t i = 0; i < structMembers.size(); ++i)
3545 		{
3546 			Type const& memberType = *structMembers[i].type;
3547 			solAssert(memberType.memoryHeadSize() == 32, "");
3548 			auto const&[slotDiff, offset] = _to.storageOffsetsOfMember(structMembers[i].name);
3549 
3550 			Whiskers t(R"(
3551 				let memberSlot := add(slot, <memberStorageSlotDiff>)
3552 				let memberSrcPtr := add(value, <memberOffset>)
3553 
3554 				<?fromCalldata>
3555 					let <memberValues> :=
3556 						<?dynamicallyEncodedMember>
3557 							<accessCalldataTail>(value, memberSrcPtr)
3558 						<!dynamicallyEncodedMember>
3559 							memberSrcPtr
3560 						</dynamicallyEncodedMember>
3561 
3562 					<?isValueType>
3563 						<memberValues> := <read>(<memberValues>)
3564 					</isValueType>
3565 				</fromCalldata>
3566 
3567 				<?fromMemory>
3568 					let <memberValues> := <read>(memberSrcPtr)
3569 				</fromMemory>
3570 
3571 				<?fromStorage>
3572 					let <memberValues> :=
3573 						<?isValueType>
3574 							<read>(memberSrcPtr)
3575 						<!isValueType>
3576 							memberSrcPtr
3577 						</isValueType>
3578 				</fromStorage>
3579 
3580 				<updateStorageValue>(memberSlot, <memberValues>)
3581 			)");
3582 			bool fromCalldata = _from.location() == DataLocation::CallData;
3583 			t("fromCalldata", fromCalldata);
3584 			bool fromMemory = _from.location() == DataLocation::Memory;
3585 			t("fromMemory", fromMemory);
3586 			bool fromStorage = _from.location() == DataLocation::Storage;
3587 			t("fromStorage", fromStorage);
3588 			t("isValueType", memberType.isValueType());
3589 			t("memberValues", suffixedVariableNameList("memberValue_", 0, memberType.stackItems().size()));
3590 
3591 			t("memberStorageSlotDiff", slotDiff.str());
3592 			if (fromCalldata)
3593 			{
3594 				t("memberOffset", to_string(_from.calldataOffsetOfMember(structMembers[i].name)));
3595 				t("dynamicallyEncodedMember", memberType.isDynamicallyEncoded());
3596 				if (memberType.isDynamicallyEncoded())
3597 					t("accessCalldataTail", accessCalldataTailFunction(memberType));
3598 				if (memberType.isValueType())
3599 					t("read", readFromCalldata(memberType));
3600 			}
3601 			else if (fromMemory)
3602 			{
3603 				t("memberOffset", _from.memoryOffsetOfMember(structMembers[i].name).str());
3604 				t("read", readFromMemory(memberType));
3605 			}
3606 			else if (fromStorage)
3607 			{
3608 				auto const& [srcSlotOffset, srcOffset] = _from.storageOffsetsOfMember(structMembers[i].name);
3609 				t("memberOffset", formatNumber(srcSlotOffset));
3610 				if (memberType.isValueType())
3611 					t("read", readFromStorageValueType(memberType, srcOffset, false));
3612 				else
3613 					solAssert(srcOffset == 0, "");
3614 
3615 			}
3616 			t("updateStorageValue", updateStorageValueFunction(
3617 				memberType,
3618 				*toStructMembers[i].type,
3619 				optional<unsigned>{offset}
3620 			));
3621 			memberParams[i]["updateMemberCall"] = t.render();
3622 		}
3623 		templ("member", memberParams);
3624 
3625 		return templ.render();
3626 	});
3627 }
3628 
arrayConversionFunction(ArrayType const & _from,ArrayType const & _to)3629 string YulUtilFunctions::arrayConversionFunction(ArrayType const& _from, ArrayType const& _to)
3630 {
3631 	if (_to.dataStoredIn(DataLocation::CallData))
3632 		solAssert(
3633 			_from.dataStoredIn(DataLocation::CallData) && _from.isByteArray() && _to.isByteArray(),
3634 			""
3635 		);
3636 
3637 	// Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
3638 	if (_to.location() == DataLocation::Storage)
3639 		solAssert(
3640 			(_to.isPointer() || (_from.isByteArray() && _to.isByteArray())) &&
3641 			_from.location() == DataLocation::Storage,
3642 			"Invalid conversion to storage type."
3643 		);
3644 
3645 	string functionName =
3646 		"convert_array_" +
3647 		_from.identifier() +
3648 		"_to_" +
3649 		_to.identifier();
3650 
3651 	return m_functionCollector.createFunction(functionName, [&]() {
3652 		Whiskers templ(R"(
3653 			function <functionName>(value<?fromCalldataDynamic>, length</fromCalldataDynamic>) -> converted <?toCalldataDynamic>, outLength</toCalldataDynamic> {
3654 				<body>
3655 				<?toCalldataDynamic>
3656 					outLength := <length>
3657 				</toCalldataDynamic>
3658 			}
3659 		)");
3660 		templ("functionName", functionName);
3661 		templ("fromCalldataDynamic", _from.dataStoredIn(DataLocation::CallData) && _from.isDynamicallySized());
3662 		templ("toCalldataDynamic", _to.dataStoredIn(DataLocation::CallData) && _to.isDynamicallySized());
3663 		templ("length", _from.isDynamicallySized() ? "length" : _from.length().str());
3664 
3665 		if (
3666 			_from == _to ||
3667 			(_from.dataStoredIn(DataLocation::Memory) && _to.dataStoredIn(DataLocation::Memory)) ||
3668 			(_from.dataStoredIn(DataLocation::CallData) && _to.dataStoredIn(DataLocation::CallData)) ||
3669 			_to.dataStoredIn(DataLocation::Storage)
3670 		)
3671 			templ("body", "converted := value");
3672 		else if (_to.dataStoredIn(DataLocation::Memory))
3673 			templ(
3674 				"body",
3675 				Whiskers(R"(
3676 					// Copy the array to a free position in memory
3677 					converted :=
3678 					<?fromStorage>
3679 						<arrayStorageToMem>(value)
3680 					</fromStorage>
3681 					<?fromCalldata>
3682 						<abiDecode>(value, <length>, calldatasize())
3683 					</fromCalldata>
3684 				)")
3685 				("fromStorage", _from.dataStoredIn(DataLocation::Storage))
3686 				("fromCalldata", _from.dataStoredIn(DataLocation::CallData))
3687 				("length", _from.isDynamicallySized() ? "length" : _from.length().str())
3688 				(
3689 					"abiDecode",
3690 					_from.dataStoredIn(DataLocation::CallData) ?
3691 					ABIFunctions(
3692 						m_evmVersion,
3693 						m_revertStrings,
3694 						m_functionCollector
3695 					).abiDecodingFunctionArrayAvailableLength(_to, false) :
3696 					""
3697 				)
3698 				(
3699 					"arrayStorageToMem",
3700 					_from.dataStoredIn(DataLocation::Storage) ? copyArrayFromStorageToMemoryFunction(_from, _to) : ""
3701 				)
3702 				.render()
3703 			);
3704 		else
3705 			solAssert(false, "");
3706 
3707 		return templ.render();
3708 	});
3709 }
3710 
cleanupFunction(Type const & _type)3711 string YulUtilFunctions::cleanupFunction(Type const& _type)
3712 {
3713 	if (auto userDefinedValueType = dynamic_cast<UserDefinedValueType const*>(&_type))
3714 		return cleanupFunction(userDefinedValueType->underlyingType());
3715 
3716 	string functionName = string("cleanup_") + _type.identifier();
3717 	return m_functionCollector.createFunction(functionName, [&]() {
3718 		Whiskers templ(R"(
3719 			function <functionName>(value) -> cleaned {
3720 				<body>
3721 			}
3722 		)");
3723 		templ("functionName", functionName);
3724 		switch (_type.category())
3725 		{
3726 		case Type::Category::Address:
3727 			templ("body", "cleaned := " + cleanupFunction(IntegerType(160)) + "(value)");
3728 			break;
3729 		case Type::Category::Integer:
3730 		{
3731 			IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
3732 			if (type.numBits() == 256)
3733 				templ("body", "cleaned := value");
3734 			else if (type.isSigned())
3735 				templ("body", "cleaned := signextend(" + to_string(type.numBits() / 8 - 1) + ", value)");
3736 			else
3737 				templ("body", "cleaned := and(value, " + toCompactHexWithPrefix((u256(1) << type.numBits()) - 1) + ")");
3738 			break;
3739 		}
3740 		case Type::Category::RationalNumber:
3741 			templ("body", "cleaned := value");
3742 			break;
3743 		case Type::Category::Bool:
3744 			templ("body", "cleaned := iszero(iszero(value))");
3745 			break;
3746 		case Type::Category::FixedPoint:
3747 			solUnimplemented("Fixed point types not implemented.");
3748 			break;
3749 		case Type::Category::Function:
3750 			switch (dynamic_cast<FunctionType const&>(_type).kind())
3751 			{
3752 				case FunctionType::Kind::External:
3753 					templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)");
3754 					break;
3755 				case FunctionType::Kind::Internal:
3756 					templ("body", "cleaned := value");
3757 					break;
3758 				default:
3759 					solAssert(false, "");
3760 					break;
3761 			}
3762 			break;
3763 		case Type::Category::Array:
3764 		case Type::Category::Struct:
3765 		case Type::Category::Mapping:
3766 			solAssert(_type.dataStoredIn(DataLocation::Storage), "Cleanup requested for non-storage reference type.");
3767 			templ("body", "cleaned := value");
3768 			break;
3769 		case Type::Category::FixedBytes:
3770 		{
3771 			FixedBytesType const& type = dynamic_cast<FixedBytesType const&>(_type);
3772 			if (type.numBytes() == 32)
3773 				templ("body", "cleaned := value");
3774 			else if (type.numBytes() == 0)
3775 				// This is disallowed in the type system.
3776 				solAssert(false, "");
3777 			else
3778 			{
3779 				size_t numBits = type.numBytes() * 8;
3780 				u256 mask = ((u256(1) << numBits) - 1) << (256 - numBits);
3781 				templ("body", "cleaned := and(value, " + toCompactHexWithPrefix(mask) + ")");
3782 			}
3783 			break;
3784 		}
3785 		case Type::Category::Contract:
3786 		{
3787 			AddressType addressType(dynamic_cast<ContractType const&>(_type).isPayable() ?
3788 				StateMutability::Payable :
3789 				StateMutability::NonPayable
3790 			);
3791 			templ("body", "cleaned := " + cleanupFunction(addressType) + "(value)");
3792 			break;
3793 		}
3794 		case Type::Category::Enum:
3795 		{
3796 			// Out of range enums cannot be truncated unambigiously and therefore it should be an error.
3797 			templ("body", "cleaned := value " + validatorFunction(_type, false) + "(value)");
3798 			break;
3799 		}
3800 		case Type::Category::InaccessibleDynamic:
3801 			templ("body", "cleaned := 0");
3802 			break;
3803 		default:
3804 			solAssert(false, "Cleanup of type " + _type.identifier() + " requested.");
3805 		}
3806 
3807 		return templ.render();
3808 	});
3809 }
3810 
validatorFunction(Type const & _type,bool _revertOnFailure)3811 string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFailure)
3812 {
3813 	string functionName = string("validator_") + (_revertOnFailure ? "revert_" : "assert_") + _type.identifier();
3814 	return m_functionCollector.createFunction(functionName, [&]() {
3815 		Whiskers templ(R"(
3816 			function <functionName>(value) {
3817 				if iszero(<condition>) { <failure> }
3818 			}
3819 		)");
3820 		templ("functionName", functionName);
3821 		PanicCode panicCode = PanicCode::Generic;
3822 
3823 		switch (_type.category())
3824 		{
3825 		case Type::Category::Address:
3826 		case Type::Category::Integer:
3827 		case Type::Category::RationalNumber:
3828 		case Type::Category::Bool:
3829 		case Type::Category::FixedPoint:
3830 		case Type::Category::Function:
3831 		case Type::Category::Array:
3832 		case Type::Category::Struct:
3833 		case Type::Category::Mapping:
3834 		case Type::Category::FixedBytes:
3835 		case Type::Category::Contract:
3836 		case Type::Category::UserDefinedValueType:
3837 		{
3838 			templ("condition", "eq(value, " + cleanupFunction(_type) + "(value))");
3839 			break;
3840 		}
3841 		case Type::Category::Enum:
3842 		{
3843 			size_t members = dynamic_cast<EnumType const&>(_type).numberOfMembers();
3844 			solAssert(members > 0, "empty enum should have caused a parser error.");
3845 			panicCode = PanicCode::EnumConversionError;
3846 			templ("condition", "lt(value, " + to_string(members) + ")");
3847 			break;
3848 		}
3849 		case Type::Category::InaccessibleDynamic:
3850 			templ("condition", "1");
3851 			break;
3852 		default:
3853 			solAssert(false, "Validation of type " + _type.identifier() + " requested.");
3854 		}
3855 
3856 		if (_revertOnFailure)
3857 			templ("failure", "revert(0, 0)");
3858 		else
3859 			templ("failure", panicFunction(panicCode) + "()");
3860 
3861 		return templ.render();
3862 	});
3863 }
3864 
packedHashFunction(vector<Type const * > const & _givenTypes,vector<Type const * > const & _targetTypes)3865 string YulUtilFunctions::packedHashFunction(
3866 	vector<Type const*> const& _givenTypes,
3867 	vector<Type const*> const& _targetTypes
3868 )
3869 {
3870 	string functionName = string("packed_hashed_");
3871 	for (auto const& t: _givenTypes)
3872 		functionName += t->identifier() + "_";
3873 	functionName += "_to_";
3874 	for (auto const& t: _targetTypes)
3875 		functionName += t->identifier() + "_";
3876 	size_t sizeOnStack = 0;
3877 	for (Type const* t: _givenTypes)
3878 		sizeOnStack += t->sizeOnStack();
3879 	return m_functionCollector.createFunction(functionName, [&]() {
3880 		Whiskers templ(R"(
3881 			function <functionName>(<variables>) -> hash {
3882 				let pos := <allocateUnbounded>()
3883 				let end := <packedEncode>(pos <comma> <variables>)
3884 				hash := keccak256(pos, sub(end, pos))
3885 			}
3886 		)");
3887 		templ("functionName", functionName);
3888 		templ("variables", suffixedVariableNameList("var_", 1, 1 + sizeOnStack));
3889 		templ("comma", sizeOnStack > 0 ? "," : "");
3890 		templ("allocateUnbounded", allocateUnboundedFunction());
3891 		templ("packedEncode", ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).tupleEncoderPacked(_givenTypes, _targetTypes));
3892 		return templ.render();
3893 	});
3894 }
3895 
forwardingRevertFunction()3896 string YulUtilFunctions::forwardingRevertFunction()
3897 {
3898 	bool forward = m_evmVersion.supportsReturndata();
3899 	string functionName = "revert_forward_" + to_string(forward);
3900 	return m_functionCollector.createFunction(functionName, [&]() {
3901 		if (forward)
3902 			return Whiskers(R"(
3903 				function <functionName>() {
3904 					let pos := <allocateUnbounded>()
3905 					returndatacopy(pos, 0, returndatasize())
3906 					revert(pos, returndatasize())
3907 				}
3908 			)")
3909 			("functionName", functionName)
3910 			("allocateUnbounded", allocateUnboundedFunction())
3911 			.render();
3912 		else
3913 			return Whiskers(R"(
3914 				function <functionName>() {
3915 					revert(0, 0)
3916 				}
3917 			)")
3918 			("functionName", functionName)
3919 			.render();
3920 	});
3921 }
3922 
decrementCheckedFunction(Type const & _type)3923 std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type)
3924 {
3925 	solAssert(_type.category() == Type::Category::Integer, "");
3926 	IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
3927 
3928 	string const functionName = "decrement_" + _type.identifier();
3929 
3930 	return m_functionCollector.createFunction(functionName, [&]() {
3931 		return Whiskers(R"(
3932 			function <functionName>(value) -> ret {
3933 				value := <cleanupFunction>(value)
3934 				if eq(value, <minval>) { <panic>() }
3935 				ret := sub(value, 1)
3936 			}
3937 		)")
3938 		("functionName", functionName)
3939 		("panic", panicFunction(PanicCode::UnderOverflow))
3940 		("minval", toCompactHexWithPrefix(type.min()))
3941 		("cleanupFunction", cleanupFunction(_type))
3942 		.render();
3943 	});
3944 }
3945 
decrementWrappingFunction(Type const & _type)3946 std::string YulUtilFunctions::decrementWrappingFunction(Type const& _type)
3947 {
3948 	solAssert(_type.category() == Type::Category::Integer, "");
3949 	IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
3950 
3951 	string const functionName = "decrement_wrapping_" + _type.identifier();
3952 
3953 	return m_functionCollector.createFunction(functionName, [&]() {
3954 		return Whiskers(R"(
3955 			function <functionName>(value) -> ret {
3956 				ret := <cleanupFunction>(sub(value, 1))
3957 			}
3958 		)")
3959 		("functionName", functionName)
3960 		("cleanupFunction", cleanupFunction(type))
3961 		.render();
3962 	});
3963 }
3964 
incrementCheckedFunction(Type const & _type)3965 std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type)
3966 {
3967 	solAssert(_type.category() == Type::Category::Integer, "");
3968 	IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
3969 
3970 	string const functionName = "increment_" + _type.identifier();
3971 
3972 	return m_functionCollector.createFunction(functionName, [&]() {
3973 		return Whiskers(R"(
3974 			function <functionName>(value) -> ret {
3975 				value := <cleanupFunction>(value)
3976 				if eq(value, <maxval>) { <panic>() }
3977 				ret := add(value, 1)
3978 			}
3979 		)")
3980 		("functionName", functionName)
3981 		("maxval", toCompactHexWithPrefix(type.max()))
3982 		("panic", panicFunction(PanicCode::UnderOverflow))
3983 		("cleanupFunction", cleanupFunction(_type))
3984 		.render();
3985 	});
3986 }
3987 
incrementWrappingFunction(Type const & _type)3988 std::string YulUtilFunctions::incrementWrappingFunction(Type const& _type)
3989 {
3990 	solAssert(_type.category() == Type::Category::Integer, "");
3991 	IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
3992 
3993 	string const functionName = "increment_wrapping_" + _type.identifier();
3994 
3995 	return m_functionCollector.createFunction(functionName, [&]() {
3996 		return Whiskers(R"(
3997 			function <functionName>(value) -> ret {
3998 				ret := <cleanupFunction>(add(value, 1))
3999 			}
4000 		)")
4001 		("functionName", functionName)
4002 		("cleanupFunction", cleanupFunction(type))
4003 		.render();
4004 	});
4005 }
4006 
negateNumberCheckedFunction(Type const & _type)4007 string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type)
4008 {
4009 	solAssert(_type.category() == Type::Category::Integer, "");
4010 	IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
4011 	solAssert(type.isSigned(), "Expected signed type!");
4012 
4013 	string const functionName = "negate_" + _type.identifier();
4014 	return m_functionCollector.createFunction(functionName, [&]() {
4015 		return Whiskers(R"(
4016 			function <functionName>(value) -> ret {
4017 				value := <cleanupFunction>(value)
4018 				if eq(value, <minval>) { <panic>() }
4019 				ret := sub(0, value)
4020 			}
4021 		)")
4022 		("functionName", functionName)
4023 		("minval", toCompactHexWithPrefix(type.min()))
4024 		("cleanupFunction", cleanupFunction(_type))
4025 		("panic", panicFunction(PanicCode::UnderOverflow))
4026 		.render();
4027 	});
4028 }
4029 
negateNumberWrappingFunction(Type const & _type)4030 string YulUtilFunctions::negateNumberWrappingFunction(Type const& _type)
4031 {
4032 	solAssert(_type.category() == Type::Category::Integer, "");
4033 	IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
4034 	solAssert(type.isSigned(), "Expected signed type!");
4035 
4036 	string const functionName = "negate_wrapping_" + _type.identifier();
4037 	return m_functionCollector.createFunction(functionName, [&]() {
4038 		return Whiskers(R"(
4039 			function <functionName>(value) -> ret {
4040 				ret := <cleanupFunction>(sub(0, value))
4041 			}
4042 		)")
4043 		("functionName", functionName)
4044 		("cleanupFunction", cleanupFunction(type))
4045 		.render();
4046 	});
4047 }
4048 
zeroValueFunction(Type const & _type,bool _splitFunctionTypes)4049 string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctionTypes)
4050 {
4051 	solAssert(_type.category() != Type::Category::Mapping, "");
4052 
4053 	string const functionName = "zero_value_for_" + string(_splitFunctionTypes ? "split_" : "") + _type.identifier();
4054 
4055 	return m_functionCollector.createFunction(functionName, [&]() {
4056 		FunctionType const* fType = dynamic_cast<FunctionType const*>(&_type);
4057 		if (fType && fType->kind() == FunctionType::Kind::External && _splitFunctionTypes)
4058 			return Whiskers(R"(
4059 				function <functionName>() -> retAddress, retFunction {
4060 					retAddress := 0
4061 					retFunction := 0
4062 				}
4063 			)")
4064 			("functionName", functionName)
4065 			.render();
4066 
4067 		if (_type.dataStoredIn(DataLocation::CallData))
4068 		{
4069 			solAssert(
4070 				_type.category() == Type::Category::Struct ||
4071 				_type.category() == Type::Category::Array,
4072 			"");
4073 			Whiskers templ(R"(
4074 				function <functionName>() -> offset<?hasLength>, length</hasLength> {
4075 					offset := calldatasize()
4076 					<?hasLength> length := 0 </hasLength>
4077 				}
4078 			)");
4079 			templ("functionName", functionName);
4080 			templ("hasLength",
4081 				_type.category() == Type::Category::Array &&
4082 				dynamic_cast<ArrayType const&>(_type).isDynamicallySized()
4083 			);
4084 
4085 			return templ.render();
4086 		}
4087 
4088 		Whiskers templ(R"(
4089 			function <functionName>() -> ret {
4090 				ret := <zeroValue>
4091 			}
4092 		)");
4093 		templ("functionName", functionName);
4094 
4095 		if (_type.isValueType())
4096 		{
4097 			solAssert((
4098 				_type.hasSimpleZeroValueInMemory() ||
4099 				(fType && (fType->kind() == FunctionType::Kind::Internal || fType->kind() == FunctionType::Kind::External))
4100 			), "");
4101 			templ("zeroValue", "0");
4102 		}
4103 		else
4104 		{
4105 			solAssert(_type.dataStoredIn(DataLocation::Memory), "");
4106 			if (auto const* arrayType = dynamic_cast<ArrayType const*>(&_type))
4107 			{
4108 				if (_type.isDynamicallySized())
4109 					templ("zeroValue", to_string(CompilerUtils::zeroPointer));
4110 				else
4111 					templ("zeroValue", allocateAndInitializeMemoryArrayFunction(*arrayType) + "(" + to_string(unsigned(arrayType->length())) + ")");
4112 
4113 			}
4114 			else if (auto const* structType = dynamic_cast<StructType const*>(&_type))
4115 				templ("zeroValue", allocateAndInitializeMemoryStructFunction(*structType) + "()");
4116 			else
4117 				solUnimplemented("");
4118 		}
4119 
4120 		return templ.render();
4121 	});
4122 }
4123 
storageSetToZeroFunction(Type const & _type)4124 string YulUtilFunctions::storageSetToZeroFunction(Type const& _type)
4125 {
4126 	string const functionName = "storage_set_to_zero_" + _type.identifier();
4127 
4128 	return m_functionCollector.createFunction(functionName, [&]() {
4129 		if (_type.isValueType())
4130 			return Whiskers(R"(
4131 				function <functionName>(slot, offset) {
4132 					let <values> := <zeroValue>()
4133 					<store>(slot, offset, <values>)
4134 				}
4135 			)")
4136 			("functionName", functionName)
4137 			("store", updateStorageValueFunction(_type, _type))
4138 			("values", suffixedVariableNameList("zero_", 0, _type.sizeOnStack()))
4139 			("zeroValue", zeroValueFunction(_type))
4140 			.render();
4141 		else if (_type.category() == Type::Category::Array)
4142 			return Whiskers(R"(
4143 				function <functionName>(slot, offset) {
4144 					if iszero(eq(offset, 0)) { <panic>() }
4145 					<clearArray>(slot)
4146 				}
4147 			)")
4148 			("functionName", functionName)
4149 			("clearArray", clearStorageArrayFunction(dynamic_cast<ArrayType const&>(_type)))
4150 			("panic", panicFunction(PanicCode::Generic))
4151 			.render();
4152 		else if (_type.category() == Type::Category::Struct)
4153 			return Whiskers(R"(
4154 				function <functionName>(slot, offset) {
4155 					if iszero(eq(offset, 0)) { <panic>() }
4156 					<clearStruct>(slot)
4157 				}
4158 			)")
4159 			("functionName", functionName)
4160 			("clearStruct", clearStorageStructFunction(dynamic_cast<StructType const&>(_type)))
4161 			("panic", panicFunction(PanicCode::Generic))
4162 			.render();
4163 		else
4164 			solUnimplemented("setToZero for type " + _type.identifier() + " not yet implemented!");
4165 	});
4166 }
4167 
conversionFunctionSpecial(Type const & _from,Type const & _to)4168 string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const& _to)
4169 {
4170 	string functionName =
4171 		"convert_" +
4172 		_from.identifier() +
4173 		"_to_" +
4174 		_to.identifier();
4175 	return m_functionCollector.createFunction(functionName, [&]() {
4176 		if (
4177 			auto fromTuple = dynamic_cast<TupleType const*>(&_from), toTuple = dynamic_cast<TupleType const*>(&_to);
4178 			fromTuple && toTuple && fromTuple->components().size() == toTuple->components().size()
4179 		)
4180 		{
4181 			size_t sourceStackSize = 0;
4182 			size_t destStackSize = 0;
4183 			std::string conversions;
4184 			for (size_t i = 0; i < fromTuple->components().size(); ++i)
4185 			{
4186 				auto fromComponent = fromTuple->components()[i];
4187 				auto toComponent = toTuple->components()[i];
4188 				solAssert(fromComponent, "");
4189 				if (toComponent)
4190 				{
4191 					conversions +=
4192 						suffixedVariableNameList("converted", destStackSize, destStackSize + toComponent->sizeOnStack()) +
4193 						(toComponent->sizeOnStack() > 0 ? " := " : "") +
4194 						conversionFunction(*fromComponent, *toComponent) +
4195 						"(" +
4196 						suffixedVariableNameList("value", sourceStackSize, sourceStackSize + fromComponent->sizeOnStack()) +
4197 						")\n";
4198 					destStackSize += toComponent->sizeOnStack();
4199 				}
4200 				sourceStackSize += fromComponent->sizeOnStack();
4201 			}
4202 			return Whiskers(R"(
4203 				function <functionName>(<values>) <arrow> <converted> {
4204 					<conversions>
4205 				}
4206 			)")
4207 			("functionName", functionName)
4208 			("values", suffixedVariableNameList("value", 0, sourceStackSize))
4209 			("arrow", destStackSize > 0 ? "->" : "")
4210 			("converted", suffixedVariableNameList("converted", 0, destStackSize))
4211 			("conversions", conversions)
4212 			.render();
4213 		}
4214 
4215 		solUnimplementedAssert(
4216 			_from.category() == Type::Category::StringLiteral,
4217 			"Type conversion " + _from.toString() + " -> " + _to.toString() + " not yet implemented."
4218 		);
4219 		string const& data = dynamic_cast<StringLiteralType const&>(_from).value();
4220 		if (_to.category() == Type::Category::FixedBytes)
4221 		{
4222 			unsigned const numBytes = dynamic_cast<FixedBytesType const&>(_to).numBytes();
4223 			solAssert(data.size() <= 32, "");
4224 			Whiskers templ(R"(
4225 				function <functionName>() -> converted {
4226 					converted := <data>
4227 				}
4228 			)");
4229 			templ("functionName", functionName);
4230 			templ("data", formatNumber(
4231 				h256::Arith(h256(data, h256::AlignLeft)) &
4232 				(~(u256(-1) >> (8 * numBytes)))
4233 			));
4234 			return templ.render();
4235 		}
4236 		else if (_to.category() == Type::Category::Array)
4237 		{
4238 			solAssert(dynamic_cast<ArrayType const&>(_to).isByteArray(), "");
4239 			Whiskers templ(R"(
4240 				function <functionName>() -> converted {
4241 					converted := <copyLiteralToMemory>()
4242 				}
4243 			)");
4244 			templ("functionName", functionName);
4245 			templ("copyLiteralToMemory", copyLiteralToMemoryFunction(data));
4246 			return templ.render();
4247 		}
4248 		else
4249 			solAssert(
4250 				false,
4251 				"Invalid conversion from string literal to " + _to.toString() + " requested."
4252 			);
4253 	});
4254 }
4255 
readFromMemoryOrCalldata(Type const & _type,bool _fromCalldata)4256 string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromCalldata)
4257 {
4258 	string functionName =
4259 		string("read_from_") +
4260 		(_fromCalldata ? "calldata" : "memory") +
4261 		_type.identifier();
4262 
4263 	// TODO use ABI functions for handling calldata
4264 	if (_fromCalldata)
4265 		solAssert(!_type.isDynamicallyEncoded(), "");
4266 
4267 	return m_functionCollector.createFunction(functionName, [&] {
4268 		if (auto refType = dynamic_cast<ReferenceType const*>(&_type))
4269 		{
4270 			solAssert(refType->sizeOnStack() == 1, "");
4271 			solAssert(!_fromCalldata, "");
4272 
4273 			return Whiskers(R"(
4274 				function <functionName>(memPtr) -> value {
4275 					value := mload(memPtr)
4276 				}
4277 			)")
4278 			("functionName", functionName)
4279 			.render();
4280 		}
4281 
4282 		solAssert(_type.isValueType(), "");
4283 		Whiskers templ(R"(
4284 			function <functionName>(ptr) -> <returnVariables> {
4285 				<?fromCalldata>
4286 					let value := calldataload(ptr)
4287 					<validate>(value)
4288 				<!fromCalldata>
4289 					let value := <cleanup>(mload(ptr))
4290 				</fromCalldata>
4291 
4292 				<returnVariables> :=
4293 				<?externalFunction>
4294 					<splitFunction>(value)
4295 				<!externalFunction>
4296 					value
4297 				</externalFunction>
4298 			}
4299 		)");
4300 		templ("functionName", functionName);
4301 		templ("fromCalldata", _fromCalldata);
4302 		if (_fromCalldata)
4303 			templ("validate", validatorFunction(_type, true));
4304 		auto const* funType = dynamic_cast<FunctionType const*>(&_type);
4305 		if (funType && funType->kind() == FunctionType::Kind::External)
4306 		{
4307 			templ("externalFunction", true);
4308 			templ("splitFunction", splitExternalFunctionIdFunction());
4309 			templ("returnVariables", "addr, selector");
4310 		}
4311 		else
4312 		{
4313 			templ("externalFunction", false);
4314 			templ("returnVariables", "returnValue");
4315 		}
4316 
4317 		// Byte array elements generally need cleanup.
4318 		// Other types are cleaned as well to account for dirty memory e.g. due to inline assembly.
4319 		templ("cleanup", cleanupFunction(_type));
4320 		return templ.render();
4321 	});
4322 }
4323 
revertReasonIfDebugFunction(string const & _message)4324 string YulUtilFunctions::revertReasonIfDebugFunction(string const& _message)
4325 {
4326 	string functionName = "revert_error_" + util::toHex(util::keccak256(_message).asBytes());
4327 	return m_functionCollector.createFunction(functionName, [&](auto&, auto&) -> string {
4328 		return revertReasonIfDebugBody(m_revertStrings, allocateUnboundedFunction() + "()", _message);
4329 	});
4330 }
4331 
revertReasonIfDebugBody(RevertStrings _revertStrings,string const & _allocation,string const & _message)4332 string YulUtilFunctions::revertReasonIfDebugBody(
4333 	RevertStrings _revertStrings,
4334 	string const& _allocation,
4335 	string const& _message
4336 )
4337 {
4338 	if (_revertStrings < RevertStrings::Debug || _message.empty())
4339 		return "revert(0, 0)";
4340 
4341 	Whiskers templ(R"(
4342 		let start := <allocate>
4343 		let pos := start
4344 		mstore(pos, <sig>)
4345 		pos := add(pos, 4)
4346 		mstore(pos, 0x20)
4347 		pos := add(pos, 0x20)
4348 		mstore(pos, <length>)
4349 		pos := add(pos, 0x20)
4350 		<#word>
4351 			mstore(add(pos, <offset>), <wordValue>)
4352 		</word>
4353 		revert(start, <overallLength>)
4354 	)");
4355 	templ("allocate", _allocation);
4356 	templ("sig", util::selectorFromSignature("Error(string)").str());
4357 	templ("length", to_string(_message.length()));
4358 
4359 	size_t words = (_message.length() + 31) / 32;
4360 	vector<map<string, string>> wordParams(words);
4361 	for (size_t i = 0; i < words; ++i)
4362 	{
4363 		wordParams[i]["offset"] = to_string(i * 32);
4364 		wordParams[i]["wordValue"] = formatAsStringOrNumber(_message.substr(32 * i, 32));
4365 	}
4366 	templ("word", wordParams);
4367 	templ("overallLength", to_string(4 + 0x20 + 0x20 + words * 32));
4368 
4369 	return templ.render();
4370 }
4371 
panicFunction(util::PanicCode _code)4372 string YulUtilFunctions::panicFunction(util::PanicCode _code)
4373 {
4374 	string functionName = "panic_error_" + toCompactHexWithPrefix(uint64_t(_code));
4375 	return m_functionCollector.createFunction(functionName, [&]() {
4376 		return Whiskers(R"(
4377 			function <functionName>() {
4378 				mstore(0, <selector>)
4379 				mstore(4, <code>)
4380 				revert(0, 0x24)
4381 			}
4382 		)")
4383 		("functionName", functionName)
4384 		("selector", util::selectorFromSignature("Panic(uint256)").str())
4385 		("code", toCompactHexWithPrefix(static_cast<unsigned>(_code)))
4386 		.render();
4387 	});
4388 }
4389 
returnDataSelectorFunction()4390 string YulUtilFunctions::returnDataSelectorFunction()
4391 {
4392 	string const functionName = "return_data_selector";
4393 	solAssert(m_evmVersion.supportsReturndata(), "");
4394 
4395 	return m_functionCollector.createFunction(functionName, [&]() {
4396 		return util::Whiskers(R"(
4397 			function <functionName>() -> sig {
4398 				if gt(returndatasize(), 3) {
4399 					returndatacopy(0, 0, 4)
4400 					sig := <shr224>(mload(0))
4401 				}
4402 			}
4403 		)")
4404 		("functionName", functionName)
4405 		("shr224", shiftRightFunction(224))
4406 		.render();
4407 	});
4408 }
4409 
tryDecodeErrorMessageFunction()4410 string YulUtilFunctions::tryDecodeErrorMessageFunction()
4411 {
4412 	string const functionName = "try_decode_error_message";
4413 	solAssert(m_evmVersion.supportsReturndata(), "");
4414 
4415 	return m_functionCollector.createFunction(functionName, [&]() {
4416 		return util::Whiskers(R"(
4417 			function <functionName>() -> ret {
4418 				if lt(returndatasize(), 0x44) { leave }
4419 
4420 				let data := <allocateUnbounded>()
4421 				returndatacopy(data, 4, sub(returndatasize(), 4))
4422 
4423 				let offset := mload(data)
4424 				if or(
4425 					gt(offset, 0xffffffffffffffff),
4426 					gt(add(offset, 0x24), returndatasize())
4427 				) {
4428 					leave
4429 				}
4430 
4431 				let msg := add(data, offset)
4432 				let length := mload(msg)
4433 				if gt(length, 0xffffffffffffffff) { leave }
4434 
4435 				let end := add(add(msg, 0x20), length)
4436 				if gt(end, add(data, sub(returndatasize(), 4))) { leave }
4437 
4438 				<finalizeAllocation>(data, add(offset, add(0x20, length)))
4439 				ret := msg
4440 			}
4441 		)")
4442 		("functionName", functionName)
4443 		("allocateUnbounded", allocateUnboundedFunction())
4444 		("finalizeAllocation", finalizeAllocationFunction())
4445 		.render();
4446 	});
4447 }
4448 
tryDecodePanicDataFunction()4449 string YulUtilFunctions::tryDecodePanicDataFunction()
4450 {
4451 	string const functionName = "try_decode_panic_data";
4452 	solAssert(m_evmVersion.supportsReturndata(), "");
4453 
4454 	return m_functionCollector.createFunction(functionName, [&]() {
4455 		return util::Whiskers(R"(
4456 			function <functionName>() -> success, data {
4457 				if gt(returndatasize(), 0x23) {
4458 					returndatacopy(0, 4, 0x20)
4459 					success := 1
4460 					data := mload(0)
4461 				}
4462 			}
4463 		)")
4464 		("functionName", functionName)
4465 		.render();
4466 	});
4467 }
4468 
extractReturndataFunction()4469 string YulUtilFunctions::extractReturndataFunction()
4470 {
4471 	string const functionName = "extract_returndata";
4472 
4473 	return m_functionCollector.createFunction(functionName, [&]() {
4474 		return util::Whiskers(R"(
4475 			function <functionName>() -> data {
4476 				<?supportsReturndata>
4477 					switch returndatasize()
4478 					case 0 {
4479 						data := <emptyArray>()
4480 					}
4481 					default {
4482 						data := <allocateArray>(returndatasize())
4483 						returndatacopy(add(data, 0x20), 0, returndatasize())
4484 					}
4485 				<!supportsReturndata>
4486 					data := <emptyArray>()
4487 				</supportsReturndata>
4488 			}
4489 		)")
4490 		("functionName", functionName)
4491 		("supportsReturndata", m_evmVersion.supportsReturndata())
4492 		("allocateArray", allocateMemoryArrayFunction(*TypeProvider::bytesMemory()))
4493 		("emptyArray", zeroValueFunction(*TypeProvider::bytesMemory()))
4494 		.render();
4495 	});
4496 }
4497 
copyConstructorArgumentsToMemoryFunction(ContractDefinition const & _contract,string const & _creationObjectName)4498 string YulUtilFunctions::copyConstructorArgumentsToMemoryFunction(
4499 	ContractDefinition const& _contract,
4500 	string const& _creationObjectName
4501 )
4502 {
4503 	string functionName = "copy_arguments_for_constructor_" +
4504 		toString(_contract.constructor()->id()) +
4505 		"_object_" +
4506 		_contract.name() +
4507 		"_" +
4508 		toString(_contract.id());
4509 
4510 	return m_functionCollector.createFunction(functionName, [&]() {
4511 		string returnParams = suffixedVariableNameList("ret_param_",0, CompilerUtils::sizeOnStack(_contract.constructor()->parameters()));
4512 		ABIFunctions abiFunctions(m_evmVersion, m_revertStrings, m_functionCollector);
4513 
4514 		return util::Whiskers(R"(
4515 			function <functionName>() -> <retParams> {
4516 				let programSize := datasize("<object>")
4517 				let argSize := sub(codesize(), programSize)
4518 
4519 				let memoryDataOffset := <allocate>(argSize)
4520 				codecopy(memoryDataOffset, programSize, argSize)
4521 
4522 				<retParams> := <abiDecode>(memoryDataOffset, add(memoryDataOffset, argSize))
4523 			}
4524 		)")
4525 		("functionName", functionName)
4526 		("retParams", returnParams)
4527 		("object", _creationObjectName)
4528 		("allocate", allocationFunction())
4529 		("abiDecode", abiFunctions.tupleDecoder(FunctionType(*_contract.constructor()).parameterTypes(), true))
4530 		.render();
4531 	});
4532 }
4533 
externalCodeFunction()4534 string YulUtilFunctions::externalCodeFunction()
4535 {
4536 	string functionName = "external_code_at";
4537 
4538 	return m_functionCollector.createFunction(functionName, [&]() {
4539 		return util::Whiskers(R"(
4540 			function <functionName>(addr) -> mpos {
4541 				let length := extcodesize(addr)
4542 				mpos := <allocateArray>(length)
4543 				extcodecopy(addr, add(mpos, 0x20), 0, length)
4544 			}
4545 		)")
4546 		("functionName", functionName)
4547 		("allocateArray", allocateMemoryArrayFunction(*TypeProvider::bytesMemory()))
4548 		.render();
4549 	});
4550 }
4551