perf(codegen): output-gating optimizations#502
Conversation
hedgar2017
commented
Jul 5, 2026
- perf(codegen): clone the size-fallback module only when reachable
- perf(codegen): emit assembly text only when a consumer wants it
- perf(codegen): skip the debug-location FFI call when debug info is off
- perf(codegen): verify LLVM IR only in debug builds
5ed3f75 to
951d381
Compare
|
📊 solx Tester Report ➡️ Download |
There was a problem hiding this comment.
Pull request overview
This PR focuses on reducing unnecessary work in the EVM codegen pipeline by gating expensive operations behind actual demand (debug info, assembly output, size-fallback reachability, and build mode), improving compiler throughput and memory use in common paths.
Changes:
- Add a helper to avoid setting LLVM debug locations (and the associated FFI) when debug info is disabled.
- Verify LLVM IR only in debug builds (
debug_assertions) instead of unconditionally. - Avoid cloning the “size-fallback” module unless the optimizer settings make the fallback path reachable.
- Emit LLVM assembly only when explicitly requested by a consumer.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| solx-codegen-evm/src/context/mod.rs | Adds set_instruction_debug_location helper and routes debug-location setting through it to skip work when debug info is off. |
| solx-codegen-evm/src/codegen/context/mod.rs | Gates IR verification to debug builds and refines when to clone modules / emit assembly based on actual output needs. |
|
📊 Hardhat Projects Report ➡️ Download |
|
📊 Foundry Projects Report ➡️ Download |
The unoptimized module was cloned before every optimization run to seed a size fallback that only triggers under cycles settings with the fallback enabled. Compute that condition once and clone only then, so default builds skip the full-module copy and the verification inkwell embeds in it.
The assembly buffer was produced whenever any output config existed, so a run dumping only other IRs paid a second ISel pass and a module clone that dump_assembly then discarded. Gate production on the same output_assembly flag the dump already checks, alongside the JSON selector.
Every builder wrapper set the instruction debug location unconditionally, crossing FFI as a null-location set per emitted instruction when debug info is disabled. Route the call sites through one trait method that sets the location only when debug info is present.
The module verifier ran unconditionally before and after optimization, a development-time structural check that walks the whole module. Gate both calls on debug_assertions so debug and CI builds keep the check while the release binary skips it.
951d381 to
0f71487
Compare
Compile-time benchmark (
|
| binary | mean ± σ | min … max | vs baseline |
|---|---|---|---|
| pr | 4.888 s ± 0.053 s | 4.856 s … 4.949 s | 1.002× |
| main | 4.880 s ± 0.061 s | 4.828 s … 4.946 s | 1.000× |
| release | 6.346 s ± 0.058 s | 6.284 s … 6.400 s | 1.301× |
openzeppelin-contracts-0.34--solx-legacy-dwarf
| binary | mean ± σ | min … max | vs baseline |
|---|---|---|---|
| pr | 52.370 s ± 0.019 s | 52.358 s … 52.393 s | 0.981× |
| main | 53.396 s ± 0.183 s | 53.187 s … 53.527 s | 1.000× |
| release | 359.359 s ± 1.758 s | 357.762 s … 361.243 s | 6.730× |
uniswap-v4-core-solx--solx-legacy-dwarf
| binary | mean ± σ | min … max | vs baseline |
|---|---|---|---|
| pr | 22.255 s ± 0.170 s | 22.115 s … 22.445 s | 0.973× |
| main | 22.869 s ± 0.187 s | 22.690 s … 23.062 s | 1.000× |
| release | 101.858 s ± 0.645 s | 101.240 s … 102.527 s | 4.454× |
|
Sorry for chiming in so late, but verifying LLVM is always preferable as it can catch bugs, so I don't think it is good to disable it in production. |
Alright, let's leave this one for discussion next week! |