mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[Sol->Yul] Fixing array clearing when copying from storage to storage.
This commit is contained in:
parent
8b6397e8fb
commit
adb9d0c41a
@ -1799,15 +1799,17 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy
|
|||||||
|
|
||||||
string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
|
string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
|
||||||
{
|
{
|
||||||
solAssert(
|
solAssert(_fromType.baseType()->isValueType(), "");
|
||||||
*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
|
solAssert(_toType.baseType()->isValueType(), "");
|
||||||
""
|
solAssert(_fromType.baseType()->isImplicitlyConvertibleTo(*_toType.baseType()), "");
|
||||||
);
|
|
||||||
solAssert(!_fromType.isByteArray(), "");
|
solAssert(!_fromType.isByteArray(), "");
|
||||||
solAssert(_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType(), "");
|
solAssert(!_toType.isByteArray(), "");
|
||||||
|
solAssert(_fromType.dataStoredIn(DataLocation::Storage), "");
|
||||||
solAssert(_toType.dataStoredIn(DataLocation::Storage), "");
|
solAssert(_toType.dataStoredIn(DataLocation::Storage), "");
|
||||||
|
|
||||||
solUnimplementedAssert(_fromType.storageStride() == _toType.storageStride(), "");
|
solAssert(_fromType.storageStride() <= _toType.storageStride(), "");
|
||||||
|
solAssert(_toType.storageStride() <= 32, "");
|
||||||
|
|
||||||
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, [&](){
|
||||||
@ -1819,19 +1821,60 @@ string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const&
|
|||||||
if gt(length, 0xffffffffffffffff) { <panic>() }
|
if gt(length, 0xffffffffffffffff) { <panic>() }
|
||||||
<resizeArray>(dst, length)
|
<resizeArray>(dst, length)
|
||||||
|
|
||||||
let srcPtr := <srcDataLocation>(src)
|
let srcSlot := <srcDataLocation>(src)
|
||||||
|
let dstSlot := <dstDataLocation>(dst)
|
||||||
let dstPtr := <dstDataLocation>(dst)
|
|
||||||
|
|
||||||
let fullSlots := div(length, <itemsPerSlot>)
|
let fullSlots := div(length, <itemsPerSlot>)
|
||||||
let i := 0
|
|
||||||
for { } lt(i, fullSlots) { i := add(i, 1) } {
|
let srcSlotValue := sload(srcSlot)
|
||||||
sstore(add(dstPtr, i), <maskFull>(sload(add(srcPtr, i))))
|
let srcItemIndexInSlot := 0
|
||||||
|
for { let i := 0 } lt(i, fullSlots) { i := add(i, 1) } {
|
||||||
|
let dstSlotValue := 0
|
||||||
|
<?sameType>
|
||||||
|
dstSlotValue := <maskFull>(srcSlotValue)
|
||||||
|
<updateSrcSlotValue>
|
||||||
|
<!sameType>
|
||||||
|
<?multipleItemsPerSlotDst>for { let j := 0 } lt(j, <itemsPerSlot>) { j := add(j, 1) } </multipleItemsPerSlotDst>
|
||||||
|
{
|
||||||
|
let itemValue := <convert>(
|
||||||
|
<extractFromSlot>(srcSlotValue, mul(<srcStride>, srcItemIndexInSlot))
|
||||||
|
)
|
||||||
|
itemValue := <prepareStore>(itemValue)
|
||||||
|
dstSlotValue :=
|
||||||
|
<?multipleItemsPerSlotDst>
|
||||||
|
<updateByteSlice>(dstSlotValue, mul(<dstStride>, j), itemValue)
|
||||||
|
<!multipleItemsPerSlotDst>
|
||||||
|
itemValue
|
||||||
|
</multipleItemsPerSlotDst>
|
||||||
|
|
||||||
|
<updateSrcSlotValue>
|
||||||
}
|
}
|
||||||
let spill := sub(length, mul(i, <itemsPerSlot>))
|
</sameType>
|
||||||
|
|
||||||
|
sstore(add(dstSlot, i), dstSlotValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
<?multipleItemsPerSlotDst>
|
||||||
|
let spill := sub(length, mul(fullSlots, <itemsPerSlot>))
|
||||||
if gt(spill, 0) {
|
if gt(spill, 0) {
|
||||||
sstore(add(dstPtr, i), <maskBytes>(sload(add(srcPtr, i)), mul(spill, <bytesPerItem>)))
|
let dstSlotValue := 0
|
||||||
|
<?sameType>
|
||||||
|
dstSlotValue := <maskBytes>(srcSlotValue, mul(spill, <srcStride>))
|
||||||
|
<updateSrcSlotValue>
|
||||||
|
<!sameType>
|
||||||
|
for { let j := 0 } lt(j, spill) { j := add(j, 1) } {
|
||||||
|
let itemValue := <convert>(
|
||||||
|
<extractFromSlot>(srcSlotValue, mul(<srcStride>, srcItemIndexInSlot))
|
||||||
|
)
|
||||||
|
itemValue := <prepareStore>(itemValue)
|
||||||
|
dstSlotValue := <updateByteSlice>(dstSlotValue, mul(<dstStride>, j), itemValue)
|
||||||
|
|
||||||
|
<updateSrcSlotValue>
|
||||||
}
|
}
|
||||||
|
</sameType>
|
||||||
|
sstore(add(dstSlot, fullSlots), dstSlotValue)
|
||||||
|
}
|
||||||
|
</multipleItemsPerSlotDst>
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
if (_fromType.dataStoredIn(DataLocation::Storage))
|
if (_fromType.dataStoredIn(DataLocation::Storage))
|
||||||
@ -1842,11 +1885,43 @@ string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const&
|
|||||||
templ("panic", panicFunction(PanicCode::ResourceError));
|
templ("panic", panicFunction(PanicCode::ResourceError));
|
||||||
templ("srcDataLocation", arrayDataAreaFunction(_fromType));
|
templ("srcDataLocation", arrayDataAreaFunction(_fromType));
|
||||||
templ("dstDataLocation", arrayDataAreaFunction(_toType));
|
templ("dstDataLocation", arrayDataAreaFunction(_toType));
|
||||||
|
templ("srcStride", to_string(_fromType.storageStride()));
|
||||||
unsigned itemsPerSlot = 32 / _toType.storageStride();
|
unsigned itemsPerSlot = 32 / _toType.storageStride();
|
||||||
templ("itemsPerSlot", to_string(itemsPerSlot));
|
templ("itemsPerSlot", to_string(itemsPerSlot));
|
||||||
templ("bytesPerItem", to_string(_toType.storageStride()));
|
templ("multipleItemsPerSlotDst", itemsPerSlot > 1);
|
||||||
|
bool sameType = _fromType.baseType() == _toType.baseType();
|
||||||
|
templ("sameType", sameType);
|
||||||
|
if (sameType)
|
||||||
|
{
|
||||||
templ("maskFull", maskLowerOrderBytesFunction(itemsPerSlot * _toType.storageStride()));
|
templ("maskFull", maskLowerOrderBytesFunction(itemsPerSlot * _toType.storageStride()));
|
||||||
templ("maskBytes", maskLowerOrderBytesFunctionDynamic());
|
templ("maskBytes", maskLowerOrderBytesFunctionDynamic());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
templ("dstStride", to_string(_toType.storageStride()));
|
||||||
|
templ("extractFromSlot", extractFromStorageValueDynamic(*_fromType.baseType()));
|
||||||
|
templ("updateByteSlice", updateByteSliceFunctionDynamic(_toType.storageStride()));
|
||||||
|
templ("convert", conversionFunction(*_fromType.baseType(), *_toType.baseType()));
|
||||||
|
templ("prepareStore", prepareStoreFunction(*_toType.baseType()));
|
||||||
|
}
|
||||||
|
templ("updateSrcSlotValue", Whiskers(R"(
|
||||||
|
<?srcReadMultiPerSlot>
|
||||||
|
srcItemIndexInSlot := add(srcItemIndexInSlot, 1)
|
||||||
|
if eq(srcItemIndexInSlot, <srcItemsPerSlot>) {
|
||||||
|
// here we are done with this slot, we need to read next one
|
||||||
|
srcSlot := add(srcSlot, 1)
|
||||||
|
srcSlotValue := sload(srcSlot)
|
||||||
|
srcItemIndexInSlot := 0
|
||||||
|
}
|
||||||
|
<!srcReadMultiPerSlot>
|
||||||
|
srcSlot := add(srcSlot, 1)
|
||||||
|
srcSlotValue := sload(srcSlot)
|
||||||
|
</srcReadMultiPerSlot>
|
||||||
|
)")
|
||||||
|
("srcReadMultiPerSlot", !sameType && _fromType.storageStride() <= 16)
|
||||||
|
("srcItemsPerSlot", to_string(32 / _fromType.storageStride()))
|
||||||
|
.render()
|
||||||
|
);
|
||||||
|
|
||||||
return templ.render();
|
return templ.render();
|
||||||
});
|
});
|
||||||
|
@ -17,5 +17,7 @@ contract c {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x05000000000000000000000000000000000000000000000000
|
// test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x05000000000000000000000000000000000000000000000000
|
||||||
|
@ -15,5 +15,7 @@ contract c {
|
|||||||
y = data2[4];
|
y = data2[4];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> 5, 4
|
// test() -> 5, 4
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
contract C {
|
||||||
|
bytes1[2] data1;
|
||||||
|
bytes2[2] data2;
|
||||||
|
function test() public returns (bytes2, bytes2) {
|
||||||
|
uint i;
|
||||||
|
for (i = 0; i < data1.length; ++i)
|
||||||
|
data1[i] = bytes1(uint8(1 + i));
|
||||||
|
data2 = data1;
|
||||||
|
return (data2[0], data2[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// test() -> left(0x01), left(0x02)
|
@ -15,5 +15,7 @@ contract c {
|
|||||||
res2 |= uint(uint16(data2[16 + i])) * 0x10000**i;
|
res2 |= uint(uint16(data2[16 + i])) * 0x10000**i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> 0xffffffff, 0x0000000000000000000000000a00090008000700060005000400030002000100, 0x0000000000000000000000000000000000000000000000000000000000000000
|
// test() -> 0xffffffff, 0x0000000000000000000000000a00090008000700060005000400030002000100, 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
@ -17,6 +17,7 @@ contract c {
|
|||||||
r3 = data2[5];
|
r3 = data2[5];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> 0x04000000000000000000000000000000000000000000000000, 0x0, 0x0
|
// test() -> 0x04000000000000000000000000000000000000000000000000, 0x0, 0x0
|
||||||
|
@ -17,5 +17,7 @@ contract c {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x0
|
// test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x0
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
contract c {
|
||||||
|
bytes9[7] data1; // 3 per slot
|
||||||
|
bytes32[10] data2; // 1 per slot
|
||||||
|
|
||||||
|
function test()
|
||||||
|
public
|
||||||
|
returns (bytes32 a, bytes32 b, bytes32 c, bytes32 d, bytes32 e)
|
||||||
|
{
|
||||||
|
for (uint256 i = 0; i < data1.length; ++i) data1[i] = bytes8(uint64(i));
|
||||||
|
data2[8] = data2[9] = bytes8(uint64(2));
|
||||||
|
data2 = data1;
|
||||||
|
a = data2[1];
|
||||||
|
b = data2[2];
|
||||||
|
c = data2[3];
|
||||||
|
d = data2[4];
|
||||||
|
e = data2[9];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x00
|
Loading…
Reference in New Issue
Block a user