Optimize DUP1, ISZERO and POP in ifStatement
- Dominant language
- C++
- Stars
- 25.7k
- Forks
- 6.2k
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 21
Description
## Description
```
function and(uint256 a, uint256 b) public returns(uint256) {
if (a > b || a > 100) {
return a - b;
} else {
return a+b;
}
}
```
The above function will generate the following opcode.
```
PUSH1 0x0 DUP2 DUP4 GT DUP1 PUSH2 0x70 JUMPI POP PUSH1 0x64 DUP4 GT JUMPDEST ISZERO PUSH2 0x88 JUMPI DUP2 DUP4
```
The corresponding assembly code is:
```
...
tag_7:
/* "condition.sol":270:277 uint256 */
0x00
/* "condition.sol":297:298 b */
dup2
/* "condition.sol":293:294 a */
dup4
/* "condition.sol":293:298 a > b */
gt
/* "condition.sol":293:309 a > b || a > 100 */
dup1
tag_11
jumpi
pop
/* "condition.sol":306:309 100 */
0x64
/* "condition.sol":302:303 a */
dup4
/* "condition.sol":302:309 a > 100 */
gt
/* "condition.sol":293:309 a > b || a > 100 */
tag_11:
/* "condition.sol":289:348 if (a > b || a > 100) {... */
iszero
tag_12
jumpi
/* "condition.sol":336:337 b */
dup2
/* "condition.sol":332:333 a */
dup4
/* "condition.sol":332:337 a - b */
…
```
In tag_7, there is a 'POP' just after 'JUMPI', which means the original value of the DUP is not used. Although this value is used in the other branch(tag_11), this can also be optimized.
## Optimization
Specifically, moving tag_11 after ```iszero tag_12 jumpi``` would achieve this optimization. The resulting assembly code is as follows:
```
...
tag_7:
/* "condition.sol":270:277 uint256 */
0x00
/* "condition.sol":297:298 b */
dup2
/* "condition.sol":293:294 a */
dup4
/* "condition.sol":293:298 a > b */
gt
/* "condition.sol":293:309 a > b || a > 100 */
tag_11
jumpi
/* "condition.sol":306:309 100 */
0x64
/* "condition.sol":302:303 a */
dup4
/* "condition.sol":302:309 a > 100 */
gt
/* "condition.sol":293:309 a > b || a > 100 */
iszero
tag_12
jumpi
tag_11:
/* "condition.sol":336:337 b */
dup2
/* "condition.sol":332:333 a */
dup4
/* "condition.sol":332:337 a - b */
…
```
In this way, for a>b, it will execute ```PUSH1 DUP2 DUP4 GT PUSH2 JUMPI JUMPDEST DUP2 DUP4``` instead of ```PUSH1 DUP2 DUP4 GT DUP1 PUSH2 JUMPI JUMPDEST ISZERO PUSH2 JUMPI DUP2 DUP4``` . For a<=b and a>100, it will execute ```PUSH1 DUP2 DUP4 GT PUSH2 JUMPI PUSH1 DUP4 GT ISZERO PUSH2 JUMPI JUMPDEST DUP2 DUP4``` instead of ```PUSH1 DUP2 DUP4 GT DUP1 PUSH2 JUMPI POP PUSH1 DUP4 GT JUMPDEST ISZERO PUSH2 JUMPI DUP2 DUP4``` .
https://github.com/ethereum/solidity/blob/b849b327781cb71478709b28c4d0d372492cbdc1/libsolidity/codegen/ExpressionCompiler.cpp#L2367-L2380
In this function, the endLabel is tag_11 in this example.
https://github.com/ethereum/solidity/blob/b849b327781cb71478709b28c4d0d372492cbdc1/libsolidity/codegen/ContractCompiler.cpp#L1153-L1172
Is it possible to move the Instruction::ISZERO and conditionalJump in this function to the front of the endLabel in the function appendAndOrOperatorCode?
Contributor guide
Research direction
Start with appendAndOrOperatorCode in libsolidity/codegen/ContractCompiler.cpp and the referenced logic in libsolidity/codegen/ExpressionCompiler.cpp. Compare the current and proposed instruction sequences for both branches, then validate that generated assembly removes the redundant DUP1, POP, and ISZERO operations without changing conditional behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, solidity
- Domain
- compilers, performance
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100