Merge pull request #10271 from ethereum/copyValueArrayFromStorageToStorage

Copy value array from storage to storage.
This commit is contained in:
chriseth 2020-11-17 11:02:13 +01:00 committed by GitHub
commit 1911a5a25a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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 YulUtilFunctions::roundUpFunction()
{ {
string functionName = "round_up_to_mul_of_32"; string functionName = "round_up_to_mul_of_32";
@ -1470,23 +1500,20 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
); );
if (_fromType.isByteArray()) if (_fromType.isByteArray())
return copyByteArrayToStorageFunction(_fromType, _toType); 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(); string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
return m_functionCollector.createFunction(functionName, [&](){ return m_functionCollector.createFunction(functionName, [&](){
Whiskers templ(R"( Whiskers templ(R"(
function <functionName>(slot, value<?isFromDynamicCalldata>, len</isFromDynamicCalldata>) { function <functionName>(slot, value<?isFromDynamicCalldata>, len</isFromDynamicCalldata>) {
<?fromStorage> if eq(slot, value) { leave } </fromStorage>
let length := <arrayLength>(value<?isFromDynamicCalldata>, len</isFromDynamicCalldata>) let length := <arrayLength>(value<?isFromDynamicCalldata>, len</isFromDynamicCalldata>)
<?isToDynamic> <?isToDynamic>
<resizeArray>(slot, length) <resizeArray>(slot, length)
</isToDynamic> </isToDynamic>
let srcPtr := let srcPtr := <srcDataLocation>(value)
<?isFromMemoryDynamic>
add(value, 0x20)
<!isFromMemoryDynamic>
value
</isFromMemoryDynamic>
let elementSlot := <dstDataLocation>(slot) let elementSlot := <dstDataLocation>(slot)
let elementOffset := 0 let elementOffset := 0
@ -1509,6 +1536,10 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
let <elementValues> := <readFromCalldataOrMemory>(srcPtr) let <elementValues> := <readFromCalldataOrMemory>(srcPtr)
</fromMemory> </fromMemory>
<?fromStorage>
let <elementValues> := srcPtr
</fromStorage>
<updateStorageValue>(elementSlot<?isValueType>, elementOffset</isValueType>, <elementValues>) <updateStorageValue>(elementSlot<?isValueType>, elementOffset</isValueType>, <elementValues>)
srcPtr := add(srcPtr, <stride>) 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); templ("functionName", functionName);
bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData); bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData);
templ("isFromDynamicCalldata", _fromType.isDynamicallySized() && fromCalldata); 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("fromCalldata", fromCalldata);
templ("isToDynamic", _toType.isDynamicallySized()); templ("isToDynamic", _toType.isDynamicallySized());
templ("srcDataLocation", arrayDataAreaFunction(_fromType));
templ("isFromMemoryDynamic", _fromType.isDynamicallySized() && _fromType.dataStoredIn(DataLocation::Memory)); templ("isFromMemoryDynamic", _fromType.isDynamicallySized() && _fromType.dataStoredIn(DataLocation::Memory));
if (fromCalldata) if (fromCalldata)
{ {
@ -1545,7 +1581,7 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
templ("arrayLength",arrayLengthFunction(_fromType)); templ("arrayLength",arrayLengthFunction(_fromType));
templ("isValueType", _fromType.baseType()->isValueType()); templ("isValueType", _fromType.baseType()->isValueType());
templ("dstDataLocation", arrayDataAreaFunction(_toType)); templ("dstDataLocation", arrayDataAreaFunction(_toType));
if (!fromCalldata || _fromType.baseType()->isValueType()) if (fromMemory || (fromCalldata && _fromType.baseType()->isValueType()))
templ("readFromCalldataOrMemory", readFromMemoryOrCalldata(*_fromType.baseType(), fromCalldata)); templ("readFromCalldataOrMemory", readFromMemoryOrCalldata(*_fromType.baseType(), fromCalldata));
templ("elementValues", suffixedVariableNameList( templ("elementValues", suffixedVariableNameList(
"elementValue_", "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 YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
{ {
string functionName = "array_convert_length_to_size_" + _type.identifier(); string functionName = "array_convert_length_to_size_" + _type.identifier();

View File

@ -104,6 +104,15 @@ public:
/// signature: (value, bytes) -> result /// signature: (value, bytes) -> result
std::string maskBytesFunctionDynamic(); 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 /// @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. /// of 32 or the input if it is a multiple of 32.
/// signature: (value) -> result /// signature: (value) -> result
@ -186,14 +195,18 @@ public:
/// signature: (slot) -> /// signature: (slot) ->
std::string clearStorageArrayFunction(ArrayType const& _type); 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) -> /// signature (to_slot, from_ptr) ->
std::string copyArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType); 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) -> /// signature (to_slot, from_ptr) ->
std::string copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType); 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 /// 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 /// size in memory (number of storage slots or calldata/memory bytes) it
/// will require. /// will require.

View File

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

View File

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

View File

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