Skip to content

Instantly share code, notes, and snippets.

@IllIllI000
Last active September 5, 2023 17:59
Show Gist options
  • Save IllIllI000/2ac9a647be917f58ffe5baa2cecbbc42 to your computer and use it in GitHub Desktop.
Save IllIllI000/2ac9a647be917f58ffe5baa2cecbbc42 to your computer and use it in GitHub Desktop.

Part 1

The only difference between uint256[] calldata and uint8[] calldata is that the latter costs more due to having to clear the unused bytes

ExtArgMemVsCallBasic_256.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; // optimize 200

/**
 * @title ExtArgMemVsCallBasic
 * @author IllIllI
 */
contract ExtArgMemVsCallBasic {
    function test(uint256[] calldata x) pure public returns (uint256) {
        uint256 total;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += x[i];
        }
        return total;
    }
}

ExtArgMemVsCallBasic_8.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; // optimize 200

/**
 * @title ExtArgMemVsCallBasic
 * @author IllIllI
 */
contract ExtArgMemVsCallBasic {
    function test(uint8[] calldata x) pure public returns (uint8) {
        uint8 total;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += x[i];
        }
        return total;
    }
}

Diff

diff --git a/ExtArgMemVsCallBasic_256.sol b/ExtArgMemVsCallBasic_8.sol
index 6beeb27..9382636 100644
--- a/ExtArgMemVsCallBasic_256.sol
+++ b/ExtArgMemVsCallBasic_8.sol
@@ -6,8 +6,8 @@ pragma solidity ^0.8.0; // optimize 200
  * @author IllIllI
  */
 contract ExtArgMemVsCallBasic {
-    function test(uint256[] calldata x) pure public returns (uint256) {
-        uint256 total;
+    function test(uint8[] calldata x) pure public returns (uint8) {
+        uint8 total;
         uint256 len = x.length;
         for (uint256 i = 0; i < len; ++i) {
             total += x[i];

Compiled binary

$ solc ExtArgMemVsCallBasic_256.sol --bin --optimize --optimize-runs 200 | egrep "^60[0-9]+"
608060405234801561001057600080fd5b506101ac806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063ca16068414610030575b600080fd5b61004361003e3660046100b5565b610059565b6040516100509190610124565b60405180910390f35b60008082815b818110156100ab5785858281811061008757634e487b7160e01b600052603260045260246000fd5b9050602002013583610099919061012d565b92506100a481610145565b905061005f565b5090949350505050565b600080602083850312156100c7578182fd5b823567ffffffffffffffff808211156100de578384fd5b818501915085601f8301126100f1578384fd5b8135818111156100ff578485fd5b8660208083028501011115610112578485fd5b60209290920196919550909350505050565b90815260200190565b6000821982111561014057610140610160565b500190565b600060001982141561015957610159610160565b5060010190565b634e487b7160e01b600052601160045260246000fdfea2646970667358221220dfc3df939370be6b43dae1ec511e98296e3eccc0becc68dfc9e34e4739370f1e64736f6c63430008000033

$ solc ExtArgMemVsCallBasic_8.sol --bin --optimize --optimize-runs 200 | egrep "^60[0-9]+"
608060405234801561001057600080fd5b506101f3806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80630c07479214610030575b600080fd5b61004361003e3660046100c2565b610059565b6040516100509190610159565b60405180910390f35b60008082815b818110156100b85785858281811061008757634e487b7160e01b600052603260045260246000fd5b905060200201602081019061009c9190610131565b6100a69084610167565b92506100b18161018c565b905061005f565b5090949350505050565b600080602083850312156100d4578182fd5b823567ffffffffffffffff808211156100eb578384fd5b818501915085601f8301126100fe578384fd5b81358181111561010c578485fd5b866020808302850101111561011f578485fd5b60209290920196919550909350505050565b600060208284031215610142578081fd5b813560ff81168114610152578182fd5b9392505050565b60ff91909116815260200190565b600060ff821660ff84168060ff03821115610184576101846101a7565b019392505050565b60006000198214156101a0576101a06101a7565b5060010190565b634e487b7160e01b600052601160045260246000fdfea2646970667358221220e1344142ded6a150fcba3805ed7d927986ae6b1cfeb59887a011ebd8da49e03e64736f6c63430008000033

IR Diff

diff --git a/a b/b
index 3fca2e6..3cd224a 100644
--- a/a
+++ b/b
@@ -32,14 +32,14 @@ object "ExtArgMemVsCallBasic_40" {
                 let selector := shift_right_224_unsigned(calldataload(0))
                 switch selector
 
-                case 0xca160684
+                case 0x0c074792
                 {
-                    // test(uint256[])
+                    // test(uint8[])
                     if callvalue() { revert(0, 0) }
-                    let param_0, param_1 :=  abi_decode_tuple_t_array$_t_uint256_$dyn_calldata_ptr(4, calldatasize())
+                    let param_0, param_1 :=  abi_decode_tuple_t_array$_t_uint8_$dyn_calldata_ptr(4, calldatasize())
                     let ret_0 :=  fun_test_39(param_0, param_1)
                     let memPos := allocateMemory(0)
-                    let memEnd := abi_encode_tuple_t_uint256__to_t_uint256__fromStack(memPos , ret_0)
+                    let memEnd := abi_encode_tuple_t_uint8__to_t_uint8__fromStack(memPos , ret_0)
                     return(memPos, sub(memEnd, memPos))
                 }
 
@@ -48,8 +48,8 @@ object "ExtArgMemVsCallBasic_40" {
             if iszero(calldatasize()) {  }
             revert(0, 0)
 
-            // uint256[]
-            function abi_decode_t_array$_t_uint256_$dyn_calldata_ptr(offset, end) -> arrayPos, length {
+            // uint8[]
+            function abi_decode_t_array$_t_uint8_$dyn_calldata_ptr(offset, end) -> arrayPos, length {
                 if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) }
                 length := calldataload(offset)
                 if gt(length, 0xffffffffffffffff) { revert(0, 0) }
@@ -57,7 +57,7 @@ object "ExtArgMemVsCallBasic_40" {
                 if gt(add(arrayPos, mul(length, 0x20)), end) { revert(0, 0) }
             }
 
-            function abi_decode_tuple_t_array$_t_uint256_$dyn_calldata_ptr(headStart, dataEnd) -> value0, value1 {
+            function abi_decode_tuple_t_array$_t_uint8_$dyn_calldata_ptr(headStart, dataEnd) -> value0, value1 {
                 if slt(sub(dataEnd, headStart), 32) { revert(0, 0) }
 
                 {
@@ -65,19 +65,19 @@ object "ExtArgMemVsCallBasic_40" {
                     let offset := calldataload(add(headStart, 0))
                     if gt(offset, 0xffffffffffffffff) { revert(0, 0) }
 
-                    value0, value1 := abi_decode_t_array$_t_uint256_$dyn_calldata_ptr(add(headStart, offset), dataEnd)
+                    value0, value1 := abi_decode_t_array$_t_uint8_$dyn_calldata_ptr(add(headStart, offset), dataEnd)
                 }
 
             }
 
-            function abi_encode_t_uint256_to_t_uint256_fromStack(value, pos) {
-                mstore(pos, cleanup_t_uint256(value))
+            function abi_encode_t_uint8_to_t_uint8_fromStack(value, pos) {
+                mstore(pos, cleanup_t_uint8(value))
             }
 
-            function abi_encode_tuple_t_uint256__to_t_uint256__fromStack(headStart , value0) -> tail {
+            function abi_encode_tuple_t_uint8__to_t_uint8__fromStack(headStart , value0) -> tail {
                 tail := add(headStart, 32)
 
-                abi_encode_t_uint256_to_t_uint256_fromStack(value0,  add(headStart, 0))
+                abi_encode_t_uint8_to_t_uint8_fromStack(value0,  add(headStart, 0))
 
             }
 
@@ -89,24 +89,24 @@ object "ExtArgMemVsCallBasic_40" {
                 mstore(64, newFreePtr)
             }
 
-            function array_length_t_array$_t_uint256_$dyn_calldata_ptr(value, len) -> length {
+            function array_length_t_array$_t_uint8_$dyn_calldata_ptr(value, len) -> length {
 
                 length := len
 
             }
 
-            function calldata_array_index_access_t_array$_t_uint256_$dyn_calldata_ptr(base_ref, length, index) -> addr {
+            function calldata_array_index_access_t_array$_t_uint8_$dyn_calldata_ptr(base_ref, length, index) -> addr {
                 if iszero(lt(index, length)) { panic_error_0x32() }
                 addr := add(base_ref, mul(index, 32))
 
             }
 
-            function checked_add_t_uint256(x, y) -> sum {
-                x := cleanup_t_uint256(x)
-                y := cleanup_t_uint256(y)
+            function checked_add_t_uint8(x, y) -> sum {
+                x := cleanup_t_uint8(x)
+                y := cleanup_t_uint8(y)
 
                 // overflow, if x > (maxValue - y)
-                if gt(x, sub(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, y)) { panic_error_0x11() }
+                if gt(x, sub(0xff, y)) { panic_error_0x11() }
 
                 sum := add(x, y)
             }
@@ -115,22 +115,26 @@ object "ExtArgMemVsCallBasic_40" {
                 cleaned := value
             }
 
+            function cleanup_t_uint8(value) -> cleaned {
+                cleaned := and(value, 0xff)
+            }
+
             function convert_t_rational_0_by_1_to_t_uint256(value) -> converted {
                 converted := cleanup_t_uint256(value)
             }
 
             function fun_test_39(vloc_x_5_offset, vloc_x_5_length) -> vloc__8 {
-                let zero_value_for_type_t_uint256_1 := zero_value_for_split_t_uint256()
-                vloc__8 := zero_value_for_type_t_uint256_1
+                let zero_value_for_type_t_uint8_1 := zero_value_for_split_t_uint8()
+                vloc__8 := zero_value_for_type_t_uint8_1
 
                 let vloc_total_11
-                let zero_value_for_type_t_uint256_2 := zero_value_for_split_t_uint256()
-                vloc_total_11 := zero_value_for_type_t_uint256_2
+                let zero_value_for_type_t_uint8_2 := zero_value_for_split_t_uint8()
+                vloc_total_11 := zero_value_for_type_t_uint8_2
                 let _3_offset := vloc_x_5_offset
                 let _3_length := vloc_x_5_length
                 let expr_15_offset := _3_offset
                 let expr_15_length := _3_length
-                let expr_16 := array_length_t_array$_t_uint256_$dyn_calldata_ptr(expr_15_offset, expr_15_length)
+                let expr_16 := array_length_t_array$_t_uint8_$dyn_calldata_ptr(expr_15_offset, expr_15_length)
                 let vloc_len_14 := expr_16
                 for {
                     let expr_20 := 0x00
@@ -154,9 +158,9 @@ object "ExtArgMemVsCallBasic_40" {
                     let expr_29_length := _8_length
                     let _9 := vloc_i_19
                     let expr_30 := _9
-                    let expr_31 := read_from_calldatat_uint256(calldata_array_index_access_t_array$_t_uint256_$dyn_calldata_ptr(expr_29_offset, expr_29_length, expr_30))
+                    let expr_31 := read_from_calldatat_uint8(calldata_array_index_access_t_array$_t_uint8_$dyn_calldata_ptr(expr_29_offset, expr_29_length, expr_30))
                     let _10 := vloc_total_11
-                    expr_31 := checked_add_t_uint256(_10, expr_31)
+                    expr_31 := checked_add_t_uint8(_10, expr_31)
                     vloc_total_11 := expr_31
                     let expr_32 := expr_31
                 }
@@ -191,10 +195,10 @@ object "ExtArgMemVsCallBasic_40" {
                 revert(0, 0x24)
             }
 
-            function read_from_calldatat_uint256(ptr) -> returnValue {
+            function read_from_calldatat_uint8(ptr) -> returnValue {
 
                 let value := calldataload(ptr)
-                validator_revert_t_uint256(value)
+                validator_revert_t_uint8(value)
 
                 returnValue :=
 
@@ -209,11 +213,11 @@ object "ExtArgMemVsCallBasic_40" {
 
             }
 
-            function validator_revert_t_uint256(value) {
-                if iszero(eq(value, cleanup_t_uint256(value))) { revert(0, 0) }
+            function validator_revert_t_uint8(value) {
+                if iszero(eq(value, cleanup_t_uint8(value))) { revert(0, 0) }
             }
 
-            function zero_value_for_split_t_uint256() -> ret {
+            function zero_value_for_split_t_uint8() -> ret {
                 ret := 0
             }
 

Opcodes

PC.Uint256 Operation.Uint256 Gas.Uint256 PC.Uint8 Operation.Uint8 Gas.Uint8
172(0xac) MUL(0x2) 5 172(0xac) MUL(0x2) 5
173(0xad) ADD(0x1) 3 173(0xad) ADD(0x1) 3
174(0xae) PUSH1(0x60) ["0x20"] 3
176(0xb0) DUP2(0x81) 3
177(0xb1) ADD(0x1) 3
178(0xb2) SWAP1(0x90) 3
174(0xae) CALLDATALOAD(0x35) 3
175(0xaf) DUP4(0x83) 3
176(0xb0) PUSH2(0x61) ["0x00","0x99"] 3 179(0xb3) PUSH2(0x61) ["0x00","0x9c"] 3
179(0xb3) SWAP2(0x91) 3 182(0xb6) SWAP2(0x91) 3
180(0xb4) SWAP1(0x90) 3 183(0xb7) SWAP1(0x90) 3
181(0xb5) PUSH2(0x61) ["0x01","0x2d"] 3 184(0xb8) PUSH2(0x61) ["0x01","0x31"] 3
184(0xb8) JUMP(0x56) 8 187(0xbb) JUMP(0x56) 8
185(0xb9) JUMPDEST(0x5b)@0xb9 1 188(0xbc) JUMPDEST(0x5b)@0xbc 1
189(0xbd) PUSH2(0x61) ["0x00","0xa6"] 3
192(0xc0) SWAP1(0x90) 3
193(0xc1) DUP5(0x84) 3
194(0xc2) PUSH2(0x61) ["0x01","0x67"] 3
197(0xc5) JUMP(0x56) 8
198(0xc6) JUMPDEST(0x5b)@0xc6 1
186(0xba) SWAP3(0x92) 3 199(0xc7) SWAP3(0x92) 3
187(0xbb) POP(0x50) 2 200(0xc8) POP(0x50) 2
188(0xbc) PUSH2(0x61) ["0x00","0xa4"] 3 201(0xc9) PUSH2(0x61) ["0x00","0xb1"] 3
191(0xbf) DUP2(0x81) 3 204(0xcc) DUP2(0x81) 3
192(0xc0) PUSH2(0x61) ["0x01","0x45"] 3 205(0xcd) PUSH2(0x61) ["0x01","0x8c"] 3
195(0xc3) JUMP(0x56) 8 208(0xd0) JUMP(0x56) 8
196(0xc4) JUMPDEST(0x5b)@0xc4 1 209(0xd1) JUMPDEST(0x5b)@0xd1 1
197(0xc5) SWAP1(0x90) 3 210(0xd2) SWAP1(0x90) 3
198(0xc6) POP(0x50) 2 211(0xd3) POP(0x50) 2
199(0xc7) PUSH2(0x61) ["0x00","0x5f"] 3 212(0xd4) PUSH2(0x61) ["0x00","0x5f"] 3
202(0xca) JUMP(0x56) 8 215(0xd7) JUMP(0x56) 8
203(0xcb) JUMPDEST(0x5b)@0xcb 1 216(0xd8) JUMPDEST(0x5b)@0xd8 1
204(0xcc) POP(0x50) 2 217(0xd9) POP(0x50) 2
205(0xcd) SWAP1(0x90) 3 218(0xda) SWAP1(0x90) 3
206(0xce) SWAP5(0x94) 3 219(0xdb) SWAP5(0x94) 3
207(0xcf) SWAP4(0x93) 3 220(0xdc) SWAP4(0x93) 3
208(0xd0) POP(0x50) 2 221(0xdd) POP(0x50) 2
209(0xd1) POP(0x50) 2 222(0xde) POP(0x50) 2
210(0xd2) POP(0x50) 2 223(0xdf) POP(0x50) 2
211(0xd3) POP(0x50) 2 224(0xe0) POP(0x50) 2
212(0xd4) JUMP(0x56) 8 225(0xe1) JUMP(0x56) 8
213(0xd5) JUMPDEST(0x5b)@0xd5 1 226(0xe2) JUMPDEST(0x5b)@0xe2 1
214(0xd6) PUSH1(0x60) ["0x00"] 3 227(0xe3) PUSH1(0x60) ["0x00"] 3
216(0xd8) DUP1(0x80) 3 229(0xe5) DUP1(0x80) 3
217(0xd9) PUSH1(0x60) ["0x20"] 3 230(0xe6) PUSH1(0x60) ["0x20"] 3
219(0xdb) DUP4(0x83) 3 232(0xe8) DUP4(0x83) 3
220(0xdc) DUP6(0x85) 3 233(0xe9) DUP6(0x85) 3
221(0xdd) SUB(0x3) 3 234(0xea) SUB(0x3) 3
222(0xde) SLT(0x12) 3 235(0xeb) SLT(0x12) 3
223(0xdf) ISZERO(0x15) 3 236(0xec) ISZERO(0x15) 3
224(0xe0) PUSH2(0x61) ["0x00","0xc7"] 3 237(0xed) PUSH2(0x61) ["0x00","0xd4"] 3
227(0xe3) JUMPI(0x57) 10 240(0xf0) JUMPI(0x57) 10
228(0xe4) DUP2(0x81) 3 241(0xf1) DUP2(0x81) 3
229(0xe5) DUP3(0x82) 3 242(0xf2) DUP3(0x82) 3
230(0xe6) REVERT(0xfd) 0 243(0xf3) REVERT(0xfd) 0
231(0xe7) JUMPDEST(0x5b)@0xe7 1 244(0xf4) JUMPDEST(0x5b)@0xf4 1
232(0xe8) DUP3(0x82) 3 245(0xf5) DUP3(0x82) 3
233(0xe9) CALLDATALOAD(0x35) 3 246(0xf6) CALLDATALOAD(0x35) 3
234(0xea) PUSH8(0x67) ["0xff","0xff",... 3 247(0xf7) PUSH8(0x67) ["0xff","0xff",... 3
243(0xf3) DUP1(0x80) 3 256(0x0100) DUP1(0x80) 3
244(0xf4) DUP3(0x82) 3 257(0x0101) DUP3(0x82) 3
245(0xf5) GT(0x11) 3 258(0x0102) GT(0x11) 3
246(0xf6) ISZERO(0x15) 3 259(0x0103) ISZERO(0x15) 3
247(0xf7) PUSH2(0x61) ["0x00","0xde"] 3 260(0x0104) PUSH2(0x61) ["0x00","0xeb"] 3
250(0xfa) JUMPI(0x57) 10 263(0x0107) JUMPI(0x57) 10
251(0xfb) DUP4(0x83) 3 264(0x0108) DUP4(0x83) 3
252(0xfc) DUP5(0x84) 3 265(0x0109) DUP5(0x84) 3
253(0xfd) REVERT(0xfd) 0 266(0x010a) REVERT(0xfd) 0
254(0xfe) JUMPDEST(0x5b)@0xfe 1 267(0x010b) JUMPDEST(0x5b)@0x010b 1
255(0xff) DUP2(0x81) 3 268(0x010c) DUP2(0x81) 3
256(0x0100) DUP6(0x85) 3 269(0x010d) DUP6(0x85) 3
257(0x0101) ADD(0x1) 3 270(0x010e) ADD(0x1) 3
258(0x0102) SWAP2(0x91) 3 271(0x010f) SWAP2(0x91) 3
259(0x0103) POP(0x50) 2 272(0x0110) POP(0x50) 2
260(0x0104) DUP6(0x85) 3 273(0x0111) DUP6(0x85) 3
261(0x0105) PUSH1(0x60) ["0x1f"] 3 274(0x0112) PUSH1(0x60) ["0x1f"] 3
263(0x0107) DUP4(0x83) 3 276(0x0114) DUP4(0x83) 3
264(0x0108) ADD(0x1) 3 277(0x0115) ADD(0x1) 3
265(0x0109) SLT(0x12) 3 278(0x0116) SLT(0x12) 3
266(0x010a) PUSH2(0x61) ["0x00","0xf1"] 3 279(0x0117) PUSH2(0x61) ["0x00","0xfe"] 3
269(0x010d) JUMPI(0x57) 10 282(0x011a) JUMPI(0x57) 10
270(0x010e) DUP4(0x83) 3 283(0x011b) DUP4(0x83) 3
271(0x010f) DUP5(0x84) 3 284(0x011c) DUP5(0x84) 3
272(0x0110) REVERT(0xfd) 0 285(0x011d) REVERT(0xfd) 0
273(0x0111) JUMPDEST(0x5b)@0x0111 1 286(0x011e) JUMPDEST(0x5b)@0x011e 1
274(0x0112) DUP2(0x81) 3 287(0x011f) DUP2(0x81) 3
275(0x0113) CALLDATALOAD(0x35) 3 288(0x0120) CALLDATALOAD(0x35) 3
276(0x0114) DUP2(0x81) 3 289(0x0121) DUP2(0x81) 3
277(0x0115) DUP2(0x81) 3 290(0x0122) DUP2(0x81) 3
278(0x0116) GT(0x11) 3 291(0x0123) GT(0x11) 3
279(0x0117) ISZERO(0x15) 3 292(0x0124) ISZERO(0x15) 3
280(0x0118) PUSH2(0x61) ["0x00","0xff"] 3 293(0x0125) PUSH2(0x61) ["0x01","0x0c"] 3
283(0x011b) JUMPI(0x57) 10 296(0x0128) JUMPI(0x57) 10
284(0x011c) DUP5(0x84) 3 297(0x0129) DUP5(0x84) 3
285(0x011d) DUP6(0x85) 3 298(0x012a) DUP6(0x85) 3
286(0x011e) REVERT(0xfd) 0 299(0x012b) REVERT(0xfd) 0
287(0x011f) JUMPDEST(0x5b)@0x011f 1 300(0x012c) JUMPDEST(0x5b)@0x012c 1
288(0x0120) DUP7(0x86) 3 301(0x012d) DUP7(0x86) 3
289(0x0121) PUSH1(0x60) ["0x20"] 3 302(0x012e) PUSH1(0x60) ["0x20"] 3
291(0x0123) DUP1(0x80) 3 304(0x0130) DUP1(0x80) 3
292(0x0124) DUP4(0x83) 3 305(0x0131) DUP4(0x83) 3
293(0x0125) MUL(0x2) 5 306(0x0132) MUL(0x2) 5
294(0x0126) DUP6(0x85) 3 307(0x0133) DUP6(0x85) 3
295(0x0127) ADD(0x1) 3 308(0x0134) ADD(0x1) 3
296(0x0128) ADD(0x1) 3 309(0x0135) ADD(0x1) 3
297(0x0129) GT(0x11) 3 310(0x0136) GT(0x11) 3
298(0x012a) ISZERO(0x15) 3 311(0x0137) ISZERO(0x15) 3
299(0x012b) PUSH2(0x61) ["0x01","0x12"] 3 312(0x0138) PUSH2(0x61) ["0x01","0x1f"] 3
302(0x012e) JUMPI(0x57) 10 315(0x013b) JUMPI(0x57) 10
303(0x012f) DUP5(0x84) 3 316(0x013c) DUP5(0x84) 3
304(0x0130) DUP6(0x85) 3 317(0x013d) DUP6(0x85) 3
305(0x0131) REVERT(0xfd) 0 318(0x013e) REVERT(0xfd) 0
306(0x0132) JUMPDEST(0x5b)@0x0132 1 319(0x013f) JUMPDEST(0x5b)@0x013f 1
307(0x0133) PUSH1(0x60) ["0x20"] 3 320(0x0140) PUSH1(0x60) ["0x20"] 3
309(0x0135) SWAP3(0x92) 3 322(0x0142) SWAP3(0x92) 3
310(0x0136) SWAP1(0x90) 3 323(0x0143) SWAP1(0x90) 3
311(0x0137) SWAP3(0x92) 3 324(0x0144) SWAP3(0x92) 3
312(0x0138) ADD(0x1) 3 325(0x0145) ADD(0x1) 3
313(0x0139) SWAP7(0x96) 3 326(0x0146) SWAP7(0x96) 3
314(0x013a) SWAP2(0x91) 3 327(0x0147) SWAP2(0x91) 3
315(0x013b) SWAP6(0x95) 3 328(0x0148) SWAP6(0x95) 3
316(0x013c) POP(0x50) 2 329(0x0149) POP(0x50) 2
317(0x013d) SWAP1(0x90) 3 330(0x014a) SWAP1(0x90) 3
318(0x013e) SWAP4(0x93) 3 331(0x014b) SWAP4(0x93) 3
319(0x013f) POP(0x50) 2 332(0x014c) POP(0x50) 2
320(0x0140) POP(0x50) 2 333(0x014d) POP(0x50) 2
321(0x0141) POP(0x50) 2 334(0x014e) POP(0x50) 2
322(0x0142) POP(0x50) 2 335(0x014f) POP(0x50) 2
323(0x0143) JUMP(0x56) 8 336(0x0150) JUMP(0x56) 8
324(0x0144) JUMPDEST(0x5b)@0x0144 1 337(0x0151) JUMPDEST(0x5b)@0x0151 1
338(0x0152) PUSH1(0x60) ["0x00"] 3
340(0x0154) PUSH1(0x60) ["0x20"] 3
342(0x0156) DUP3(0x82) 3
343(0x0157) DUP5(0x84) 3
344(0x0158) SUB(0x3) 3
345(0x0159) SLT(0x12) 3
346(0x015a) ISZERO(0x15) 3
347(0x015b) PUSH2(0x61) ["0x01","0x42"] 3
350(0x015e) JUMPI(0x57) 10
351(0x015f) DUP1(0x80) 3
352(0x0160) DUP2(0x81) 3
353(0x0161) REVERT(0xfd) 0
354(0x0162) JUMPDEST(0x5b)@0x0162 1
355(0x0163) DUP2(0x81) 3
356(0x0164) CALLDATALOAD(0x35) 3
357(0x0165) PUSH1(0x60) ["0xff"] 3
359(0x0167) DUP2(0x81) 3
360(0x0168) AND(0x16) 3
361(0x0169) DUP2(0x81) 3
362(0x016a) EQ(0x14) 3
363(0x016b) PUSH2(0x61) ["0x01","0x52"] 3
366(0x016e) JUMPI(0x57) 10
367(0x016f) DUP2(0x81) 3
368(0x0170) DUP3(0x82) 3
369(0x0171) REVERT(0xfd) 0
370(0x0172) JUMPDEST(0x5b)@0x0172 1
371(0x0173) SWAP4(0x93) 3
372(0x0174) SWAP3(0x92) 3
373(0x0175) POP(0x50) 2
374(0x0176) POP(0x50) 2
375(0x0177) POP(0x50) 2
376(0x0178) JUMP(0x56) 8
377(0x0179) JUMPDEST(0x5b)@0x0179 1
378(0x017a) PUSH1(0x60) ["0xff"] 3
380(0x017c) SWAP2(0x91) 3
325(0x0145) SWAP1(0x90) 3 381(0x017d) SWAP1(0x90) 3
382(0x017e) SWAP2(0x91) 3
383(0x017f) AND(0x16) 3
326(0x0146) DUP2(0x81) 3 384(0x0180) DUP2(0x81) 3
327(0x0147) MSTORE(0x52) 3 385(0x0181) MSTORE(0x52) 3
328(0x0148) PUSH1(0x60) ["0x20"] 3 386(0x0182) PUSH1(0x60) ["0x20"] 3
330(0x014a) ADD(0x1) 3 388(0x0184) ADD(0x1) 3
331(0x014b) SWAP1(0x90) 3 389(0x0185) SWAP1(0x90) 3
332(0x014c) JUMP(0x56) 8 390(0x0186) JUMP(0x56) 8
333(0x014d) JUMPDEST(0x5b)@0x014d 1 391(0x0187) JUMPDEST(0x5b)@0x0187 1
334(0x014e) PUSH1(0x60) ["0x00"] 3 392(0x0188) PUSH1(0x60) ["0x00"] 3
394(0x018a) PUSH1(0x60) ["0xff"] 3
336(0x0150) DUP3(0x82) 3 396(0x018c) DUP3(0x82) 3
397(0x018d) AND(0x16) 3
398(0x018e) PUSH1(0x60) ["0xff"] 3
400(0x0190) DUP5(0x84) 3
401(0x0191) AND(0x16) 3
402(0x0192) DUP1(0x80) 3
403(0x0193) PUSH1(0x60) ["0xff"] 3
405(0x0195) SUB(0x3) 3
337(0x0151) NOT(0x19) 3
338(0x0152) DUP3(0x82) 3 406(0x0196) DUP3(0x82) 3
339(0x0153) GT(0x11) 3 407(0x0197) GT(0x11) 3
340(0x0154) ISZERO(0x15) 3 408(0x0198) ISZERO(0x15) 3
341(0x0155) PUSH2(0x61) ["0x01","0x40"] 3 409(0x0199) PUSH2(0x61) ["0x01","0x84"] 3
344(0x0158) JUMPI(0x57) 10 412(0x019c) JUMPI(0x57) 10
345(0x0159) PUSH2(0x61) ["0x01","0x40"] 3 413(0x019d) PUSH2(0x61) ["0x01","0x84"] 3
348(0x015c) PUSH2(0x61) ["0x01","0x60"] 3 416(0x01a0) PUSH2(0x61) ["0x01","0xa7"] 3
351(0x015f) JUMP(0x56) 8 419(0x01a3) JUMP(0x56) 8
352(0x0160) JUMPDEST(0x5b)@0x0160 1 420(0x01a4) JUMPDEST(0x5b)@0x01a4 1
421(0x01a5) ADD(0x1) 3
422(0x01a6) SWAP4(0x93) 3
423(0x01a7) SWAP3(0x92) 3
353(0x0161) POP(0x50) 2 424(0x01a8) POP(0x50) 2
425(0x01a9) POP(0x50) 2
426(0x01aa) POP(0x50) 2
354(0x0162) ADD(0x1) 3
355(0x0163) SWAP1(0x90) 3
356(0x0164) JUMP(0x56) 8 427(0x01ab) JUMP(0x56) 8
Total.Uint256: 465 Total.Uint8: 635

The change costs 170 more gas

Part 2

It's cheaper to use calldata as long as the array isn't an array of basic types whose sizes are each less than 256 bits

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; // optimize 200


struct S {
    uint256 word;
    uint128 hword1;
    uint128 hword2;
}


struct SA {
    address addr;
}


/**
 * @title ExtArgMemVsCall_c
 * @author IllIllI
 */
contract ExtArgMemVsCall_c {
    function basicAddr(address[] calldata x) pure public returns (uint256) {
        uint256 total;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += uint256(uint160(x[i]));
        }
        return total;
    }

    function basicUint8(uint8[] calldata x) pure public returns (uint8) {
        uint8 total;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += x[i];
        }
        return total;
    }

    function basicUint248(uint248[] calldata x) pure public returns (uint248) {
        uint248 total;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += x[i];
        }
        return total;
    }

    function basicUint256(uint256[] calldata x) pure public returns (uint256) {
        uint256 total;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += x[i];
        }
        return total;
    }
    
    function structFull(S[] calldata x) pure public returns (uint256) {
        uint256 total;
        uint128 total2;
        uint128 total3;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += x[i].word;
            total2 += x[i].hword1;
            total3 += x[i].hword2;
        }
        return total + total2 + total3;
    }

    function structPart(S[] calldata x) pure public returns (uint256) {
        uint256 total;
        uint128 total2;
        uint128 total3;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += x[i].word;
        }
        return total + total2 + total3;
    }

    function structFullPointer(S[] calldata x) pure public returns (uint256) {
        uint256 total;
        uint128 total2;
        uint128 total3;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            S calldata xi = x[i];
            total += xi.word;
            total2 += xi.hword1;
            total3 += xi.hword2;
        }
        return total + total2 + total3;
    }

    function structFullPointerMem(S[] calldata x) pure public returns (uint256) {
        uint256 total;
        uint128 total2;
        uint128 total3;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            S memory xi = x[i];
            total += xi.word;
            total2 += xi.hword1;
            total3 += xi.hword2;
        }
        return total + total2 + total3;
    }

    function structAddr(SA[] calldata x) pure public returns (uint256) {
        uint256 total;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += uint256(uint160(x[i].addr));
        }
        return total;
    }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; // optimize 200


struct S {
    uint256 word;
    uint128 hword1;
    uint128 hword2;
}


struct SA {
    address addr;
}

/**
 * @title ExtArgMemVsCall_m
 * @author IllIllI
 */
contract ExtArgMemVsCall_m {
    function basicAddr(address[] memory x) pure public returns (uint256) {
        uint256 total;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += uint256(uint160(x[i]));
        }
        return total;
    }

    function basicUint8(uint8[] memory x) pure public returns (uint8) {
        uint8 total;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += x[i];
        }
        return total;
    }

    function basicUint248(uint248[] memory x) pure public returns (uint248) {
        uint248 total;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += x[i];
        }
        return total;
    }

    function basicUint256(uint256[] memory x) pure public returns (uint256) {
        uint256 total;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += x[i];
        }
        return total;
    }
    
    function structFull(S[] memory x) pure public returns (uint256) {
        uint256 total;
        uint128 total2;
        uint128 total3;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += x[i].word;
            total2 += x[i].hword1;
            total3 += x[i].hword2;
        }
        return total + total2 + total3;
    }

    function structPart(S[] memory x) pure public returns (uint256) {
        uint256 total;
        uint128 total2;
        uint128 total3;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += x[i].word;
        }
        return total + total2 + total3;
    }

    function structFullPointer(S[] memory x) pure public returns (uint256) {
        uint256 total;
        uint128 total2;
        uint128 total3;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            S memory xi = x[i];
            total += xi.word;
            total2 += xi.hword1;
            total3 += xi.hword2;
        }
        return total + total2 + total3;
    }

    function structFullPointerMem(S[] memory x) pure public returns (uint256) {
        uint256 total;
        uint128 total2;
        uint128 total3;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            S memory xi = x[i];
            total += xi.word;
            total2 += xi.hword1;
            total3 += xi.hword2;
        }
        return total + total2 + total3;
    }

    function structAddr(SA[] memory x) pure public returns (uint256) {
        uint256 total;
        uint256 len = x.length;
        for (uint256 i = 0; i < len; ++i) {
            total += uint256(uint160(x[i].addr));
        }
        return total;
    }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ExtArgMemVsCall_c, S, SA} from "../src/ExtArgMemVsCall_c.sol";

contract Tst {
    uint256 constant ITERATIONS_S = 2;
    uint256 constant ITERATIONS_M = 128;
    uint256 constant ITERATIONS_L = 256;

    function testBasicAddr() external {
        ExtArgMemVsCall_c c = new ExtArgMemVsCall_c();
        c.basicAddr(new address[](ITERATIONS_S));
        c.basicAddr(new address[](ITERATIONS_M));
        c.basicAddr(new address[](ITERATIONS_L));
    }

    function testBasicUint8() external {
        ExtArgMemVsCall_c c = new ExtArgMemVsCall_c();
        c.basicUint8(new uint8[](ITERATIONS_S));
        c.basicUint8(new uint8[](ITERATIONS_M));
        c.basicUint8(new uint8[](ITERATIONS_L));
    }

    function testBasicUint248() external {
        ExtArgMemVsCall_c c = new ExtArgMemVsCall_c();
        c.basicUint248(new uint248[](ITERATIONS_S));
        c.basicUint248(new uint248[](ITERATIONS_M));
        c.basicUint248(new uint248[](ITERATIONS_L));
    }

    function testBasicUint256() external {
        ExtArgMemVsCall_c c = new ExtArgMemVsCall_c();
        c.basicUint256(new uint256[](ITERATIONS_S));
        c.basicUint256(new uint256[](ITERATIONS_M));
        c.basicUint256(new uint256[](ITERATIONS_L));
    }
    
    function testStructFull() external {
        ExtArgMemVsCall_c c = new ExtArgMemVsCall_c();
        c.structFull(new S[](ITERATIONS_S));
        c.structFull(new S[](ITERATIONS_M));
        c.structFull(new S[](ITERATIONS_L));
    }

    function testStructPart() external {
        ExtArgMemVsCall_c c = new ExtArgMemVsCall_c();
        c.structPart(new S[](ITERATIONS_S));
        c.structPart(new S[](ITERATIONS_M));
        c.structPart(new S[](ITERATIONS_L));
    }

    function testStructFullPointer() external {
        ExtArgMemVsCall_c c = new ExtArgMemVsCall_c();
        c.structFullPointer(new S[](ITERATIONS_S));
        c.structFullPointer(new S[](ITERATIONS_M));
        c.structFullPointer(new S[](ITERATIONS_L));
    }

    function testStructFullPointerMem() external {
        ExtArgMemVsCall_c c = new ExtArgMemVsCall_c();
        c.structFullPointerMem(new S[](ITERATIONS_S));
        c.structFullPointerMem(new S[](ITERATIONS_M));
        c.structFullPointerMem(new S[](ITERATIONS_L));
    }

    function testStructAddr() external {
        ExtArgMemVsCall_c c = new ExtArgMemVsCall_c();
        c.structAddr(new SA[](ITERATIONS_S));
        c.structAddr(new SA[](ITERATIONS_M));
        c.structAddr(new SA[](ITERATIONS_L));
    }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ExtArgMemVsCall_m, S, SA} from "../src/ExtArgMemVsCall_m.sol";

contract Tst {
    uint256 constant ITERATIONS_S = 2;
    uint256 constant ITERATIONS_M = 128;
    uint256 constant ITERATIONS_L = 256;

    function testBasicAddr() external {
        ExtArgMemVsCall_m c = new ExtArgMemVsCall_m();
        c.basicAddr(new address[](ITERATIONS_S));
        c.basicAddr(new address[](ITERATIONS_M));
        c.basicAddr(new address[](ITERATIONS_L));
    }

    function testBasicUint8() external {
        ExtArgMemVsCall_m c = new ExtArgMemVsCall_m();
        c.basicUint8(new uint8[](ITERATIONS_S));
        c.basicUint8(new uint8[](ITERATIONS_M));
        c.basicUint8(new uint8[](ITERATIONS_L));
    }

    function testBasicUint248() external {
        ExtArgMemVsCall_m c = new ExtArgMemVsCall_m();
        c.basicUint248(new uint248[](ITERATIONS_S));
        c.basicUint248(new uint248[](ITERATIONS_M));
        c.basicUint248(new uint248[](ITERATIONS_L));
    }

    function testBasicUint256() external {
        ExtArgMemVsCall_m c = new ExtArgMemVsCall_m();
        c.basicUint256(new uint256[](ITERATIONS_S));
        c.basicUint256(new uint256[](ITERATIONS_M));
        c.basicUint256(new uint256[](ITERATIONS_L));
    }
    
    function testStructFull() external {
        ExtArgMemVsCall_m c = new ExtArgMemVsCall_m();
        c.structFull(new S[](ITERATIONS_S));
        c.structFull(new S[](ITERATIONS_M));
        c.structFull(new S[](ITERATIONS_L));
    }

    function testStructPart() external {
        ExtArgMemVsCall_m c = new ExtArgMemVsCall_m();
        c.structPart(new S[](ITERATIONS_S));
        c.structPart(new S[](ITERATIONS_M));
        c.structPart(new S[](ITERATIONS_L));
    }

    function testStructFullPointer() external {
        ExtArgMemVsCall_m c = new ExtArgMemVsCall_m();
        c.structFullPointer(new S[](ITERATIONS_S));
        c.structFullPointer(new S[](ITERATIONS_M));
        c.structFullPointer(new S[](ITERATIONS_L));
    }

    function testStructFullPointerMem() external {
        ExtArgMemVsCall_m c = new ExtArgMemVsCall_m();
        c.structFullPointerMem(new S[](ITERATIONS_S));
        c.structFullPointerMem(new S[](ITERATIONS_M));
        c.structFullPointerMem(new S[](ITERATIONS_L));
    }

    function testStructAddr() external {
        ExtArgMemVsCall_m c = new ExtArgMemVsCall_m();
        c.structAddr(new SA[](ITERATIONS_S));
        c.structAddr(new SA[](ITERATIONS_M));
        c.structAddr(new SA[](ITERATIONS_L));
    }
}
Compiling 4 files with 0.8.20
Solc 0.8.20 finished in 966.83ms
Compiler run �[32msuccessful!�[0m

Running 9 tests for test/Memory.sol:Tst
�[32m[PASS]�[0m testBasicAddr() (gas: 792070)
�[32m[PASS]�[0m testBasicUint248() (gas: 784135)
�[32m[PASS]�[0m testBasicUint256() (gas: 748596)
�[32m[PASS]�[0m testBasicUint8() (gas: 768084)
�[32m[PASS]�[0m testStructAddr() (gas: 878300)
�[32m[PASS]�[0m testStructFull() (gas: 1105563)
�[32m[PASS]�[0m testStructFullPointer() (gas: 1074107)
�[32m[PASS]�[0m testStructFullPointerMem() (gas: 1074165)
�[32m[PASS]�[0m testStructPart() (gas: 969799)
Test result: �[32mok�[0m. 9 passed; 0 failed; finished in 31.26ms

Running 9 tests for test/Calldata.sol:Tst
�[32m[PASS]�[0m testBasicAddr() (gas: 659952)
�[32m[PASS]�[0m testBasicUint248() (gas: 665527)
�[32m[PASS]�[0m testBasicUint256() (gas: 595634)
�[32m[PASS]�[0m testBasicUint8() (gas: 649476)
�[32m[PASS]�[0m testStructAddr() (gas: 693197)
�[32m[PASS]�[0m testStructFull() (gas: 942337)
�[32m[PASS]�[0m testStructFullPointer() (gas: 906635)
�[32m[PASS]�[0m testStructFullPointerMem() (gas: 931315)
�[32m[PASS]�[0m testStructPart() (gas: 682281)
Test result: �[32mok�[0m. 9 passed; 0 failed; finished in 31.44ms
| src/ExtArgMemVsCall_c.sol:ExtArgMemVsCall_c contract |                 |        |        |        |         |
|------------------------------------------------------|-----------------|--------|--------|--------|---------|
| Deployment Cost                                      | Deployment Size |        |        |        |         |
| 432668                                               | 2193            |        |        |        |         |
| Function Name                                        | min             | avg    | median | max    | # calls |
| basicAddr                                            | 1412            | 50558  | 50300  | 99964  | 3       |
| basicUint248                                         | 1429            | 52349  | 52081  | 103537 | 3       |
| basicUint256                                         | 1028            | 31428  | 31268  | 61988  | 3       |
| basicUint8                                           | 1337            | 48583  | 48335  | 96079  | 3       |
| structAddr                                           | 1390            | 50536  | 50278  | 99942  | 3       |
| structFull                                           | 2554            | 118707 | 118096 | 235472 | 3       |
| structFullPointer                                    | 2411            | 106784 | 106235 | 211707 | 3       |
| structFullPointerMem                                 | 2554            | 115014 | 114233 | 228257 | 3       |
| structPart                                           | 1227            | 32007  | 31845  | 62949  | 3       |


| src/ExtArgMemVsCall_m.sol:ExtArgMemVsCall_m contract |                 |        |        |        |         |
|------------------------------------------------------|-----------------|--------|--------|--------|---------|
| Deployment Cost                                      | Deployment Size |        |        |        |         |
| 555187                                               | 2805            |        |        |        |         |
| Function Name                                        | min             | avg    | median | max    | # calls |
| basicAddr                                            | 1623            | 53739  | 53444  | 106151 | 3       |
| basicUint248                                         | 1570            | 51026  | 50745  | 100764 | 3       |
| basicUint256                                         | 1347            | 41556  | 41324  | 81999  | 3       |
| basicUint8                                           | 1478            | 47261  | 46999  | 93306  | 3       |
| structAddr                                           | 1873            | 71379  | 70929  | 141335 | 3       |
| structFull                                           | 2952            | 132257 | 131240 | 262580 | 3       |
| structFullPointer                                    | 2831            | 121749 | 120787 | 241631 | 3       |
| structFullPointerMem                                 | 2854            | 121772 | 120810 | 241654 | 3       |
| structPart                                           | 2269            | 86987  | 86205  | 172489 | 3       |

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment