Copy value array from storage to storage.

This commit is contained in:
chriseth 2020-11-12 14:46:10 +01:00
parent f6ac6b738d
commit d9fb17a85e
5 changed files with 125 additions and 11 deletions

View File

@ -449,6 +449,36 @@ string YulUtilFunctions::maskBytesFunctionDynamic()
});
}
string YulUtilFunctions::maskLowerOrderBytesFunction(size_t _bytes)
{
string functionName = "mask_lower_order_bytes_" + to_string(_bytes);
solAssert(_bytes <= 32, "");
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(data) -> result {
result := and(data, <mask>)
})")
("functionName", functionName)
("mask", formatNumber((~u256(0)) >> (256 - 8 * _bytes)))
.render();
});
}
string YulUtilFunctions::maskLowerOrderBytesFunctionDynamic()
{
string functionName = "mask_lower_order_bytes_dynamic";
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(data, bytes) -> result {
let mask := not(<shl>(mul(8, bytes), not(0)))
result := and(data, mask)
})")
("functionName", functionName)
("shl", shiftLeftFunctionDynamic())
.render();
});
}
string YulUtilFunctions::roundUpFunction()
{
string functionName = "round_up_to_mul_of_32";
@ -1470,23 +1500,20 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
);
if (_fromType.isByteArray())
return copyByteArrayToStorageFunction(_fromType, _toType);
solUnimplementedAssert(!_fromType.dataStoredIn(DataLocation::Storage), "");
if (_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType())
return copyValueArrayStorageToStorageFunction(_fromType, _toType);
string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
return m_functionCollector.createFunction(functionName, [&](){
Whiskers templ(R"(
function <functionName>(slot, value<?isFromDynamicCalldata>, len</isFromDynamicCalldata>) {
<?fromStorage> if eq(slot, value) { leave } </fromStorage>
let length := <arrayLength>(value<?isFromDynamicCalldata>, len</isFromDynamicCalldata>)
<?isToDynamic>
<resizeArray>(slot, length)
</isToDynamic>
let srcPtr :=
<?isFromMemoryDynamic>
add(value, 0x20)
<!isFromMemoryDynamic>
value
</isFromMemoryDynamic>
let srcPtr := <srcDataLocation>(value)
let elementSlot := <dstDataLocation>(slot)
let elementOffset := 0
@ -1509,6 +1536,10 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
let <elementValues> := <readFromCalldataOrMemory>(srcPtr)
</fromMemory>
<?fromStorage>
let <elementValues> := srcPtr
</fromStorage>
<updateStorageValue>(elementSlot<?isValueType>, elementOffset</isValueType>, <elementValues>)
srcPtr := add(srcPtr, <stride>)
@ -1526,12 +1557,17 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
}
}
)");
if (_fromType.dataStoredIn(DataLocation::Storage))
solAssert(!_fromType.isValueType(), "");
templ("functionName", functionName);
bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData);
templ("isFromDynamicCalldata", _fromType.isDynamicallySized() && fromCalldata);
templ("fromMemory", _fromType.dataStoredIn(DataLocation::Memory));
templ("fromStorage", _fromType.dataStoredIn(DataLocation::Storage));
bool fromMemory = _fromType.dataStoredIn(DataLocation::Memory);
templ("fromMemory", fromMemory);
templ("fromCalldata", fromCalldata);
templ("isToDynamic", _toType.isDynamicallySized());
templ("srcDataLocation", arrayDataAreaFunction(_fromType));
templ("isFromMemoryDynamic", _fromType.isDynamicallySized() && _fromType.dataStoredIn(DataLocation::Memory));
if (fromCalldata)
{
@ -1545,7 +1581,7 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
templ("arrayLength",arrayLengthFunction(_fromType));
templ("isValueType", _fromType.baseType()->isValueType());
templ("dstDataLocation", arrayDataAreaFunction(_toType));
if (!fromCalldata || _fromType.baseType()->isValueType())
if (fromMemory || (fromCalldata && _fromType.baseType()->isValueType()))
templ("readFromCalldataOrMemory", readFromMemoryOrCalldata(*_fromType.baseType(), fromCalldata));
templ("elementValues", suffixedVariableNameList(
"elementValue_",
@ -1643,6 +1679,65 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy
});
}
string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
{
solAssert(
*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
""
);
solAssert(!_fromType.isByteArray(), "");
solAssert(_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType(), "");
solAssert(_toType.dataStoredIn(DataLocation::Storage), "");
string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
return m_functionCollector.createFunction(functionName, [&](){
Whiskers templ(R"(
function <functionName>(dst, src) {
if eq(dst, src) { leave }
let length := <arrayLength>(src)
// Make sure array length is sane
if gt(length, 0xffffffffffffffff) { <panic>() }
<?isToDynamic>
<resizeArray>(dst, length)
</isToDynamic>
let srcPtr := <srcDataLocation>(src)
let dstPtr := <dstDataLocation>(dst)
let fullSlots := div(length, <itemsPerSlot>)
let i := 0
for { } lt(i, fullSlots) { i := add(i, 1) } {
sstore(add(dstPtr, i), <maskFull>(sload(add(srcPtr, i))))
}
let spill := sub(length, mul(i, <itemsPerSlot>))
if gt(spill, 0) {
sstore(add(dstPtr, i), <maskBytes>(sload(add(srcPtr, i)), mul(spill, <bytesPerItem>)))
}
}
)");
if (_fromType.dataStoredIn(DataLocation::Storage))
solAssert(!_fromType.isValueType(), "");
templ("functionName", functionName);
templ("isToDynamic", _toType.isDynamicallySized());
if (_toType.isDynamicallySized())
templ("resizeArray", resizeDynamicArrayFunction(_toType));
templ("arrayLength",arrayLengthFunction(_fromType));
templ("panic", panicFunction());
templ("srcDataLocation", arrayDataAreaFunction(_fromType));
templ("dstDataLocation", arrayDataAreaFunction(_toType));
unsigned itemsPerSlot = 32 / _toType.storageStride();
templ("itemsPerSlot", to_string(itemsPerSlot));
templ("bytesPerItem", to_string(_toType.storageStride()));
templ("maskFull", maskLowerOrderBytesFunction(itemsPerSlot * _toType.storageStride()));
templ("maskBytes", maskLowerOrderBytesFunctionDynamic());
return templ.render();
});
}
string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
{
string functionName = "array_convert_length_to_size_" + _type.identifier();

View File

@ -104,6 +104,15 @@ public:
/// signature: (value, bytes) -> result
std::string maskBytesFunctionDynamic();
/// Zeroes out all bytes above the first ``_bytes`` lower order bytes.
/// signature: (value) -> result
std::string maskLowerOrderBytesFunction(size_t _bytes);
/// Zeroes out all bytes above the first ``bytes`` lower order bytes.
/// @note ``bytes`` has to be small enough not to overflow ``8 * bytes``.
/// signature: (value, bytes) -> result
std::string maskLowerOrderBytesFunctionDynamic();
/// @returns the name of a function that rounds its input to the next multiple
/// of 32 or the input if it is a multiple of 32.
/// signature: (value) -> result
@ -186,14 +195,18 @@ public:
/// signature: (slot) ->
std::string clearStorageArrayFunction(ArrayType const& _type);
/// @returns the name of a function that will copy array from calldata or memory to storage
/// @returns the name of a function that will copy an array to storage
/// signature (to_slot, from_ptr) ->
std::string copyArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
/// @returns the name of a function that will copy a byte array from calldata or memory to storage
/// @returns the name of a function that will copy a byte array to storage
/// signature (to_slot, from_ptr) ->
std::string copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
/// @returns the name of a function that will copy an array of value types from storage to storage.
/// signature (to_slot, from_slot) ->
std::string copyValueArrayStorageToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
/// Returns the name of a function that will convert a given length to the
/// size in memory (number of storage slots or calldata/memory bytes) it
/// will require.

View File

@ -12,6 +12,8 @@ contract c {
len = data2.length; if (index < len) val = data2[index];
}
}
// ====
// compileViaYul: also
// ----
// setData1(uint256,uint256,uint256): 10, 5, 4 ->
// copyStorageStorage() ->

View File

@ -10,5 +10,7 @@ contract c {
}
}
// ====
// compileViaYul: also
// ----
// test() -> 9, 4

View File

@ -14,5 +14,7 @@ contract C {
}
}
// ====
// compileViaYul: also
// ----
// test() -> 7