do-while results in redundant branching instructions when compiling via IR
- Dominant language
- C++
- Stars
- 25.7k
- Forks
- 6.2k
- Avg merge
- 2d 19h
- Merged PRs (30d)
- 29
Description
## Description
When using a do-while control flow statement such as in the following code:
```solidity
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:
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:
0x00
tag_doWhileEntry
jump
tag_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:
```yul
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
```solidity
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);
}
}
```
Contributor guide
Research direction
Start by compiling the provided Sum contract with viaIR and compare its generated assembly and Yul with the legacy pipeline. Trace how the do-while loop is lowered in the IR pipeline; done means preserving behavior while eliminating the redundant branching instructions shown in the report.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, solidity
- Domain
- compilers, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100