Skip to content

Commit 07e67a8

Browse files
clonkerr0qs
authored andcommitted
Adapt ArrayUtils to deal with wrap-around static arrays
Also sometimes cleanup can be skipped when copying from a smaller source array into a larger target array
1 parent f67620e commit 07e67a8

27 files changed

+87
-19
lines changed

libsolidity/codegen/ArrayUtils.cpp

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,58 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
290290
_context << Instruction::POP << Instruction::SWAP1 << Instruction::POP;
291291
// stack: target_ref target_data_end target_data_pos_updated
292292
if (targetBaseType->storageBytes() < 32)
293+
{
294+
if (!targetType->isDynamicallySized() && !sourceType->isDynamicallySized())
295+
if (auto const* targetArrayType = dynamic_cast<ArrayType const*>(targetType))
296+
{
297+
auto const* sourceArrayType = dynamic_cast<ArrayType const*>(sourceType);
298+
solAssert(sourceArrayType);
299+
300+
// length of source and target arrays, respectively
301+
auto const& sourceLength = sourceArrayType->length();
302+
auto const& targetLength = targetArrayType->length();
303+
304+
// number of source and target elements per slot, respectively
305+
auto const nSourcePerSlot = 32 / sourceArrayType->baseType()->storageBytes();
306+
auto const nTargetPerSlot = 32 / targetArrayType->baseType()->storageBytes();
307+
308+
// number of source elements that are partially embedded in the final source slot
309+
auto const sourceRemainder = sourceLength % nSourcePerSlot;
310+
311+
// number of source elements in the final slot that are not used by the source array
312+
auto const nSourceCarryOver = (nSourcePerSlot - sourceRemainder) % nSourcePerSlot;
313+
314+
// number of source elements over how many elements we can put into one target slot
315+
// This will floor, e.g.:
316+
// source length = 8, nTargetPerSlot = 3 -> targetCopyEndSlotWithoutRemainder = 8 // 3 = 2
317+
// source length = 8, nTargetPerSlot = 2 -> targetCopyEndSlotWithoutRemainder = 8 // 2 = 4
318+
auto const targetCopyEndSlotWithoutRemainder = sourceLength / nTargetPerSlot;
319+
// now we take into account the target remainder, i.e., the elements that were copied which did
320+
// not fill up an entire target slot (sourceLength % nTargetPerSlot)
321+
// and also add the number of source elements which are artificially copied as zeros if they
322+
// were in a non-filled source slot
323+
auto const numSlotsNeededForRemainder = [&]() -> u256
324+
{
325+
auto const targetCopyRemainder = sourceLength % nTargetPerSlot;
326+
if (targetCopyRemainder + nSourceCarryOver == 0)
327+
return 0;
328+
// this is equivalent to ceil((targetCopyRemainder+nSourceCarryOver) / nTargetPerSlot)
329+
return (targetCopyRemainder + nSourceCarryOver - 1) / nTargetPerSlot + 1;
330+
}();
331+
auto const updatedTargetCopyEndPos = targetCopyEndSlotWithoutRemainder + numSlotsNeededForRemainder;
332+
333+
// by copying the rest of the source elements of a slot that wasn't full, it can happen that we
334+
// meet or even exceed the bounds of the target array - in this case we don't have to clean up
335+
auto const targetRemainder = targetLength % nTargetPerSlot;
336+
auto const targetEndSlot = targetLength / nTargetPerSlot + (targetRemainder > 0 ? 1 : 0);
337+
if (updatedTargetCopyEndPos >= targetEndSlot)
338+
{
339+
_context << Instruction::POP << Instruction::POP;
340+
return;
341+
}
342+
}
293343
utils.clearStorageLoop(TypeProvider::uint256(), !targetType->isDynamicallySized());
344+
}
294345
else
295346
utils.clearStorageLoop(targetBaseType, !targetType->isDynamicallySized());
296347
_context << Instruction::POP;
@@ -775,7 +826,7 @@ void ArrayUtils::clearStorageLoop(Type const* _type, bool _canOverflow) const
775826
{
776827
solAssert(_type->storageBytes() >= 32, "");
777828
m_context.callLowLevelFunction(
778-
"$clearStorageLoop_" + _type->identifier(),
829+
"$clearStorageLoop_" + _type->identifier() + (_canOverflow ? "_canOverflow" : "cannotOverflow"),
779830
2,
780831
1,
781832
[_type, _canOverflow](CompilerContext& _context)

test/libsolidity/semanticTests/abiEncoderV2/calldata_overlapped_dynamic_arrays.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ contract C {
3434
// f_which(uint256[],uint256[2],uint256): 0x40, 1, 2, 1 -> FAILURE
3535
// f_storage(uint256[],uint256[2]): 0x20, 1, 2 -> 0x20, 0x60, 0x20, 1, 2
3636
// gas irOptimized: 111409
37-
// gas legacy: 112707
37+
// gas legacy: 112704
3838
// gas legacyOptimized: 111845
3939
// f_storage(uint256[],uint256[2]): 0x40, 1, 2, 5, 6 -> 0x20, 0x80, 0x20, 2, 5, 6
4040
// f_storage(uint256[],uint256[2]): 0x40, 1, 2, 5 -> FAILURE

test/libsolidity/semanticTests/abiEncoderV2/storage_array_encoding.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ contract C {
1919
// ----
2020
// h(uint256[2][]): 0x20, 3, 123, 124, 223, 224, 323, 324 -> 32, 256, 0x20, 3, 123, 124, 223, 224, 323, 324
2121
// gas irOptimized: 180080
22-
// gas legacy: 184233
22+
// gas legacy: 184224
2323
// gas legacyOptimized: 180856
2424
// i(uint256[2][2]): 123, 124, 223, 224 -> 32, 128, 123, 124, 223, 224
2525
// gas irOptimized: 112031
26-
// gas legacy: 115091
26+
// gas legacy: 115082
2727
// gas legacyOptimized: 112657

test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ contract Test {
1313
// ----
1414
// set(uint24[3][]): 0x20, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 -> 0x06
1515
// gas irOptimized: 185216
16-
// gas legacy: 211054
16+
// gas legacy: 211036
1717
// gas legacyOptimized: 206077
1818
// data(uint256,uint256): 0x02, 0x02 -> 0x09
1919
// data(uint256,uint256): 0x05, 0x01 -> 0x11

test/libsolidity/semanticTests/array/constant_var_as_array_length.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ contract C {
1010
// constructor(): 1, 2, 3 ->
1111
// gas irOptimized: 124991
1212
// gas irOptimized code: 14800
13-
// gas legacy: 134317
13+
// gas legacy: 134298
1414
// gas legacy code: 46200
1515
// gas legacyOptimized: 127166
1616
// gas legacyOptimized code: 23400

test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ contract c {
2121
// ----
2222
// store(uint256[9],uint8[3][]): 21, 22, 23, 24, 25, 26, 27, 28, 29, 0x140, 4, 1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33 -> 32
2323
// gas irOptimized: 647725
24-
// gas legacy: 694354
24+
// gas legacy: 694351
2525
// gas legacyOptimized: 693849
2626
// retrieve() -> 9, 28, 9, 28, 4, 3, 32

test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ contract c {
3636
// ----
3737
// test() -> 0x02000202
3838
// gas irOptimized: 4549676
39-
// gas legacy: 4473477
39+
// gas legacy: 4473198
4040
// gas legacyOptimized: 4445748
4141
// storageEmpty -> 1
4242
// clear() -> 0, 0

test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ contract c {
1313
// ----
1414
// test(uint256[2][]): 32, 3, 7, 8, 9, 10, 11, 12 -> 10
1515
// gas irOptimized: 689552
16-
// gas legacy: 686176
16+
// gas legacy: 686086
1717
// gas legacyOptimized: 685611

test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base_nested.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@ contract c {
2222
// ----
2323
// test() -> 3, 4
2424
// gas irOptimized: 169602
25-
// gas legacy: 175415
25+
// gas legacy: 175289
2626
// gas legacyOptimized: 172533

test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_static.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ contract c {
1515
// ----
1616
// test() -> 8, 0
1717
// gas irOptimized: 196251
18-
// gas legacy: 194842
18+
// gas legacy: 194779
1919
// gas legacyOptimized: 194281

0 commit comments

Comments
 (0)