Merge pull request #10492 from ethereum/arrayClearingDiffBaseSol2Yul

[Sol->Yul] Fixing array clearing when copying from storage to storage.
This commit is contained in:
chriseth 2020-12-22 15:15:03 +01:00 committed by GitHub
commit 04d83af1d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 180 additions and 20 deletions

View File

@ -1810,15 +1810,17 @@ 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.baseType()->isValueType(), "");
solAssert(_toType.baseType()->isValueType(), "");
solAssert(_fromType.baseType()->isImplicitlyConvertibleTo(*_toType.baseType()), "");
solAssert(!_fromType.isByteArray(), "");
solAssert(_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType(), "");
solAssert(!_toType.isByteArray(), "");
solAssert(_fromType.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();
return m_functionCollector.createFunction(functionName, [&](){
@ -1830,19 +1832,60 @@ string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const&
if gt(length, 0xffffffffffffffff) { <panic>() }
<resizeArray>(dst, length)
let srcPtr := <srcDataLocation>(src)
let dstPtr := <dstDataLocation>(dst)
let srcSlot := <srcDataLocation>(src)
let dstSlot := <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>)))
let srcSlotValue := sload(srcSlot)
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>
}
</sameType>
sstore(add(dstSlot, i), dstSlotValue)
}
<?multipleItemsPerSlotDst>
let spill := sub(length, mul(fullSlots, <itemsPerSlot>))
if gt(spill, 0) {
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))
@ -1853,11 +1896,43 @@ string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const&
templ("panic", panicFunction(PanicCode::ResourceError));
templ("srcDataLocation", arrayDataAreaFunction(_fromType));
templ("dstDataLocation", arrayDataAreaFunction(_toType));
templ("srcStride", to_string(_fromType.storageStride()));
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());
templ("multipleItemsPerSlotDst", itemsPerSlot > 1);
bool sameType = _fromType.baseType() == _toType.baseType();
templ("sameType", sameType);
if (sameType)
{
templ("maskFull", maskLowerOrderBytesFunction(itemsPerSlot * _toType.storageStride()));
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();
});

View File

@ -17,5 +17,7 @@ contract c {
}
}
// ====
// compileViaYul: also
// ----
// test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x05000000000000000000000000000000000000000000000000

View File

@ -15,5 +15,7 @@ contract c {
y = data2[4];
}
}
// ====
// compileViaYul: also
// ----
// test() -> 5, 4

View File

@ -0,0 +1,25 @@
contract c {
uint48[5][2] data1;
uint120[6][3] data2;
function test() public returns (uint256 x, uint120 y) {
data2[0][0] = 11;
data2[1][0] = 22;
data2[2][0] = 33;
data1[0][0] = 0;
data1[0][1] = 1;
data1[0][2] = 2;
data1[0][3] = 3;
data1[0][4] = 4;
data1[1][0] = 3;
data2 = data1;
assert(data1[0][1] == data2[0][1]);
x = data2.length;
y = data2[0][4];
}
}
// ====
// compileViaYul: also
// ----
// test() -> 3, 4

View File

@ -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)

View File

@ -15,5 +15,7 @@ contract c {
res2 |= uint(uint16(data2[16 + i])) * 0x10000**i;
}
}
// ====
// compileViaYul: also
// ----
// test() -> 0xffffffff, 0x0000000000000000000000000a00090008000700060005000400030002000100, 0x0000000000000000000000000000000000000000000000000000000000000000

View File

@ -17,6 +17,7 @@ contract c {
r3 = data2[5];
}
}
// ====
// compileViaYul: also
// ----
// test() -> 0x04000000000000000000000000000000000000000000000000, 0x0, 0x0

View File

@ -17,5 +17,7 @@ contract c {
}
}
// ====
// compileViaYul: also
// ----
// test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x0

View File

@ -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

View File

@ -0,0 +1,13 @@
contract C {
int256[32] data1;
uint256[10] data2;
function f() external {
data1 = data2;
}
function g() external {
data2 = data1;
}
}
// ----
// TypeError 7407: (102-107): Type uint256[10] storage ref is not implicitly convertible to expected type int256[32] storage ref.
// TypeError 7407: (159-164): Type int256[32] storage ref is not implicitly convertible to expected type uint256[10] storage ref.