argotorg / argotorg/solidity

Poor optimization of `call` and `staticcall` with `--via-ir`

Open
#15,295 5 comments 0 reactions 0 assignees View on GitHub
low effort low impact optimizer waiting for more input
Dominant language
C++
Stars
25.7k
Forks
6.2k
Avg merge
2d 19h
Merged PRs (30d)
29

Description

## Description

I came across that calls are rather poorly optimized with `--via-ir`, although everything else is much better with this flag.

## Environment

- Compiler version: 0.8.26+commit.8a97fa7a.Linux.g++
- Target EVM version (as per compiler settings): cancun
- Framework/IDE (e.g. Truffle or Remix): none
- EVM execution environment / backend / blockchain client: none
- Operating system: Linux

## Steps to Reproduce

`MyContract.sol`
```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.26;

contract MyContract {
function sendViaCall(address payable to) public payable {
(bool sent,) = to.call{value: msg.value}("");
require(sent);
}
}
```

```
$ solc --optimize --via-ir --no-cbor-metadata --bin-runtime MyContract.sol

======= MyContract.sol:MyContract =======
Binary of the runtime part:
608060405260043610156010575f80fd5b5f3560e01c63830c29ae146022575f80fd5b60203660031901126097576004356001600160a01b038116908190036097575f8080809334905af13d1560af573d67ffffffffffffffff8111609b5760405190601f8101601f19908116603f0116820167ffffffffffffffff811183821017609b5760405281525f60203d92013e5b15609757005b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b609156
```

Then I use decompiler like https://app.dedaub.com to look at the Yul pseudo code. I don't understand why check is made to ensure that the free memory pointer does not exceed `0xffffffffffffffff`. This makes no sense since `retOffset` and
`retSize` have been set to `0`.
```yul
function func_sendViaCall() {
let _3 := slt(add(not(0x3), calldatasize()), 0x20)
require(_3)
let _4 := calldataload(0x4)
let _5 := and(_4, sub(shl(0xa0, 0x1), 0x1))
let _6 := sub(_4, _5)
require(_6)
let _7 := call(gas(), _5, callvalue(), 0x0, 0x0, 0x0, 0x0)
let _8 := iszero(returndatasize())
if not(_8){
let _9 := returndatasize()
let _10 := gt(_9, 0xffffffffffffffff)
if _10{
mstore(0x0, shl(0xe0, 0x4e487b71))
mstore(0x4, 0x41)
revert(0x0, 0x24)
}
if not(_10){
let _11 := mload(0x40)
let _12 := not(0x1f)
let _13 := add(_11, and(add(0x3f, and(_12, add(_9, 0x1f))), _12))
let _14 := or(lt(_13, _11), gt(_13, 0xffffffffffffffff))
if _14{
mstore(0x0, shl(0xe0, 0x4e487b71))
mstore(0x4, 0x41)
revert(0x0, 0x24)
}
if not(_14){
mstore(0x40, _13)
mstore(_11, _9)
returndatacopy(add(_11, 0x20), 0x0, returndatasize())
}
}
}
let _15 := iszero(_7)
require(_15)
stop()
}
```

Contributor guide

Open the contributing guide

Research direction

Start by compiling MyContract.sol with solc 0.8.26 using --optimize --via-ir --no-cbor-metadata --bin-runtime, then inspect the generated Yul around call and returndatacopy. Trace the via-IR compiler entry points responsible for lowering call and staticcall and compare the bounds checks with the zero return-data case. Done means the redundant checks are removed where valid and regression coverage confirms the optimized output.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, solidity
Domain
blockchain, compilers, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.