mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	[Sol->Yul] Adding cleanUpArrayEnd util function.
Co-authored-by: chriseth <chris@ethereum.org> Co-authored-by: Alex Beregszaszi <alex@rtfs.hu> Co-authored-by: Kamil Śliwak <kamil.sliwak@codepoets.it> Co-authored-by: Alex Beregszaszi <alex@rtfs.hu> Co-authored-by: Kamil Śliwak <kamil.sliwak@codepoets.it>
This commit is contained in:
		
							parent
							
								
									1d1175c292
								
							
						
					
					
						commit
						3e5f5fccf9
					
				| @ -1168,21 +1168,7 @@ std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type) | ||||
| 				</isDynamic> | ||||
| 
 | ||||
| 				<?needsClearing> | ||||
| 					// Size was reduced, clear end of array
 | ||||
| 					if lt(newLen, oldLen) { | ||||
| 						let oldSlotCount := <convertToSize>(oldLen) | ||||
| 						let newSlotCount := <convertToSize>(newLen) | ||||
| 						let arrayDataStart := <dataPosition>(array) | ||||
| 						let deleteStart := add(arrayDataStart, newSlotCount) | ||||
| 						let deleteEnd := add(arrayDataStart, oldSlotCount) | ||||
| 						<?packed> | ||||
| 							// if we are dealing with packed array and offset is greater than zero
 | ||||
| 							// we have  to partially clear last slot that is still used, so decreasing start by one
 | ||||
| 							let offset := mul(mod(newLen, <itemsPerSlot>), <storageBytes>) | ||||
| 							if gt(offset, 0) { <partialClearStorageSlot>(sub(deleteStart, 1), offset) } | ||||
| 						</packed> | ||||
| 						<clearStorageRange>(deleteStart, deleteEnd) | ||||
| 					} | ||||
| 					<cleanUpArrayEnd>(array, oldLen, newLen) | ||||
| 				</needsClearing> | ||||
| 			})"); | ||||
| 			templ("functionName", functionName); | ||||
| @ -1193,19 +1179,49 @@ std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type) | ||||
| 			bool isMappingBase = _type.baseType()->category() == Type::Category::Mapping; | ||||
| 			templ("needsClearing", !isMappingBase); | ||||
| 			if (!isMappingBase) | ||||
| 			{ | ||||
| 				templ("convertToSize", arrayConvertLengthToSize(_type)); | ||||
| 				templ("dataPosition", arrayDataAreaFunction(_type)); | ||||
| 				templ("clearStorageRange", clearStorageRangeFunction(*_type.baseType())); | ||||
| 				templ("packed", _type.baseType()->storageBytes() <= 16); | ||||
| 				templ("itemsPerSlot", to_string(32 / _type.baseType()->storageBytes())); | ||||
| 				templ("storageBytes", to_string(_type.baseType()->storageBytes())); | ||||
| 				templ("partialClearStorageSlot", partialClearStorageSlotFunction()); | ||||
| 			} | ||||
| 				templ("cleanUpArrayEnd", cleanUpStorageArrayEndFunction(_type)); | ||||
| 			return templ.render(); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| string YulUtilFunctions::cleanUpStorageArrayEndFunction(ArrayType const& _type) | ||||
| { | ||||
| 	solAssert(_type.location() == DataLocation::Storage, ""); | ||||
| 	solAssert(_type.baseType()->category() != Type::Category::Mapping, ""); | ||||
| 	solAssert(!_type.isByteArray(), ""); | ||||
| 	solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, ""); | ||||
| 
 | ||||
