mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #10271 from ethereum/copyValueArrayFromStorageToStorage
Copy value array from storage to storage.
This commit is contained in:
commit
1911a5a25a
@ -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();
|
||||||
|
@ -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.
|
||||||
|
@ -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() ->
|
||||||
|
@ -10,5 +10,7 @@ contract c {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> 9, 4
|
// test() -> 9, 4
|
||||||
|
@ -14,5 +14,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> 7
|
// test() -> 7
|
||||||
|
Loading…
Reference in New Issue
Block a user