Open
Description
Description
When using a do-while control flow statement such as in the following code:
contract Sum {
function sum(uint256[] calldata nums) public pure returns (uint256 total) {
if (nums.length == 0) return 0;
uint256 i = 0;
do {
assembly ("memory-safe") {
total := add(calldataload(add(nums.offset, shl(5, i))), total)
i := add(i, 1)
}
} while (i < nums.length);
}
}
I expect solidity to generate the relatively straight-forward & maximally efficient assembly:
tag_doWhileBodyEntry:
<doWhileBody>
<whileCondition>
tag_doWhileBodyEntry
jumpi
This is the case when compiling with the legacy pipeline but using the IR pipeline gives you roughly the following structure:
0x01
tag_doWhileEntry:
iszero
tag_whileCondition
jumpi
tag_doWhileBody:
<doWhileBody>
0x00
tag_doWhileEntry
jump
tag_whileCondition:
<whileCondition>
tag_doWhileBody
jumpi
This structure, while correct is not efficient. Digging deeper the origin of this structure becomes more apparent when looking at the generated IR:
function fun_sum(var_nums_offset, var_nums_length) -> var_total {
var_total := 0
if iszero(var_nums_length) {
var_total := 0
leave
}
let var_i := 0
let _1 := 1
for { } 1 { } {
if iszero(_1) {
if iszero(lt(var_i, var_nums_length)) { break }
}
_1 := 0
var_total := add(calldataload(add(var_nums_offset, shl(5, var_i))), var_total)
var_i := add(var_i, 1)
}
}
Environment
- Compiler version: 0.8.27
- Target EVM version (as per compiler settings): cancun
- Framework/IDE (e.g. Truffle or Remix): foundry
- EVM execution environment / backend / blockchain client: viaIR
- Operating system: macOS
Steps to Reproduce
Compile the following contract with viaIR
contract Sum {
function sum(uint256[] calldata nums) public pure returns (uint256 total) {
if (nums.length == 0) return 0;
uint256 i = 0;
do {
assembly ("memory-safe") {
total := add(calldataload(add(nums.offset, shl(5, i))), total)
i := add(i, 1)
}
} while (i < nums.length);
}
}