| 	string functionName = "cleanup_storage_array_end_" + _type.identifier(); | ||||
| 	return m_functionCollector.createFunction(functionName, [&](vector<string>& _args, vector<string>&) { | ||||
| 		_args = {"array", "len", "startIndex"}; | ||||
| 		return Whiskers(R"( | ||||
| 			if lt(startIndex, len) { | ||||
| 				// Size was reduced, clear end of array
 | ||||
| 				let oldSlotCount := <convertToSize>(len) | ||||
| 				let newSlotCount := <convertToSize>(startIndex) | ||||
| 				let arrayDataStart := <dataPosition>(array) | ||||
| 				let deleteStart := add(arrayDataStart, newSlotCount) | ||||
| 				let deleteEnd := add(arrayDataStart, oldSlotCount) | ||||
| 				<?packed> | ||||
| 					// if we are dealing with packed array and offset is greater than zero
 | ||||
| 					// we have  to partially clear last slot that is still used, so decreasing start by one
 | ||||
| 					let offset := mul(mod(startIndex, <itemsPerSlot>), <storageBytes>) | ||||
| 					if gt(offset, 0) { <partialClearStorageSlot>(sub(deleteStart, 1), offset) } | ||||
| 				</packed> | ||||
| 				<clearStorageRange>(deleteStart, deleteEnd) | ||||
| 			} | ||||
| 		)") | ||||
| 		("convertToSize", arrayConvertLengthToSize(_type)) | ||||
| 		("dataPosition", arrayDataAreaFunction(_type)) | ||||
| 		("clearStorageRange", clearStorageRangeFunction(*_type.baseType())) | ||||
| 		("packed", _type.baseType()->storageBytes() <= 16) | ||||
| 		("itemsPerSlot", to_string(32 / _type.baseType()->storageBytes())) | ||||
| 		("storageBytes", to_string(_type.baseType()->storageBytes())) | ||||
| 		("partialClearStorageSlot", partialClearStorageSlotFunction()) | ||||
| 		.render(); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| string YulUtilFunctions::resizeDynamicByteArrayFunction(ArrayType const& _type) | ||||
| { | ||||
| 	string functionName = "resize_array_" + _type.identifier(); | ||||
| @ -1230,6 +1246,30 @@ string YulUtilFunctions::resizeDynamicByteArrayFunction(ArrayType const& _type) | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| string YulUtilFunctions::cleanUpDynamicByteArrayEndSlotsFunction(ArrayType const& _type) | ||||
| { | ||||
| 	solAssert(_type.isByteArray(), ""); | ||||
| 	solAssert(_type.isDynamicallySized(), ""); | ||||
| 
 | ||||
| 	string functionName = "clean_up_bytearray_end_slots_" + _type.identifier(); | ||||
| 	return m_functionCollector.createFunction(functionName, [&](vector<string>& _args, vector<string>&) { | ||||
| 		_args = {"array", "len", "startIndex"}; | ||||
| 		return Whiskers(R"( | ||||
| 			if gt(len, 31) { | ||||
| 				let dataArea := <dataLocation>(array) | ||||
| 				let deleteStart := add(dataArea, div(<roundUp>(startIndex), 32)) | ||||
| 				// If we are clearing array to be short byte array, we want to clear only data starting from array data area.
 | ||||
| 				if lt(startIndex, 32) { deleteStart := dataArea } | ||||
| 				<clearStorageRange>(deleteStart, add(dataArea, div(add(len, 31), 32))) | ||||
| 			} | ||||
| 		)") | ||||
| 		("dataLocation", arrayDataAreaFunction(_type)) | ||||
| 		("roundUp", roundUpFunction()) | ||||
| 		("clearStorageRange", clearStorageRangeFunction(*_type.baseType())) | ||||
| 		.render(); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _type) | ||||
| { | ||||
| 	string functionName = "byte_array_decrease_size_" + _type.identifier(); | ||||
| @ -1805,28 +1845,19 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy | ||||
| 
 | ||||
| 				let oldLen := <byteArrayLength>(sload(slot)) | ||||
| 
 | ||||
| 				// potentially truncate data
 | ||||
| 				<cleanUpEndArray>(slot, oldLen, newLen) | ||||
| 
 | ||||
| 				let srcOffset := 0 | ||||
| 				<?fromMemory> | ||||
| 					srcOffset := 0x20 | ||||
| 				</fromMemory> | ||||
| 
 | ||||
| 				// This is not needed in all branches.
 | ||||
| 				let dstDataArea | ||||
| 				if or(gt(oldLen, 31), gt(newLen, 31)) { | ||||
| 					dstDataArea := <dstDataLocation>(slot) | ||||
| 				} | ||||
| 
 | ||||
| 				if gt(oldLen, 31) { | ||||
| 					// potentially truncate data
 | ||||
| 					let deleteStart := add(dstDataArea, div(add(newLen, 31), 32)) | ||||
| 					if lt(newLen, 32) { deleteStart := dstDataArea } | ||||
| 					<clearStorageRange>(deleteStart, add(dstDataArea, div(add(oldLen, 31), 32))) | ||||
| 				} | ||||
| 				switch gt(newLen, 31) | ||||
| 				case 1 { | ||||
| 					let loopEnd := and(newLen, not(0x1f)) | ||||
| 					<?fromStorage> src := <srcDataLocation>(src) </fromStorage> | ||||
| 					let dstPtr := dstDataArea | ||||
| 					let dstPtr := <dstDataLocation>(slot) | ||||
| 					let i := 0 | ||||
| 					for { } lt(i, loopEnd) { i := add(i, 0x20) } { | ||||
| 						sstore(dstPtr, <read>(add(src, srcOffset))) | ||||
| @ -1860,7 +1891,7 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy | ||||
| 		templ("dstDataLocation", arrayDataAreaFunction(_toType)); | ||||
| 		if (fromStorage) | ||||
| 			templ("srcDataLocation", arrayDataAreaFunction(_fromType)); | ||||
| 		templ("clearStorageRange", clearStorageRangeFunction(*_toType.baseType())); | ||||
| 		templ("cleanUpEndArray", cleanUpDynamicByteArrayEndSlotsFunction(_toType)); | ||||
| 		templ("srcIncrement", to_string(fromStorage ? 1 : 0x20)); | ||||
| 		templ("read", fromStorage ? "sload" : fromCalldata ? "calldataload" : "mload"); | ||||
| 		templ("maskBytes", maskBytesFunctionDynamic()); | ||||
|  | ||||
| @ -207,6 +207,11 @@ public: | ||||
| 	/// signature: (array, newLen)
 | ||||
| 	std::string resizeArrayFunction(ArrayType const& _type); | ||||
| 
 | ||||
| 	/// @returns the name of a function that zeroes all storage array elements from `startIndex` to `len` (excluding).
 | ||||
| 	/// Assumes that `len` is the array length. Does nothing if `startIndex >= len`. Does not modify the stored length.
 | ||||
| 	/// signature: (array, len, startIndex)
 | ||||
| 	std::string cleanUpStorageArrayEndFunction(ArrayType const& _type); | ||||
| 
 | ||||
| 	/// @returns the name of a function that reduces the size of a storage array by one element
 | ||||
| 	/// signature: (array)
 | ||||
| 	std::string storageArrayPopFunction(ArrayType const& _type); | ||||
| @ -543,6 +548,14 @@ private: | ||||
| 	/// signature: (array, newLen)
 | ||||
| 	std::string resizeDynamicByteArrayFunction(ArrayType const& _type); | ||||
| 
 | ||||
| 	/// @returns the name of a function that cleans up elements of a storage byte array starting from startIndex.
 | ||||
| 	/// It will not copy elements in case of transformation to short byte array, and will not change array length.
 | ||||
| 	/// In case of startIndex is greater than len, doesn't do anything.
 | ||||
| 	/// In case of short byte array (< 32 bytes) doesn't do anything.
 | ||||
| 	/// If the first slot to be cleaned up is partially occupied, does not touch it. Cleans up only completely unused slots.
 | ||||
| 	/// signature: (array, len, startIndex)
 | ||||
| 	std::string cleanUpDynamicByteArrayEndSlotsFunction(ArrayType const& _type); | ||||
| 
 | ||||
| 	/// @returns the name of a function that increases size of byte array
 | ||||
| 	/// when we resize byte array frextractUsedSetLenom < 32 elements to >= 32 elements or we push to byte array of size 31 copying of data will  occur
 | ||||
| 	/// signature: (array, data, oldLen, newLen)
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user