# solx Beta: No stack too deep. No semantic changes.

By [ZKsync](https://paragraph.com/@zksync) · 2025-07-11

---

With the new release, **solx fixes solc’s notorious _stack-too-deep_ failure without altering contract semantics** — your contract behaves exactly as if compiled with solc, just cheaper and with fewer compilation failures. It now reliably eliminates _stack-too-deep_ and, since our first release in May, has tightened the byte-code-size and compile-time gap while further reducing runtime gas consumption.

With that, we believe solx is ready for mainnet deployment of non-critical, well-tested contracts. We’re still raising the security bar, so please don’t rush into gas-saving migrations just yet.

> solx ships two pipelines—default and `--via-ir`, the latter applying [rewrites that can change semantics](https://docs.soliditylang.org/en/latest/ir-breaking-changes.html). solx leaves both untouched: `solx` (default) matches solc’s default semantics, and `solx --via-ir` mirrors `solc --via-ir`. If something looks off, double-check your Foundry or other configuration to ensure the expected pipeline is in use before filing a bug.

Stack-Too-Deep? Fixed — But Mind Inline Assembly
------------------------------------------------

solx Beta eliminates the _stack-too-deep_ error that has topped every Solidity-developer pain-point survey for years.

Like the `solc --via-ir` pipeline, solx resolves the issue by **spilling** selected stack values into a region of memory that starts right after `0x80` and is sized _exactly_ to fit the spilled variables. The free-memory pointer (`0x40`) is then bumped so user-level allocations never collide with that region.

![](https://storage.googleapis.com/papyrus_images/d45a2e0cef8aceaf11918e3545a66b912466627f37ea013bd6d6ebe3e1c27a83.png)

The approach carries the _same inherent limits_ as `--via-ir`:

*   **The compiler can’t spill inside recursion** because it must know the total spill size at compile time.
    
*   [**Memory-unsafe inline assembly**](https://docs.soliditylang.org/en/latest/assembly.html#memory-safe-assembly) disables spilling for the entire contract.
    

Where **solx differs** is in _how_ it achieves the fix: solx plugs the spilling logic into the **default compiler pipeline**, so **no** [**semantic changes**](https://docs.soliditylang.org/en/latest/ir-breaking-changes.html) are introduced. Your contract behaves exactly as it would under the legacy pipeline—just without the stack error.

> **Heads-up on inline assembly**
> 
> *   Whenever possible, **prefer high-level Solidity**; solx is steadily eroding the old performance edge of hand-rolled assembly.
>     
> *   If you do need assembly, be honest: mark _only_ truly safe blocks with `assembly ("memory-safe") // preferred, future-proof` or `/// @solidity memory-safe-assembly // legacy form, kept only for old compilers`. Mis-labelling unsafe code lets both your logic _and_ the spill logic write to the same slots, silently corrupting data.
>     
> *   In doubtful cases, skip the annotation and let the compiler disable spilling rather than risk undefined behavior.
>     

We’ll publish an in-depth blog post on the algorithm later. For now remember: **enjoy the automatic fix—just keep your assembly minimal, accurate, and genuinely memory-safe.**

Runtime gas — widening lead
===========================

A proper benchmark that weights each function by real-world call frequency and uses representative inputs is still on the to-do list. For now we rely on raw `forge test --gas-report` data. Across **2 057 cases** drawn from popular projects, **solx consumes less gas than solc in 1 888 of them—about 92 %**. The advantage ranges from small fractions of percent to double-digit cuts, depending on the code path. You can browse the full snapshot at [https://matter-labs.github.io/solx/dashboard/](https://matter-labs.github.io/solx/dashboard/). Pick any project from the drop-down to see a visual, test-by-test comparison of `forge test --gas-report` for **solc** vs. **solx**.

**Recent compiler changes**

*   **EVM constant folding** ([PR #835](https://github.com/matter-labs/era-compiler-llvm/pull/835))
    
    `add`, `mulmod`, `exp`, `signext`, and `byte` now fold to literals when inputs are known at compile time, removing whole instruction sequences in the legacy pipeline.
    
*   **Sharper branch lowering** ([PR #817](https://github.com/matter-labs/era-compiler-llvm/pull/817))
    
    solx now emits fewer instructions to set a `JUMPI` condition.
    

These wins were partly offset when we restored explicit local function calls and let LLVM decide what to inline ([PR #1048](https://github.com/matter-labs/era-solidity/pull/1048)). The refactor produced smaller byte-code and faster builds, but runtime gas ticked up slightly because LLVM’s default inliner is too cautious for the EVM cost model. We’re retuning those heuristics and expect to regain the lost gas efficiency.

**Path forward**

LLVM underpins both the default and `--via-ir` modes, so every EVM-specific optimization that lives only in `--via-ir`can migrate into the main LLVM path. Constant folding was the first step; more moves are underway, so the _default_ pipeline will ultimately include **all** LLVM optimizations **and** every `--via-ir` improvement.

ERC-20 Gas Savings — Potential Network Impact
=============================================

ERC-20 tokens (USDT, USDC, DAI, …) dominate Ethereum traffic, so we measured gas on the lean, audited **Solady** implementation. The same contract was compiled with **solc 0.8.30** and with the current **solx beta**, then run through Foundry’s `forge test --gas-report` **200 times each** (Solady’s harness injects randomness, so single runs are noisy). Average gas per call:

![](https://storage.googleapis.com/papyrus_images/d86229d943af550fea6ea9d0e7b6398ed93616391f4f4fd20c10de483095f533.png)

_(Negative Δ means solx is cheaper.)_

**Why Solady?**

It mirrors real-world ERC-20 patterns while staying easy to benchmark. Solady ships its own gas-benchmarking harness, making large-sample runs easy. OpenZeppelin’s implementation doesn’t include comparable tooling out of the box, so Solady was the quicker path; quick hand-written trials on OpenZeppelin actually showed larger gas cuts, making the table above a conservative lower bound on real-world savings.

### Aggregating the savings — how much “-255 gas per transfer” is in dollars

Ethereum sees about 1.5 million main-net transactions a day, and roughly 60 % of them are simple ERC-20 `transfers` — close to 900 000 moves of tokens every 24 hours. Shaving 255 gas off each call with solx (-0.56 % versus solc) removes around 230 million gas daily. In the quieter 2025 market, where gas has hovered near 2.6 Gwei, that’s roughly 0.60 ETH saved every day — about $1 600, or $0.6 million a year at today’s $2 800/ETH. During 2024’s busier spell, with averages nearer 15 Gwei and frequent spikes, the same 230 million gas translated to about $10 000 per day, or $3–4 million over the year; slicing 0.56 % off the $2.48 billion users paid in 2024 fees would have left roughly $14 million in their wallets. Even if only half of new tokens adopted solx, annual network-wide savings would still run well into seven figures — without touching a single line of Solidity.

> Please don’t rush critical contracts to mainnet.
> 
> solx already cuts gas and removes _stack-too-deep_, but we’re still hardening security. Treat these numbers as a preview of what full-scale adoption could unlock; test thoroughly before deploying anything mission-critical.

Code size — closing the gap
===========================

When we first spot-checked solx binaries earlier this year they were a little over **20 % larger** than solc’s. In the current beta that overhead has fallen to **about 6 %**. The code-size dashboard ([https://matter-labs.github.io/solx/codesize/0.1.0/](https://matter-labs.github.io/solx/codesize/0.1.0/)) shows the details contract by contract; at this point only two default-pipeline builds —Aave’s _StataTokenV2_ and Beefy’s _StrategyAuraGyroMainnet_ — still exceed Ethereum’s 24 576-byte limit.

**Recent compiler changes**

*   **Inlining shifted to LLVM.** Already explained in the previous section.
    
*   **Large-constant unfolding** ([PR #833](https://github.com/matter-labs/era-compiler-llvm/pull/833))**.** The backend now emits shorter sequences for small negatives and other literals when the gas trade-off is minimal, avoiding full-width `PUSH` instructions.
    

The **default pipeline remains gas-first** — it adopts size tweaks only when they cost little or no runtime gas. For the few outlier contracts that still exceed the cap, we plan to offer an **aggressive size-optimization fallback** that prioritizes byte-code shrinkage over gas efficiency.

Compilation time — catching up
==============================

Right now the default solx pipeline still lags solc’s by a few-fold on wall-clock time, although it is already noticeably faster than `solc --via-ir --optimize`. We don’t have a public dashboard for this metric yet, but every pull request in the solx repo now gets a compile-time report from CI, so progress is tracked continuously.

Looking ahead, our **MLIR pipeline** is showing encouraging numbers: with LLVM optimizations enabled it compiles only a few‐tens-of-percent slower than solc **with optimizations off**. Once that pipeline is production-ready, we expect compilation speed to tighten further while keeping all current gas and size wins.

Why Trust a “New” Compiler? Because It Isn’t
============================================

Our May-2025 pre-alpha already cleared the Solidity compiler test-suite and real-world projects such as Uniswap V2 and Solmate. A detailed write-up (“[Solx: The New Solidity Compiler — Why It Matters to You](https://zksync.mirror.xyz/aCTbO6aDQdrPbUOFR9YMCt24p7-z5KZMMw_GqFz4tpE)”) covers those results.

With the _stack-too-deep_ issue resolved, we have widened our test pool. Every project below builds cleanly, and all unit tests pass on solx beta:

![](https://storage.googleapis.com/papyrus_images/33d93de00848842752ab591ebb86cc99b27ec785eb05575b67f52a61963ad664.png)

† _Beefy note_ Inline assembly in Beefy lacks the `memory-safe-assembly` tag; adding it turns the suite green.

Totals: ≈ 2 000 contracts, ≈ 6 400 unit tests — **all green**.

That is far from all the testing we have run. While solx may look new, its core is not: the same LLVM fork has powered **zksolc** (Solidity) and **zkvyper** (Vyper) on ZKsync since 2021. The only delta is about 9 000 LoC in the EVM code generator. Across contracts deployed to ZKsync we have seen **zero** post-deployment miscompilations; the lone genuine LLVM issue—the [AAVE miscompilation](https://github.com/matter-labs/era-compiler-solidity/security/advisories/GHSA-fpx7-8vc6-frjj)—was caught in testing and patched before release, plus [five additional miscompilations](https://github.com/matter-labs/era-compiler-solidity/security/advisories) introduced in our own ZKsync VM (EraVM) specific code; each was discovered during testing or bug-bounty report and is now fixed.

Today we believe solx is comparable at safety to `solc --via-ir --optimize`. The legacy solc pipeline — though ultra-conservative — delivers far fewer optimizations and often forces developers to hand-tune code. As compiler engineers we don’t think people at scale can do optimizations more securely than a thoroughly tested compiler. Nevertheless, the 401 open LLVM “miscompilation” tickets ([label = miscompilation, 11 Jul 2025](https://github.com/llvm/llvm-project/issues?q=is%3Aissue%20state%3Aopen%20label%3Amiscompilation)) remain a concern, and we do not assume LLVM is _secure enough_ by default. Our first triage suggests that only 42 of them could realistically surface in solx:

![](https://storage.googleapis.com/papyrus_images/d7d52b3050841f82fe67ead859cd0bb6f182a122c6b8d57eb9d9366fe9bd22c0.png)

Before tackling those 42, we are defining a permanent triage and tracking process so we never repeat this snapshot exercise. Reported LLVM miscompilations will be kept out of our next releases.

> Recommendations
> 
> *   Maintain **100 % test coverage** in your own codebases; well-tested code is far less likely to conceal an undetected optimization bug.
>     
> *   Feel free to **battle-test solx on non-critical contracts today**. For mission-critical deployments, wait until we raise our security bar further.
>     

Conclusion — has solx _really_ fixed stack-too-deep?
====================================================

Two months after our first public release, the beta compiles every contract that previously tripped over _stack-too-deep_—the error that has topped Solidity-developer pain charts for years.

So is the problem gone for good? **Partly.**

_Stack-too-deep_ is eliminated in **optimizing builds**, meaning you can point solx at production code today and ship byte-code that solc’s default pipeline could never produce. What remains is a smooth **development-time** experience: lightning-fast, non-optimizing builds with source maps and coverage. Delivering that requires a “debug” pipeline, but—unlike `solc --via-ir`  detour—solx doesn’t need a separate code generator. Turning off most LLVM passes is enough, because the spill strategy already lives in the backend.

That puts a developer-friendly, non-optimizing pipeline well within reach. Once it lands, teams will compile fast, debug smoothly, and then flip the optimizer on for deployment — all without ever seeing _stack-too-deep_ again.

This is our best idea for what to tackle next, but if you think we’re wrong — or have any feedback at all — please reach out on [Telegram](https://t.me/solx_devs) or email. Above all else we want solx to meet _your_ needs. And if you haven’t tried it yet, head over to [**solx.zksync.io**](http://solx.zksync.io) for an easy start.

---

*Originally published on [ZKsync](https://paragraph.com/@zksync/solx-beta-no-stack-too-deep-no-semantic-changes)*
