Pathological compile time (and ~2 GB peak RSS without optimizer) under `--via-ir` on long `++x+x+x+x+…` arithmetic chain (`--via-ssa-cfg` also affected)
- Dominant language
- C++
- Stars
- 25.7k
- Forks
- 6.2k
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 21
Description
## Summary
A ~6.6 KB / 14-line Solidity file with a single function whose return expression is one ~3000-term `+` chain (mostly bare `x` operands, with 41 mid-chain `++x` pre-increments) causes `solc`'s via-IR pipeline to consume the full 12 s budget *without* the optimizer and ~9–10 s *with* the optimizer (also under `--experimental --via-ssa-cfg`). The legacy backend compiles the same file in 82 ms (no `--optimize`) / 302 ms (`--optimize`).
Without the Yul optimizer, peak RSS reaches **~1.87 GB** (vs ~32–37 MB on legacy and ~180 MB on the optimized via-IR / ssa-cfg paths). The two pipelines hit different hot paths:
- `--via-ir` (no optimizer): dominated by `StackCompressor::run` → `StackLayoutGenerator::reportStackTooDeep` → `findStackTooDeep`, with `Multiplicity::operator[]` at 39 % self.
- `--via-ir --optimize` and `--via-ssa-cfg --optimize`: dominated by `DataFlowAnalyzer::clearValues` (~48–50 % self), `CommonSubexpressionEliminator::run`, and `LiteralRematerialiser::run`.
Found via AFL fuzzing of `solc`. This is one of 6 corroborating instances in the same campaign sharing the same source shape (single line, ~3000-term `+` chain).
## Environment
- Compiler: `0.8.36-develop.2026.5.18+commit.090bc8ff` (solidity submodule at `090bc8ff2`, top of `develop` after PR #16709), Release build.
- OS: `Linux 7.0.5-arch1-1`, AMD Ryzen 9 3900.
## Steps to reproduce
Reproducer (6591 bytes, 14 lines):
[`source.sol`](https://gist.github.com/msooseth/786a7a65b47d3537cab20b647fd48093).
Shape:
```solidity
contract C layout at 2**256 - 2 {
uint public x;
function f(uint a) public returns (uint) {
++x+x+x+x+...+x; // ~3000-term '+' chain on one line, 41 mid-chain '++x'
++x;
--x;
x++;
x--;
return x;
}
}
```
Line 4 of the file contains the long chain: ~3000 `+` operators / `x` operands, plus 41 `+++` substrings (i.e. mid-chain `++x` pre-increments). Total line length: 6370 characters.
```bash
# Times measured with a 12 s wall-clock cap per invocation:
solc --bin --evm-version osaka --via-ir source.sol # >12 s (timeout), peak RSS 1868 MB
solc --bin --evm-version osaka --via-ir --optimize source.sol # 9277 ms, peak RSS 179 MB
solc --bin --evm-version osaka source.sol # 82 ms, peak RSS 32 MB
solc --bin --evm-version osaka --optimize source.sol # 302 ms, peak RSS 37 MB
solc --bin --evm-version osaka --via-ir --optimize --experimental --via-ssa-cfg source.sol # 9782 ms, peak RSS 179 MB
```
## Profile — `solc --bin --via-ir source.sol` (no `--optimize`, truncated at 12 s, ~1.87 GB peak)
| Incl. % | Self % | Symbol |
|---|---|---|
| 99.00 | 0.00 | `frontend::CompilerStack::generateIR` |
| 97.65 | 0.00 | `yul::ObjectOptimizer::optimize` |
| 97.46 | 0.00 | `yul::OptimiserSuite::run` |
| **97.31** | 0.00 | **`yul::StackCompressor::run`** |
| 97.09 | 0.00 | `yul::StackLayoutGenerator::reportStackTooDeep` |
| 97.08 | 0.01 | `yul::StackLayoutGenerator::processEntryPoint` |
| **84.52** | 1.63 | **`(anonymous namespace)::findStackTooDeep`** |
| **51.44** | **38.97** | **`yul::Multiplicity::operator[]`** |
| 11.63 | 0.22 | `StackLayoutGenerator::propagateStackThroughOperation` |
| 11.56 | 0.03 | `yul::Multiplicity::~Multiplicity` |
| 8.67 | 0.23 | `yul::Shuffler<…findStackTooDeep…>` |
| 8.58 | 8.58 | `yul::Multiplicity::at` |
| 4.82 | 4.82 | `std::_Rb_tree_insert_and_rebalance` |
| 4.02 | 2.23 | `yul::createStackLayout<…findStackTooDeep…>` |
## Profile — `solc --bin --via-ir --optimize source.sol` (9.3 s, ~179 MB peak)
| Incl. % | Self % | Symbol |
|---|---|---|
| 93.97 | 0.00 | `CompilerStack::generateIR` |
| 89.37 | 0.00 | `yul::YulStack::optimize` |
| 82.66 | 0.00 | `OptimiserSuite::runSequence` |
| 72.93 | 0.22 | `ASTModifier::operator()(yul::Block&)` |
| 71.61 | 0.00 | `DataFlowAnalyzer::operator()(yul::Block&)` |
| 56.18 | 0.14 | `DataFlowAnalyzer::handleAssignment` |
| 54.91 | 0.00 | `DataFlowAnalyzer::operator()(yul::FunctionDefinition&)` |
| 51.20 | 0.01 | `DataFlowAnalyzer::operator()(yul::Assignment&)` |
| **47.94** | **47.73** | **`yul::DataFlowAnalyzer::clearValues`** |
| **38.23** | 0.00 | **`yul::CommonSubexpressionEliminator::run`** |
| 29.45 | 0.00 | `yul::LiteralRematerialiser::run` |
| 16.49 | 0.00 | `DataFlowAnalyzer::operator()(yul::If&)` |
| 15.84 | 0.51 | `DataFlowAnalyzer::operator()(yul::VariableDeclaration&)` |
| 5.87 | 0.00 | `Object::toString` / `AsmPrinter::operator()` |
| 3.48 | 0.00 | `yul::YulStack::reparse` |
| 2.45 | 0.00 | `StackLayoutGenerator::reportStackTooDeep` (small at this point) |
The `--via-ssa-cfg --optimize` profile is essentially identical to `--via-ir --optimize` (`DataFlowAnalyzer::clearValues` ~49.7 % self, `CommonSubexpressionEliminator::run` ~38.6 %, `LiteralRematerialiser::run` ~29.9 %).
## Notes / possibly related
- #16745 — long `new C(N+1)()` constructor chain; same config fingerprint (`--via-ir` and `--via-ssa-cfg`) but different source shape (~250 contracts, ~16 KB) and a different hot path (`Object::summarizeStructure` / `AsmPrinter`). Filed separately.
- #16746 — long `((super).f)() | this.f() | …` diamond chain; `--via-ir` is fine *without* the optimizer there (1 s) but hangs *with* the optimizer; here `--via-ir` hangs even without `--optimize`, and the peak-RSS blowup (~1.87 GB) is unique to NEW-A.
- #14885 — `StackCompressor` infinite loop on assembly input; same component is hot here but the trigger is a long Solidity expression rather than handwritten assembly.
Contributor guide
Research direction
Start by reproducing the timings and memory use with source.sol under the listed --via-ir and --via-ssa-cfg commands. Profile CompilerStack::generateIR, then inspect StackCompressor::run and StackLayoutGenerator::reportStackTooDeep for the unoptimized case, and DataFlowAnalyzer::clearValues, CommonSubexpressionEliminator::run, and LiteralRematerialiser::run for optimized cases. Done means the long-chain reproducer no longer exhibits the reported timeout or memory blowup.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, solidity
- Domain
- compilers, performance
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100