diff --git a/docs/bugs.json b/docs/bugs.json index 4b5dd74f8571..06a74c3b2835 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,51 @@ [ + { + "uid": "SOL-2026-3", + "name": "InheritanceOrderReversalOnStorageEndWarning", + "summary": "Emitting a warning about storage base location being too close to the storage end unintentionally reversed the ``linearizedBaseContracts`` annotation, possibly leading to miscompilation due to reversing the order in which inheritance is resolved.", + "description": "When the compiler detects that a custom layout specifier puts contract's static storage area too close to the end of the address space, it emits a warning. To make the warning more useful, the compiler tries to point at the last storage variable in that area. For this reason it walks the linearized inheritance hierarchy in reverse (from the least to the most derived). The list is calculated once and stored in an AST annotation called ``linearizedBaseContracts``. The direct cause of the bug was the fact that the code that reverses the list was doing it in place rather than on a copy, modifying the annotation. This effectively reversed the order of base contracts seen by any component that runs after layout checks: later phases of analysis, AST export, code generator, SMTChecker, etc. The observable effect was a reversed order of state variable initialization, constructor invocation, virtual function/modifier resolution, leading either to miscompilations or internal compiler errors, depending on the specific usage. Since the source of the bug was in the analysis stage, it was independent of the codegen pipeline or optimizer settings. The main condition necessary to trigger the bug was the presence of the warning in the output. The other is presence of language constructs whose evaluation depends on the inheritance order. While potential effects are very serious, this requirement excludes the vast majority of contracts as intentionally placing the storage variables in the last 2**64 slots is highly discouraged, which was actually the main reason for adding this warning.", + "link": "https://blog.soliditylang.org/2026/07/09/inheritance-order-reversal-on-storage-end-warning-bug/", + "introduced": "0.8.29", + "fixed": "0.8.36", + "severity": "medium" + }, + { + "uid": "SOL-2026-2", + "name": "UnsoundSpillInMutualRecursion", + "summary": "Local variables of a function involved in mutual recursion may spuriously be moved to fixed memory offsets and overwritten across recursive calls.", + "description": "To work around the 16-slot stack access limit of the EVM, the IR-based code generator can move local variables of stack-too-deep functions to fixed memory offsets. This relocation is unsound for recursive functions: a fixed offset would be shared by all activations of the function, so a recursive call would overwrite the caller's value. The stack limit evader therefore must not relocate variables of functions that are part of a recursive call chain. To this end, the call graph was searched for cycles using a path-based depth-first search that, once a function had been fully explored and popped from the search path, short-circuited on it on any later visit. As a result, a function shared between several intersecting cycles could be reached first through a path that did not yet close a cycle through it, get marked as finished, and then be skipped when a later path would have revealed that it does lie on a cycle. Such a function was misclassified as non-recursive. When a misclassified function was complex enough for the stack limit evader to relocate some of its variables, those variables were moved to fixed memory offsets and silently corrupted on recursion, producing wrong results rather than a compile-time error. Triggering the bug requires the IR pipeline, a set of mutually recursive functions whose call graph contains intersecting cycles, at least one of the functions in an undetected part of a cycle being complex enough to require relocation to memory, and an unfortunate processing order of the functions (which depends on the hashes of their Yul names). It is independent of whether the optimizer is enabled.", + "link": "https://blog.soliditylang.org/2026/07/08/unsound-spill-in-mutual-recursion-bug/", + "introduced": "0.7.2", + "fixed": "0.8.36", + "severity": "medium", + "conditions": { + "viaIR": true + } + }, + { + "uid": "SOL-2026-1", + "name": "TransientStorageClearingHelperCollision", + "summary": "Clearing both storage and transient storage variables in the same contract may result in only one of these locations being cleared.", + "description": "The IR-based code generator provides a set of Yul helper functions for basic operations, such as clearing, copying, encoding or type conversions. Not all functions are used by every contract. The codegen appends them to the generated sources individually, only when an operation that would invoke one of them is encountered. Utility functions are often specialized for different types and locations. Since Yul does not support generic functions, specialization is done by generating multiple versions of the same function, with the information distinguishing the variants embedded in their names. However, if not all the necessary bits of distinguishing information are properly accounted for, two helpers may end up with the same name, causing a collision. In this situation the codegen includes only one of them, with calls to both variants invoking it. This happened with the ``set_to_zero`` helper used when an area of transient or persistent storage needs to be cleared. The helper name was missing the location information, which resulted in a collision between the persistent and transient storage variants for the same type. This meant that contracts clearing both locations would actually clear only one, leaving the other untouched. Which location ended up being cleared depended on the order in which the code generator processed the input. The necessary condition to trigger the bug was the use of ``delete`` operator on a transient storage variable. This was due to value types being the only types supported in transient storage and ``delete`` being the only operation invoking the helper allowed on such types. The other necessary condition was clearing of persistent storage and in this case the range of affected operations was wider: operator ``delete``, array ``pop()`` or assignment that resulted in a longer array being overwritten with a shorter one. The cleared variable itself also did not necessarily have to be of the same type. It was enough that a matching value type was nested in it. It also did not always have to be the exact same value type - clearing operations on reference types are usually performed at slot granularity, treating every slot as ``uint256`` rather than clearing every value packed into it individually. To trigger the bug both operations had to be present within the same piece of bytecode. Independent contracts, not related through inheritance, would not affect each other this way. The presence of one operation only in creation code and the other only in deployed code would not trigger the bug either.", + "link": "https://blog.soliditylang.org/2026/02/18/transient-storage-clearing-helper-collision-bug/", + "introduced": "0.8.28", + "fixed": "0.8.34", + "severity": "high", + "conditions": { + "viaIR": true, + "evmVersion": ">=cancun" + } + }, + { + "uid": "SOL-2025-1", + "name": "LostStorageArrayWriteOnSlotOverflow", + "summary": "Operations that involve clearing or copying from arrays that straddle the end of storage could result in silent data retention.", + "description": "Solidity makes it possible to define variables that extend past the last (2**256-th) slot of storage, which results in wrap-around back to slot zero. Since EVM uses 256-bit integer arithmetic, most operations on such variables just work. The only situation which requires special attention is iteration against absolute slot addresses: the invariant that the last slot belonging to a variable has the highest address does not hold. When implemented incorrectly, a loop over an array will immediately terminate if the container spans the end of storage - due to the initial position already being greater than the end position. This affected storage array clearing loops generated by both evmasm and IR pipelines. Additionally, (only in the evmasm pipeline) copying operations whose source was an array straddling the end of storage were also affected. At the language level, the buggy code would be generated for array assignment, array initialization, delete operator, .pop() and .push(). Note that a clearing loop is inserted by the compiler not only for invocations of the delete operator, but also to zero storage when overwriting a longer array with a shorter one, popping an element or even pushing an empty element to a dynamic array. Since clearing is a separate loop, it is possible for the bug to only affect it and not the copy operation it follows (which is always the case in the IR pipeline). The bug is extremely unlikely to be triggered accidentally due to the probabilistic impossibility of a short dynamic array being allocated right at the storage boundary. On the other hand, scenarios in which a user may place a static array there intentionally do not seem realistic and are limited to unusual layouts, in which a contract does not place any storage variables at slot zero (otherwise they would overlap the array).", + "link": "https://blog.soliditylang.org/2025/12/18/lost-storage-array-write-on-slot-overflow-bug/", + "introduced": "0.1.0", + "fixed": "0.8.32", + "severity": "low" + }, { "uid": "SOL-2023-3", "name": "VerbatimInvalidDeduplication", @@ -476,7 +523,7 @@ "uid": "SOL-2017-5", "name": "ZeroFunctionSelector", "summary": "It is possible to craft the name of a function such that it is executed instead of the fallback function in very specific circumstances.", - "description": "If a function has a selector consisting only of zeros, is payable and part of a contract that does not have a fallback function and at most five external functions in total, this function is called instead of the fallback function if Trx is sent to the contract without data.", + "description": "If a function has a selector consisting only of zeros, is payable and part of a contract that does not have a fallback function and at most five external functions in total, this function is called instead of the fallback function if Ether is sent to the contract without data.", "fixed": "0.4.18", "severity": "very low" }, @@ -561,8 +608,8 @@ { "uid": "SOL-2016-7", "name": "LibrariesNotCallableFromPayableFunctions", - "summary": "Library functions threw an exception when called from a call that received Trx.", - "description": "Library functions are protected against sending them Trx through a call. Since the DELEGATECALL opcode forwards the information about how much Trx was sent with a call, the library function incorrectly assumed that Trx was sent to the library and threw an exception.", + "summary": "Library functions threw an exception when called from a call that received Ether.", + "description": "Library functions are protected against sending them Ether through a call. Since the DELEGATECALL opcode forwards the information about how much Ether was sent with a call, the library function incorrectly assumed that Ether was sent to the library and threw an exception.", "severity": "low", "introduced": "0.4.0", "fixed": "0.4.2" @@ -570,8 +617,8 @@ { "uid": "SOL-2016-6", "name": "SendFailsForZeroEther", - "summary": "The send function did not provide enough gas to the recipient if no Trx was sent with it.", - "description": "The recipient of an Trx transfer automatically receives a certain amount of gas from the EVM to handle the transfer. In the case of a zero-transfer, this gas is not provided which causes the recipient to throw an exception.", + "summary": "The send function did not provide enough gas to the recipient if no Ether was sent with it.", + "description": "The recipient of an Ether transfer automatically receives a certain amount of gas from the EVM to handle the transfer. In the case of a zero-transfer, this gas is not provided which causes the recipient to throw an exception.", "severity": "low", "fixed": "0.4.0" }, diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 1a210b885c2a..0cc12bfe9eb7 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1,6 +1,7 @@ { "0.1.0": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -23,6 +24,7 @@ }, "0.1.1": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -45,6 +47,7 @@ }, "0.1.2": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -67,6 +70,7 @@ }, "0.1.3": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -89,6 +93,7 @@ }, "0.1.4": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -112,6 +117,7 @@ }, "0.1.5": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -135,6 +141,7 @@ }, "0.1.6": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -160,6 +167,7 @@ }, "0.1.7": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -185,6 +193,7 @@ }, "0.2.0": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -211,6 +220,7 @@ }, "0.2.1": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -237,6 +247,7 @@ }, "0.2.2": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -263,6 +274,7 @@ }, "0.3.0": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -291,6 +303,7 @@ }, "0.3.1": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -318,6 +331,7 @@ }, "0.3.2": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -345,6 +359,7 @@ }, "0.3.3": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -371,6 +386,7 @@ }, "0.3.4": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -397,6 +413,7 @@ }, "0.3.5": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -423,6 +440,7 @@ }, "0.3.6": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -447,6 +465,7 @@ }, "0.4.0": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -471,6 +490,7 @@ }, "0.4.1": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -495,6 +515,7 @@ }, "0.4.10": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -518,6 +539,7 @@ }, "0.4.11": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -540,6 +562,7 @@ }, "0.4.12": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -561,6 +584,7 @@ }, "0.4.13": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -582,6 +606,7 @@ }, "0.4.14": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -602,6 +627,7 @@ }, "0.4.15": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -621,6 +647,7 @@ }, "0.4.16": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -643,6 +670,7 @@ }, "0.4.17": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -666,6 +694,7 @@ }, "0.4.18": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -688,6 +717,7 @@ }, "0.4.19": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -711,6 +741,7 @@ }, "0.4.2": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -734,6 +765,7 @@ }, "0.4.20": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -757,6 +789,7 @@ }, "0.4.21": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -780,6 +813,7 @@ }, "0.4.22": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -803,6 +837,7 @@ }, "0.4.23": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -825,6 +860,7 @@ }, "0.4.24": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -847,6 +883,7 @@ }, "0.4.25": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -867,6 +904,7 @@ }, "0.4.26": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -884,6 +922,7 @@ }, "0.4.3": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -906,6 +945,7 @@ }, "0.4.4": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -927,6 +967,7 @@ }, "0.4.5": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -951,6 +992,7 @@ }, "0.4.6": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -974,6 +1016,7 @@ }, "0.4.7": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -997,6 +1040,7 @@ }, "0.4.8": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -1020,6 +1064,7 @@ }, "0.4.9": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -1043,6 +1088,7 @@ }, "0.5.0": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1063,6 +1109,7 @@ }, "0.5.1": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1083,6 +1130,7 @@ }, "0.5.10": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1101,6 +1149,7 @@ }, "0.5.11": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1118,6 +1167,7 @@ }, "0.5.12": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1135,6 +1185,7 @@ }, "0.5.13": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1152,6 +1203,7 @@ }, "0.5.14": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1171,6 +1223,7 @@ }, "0.5.15": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1189,6 +1242,7 @@ }, "0.5.16": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1206,6 +1260,7 @@ }, "0.5.17": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1222,6 +1277,7 @@ }, "0.5.2": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1242,6 +1298,7 @@ }, "0.5.3": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1262,6 +1319,7 @@ }, "0.5.4": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1282,6 +1340,7 @@ }, "0.5.5": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1304,6 +1363,7 @@ }, "0.5.6": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1326,6 +1386,7 @@ }, "0.5.7": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1346,6 +1407,7 @@ }, "0.5.8": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1367,6 +1429,7 @@ }, "0.5.9": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1387,6 +1450,7 @@ }, "0.6.0": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1405,6 +1469,7 @@ }, "0.6.1": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1422,6 +1487,7 @@ }, "0.6.10": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1438,6 +1504,7 @@ }, "0.6.11": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1454,6 +1521,7 @@ }, "0.6.12": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1470,6 +1538,7 @@ }, "0.6.2": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1488,6 +1557,7 @@ }, "0.6.3": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1506,6 +1576,7 @@ }, "0.6.4": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1524,6 +1595,7 @@ }, "0.6.5": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1542,6 +1614,7 @@ }, "0.6.6": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1559,6 +1632,7 @@ }, "0.6.7": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1577,6 +1651,7 @@ }, "0.6.8": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1592,6 +1667,7 @@ }, "0.6.9": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1609,6 +1685,7 @@ }, "0.7.0": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1625,6 +1702,7 @@ }, "0.7.1": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1642,6 +1720,8 @@ }, "0.7.2": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1658,6 +1738,8 @@ }, "0.7.3": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1673,6 +1755,8 @@ }, "0.7.4": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1687,6 +1771,8 @@ }, "0.7.5": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1701,6 +1787,8 @@ }, "0.7.6": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1715,6 +1803,8 @@ }, "0.8.0": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1729,6 +1819,8 @@ }, "0.8.1": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1743,6 +1835,8 @@ }, "0.8.10": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1755,6 +1849,8 @@ }, "0.8.11": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1768,6 +1864,8 @@ }, "0.8.12": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1781,6 +1879,8 @@ }, "0.8.13": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1795,6 +1895,8 @@ }, "0.8.14": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1807,6 +1909,8 @@ }, "0.8.15": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1817,6 +1921,8 @@ }, "0.8.16": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1826,6 +1932,8 @@ }, "0.8.17": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" @@ -1834,6 +1942,8 @@ }, "0.8.18": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" @@ -1842,6 +1952,8 @@ }, "0.8.19": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" @@ -1850,6 +1962,8 @@ }, "0.8.2": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1864,6 +1978,8 @@ }, "0.8.20": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" @@ -1872,46 +1988,76 @@ }, "0.8.21": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication" ], "released": "2023-07-19" }, "0.8.22": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication" ], "released": "2023-10-25" }, "0.8.23": { - "bugs": [], + "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2023-11-08" }, "0.8.24": { - "bugs": [], + "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2024-01-25" }, "0.8.25": { - "bugs": [], + "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2024-03-14" }, "0.8.26": { - "bugs": [], + "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2024-05-21" }, "0.8.27": { - "bugs": [], + "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2024-09-04" }, "0.8.28": { - "bugs": [], + "bugs": [ + "UnsoundSpillInMutualRecursion", + "TransientStorageClearingHelperCollision", + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2024-10-09" }, "0.8.29": { - "bugs": [], + "bugs": [ + "InheritanceOrderReversalOnStorageEndWarning", + "UnsoundSpillInMutualRecursion", + "TransientStorageClearingHelperCollision", + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2025-03-12" }, "0.8.3": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1924,11 +2070,57 @@ "released": "2021-03-23" }, "0.8.30": { - "bugs": [], + "bugs": [ + "InheritanceOrderReversalOnStorageEndWarning", + "UnsoundSpillInMutualRecursion", + "TransientStorageClearingHelperCollision", + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2025-05-07" }, + "0.8.31": { + "bugs": [ + "InheritanceOrderReversalOnStorageEndWarning", + "UnsoundSpillInMutualRecursion", + "TransientStorageClearingHelperCollision", + "LostStorageArrayWriteOnSlotOverflow" + ], + "released": "2025-12-03" + }, + "0.8.32": { + "bugs": [ + "InheritanceOrderReversalOnStorageEndWarning", + "UnsoundSpillInMutualRecursion", + "TransientStorageClearingHelperCollision" + ], + "released": "2025-12-18" + }, + "0.8.33": { + "bugs": [ + "InheritanceOrderReversalOnStorageEndWarning", + "UnsoundSpillInMutualRecursion", + "TransientStorageClearingHelperCollision" + ], + "released": "2025-12-18" + }, + "0.8.34": { + "bugs": [ + "InheritanceOrderReversalOnStorageEndWarning", + "UnsoundSpillInMutualRecursion" + ], + "released": "2026-02-18" + }, + "0.8.35": { + "bugs": [ + "InheritanceOrderReversalOnStorageEndWarning", + "UnsoundSpillInMutualRecursion" + ], + "released": "2026-04-29" + }, "0.8.4": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1941,6 +2133,8 @@ }, "0.8.5": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1954,6 +2148,8 @@ }, "0.8.6": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1967,6 +2163,8 @@ }, "0.8.7": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1980,6 +2178,8 @@ }, "0.8.8": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1994,6 +2194,8 @@ }, "0.8.9": { "bugs": [ + "UnsoundSpillInMutualRecursion", + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -2004,4 +2206,4 @@ ], "released": "2021-09-29" } -} \ No newline at end of file +} diff --git a/libsolidity/analysis/PostTypeContractLevelChecker.cpp b/libsolidity/analysis/PostTypeContractLevelChecker.cpp index c7b7b0961622..ae1246cd4141 100644 --- a/libsolidity/analysis/PostTypeContractLevelChecker.cpp +++ b/libsolidity/analysis/PostTypeContractLevelChecker.cpp @@ -30,6 +30,7 @@ #include #include +#include #include @@ -155,7 +156,7 @@ namespace VariableDeclaration const* findLastStorageVariable(ContractDefinition const& _contract) { - for (ContractDefinition const* baseContract: ranges::actions::reverse(_contract.annotation().linearizedBaseContracts)) + for (ContractDefinition const* baseContract: ranges::views::reverse(_contract.annotation().linearizedBaseContracts)) for (VariableDeclaration const* stateVariable: ranges::actions::reverse(baseContract->stateVariables())) if (stateVariable->referenceLocation() == VariableDeclaration::Location::Unspecified) return stateVariable; diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 455cfe0842ed..841161fa91b6 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -46,256 +46,65 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons // this copies source to target and also clears target if it was larger // need to leave "target_ref target_byte_off" on the stack at the end - // stack layout: [source_ref] [source length] target_ref (top) + // stack layout: source_ref [source length] target_ref (top) solAssert(_targetType.location() == DataLocation::Storage, ""); - Type const* targetBaseType = _targetType.baseType(); - Type const* sourceBaseType = _sourceType.baseType(); - // TODO unroll loop for small sizes - bool sourceIsStorage = _sourceType.location() == DataLocation::Storage; bool fromCalldata = _sourceType.location() == DataLocation::CallData; - bool directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType; - bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->storageBytes() <= 16; - bool haveByteOffsetTarget = !directCopy && targetBaseType->storageBytes() <= 16; - unsigned byteOffsetSize = (haveByteOffsetSource ? 1u : 0u) + (haveByteOffsetTarget ? 1u : 0u); + bool haveSourceLengthOnStack = fromCalldata && _sourceType.isDynamicallySized(); - // stack: source_ref [source_length] target_ref - // store target_ref for (unsigned i = _sourceType.sizeOnStack(); i > 0; --i) m_context << swapInstruction(i); // stack: target_ref source_ref [source_length] - if (_targetType.isByteArrayOrString()) + if (_sourceType.baseType()->category() == Type::Category::Array) { - // stack: target_ref source_ref [source_length] - if (fromCalldata && _sourceType.isDynamicallySized()) - { - // stack: target_ref source_ref source_length - m_context << Instruction::SWAP1; - // stack: target_ref source_length source_ref - m_context << Instruction::DUP3; - // stack: target_ref source_length source_ref target_ref - m_context.callYulFunction( - m_context.utilFunctions().copyByteArrayToStorageFunction(_sourceType, _targetType), - 3, - 0 - ); - } - else - { - // stack: target_ref source_ref - m_context << Instruction::DUP2; - // stack: target_ref source_ref target_ref - m_context.callYulFunction( - m_context.utilFunctions().copyByteArrayToStorageFunction(_sourceType, _targetType), - 2, - 0 - ); - } - // stack: target_ref - return; + // TODO: This limitation can now be removed since we use Yul utility functions that handle nested arrays correctly. + // The old inline assembly implementation couldn't handle nested calldata dynamic arrays, but the Yul functions + // support them through recursive calls. We keep this check temporarily for backward compatibility. + auto const& sourceBaseArrayType = dynamic_cast(*_sourceType.baseType()); + solUnimplementedAssert( + !fromCalldata || + !_sourceType.isDynamicallyEncoded() || + !sourceBaseArrayType.isDynamicallySized(), + "Copying nested calldata dynamic arrays to storage is not implemented in the old code generator." + ); } - - // retrieve source length - if (_sourceType.location() != DataLocation::CallData || !_sourceType.isDynamicallySized()) - retrieveLength(_sourceType); // otherwise, length is already there - if (_sourceType.location() == DataLocation::Memory && _sourceType.isDynamicallySized()) + else { - // increment source pointer to point to data - m_context << Instruction::SWAP1 << u256(0x20); - m_context << Instruction::ADD << Instruction::SWAP1; + // TODO: This limitation can now be removed since we use Yul utility functions that handle non-value types correctly. + // The old inline assembly implementation couldn't handle copying arrays of non-value types from memory or calldata to storage, + // but the Yul functions support them. We keep this check temporarily for backward compatibility. + bool fromMemoryOrCalldata = _sourceType.location() == DataLocation::Memory || _sourceType.location() == DataLocation::CallData; + solUnimplementedAssert( + _sourceType.baseType()->isValueType() || !fromMemoryOrCalldata, + "Copying of type " + _sourceType.toString(false) + " to storage is not supported in legacy (only supported by the IR pipeline). " + + "Hint: try compiling with `--via-ir` (CLI) or the equivalent `viaIR: true` (Standard JSON)." + ); } - // stack: target_ref source_ref source_length - Type const* targetType = &_targetType; - Type const* sourceType = &_sourceType; - m_context.callLowLevelFunction( - "$copyArrayToStorage_" + sourceType->identifier() + "_to_" + targetType->identifier(), - 3, - 1, - [=](CompilerContext& _context) - { - ArrayUtils utils(_context); - ArrayType const& _sourceType = dynamic_cast(*sourceType); - ArrayType const& _targetType = dynamic_cast(*targetType); - // stack: target_ref source_ref source_length - _context << Instruction::DUP3; - // stack: target_ref source_ref source_length target_ref - utils.retrieveLength(_targetType); - // stack: target_ref source_ref source_length target_ref target_length - if (_targetType.isDynamicallySized()) - { - // store new target length - solAssert(!_targetType.isByteArrayOrString()); - _context << Instruction::DUP3 << Instruction::DUP3 << Instruction::SSTORE; - } - if (sourceBaseType->category() == Type::Category::Mapping) - { - solAssert(targetBaseType->category() == Type::Category::Mapping, ""); - solAssert(_sourceType.location() == DataLocation::Storage, ""); - // nothing to copy - _context - << Instruction::POP << Instruction::POP - << Instruction::POP << Instruction::POP; - return; - } - // stack: target_ref source_ref source_length target_ref target_length - // compute hashes (data positions) - _context << Instruction::SWAP1; - if (_targetType.isDynamicallySized()) - CompilerUtils(_context).computeHashStatic(); - // stack: target_ref source_ref source_length target_length target_data_pos - _context << Instruction::SWAP1; - utils.convertLengthToSize(_targetType); - _context << Instruction::DUP2 << Instruction::ADD; - // stack: target_ref source_ref source_length target_data_pos target_data_end - _context << Instruction::SWAP3; - // stack: target_ref target_data_end source_length target_data_pos source_ref - - evmasm::AssemblyItem copyLoopEndWithoutByteOffset = _context.newTag(); - solAssert(!_targetType.isByteArrayOrString()); - // skip copying if source length is zero - _context << Instruction::DUP3 << Instruction::ISZERO; - _context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset); - - if (_sourceType.location() == DataLocation::Storage && _sourceType.isDynamicallySized()) - CompilerUtils(_context).computeHashStatic(); - // stack: target_ref target_data_end source_length target_data_pos source_data_pos - _context << Instruction::SWAP2; - utils.convertLengthToSize(_sourceType); - _context << Instruction::DUP3 << Instruction::ADD; - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end - if (haveByteOffsetTarget) - _context << u256(0); - if (haveByteOffsetSource) - _context << u256(0); - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - evmasm::AssemblyItem copyLoopStart = _context.newTag(); - _context << copyLoopStart; - // check for loop condition - _context - << dupInstruction(3 + byteOffsetSize) << dupInstruction(2 + byteOffsetSize) - << Instruction::GT << Instruction::ISZERO; - evmasm::AssemblyItem copyLoopEnd = _context.appendConditionalJump(); - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - // copy - if (sourceBaseType->category() == Type::Category::Array) - { - solAssert(byteOffsetSize == 0, "Byte offset for array as base type."); - auto const& sourceBaseArrayType = dynamic_cast(*sourceBaseType); - - solUnimplementedAssert( - _sourceType.location() != DataLocation::CallData || - !_sourceType.isDynamicallyEncoded() || - !sourceBaseArrayType.isDynamicallySized(), - "Copying nested calldata dynamic arrays to storage is not implemented in the old code generator." - ); - _context << Instruction::DUP3; - if (sourceBaseArrayType.location() == DataLocation::Memory) - _context << Instruction::MLOAD; - _context << Instruction::DUP3; - utils.copyArrayToStorage(dynamic_cast(*targetBaseType), sourceBaseArrayType); - _context << Instruction::POP; - } - else if (directCopy) - { - solAssert(byteOffsetSize == 0, "Byte offset for direct copy."); - _context - << Instruction::DUP3 << Instruction::SLOAD - << Instruction::DUP3 << Instruction::SSTORE; - } - else - { - // Note that we have to copy each element on its own in case conversion is involved. - // We might copy too much if there is padding at the last element, but this way end - // checking is easier. - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - _context << dupInstruction(3 + byteOffsetSize); - if (_sourceType.location() == DataLocation::Storage) - { - if (haveByteOffsetSource) - _context << Instruction::DUP2; - else - _context << u256(0); - StorageItem(_context, *sourceBaseType).retrieveValue(SourceLocation(), true); - } - else if (sourceBaseType->isValueType()) - CompilerUtils(_context).loadFromMemoryDynamic(*sourceBaseType, fromCalldata, true, false); - else - solUnimplemented("Copying of type " + _sourceType.toString(false) + " to storage is not supported in legacy (only supported by the IR pipeline). Hint: try compiling with `--via-ir` (CLI) or the equivalent `viaIR: true` (Standard JSON)"); - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] ... - assertThrow( - 2 + byteOffsetSize + sourceBaseType->sizeOnStack() <= 16, - StackTooDeepError, - util::stackTooDeepString - ); - // fetch target storage reference - _context << dupInstruction(2 + byteOffsetSize + sourceBaseType->sizeOnStack()); - if (haveByteOffsetTarget) - _context << dupInstruction(1 + byteOffsetSize + sourceBaseType->sizeOnStack()); - else - _context << u256(0); - StorageItem(_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true); - } - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - // increment source - if (haveByteOffsetSource) - utils.incrementByteOffset(sourceBaseType->storageBytes(), 1, haveByteOffsetTarget ? 5 : 4); - else - { - _context << swapInstruction(2 + byteOffsetSize); - if (sourceIsStorage) - _context << sourceBaseType->storageSize(); - else if (_sourceType.location() == DataLocation::Memory) - _context << sourceBaseType->memoryHeadSize(); - else - _context << sourceBaseType->calldataHeadSize(); - _context - << Instruction::ADD - << swapInstruction(2 + byteOffsetSize); - } - // increment target - if (haveByteOffsetTarget) - utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2); - else - _context - << swapInstruction(1 + byteOffsetSize) - << targetBaseType->storageSize() - << Instruction::ADD - << swapInstruction(1 + byteOffsetSize); - _context.appendJumpTo(copyLoopStart); - _context << copyLoopEnd; - if (haveByteOffsetTarget) - { - // clear elements that might be left over in the current slot in target - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end target_byte_offset [source_byte_offset] - _context << dupInstruction(byteOffsetSize) << Instruction::ISZERO; - evmasm::AssemblyItem copyCleanupLoopEnd = _context.appendConditionalJump(); - _context << dupInstruction(2 + byteOffsetSize) << dupInstruction(1 + byteOffsetSize); - StorageItem(_context, *targetBaseType).setToZero(SourceLocation(), true); - utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2); - _context.appendJumpTo(copyLoopEnd); - - _context << copyCleanupLoopEnd; - _context << Instruction::POP; // might pop the source, but then target is popped next - } - if (haveByteOffsetSource) - _context << Instruction::POP; - _context << copyLoopEndWithoutByteOffset; + if (haveSourceLengthOnStack) + { + // stack: target_ref source_ref source_length + m_context << Instruction::SWAP1; + // stack: target_ref source_length source_ref + m_context << Instruction::DUP3; + // stack: target_ref source_length source_ref target_ref + } + else + { + // stack: target_ref source_ref + m_context << Instruction::DUP2; + // stack: target_ref source_ref target_ref + } - // zero-out leftovers in target - // stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end - _context << Instruction::POP << Instruction::SWAP1 << Instruction::POP; - // stack: target_ref target_data_end target_data_pos_updated - if (targetBaseType->storageBytes() < 32) - utils.clearStorageLoop(TypeProvider::uint256()); - else - utils.clearStorageLoop(targetBaseType); - _context << Instruction::POP; - } + m_context.callYulFunction( + m_context.utilFunctions().copyArrayToStorageFunction(_sourceType, _targetType), + haveSourceLengthOnStack ? 3 : 2, + 0 ); + // stack: target_ref } void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWordBoundaries) const @@ -586,9 +395,9 @@ void ArrayUtils::clearArray(ArrayType const& _typeIn) const } else { - _context << Instruction::DUP1 << _type.length(); + _context << _type.length(); ArrayUtils(_context).convertLengthToSize(_type); - _context << Instruction::ADD << Instruction::SWAP1; + // stack: storage_ref slot_count if (_type.baseType()->storageBytes() < 32) ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256()); else @@ -623,13 +432,12 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const } // stack: ref old_length convertLengthToSize(_type); - // compute data positions + // stack: ref slot_count m_context << Instruction::SWAP1; CompilerUtils(m_context).computeHashStatic(); - // stack: len data_pos - m_context << Instruction::SWAP1 << Instruction::DUP2 << Instruction::ADD - << Instruction::SWAP1; - // stack: data_pos_end data_pos + // stack: slot_count data_pos + m_context << Instruction::SWAP1; + // stack: data_pos slot_count if (_type.storageStride() < 32) clearStorageLoop(TypeProvider::uint256()); else @@ -639,156 +447,6 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const m_context << Instruction::POP; } -void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const -{ - Type const* type = &_typeIn; - m_context.callLowLevelFunction( - "$resizeDynamicArray_" + _typeIn.identifier(), - 2, - 0, - [type](CompilerContext& _context) - { - ArrayType const& _type = dynamic_cast(*type); - solAssert(_type.location() == DataLocation::Storage, ""); - solAssert(_type.isDynamicallySized(), ""); - if (!_type.isByteArrayOrString() && _type.baseType()->storageBytes() < 32) - solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); - - unsigned stackHeightStart = _context.stackHeight(); - evmasm::AssemblyItem resizeEnd = _context.newTag(); - - // stack: ref new_length - // fetch old length - ArrayUtils(_context).retrieveLength(_type, 1); - // stack: ref new_length old_length - solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "2"); - - // Special case for short byte arrays, they are stored together with their length - if (_type.isByteArrayOrString()) - { - evmasm::AssemblyItem regularPath = _context.newTag(); - // We start by a large case-distinction about the old and new length of the byte array. - - _context << Instruction::DUP3 << Instruction::SLOAD; - // stack: ref new_length current_length ref_value - - solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - _context << Instruction::DUP2 << u256(31) << Instruction::LT; - evmasm::AssemblyItem currentIsLong = _context.appendConditionalJump(); - _context << Instruction::DUP3 << u256(31) << Instruction::LT; - evmasm::AssemblyItem newIsLong = _context.appendConditionalJump(); - - // Here: short -> short - - // Compute 1 << (256 - 8 * new_size) - evmasm::AssemblyItem shortToShort = _context.newTag(); - _context << shortToShort; - _context << Instruction::DUP3 << u256(8) << Instruction::MUL; - _context << u256(0x100) << Instruction::SUB; - _context << u256(2) << Instruction::EXP; - // Divide and multiply by that value, clearing bits. - _context << Instruction::DUP1 << Instruction::SWAP2; - _context << Instruction::DIV << Instruction::MUL; - // Insert 2*length. - _context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD; - _context << Instruction::OR; - // Store. - _context << Instruction::DUP4 << Instruction::SSTORE; - solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3"); - _context.appendJumpTo(resizeEnd); - - _context.adjustStackOffset(1); // we have to do that because of the jumps - // Here: short -> long - - _context << newIsLong; - // stack: ref new_length current_length ref_value - solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - // Zero out lower-order byte. - _context << u256(0xff) << Instruction::NOT << Instruction::AND; - // Store at data location. - _context << Instruction::DUP4; - CompilerUtils(_context).computeHashStatic(); - _context << Instruction::SSTORE; - // stack: ref new_length current_length - // Store new length: Compute 2*length + 1 and store it. - _context << Instruction::DUP2 << Instruction::DUP1 << Instruction::ADD; - _context << u256(1) << Instruction::ADD; - // stack: ref new_length current_length 2*new_length+1 - _context << Instruction::DUP4 << Instruction::SSTORE; - solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3"); - _context.appendJumpTo(resizeEnd); - - _context.adjustStackOffset(1); // we have to do that because of the jumps - - _context << currentIsLong; - _context << Instruction::DUP3 << u256(31) << Instruction::LT; - _context.appendConditionalJumpTo(regularPath); - - // Here: long -> short - // Read the first word of the data and store it on the stack. Clear the data location and - // then jump to the short -> short case. - - // stack: ref new_length current_length ref_value - solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - _context << Instruction::POP << Instruction::DUP3; - CompilerUtils(_context).computeHashStatic(); - _context << Instruction::DUP1 << Instruction::SLOAD << Instruction::SWAP1; - // stack: ref new_length current_length first_word data_location - _context << Instruction::DUP3; - ArrayUtils(_context).convertLengthToSize(_type); - _context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1; - // stack: ref new_length current_length first_word data_location_end data_location - ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256()); - _context << Instruction::POP; - // stack: ref new_length current_length first_word - solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - _context.appendJumpTo(shortToShort); - - _context << regularPath; - // stack: ref new_length current_length ref_value - _context << Instruction::POP; - } - - // Change of length for a regular array (i.e. length at location, data at KECCAK256(location)). - // stack: ref new_length old_length - // store new length - _context << Instruction::DUP2; - if (_type.isByteArrayOrString()) - // For a "long" byte array, store length as 2*length+1 - _context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD; - _context << Instruction::DUP4 << Instruction::SSTORE; - // skip if size is not reduced - _context << Instruction::DUP2 << Instruction::DUP2 - << Instruction::GT << Instruction::ISZERO; - _context.appendConditionalJumpTo(resizeEnd); - - // size reduced, clear the end of the array - // stack: ref new_length old_length - ArrayUtils(_context).convertLengthToSize(_type); - _context << Instruction::DUP2; - ArrayUtils(_context).convertLengthToSize(_type); - // stack: ref new_length old_size new_size - // compute data positions - _context << Instruction::DUP4; - CompilerUtils(_context).computeHashStatic(); - // stack: ref new_length old_size new_size data_pos - _context << Instruction::SWAP2 << Instruction::DUP3 << Instruction::ADD; - // stack: ref new_length data_pos new_size delete_end - _context << Instruction::SWAP2 << Instruction::ADD; - // stack: ref new_length delete_end delete_start - if (_type.storageStride() < 32) - ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256()); - else - ArrayUtils(_context).clearStorageLoop(_type.baseType()); - - _context << resizeEnd; - // cleanup - _context << Instruction::POP << Instruction::POP << Instruction::POP; - solAssert(_context.stackHeight() == stackHeightStart - 2, ""); - } - ); -} - void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const { solAssert(_type.location() == DataLocation::Storage, ""); @@ -936,29 +594,32 @@ void ArrayUtils::clearStorageLoop(Type const* _type) const _context << Instruction::POP; return; } - // stack: end_pos pos - + // stack: start_pos slot_count + // Initialize loop counter i = 0 + _context << u256(0); + // stack: start_pos slot_count i evmasm::AssemblyItem loopStart = _context.appendJumpToNew(); _context << loopStart; - // check for loop condition - _context << - Instruction::DUP1 << - Instruction::DUP3 << - Instruction::GT << - Instruction::ISZERO; + // check for loop condition: !(slot_count > i) = (i >= slot_count) + _context << Instruction::DUP1 << Instruction::DUP3 << Instruction::GT << Instruction::ISZERO; evmasm::AssemblyItem zeroLoopEnd = _context.newTag(); _context.appendConditionalJumpTo(zeroLoopEnd); - // delete + // stack: start_pos slot_count i + // compute storage position: start_pos + i + _context << Instruction::DUP3 << Instruction::DUP2 << Instruction::ADD; + // stack: start_pos slot_count i (start_pos+i) + // delete storage slot _context << u256(0); - StorageItem(_context, *_type).setToZero(SourceLocation(), false); - _context << Instruction::POP; - // increment + StorageItem(_context, *_type).setToZero(SourceLocation(), /* _removeReference = */ true); + // stack: start_pos slot_count i + // increment counter: i += storageSize _context << _type->storageSize() << Instruction::ADD; _context.appendJumpTo(loopStart); // cleanup _context << zeroLoopEnd; - _context << Instruction::POP; - + // stack: start_pos slot_count i + _context << Instruction::POP << Instruction::POP; + // stack: start_pos solAssert(_context.stackHeight() == stackHeightStart - 1, ""); } ); diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index 326084cb63a8..a62df8a7b2ab 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -61,10 +61,6 @@ class ArrayUtils /// Stack pre: reference (excludes byte offset) /// Stack post: void clearDynamicArray(ArrayType const& _type) const; - /// Changes the size of a dynamic array and clears the tail if it is shortened. - /// Stack pre: reference (excludes byte offset) new_length - /// Stack post: - void resizeDynamicArray(ArrayType const& _type) const; /// Increments the size of a dynamic array by one. /// Does not touch the new data element. In case of a byte array, this might move the /// data. @@ -76,9 +72,9 @@ class ArrayUtils /// Stack pre: reference /// Stack post: void popStorageArrayElement(ArrayType const& _type) const; - /// Appends a loop that clears a sequence of storage slots of the given type (excluding end). - /// Stack pre: end_ref start_ref - /// Stack post: end_ref + /// Appends a loop that clears a sequence of storage slots of the given type. + /// Stack pre: start_ref slot_count + /// Stack post: start_ref void clearStorageLoop(Type const* _type) const; /// Converts length to size (number of storage slots or calldata/memory bytes). /// if @a _pad then add padding to multiples of 32 bytes for calldata/memory. diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index abb2fdd100aa..4c3cd8c0fc69 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1419,14 +1419,13 @@ std::string YulUtilFunctions::cleanUpStorageArrayEndFunction(ArrayType const& _t let newSlotCount := (startIndex) let arrayDataStart := (array) let deleteStart := add(arrayDataStart, newSlotCount) - let deleteEnd := add(arrayDataStart, oldSlotCount) // if we are dealing with packed array and offset is greater than zero // we have to partially clear last slot that is still used, so decreasing start by one let offset := mul(mod(startIndex, ), ) if gt(offset, 0) { (sub(deleteStart, 1), offset) } - (deleteStart, deleteEnd) + (deleteStart, sub(oldSlotCount, newSlotCount)) } )") ("convertToSize", arrayConvertLengthToSize(_type)) @@ -1474,11 +1473,17 @@ std::string YulUtilFunctions::cleanUpDynamicByteArrayEndSlotsFunction(ArrayType _args = {"array", "len", "startIndex"}; return Whiskers(R"( if gt(len, 31) { - let dataArea := (array) - let deleteStart := add(dataArea, (startIndex)) - // If we are clearing array to be short byte array, we want to clear only data starting from array data area. - if lt(startIndex, 32) { deleteStart := dataArea } - (deleteStart, add(dataArea, (len))) + if gt(len, startIndex) { + let dataArea := (array) + let oldSlotCount := (len) + let newSlotCount := (startIndex) + // If we are clearing array to be short byte array, we want to clear only data starting from array data area. + if lt(startIndex, 32) { + newSlotCount := 0 + } + let deleteStart := add(dataArea, newSlotCount) + (deleteStart, sub(oldSlotCount, newSlotCount)) + } } )") ("dataLocation", arrayDataAreaFunction(_type)) @@ -1496,14 +1501,21 @@ std::string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _ty function (array, data, oldLen, newLen) { switch lt(newLen, 32) case 0 { + // NOTE: This branch (newLen >= 32) is currently unreachable from Solidity code. + // All delete operations use newLen=0, and assignment uses copyByteArrayToStorageFunction. + // However, we maintain correct logic here for future-proofing and consistency. + let newSlots := (newLen) + let oldSlots := (oldLen) let arrayDataStart := (array) - let deleteStart := add(arrayDataStart, (newLen)) + let deleteStart := add(arrayDataStart, newSlots) // we have to partially clear last slot that is still used let offset := and(newLen, 0x1f) if offset { (sub(deleteStart, 1), offset) } - (deleteStart, add(arrayDataStart, (oldLen))) + if gt(oldSlots, newSlots) { + (deleteStart, sub(oldSlots, newSlots)) + } sstore(array, or(mul(2, newLen), 1)) } @@ -1511,8 +1523,9 @@ std::string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _ty switch gt(oldLen, 31) case 1 { let arrayDataStart := (array) - // clear whole old array, as we are transforming to short bytes array - (add(arrayDataStart, 1), add(arrayDataStart, (oldLen))) + // Clear whole old array, as we are transforming to short bytes array. + // () will clear the first slot after copying its content. + (add(arrayDataStart, 1), sub((oldLen), 1)) (array, newLen) } default { @@ -1826,10 +1839,10 @@ std::string YulUtilFunctions::clearStorageRangeFunction(Type const& _type) return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( - function (start, end) { - for {} lt(start, end) { start := add(start, ) } + function (startSlot, slotCount) { + for { let i := 0 } lt(i, slotCount) { i := add(i, ) } { - (start, 0) + (add(startSlot, i), 0) } } )") @@ -1861,7 +1874,7 @@ std::string YulUtilFunctions::clearStorageArrayFunction(ArrayType const& _type) (slot, 0) - (slot, add(slot, ())) + (slot, ()) } )") @@ -4348,7 +4361,22 @@ std::string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFu std::string YulUtilFunctions::storageSetToZeroFunction(Type const& _type, VariableDeclaration::Location _location) { - std::string const functionName = "storage_set_to_zero_" + _type.identifier(); + solAssert( + _location == VariableDeclaration::Location::Transient || + _location == VariableDeclaration::Location::Unspecified, + "Invalid location for the storage_set_to_zero function" + ); + + if (dynamic_cast(&_type)) + solAssert( + _location == VariableDeclaration::Location::Unspecified && + _type.dataStoredIn(DataLocation::Storage) + ); + + std::string const functionName = + (_location == VariableDeclaration::Location::Transient ? "transient_"s : "") + + "storage_set_to_zero_" + + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { if (_type.isValueType()) diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index c28ba855c816..f4172017b44e 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -266,8 +266,11 @@ class YulUtilFunctions std::string storageArrayPushZeroFunction(ArrayType const& _type); /// @returns the name of a function that will clear the storage area given - /// by the start and end (exclusive) parameters (slots). - /// signature: (start, end) + /// by the start position and number of slots to clear. The start position is in terms of storage slots and we + /// assume that the beginning of the clear range starts at the beginning of the start slot. + /// `slot_count` is assumed to be a multiple of `_type.storageSize()`. The function clears storage in increments + /// of `_type.storageSize()` and does not perform any runtime checks. + /// signature: (start_slot, slot_count) std::string clearStorageRangeFunction(Type const& _type); /// @returns the name of a function that will clear the given storage array @@ -297,7 +300,7 @@ class YulUtilFunctions /// The function reverts for too large lengths. std::string arrayAllocationSizeFunction(ArrayType const& _type); - /// @returns the name of a function that converts a storage slot number + /// @returns the name of a function that converts a storage slot number, /// a memory pointer or a calldata pointer to the slot number / memory pointer / calldata pointer /// for the data position of an array which is stored in that slot / memory area / calldata area. std::string arrayDataAreaFunction(ArrayType const& _type); @@ -618,7 +621,7 @@ class YulUtilFunctions std::string cleanUpDynamicByteArrayEndSlotsFunction(ArrayType const& _type); /// @returns the name of a function that increases size of byte array - /// when we resize byte array frextractUsedSetLenom < 32 elements to >= 32 elements or we push to byte array of size 31 copying of data will occur + /// when we resize byte array from < 32 elements to >= 32 elements or we push to byte array of size 31 copying of data will occur /// signature: (array, data, oldLen, newLen) std::string increaseByteArraySizeFunction(ArrayType const& _type); diff --git a/libsolutil/CMakeLists.txt b/libsolutil/CMakeLists.txt index 1ec12051a619..69c53cf9c086 100644 --- a/libsolutil/CMakeLists.txt +++ b/libsolutil/CMakeLists.txt @@ -35,6 +35,7 @@ set(sources StringUtils.h SwarmHash.cpp SwarmHash.h + TarjanSCC.h TemporaryDirectory.cpp TemporaryDirectory.h UTF8.cpp diff --git a/libsolutil/TarjanSCC.h b/libsolutil/TarjanSCC.h new file mode 100644 index 000000000000..a86dcb899c7d --- /dev/null +++ b/libsolutil/TarjanSCC.h @@ -0,0 +1,155 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace solidity::util +{ + +namespace detail +{ + +/// Callers must supply a dense `[0, N)` remapping; this struct conflates the node's +/// identity with its position in `adjacency` for performance. +template +struct TarjanSCC +{ + static constexpr std::size_t undefined = std::numeric_limits::max(); + + struct Frame + { + NodeIndex node; + std::size_t childIdx; + }; + + std::vector> const& adjacency; + /// numbers the nodes consecutively in the order in which they are discovered + std::vector discoveryIndex; + /// lowlink[v] corresponds to the smallest index of a node reachable through v's DFS subtree + std::vector lowlink; + std::vector onStack; + std::vector nodeStack; + std::vector workStack; + std::size_t nextIndex = 0; + std::vector> sccs; + + explicit TarjanSCC(std::vector> const& _adjacency): + adjacency(_adjacency), + discoveryIndex(_adjacency.size(), undefined), + lowlink(_adjacency.size(), 0), + onStack(_adjacency.size(), false) + {} + + void enter(NodeIndex const _v) + { + solAssert(_v < adjacency.size()); + discoveryIndex[_v] = nextIndex; + lowlink[_v] = nextIndex; + ++nextIndex; + nodeStack.push_back(_v); + onStack[_v] = true; + workStack.push_back({_v, 0}); + } + + void emitSCC(NodeIndex const _root) + { + solAssert(!nodeStack.empty()); + std::vector scc; + NodeIndex w; + do + { + w = nodeStack.back(); + nodeStack.pop_back(); + onStack[w] = false; + scc.push_back(w); + } + while (w != _root); + sccs.push_back(std::move(scc)); + } + + std::vector> run() && + { + for (NodeIndex root = 0; root < discoveryIndex.size(); ++root) + { + if (discoveryIndex[root] != undefined) + continue; + + // start new DFS tree + enter(root); + while (!workStack.empty()) + { + NodeIndex const v = workStack.back().node; + std::size_t const childIdx = workStack.back().childIdx; + if (childIdx < adjacency[v].size()) + { + // process next outgoing edge + NodeIndex const w = adjacency[v][childIdx]; + // advance v's currently handled edge before pushing w, so v resumes at the next child when w finishes + workStack.back().childIdx = childIdx + 1; + if (discoveryIndex[w] == undefined) + // Successor w has not yet been visited; recurse on it + enter(w); + else if (onStack[w]) + // Successor w is in stack S and hence in the current SCC + lowlink[v] = std::min(lowlink[v], discoveryIndex[w]); + } + else + { + // if v is a root node + if (lowlink[v] == discoveryIndex[v]) + // generate an SCC and pop the node stack + emitSCC(v); + + // pop v and propagate its lowlink to the parent (post-recursion update) + workStack.pop_back(); + if (!workStack.empty()) + { + NodeIndex const parent = workStack.back().node; + lowlink[parent] = std::min(lowlink[parent], lowlink[v]); + } + } + } + } + return std::move(sccs); + } +}; + +} + +/// Tarjan's strongly-connected-components algorithm. +/// Takes an adjacency list where `_adjacency[v]` is the list of successors of node `v`. Node IDs must lie in `[0, _adjacency.size())`. +/// Implementation based on the Wikipedia pseudocode. +/// +/// Wikipedia contributors, "Tarjan's strongly connected components algorithm," Wikipedia, The Free Encyclopedia, +/// https://en.wikipedia.org/w/index.php?title=Tarjan%27s_strongly_connected_components_algorithm&oldid=1341352351 +/// Tarjan, R.E., "Depth-First Search and Linear Graph Algorithms", https://doi.org/10.1137/0201010 +template +std::vector> computeStronglyConnectedComponents(std::vector> const& _adjacency) +{ + return detail::TarjanSCC(_adjacency).run(); +} + +} diff --git a/libyul/Utilities.cpp b/libyul/Utilities.cpp index e7caf1b2dff4..5c87cb75a228 100644 --- a/libyul/Utilities.cpp +++ b/libyul/Utilities.cpp @@ -251,6 +251,15 @@ std::string_view yul::resolveFunctionName(FunctionName const& _functionName, Dia return std::visit(visitor, _functionName); } +std::string_view yul::resolveFunctionName(FunctionHandle const& _functionHandle, Dialect const& _dialect) +{ + GenericVisitor visitor{ + [&](YulName const& _name) -> std::string const& { return _name.str(); }, + [&](BuiltinHandle const& _handle) -> std::string const& { return _dialect.builtin(_handle).name; } + }; + return std::visit(visitor, _functionHandle); +} + BuiltinFunction const* yul::resolveBuiltinFunction(FunctionName const& _functionName, Dialect const& _dialect) { GenericVisitor visitor{ diff --git a/libyul/Utilities.h b/libyul/Utilities.h index 64b5f456ac1f..f3def59ab4f5 100644 --- a/libyul/Utilities.h +++ b/libyul/Utilities.h @@ -89,6 +89,7 @@ struct SwitchCaseCompareByLiteralValue }; std::string_view resolveFunctionName(FunctionName const& _functionName, Dialect const& _dialect); +std::string_view resolveFunctionName(FunctionHandle const& _functionHandle, Dialect const& _dialect); BuiltinFunction const* resolveBuiltinFunction(FunctionName const& _functionName, Dialect const& _dialect); BuiltinFunctionForEVM const* resolveBuiltinFunctionForEVM(FunctionName const& _functionName, EVMDialect const& _dialect); diff --git a/libyul/optimiser/CallGraphGenerator.cpp b/libyul/optimiser/CallGraphGenerator.cpp index 9b69835dc4f1..15a31d05c7f6 100644 --- a/libyul/optimiser/CallGraphGenerator.cpp +++ b/libyul/optimiser/CallGraphGenerator.cpp @@ -22,10 +22,17 @@ #include #include +#include + #include +#include #include -#include +#include +#include +#include + +#include using namespace solidity; using namespace solidity::yul; @@ -33,43 +40,82 @@ using namespace solidity::util; namespace { -// TODO: This algorithm is non-optimal. -struct CallGraphCycleFinder -{ - CallGraph const& callGraph; - std::set containedInCycle{}; - std::set visited{}; - std::vector currentPath{}; - void visit(FunctionHandle const& _function) +class FunctionToIndexBiMapping +{ +public: + explicit FunctionToIndexBiMapping(std::map> const& _functionCalls) { - if (visited.count(_function)) - return; - if ( - auto it = find(currentPath.begin(), currentPath.end(), _function); - it != currentPath.end() - ) - containedInCycle.insert(it, currentPath.end()); - else + for (auto const& [function, callees]: _functionCalls) { - currentPath.emplace_back(_function); - if (callGraph.functionCalls.count(_function)) - for (auto const& child: callGraph.functionCalls.at(_function)) - visit(child); - currentPath.pop_back(); - visited.insert(_function); + if (m_functionToIndex.try_emplace(function, m_indexToFunction.size()).second) + m_indexToFunction.emplace_back(function); + for (auto const& callee: callees) + if (m_functionToIndex.try_emplace(callee, m_indexToFunction.size()).second) + m_indexToFunction.emplace_back(callee); } } + + FunctionHandle const& indexToFunction(std::size_t const _index) const + { + return m_indexToFunction.at(_index); + } + + std::size_t numFunctions() const + { + return m_indexToFunction.size(); + } + + std::size_t functionToIndex(FunctionHandle const& _functionHandle) const + { + return m_functionToIndex.at(_functionHandle); + } + +private: + std::vector m_indexToFunction; + std::map m_functionToIndex; }; + } std::set CallGraph::recursiveFunctions() const { - CallGraphCycleFinder cycleFinder{*this}; - // Visiting the root only is not enough, since there may be disconnected recursive functions. - for (auto const& call: functionCalls) - cycleFinder.visit(call.first); - return cycleFinder.containedInCycle; + // A function is recursive iff it is part of a non-trivial strongly-connected component of the call graph (a + // mutual-recursion cycle of any length) or it directly calls itself. The SCCs are computed with Tarjan's algorithm. + // Tarjan's implementation requires dense node indices in [0, N), so assign each function handle + // (both callers and callees) a consecutive index. + FunctionToIndexBiMapping const functionIndexBimap(functionCalls); + + // Build list of edges in the call graph + std::vector> indexBasedAdjacencyList(functionIndexBimap.numFunctions()); + for (auto const& [function, callees]: functionCalls) + for (auto const& callee: callees) + indexBasedAdjacencyList[functionIndexBimap.functionToIndex(function)].emplace_back(functionIndexBimap.functionToIndex(callee)); + + // Sort and deduplicate each adjacency list so the self-loop check below can use a binary search + for (auto& callees: indexBasedAdjacencyList) + { + ranges::sort(callees); + callees.erase(ranges::unique(callees), callees.end()); + } + + std::set recursiveFunctionHandleSet; + for (std::vector const& scc: util::computeStronglyConnectedComponents(indexBasedAdjacencyList)) + { + yulAssert(!scc.empty()); + if (scc.size() > 1) + // more than one element in the SCC: everything in it is mutually recursive + for (std::size_t const node: scc) + recursiveFunctionHandleSet.insert(functionIndexBimap.indexToFunction(node)); + else if (ranges::binary_search(indexBasedAdjacencyList[scc.front()], scc.front())) + // self-recursion f -> f + recursiveFunctionHandleSet.insert(functionIndexBimap.indexToFunction(scc.front())); + } + + yulAssert(!recursiveFunctionHandleSet.contains(YulName{}), "the top-level block cannot be recursive"); + for (FunctionHandle const& recursiveFunction: recursiveFunctionHandleSet) + yulAssert(std::holds_alternative(recursiveFunction), "a builtin cannot be recursive"); + return recursiveFunctionHandleSet; } CallGraph CallGraphGenerator::callGraph(Block const& _ast) @@ -99,9 +145,13 @@ void CallGraphGenerator::operator()(ForLoop const& _forLoop) void CallGraphGenerator::operator()(FunctionDefinition const& _functionDefinition) { + solRequire( + !m_callGraph.functionCalls.contains(_functionDefinition.name), + InputNotDisambiguatedException, + "CallGraphGenerator requires a disambiguated AST: duplicate function name " + _functionDefinition.name.str() + "." + ); YulName previousFunction = m_currentFunction; m_currentFunction = _functionDefinition.name; - yulAssert(m_callGraph.functionCalls.count(m_currentFunction) == 0, ""); m_callGraph.functionCalls[m_currentFunction] = {}; ASTWalker::operator()(_functionDefinition); m_currentFunction = previousFunction; diff --git a/libyul/optimiser/CallGraphGenerator.h b/libyul/optimiser/CallGraphGenerator.h index 722cbba4a796..f89e5a04cafa 100644 --- a/libyul/optimiser/CallGraphGenerator.h +++ b/libyul/optimiser/CallGraphGenerator.h @@ -17,6 +17,8 @@ // SPDX-License-Identifier: GPL-3.0 /** * Specific AST walker that generates the call graph. + * + * Prerequisites: Disambiguator */ #pragma once @@ -38,6 +40,9 @@ struct CallGraph /// @returns the set of functions contained in cycles in the call graph, i.e. /// functions that are part of a (mutual) recursion. /// Note that this does not include functions that merely call recursive functions. + /// Postcondition: the result never contains a builtin. At Yul level builtins cannot call other + /// functions, so they have no outgoing call-graph edges and can be neither mutually nor directly + /// recursive. std::set recursiveFunctions() const; }; @@ -47,10 +52,14 @@ struct CallGraph * It also generates information about which functions contain for loops. * * The outermost (non-function) context is denoted by the empty string. + * + * @throws InputNotDisambiguatedException if input is not disambiguated */ class CallGraphGenerator: public ASTWalker { public: + struct InputNotDisambiguatedException: virtual YulException {}; + static CallGraph callGraph(Block const& _ast); using ASTWalker::operator(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 211606363bc4..dbbcf3eaec81 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -43,6 +43,7 @@ set(libsolutil_sources libsolutil/LEB128.cpp libsolutil/StringUtils.cpp libsolutil/SwarmHash.cpp + libsolutil/TarjanSCC.cpp libsolutil/TemporaryDirectoryTest.cpp libsolutil/UTF8.cpp libsolutil/Whiskers.cpp @@ -143,6 +144,8 @@ detect_stray_source_files("${libsolidity_util_sources}" "libsolidity/util/") set(libyul_sources libyul/Common.cpp libyul/Common.h + libyul/CallGraphTest.cpp + libyul/CallGraphTest.h libyul/CompilabilityChecker.cpp libyul/ControlFlowGraphTest.cpp libyul/ControlFlowGraphTest.h diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h index 107a6aae0bef..d53406ef693b 100644 --- a/test/InteractiveTests.h +++ b/test/InteractiveTests.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,7 @@ Testsuite const g_interactiveTestsuites[] = { {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, {"Yul Control Flow Graph", "libyul", "yulControlFlowGraph", false, false, &yul::test::ControlFlowGraphTest::create}, {"Yul SSA Control Flow Graph", "libyul", "yulSSAControlFlowGraph", false, false, &yul::test::SSAControlFlowGraphTest::create}, + {"Yul Call Graph", "libyul", "yulCallGraph", false, false, &yul::test::CallGraphTest::create}, {"Yul Stack Layout", "libyul", "yulStackLayout", false, false, &yul::test::StackLayoutGeneratorTest::create}, {"Yul Stack Shuffling", "libyul", "yulStackShuffling", false, false, &yul::test::StackShufflingTest::create}, {"Control Flow Side Effects", "libyul", "controlFlowSideEffects", false, false, &yul::test::ControlFlowSideEffectsTest::create}, diff --git a/test/cmdlineTests/standard_via_ir_intersecting_recursive_cycles_stack_too_deep/in.sol b/test/cmdlineTests/standard_via_ir_intersecting_recursive_cycles_stack_too_deep/in.sol new file mode 100644 index 000000000000..3e2f0619cf8e --- /dev/null +++ b/test/cmdlineTests/standard_via_ir_intersecting_recursive_cycles_stack_too_deep/in.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity *; + +// Regression test for stack-to-memory spilling miscompilation SOL-2026-2 caused by the call graph cycle detection +// misclassifying functions in intersecting recursive cycles. +// +// Call graph: a -> {b, c}, b -> a, c -> b +// so {a, b, c} is a single strongly-connected component and all three functions are recursive, spilling is unsound. +// The stack limit evader must refuse to spill here. +contract C { + uint256[26] public seed; + + function trigger() external returns (uint256 storedAt3) { + a(3); + storedAt3 = seed[3]; + } + + function a(uint256 m) internal { + if (m == 0) return; + b(m); + c(m); + } + function b(uint256 m) internal { + if (m == 0) return; + a(m - 1); + } + function c(uint256 m) internal { + if (m == 0) return; + uint256 v1 = seed[0] ^ m; + uint256 v2 = seed[1] ^ m; + uint256 v3 = seed[2] ^ m; + uint256 v4 = seed[3] ^ m; + uint256 v5 = seed[4] ^ m; + uint256 v6 = seed[5] ^ m; + uint256 v7 = seed[6] ^ m; + uint256 v8 = seed[7] ^ m; + uint256 v9 = seed[8] ^ m; + uint256 v10 = seed[9] ^ m; + uint256 v11 = seed[10] ^ m; + uint256 v12 = seed[11] ^ m; + uint256 v13 = seed[12] ^ m; + uint256 v14 = seed[13] ^ m; + uint256 v15 = seed[14] ^ m; + uint256 v16 = seed[15] ^ m; + uint256 v17 = seed[16] ^ m; + uint256 v18 = seed[17] ^ m; + uint256 v19 = seed[18] ^ m; + uint256 v20 = seed[19] ^ m; + uint256 v21 = seed[20] ^ m; + uint256 v22 = seed[21] ^ m; + uint256 v23 = seed[22] ^ m; + uint256 v24 = seed[23] ^ m; + uint256 v25 = seed[24] ^ m; + + b(m - 1); + + // Read m back, provoking a stack too deep situation + seed[m] = m; + // Keep v1..v25 alive across the b(m - 1) call + seed[25] = + v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + + v11 + v12 + v13 + v14 + v15 + v16 + v17 + v18 + v19 + v20 + + v21 + v22 + v23 + v24 + v25; + } +} diff --git a/test/cmdlineTests/standard_via_ir_intersecting_recursive_cycles_stack_too_deep/input.json b/test/cmdlineTests/standard_via_ir_intersecting_recursive_cycles_stack_too_deep/input.json new file mode 100644 index 000000000000..dc86ac68494e --- /dev/null +++ b/test/cmdlineTests/standard_via_ir_intersecting_recursive_cycles_stack_too_deep/input.json @@ -0,0 +1,12 @@ +{ + "language": "Solidity", + "sources": { + "in.sol": {"urls": ["in.sol"]} + }, + "settings": { + "viaIR": true, + "outputSelection": { + "*": {"*": ["evm.bytecode"]} + } + } +} diff --git a/test/cmdlineTests/standard_via_ir_intersecting_recursive_cycles_stack_too_deep/output.json b/test/cmdlineTests/standard_via_ir_intersecting_recursive_cycles_stack_too_deep/output.json new file mode 100644 index 000000000000..6650da378c61 --- /dev/null +++ b/test/cmdlineTests/standard_via_ir_intersecting_recursive_cycles_stack_too_deep/output.json @@ -0,0 +1,29 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "YulException: Cannot swap Variable var_m with Variable expr_70: too deep in the stack by 1 slots in [ RET var_m var_m var_m var_m var_m var_m var_m var_m var_m expr_66 var_v15 var_v14 var_v13 var_v12 var_v11 var_v10 var_v9 var_v8 var_v7 var_v6 var_v5 var_v4 var_v3 var_v2 var_v1 expr_70 ] +memoryguard was present. + --> in.sol:46:23: + | +46 | uint256 v18 = seed[17] ^ m; + | ^^^^ + +", + "message": "Cannot swap Variable var_m with Variable expr_70: too deep in the stack by 1 slots in [ RET var_m var_m var_m var_m var_m var_m var_m var_m var_m expr_66 var_v15 var_v14 var_v13 var_v12 var_v11 var_v10 var_v9 var_v8 var_v7 var_v6 var_v5 var_v4 var_v3 var_v2 var_v1 expr_70 ] +memoryguard was present.", + "severity": "error", + "sourceLocation": { + "end": 1488, + "file": "in.sol", + "start": 1484 + }, + "type": "YulException" + } + ], + "sources": { + "in.sol": { + "id": 0 + } + } +} diff --git a/test/cmdlineTests/storage_layout_specifier_with_inheritance/args b/test/cmdlineTests/storage_layout_specifier_with_inheritance/args new file mode 100644 index 000000000000..23f63dd3b141 --- /dev/null +++ b/test/cmdlineTests/storage_layout_specifier_with_inheritance/args @@ -0,0 +1 @@ +--storage-layout --pretty-json --json-indent 4 - diff --git a/test/cmdlineTests/storage_layout_specifier_with_inheritance/err b/test/cmdlineTests/storage_layout_specifier_with_inheritance/err new file mode 100644 index 000000000000..12e97c1e44f3 --- /dev/null +++ b/test/cmdlineTests/storage_layout_specifier_with_inheritance/err @@ -0,0 +1,10 @@ +Warning: This contract is very close to the end of storage. This limits its future upgradability. + --> :6:17: + | +6 | contract C is B layout at 2**256 - 2**40 { uint z; } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +Note: There are 0xffFFFFfffc storage slots between this state variable and the end of storage. + --> :4:14: + | +4 | contract A { uint x; } + | ^^^^^^ diff --git a/test/cmdlineTests/storage_layout_specifier_with_inheritance/output b/test/cmdlineTests/storage_layout_specifier_with_inheritance/output new file mode 100644 index 000000000000..b9fc73291a08 --- /dev/null +++ b/test/cmdlineTests/storage_layout_specifier_with_inheritance/output @@ -0,0 +1,90 @@ + +======= :A ======= +Contract Storage Layout: +{ + "storage": [ + { + "astId": 3, + "contract": ":A", + "label": "x", + "offset": 0, + "slot": "0", + "type": "t_uint256" + } + ], + "types": { + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } +} + +======= :B ======= +Contract Storage Layout: +{ + "storage": [ + { + "astId": 3, + "contract": ":B", + "label": "x", + "offset": 0, + "slot": "0", + "type": "t_uint256" + }, + { + "astId": 8, + "contract": ":B", + "label": "y", + "offset": 0, + "slot": "1", + "type": "t_uint256" + } + ], + "types": { + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } +} + +======= :C ======= +Contract Storage Layout: +{ + "storage": [ + { + "astId": 3, + "contract": ":C", + "label": "x", + "offset": 0, + "slot": "115792089237316195423570985008687907853269984665640564039457584006813618012160", + "type": "t_uint256" + }, + { + "astId": 8, + "contract": ":C", + "label": "y", + "offset": 0, + "slot": "115792089237316195423570985008687907853269984665640564039457584006813618012161", + "type": "t_uint256" + }, + { + "astId": 21, + "contract": ":C", + "label": "z", + "offset": 0, + "slot": "115792089237316195423570985008687907853269984665640564039457584006813618012162", + "type": "t_uint256" + } + ], + "types": { + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } +} diff --git a/test/cmdlineTests/storage_layout_specifier_with_inheritance/stdin b/test/cmdlineTests/storage_layout_specifier_with_inheritance/stdin new file mode 100644 index 000000000000..5d7bb66b6b79 --- /dev/null +++ b/test/cmdlineTests/storage_layout_specifier_with_inheritance/stdin @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; + +contract A { uint x; } +contract B is A { uint y; } +contract C is B layout at 2**256 - 2**40 { uint z; } diff --git a/test/cmdlineTests/storage_transient_storage_collision_ir_output/args b/test/cmdlineTests/storage_transient_storage_collision_ir_output/args new file mode 100644 index 000000000000..1b58291152e4 --- /dev/null +++ b/test/cmdlineTests/storage_transient_storage_collision_ir_output/args @@ -0,0 +1 @@ +--ir-optimized --evm-version cancun --debug-info none --no-cbor-metadata diff --git a/test/cmdlineTests/storage_transient_storage_collision_ir_output/input.sol b/test/cmdlineTests/storage_transient_storage_collision_ir_output/input.sol new file mode 100644 index 000000000000..e7d3930152fa --- /dev/null +++ b/test/cmdlineTests/storage_transient_storage_collision_ir_output/input.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 +pragma solidity >=0.0; + +contract C { + uint256 transient varTransient; + uint256 public varStorage = 0xeeeeeeeeee; + + function foo() external returns (uint256) { + varTransient = 0xffffffff; + delete varTransient; + delete varStorage; + + return varStorage; + } +} diff --git a/test/cmdlineTests/storage_transient_storage_collision_ir_output/output b/test/cmdlineTests/storage_transient_storage_collision_ir_output/output new file mode 100644 index 000000000000..50f2fcb90fc7 --- /dev/null +++ b/test/cmdlineTests/storage_transient_storage_collision_ir_output/output @@ -0,0 +1,252 @@ +Optimized IR: +/// @use-src 0:"input.sol" +object "C_25" { + code { + { + mstore(64, memoryguard(0x80)) + if callvalue() + { + revert_error_8061d93638ee074115f764ac3d48f9e554bb1b90dfef10edac8a99bf7cd9567b() + } + if calltokenid() + { + revert_error_9a62d7e684059bba6a84639281e9dae3baecf10b1b86b3ab661f6dcc72bcade3() + } + if calltokenvalue() + { + revert_error_9a62d7e684059bba6a84639281e9dae3baecf10b1b86b3ab661f6dcc72bcade3() + } + constructor_C() + let _1 := allocate_unbounded() + codecopy(_1, dataoffset("C_25_deployed"), datasize("C_25_deployed")) + return(_1, datasize("C_25_deployed")) + } + function allocate_unbounded() -> memPtr + { memPtr := mload(64) } + function revert_error_8061d93638ee074115f764ac3d48f9e554bb1b90dfef10edac8a99bf7cd9567b() + { revert(0, 0) } + function revert_error_9a62d7e684059bba6a84639281e9dae3baecf10b1b86b3ab661f6dcc72bcade3() + { revert(0, 0) } + function shift_left(value) -> newValue + { newValue := shl(0, value) } + function update_byte_slice_shift(value, toInsert) -> result + { + let mask := not(0) + toInsert := shift_left(toInsert) + value := and(value, not(mask)) + result := or(value, and(toInsert, mask)) + } + function cleanup_rational_by(value) -> cleaned + { cleaned := value } + function cleanup_uint256(value) -> cleaned + { cleaned := value } + function identity(value) -> ret + { ret := value } + function convert_rational_by_to_uint256(value) -> converted + { + converted := cleanup_uint256(identity(cleanup_rational_by(value))) + } + function prepare_store_uint256(value) -> ret + { ret := value } + function update_storage_value_offset_rational_by_to_uint256(slot, value) + { + let convertedValue := convert_rational_by_to_uint256(value) + sstore(slot, update_byte_slice_shift(sload(slot), prepare_store_uint256(convertedValue))) + } + function constructor_C() + { + let expr := 0xeeeeeeeeee + update_storage_value_offset_rational_by_to_uint256(0x00, expr) + } + } + /// @use-src 0:"input.sol" + object "C_25_deployed" { + code { + { + mstore(64, memoryguard(0x80)) + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_unsigned(calldataload(0)) + switch selector + case 0xc2985578 { external_fun_foo() } + case 0xff2558ff { external_fun_varStorage() } + default { } + } + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + } + function shift_right_unsigned(value) -> newValue + { newValue := shr(224, value) } + function allocate_unbounded() -> memPtr + { memPtr := mload(64) } + function revert_error_8061d93638ee074115f764ac3d48f9e554bb1b90dfef10edac8a99bf7cd9567b() + { revert(0, 0) } + function revert_error_9a62d7e684059bba6a84639281e9dae3baecf10b1b86b3ab661f6dcc72bcade3() + { revert(0, 0) } + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() + { revert(0, 0) } + function abi_decode(headStart, dataEnd) + { + if slt(sub(dataEnd, headStart), 0) + { + revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() + } + } + function cleanup_uint256(value) -> cleaned + { cleaned := value } + function abi_encode_uint256_to_uint256(value, pos) + { + mstore(pos, cleanup_uint256(value)) + } + function abi_encode_uint256(headStart, value0) -> tail + { + tail := add(headStart, 32) + abi_encode_uint256_to_uint256(value0, add(headStart, 0)) + } + function external_fun_foo() + { + if callvalue() + { + revert_error_8061d93638ee074115f764ac3d48f9e554bb1b90dfef10edac8a99bf7cd9567b() + } + if calltokenid() + { + revert_error_9a62d7e684059bba6a84639281e9dae3baecf10b1b86b3ab661f6dcc72bcade3() + } + if calltokenvalue() + { + revert_error_9a62d7e684059bba6a84639281e9dae3baecf10b1b86b3ab661f6dcc72bcade3() + } + abi_decode(4, calldatasize()) + let ret := fun_foo() + let memPos := allocate_unbounded() + let memEnd := abi_encode_uint256(memPos, ret) + return(memPos, sub(memEnd, memPos)) + } + function shift_right_unsigned_dynamic(bits, value) -> newValue + { newValue := shr(bits, value) } + function cleanup_from_storage_uint256(value) -> cleaned + { cleaned := value } + function extract_from_storage_value_dynamict_uint256(slot_value, offset) -> value + { + value := cleanup_from_storage_uint256(shift_right_unsigned_dynamic(mul(offset, 8), slot_value)) + } + function read_from_storage_split_dynamic_uint256(slot, offset) -> value + { + value := extract_from_storage_value_dynamict_uint256(sload(slot), offset) + } + function getter_fun_varStorage() -> ret + { + let slot := 0 + let offset := 0 + ret := read_from_storage_split_dynamic_uint256(slot, offset) + } + function external_fun_varStorage() + { + if callvalue() + { + revert_error_8061d93638ee074115f764ac3d48f9e554bb1b90dfef10edac8a99bf7cd9567b() + } + if calltokenid() + { + revert_error_9a62d7e684059bba6a84639281e9dae3baecf10b1b86b3ab661f6dcc72bcade3() + } + if calltokenvalue() + { + revert_error_9a62d7e684059bba6a84639281e9dae3baecf10b1b86b3ab661f6dcc72bcade3() + } + abi_decode(4, calldatasize()) + let ret := getter_fun_varStorage() + let memPos := allocate_unbounded() + let memEnd := abi_encode_uint256(memPos, ret) + return(memPos, sub(memEnd, memPos)) + } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + { revert(0, 0) } + function zero_value_for_split_uint256() -> ret + { ret := 0 } + function cleanup_rational_by(value) -> cleaned + { cleaned := value } + function identity(value) -> ret + { ret := value } + function convert_rational_by_to_uint256(value) -> converted + { + converted := cleanup_uint256(identity(cleanup_rational_by(value))) + } + function shift_left(value) -> newValue + { newValue := shl(0, value) } + function update_byte_slice_shift(value, toInsert) -> result + { + let mask := not(0) + toInsert := shift_left(toInsert) + value := and(value, not(mask)) + result := or(value, and(toInsert, mask)) + } + function convert_uint256_to_uint256(value) -> converted + { + converted := cleanup_uint256(identity(cleanup_uint256(value))) + } + function prepare_store_uint256(value) -> ret + { ret := value } + function update_transient_storage_value_offset_uint256_to_uint256(slot, value) + { + let convertedValue := convert_uint256_to_uint256(value) + tstore(slot, update_byte_slice_shift(tload(slot), prepare_store_uint256(convertedValue))) + } + function shift_left_dynamic(bits, value) -> newValue + { newValue := shl(bits, value) } + function update_byte_slice_dynamic32(value, shiftBytes, toInsert) -> result + { + let shiftBits := mul(shiftBytes, 8) + let mask := shift_left_dynamic(shiftBits, not(0)) + toInsert := shift_left_dynamic(shiftBits, toInsert) + value := and(value, not(mask)) + result := or(value, and(toInsert, mask)) + } + function update_transient_storage_value_uint256_to_uint256(slot, offset, value) + { + let convertedValue := convert_uint256_to_uint256(value) + tstore(slot, update_byte_slice_dynamic32(tload(slot), offset, prepare_store_uint256(convertedValue))) + } + function transient_storage_set_to_zero_uint256(slot, offset) + { + let zero := zero_value_for_split_uint256() + update_transient_storage_value_uint256_to_uint256(slot, offset, zero) + } + function update_storage_value_uint256_to_uint256(slot, offset, value) + { + let convertedValue := convert_uint256_to_uint256(value) + sstore(slot, update_byte_slice_dynamic32(sload(slot), offset, prepare_store_uint256(convertedValue))) + } + function storage_set_to_zero_uint256(slot, offset) + { + let zero := zero_value_for_split_uint256() + update_storage_value_uint256_to_uint256(slot, offset, zero) + } + function shift_right_0_unsigned(value) -> newValue + { newValue := shr(0, value) } + function extract_from_storage_value_offset_uint256(slot_value) -> value + { + value := cleanup_from_storage_uint256(shift_right_0_unsigned(slot_value)) + } + function read_from_storage_split_offset_uint256(slot) -> value + { + value := extract_from_storage_value_offset_uint256(sload(slot)) + } + function fun_foo() -> var + { + let zero_uint256 := zero_value_for_split_uint256() + var := zero_uint256 + let expr := 0xffffffff + let _1 := convert_rational_by_to_uint256(expr) + update_transient_storage_value_offset_uint256_to_uint256(0x00, _1) + transient_storage_set_to_zero_uint256(0x00, 0) + storage_set_to_zero_uint256(0x00, 0) + let _2 := read_from_storage_split_offset_uint256(0x00) + let expr_1 := _2 + var := expr_1 + leave + } + } + data ".metadata" hex"" + } +} diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index d52ad6b9c48a..0a0ec6a879a1 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -634,8 +634,8 @@ BOOST_AUTO_TEST_CASE(optimise_multi_stores) )"; compileBothVersions(sourceCode); compareVersions("f()"); - BOOST_CHECK_EQUAL(numInstructions(m_nonOptimizedBytecode, Instruction::SSTORE), 8); - BOOST_CHECK_EQUAL(numInstructions(m_optimizedBytecode, Instruction::SSTORE), 7); + BOOST_CHECK_EQUAL(numInstructions(m_nonOptimizedBytecode, Instruction::SSTORE), 9); + BOOST_CHECK_EQUAL(numInstructions(m_optimizedBytecode, Instruction::SSTORE), 6); } BOOST_AUTO_TEST_CASE(optimise_constant_to_codecopy) diff --git a/test/libsolidity/semanticTests/abiEncodeDecode/abi_decode_simple_storage.sol b/test/libsolidity/semanticTests/abiEncodeDecode/abi_decode_simple_storage.sol index 54ce693d5ca4..6c4fd52b36ed 100644 --- a/test/libsolidity/semanticTests/abiEncodeDecode/abi_decode_simple_storage.sol +++ b/test/libsolidity/semanticTests/abiEncodeDecode/abi_decode_simple_storage.sol @@ -8,6 +8,6 @@ contract C { } // ---- // f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg" -// gas irOptimized: 135499 +// gas irOptimized: 135441 // gas legacy: 137095 -// gas legacyOptimized: 135823 +// gas legacyOptimized: 135828 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol index 5c3bcba971c2..e5aa910ce1be 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol @@ -21,6 +21,6 @@ contract C { } // ---- // f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb -// gas irOptimized: 203167 +// gas irOptimized: 203109 // gas legacy: 206263 -// gas legacyOptimized: 203172 +// gas legacyOptimized: 203177 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/struct/struct_storage_ptr.sol b/test/libsolidity/semanticTests/abiEncoderV1/struct/struct_storage_ptr.sol index ed983c77b506..ff98c66ca9c6 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/struct/struct_storage_ptr.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/struct/struct_storage_ptr.sol @@ -24,6 +24,6 @@ contract C { // ---- // library: L // f() -> 8, 7, 1, 2, 7, 12 -// gas irOptimized: 166761 -// gas legacy: 169273 -// gas legacyOptimized: 167243 +// gas irOptimized: 166766 +// gas legacy: 170486 +// gas legacyOptimized: 167252 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_overlapped_dynamic_arrays.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_overlapped_dynamic_arrays.sol index aa1e494c2133..302aeb5de4d5 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_overlapped_dynamic_arrays.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_overlapped_dynamic_arrays.sol @@ -33,8 +33,8 @@ contract C { // f_which(uint256[],uint256[2],uint256): 0x40, 1, 2, 1, 5, 6 -> 0x20, 0x40, 5, 2 // f_which(uint256[],uint256[2],uint256): 0x40, 1, 2, 1 -> FAILURE // f_storage(uint256[],uint256[2]): 0x20, 1, 2 -> 0x20, 0x60, 0x20, 1, 2 -// gas irOptimized: 111409 -// gas legacy: 112707 -// gas legacyOptimized: 111845 +// gas irOptimized: 111356 +// gas legacy: 113826 +// gas legacyOptimized: 111724 // f_storage(uint256[],uint256[2]): 0x40, 1, 2, 5, 6 -> 0x20, 0x80, 0x20, 2, 5, 6 // f_storage(uint256[],uint256[2]): 0x40, 1, 2, 5 -> FAILURE diff --git a/test/libsolidity/semanticTests/abiEncoderV2/storage_array_encoding.sol b/test/libsolidity/semanticTests/abiEncoderV2/storage_array_encoding.sol index 9599af6c8815..31f759d3bfd4 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/storage_array_encoding.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/storage_array_encoding.sol @@ -18,10 +18,10 @@ contract C { // EVMVersion: >homestead // ---- // h(uint256[2][]): 0x20, 3, 123, 124, 223, 224, 323, 324 -> 32, 256, 0x20, 3, 123, 124, 223, 224, 323, 324 -// gas irOptimized: 180080 -// gas legacy: 184233 -// gas legacyOptimized: 180856 +// gas irOptimized: 180022 +// gas legacy: 186541 +// gas legacyOptimized: 180429 // i(uint256[2][2]): 123, 124, 223, 224 -> 32, 128, 123, 124, 223, 224 // gas irOptimized: 112031 -// gas legacy: 115091 -// gas legacyOptimized: 112657 +// gas legacy: 116683 +// gas legacyOptimized: 112303 diff --git a/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol b/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol index a9d08ec0c0cb..ac3cd8495f73 100644 --- a/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol +++ b/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol @@ -12,9 +12,9 @@ contract Test { } // ---- // set(uint24[3][]): 0x20, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 -> 0x06 -// gas irOptimized: 185216 -// gas legacy: 211054 -// gas legacyOptimized: 206077 +// gas irOptimized: 185221 +// gas legacy: 199970 +// gas legacyOptimized: 186342 // data(uint256,uint256): 0x02, 0x02 -> 0x09 // data(uint256,uint256): 0x05, 0x01 -> 0x11 // data(uint256,uint256): 0x06, 0x00 -> FAILURE diff --git a/test/libsolidity/semanticTests/array/bytes_length_member.sol b/test/libsolidity/semanticTests/array/bytes_length_member.sol index 5159538bc1de..85b9282c6603 100644 --- a/test/libsolidity/semanticTests/array/bytes_length_member.sol +++ b/test/libsolidity/semanticTests/array/bytes_length_member.sol @@ -13,7 +13,7 @@ contract c { // ---- // getLength() -> 0 // set(): 1, 2 -> true -// gas irOptimized: 110422 +// gas irOptimized: 110393 // gas legacy: 110951 -// gas legacyOptimized: 110576 +// gas legacyOptimized: 110577 // getLength() -> 68 diff --git a/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol index 342f3218fcb4..833c9dec3e42 100644 --- a/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol +++ b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol @@ -10,9 +10,9 @@ contract C { // constructor(): 1, 2, 3 -> // gas irOptimized: 124991 // gas irOptimized code: 14800 -// gas legacy: 134317 +// gas legacy: 142553 // gas legacy code: 46200 -// gas legacyOptimized: 127166 +// gas legacyOptimized: 126306 // gas legacyOptimized code: 23400 // a(uint256): 0 -> 1 // a(uint256): 1 -> 2 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol b/test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol index ecfe565f12cb..8131e7434d25 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol @@ -20,7 +20,7 @@ contract c { } // ---- // 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 -// gas irOptimized: 647725 -// gas legacy: 694354 -// gas legacyOptimized: 693849 +// gas irOptimized: 647730 +// gas legacy: 659420 +// gas legacyOptimized: 648899 // retrieve() -> 9, 28, 9, 28, 4, 3, 32 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol index 761feebec9d5..ba05d8fb670d 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol @@ -21,6 +21,6 @@ contract C { } // ---- // f() -> true -// gas irOptimized: 92740 -// gas legacy: 93035 -// gas legacyOptimized: 92257 +// gas irOptimized: 73981 +// gas legacy: 75550 +// gas legacyOptimized: 73812 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint40.sol b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint40.sol index d65f4f37c104..e4e7bf62f3a0 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint40.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint40.sol @@ -46,6 +46,6 @@ contract C { } // ---- // f() -> true -// gas irOptimized: 122541 -// gas legacy: 124643 -// gas legacyOptimized: 122801 +// gas irOptimized: 122596 +// gas legacy: 125651 +// gas legacyOptimized: 121668 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage.sol b/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage.sol index e9f1448a27b1..f206b432c9a5 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage.sol @@ -13,6 +13,6 @@ contract C { } // ---- // f() -> 0 -// gas irOptimized: 108229 -// gas legacy: 108216 -// gas legacyOptimized: 107625 +// gas irOptimized: 108096 +// gas legacy: 110351 +// gas legacyOptimized: 107708 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage_packed.sol b/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage_packed.sol index 13cb9c28fd26..12d9bf1fd3c0 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage_packed.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage_packed.sol @@ -40,11 +40,11 @@ contract C { } // ---- // f() -> 0 -// gas irOptimized: 92800 -// gas legacy: 93006 -// gas legacyOptimized: 92261 +// gas irOptimized: 74044 +// gas legacy: 75528 +// gas legacyOptimized: 73861 // g() -> 0 // h() -> 0 -// gas irOptimized: 92862 -// gas legacy: 93028 -// gas legacyOptimized: 92303 +// gas irOptimized: 74114 +// gas legacy: 75553 +// gas legacyOptimized: 73904 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_different_packing.sol b/test/libsolidity/semanticTests/array/copying/array_copy_different_packing.sol index ee564d01a2d9..1bbb541911a0 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_different_packing.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_different_packing.sol @@ -19,5 +19,5 @@ contract c { // ---- // test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x05000000000000000000000000000000000000000000000000 // gas irOptimized: 208415 -// gas legacy: 220711 -// gas legacyOptimized: 220097 +// gas legacy: 220059 +// gas legacyOptimized: 211458 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol index bb621fcc84b5..ba50c7300f65 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol @@ -35,12 +35,12 @@ contract c { } // ---- // test() -> 0x02000202 -// gas irOptimized: 4549676 -// gas legacy: 4475394 -// gas legacyOptimized: 4447665 +// gas irOptimized: 4560468 +// gas legacy: 4536566 +// gas legacyOptimized: 4456759 // storageEmpty -> 1 // clear() -> 0, 0 -// gas irOptimized: 4478184 -// gas legacy: 4407185 -// gas legacyOptimized: 4381337 +// gas irOptimized: 4488719 +// gas legacy: 4407780 +// gas legacyOptimized: 4385089 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol b/test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol index 2d325a804bfa..34d6c75ef8a6 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol @@ -12,6 +12,6 @@ contract c { } // ---- // test(uint256[2][]): 32, 3, 7, 8, 9, 10, 11, 12 -> 10 -// gas irOptimized: 689552 -// gas legacy: 686176 -// gas legacyOptimized: 685611 +// gas irOptimized: 689539 +// gas legacy: 713478 +// gas legacyOptimized: 690424 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base.sol index 54639df55d66..af31d4b8e495 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base.sol @@ -18,5 +18,5 @@ contract c { // ---- // test() -> 5, 4 // gas irOptimized: 205667 -// gas legacy: 213863 -// gas legacyOptimized: 212901 +// gas legacy: 207971 +// gas legacyOptimized: 204828 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base_nested.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base_nested.sol index ad745ff4203f..6b1dd7ff02c0 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base_nested.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base_nested.sol @@ -21,6 +21,6 @@ contract c { } // ---- // test() -> 3, 4 -// gas irOptimized: 169669 -// gas legacy: 175415 -// gas legacyOptimized: 172533 +// gas irOptimized: 169553 +// gas legacy: 182361 +// gas legacyOptimized: 169554 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol index a38685404dc7..98e2f5adb45e 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol @@ -15,9 +15,9 @@ contract c { // ---- // setData1(uint256,uint256,uint256): 10, 5, 4 -> // copyStorageStorage() -> -// gas irOptimized: 111350 -// gas legacy: 109272 -// gas legacyOptimized: 109262 +// gas irOptimized: 111321 +// gas legacy: 112434 +// gas legacyOptimized: 111494 // getData2(uint256): 5 -> 10, 4 // setData1(uint256,uint256,uint256): 0, 0, 0 -> // copyStorageStorage() -> diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dynamic_dynamic.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dynamic_dynamic.sol index 83df3aef8f43..5a66704b5506 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dynamic_dynamic.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dynamic_dynamic.sol @@ -18,5 +18,5 @@ contract c { // ---- // test() -> 5, 4 // gas irOptimized: 253591 -// gas legacy: 250892 -// gas legacyOptimized: 250045 +// gas legacy: 253664 +// gas legacyOptimized: 252200 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol index 94e57c96da7b..29d8bc0ebed7 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol @@ -12,5 +12,5 @@ contract c { // ---- // test() -> 9, 4 // gas irOptimized: 123180 -// gas legacy: 123566 -// gas legacyOptimized: 123202 +// gas legacy: 124642 +// gas legacyOptimized: 123345 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_simple.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_simple.sol index 2fccff774e94..29e62dd3ce8f 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_simple.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_simple.sol @@ -11,5 +11,5 @@ contract C { } // ---- // test() -> left(0x01), left(0x02) -// gas legacy: 90001 -// gas legacyOptimized: 89085 +// gas legacy: 69235 +// gas legacyOptimized: 66974 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_static.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_static.sol index d1f2cbdedcde..65ce656548eb 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_static.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_static.sol @@ -14,6 +14,6 @@ contract c { } // ---- // test() -> 8, 0 -// gas irOptimized: 196251 -// gas legacy: 194842 -// gas legacyOptimized: 194281 +// gas irOptimized: 196370 +// gas legacy: 210073 +// gas legacyOptimized: 196759 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol index e638f2dad10c..f1cd76a3adc5 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol @@ -17,7 +17,7 @@ contract c { } // ---- // test() -> 4, 5 -// gas irOptimized: 190628 -// gas legacy: 190828 -// gas legacyOptimized: 189657 +// gas irOptimized: 190676 +// gas legacy: 210706 +// gas legacyOptimized: 190472 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover.sol b/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover.sol index 22105f6429b9..2deb372fa381 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover.sol @@ -17,6 +17,6 @@ contract c { } // ---- // test() -> 0xffffffff, 0x0000000000000000000000000a00090008000700060005000400030002000100, 0x0000000000000000000000000000000000000000000000000000000000000000 -// gas irOptimized: 100496 -// gas legacy: 158109 -// gas legacyOptimized: 141019 +// gas irOptimized: 100506 +// gas legacy: 146700 +// gas legacyOptimized: 122680 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover2.sol b/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover2.sol index 0a2bcffce201..706130082fff 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover2.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover2.sol @@ -19,6 +19,6 @@ contract c { } // ---- // test() -> 0x04000000000000000000000000000000000000000000000000, 0x0, 0x0 -// gas irOptimized: 93858 -// gas legacy: 97451 -// gas legacyOptimized: 94200 +// gas irOptimized: 91320 +// gas legacy: 97777 +// gas legacyOptimized: 91993 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_target_simple.sol b/test/libsolidity/semanticTests/array/copying/array_copy_target_simple.sol index 27fb9f0c5736..054d61c6772b 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_target_simple.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_target_simple.sol @@ -18,6 +18,6 @@ contract c { } // ---- // test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x0 -// gas irOptimized: 273543 -// gas legacy: 282601 -// gas legacyOptimized: 281510 +// gas irOptimized: 273548 +// gas legacy: 280148 +// gas legacyOptimized: 274502 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_target_simple_2.sol b/test/libsolidity/semanticTests/array/copying/array_copy_target_simple_2.sol index 980e414d1451..7ceadf481c66 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_target_simple_2.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_target_simple_2.sol @@ -18,6 +18,6 @@ contract c { } // ---- // test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x00 -// gas irOptimized: 232995 -// gas legacy: 235694 -// gas legacyOptimized: 235193 +// gas irOptimized: 233012 +// gas legacy: 239400 +// gas legacyOptimized: 233948 diff --git a/test/libsolidity/semanticTests/array/copying/array_elements_to_mapping.sol b/test/libsolidity/semanticTests/array/copying/array_elements_to_mapping.sol index becc3d4cdefd..a1849bd65fce 100644 --- a/test/libsolidity/semanticTests/array/copying/array_elements_to_mapping.sol +++ b/test/libsolidity/semanticTests/array/copying/array_elements_to_mapping.sol @@ -52,9 +52,9 @@ contract C { } // ---- // from_storage() -> 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 -// gas irOptimized: 149868 -// gas legacy: 150737 -// gas legacyOptimized: 148690 +// gas irOptimized: 149856 +// gas legacy: 156721 +// gas legacyOptimized: 149000 // from_storage_ptr() -> 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 // from_memory() -> 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 // from_calldata(uint8[][]): 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 -> 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 diff --git a/test/libsolidity/semanticTests/array/copying/array_nested_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/array_nested_calldata_to_storage.sol index 5d4e5b45e4a9..ffa0c68adcbe 100644 --- a/test/libsolidity/semanticTests/array/copying/array_nested_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/array_nested_calldata_to_storage.sol @@ -38,10 +38,10 @@ contract c { // compileViaYul: true // ---- // test1(uint256[][]): 0x20, 2, 0x40, 0x40, 2, 23, 42 -> 2, 65 -// gas irOptimized: 181029 +// gas irOptimized: 180918 // test2(uint256[][2]): 0x20, 0x40, 0x40, 2, 23, 42 -> 2, 65 // gas irOptimized: 157604 // test3(uint256[2][]): 0x20, 2, 23, 42, 23, 42 -> 2, 65 -// gas irOptimized: 134801 +// gas irOptimized: 134685 // test4(uint256[2][2]): 23, 42, 23, 42 -> 65 // gas irOptimized: 111177 diff --git a/test/libsolidity/semanticTests/array/copying/array_nested_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/array_nested_memory_to_storage.sol index b892ec991ce3..933a1ab1b1c5 100644 --- a/test/libsolidity/semanticTests/array/copying/array_nested_memory_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/array_nested_memory_to_storage.sol @@ -38,12 +38,14 @@ contract Test { } // ---- // test() -> 24 -// gas irOptimized: 226734 -// gas legacy: 227083 -// gas legacyOptimized: 226529 +// gas irOptimized: 226647 +// gas legacy: 229060 +// gas legacyOptimized: 226495 // test1() -> 3 // test2() -> 6 +// gas irOptimized: 95905 +// gas legacy: 100519 // test3() -> 24 -// gas irOptimized: 141319 -// gas legacy: 142238 -// gas legacyOptimized: 141365 +// gas irOptimized: 141297 +// gas legacy: 146668 +// gas legacyOptimized: 141331 diff --git a/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic.sol b/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic.sol index ab3c172d3399..e3b9cdaadcb9 100644 --- a/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic.sol +++ b/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic.sol @@ -45,7 +45,7 @@ contract C { } // ---- // copyExternalStorageArrayOfFunctionType() -> true -// gas irOptimized: 104566 -// gas legacy: 108554 -// gas legacyOptimized: 102405 +// gas irOptimized: 104629 +// gas legacy: 111170 +// gas legacyOptimized: 104620 // copyInternalArrayOfFunctionType() -> true diff --git a/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic_different_mutability.sol b/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic_different_mutability.sol index 2bb3bcf333e3..92b61b9291cd 100644 --- a/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic_different_mutability.sol +++ b/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic_different_mutability.sol @@ -49,7 +49,7 @@ contract C { // ---- // copyExternalStorageArraysOfFunctionType() -> true // gas irOptimized: 104238 -// gas legacy: 108295 -// gas legacyOptimized: 102162 +// gas legacy: 110911 +// gas legacyOptimized: 104377 // copyInternalArrayOfFunctionType() -> true -// gas legacy: 104178 +// gas legacy: 38695 diff --git a/test/libsolidity/semanticTests/array/copying/array_of_struct_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/array_of_struct_calldata_to_storage.sol index 3147401cfa1d..e4b3d14fd76b 100644 --- a/test/libsolidity/semanticTests/array/copying/array_of_struct_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/array_of_struct_calldata_to_storage.sol @@ -17,4 +17,4 @@ contract C { // compileViaYul: true // ---- // f((uint128,uint64,uint128)[]): 0x20, 3, 0, 0, 12, 0, 11, 0, 10, 0, 0 -> 10, 11, 12 -// gas irOptimized: 120747 +// gas irOptimized: 120751 diff --git a/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_calldata_to_storage.sol index 09037719a6f0..acc968389229 100644 --- a/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_calldata_to_storage.sol @@ -23,4 +23,4 @@ contract C { // compileViaYul: true // ---- // f((uint256[])[]): 0x20, 3, 0x60, 0x60, 0x60, 0x20, 3, 1, 2, 3 -> 3, 1 -// gas irOptimized: 327456 +// gas irOptimized: 327461 diff --git a/test/libsolidity/semanticTests/array/copying/array_to_mapping.sol b/test/libsolidity/semanticTests/array/copying/array_to_mapping.sol index c5519e83aa43..667685c9c83f 100644 --- a/test/libsolidity/semanticTests/array/copying/array_to_mapping.sol +++ b/test/libsolidity/semanticTests/array/copying/array_to_mapping.sol @@ -37,8 +37,8 @@ contract C { } // ---- // from_storage() -> 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 -// gas irOptimized: 147755 -// gas legacy: 148892 -// gas legacyOptimized: 146917 +// gas irOptimized: 147749 +// gas legacy: 154218 +// gas legacyOptimized: 147020 // from_storage_ptr() -> 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 // from_memory() -> 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 diff --git a/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol b/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol index af2983b066bd..fea2c895d81c 100644 --- a/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol @@ -10,9 +10,9 @@ contract Test { } // ---- // set(uint24[]): 0x20, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 -> 18 -// gas irOptimized: 99616 -// gas legacy: 103509 -// gas legacyOptimized: 101266 +// gas irOptimized: 95505 +// gas legacy: 104176 +// gas legacyOptimized: 96785 // data(uint256): 7 -> 8 // data(uint256): 15 -> 16 // data(uint256): 18 -> FAILURE diff --git a/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol b/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol index 90012eed4c77..4ab381f4e614 100644 --- a/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol +++ b/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol @@ -5,17 +5,20 @@ contract c { } // ---- // set(uint256): 1, 2 -> true -// gas irOptimized: 110550 +// gas irOptimized: 110560 // gas legacy: 111310 // gas legacyOptimized: 110741 // set(uint256): 2, 2, 3, 4, 5 -> true -// gas irOptimized: 177501 +// gas irOptimized: 177511 // gas legacy: 178312 // gas legacyOptimized: 177716 // storageEmpty -> 0 // copy(uint256,uint256): 1, 2 -> true +// gas irOptimized: 48421 // storageEmpty -> 0 // copy(uint256,uint256): 99, 1 -> true +// gas irOptimized: 35641 // storageEmpty -> 0 // copy(uint256,uint256): 99, 2 -> true +// gas irOptimized: 35641 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol b/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol index fe758fe8fd13..37769cb4966c 100644 --- a/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol @@ -19,23 +19,24 @@ contract c { // f(uint256): 31 -> 0x20, 0x1f, 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e00 // gas irOptimized: 103268 // gas legacy: 112904 -// gas legacyOptimized: 112647 +// gas legacyOptimized: 112645 // f(uint256): 32 -> 0x20, 0x20, 1780731860627700044960722568376592200742329637303199754547598369979440671 // gas irOptimized: 117825 // gas legacy: 128964 -// gas legacyOptimized: 128854 +// gas legacyOptimized: 128859 // f(uint256): 33 -> 0x20, 33, 1780731860627700044960722568376592200742329637303199754547598369979440671, 0x2000000000000000000000000000000000000000000000000000000000000000 -// gas irOptimized: 124091 -// gas legacy: 136092 -// gas legacyOptimized: 135469 +// gas irOptimized: 123888 +// gas legacy: 135510 +// gas legacyOptimized: 135218 // f(uint256): 63 -> 0x20, 0x3f, 1780731860627700044960722568376592200742329637303199754547598369979440671, 14532552714582660066924456880521368950258152170031413196862950297402215316992 -// gas irOptimized: 127151 -// gas legacy: 148692 -// gas legacyOptimized: 148699 +// gas irOptimized: 126948 +// gas legacy: 148110 +// gas legacyOptimized: 148448 // f(uint256): 12 -> 0x20, 0x0c, 0x0102030405060708090a0b0000000000000000000000000000000000000000 -// gas legacy: 59345 -// gas legacyOptimized: 57279 +// gas irOptimized: 54204 +// gas legacy: 59832 +// gas legacyOptimized: 57268 // f(uint256): 129 -> 0x20, 0x81, 1780731860627700044960722568376592200742329637303199754547598369979440671, 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f, 29063324697304692433803953038474361308315562010425523193971352996434451193439, 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f, -57896044618658097711785492504343953926634992332820282019728792003956564819968 // gas irOptimized: 416918 // gas legacy: 458997 -// gas legacyOptimized: 460664 +// gas legacyOptimized: 460669 diff --git a/test/libsolidity/semanticTests/array/copying/calldata_array_dynamic_to_storage.sol b/test/libsolidity/semanticTests/array/copying/calldata_array_dynamic_to_storage.sol index 3df22c725192..400834772869 100644 --- a/test/libsolidity/semanticTests/array/copying/calldata_array_dynamic_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/calldata_array_dynamic_to_storage.sol @@ -9,6 +9,6 @@ contract C { } // ---- // f(uint256[]): 0x20, 0x03, 0x1, 0x2, 0x3 -> 0x1 -// gas irOptimized: 111084 -// gas legacy: 111548 -// gas legacyOptimized: 111321 +// gas irOptimized: 110973 +// gas legacy: 112436 +// gas legacyOptimized: 111313 diff --git a/test/libsolidity/semanticTests/array/copying/cleanup_during_multi_element_per_slot_copy.sol b/test/libsolidity/semanticTests/array/copying/cleanup_during_multi_element_per_slot_copy.sol index e6284072d1f9..cdc3c4f66765 100644 --- a/test/libsolidity/semanticTests/array/copying/cleanup_during_multi_element_per_slot_copy.sol +++ b/test/libsolidity/semanticTests/array/copying/cleanup_during_multi_element_per_slot_copy.sol @@ -16,10 +16,10 @@ contract C { } // ---- // constructor() -// gas irOptimized: 90065 -// gas irOptimized code: 149000 -// gas legacy: 89553 -// gas legacy code: 126200 -// gas legacyOptimized: 83556 -// gas legacyOptimized code: 98200 +// gas irOptimized: 89349 +// gas irOptimized code: 140200 +// gas legacy: 100902 +// gas legacy code: 271400 +// gas legacyOptimized: 83062 +// gas legacyOptimized code: 91800 // f() -> 0 diff --git a/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol b/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol index 787973c5f0e5..9a68f88d8971 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol @@ -37,10 +37,11 @@ contract C { // f() -> 0x40, 0x80, 6, 0x6162636465660000000000000000000000000000000000000000000000000000, 0x49, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738390000000000000000000000000000000000000000000000 // gas irOptimized: 179405 // gas legacy: 180675 -// gas legacyOptimized: 179686 +// gas legacyOptimized: 179690 // g() -> 0x40, 0xc0, 0x49, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738390000000000000000000000000000000000000000000000, 0x11, 0x3132333435363738393233343536373839000000000000000000000000000000 -// gas irOptimized: 106338 -// gas legacy: 109415 -// gas legacyOptimized: 106600 +// gas irOptimized: 106381 +// gas legacy: 109472 +// gas legacyOptimized: 106648 // h() -> 0x40, 0x60, 0x00, 0x00 +// gas irOptimized: 46948 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol b/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol index fc274bf47b28..caa0596610ad 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol @@ -46,6 +46,6 @@ contract C { } // ---- // f() -> 0xff -// gas irOptimized: 143857 -// gas legacy: 153404 -// gas legacyOptimized: 146676 +// gas irOptimized: 143935 +// gas legacy: 153487 +// gas legacyOptimized: 146748 diff --git a/test/libsolidity/semanticTests/array/copying/copy_function_internal_storage_array.sol b/test/libsolidity/semanticTests/array/copying/copy_function_internal_storage_array.sol index 7db82f891dfb..5fc981340942 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_function_internal_storage_array.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_function_internal_storage_array.sol @@ -15,6 +15,6 @@ contract C { } // ---- // test() -> 7 -// gas irOptimized: 122459 -// gas legacy: 205176 -// gas legacyOptimized: 204971 +// gas irOptimized: 122575 +// gas legacy: 208558 +// gas legacyOptimized: 202409 diff --git a/test/libsolidity/semanticTests/array/copying/copy_internal_function_array_to_storage.sol b/test/libsolidity/semanticTests/array/copying/copy_internal_function_array_to_storage.sol index 95ba9d8ed8e8..6c1d061d309b 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_internal_function_array_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_internal_function_array_to_storage.sol @@ -18,6 +18,6 @@ contract C { } // ---- // one() -> 3 -// gas legacy: 140253 -// gas legacyOptimized: 140093 +// gas legacy: 142687 +// gas legacyOptimized: 135991 // two() -> FAILURE, hex"4e487b71", 0x51 diff --git a/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol b/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol index 46216e912ce4..6872995d1cbb 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol @@ -9,7 +9,8 @@ contract c { // set(): 1, 2, 3, 4, 5 -> true // gas irOptimized: 177344 // gas legacy: 177953 -// gas legacyOptimized: 177550 +// gas legacyOptimized: 177551 // storageEmpty -> 0 // reset() -> true +// gas irOptimized: 47341 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/copying_bytes_multiassign.sol b/test/libsolidity/semanticTests/array/copying/copying_bytes_multiassign.sol index ce4344e871db..d29feccf421c 100644 --- a/test/libsolidity/semanticTests/array/copying/copying_bytes_multiassign.sol +++ b/test/libsolidity/semanticTests/array/copying/copying_bytes_multiassign.sol @@ -18,13 +18,15 @@ contract sender { } // ---- // (): 7 -> -// gas irOptimized: 110822 +// gas irOptimized: 110735 // gas legacy: 111388 -// gas legacyOptimized: 111065 +// gas legacyOptimized: 111066 // val() -> 0 // forward(bool): true -> true +// gas irOptimized: 49573 // val() -> 0x80 // forward(bool): false -> true +// gas irOptimized: 31410 // val() -> 0x80 // forward(bool): true -> true // val() -> 0x80 diff --git a/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_calldata_to_storage.sol index 672e7435f1a9..5c64ba63f8f8 100644 --- a/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_calldata_to_storage.sol @@ -31,8 +31,8 @@ contract C { // compileViaYul: true // ---- // test1((uint8[],uint8[2])[][][]): 0x20, 1, 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -> 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -// gas irOptimized: 327921 +// gas irOptimized: 327936 // test2((uint8[],uint8[2])[][1][]): 0x20, 2, 0x40, 0x0160, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13, 0x20, 1, 0x20, 0x60, 31, 37, 2, 23, 29 -> 0x20, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 141162 +// gas irOptimized: 141168 // test3((uint8[],uint8[2])[1][][2]): 0x20, 0x40, 0x60, 0, 2, 0x40, 288, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -> 0x20, 2, 0x40, 288, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 188442 +// gas irOptimized: 188448 diff --git a/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_memory_to_storage.sol index f232d8aba9d4..12c40849fe6f 100644 --- a/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_memory_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_memory_to_storage.sol @@ -31,8 +31,8 @@ contract C { // compileViaYul: true // ---- // test1((uint8[],uint8[2])[][][]): 0x20, 1, 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -> 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -// gas irOptimized: 332567 +// gas irOptimized: 332582 // test2((uint8[],uint8[2])[][1][]): 0x20, 2, 0x40, 0x0160, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13, 0x20, 1, 0x20, 0x60, 31, 37, 2, 23, 29 -> 0x20, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 145409 +// gas irOptimized: 145415 // test3((uint8[],uint8[2])[1][][2]): 0x20, 0x40, 0x60, 0, 2, 0x40, 288, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -> 0x20, 2, 0x40, 288, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 192248 +// gas irOptimized: 192254 diff --git a/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol b/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol index 640046a11a4f..a5cf48011226 100644 --- a/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol @@ -46,11 +46,11 @@ contract C { } // ---- // test() -> 0x20, 0x14, "[a called][b called]" -// gas irOptimized: 116518 -// gas legacy: 118841 -// gas legacyOptimized: 116843 +// gas irOptimized: 116512 +// gas legacy: 120226 +// gas legacyOptimized: 116941 // test2() -> 0x20, 0x14, "[b called][a called]" // test3() -> 0x20, 0x14, "[b called][a called]" -// gas irOptimized: 103144 -// gas legacy: 102654 -// gas legacyOptimized: 101556 +// gas irOptimized: 103138 +// gas legacy: 105192 +// gas legacyOptimized: 103752 diff --git a/test/libsolidity/semanticTests/array/copying/memory_dyn_2d_bytes_to_storage.sol b/test/libsolidity/semanticTests/array/copying/memory_dyn_2d_bytes_to_storage.sol index 8a4d870b22f8..93eb84427d66 100644 --- a/test/libsolidity/semanticTests/array/copying/memory_dyn_2d_bytes_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/memory_dyn_2d_bytes_to_storage.sol @@ -18,6 +18,6 @@ contract C { } // ---- // f() -> 3 -// gas irOptimized: 128296 -// gas legacy: 129077 -// gas legacyOptimized: 128210 +// gas irOptimized: 128301 +// gas legacy: 129528 +// gas legacyOptimized: 128171 diff --git a/test/libsolidity/semanticTests/array/copying/nested_array_element_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/nested_array_element_calldata_to_storage.sol index 5040a72cadb5..f9d14b59edf5 100644 --- a/test/libsolidity/semanticTests/array/copying/nested_array_element_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/nested_array_element_calldata_to_storage.sol @@ -25,5 +25,5 @@ contract C { // ---- // test(uint8[2][2][2]): 1, 2, 3, 4, 5, 6, 7, 8 // test2(uint8[2][2]): 1, 2, 3, 4 -// gas irOptimized: 119939 -// gas legacyOptimized: 120228 +// gas irOptimized: 72076 +// gas legacyOptimized: 72086 diff --git a/test/libsolidity/semanticTests/array/copying/nested_array_element_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/nested_array_element_memory_to_storage.sol index 2f4b4ca7c9a1..d604a11e6cd4 100644 --- a/test/libsolidity/semanticTests/array/copying/nested_array_element_memory_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/nested_array_element_memory_to_storage.sol @@ -25,5 +25,5 @@ contract C { // ---- // test(uint8[2][2][2]): 1, 2, 3, 4, 5, 6, 7, 8 // test2(uint8[2][2]): 1, 2, 3, 4 -// gas irOptimized: 119939 -// gas legacyOptimized: 120228 +// gas irOptimized: 73031 +// gas legacyOptimized: 73155 diff --git a/test/libsolidity/semanticTests/array/copying/nested_array_element_storage_to_storage.sol b/test/libsolidity/semanticTests/array/copying/nested_array_element_storage_to_storage.sol index f3934fec0f6f..1a3fb70e0845 100644 --- a/test/libsolidity/semanticTests/array/copying/nested_array_element_storage_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/nested_array_element_storage_to_storage.sol @@ -71,14 +71,14 @@ contract C { // ---- // test1() -> // gas irOptimized: 150488 -// gas legacy: 150949 -// gas legacyOptimized: 150906 +// gas legacy: 156275 +// gas legacyOptimized: 151018 // test2() -> FAILURE // gas irOptimized: 150389 -// gas legacy: 150672 -// gas legacyOptimized: 150575 +// gas legacy: 155998 +// gas legacyOptimized: 150684 // test3() -> // gas irOptimized: 124300 -// gas legacy: 125333 -// gas legacyOptimized: 125127 +// gas legacy: 130649 +// gas legacyOptimized: 125142 // test4() -> FAILURE diff --git a/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_calldata_to_storage.sol index 7621b2477e03..b27fe9e06453 100644 --- a/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_calldata_to_storage.sol @@ -29,8 +29,8 @@ contract C { // compileViaYul: true // ---- // test1((uint8[],uint8[2])[][]): 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -> 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -// gas irOptimized: 304788 +// gas irOptimized: 304750 // test2((uint8[],uint8[2])[][1]): 0x20, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13 -> 0x20, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 116653 +// gas irOptimized: 116659 // test3((uint8[],uint8[2])[1][]): 0x20, 2, 0x40, 0x0120, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -> 0x20, 2, 0x40, 0x0120, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 187994 +// gas irOptimized: 187942 diff --git a/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_memory_to_storage.sol index 8ec3f77691b5..146de2f2af6a 100644 --- a/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_memory_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_memory_to_storage.sol @@ -29,8 +29,8 @@ contract C { // compileViaYul: true // ---- // test1((uint8[],uint8[2])[][]): 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -> 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -// gas irOptimized: 309072 +// gas irOptimized: 309034 // test2((uint8[],uint8[2])[][1]): 0x20, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13 -> 0x20, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 118314 +// gas irOptimized: 118320 // test3((uint8[],uint8[2])[1][]): 0x20, 2, 0x40, 0x0120, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -> 0x20, 2, 0x40, 0x0120, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 191045 +// gas irOptimized: 190993 diff --git a/test/libsolidity/semanticTests/array/copying/nested_dynamic_array_element_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/nested_dynamic_array_element_calldata_to_storage.sol index 57b3dc521f65..7583f5ab4f0d 100644 --- a/test/libsolidity/semanticTests/array/copying/nested_dynamic_array_element_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/nested_dynamic_array_element_calldata_to_storage.sol @@ -30,7 +30,7 @@ contract C { // compileViaYul: true // ---- // test(uint8[][][]): 0x20, 2, 0x40, 0x60, 0, 2, 0x40, 0x80, 1, 7, 2, 8, 9 -// gas irOptimized: 137897 +// gas irOptimized: 137891 // test2(uint8[][]): 0x20, 2, 0x40, 0x80, 1, 7, 2, 8, 9 -// gas irOptimized: 164482 +// gas irOptimized: 164249 // gas legacyOptimized: 120228 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol index e5a6ae53b2da..3f52085f4b34 100644 --- a/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol @@ -13,4 +13,4 @@ contract C { // f() -> 0x20, 0x02, 0x40, 0x80, 3, 0x6162630000000000000000000000000000000000000000000000000000000000, 0x99, 44048183304486788312148433451363384677562265908331949128489393215789685032262, 32241931068525137014058842823026578386641954854143559838526554899205067598957, 49951309422467613961193228765530489307475214998374779756599339590522149884499, 0x54555658595a6162636465666768696a6b6c6d6e6f707172737475767778797a, 0x4142434445464748494a4b4c4d4e4f5051525354555658595a00000000000000 // gas irOptimized: 202083 // gas legacy: 204327 -// gas legacyOptimized: 202899 +// gas legacyOptimized: 202903 diff --git a/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol b/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol index f776626ae8e3..03c47f69b414 100644 --- a/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol +++ b/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol @@ -16,6 +16,6 @@ contract c { } // ---- // test1() -> true -// gas irOptimized: 218449 +// gas irOptimized: 218420 // gas legacy: 242263 -// gas legacyOptimized: 241182 +// gas legacyOptimized: 241187 diff --git a/test/libsolidity/semanticTests/array/delete/delete_bytes_array.sol b/test/libsolidity/semanticTests/array/delete/delete_bytes_array.sol index b735d182d555..32a96c72c310 100644 --- a/test/libsolidity/semanticTests/array/delete/delete_bytes_array.sol +++ b/test/libsolidity/semanticTests/array/delete/delete_bytes_array.sol @@ -32,3 +32,4 @@ contract C { // ---- // f() -> 0 // g() -> 0 +// gas irOptimized: 56617 diff --git a/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol b/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol index 9665201c98fd..23b0466e7530 100644 --- a/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol +++ b/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol @@ -14,6 +14,6 @@ contract C { } // ---- // f() -> 0, 0, 0 -// gas irOptimized: 90992 -// gas legacy: 111037 -// gas legacyOptimized: 109633 +// gas irOptimized: 88028 +// gas legacy: 88799 +// gas legacyOptimized: 87706 diff --git a/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol b/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol index ebe2186fd60f..af0ec6b7faf0 100644 --- a/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol +++ b/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol @@ -15,7 +15,7 @@ contract c { // gas legacyOptimized: 466238 // storageEmpty -> 0 // clear() -> -// gas irOptimized: 122148 -// gas legacy: 122440 -// gas legacyOptimized: 122259 +// gas irOptimized: 97800 +// gas legacy: 97947 +// gas legacyOptimized: 97882 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/invalid_encoding_for_storage_byte_array.sol b/test/libsolidity/semanticTests/array/invalid_encoding_for_storage_byte_array.sol index 95df74b4d77b..a53d00eaaf31 100644 --- a/test/libsolidity/semanticTests/array/invalid_encoding_for_storage_byte_array.sol +++ b/test/libsolidity/semanticTests/array/invalid_encoding_for_storage_byte_array.sol @@ -40,16 +40,18 @@ contract C { // copyFromStorageShort() // x() -> 0x20, 3, 0x6162630000000000000000000000000000000000000000000000000000000000 // copyFromStorageLong() -// gas irOptimized: 121095 +// gas irOptimized: 121092 // gas legacy: 121904 -// gas legacyOptimized: 121388 +// gas legacyOptimized: 121393 // x() -> 0x20, 0x25, 0x3132333435363738393031323334353637383930313233343536373839303132, 0x3334353637000000000000000000000000000000000000000000000000000000 // copyToStorage() // x() -> 0x20, 0x25, 0x3132333435363738393031323334353637383930313233343536373839303132, 0x3334353637000000000000000000000000000000000000000000000000000000 // y() -> 0x20, 0x25, 0x3132333435363738393031323334353637383930313233343536373839303132, 0x3334353637000000000000000000000000000000000000000000000000000000 // del() +// gas irOptimized: 29404 // x() -> 0x20, 0x00 // invalidateXLong() +// gas irOptimized: 47028 // x() -> FAILURE, hex"4e487b71", 0x22 // abiEncode() -> FAILURE, hex"4e487b71", 0x22 // abiEncodePacked() -> FAILURE, hex"4e487b71", 0x22 diff --git a/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_delete.sol b/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_delete.sol new file mode 100644 index 000000000000..b638664aaca1 --- /dev/null +++ b/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_delete.sol @@ -0,0 +1,77 @@ +contract C { + bytes testArray; + + struct Canary { + uint256 value; + } + + function getCanary() internal pure returns (Canary storage canary) { + assembly { + mstore(0, testArray.slot) + let testArrayDataArea := keccak256(0, 0x20) + // testArray's data area occupies 3 slots when filled. Canary goes right after + canary.slot := add(testArrayDataArea, 3) + } + } + + constructor() { + Canary storage canary = getCanary(); + canary.value = type(uint256).max; + } + + function getArrayDataAreaSlot() public pure returns (uint256 slot) { + assembly { + mstore(0, testArray.slot) + slot := keccak256(0, 0x20) + } + assert(slot == 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563); + return slot; + } + + function getCanarySlot() public pure returns (uint256) { + return getArrayDataAreaSlot() + 3; + } + + function checkSlots() public view returns (uint256, uint256, uint256, uint256, uint256) { + uint256 dataSlot = getArrayDataAreaSlot(); + uint256 slot0; + uint256 slot1; + uint256 slot2; + uint256 slot3; + assembly { + slot0 := sload(dataSlot) + slot1 := sload(add(dataSlot, 1)) + slot2 := sload(add(dataSlot, 2)) + slot3 := sload(add(dataSlot, 3)) + } + return (slot0, slot1, slot2, slot3, testArray.length); + } + + function fillArray() public { + // Fill testArray to exactly 96 bytes (3 slots in its data area) + for (uint i = 0; i < 96; i++) { + testArray.push(bytes1(uint8(i + 1))); + } + } + + function deleteArray() public { + // Should clear 3 slots without touching canary + delete testArray; + } + + function canaryValue() public view returns (uint256) { + return getCanary().value; + } +} +// ---- +// getArrayDataAreaSlot() -> 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 +// getCanarySlot() -> 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e566 +// checkSlots() -> 0, 0, 0, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// fillArray() +// gas irOptimized: 197289 +// gas legacy: 220574 +// gas legacyOptimized: 206839 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// deleteArray() +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_overwrite_with_long.sol b/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_overwrite_with_long.sol new file mode 100644 index 000000000000..edb00809bcd3 --- /dev/null +++ b/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_overwrite_with_long.sol @@ -0,0 +1,105 @@ +contract C { + bytes testArray; + + struct Canary { + uint256 value; + } + + function getCanary() internal pure returns (Canary storage canary) { + assembly { + mstore(0, testArray.slot) + let testArrayDataArea := keccak256(0, 0x20) + // testArray's data area occupies 3 slots when filled. Canary goes right after + canary.slot := add(testArrayDataArea, 3) + } + } + + constructor() { + Canary storage canary = getCanary(); + canary.value = type(uint256).max; // Should not be overwritten + } + + function fillArray() public { + // Fill testArray to exactly 96 bytes (3 slots in its data area) + for (uint i = 0; i < 96; i++) { + testArray.push(bytes1(uint8(i + 1))); + } + } + + function shrinkArray() public returns (uint256) { + // Shrink from 96 to 50 bytes. Should clear slot 2 without touching canary + bytes memory newData = new bytes(50); + for (uint i = 0; i < 50; i++) { + newData[i] = bytes1(uint8(i + 2)); + } + testArray = newData; + return testArray.length; + } + + function canaryValue() public view returns (uint256) { + return getCanary().value; + } + + function arrayLength() public view returns (uint256) { + return testArray.length; + } + + function getDataSlotContent(uint256 index) public view returns (bytes32 value) { + assembly { + mstore(0, testArray.slot) + let testArrayDataArea := keccak256(0, 0x20) + let slot := add(testArrayDataArea, index) + value := sload(slot) + } + return value; + } + + function checkSlots() public view returns (bytes32, bytes32, bytes32, uint256, uint256) { + return ( + getDataSlotContent(0), // First data slot + getDataSlotContent(1), // Second data slot (partial cleanup expected) + getDataSlotContent(2), // Third data slot (should be cleared after shrink) + canaryValue(), // Canary value (should never change) + arrayLength() // Current array length + ); + } + + function getSlot1LastBytes() public view returns (bytes14 lastBytes) { + // Get the last 14 bytes of slot 1, which should be zero after partial cleanup + bytes32 slot1 = getDataSlotContent(1); + assembly { + // Shift left by 18 bytes (144 bits) to move the last 14 bytes to the front + lastBytes := shl(144, slot1) + } + return lastBytes; + } + + function getArrayBytes(uint256 start, uint256 count) public view returns (bytes memory) { + bytes memory result = new bytes(count); + for (uint i = 0; i < count && start + i < testArray.length; i++) { + result[i] = testArray[start + i]; + } + return result; + } +} +// ---- +// arrayLength() ->0 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// fillArray() +// gas irOptimized: 197352 +// gas legacy: 220574 +// gas legacyOptimized: 206839 +// arrayLength() ->96 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getArrayBytes(uint256,uint256): 0, 5 -> 0x20, 5, 0x0102030405000000000000000000000000000000000000000000000000000000 +// getArrayBytes(uint256,uint256): 32, 5 -> 0x20, 5, 0x2122232425000000000000000000000000000000000000000000000000000000 +// getArrayBytes(uint256,uint256): 64, 5 -> 0x20, 5, 0x4142434445000000000000000000000000000000000000000000000000000000 +// shrinkArray() -> 50 +// arrayLength() ->50 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getArrayBytes(uint256,uint256): 0, 5 -> 0x20, 5, 0x0203040506000000000000000000000000000000000000000000000000000000 +// getArrayBytes(uint256,uint256): 32, 5 -> 0x20, 5, 0x2223242526000000000000000000000000000000000000000000000000000000 +// getArrayBytes(uint256,uint256): 45, 5 -> 0x20, 5, 0x2f30313233000000000000000000000000000000000000000000000000000000 +// getSlot1LastBytes() -> 0 +// getDataSlotContent(uint256): 2 -> 0 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/array/nested_calldata_storage2.sol b/test/libsolidity/semanticTests/array/nested_calldata_storage2.sol index f8db31b54cc8..f3cb51187103 100644 --- a/test/libsolidity/semanticTests/array/nested_calldata_storage2.sol +++ b/test/libsolidity/semanticTests/array/nested_calldata_storage2.sol @@ -8,6 +8,6 @@ contract C { // compileViaYul: true // ---- // i(uint256[][]): 0x20, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -// gas irOptimized: 245506 +// gas irOptimized: 245511 // tmp_i(uint256,uint256): 0, 0 -> 0x0A01 // tmp_i(uint256,uint256): 1, 0 -> 0x0B01 diff --git a/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol b/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol index 96fb11a2dc94..3b96397166ea 100644 --- a/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol +++ b/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol @@ -23,7 +23,7 @@ contract c { } // ---- // test() -> 1, 2, 3 -// gas irOptimized: 1828226 -// gas legacy: 1822464 -// gas legacyOptimized: 1813404 +// gas irOptimized: 1828383 +// gas legacy: 1841995 +// gas legacyOptimized: 1821687 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/push/array_push_nested_from_calldata.sol b/test/libsolidity/semanticTests/array/push/array_push_nested_from_calldata.sol index 77ae8c4e23e4..83c5c1724307 100644 --- a/test/libsolidity/semanticTests/array/push/array_push_nested_from_calldata.sol +++ b/test/libsolidity/semanticTests/array/push/array_push_nested_from_calldata.sol @@ -13,5 +13,5 @@ contract C { // ---- // f(uint120[]): 0x20, 3, 1, 2, 3 -> 1 // gas irOptimized: 112852 -// gas legacy: 113657 -// gas legacyOptimized: 113465 +// gas legacy: 114404 +// gas legacyOptimized: 113087 diff --git a/test/libsolidity/semanticTests/array/push/array_push_struct.sol b/test/libsolidity/semanticTests/array/push/array_push_struct.sol index 92f071e1fb22..4e1e8b4b3db0 100644 --- a/test/libsolidity/semanticTests/array/push/array_push_struct.sol +++ b/test/libsolidity/semanticTests/array/push/array_push_struct.sol @@ -21,5 +21,5 @@ contract c { // ---- // test() -> 2, 3, 4, 5 // gas irOptimized: 135329 -// gas legacy: 147437 -// gas legacyOptimized: 146429 +// gas legacy: 139481 +// gas legacyOptimized: 135795 diff --git a/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol b/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol index 5633199d4865..04b2a4a736c9 100644 --- a/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol +++ b/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol @@ -15,4 +15,4 @@ contract C { // f() -> // gas irOptimized: 179534 // gas legacy: 181013 -// gas legacyOptimized: 180397 +// gas legacyOptimized: 180406 diff --git a/test/libsolidity/semanticTests/calldata/copy_from_calldata_removes_bytes_data.sol b/test/libsolidity/semanticTests/calldata/copy_from_calldata_removes_bytes_data.sol index 33ab18450624..c3f2e8d751e5 100644 --- a/test/libsolidity/semanticTests/calldata/copy_from_calldata_removes_bytes_data.sol +++ b/test/libsolidity/semanticTests/calldata/copy_from_calldata_removes_bytes_data.sol @@ -11,7 +11,8 @@ contract c { // (): 1, 2, 3, 4, 5 -> // gas irOptimized: 155122 // gas legacy: 155473 -// gas legacyOptimized: 155295 +// gas legacyOptimized: 155296 // checkIfDataIsEmpty() -> false // sendMessage() -> true, 0x40, 0 +// gas irOptimized: 41925 // checkIfDataIsEmpty() -> true diff --git a/test/libsolidity/semanticTests/cleanup/byte_array_to_storage_cleanup.sol b/test/libsolidity/semanticTests/cleanup/byte_array_to_storage_cleanup.sol index 238f96b7e588..4c75ad452b8d 100644 --- a/test/libsolidity/semanticTests/cleanup/byte_array_to_storage_cleanup.sol +++ b/test/libsolidity/semanticTests/cleanup/byte_array_to_storage_cleanup.sol @@ -28,13 +28,14 @@ contract C { // compileViaYul: also // ---- // constructor() -> -// gas irOptimized: 82100 -// gas irOptimized code: 357600 -// gas legacy: 101532 -// gas legacy code: 604800 -// gas legacyOptimized: 84956 -// gas legacyOptimized code: 391800 +// gas irOptimized: 82586 +// gas irOptimized code: 363600 +// gas legacy: 101811 +// gas legacy code: 608200 +// gas legacyOptimized: 85196 +// gas legacyOptimized code: 394800 // h() -> 0x20, 0x40, 0x00, 0 // ~ emit ev(uint256[],uint256): 0x40, 0x21, 0x02, 0x00, 0x00 // g() -> 0x20, 0x40, 0, 0x00 // f(bytes): 0x20, 33, 0, -1 -> 0x20, 0x22, 0, 0xff00000000000000000000000000000000000000000000000000000000000000 +// gas irOptimized: 54117 diff --git a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol index aeeb1a0c1aa4..76a4dd961fe4 100644 --- a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol +++ b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol @@ -28,7 +28,7 @@ contract Creator { // f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8 // gas irOptimized: 327784 // gas irOptimized code: 94000 -// gas legacy: 336623 +// gas legacy: 338477 // gas legacy code: 244800 -// gas legacyOptimized: 329515 +// gas legacyOptimized: 329166 // gas legacyOptimized code: 117000 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol index 55abe4804c56..91be8e3789f3 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol @@ -26,9 +26,9 @@ contract Creator { // bytecodeFormat: legacy,>=EOFv1 // ---- // f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h" -// gas irOptimized: 169292 +// gas irOptimized: 169297 // gas irOptimized code: 99600 -// gas legacy: 172941 +// gas legacy: 172946 // gas legacy code: 239800 -// gas legacyOptimized: 169815 +// gas legacyOptimized: 169823 // gas legacyOptimized code: 118600 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol index 13db633b9b06..8a072f3ebed6 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol @@ -10,11 +10,11 @@ contract Test { // bytecodeFormat: legacy,>=EOFv1 // ---- // constructor(): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> -// gas irOptimized: 181465 +// gas irOptimized: 181629 // gas irOptimized code: 78400 -// gas legacy: 195212 +// gas legacy: 195484 // gas legacy code: 109400 -// gas legacyOptimized: 181608 +// gas legacyOptimized: 181853 // gas legacyOptimized code: 71400 // m_x() -> 7 // m_s() -> 0x20, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" diff --git a/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol index ed4eb6e4d15f..cc196a95c802 100644 --- a/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol +++ b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol @@ -13,9 +13,9 @@ contract C { // constructor(): 1, 2, 3, 4 -> // gas irOptimized: 148129 // gas irOptimized code: 23000 -// gas legacy: 157977 +// gas legacy: 166201 // gas legacy code: 60400 -// gas legacyOptimized: 149973 +// gas legacyOptimized: 149177 // gas legacyOptimized code: 26200 // a() -> 1 // b(uint256): 0 -> 2 diff --git a/test/libsolidity/semanticTests/events/event_dynamic_nested_array_storage_v2.sol b/test/libsolidity/semanticTests/events/event_dynamic_nested_array_storage_v2.sol index 1ff58d65b566..42b9b50dd779 100644 --- a/test/libsolidity/semanticTests/events/event_dynamic_nested_array_storage_v2.sol +++ b/test/libsolidity/semanticTests/events/event_dynamic_nested_array_storage_v2.sol @@ -16,5 +16,5 @@ contract C { // createEvent(uint256): 42 -> // ~ emit E(uint256[][]): 0x20, 0x02, 0x40, 0xa0, 0x02, 0x2a, 0x2b, 0x02, 0x2c, 0x2d // gas irOptimized: 185148 -// gas legacy: 187493 +// gas legacy: 188615 // gas legacyOptimized: 184548 diff --git a/test/libsolidity/semanticTests/fallback/call_forward_bytes.sol b/test/libsolidity/semanticTests/fallback/call_forward_bytes.sol index 7bf3aee3acfe..dfd3d658fe2f 100644 --- a/test/libsolidity/semanticTests/fallback/call_forward_bytes.sol +++ b/test/libsolidity/semanticTests/fallback/call_forward_bytes.sol @@ -20,6 +20,7 @@ contract sender { // forward() -> true // val() -> 8 // clear() -> true +// gas irOptimized: 29243 // val() -> 8 // forward() -> true // val() -> 0x80 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_inherited_storage_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_inherited_storage_same_value_type.sol new file mode 100644 index 000000000000..dae46cd32b51 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_inherited_storage_same_value_type.sol @@ -0,0 +1,25 @@ +contract Base { + uint256 public x; + + function deleteX() internal { + delete x; + } +} + +contract C is Base { + uint256 transient t; + + function setAndClear() external { + x = 1; + t = 2; + deleteX(); + delete t; + assert(t == 0); + } +} + +// ==== +// EVMVersion: >=cancun +// ---- +// setAndClear() -> +// x() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_array_delete_different_base_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_array_delete_different_base_type.sol new file mode 100644 index 000000000000..d9a904f3caff --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_array_delete_different_base_type.sol @@ -0,0 +1,23 @@ +contract C { + bool[3] flags = [true, true, true]; + uint256 transient temp; + + function setAndClear() external { + temp = 0xffffffff; + delete flags; + delete temp; + assert(temp == 0); + } + + function getFlags() external returns(bool[3] memory) + { + return flags; + } +} + +// ==== +// EVMVersion: >=cancun +// ---- +// getFlags() -> true, true, true +// setAndClear() -> +// getFlags() -> false, false, false diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_array_pop_same_base_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_array_pop_same_base_type.sol new file mode 100644 index 000000000000..76dc10c43c11 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_array_pop_same_base_type.sol @@ -0,0 +1,30 @@ +contract C { + uint256[] arr; + uint256 transient t; + + function pushArr() external { + arr.push(1); + } + + function setAndClear() external { + t = 2; + delete t; + assert(t == 0); + arr.pop(); + } + + // Get value at index 0, which should have been cleared after arr.pop() + function getArr() external returns (uint256 value) { + assembly { + mstore(0, arr.slot) + value := sload(keccak256(0x00, 0x20)) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// pushArr() -> +// getArr() -> 1 +// setAndClear() -> +// getArr() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_delete_same_value_type.sol new file mode 100644 index 000000000000..ad206c6eeee0 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_delete_same_value_type.sol @@ -0,0 +1,18 @@ +contract C { + uint256 transient varTransient; + uint256 public varStorage = 0xeeeeeeeeee; + + function setAndClear() external { + varTransient = 0xffffffff; + delete varStorage; + delete varTransient; + assert(varTransient == 0); + } +} + +// ==== +// EVMVersion: >=cancun +// ---- +// varStorage() -> 0xeeeeeeeeee +// setAndClear() -> +// varStorage() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_mapping_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_mapping_delete_same_value_type.sol new file mode 100644 index 000000000000..842cd47504b9 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_mapping_delete_same_value_type.sol @@ -0,0 +1,22 @@ +contract C { + mapping(uint256 => uint256) m; + uint256 transient t; + + function setAndClear() external { + m[0] = 1; + t = 2; + delete m[0]; + delete t; + assert(t == 0); + } + + function getM() external view returns (uint256) { + return m[0]; + } +} + +// ==== +// EVMVersion: >=cancun +// ---- +// setAndClear() -> +// getM() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_struct_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_struct_delete_same_value_type.sol new file mode 100644 index 000000000000..703816296268 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_struct_delete_same_value_type.sol @@ -0,0 +1,26 @@ +contract C { + struct S { + uint256 a; + address b; + } + + S s = S(1, address(0x1234)); + uint256 transient t; + + function setAndDelete() external { + t = 2; + delete s; + delete t; + assert(t == 0); + } + + function getS() external view returns (uint256, address) { + return (s.a, s.b); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// getS() -> 1, 4660 +// setAndDelete() -> +// getS() -> 0, 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_inherited_storage_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_inherited_storage_same_value_type.sol new file mode 100644 index 000000000000..3af1b66e591c --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_inherited_storage_same_value_type.sol @@ -0,0 +1,25 @@ +contract Base { + uint256 public x; + + function deleteX() internal { + delete x; + } +} + +contract C is Base { + uint256 transient t; + + function setAndClear() external { + x = 1; + t = 2; + delete t; + assert(t == 0); + deleteX(); + } +} + +// ==== +// EVMVersion: >=cancun +// ---- +// setAndClear() -> +// x() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_array_delete_different_base_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_array_delete_different_base_type.sol new file mode 100644 index 000000000000..c2e1c1a241cc --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_array_delete_different_base_type.sol @@ -0,0 +1,23 @@ +contract C { + bool[3] flags = [true, true, true]; + uint256 transient temp; + + function setAndClear() external { + temp = 0xffffffff; + delete temp; + assert(temp == 0); + delete flags; + } + + function getFlags() external returns(bool[3] memory) + { + return flags; + } +} + +// ==== +// EVMVersion: >=cancun +// ---- +// getFlags() -> true, true, true +// setAndClear() -> +// getFlags() -> false, false, false diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_array_partial_assignment_same_base_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_array_partial_assignment_same_base_type.sol new file mode 100644 index 000000000000..a9d3e0ae7384 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_array_partial_assignment_same_base_type.sol @@ -0,0 +1,27 @@ +contract C { + uint256[2] small; + uint256[4] large; + uint256 transient t; + + function setAndClear() external { + large = [1,2,3,4]; + small = [10, 20]; + t = 99; + + delete t; + assert(t == 0); + large = small; + } + + function getLarge() external view returns (uint256[4] memory) { + return large; + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// setAndClear() -> +// gas irOptimized: 124683 +// gas legacy: 127807 +// gas legacyOptimized: 124828 +// getLarge() -> 10, 20, 0, 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_delete_same_value_type.sol new file mode 100644 index 000000000000..26e34fc26a55 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_delete_same_value_type.sol @@ -0,0 +1,18 @@ +contract C { + uint256 transient varTransient; + uint256 public varStorage = 0xeeeeeeeeee; + + function setAndClear() external { + varTransient = 0xffffffff; + delete varTransient; + assert(varTransient == 0); + delete varStorage; + } +} + +// ==== +// EVMVersion: >=cancun +// ---- +// varStorage() -> 0xeeeeeeeeee +// setAndClear() -> +// varStorage() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_mapping_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_mapping_delete_same_value_type.sol new file mode 100644 index 000000000000..6e262c8a6269 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_mapping_delete_same_value_type.sol @@ -0,0 +1,22 @@ +contract C { + mapping(uint256 => uint256) m; + uint256 transient t; + + function setAndClear() external { + m[0] = 1; + t = 2; + delete t; + assert(t == 0); + delete m[0]; + } + + function getM() external view returns (uint256) { + return m[0]; + } +} + +// ==== +// EVMVersion: >=cancun +// ---- +// setAndClear() -> +// getM() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_struct_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_struct_delete_same_value_type.sol new file mode 100644 index 000000000000..c2127c9bad1f --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_struct_delete_same_value_type.sol @@ -0,0 +1,26 @@ +contract C { + struct S { + uint256 a; + address b; + } + + S s = S(1, address(0x1234)); + uint256 transient t; + + function setAndDelete() external { + t = 2; + delete t; + assert(t == 0); + delete s; + } + + function getS() external view returns (uint256, address) { + return (s.a, s.b); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// getS() -> 1, 4660 +// setAndDelete() -> +// getS() -> 0, 0 diff --git a/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol b/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol index 8a795373e061..8d72f7c04358 100644 --- a/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol +++ b/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol @@ -22,11 +22,12 @@ contract Test { // set(bytes): 0x20, 0 // storageEmpty -> 1 // set(bytes): 0x20, 66, "12345678901234567890123456789012", "12345678901234567890123456789012", "12" -// gas irOptimized: 111849 +// gas irOptimized: 111820 // gas legacy: 112734 -// gas legacyOptimized: 112084 +// gas legacyOptimized: 112089 // storageEmpty -> 0 // set(bytes): 0x20, 3, "abc" +// gas irOptimized: 33989 // storageEmpty -> 0 // set(bytes): 0x20, 0 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/storage/static_array_copy_cleanup.sol b/test/libsolidity/semanticTests/storage/static_array_copy_cleanup.sol new file mode 100644 index 000000000000..ca471161d5e2 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/static_array_copy_cleanup.sol @@ -0,0 +1,94 @@ +contract C { + struct S { + uint64 a; + uint64 b; + uint64 c; + uint64 d; + // All fit in one slot (4 * 64 = 256 bits) + } + + S[5] source; + S[10] dest; + uint256 public canary = type(uint256).max; + + function fillSource() public { + for (uint i = 0; i < 5; i++) { + source[i] = S({ + a: uint64(1 + i * 4), + b: uint64(2 + i * 4), + c: uint64(3 + i * 4), + d: uint64(4 + i * 4) + }); + } + } + + function fillDest() public { + for (uint i = 0; i < 10; i++) { + dest[i] = S({ + a: uint64(100 + i * 4), + b: uint64(101 + i * 4), + c: uint64(102 + i * 4), + d: uint64(103 + i * 4) + }); + } + } + + function copySourceToDest() public { + dest = source; + } + + function deleteSource() public { + delete source; + } + + function deleteDest() public { + delete dest; + } + + function getSourceAsUint() public view returns (uint64[20] memory result) { + for (uint i = 0; i < 5; i++) { + result[i * 4] = source[i].a; + result[i * 4 + 1] = source[i].b; + result[i * 4 + 2] = source[i].c; + result[i * 4 + 3] = source[i].d; + } + } + + function getDestAsUint() public view returns (uint64[40] memory result) { + for (uint i = 0; i < 10; i++) { + result[i * 4] = dest[i].a; + result[i * 4 + 1] = dest[i].b; + result[i * 4 + 2] = dest[i].c; + result[i * 4 + 3] = dest[i].d; + } + } +} +// ---- +// canary() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getSourceAsUint() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// getDestAsUint() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillSource() +// gas irOptimized: 135018 +// gas legacy: 146851 +// gas legacyOptimized: 137549 +// canary() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getSourceAsUint() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 +// fillDest() +// gas irOptimized: 248706 +// gas legacy: 272468 +// gas legacyOptimized: 253871 +// canary() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getSourceAsUint() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 +// getDestAsUint() -> 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139 +// copySourceToDest() +// canary() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getSourceAsUint() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 +// getDestAsUint() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// deleteSource() +// canary() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getSourceAsUint() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// getDestAsUint() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// deleteDest() +// canary() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getSourceAsUint() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// getDestAsUint() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_and_partial_assignment_with_layout.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_and_partial_assignment_with_layout.sol new file mode 100644 index 000000000000..1e2637c851f9 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_and_partial_assignment_with_layout.sol @@ -0,0 +1,54 @@ +contract C layout at 2**256 - 5 { + uint256 a; + + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := a.slot + } + } + + function fillArray() public { + uint256[10][1] storage _x = getArray(); + for (uint i = 1; i < 10; i++) + _x[0][i] = i; + } + + function partialAssignArrayBeforeStorageBoundary() public { + uint256[10][1] storage _x = getArray(); + _x[0] = [11, 12, 13]; + } + + function partialAssignArrayCrossStorageBoundary() public { + uint256[10][1] storage _x = getArray(); + _x[0] = [14, 15, 16, 17, 18, 19, 20]; + } + + function clearArray() public { + uint256[10][1] storage _x = getArray(); + delete _x[0]; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 220749 +// gas legacy: 221473 +// gas legacyOptimized: 220915 +// partialAssignArrayBeforeStorageBoundary() +// x() -> 11, 12, 13, 0, 0, 0, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 186549 +// gas legacy: 187273 +// gas legacyOptimized: 186715 +// x() -> 11, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// partialAssignArrayCrossStorageBoundary() +// x() -> 14, 15, 16, 17, 18, 19, 20, 0, 0, 0 +// clearArray() +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 44183 +// gas legacy: 46007 +// gas legacyOptimized: 43907 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_assignment.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_assignment.sol new file mode 100644 index 000000000000..0de66a71472f --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_assignment.sol @@ -0,0 +1,28 @@ +contract C { + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function assignArray(uint256[10] memory y) public { + uint256[10][1] storage _x = getArray(); + _x[0] = y; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// assignArray(uint256[10]): 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> +// gas irOptimized: 245236 +// gas legacy: 249351 +// gas legacyOptimized: 245365 +// x() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// assignArray(uint256[10]): 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 -> +// x() -> 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 +// gas irOptimized: 44183 +// gas legacy: 46012 +// gas legacyOptimized: 43907 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_copy.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_copy.sol new file mode 100644 index 000000000000..949edc4907eb --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_copy.sol @@ -0,0 +1,61 @@ +contract C { + constructor() { + uint256[10][1] storage _x = getX(); + _x[0] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + } + + function getX() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function getY() internal pure returns (uint256[10][1] storage _y) { + assembly { + _y.slot := 5 + } + } + + function copyXToY() public { + uint256[10][1] storage _x = getX(); + uint256[10][1] storage _y = getY(); + _y[0] = _x[0]; + } + + function clearX() public { + uint256[10][1] storage _x = getX(); + delete _x[0]; + } + + function copyYToX() public { + uint256[10][1] storage _x = getX(); + uint256[10][1] storage _y = getY(); + _x[0] = _y[0]; + } + + function x() public view returns (uint256[10] memory) { + return getX()[0]; + } + + function y() public view returns (uint256[10] memory) { + return getY()[0]; + } +} +// ---- +// x() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// y() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// copyXToY() +// gas irOptimized: 264224 +// gas legacy: 265434 +// gas legacyOptimized: 264247 +// x() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// y() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// clearX() +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// y() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// copyYToX() +// gas irOptimized: 266243 +// gas legacy: 267456 +// gas legacyOptimized: 266280 +// x() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// y() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_delete.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_delete.sol new file mode 100644 index 000000000000..20d2e2ec9df5 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_delete.sol @@ -0,0 +1,34 @@ +contract C { + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function fillArray() public { + uint256[10][1] storage _x = getArray(); + for (uint i = 1; i < 10; i++) + _x[0][i] = i; + } + + function clearArray() public { + uint256[10][1] storage _x = getArray(); + delete _x[0]; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 220705 +// gas legacy: 221434 +// gas legacyOptimized: 220871 +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// clearArray() +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 44183 +// gas legacy: 46012 +// gas legacyOptimized: 43907 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_delete_overlapping_variable.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_delete_overlapping_variable.sol new file mode 100644 index 000000000000..3bb605cc919e --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_delete_overlapping_variable.sol @@ -0,0 +1,40 @@ +contract C { + uint256 public y = 42; + + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function fillArray() public { + uint256[10][1] storage _x = getArray(); + for (uint i = 1; i < 10; i++) + _x[0][i] = i; + } + + function clearArray() public { + uint256[10][1] storage _x = getArray(); + delete _x[0]; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } +} + +// ---- +// y() -> 42 +// x() -> 0, 0, 0, 0, 0, 42, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 203627 +// gas legacy: 204356 +// gas legacyOptimized: 203793 +// y() -> 5 +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// clearArray() +// y() -> 0 +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 44183 +// gas legacy: 46012 +// gas legacyOptimized: 43907 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_packing_not_overlapping_variable.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_packing_not_overlapping_variable.sol new file mode 100644 index 000000000000..77a6158acc84 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_packing_not_overlapping_variable.sol @@ -0,0 +1,63 @@ +contract C { + struct Canary { + uint256 value; + } + + constructor() { + Canary storage canary = getCanary(); + canary.value = type(uint256).max; // Should not be overwritten + } + + function getArray() internal pure returns (uint64[10][1] storage _x) { + // Array of 10 * uint64 values (8 bytes each) + // Packs 4 uint64 per slot -> 3 slots total (slots -1, 0, 1) + assembly { + _x.slot := sub(0, 1) + } + } + + function getCanary() internal pure returns (Canary storage canary) { + // Canary at slot 2, right after the array ends at slot 1 + assembly { + canary.slot := 2 + } + } + + function fillArray() public { + uint64[10][1] storage _x = getArray(); + for (uint64 i = 0; i < 10; i++) + _x[0][i] = i; + } + + function shrinkTo5() public { + uint64[10][1] storage _x = getArray(); + // Resize by assigning a smaller array + // This should clear items [5..9] without touching y + _x[0] = [uint64(11), 12, 13, 14, 15]; + } + + function clearArray() public { + uint64[10][1] storage _x = getArray(); + delete _x[0]; + } + + function x() public view returns (uint64[10] memory) { + return getArray()[0]; + } + + function canaryValue() public view returns (uint256) { + return getCanary().value; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// fillArray() +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shrinkTo5() +// x() -> 11, 12, 13, 14, 15, 0, 0, 0, 0, 0 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// clearArray() +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_partial_assignment.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_partial_assignment.sol new file mode 100644 index 000000000000..c4e3ee7794c0 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_partial_assignment.sol @@ -0,0 +1,41 @@ +contract C { + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function fillArray() public { + uint256[10][1] storage _x = getArray(); + for (uint i = 1; i < 10; i++) + _x[0][i] = i; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } + + function partialAssignArrayBeforeStorageBoundary() public { + uint256[10][1] storage _x = getArray(); + _x[0] = [21, 22, 23]; + } + + function partialAssignArrayCrossStorageBoundary() public { + uint256[10][1] storage _x = getArray(); + _x[0] = [11, 12, 13, 14, 15, 16, 17]; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 220727 +// gas legacy: 221456 +// gas legacyOptimized: 220893 +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// partialAssignArrayCrossStorageBoundary() +// x() -> 11, 12, 13, 14, 15, 16, 17, 0, 0, 0 +// partialAssignArrayBeforeStorageBoundary() +// x() -> 21, 22, 23, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 44183 +// gas legacy: 46012 +// gas legacyOptimized: 43907 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_delete_overflow_bug.sol b/test/libsolidity/semanticTests/storage/storage_boundary_delete_overflow_bug.sol new file mode 100644 index 000000000000..3ffb7774710a --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_delete_overflow_bug.sol @@ -0,0 +1,80 @@ +contract C { + mapping(string => uint256[256][2**240]) m; + + function getSlot() internal view returns (uint256) { + uint256[256][2**240] storage _x = m["v 2.2.3"]; + uint256 slot; + assembly { + slot := _x.slot + } + assert(slot == 0xffdb3f1d9f54eb0b5012935c286c508459d381405d269e01c15f4ec2826edbbf); + return slot; + } + + function getIndex() internal view returns (uint256) { + uint256 slot = getSlot(); + // Pick the largest index such that `slot + 256 * index` <= `2**256 - 1` + uint256 index = (type(uint256).max - slot) / 256; + assert(index <= type(uint240).max); + assert((type(uint256).max - slot + 1) % 256 != 0); + + return index; + } + + function getArray() internal view returns (uint256[256][2**240] storage _x) { + uint256 s = getSlot(); + assembly { + _x.slot := s + } + } + + function fillArray() public { + uint256[256][2**240] storage _x = getArray(); + for (uint i = 1; i < 256; i++) + _x[getIndex()][i] = i; + } + + function partialAssignArray() public { + uint256[256][2**240] storage _x = getArray(); + _x[getIndex()] = [11, 22, 33, 44, 55, 66, 77, 88, 99]; + } + + function clearArray() public { + uint256[256][2**240] storage _x = getArray(); + delete _x[getIndex()]; + } + + function x() public view returns (uint256[256] memory) { + return getArray()[getIndex()]; + } +} + +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 604108 +// gas legacy: 644983 +// gas legacyOptimized: 598016 +// fillArray() +// gas irOptimized: 5782148 +// gas legacy: 6044562 +// gas legacyOptimized: 5853893 +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 +// gas irOptimized: 604108 +// gas legacy: 644983 +// gas legacyOptimized: 598016 +// partialAssignArray() +// gas irOptimized: 1067376 +// gas legacy: 1177356 +// gas legacyOptimized: 1068067 +// x() -> 11, 22, 33, 44, 55, 66, 77, 88, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 604108 +// gas legacy: 644983 +// gas legacyOptimized: 598016 +// clearArray() +// gas irOptimized: 580378 +// gas legacy: 582976 +// gas legacyOptimized: 581290 +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 604108 +// gas legacy: 644983 +// gas legacyOptimized: 598016 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_packed_array.sol b/test/libsolidity/semanticTests/storage/storage_boundary_packed_array.sol new file mode 100644 index 000000000000..aa30a2bcac49 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_packed_array.sol @@ -0,0 +1,35 @@ +contract C { + function getArray() internal pure returns (uint64[40][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function fillArray() public { + uint64[40][1] storage _x = getArray(); + for (uint64 i = 1; i < 40; i++) + _x[0][i] = i; + } + + function clearArray() public { + uint64[40][1] storage _x = getArray(); + delete _x[0]; + } + + function x() public view returns (uint64[40] memory) { + return getArray()[0]; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 254227 +// gas legacy: 258712 +// gas legacyOptimized: 257258 +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39 +// clearArray() +// gas irOptimized: 57426 +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 48087 +// gas legacy: 64080 +// gas legacyOptimized: 56602 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol new file mode 100644 index 000000000000..213dee1dae23 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol @@ -0,0 +1,172 @@ +pragma abicoder v2; + +contract C { + struct S { + uint256 a; // slot 0 (bytes 0-31) + uint128 b; // slot 1 (bytes 0-15) + uint64 c; // slot 1 (bytes 16-23) + bytes32 d; // slot 2 (bytes 0-31) + bool e; // slot 3 (byte 0) + // Total: 4 slots per struct + } + + struct Canary { + uint256 value; + } + + function getBoundaryArray() internal pure returns (S[10][1] storage arr) { + // 10 structs * 4 slots = 40 slots total + // Starts at -20, ends at slot 19 + assembly { + arr.slot := sub(0, 20) + } + } + + function getDest() internal pure returns (S[10][1] storage arr) { + assembly { + arr.slot := 21 + } + } + + function getCanary() internal pure returns (Canary storage canary) { + // Array ends at slot 19, canary at slot 20 + assembly { + canary.slot := 20 + } + } + + constructor() { + Canary storage canary = getCanary(); + canary.value = type(uint256).max; + } + + function fillBoundaryArray() public { + S[10][1] storage arr = getBoundaryArray(); + for (uint i = 0; i < 10; i++) { + arr[0][i] = S({ + a: 1 + i * 5, + b: uint128(2 + i * 5), + c: uint64(3 + i * 5), + d: bytes32(uint256(4 + i * 5)), + e: true + }); + } + } + + function deleteBoundaryArray() public { + S[10][1] storage arr = getBoundaryArray(); + delete arr[0]; + } + + function copyFromBoundary() public { + S[10][1] storage source = getBoundaryArray(); + S[10][1] storage dest = getDest(); + dest[0] = source[0]; + } + + function copyToBoundary() public { + S[10][1] storage source = getDest(); + S[10][1] storage dest = getBoundaryArray(); + dest[0] = source[0]; + } + + function fillDestArray() public { + S[10][1] storage dest = getDest(); + for (uint i = 0; i < 10; i++) { + dest[0][i] = S({ + a: 51 + i * 5, + b: uint128(52 + i * 5), + c: uint64(53 + i * 5), + d: bytes32(uint256(54 + i * 5)), + e: true + }); + } + } + + function boundaryArray() public view returns (S[10] memory) { + return getBoundaryArray()[0]; + } + + function destArray() public view returns (S[10] memory) { + return getDest()[0]; + } + + function canaryValue() public view returns (uint256) { + return getCanary().value; + } +} +// ---- +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 113169 +// gas legacy: 120742 +// gas legacyOptimized: 112518 +// destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 113078 +// gas legacy: 120738 +// gas legacyOptimized: 112505 +// fillBoundaryArray() +// gas irOptimized: 912522 +// gas legacy: 930728 +// gas legacyOptimized: 916628 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true +// gas irOptimized: 113169 +// gas legacy: 120742 +// gas legacyOptimized: 112518 +// destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 113078 +// gas legacy: 120738 +// gas legacyOptimized: 112505 +// copyFromBoundary() +// gas irOptimized: 994579 +// gas legacy: 1023407 +// gas legacyOptimized: 994746 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true +// gas irOptimized: 113169 +// gas legacy: 120742 +// gas legacyOptimized: 112518 +// destArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true +// gas irOptimized: 113078 +// gas legacy: 120738 +// gas legacyOptimized: 112505 +// fillDestArray() +// gas irOptimized: 200426 +// gas legacy: 218746 +// gas legacyOptimized: 204648 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true +// gas irOptimized: 113169 +// gas legacy: 120742 +// gas legacyOptimized: 112518 +// destArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true +// gas irOptimized: 113078 +// gas legacy: 120738 +// gas legacyOptimized: 112505 +// copyToBoundary() +// gas irOptimized: 282623 +// gas legacy: 311362 +// gas legacyOptimized: 282712 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true +// gas irOptimized: 113169 +// gas legacy: 120742 +// gas legacyOptimized: 112518 +// destArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true +// gas irOptimized: 113078 +// gas legacy: 120738 +// gas legacyOptimized: 112505 +// deleteBoundaryArray() +// gas irOptimized: 177968 +// gas legacy: 180997 +// gas legacyOptimized: 178175 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 113169 +// gas legacy: 120742 +// gas legacyOptimized: 112518 +// destArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true +// gas irOptimized: 113078 +// gas legacy: 120738 +// gas legacyOptimized: 112505 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol new file mode 100644 index 000000000000..55e36cd08e06 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol @@ -0,0 +1,128 @@ +pragma abicoder v2; + +contract C { + struct S { + uint256 a; + uint256 b; + uint256 c; + // 3 slots per struct + } + + struct Canary { + uint256 value; + } + + function getBoundaryArray() internal pure returns (S[10][1] storage arr) { + // 10 structs, each 3 slots = 30 slots total + assembly { + arr.slot := sub(0, 15) + } + } + + function getDest() internal pure returns (S[10][1] storage arr) { + assembly { + arr.slot := 16 + } + } + + function getCanary() internal pure returns (Canary storage canary) { + assembly { + canary.slot := 15 + } + } + + constructor() { + Canary storage canary = getCanary(); + canary.value = type(uint256).max; + } + + function fillBoundaryArray() public { + S[10][1] storage arr = getBoundaryArray(); + for (uint i = 0; i < 10; i++) { + arr[0][i] = S({ + a: 1 + i * 3, + b: 2 + i * 3, + c: 3 + i * 3 + }); + } + } + + function deleteBoundaryArray() public { + S[10][1] storage arr = getBoundaryArray(); + delete arr[0]; + } + + function copyFromBoundary() public { + S[10][1] storage source = getBoundaryArray(); + S[10][1] storage dest = getDest(); + dest[0] = source[0]; + } + + function copyToBoundary() public { + S[10][1] storage source = getDest(); + S[10][1] storage dest = getBoundaryArray(); + dest[0] = source[0]; + } + + function fillDestArray() public { + S[10][1] storage dest = getDest(); + for (uint i = 0; i < 10; i++) { + dest[0][i] = S({ + a: 31 + i * 3, + b: 32 + i * 3, + c: 33 + i * 3 + }); + } + } + + function boundaryArray() public view returns (S[10] memory) { + return getBoundaryArray()[0]; + } + + function destArray() public view returns (S[10] memory) { + return getDest()[0]; + } + + function canaryValue() public view returns (uint256) { + return getCanary().value; + } +} +// ---- +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillBoundaryArray() +// gas irOptimized: 688719 +// gas legacy: 700075 +// gas legacyOptimized: 691605 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 +// destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// copyFromBoundary() +// gas irOptimized: 748779 +// gas legacy: 767297 +// gas legacyOptimized: 748756 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 +// destArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 +// fillDestArray() +// gas irOptimized: 175623 +// gas legacy: 187093 +// gas legacyOptimized: 178625 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 +// destArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 +// copyToBoundary() +// gas irOptimized: 235823 +// gas legacy: 254252 +// gas legacyOptimized: 235722 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 +// destArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 +// deleteBoundaryArray() +// gas irOptimized: 137824 +// gas legacy: 137973 +// gas legacyOptimized: 137743 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// destArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_packed.sol b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_packed.sol new file mode 100644 index 000000000000..5b4085acf8f9 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_packed.sol @@ -0,0 +1,128 @@ +pragma abicoder v2; + +contract C { + struct S { + uint64 a; + uint64 b; + uint64 c; + uint64 d; + // All fit in one slot (4 * 64 = 256 bits) + } + + struct Canary { + uint256 value; + } + + function getBoundaryArray() internal pure returns (S[10][1] storage arr) { + // 10 structs, each 1 slot = 10 slots total + assembly { + arr.slot := sub(0, 5) + } + } + + function getDest() internal pure returns (S[10][1] storage arr) { + assembly { + arr.slot := 6 + } + } + + function getCanary() internal pure returns (Canary storage canary) { + // Array ends at slot 4, canary at slot 5 + assembly { + canary.slot := 5 + } + } + + constructor() { + Canary storage canary = getCanary(); + canary.value = type(uint256).max; + } + + function fillBoundaryArray() public { + S[10][1] storage arr = getBoundaryArray(); + for (uint i = 0; i < 10; i++) { + arr[0][i] = S({ + a: uint64(1 + i * 4), + b: uint64(2 + i * 4), + c: uint64(3 + i * 4), + d: uint64(4 + i * 4) + }); + } + } + + function deleteBoundaryArray() public { + S[10][1] storage arr = getBoundaryArray(); + delete arr[0]; + } + + function copyFromBoundary() public { + S[10][1] storage source = getBoundaryArray(); + S[10][1] storage dest = getDest(); + dest[0] = source[0]; + } + + function copyToBoundary() public { + S[10][1] storage source = getDest(); + S[10][1] storage dest = getBoundaryArray(); + dest[0] = source[0]; + } + + function fillDestArray() public { + S[10][1] storage dest = getDest(); + for (uint i = 0; i < 10; i++) { + dest[0][i] = S({ + a: uint64(41 + i * 4), + b: uint64(42 + i * 4), + c: uint64(43 + i * 4), + d: uint64(44 + i * 4) + }); + } + } + + function boundaryArray() public view returns (S[10] memory) { + return getBoundaryArray()[0]; + } + + function destArray() public view returns (S[10] memory) { + return getDest()[0]; + } + + function canaryValue() public view returns (uint256) { + return getCanary().value; + } +} +// ---- +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillBoundaryArray() +// gas irOptimized: 248990 +// gas legacy: 272856 +// gas legacyOptimized: 253856 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 +// destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// copyFromBoundary() +// gas irOptimized: 274279 +// gas legacy: 298927 +// gas legacyOptimized: 272256 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 +// destArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 +// fillDestArray() +// gas legacy: 101874 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 +// destArray() -> 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 +// gas legacy: 59729 +// copyToBoundary() +// gas irOptimized: 103323 +// gas legacy: 127882 +// gas legacyOptimized: 101222 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 +// destArray() -> 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 +// deleteBoundaryArray() +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// destArray() -> 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 diff --git a/test/libsolidity/semanticTests/storage/storage_packed_array_copy.sol b/test/libsolidity/semanticTests/storage/storage_packed_array_copy.sol new file mode 100644 index 000000000000..d4edc5aecd53 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_packed_array_copy.sol @@ -0,0 +1,35 @@ +contract C { + bytes8[9] _x; // 4 per slot + bytes17[10] _y; // 1 per slot, no offset counter + + constructor() { + for (uint256 i = 0; i < _x.length; ++i) _x[i] = bytes8(uint64(i)); + _y[8] = _y[9] = bytes8(uint64(2)); + } + + function getXAsUint() public view returns (uint64[9] memory result) { + for (uint i = 0; i < 9; i++) { + result[i] = uint64(_x[i]); + } + } + + function getYAsUint() public view returns (uint64[10] memory result) { + for (uint i = 0; i < 10; i++) { + result[i] = uint64(bytes8(_y[i])); + } + } + + function copy() public { + _y = _x; + } +} + +// ---- +// getXAsUint() -> 0, 1, 2, 3, 4, 5, 6, 7, 8 +// getYAsUint() -> 0, 0, 0, 0, 0, 0, 0, 0, 2, 2 +// copy() +// gas irOptimized: 190810 +// gas legacy: 195580 +// gas legacyOptimized: 190906 +// getXAsUint() -> 0, 1, 2, 3, 4, 5, 6, 7, 8 +// getYAsUint() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 0 diff --git a/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_constructor_order_calling_revert.sol b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_constructor_order_calling_revert.sol new file mode 100644 index 000000000000..760439091716 --- /dev/null +++ b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_constructor_order_calling_revert.sol @@ -0,0 +1,15 @@ +contract A { constructor() { revert("A"); } } +contract B is A { constructor() { revert("B"); } } +contract C1 is B layout at 2**256 - 2**42 { constructor() { revert("C"); } } +contract C2 is B { constructor() { revert("C"); } } +contract F { + function withSpecifier() public returns (string memory) { + new C1(); + } + function withoutSpecifier() public returns (string memory) { + new C2(); + } +} +// ---- +// withSpecifier() -> FAILURE, hex"08c379a0", 0x20, 1, "A" +// withoutSpecifier() -> FAILURE, hex"08c379a0", 0x20, 1, "A" diff --git a/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_constructor_order_setting_storage_var.sol b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_constructor_order_setting_storage_var.sol new file mode 100644 index 000000000000..d9e597e62e1e --- /dev/null +++ b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_constructor_order_setting_storage_var.sol @@ -0,0 +1,19 @@ +contract A { uint public x; constructor() { x = 1; } } +contract B is A { constructor() { x = x * 10; } } +contract C1 is B layout at 2**256 - 2**40 { constructor() { x = x + 5; } } +contract C2 is B { constructor() { x = x + 5; } } +contract F { + function withSpecifier() public returns (uint) { + return new C1().x(); + } + function withoutSpecifier() public returns (uint) { + return new C2().x(); + } +} +// ---- +// withSpecifier() -> 15 +// gas legacy: 77592 +// gas legacy code: 30000 +// withoutSpecifier() -> 15 +// gas legacy: 77502 +// gas legacy code: 23600 diff --git a/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_constructors_with_different_number_of_arguments.sol b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_constructors_with_different_number_of_arguments.sol new file mode 100644 index 000000000000..16394592d436 --- /dev/null +++ b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_constructors_with_different_number_of_arguments.sol @@ -0,0 +1,15 @@ +contract A { constructor(uint x) {} } +contract B is A(1) { constructor(uint y, uint z) {} } +contract C1 is B(2, 3) layout at 2**256 - 2**42 { constructor() {} } +contract C2 is B(2, 3) { constructor() {} } +contract F { + function withSpecifier() public { + new C1(); + } + function withoutSpecifier() public { + new C2(); + } +} +// ---- +// withSpecifier() -> +// withoutSpecifier() -> diff --git a/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_fallback_calling_revert.sol b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_fallback_calling_revert.sol new file mode 100644 index 000000000000..e0f0cfcfd7bd --- /dev/null +++ b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_fallback_calling_revert.sol @@ -0,0 +1,28 @@ +contract A { fallback() external virtual { revert("A"); } } +contract B is A { fallback() external override virtual { revert("B"); } } +contract C1 is B layout at 2**256 - 2**42 { fallback() external override virtual { revert("C"); } } +contract C2 is B { fallback() external override virtual { revert("C"); } } +contract F { + function decode(bytes memory data) internal returns (string memory) { + assembly { + // Shift the memory pointer forward by 4 bytes to skip the selector + data := add(data, 4) + } + return abi.decode(data, (string)); + } + function withSpecifier() public returns (string memory) { + (bool success, bytes memory data) = address(new C1()).call(hex"beee"); + require(!success, "Must fail!"); + return decode(data); + } + function withoutSpecifier() public returns (string memory) { + (bool success, bytes memory data) = address(new C2()).call(hex"beee"); + require(!success, "Must fail!"); + return decode(data); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// withSpecifier() -> 0x20, 1, "C" +// withoutSpecifier() -> 0x20, 1, "C" diff --git a/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_immutables.sol b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_immutables.sol new file mode 100644 index 000000000000..7c2d65c67681 --- /dev/null +++ b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_immutables.sol @@ -0,0 +1,25 @@ +contract A { uint public immutable a = 1; } +contract B is A { uint public immutable b = 2; } +contract C1 is B layout at 2**256 - 2**42 { uint public immutable c = 3; } +contract C2 is B { uint public immutable c = 3; } +contract F { + function withSpecifier() public returns(uint, uint, uint) { + C1 c = new C1(); + return (c.a(), c.b(), c.c()); + } + function withoutSpecifier() public returns(uint, uint, uint) { + C2 c = new C2(); + return (c.a(), c.b(), c.c()); + } +} +// ---- +// withSpecifier() -> 1, 2, 3 +// gas irOptimized: 55312 +// gas irOptimized code: 45400 +// gas legacy: 56473 +// gas legacy code: 62800 +// withoutSpecifier() -> 1, 2, 3 +// gas irOptimized: 55286 +// gas irOptimized code: 45400 +// gas legacy: 56451 +// gas legacy code: 62800 diff --git a/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_modifier_calling_revert.sol b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_modifier_calling_revert.sol new file mode 100644 index 000000000000..6783ce94a295 --- /dev/null +++ b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_modifier_calling_revert.sol @@ -0,0 +1,38 @@ +contract A { + modifier m() virtual { + revert("A"); + _; + } +} +contract B is A { + modifier m() virtual override { + revert("B"); + _; + } +} +contract C1 is B layout at 2**256 - 2**42 { + function f() public m {} + modifier m() virtual override { + revert("C"); + _; + } +} +contract C2 is B { + function f() public m {} + modifier m() virtual override { + revert("C"); + _; + } +} + +contract F { + function withSpecifier() public returns (string memory) { + new C1().f(); + } + function withoutSpecifier() public returns (string memory) { + new C2().f(); + } +} +// ---- +// withSpecifier() -> FAILURE, hex"08c379a0", 0x20, 1, "C" +// withoutSpecifier() -> FAILURE, hex"08c379a0", 0x20, 1, "C" diff --git a/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_multiple_parents_constructors_with_arguments.sol b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_multiple_parents_constructors_with_arguments.sol new file mode 100644 index 000000000000..e7032107d06e --- /dev/null +++ b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_multiple_parents_constructors_with_arguments.sol @@ -0,0 +1,15 @@ +contract A { constructor(uint x) {} } +contract B { constructor(uint x) {} } +contract C1 is A(2), B(3) layout at 2**256 - 2**42 { constructor(uint x) {} } +contract C2 is A(2), B(3) { constructor(uint x) {} } +contract F { + function withSpecifier() public { + new C1(4); + } + function withoutSpecifier() public { + new C2(4); + } +} +// ---- +// withSpecifier() -> +// withoutSpecifier() -> diff --git a/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_receive_calling_revert.sol b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_receive_calling_revert.sol new file mode 100644 index 000000000000..a867a6090819 --- /dev/null +++ b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_receive_calling_revert.sol @@ -0,0 +1,28 @@ +contract A { receive() external payable virtual { revert("A"); } } +contract B is A { receive() external payable override virtual { revert("B"); } } +contract C1 is B layout at 2**256 - 2**42 { receive() external payable override virtual { revert("C"); } } +contract C2 is B { receive() external payable override virtual { revert("C"); } } +contract F { + function decode(bytes memory data) internal returns (string memory) { + assembly { + // Shift the memory pointer forward by 4 bytes to skip the selector + data := add(data, 4) + } + return abi.decode(data, (string)); + } + function withSpecifier() public returns (string memory) { + (bool success, bytes memory data) = payable(new C1()).call{value: 0}(""); + require(!success, "Must fail!"); + return decode(data); + } + function withoutSpecifier() public returns (string memory) { + (bool success, bytes memory data) = payable(new C2()).call{value: 0}(""); + require(!success, "Must fail!"); + return decode(data); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// withSpecifier() -> 0x20, 1, "C" +// withoutSpecifier() -> 0x20, 1, "C" diff --git a/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_storage_vars_initialization.sol b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_storage_vars_initialization.sol new file mode 100644 index 000000000000..87fbe48bc973 --- /dev/null +++ b/test/libsolidity/semanticTests/storageLayoutSpecifier/inheritance_storage_vars_initialization.sol @@ -0,0 +1,29 @@ +contract A { uint public x = 1; } +contract B is A { uint public y = x * 10; } +contract C1 is B layout at 2**256 - 2**40 { uint public z = y + 5; } +contract C2 is B { uint public z = y + 5; } +contract F { + function withSpecifier() public returns (uint, uint, uint) { + C1 c = new C1(); + return (c.x(), c.y(), c.z()); + } + function withoutSpecifier() public returns (uint, uint, uint) { + C2 c = new C2(); + return (c.x(), c.y(), c.z()); + } +} +// ---- +// withSpecifier() -> 1, 10, 15 +// gas irOptimized: 121822 +// gas irOptimized code: 30800 +// gas legacy: 123715 +// gas legacy code: 63400 +// gas legacyOptimized: 121966 +// gas legacyOptimized code: 23800 +// withoutSpecifier() -> 1, 10, 15 +// gas irOptimized: 121768 +// gas irOptimized code: 27600 +// gas legacy: 123599 +// gas legacy code: 40400 +// gas legacyOptimized: 121916 +// gas legacyOptimized code: 20600 diff --git a/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_dynamic_array.sol b/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_dynamic_array.sol index 1737c35bb87c..bcad9609ee07 100644 --- a/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_dynamic_array.sol +++ b/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_dynamic_array.sol @@ -41,9 +41,9 @@ contract C is A layout at 42 { // arrayALength() -> 3 // arrayCLength() -> 0 // initCFromAInReverse() -> 3, 2, 1 -// gas irOptimized: 121276 -// gas legacy: 121213 -// gas legacyOptimized: 120843 +// gas irOptimized: 121281 +// gas legacy: 121937 +// gas legacyOptimized: 120853 // clearA() -> // arrayC(uint256): 0 -> 3 // arrayALength() -> 0 diff --git a/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_mapping.sol b/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_mapping.sol index e33df29ea604..47c4786c841d 100644 --- a/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_mapping.sol +++ b/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_mapping.sol @@ -27,7 +27,7 @@ contract C layout at 42 is A { // setup() -> // gas irOptimized: 159082 // gas legacy: 161738 -// gas legacyOptimized: 160222 +// gas legacyOptimized: 160218 // open(uint256): 3 -> 0x20, 5, "Empty" // open(uint256): 2 -> 0x20, 6, "Locked" // open(uint256): 1 -> 0x20, 7, "Monster" diff --git a/test/libsolidity/semanticTests/structs/copy_from_mapping.sol b/test/libsolidity/semanticTests/structs/copy_from_mapping.sol index 037ff20679f0..fccdf0030ffb 100644 --- a/test/libsolidity/semanticTests/structs/copy_from_mapping.sol +++ b/test/libsolidity/semanticTests/structs/copy_from_mapping.sol @@ -37,7 +37,7 @@ contract C { // ---- // to_state() -> 0x20, 0x60, 0xa0, 7, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 121282 -// gas legacy: 122977 -// gas legacyOptimized: 121652 +// gas legacy: 125480 +// gas legacyOptimized: 121695 // to_storage() -> 0x20, 0x60, 0xa0, 7, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // to_memory() -> 0x20, 0x60, 0xa0, 7, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 diff --git a/test/libsolidity/semanticTests/structs/copy_struct_array_from_storage.sol b/test/libsolidity/semanticTests/structs/copy_struct_array_from_storage.sol index 600eb35ab2ee..50babde562ce 100644 --- a/test/libsolidity/semanticTests/structs/copy_struct_array_from_storage.sol +++ b/test/libsolidity/semanticTests/structs/copy_struct_array_from_storage.sol @@ -88,8 +88,8 @@ contract Test { // ---- // test1() -> true // gas irOptimized: 152965 -// gas legacy: 153010 -// gas legacyOptimized: 152636 +// gas legacy: 155952 +// gas legacyOptimized: 152864 // test2() -> true // test3() -> true // test4() -> true diff --git a/test/libsolidity/semanticTests/structs/copy_substructures_from_mapping.sol b/test/libsolidity/semanticTests/structs/copy_substructures_from_mapping.sol index 7188e1befe7a..9f0b7cbe64c9 100644 --- a/test/libsolidity/semanticTests/structs/copy_substructures_from_mapping.sol +++ b/test/libsolidity/semanticTests/structs/copy_substructures_from_mapping.sol @@ -45,7 +45,7 @@ contract C { // ---- // to_state() -> 0x20, 0x60, 0xa0, 7, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 121454 -// gas legacy: 123114 -// gas legacyOptimized: 121659 +// gas legacy: 125617 +// gas legacyOptimized: 121699 // to_storage() -> 0x20, 0x60, 0xa0, 7, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // to_memory() -> 0x20, 0x60, 0xa0, 7, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 diff --git a/test/libsolidity/semanticTests/structs/copy_substructures_to_mapping.sol b/test/libsolidity/semanticTests/structs/copy_substructures_to_mapping.sol index 54b659ba0757..3e86495713be 100644 --- a/test/libsolidity/semanticTests/structs/copy_substructures_to_mapping.sol +++ b/test/libsolidity/semanticTests/structs/copy_substructures_to_mapping.sol @@ -53,13 +53,13 @@ contract C { // ---- // from_memory() -> 0x20, 0x60, 0xa0, 0x15, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 122720 -// gas legacy: 130131 -// gas legacyOptimized: 128648 +// gas legacy: 125558 +// gas legacyOptimized: 123322 // from_state() -> 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 121424 -// gas legacy: 123190 -// gas legacyOptimized: 121758 +// gas legacy: 125693 +// gas legacyOptimized: 121804 // from_calldata((bytes,uint16[],uint16)): 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 -> 0x20, 0x60, 0xa0, 0x15, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 114852 -// gas legacy: 122423 -// gas legacyOptimized: 120698 +// gas legacy: 117950 +// gas legacyOptimized: 115526 diff --git a/test/libsolidity/semanticTests/structs/copy_to_mapping.sol b/test/libsolidity/semanticTests/structs/copy_to_mapping.sol index d020998b47c5..b31d0e580444 100644 --- a/test/libsolidity/semanticTests/structs/copy_to_mapping.sol +++ b/test/libsolidity/semanticTests/structs/copy_to_mapping.sol @@ -46,16 +46,16 @@ contract C { // ---- // from_state() -> 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 121515 -// gas legacy: 123051 -// gas legacyOptimized: 121704 +// gas legacy: 125554 +// gas legacyOptimized: 121747 // from_storage() -> 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 121559 -// gas legacy: 123109 -// gas legacyOptimized: 121756 +// gas legacy: 125612 +// gas legacyOptimized: 121799 // from_memory() -> 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 122740 -// gas legacy: 129996 -// gas legacyOptimized: 128644 +// gas legacy: 125423 +// gas legacyOptimized: 123323 // from_calldata((bytes,uint16[],uint16)): 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 -> 0x20, 0x60, 0xa0, 21, 3, 0x666f6f0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 114824 // gas legacy: 118207 diff --git a/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol index e50265e5175d..3912a4655505 100644 --- a/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol +++ b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol @@ -25,7 +25,7 @@ contract c { // set(uint256,bytes,uint256): 12, 0x60, 13, 33, "12345678901234567890123456789012", "3" -> true // gas irOptimized: 133557 // gas legacy: 134624 -// gas legacyOptimized: 133856 +// gas legacyOptimized: 133857 // test(uint256): 32 -> "3" // storageEmpty -> 0 // copy() -> true @@ -33,7 +33,7 @@ contract c { // set(uint256,bytes,uint256): 12, 0x60, 13, 33, "12345678901234567890123456789012", "3" -> true // gas irOptimized: 133557 // gas legacy: 134624 -// gas legacyOptimized: 133856 +// gas legacyOptimized: 133857 // storageEmpty -> 0 // del() -> true // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol b/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol index 57bf38e52c6b..12cbf0799e04 100644 --- a/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol +++ b/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol @@ -33,4 +33,4 @@ contract C { // compileViaYul: true // ---- // f() -> 0, 0, 0 -// gas irOptimized: 117101 +// gas irOptimized: 93835 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_storage_with_array.sol b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_array.sol index cea1105669aa..cd49ea27ad0e 100644 --- a/test/libsolidity/semanticTests/structs/struct_delete_storage_with_array.sol +++ b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_array.sol @@ -42,10 +42,10 @@ contract C { } // ---- // f() -> -// gas irOptimized: 113465 -// gas legacy: 113591 -// gas legacyOptimized: 113098 +// gas irOptimized: 113388 +// gas legacy: 113588 +// gas legacyOptimized: 113104 // g() -> -// gas irOptimized: 118828 -// gas legacy: 118764 -// gas legacyOptimized: 118168 +// gas irOptimized: 118768 +// gas legacy: 118766 +// gas legacyOptimized: 118188 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_storage_with_arrays_small.sol b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_arrays_small.sol index 2345fea3cfe2..1a047049f205 100644 --- a/test/libsolidity/semanticTests/structs/struct_delete_storage_with_arrays_small.sol +++ b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_arrays_small.sol @@ -27,4 +27,4 @@ contract C { // compileViaYul: true // ---- // f() -> 0 -// gas irOptimized: 111570 +// gas irOptimized: 89468 diff --git a/test/libsolidity/semanticTests/types/mapping/copy_from_mapping_to_mapping.sol b/test/libsolidity/semanticTests/types/mapping/copy_from_mapping_to_mapping.sol index 26e8294e3ac9..47fa27fe3c28 100644 --- a/test/libsolidity/semanticTests/types/mapping/copy_from_mapping_to_mapping.sol +++ b/test/libsolidity/semanticTests/types/mapping/copy_from_mapping_to_mapping.sol @@ -30,5 +30,5 @@ contract C { // ---- // f() -> 0x20, 7, 8, 9, 0xa0, 13, 2, 0x40, 0xa0, 2, 3, 4, 2, 3, 4 // gas irOptimized: 197102 -// gas legacy: 199887 -// gas legacyOptimized: 196845 +// gas legacy: 205706 +// gas legacyOptimized: 196739 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/calldata.sol b/test/libsolidity/semanticTests/userDefinedValueType/calldata.sol index 015d51c1f54b..03c6e11bcc3c 100644 --- a/test/libsolidity/semanticTests/userDefinedValueType/calldata.sol +++ b/test/libsolidity/semanticTests/userDefinedValueType/calldata.sol @@ -49,13 +49,13 @@ contract C { } // ---- // test_f() -> true -// gas irOptimized: 122201 -// gas legacy: 125333 -// gas legacyOptimized: 122693 +// gas irOptimized: 122114 +// gas legacy: 126162 +// gas legacyOptimized: 122757 // test_g() -> true -// gas irOptimized: 106408 -// gas legacy: 111133 -// gas legacyOptimized: 106925 +// gas irOptimized: 106248 +// gas legacy: 111824 +// gas legacyOptimized: 106606 // addresses(uint256): 0 -> 0x18 // addresses(uint256): 1 -> 0x19 // addresses(uint256): 3 -> 0x1b diff --git a/test/libsolidity/semanticTests/userDefinedValueType/calldata_to_storage.sol b/test/libsolidity/semanticTests/userDefinedValueType/calldata_to_storage.sol index c3d58811ae4e..0815a2da7ace 100644 --- a/test/libsolidity/semanticTests/userDefinedValueType/calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/userDefinedValueType/calldata_to_storage.sol @@ -23,19 +23,19 @@ contract C { // ---- // s() -> 0, 0, 0x00, 0 // f((uint8,uint16,bytes2,uint8)): 1, 0xff, "ab", 15 -> -// gas irOptimized: 44405 -// gas legacy: 47200 -// gas legacyOptimized: 44923 +// gas irOptimized: 44237 +// gas legacy: 47154 +// gas legacyOptimized: 44982 // s() -> 1, 0xff, 0x6162000000000000000000000000000000000000000000000000000000000000, 15 // g(uint16[]): 0x20, 3, 1, 2, 3 -> 0x20, 3, 1, 2, 3 -// gas irOptimized: 69097 -// gas legacy: 75466 -// gas legacyOptimized: 74255 +// gas irOptimized: 68578 +// gas legacy: 71400 +// gas legacyOptimized: 69139 // small(uint256): 0 -> 1 // small(uint256): 1 -> 2 // h(bytes2[]): 0x20, 3, "ab", "cd", "ef" -> 0x20, 3, "ab", "cd", "ef" -// gas irOptimized: 69174 -// gas legacy: 75156 -// gas legacyOptimized: 74342 +// gas irOptimized: 68635 +// gas legacy: 71231 +// gas legacyOptimized: 69289 // l(uint256): 0 -> 0x6162000000000000000000000000000000000000000000000000000000000000 // l(uint256): 1 -> 0x6364000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/memory_to_storage.sol b/test/libsolidity/semanticTests/userDefinedValueType/memory_to_storage.sol index db745dce31ec..40005a035735 100644 --- a/test/libsolidity/semanticTests/userDefinedValueType/memory_to_storage.sol +++ b/test/libsolidity/semanticTests/userDefinedValueType/memory_to_storage.sol @@ -28,14 +28,14 @@ contract C { // gas legacyOptimized: 44671 // s() -> 1, 0xff, 0x6162000000000000000000000000000000000000000000000000000000000000, 15 // g(uint16[]): 0x20, 3, 1, 2, 3 -> 0x20, 3, 1, 2, 3 -// gas irOptimized: 69555 -// gas legacy: 76557 -// gas legacyOptimized: 74834 +// gas irOptimized: 68898 +// gas legacy: 72336 +// gas legacyOptimized: 69607 // small(uint256): 0 -> 1 // small(uint256): 1 -> 2 // h(bytes2[]): 0x20, 3, "ab", "cd", "ef" -> 0x20, 3, "ab", "cd", "ef" -// gas irOptimized: 69617 -// gas legacy: 76238 -// gas legacyOptimized: 74921 +// gas irOptimized: 68937 +// gas legacy: 72167 +// gas legacyOptimized: 69802 // l(uint256): 0 -> 0x6162000000000000000000000000000000000000000000000000000000000000 // l(uint256): 1 -> 0x6364000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/various/address_code.sol b/test/libsolidity/semanticTests/various/address_code.sol index 4d810f970987..eda17b3c2775 100644 --- a/test/libsolidity/semanticTests/various/address_code.sol +++ b/test/libsolidity/semanticTests/various/address_code.sol @@ -16,11 +16,11 @@ contract C { // bytecodeFormat: legacy // ---- // constructor() -> -// gas irOptimized: 70760 +// gas irOptimized: 70924 // gas irOptimized code: 94600 -// gas legacy: 82428 +// gas legacy: 82688 // gas legacy code: 153800 -// gas legacyOptimized: 69400 +// gas legacyOptimized: 69675 // gas legacyOptimized code: 79200 // initCode() -> 0x20, 0 // f() -> true diff --git a/test/libsolidity/semanticTests/various/create_calldata.sol b/test/libsolidity/semanticTests/various/create_calldata.sol index 7e3e326228a7..c4d17d4299ca 100644 --- a/test/libsolidity/semanticTests/various/create_calldata.sol +++ b/test/libsolidity/semanticTests/various/create_calldata.sol @@ -10,10 +10,10 @@ contract C { // bytecodeFormat: legacy // ---- // constructor(): 42 -> -// gas irOptimized: 68239 +// gas irOptimized: 68387 // gas irOptimized code: 69000 -// gas legacy: 78076 +// gas legacy: 78348 // gas legacy code: 90200 -// gas legacyOptimized: 68321 +// gas legacyOptimized: 68548 // gas legacyOptimized code: 64600 // s() -> 0x20, 0 diff --git a/test/libsolidity/semanticTests/various/destructuring_assignment.sol b/test/libsolidity/semanticTests/various/destructuring_assignment.sol index a961870ee5f2..44fcd0f6aaa2 100644 --- a/test/libsolidity/semanticTests/various/destructuring_assignment.sol +++ b/test/libsolidity/semanticTests/various/destructuring_assignment.sol @@ -33,6 +33,6 @@ contract C { } // ---- // f(bytes): 0x20, 0x5, "abcde" -> 0 -// gas irOptimized: 242027 -// gas legacy: 243281 -// gas legacyOptimized: 242392 +// gas irOptimized: 242037 +// gas legacy: 246685 +// gas legacyOptimized: 242396 diff --git a/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol index 8430da4e8d12..1af8a04195a4 100644 --- a/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol +++ b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol @@ -20,5 +20,5 @@ contract C { // ---- // g() -> 2, 6 // gas irOptimized: 178195 -// gas legacy: 180653 -// gas legacyOptimized: 179144 +// gas legacy: 181357 +// gas legacyOptimized: 179055 diff --git a/test/libsolidity/syntaxTests/array/copy_calldata_struct_array_to_storage_legacy.sol b/test/libsolidity/syntaxTests/array/copy_calldata_struct_array_to_storage_legacy.sol new file mode 100644 index 000000000000..8c45b5b9f520 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/copy_calldata_struct_array_to_storage_legacy.sol @@ -0,0 +1,16 @@ +contract C { + struct S { + uint256 a; + uint256 b; + } + + S[] storageArray; + + function copyFromCalldata(S[] calldata calldataArray) public { + storageArray = calldataArray; + } +} +// ==== +// compileViaYul: false +// ---- +// UnimplementedFeatureError 1834: (0-208): Copying of type struct C.S calldata[] calldata to storage is not supported in legacy (only supported by the IR pipeline). Hint: try compiling with `--via-ir` (CLI) or the equivalent `viaIR: true` (Standard JSON). diff --git a/test/libsolidity/syntaxTests/array/copy_memory_struct_array_to_storage_legacy.sol b/test/libsolidity/syntaxTests/array/copy_memory_struct_array_to_storage_legacy.sol new file mode 100644 index 000000000000..86e8bf1f6344 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/copy_memory_struct_array_to_storage_legacy.sol @@ -0,0 +1,17 @@ +contract C { + struct S { + uint256 a; + uint256 b; + } + + S[] storageArray; + + function copyFromMemory() public { + S[] memory memArray = new S[](3); + storageArray = memArray; + } +} +// ==== +// compileViaYul: false +// ---- +// UnimplementedFeatureError 1834: (0-217): Copying of type struct C.S memory[] memory to storage is not supported in legacy (only supported by the IR pipeline). Hint: try compiling with `--via-ir` (CLI) or the equivalent `viaIR: true` (Standard JSON). diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/inheritance_calling_super_specifier_on_caller.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/inheritance_calling_super_specifier_on_caller.sol new file mode 100644 index 000000000000..801aa813091b --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/inheritance_calling_super_specifier_on_caller.sol @@ -0,0 +1,5 @@ +contract A { function f() public virtual {} } +contract B1 is A layout at 2**256 - 2**42 { function f() public override virtual { super.f(); } } +contract B2 is A { function f() public override virtual { super.f(); } } +// ---- +// Warning 3495: (63-87): This contract is very close to the end of storage. This limits its future upgradability. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/inheritance_calling_super_specifier_on_child.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/inheritance_calling_super_specifier_on_child.sol new file mode 100644 index 000000000000..030a54f084a0 --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/inheritance_calling_super_specifier_on_child.sol @@ -0,0 +1,6 @@ +contract A { function f() public virtual {} } +contract B is A { function f() public override virtual { super.f(); } } +contract C1 is B layout at 2**256 - 2**42 {} +contract C2 is B {} +// ---- +// Warning 3495: (135-159): This contract is very close to the end of storage. This limits its future upgradability. diff --git a/test/libsolutil/TarjanSCC.cpp b/test/libsolutil/TarjanSCC.cpp new file mode 100644 index 000000000000..c6e8c350c711 --- /dev/null +++ b/test/libsolutil/TarjanSCC.cpp @@ -0,0 +1,149 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Unit tests for the Tarjan SCC utility. + */ + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace solidity::util::test +{ + +using NodeID = std::uint32_t; +using Graph = std::vector>; +using SCCList = std::vector>; + +namespace +{ + +namespace bdata = boost::unit_test::data; + +SCCList compute(Graph const& _g) +{ + return solidity::util::computeStronglyConnectedComponents(_g); +} + +SCCList canonicalize(SCCList _sccs) +{ + for (auto& scc: _sccs) + ranges::sort(scc); + ranges::sort(_sccs); + return _sccs; +} + +std::string formatSCCs(SCCList const& _sccs) +{ + auto const formatScc = [](std::vector const& _scc) { + return fmt::format("{{{}}}", fmt::join(_scc, ", ")); + }; + return fmt::format("{{{}}}", fmt::join(_sccs | ranges::views::transform(formatScc), ", ")); +} + +boost::test_tools::predicate_result sccsEqual(SCCList const& _actual, SCCList const& _expected) +{ + SCCList const canonicalActual = canonicalize(_actual); + SCCList const canonicalExpected = canonicalize(_expected); + if (canonicalActual == canonicalExpected) + return true; + boost::test_tools::predicate_result result(false); + result.message() + << "SCC partitions differ (compared as sets of sets).\n" + << " actual: " << formatSCCs(canonicalActual) << "\n" + << " expected: " << formatSCCs(canonicalExpected); + return result; +} + +struct PartitionCase +{ + std::string name; + Graph graph; + SCCList expectedSCCs; +}; + +std::ostream& operator<<(std::ostream& _os, PartitionCase const& _case) +{ + return _os << _case.name; +} + +std::vector const partitionCases = { + {"empty_graph", {}, {}}, + {"single_node_no_edges", Graph(1), {{0}}}, + {"single_node_self_loop", {{0}}, {{0}}}, + {"two_cycle", {{1}, {0}}, {{0, 1}}}, + {"three_cycle", {{1}, {2}, {0}}, {{0, 1, 2}}}, + {"linear_chain", {{1}, {2}, {3}, {}}, {{0}, {1}, {2}, {3}}}, + // 0 <-> 1, plus self-loop on 0. Yields one SCC {0, 1}. + {"self_loop_inside_two_cycle", {{0, 1}, {0}}, {{0, 1}}}, + // 0 -> 2, 1 -> 2: three singleton SCCs. + {"dag_with_shared_successor", {{2}, {2}, {}}, {{0}, {1}, {2}}}, + { + "clrs_example", + // CLRS, 3rd ed., Fig. 22.9 (relabeled a..h -> 0..7). + // 0 -> 1 + // 1 -> 2, 4, 5 + // 2 -> 3, 6 + // 3 -> 2, 7 + // 4 -> 0, 5 + // 5 -> 6 + // 6 -> 5, 7 + // 7 -> 7 + {{1}, {2, 4, 5}, {3, 6}, {2, 7}, {0, 5}, {6}, {5, 7}, {7}}, + {{0, 1, 4}, {2, 3}, {5, 6}, {7}}, + }, + { + "multiple_disjoint_components", + // Four components: 0 <-> 1, 2 <-> 3, 4 -> 5. + {{1}, {0}, {3}, {2}, {5}, {}}, + {{0, 1}, {2, 3}, {4}, {5}}, + }, + { + "nested_sccs_with_cross_edges", + // Two SCCs connected by an inter-SCC edge: 0 <-> 1 (A), 2 <-> 3 (B), 1 -> 2. + {{1}, {0, 2}, {3}, {2}}, + {{0, 1}, {2, 3}}, + }, + // 0 -> 1, node 2 isolated: 2 still emitted as its own SCC. + {"unreachable_node_still_emitted", {{1}, {}, {}}, {{0}, {1}, {2}}}, +}; + +} + +BOOST_AUTO_TEST_SUITE(TarjanSCC) + +BOOST_DATA_TEST_CASE(partition, bdata::make(partitionCases), testCase) +{ + BOOST_CHECK(sccsEqual(compute(testCase.graph), testCase.expectedSCCs)); +} + +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/libyul/CallGraphTest.cpp b/test/libyul/CallGraphTest.cpp new file mode 100644 index 000000000000..f55ff2475144 --- /dev/null +++ b/test/libyul/CallGraphTest.cpp @@ -0,0 +1,137 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +using namespace solidity; +using namespace solidity::util; +using namespace solidity::langutil; +using namespace solidity::yul; +using namespace solidity::yul::test; +using namespace solidity::frontend; +using namespace solidity::frontend::test; + +namespace +{ + +/// Collects function names in the order in which the functions are defined, descending into nested +/// functions, so that the call graph can be printed deterministically. +struct FunctionNameCollector: ASTWalker +{ + using ASTWalker::operator(); + void operator()(FunctionDefinition const& _function) override + { + names.emplace_back(_function.name); + ASTWalker::operator()(_function); + } + + std::vector names; +}; + +} + +std::unique_ptr CallGraphTest::create(Config const& _config) +{ + return std::make_unique(_config.filename); +} + +CallGraphTest::CallGraphTest(std::string const& _filename): + TestCase(_filename) +{ + m_source = m_reader.source(); + m_expectation = m_reader.simpleExpectations(); +} + +TestCase::TestResult CallGraphTest::run(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted) +{ + YulStack const yulStack = parseYul(m_source); + solUnimplementedAssert(yulStack.parserResult()->subObjects.empty(), "Tests with subobjects not supported."); + + if (yulStack.hasErrors()) + { + printYulErrors(yulStack, _stream, _linePrefix, _formatted); + return TestResult::FatalError; + } + + Block const& root = yulStack.parserResult()->code()->root(); + + std::ostringstream out; + try + { + CallGraph const callGraph = CallGraphGenerator::callGraph(root); + std::set const recursiveFunctionHandles = callGraph.recursiveFunctions(); + Dialect const& dialect = yulStack.dialect(); + + auto printNode = [&](YulName const _name) { + FunctionHandle const handle{_name}; + out << (_name == YulName{} ? "
" : _name.str()); + + std::vector annotations; + if (recursiveFunctionHandles.contains(handle)) + annotations.emplace_back("recursive"); + if (callGraph.functionsWithLoops.contains(_name)) + annotations.emplace_back("loops"); + if (!annotations.empty()) + out << " (" << joinHumanReadable(annotations) << ")"; + + std::vector renderedCallees; + for (FunctionHandle const& callee: callGraph.functionCalls.at(handle)) + renderedCallees.emplace_back(resolveFunctionName(callee, dialect)); + if (!renderedCallees.empty()) + out << " -> " << joinHumanReadable(renderedCallees); + + out << "\n"; + }; + + FunctionNameCollector collector; + collector(root); + + printNode(YulName{}); + for (YulName const& name: collector.names) + printNode(name); + + yulAssert(!recursiveFunctionHandles.contains(YulName{}), "main cannot be recursive"); + } + catch (CallGraphGenerator::InputNotDisambiguatedException const& _exception) + { + out << "InputNotDisambiguatedException: " << (_exception.comment() ? *_exception.comment() : "") << "\n"; + } + m_obtainedResult = out.str(); + + return checkResult(_stream, _linePrefix, _formatted); +} diff --git a/test/libyul/CallGraphTest.h b/test/libyul/CallGraphTest.h new file mode 100644 index 000000000000..0b644cb2e1b9 --- /dev/null +++ b/test/libyul/CallGraphTest.h @@ -0,0 +1,36 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +#include + +namespace solidity::yul::test +{ + +class CallGraphTest: public frontend::test::TestCase +{ +public: + static std::unique_ptr create(Config const& _config); + explicit CallGraphTest(std::string const& _filename); + TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; +}; + +} diff --git a/test/libyul/functionSideEffects/intersecting_recursive_cycles.yul b/test/libyul/functionSideEffects/intersecting_recursive_cycles.yul new file mode 100644 index 000000000000..9cda724b46e7 --- /dev/null +++ b/test/libyul/functionSideEffects/intersecting_recursive_cycles.yul @@ -0,0 +1,15 @@ +{ + // Two recursive cycles sharing alpha: alpha <-> beta and alpha -> gamma -> beta -> alpha, + // so {alpha, beta, gamma} is a single strongly-connected component (all three are recursive). + // + // The old call-graph cycle finder mis-classified gamma as non-recursive (SOL-2026-2), but the + // side-effect result is unaffected. + function alpha() { beta() gamma() } + function beta() { alpha() } + function gamma() { beta() } +} +// ---- +// : movable, movable apart from effects, can be removed, can be removed if no msize +// alpha: movable apart from effects, can loop +// beta: movable apart from effects, can loop +// gamma: movable apart from effects, can loop diff --git a/test/libyul/yulCallGraph/ambiguous_names.yul b/test/libyul/yulCallGraph/ambiguous_names.yul new file mode 100644 index 000000000000..bf1bdbd7ca35 --- /dev/null +++ b/test/libyul/yulCallGraph/ambiguous_names.yul @@ -0,0 +1,6 @@ +{ + { function f() {} } + { function f() {} } +} +// ---- +// InputNotDisambiguatedException: CallGraphGenerator requires a disambiguated AST: duplicate function name f. diff --git a/test/libyul/yulCallGraph/ambiguous_names_nested.yul b/test/libyul/yulCallGraph/ambiguous_names_nested.yul new file mode 100644 index 000000000000..3a843bdad9c5 --- /dev/null +++ b/test/libyul/yulCallGraph/ambiguous_names_nested.yul @@ -0,0 +1,12 @@ +{ + { + function f() { + function g() {} + } + } + { + function g() {} + } +} +// ---- +// InputNotDisambiguatedException: CallGraphGenerator requires a disambiguated AST: duplicate function name g. diff --git a/test/libyul/yulCallGraph/branching_into_and_out_of_scc.yul b/test/libyul/yulCallGraph/branching_into_and_out_of_scc.yul new file mode 100644 index 000000000000..50d500e8c3a6 --- /dev/null +++ b/test/libyul/yulCallGraph/branching_into_and_out_of_scc.yul @@ -0,0 +1,13 @@ +{ + function f() { + g() + h() + } + function g() { f() } + function h() {} +} +// ---- +//
+// f (recursive) -> g, h +// g (recursive) -> f +// h diff --git a/test/libyul/yulCallGraph/builtin_call.yul b/test/libyul/yulCallGraph/builtin_call.yul new file mode 100644 index 000000000000..8a38e75cac61 --- /dev/null +++ b/test/libyul/yulCallGraph/builtin_call.yul @@ -0,0 +1,9 @@ +{ + function f() { + mstore(0, 1) + } + f() +} +// ---- +//
-> f +// f -> mstore diff --git a/test/libyul/yulCallGraph/builtin_in_recursive_cycle.yul b/test/libyul/yulCallGraph/builtin_in_recursive_cycle.yul new file mode 100644 index 000000000000..018bdd9c7771 --- /dev/null +++ b/test/libyul/yulCallGraph/builtin_in_recursive_cycle.yul @@ -0,0 +1,8 @@ +{ + function f() { g() mstore(0, 1) } + function g() { f() } +} +// ---- +//
+// f (recursive) -> g, mstore +// g (recursive) -> f diff --git a/test/libyul/yulCallGraph/caller_into_scc.yul b/test/libyul/yulCallGraph/caller_into_scc.yul new file mode 100644 index 000000000000..b8007d9bf320 --- /dev/null +++ b/test/libyul/yulCallGraph/caller_into_scc.yul @@ -0,0 +1,10 @@ +{ + function f() { g() } + function g() { h() } + function h() { g() } +} +// ---- +//
+// f -> g +// g (recursive) -> h +// h (recursive) -> g diff --git a/test/libyul/yulCallGraph/direct_plus_mutual.yul b/test/libyul/yulCallGraph/direct_plus_mutual.yul new file mode 100644 index 000000000000..0d52709bef1e --- /dev/null +++ b/test/libyul/yulCallGraph/direct_plus_mutual.yul @@ -0,0 +1,10 @@ +{ + function f() { f() } + function g() { h() } + function h() { g() } +} +// ---- +//
+// f (recursive) -> f +// g (recursive) -> h +// h (recursive) -> g diff --git a/test/libyul/yulCallGraph/direct_self_call.yul b/test/libyul/yulCallGraph/direct_self_call.yul new file mode 100644 index 000000000000..0a902ab41c00 --- /dev/null +++ b/test/libyul/yulCallGraph/direct_self_call.yul @@ -0,0 +1,6 @@ +{ + function f() { f() } +} +// ---- +//
+// f (recursive) -> f diff --git a/test/libyul/yulCallGraph/duplicate_callees.yul b/test/libyul/yulCallGraph/duplicate_callees.yul new file mode 100644 index 000000000000..df6950c6607e --- /dev/null +++ b/test/libyul/yulCallGraph/duplicate_callees.yul @@ -0,0 +1,11 @@ +{ + function f() { + g() + g() + } + function g() {} +} +// ---- +//
+// f -> g +// g diff --git a/test/libyul/yulCallGraph/intersecting_cycles.yul b/test/libyul/yulCallGraph/intersecting_cycles.yul new file mode 100644 index 000000000000..baaa4ecaef7a --- /dev/null +++ b/test/libyul/yulCallGraph/intersecting_cycles.yul @@ -0,0 +1,12 @@ +{ + // Two recursive cycles sharing alpha: alpha <-> beta and alpha -> gamma -> beta -> alpha, + // so {alpha, beta, gamma} is a single strongly-connected component and all three are recursive. + function alpha() { beta() gamma() } + function beta() { alpha() } + function gamma() { beta() } +} +// ---- +//
+// alpha (recursive) -> beta, gamma +// beta (recursive) -> alpha +// gamma (recursive) -> beta diff --git a/test/libyul/yulCallGraph/intersecting_cycles_visitation_order.yul b/test/libyul/yulCallGraph/intersecting_cycles_visitation_order.yul new file mode 100644 index 000000000000..3ae2e10fc95f --- /dev/null +++ b/test/libyul/yulCallGraph/intersecting_cycles_visitation_order.yul @@ -0,0 +1,12 @@ +{ + // Same topology as intersecting_cycles.yul (two recursive cycles sharing a node, so the whole + // {hub, spoke, rim} set is a single strongly-connected component) but with different function names. + function hub() { spoke() rim() } + function spoke() { hub() } + function rim() { spoke() } +} +// ---- +//
+// hub (recursive) -> spoke, rim +// spoke (recursive) -> hub +// rim (recursive) -> spoke diff --git a/test/libyul/yulCallGraph/leaf.yul b/test/libyul/yulCallGraph/leaf.yul new file mode 100644 index 000000000000..06a0ba5ad89c --- /dev/null +++ b/test/libyul/yulCallGraph/leaf.yul @@ -0,0 +1,6 @@ +{ + function f() {} +} +// ---- +//
+// f diff --git a/test/libyul/yulCallGraph/loop_in_main.yul b/test/libyul/yulCallGraph/loop_in_main.yul new file mode 100644 index 000000000000..debeabb9cf52 --- /dev/null +++ b/test/libyul/yulCallGraph/loop_in_main.yul @@ -0,0 +1,5 @@ +{ + for {} 1 {} {} +} +// ---- +//
(loops) diff --git a/test/libyul/yulCallGraph/loop_in_nested_function.yul b/test/libyul/yulCallGraph/loop_in_nested_function.yul new file mode 100644 index 000000000000..064933b5f1c0 --- /dev/null +++ b/test/libyul/yulCallGraph/loop_in_nested_function.yul @@ -0,0 +1,14 @@ +{ + function outer() { + function inner() { + for {} 1 {} {} + outer() + } + inner() + } + outer() +} +// ---- +//
-> outer +// outer (recursive) -> inner +// inner (recursive, loops) -> outer diff --git a/test/libyul/yulCallGraph/mixed.yul b/test/libyul/yulCallGraph/mixed.yul new file mode 100644 index 000000000000..c3541316f360 --- /dev/null +++ b/test/libyul/yulCallGraph/mixed.yul @@ -0,0 +1,10 @@ +{ + function f() { g() } + function g() { f() } + function h() {} +} +// ---- +//
+// f (recursive) -> g +// g (recursive) -> f +// h diff --git a/test/libyul/yulCallGraph/multi_callee_three_scc.yul b/test/libyul/yulCallGraph/multi_callee_three_scc.yul new file mode 100644 index 000000000000..0d787837a7ee --- /dev/null +++ b/test/libyul/yulCallGraph/multi_callee_three_scc.yul @@ -0,0 +1,13 @@ +{ + function a() { + b() + c() + } + function b() { a() } + function c() { b() } +} +// ---- +//
+// a (recursive) -> b, c +// b (recursive) -> a +// c (recursive) -> b diff --git a/test/libyul/yulCallGraph/multiple_distinct_callees.yul b/test/libyul/yulCallGraph/multiple_distinct_callees.yul new file mode 100644 index 000000000000..c08aba6e68d0 --- /dev/null +++ b/test/libyul/yulCallGraph/multiple_distinct_callees.yul @@ -0,0 +1,13 @@ +{ + function f() { + g() + h() + } + function g() {} + function h() {} +} +// ---- +//
+// f -> g, h +// g +// h diff --git a/test/libyul/yulCallGraph/multiple_loops.yul b/test/libyul/yulCallGraph/multiple_loops.yul new file mode 100644 index 000000000000..144e638de251 --- /dev/null +++ b/test/libyul/yulCallGraph/multiple_loops.yul @@ -0,0 +1,13 @@ +{ + for {} 1 {} {} + function f() { + for {} 1 {} { + for {} 1 {} {} + } + } + function g() {} +} +// ---- +//
(loops) +// f (loops) +// g diff --git a/test/libyul/yulCallGraph/mutual_three_cycle.yul b/test/libyul/yulCallGraph/mutual_three_cycle.yul new file mode 100644 index 000000000000..f2d4af836885 --- /dev/null +++ b/test/libyul/yulCallGraph/mutual_three_cycle.yul @@ -0,0 +1,10 @@ +{ + function f() { g() } + function g() { h() } + function h() { f() } +} +// ---- +//
+// f (recursive) -> g +// g (recursive) -> h +// h (recursive) -> f diff --git a/test/libyul/yulCallGraph/mutual_two_cycle.yul b/test/libyul/yulCallGraph/mutual_two_cycle.yul new file mode 100644 index 000000000000..cc187f1c140f --- /dev/null +++ b/test/libyul/yulCallGraph/mutual_two_cycle.yul @@ -0,0 +1,8 @@ +{ + function f() { g() } + function g() { f() } +} +// ---- +//
+// f (recursive) -> g +// g (recursive) -> f diff --git a/test/libyul/yulCallGraph/nested_functions.yul b/test/libyul/yulCallGraph/nested_functions.yul new file mode 100644 index 000000000000..5e7068b758fd --- /dev/null +++ b/test/libyul/yulCallGraph/nested_functions.yul @@ -0,0 +1,16 @@ +{ + // Nested function definitions with a cycle a -> b -> c -> a. + function a() { + function b() { + function c() { a() } + c() + } + b() + } + a() +} +// ---- +//
-> a +// a (recursive) -> b +// b (recursive) -> c +// c (recursive) -> a diff --git a/test/libyul/yulCallGraph/non_recursive_chain.yul b/test/libyul/yulCallGraph/non_recursive_chain.yul new file mode 100644 index 000000000000..6d3f95ef1a5b --- /dev/null +++ b/test/libyul/yulCallGraph/non_recursive_chain.yul @@ -0,0 +1,10 @@ +{ + function f() { g() } + function g() { h() } + function h() {} +} +// ---- +//
+// f -> g +// g -> h +// h diff --git a/test/libyul/yulCallGraph/self_and_other.yul b/test/libyul/yulCallGraph/self_and_other.yul new file mode 100644 index 000000000000..f60f8723d946 --- /dev/null +++ b/test/libyul/yulCallGraph/self_and_other.yul @@ -0,0 +1,11 @@ +{ + function f() { + f() + g() + } + function g() {} +} +// ---- +//
+// f (recursive) -> f, g +// g diff --git a/test/libyul/yulCallGraph/shared_callee.yul b/test/libyul/yulCallGraph/shared_callee.yul new file mode 100644 index 000000000000..88776dd0a912 --- /dev/null +++ b/test/libyul/yulCallGraph/shared_callee.yul @@ -0,0 +1,10 @@ +{ + function f() { h() } + function g() { h() } + function h() {} +} +// ---- +//
+// f -> h +// g -> h +// h diff --git a/test/libyul/yulOptimizerTests/functionSpecializer/intersecting_recursive_cycles.yul b/test/libyul/yulOptimizerTests/functionSpecializer/intersecting_recursive_cycles.yul new file mode 100644 index 000000000000..9bcfd14d26a8 --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionSpecializer/intersecting_recursive_cycles.yul @@ -0,0 +1,35 @@ +{ + // Intersecting recursive cycles e -> b -> d -> e and e -> c -> d -> e share {d, e}, so the whole + // {e, b, c, d} set is a single strongly-connected component and all of them are recursive. + // In particular c() must be recognized as recursive and therefore left un-specialized, despite the + // constant call site c(10). This is a regression test for the call graph cycle-detection bug that + // misclassified some members of intersecting cycles as non-recursive: specializing c(10) here would + // produce code that is not equivalent to the original. + function e(n) { + if eq(n, 0) { leave } + b(sub(n, 1)) + c(sub(n, 1)) + } + function b(n) { d(sub(n, 1)) } + function c(n) { d(sub(n, 1)) } + function d(n) { e(sub(n, 1)) } + c(10) +} +// ---- +// step: functionSpecializer +// +// { +// c(10) +// function e(n) +// { +// if eq(n, 0) { leave } +// b(sub(n, 1)) +// c(sub(n, 1)) +// } +// function b(n_1) +// { d(sub(n_1, 1)) } +// function c(n_2) +// { d(sub(n_2, 1)) } +// function d(n_3) +// { e(sub(n_3, 1)) } +// } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/intersecting_cycles.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/intersecting_cycles.yul new file mode 100644 index 000000000000..a875fda5280c --- /dev/null +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/intersecting_cycles.yul @@ -0,0 +1,105 @@ +{ + mstore(0x40, memoryguard(128)) + ba() + function ba() { + z() + y() + } + function z() { + ba() + } + function y() { + let a1 := calldataload(mul(1,4)) + let a2 := calldataload(mul(2,4)) + let a3 := calldataload(mul(3,4)) + let a4 := calldataload(mul(4,4)) + let a5 := calldataload(mul(5,4)) + let a6 := calldataload(mul(6,4)) + let a7 := calldataload(mul(7,4)) + let a8 := calldataload(mul(8,4)) + let a9 := calldataload(mul(9,4)) + a1 := calldataload(mul(0,4)) + let a10 := calldataload(mul(10,4)) + let a11 := calldataload(mul(11,4)) + let a12 := calldataload(mul(12,4)) + let a13 := calldataload(mul(13,4)) + let a14 := calldataload(mul(14,4)) + let a15 := calldataload(mul(15,4)) + let a16 := calldataload(mul(16,4)) + let a17 := calldataload(mul(17,4)) + z() + sstore(0, a1) + sstore(mul(17,4), a17) + sstore(mul(16,4), a16) + sstore(mul(15,4), a15) + sstore(mul(14,4), a14) + sstore(mul(13,4), a13) + sstore(mul(12,4), a12) + sstore(mul(11,4), a11) + sstore(mul(10,4), a10) + sstore(mul(9,4), a9) + sstore(mul(8,4), a8) + sstore(mul(7,4), a7) + sstore(mul(6,4), a6) + sstore(mul(5,4), a5) + sstore(mul(4,4), a4) + sstore(mul(3,4), a3) + sstore(mul(2,4), a2) + sstore(mul(1,4), a1) + } +} +// ==== +// ---- +// step: stackLimitEvader +// +// { +// mstore(0x40, memoryguard(128)) +// ba() +// function ba() +// { +// z() +// y() +// } +// function z() +// { ba() } +// function y() +// { +// let a1 := calldataload(mul(1, 4)) +// let a2 := calldataload(mul(2, 4)) +// let a3 := calldataload(mul(3, 4)) +// let a4 := calldataload(mul(4, 4)) +// let a5 := calldataload(mul(5, 4)) +// let a6 := calldataload(mul(6, 4)) +// let a7 := calldataload(mul(7, 4)) +// let a8 := calldataload(mul(8, 4)) +// let a9 := calldataload(mul(9, 4)) +// a1 := calldataload(mul(0, 4)) +// let a10 := calldataload(mul(10, 4)) +// let a11 := calldataload(mul(11, 4)) +// let a12 := calldataload(mul(12, 4)) +// let a13 := calldataload(mul(13, 4)) +// let a14 := calldataload(mul(14, 4)) +// let a15 := calldataload(mul(15, 4)) +// let a16 := calldataload(mul(16, 4)) +// let a17 := calldataload(mul(17, 4)) +// z() +// sstore(0, a1) +// sstore(mul(17, 4), a17) +// sstore(mul(16, 4), a16) +// sstore(mul(15, 4), a15) +// sstore(mul(14, 4), a14) +// sstore(mul(13, 4), a13) +// sstore(mul(12, 4), a12) +// sstore(mul(11, 4), a11) +// sstore(mul(10, 4), a10) +// sstore(mul(9, 4), a9) +// sstore(mul(8, 4), a8) +// sstore(mul(7, 4), a7) +// sstore(mul(6, 4), a6) +// sstore(mul(5, 4), a5) +// sstore(mul(4, 4), a4) +// sstore(mul(3, 4), a3) +// sstore(mul(2, 4), a2) +// sstore(mul(1, 4), a1) +// } +// } diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 289f2f2def35..84eb69013418 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -40,6 +40,7 @@ add_executable(isoltest ../libsolidity/ASTPropertyTest.cpp ../libsolidity/FunctionDependencyGraphTest.cpp ../libsolidity/SMTCheckerTest.cpp + ../libyul/CallGraphTest.cpp ../libyul/Common.cpp ../libyul/ControlFlowGraphTest.cpp ../libyul/SSAControlFlowGraphTest.cpp