Skip to content

fix(compiler): harden EVM stack-shape analysis before JIT admission#560

Open
abmcar wants to merge 20 commits into
DTVMStack:mainfrom
abmcar:fix/evm-lift-dynamic-boundary-soundness
Open

fix(compiler): harden EVM stack-shape analysis before JIT admission#560
abmcar wants to merge 20 commits into
DTVMStack:mainfrom
abmcar:fix/evm-lift-dynamic-boundary-soundness

Conversation

@abmcar

@abmcar abmcar commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • harden EVM stack-shape analysis at dynamic-dispatch and unresolved-depth boundaries;
  • preserve interpreter fallback when the JIT cannot materialize a complete runtime stack shape;
  • keep MIR JUMPDEST predecessors aligned with analyzer metadata by excluding dead post-RETURN and post-REVERT fallthroughs;
  • make JIT compilation failure a sticky, atomic interpreter fallback decision;
  • replace repeated hidden-boundary rescans with a transition-driven work queue.

Root cause and behavior

A block reached through runtime jump dispatch may carry caller-frame slots that are absent from its statically inferred entry depth. Lifting that block can materialize a stack that is too shallow and diverge from interpreter execution. This change prevents runtime-dispatch targets and their affected static closure from using an unproven lifted entry state. It also retains interpreter admission for reachable non-lifted blocks that consume unresolved caller-frame slots.

The MIR builder previously inferred linear fallthrough into JUMPDEST from the current insertion block. A terminating opcode can leave an empty post-termination block with no live predecessor, but the builder still connected it to the next destination. MIR then had one more predecessor than the analyzer's phi model. The visitor now passes fallthrough liveness explicitly, and the builder adds the edge only when it is live.

Frontend consistency errors already reach the shared EVM compile boundary. If compilation fails, the module now records an atomic interpreter fallback so eager and profile-guided background paths do not retry the same failed compilation.

Validation

Current post-review source (629bb22)

Check Result
evmJitFrontendTests, Lift OFF 46/46
evmJitFrontendTests, Lift ON 46/46
evmDifferentialTests, Lift OFF 54/54
evmDifferentialTests, Lift ON 54/54
evmone multipass unit tests 223/223
EEST state tests 2723/2723
EVM assembly tests 209/209
default-build CTest targets 12/12

A 252-bytecode mainnet-v2 analyzer scan matched the pre-hardening PR head on every non-timing field, including block count, liftability, dynamic-entry taint, and interpreter admission.

Frozen replay evidence

The earlier frozen Lift OFF and Lift ON source snapshots passed 300/300 mainnet-replay-v2 Cancun fixtures and the continuous replay of blocks 21800000..21800099: 100 blocks, 14,150 transactions, 1,825,295,016 gas, and matching final block hash 0xaa5e1274f0125381d5a07cd4521b71dd808dd581f46587f8eb82083b1052a812.

Those replay binaries predate the post-review work-queue and sticky-fallback hardening. Their source hashes and evidence boundaries are recorded in docs/changes/2026-07-02-evm-lift-dynamic-boundary-soundness/README.md.

Performance and conservative scope

On the same synthetic 20,003-byte analyzer probe with 4,000 chained hidden-prefix blocks, the work-queue implementation reduced a representative Release run from 462,992 us to 3,908 us while preserving the analyzer result. This isolates the repeated-rescan mechanism and is not an end-to-end execution benchmark.

Stack SSA lifting remains disabled by default. In the frozen 300-fixture load trace, the PR introduced no JIT admission loss relative to the exact-base predicate. Among the seven modules admitted by both versions, load-weighted lifted-block coverage changed from 72 to 63, a 12.5% reduction in optimization coverage. This is not a measured 12.5% execution slowdown.

The full correctness evidence, limitations, and follow-up options are in the committed change document.

abmcar and others added 8 commits July 2, 2026 21:32
…fix offset

A materializing exit of a lifted block spilled its logical stack at byte
offset Hidden*32 and set StackSize = Hidden + size. But the lifted logical
stack already spans the full absolute entry depth (FullEntryStateDepth =
ResolvedEntryStackDepth): the lifter builds entry operands for every absolute
slot including the hidden-prefix slots, and handleBeginBlock restores all of
them onto the logical stack. Draining that stack at exit therefore yields the
full absolute stack, so spilling it at Hidden*32 wrote the stack Hidden slots
too high and inflated the recorded depth by Hidden. Top-relative contents
stayed consistent, but the inflated StackSize caused spurious stack-overflow
traps near the 1024 limit and suppressed underflow traps in later blocks.

Under the full-absolute-depth entry-state model the correct spill prefix is 0
(the stack bottom). Fix the lifted branch of finalizeBlockExit to spill at
prefix 0 and remove the now-dead CurrentBlockHiddenLiveInPrefixDepth tracking.
The non-lifted branch is unchanged, so lift-OFF codegen is byte-identical.

Add a differential regression test: a lifted block with Hidden=1 takes a
materializing constant JUMP to a non-lifted block that pops two slots while
only the prefix slot is live. The interpreter underflows; before the fix the
JIT's inflated StackSize let the underflow check pass and the block ran to
STOP, diverging in status. Also complete the MockOperand test double with
isEmpty() so evmJitFrontendTests builds under ZEN_ENABLE_EVM_STACK_SSA_LIFT.

This is Stage 0 of the SSA-lift dynamic-boundary soundness change; the design
doc is included.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwhdGjJ1rKgZXz9sUQJZys
…xits of lifted blocks

A dynamic dispatch can land on any JUMPDEST via the jump table, so the
runtime stack must be valid at every dynamic (non-constant) exit of a
lifted block. Previously a dynamic JUMP skipped materialization whenever
the compatible-dynamic target set was non-empty, and a dynamic-dest JUMPI
decided materialization only from fallthrough liftability, ignoring the
taken edge. Both left non-lifted and out-of-model dynamic targets reading
a stale runtime stack.

Force NeedsRuntimeMaterialization for any dynamic dest at both lifted
exit-decision points (handleJumpOpcode, handleJumpIOpcode partial path).
SSA entry-state assignment is kept and stays additive: lifted compatible
targets retain their zero-reload SSA entry while the materialized memory
image backs non-lifted and out-of-model targets. Statically wired
lifted->lifted skip sites (constant JUMP/JUMPI, fallthrough, already-begun
entry) are unchanged. Lift-OFF codegen is unaffected (changes are inside
CurrentBlockLifted branches).

This is Stage 1 of the SSA-lift dynamic-boundary soundness change;
lifted dynamic targets are closed separately by the Stage 2 liftability
rules.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwhdGjJ1rKgZXz9sUQJZys
Make the SSA stack-lift dynamic boundary sound so lifted-block codegen
matches the interpreter on modules with dynamic jumps or hidden live-in
prefixes.

- Exclude every jump-table-enterable JUMPDEST from lifting: whenever a
  module has any dynamic jump the runtime dispatch can land on any
  JUMPDEST, and lifted SSA entry state does not model that runtime stack.
  The exclusion equals codegen's indirect switch-emission set.
- Assert at the visitor that a non-constant JUMP/JUMPI dest (which drives
  codegen's indirect dispatch) implies the analyzer marked the block
  dynamic; a mismatch fails the compile loudly instead of miscompiling.
- Do not lift a block whose static predecessor has an unresolved exit
  depth (its runtime-entry assignment would be silently skipped).
- Do not lift a hidden-live-in-prefix block that reconciles its prefix
  with the runtime stack at a lifted/non-lifted materialization boundary
  or a dynamic exit; iterate to a fixpoint. Pure-SSA-island hidden blocks
  keep lifting.
- Add debug-only structural invariants: a lifted block is never an
  indirect-dispatch landing; each static predecessor's exit depth equals
  the lifted block's full entry-state depth; the logical entry state is
  fully defined at lifted block begin.

Validated interpreter-vs-multipass equality under forced JIT on the
Sourcify corpus (56/56), 8 evmone kernels, and EEST fork_Cancun
statetests (2723/2723) in both lift-ON and lift-OFF builds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwhdGjJ1rKgZXz9sUQJZys
…uards

Remove hasUnresolvedCompatibleDynamicReturnTrampoline and
hasUnresolvedNonLiftedDeepEntryRisk from the ShouldFallbackToInterp
computation, plus the now-unused analyzer helpers they relied on. Add the
deep-entry differential reproducer as a unit test.

State at this commit: lift-OFF is fully green (2723/2723 EEST fork_Cancun).
lift-ON exposes 4 residual wallet miscompiles (logs_hash + state_root
divergence): stWalletTest.dayLimitSetDailyLimit, dayLimitSetDailyLimitNoData,
multiOwnedChangeOwner, walletChangeOwnerRemovePendingTransaction. These
modules previously fell back to the interpreter behind the guards, masking a
real lift-ON dynamic-boundary unsoundness that the next commit addresses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwhdGjJ1rKgZXz9sUQJZys
…their pops

An internal-function return continuation is reached only by a dynamic return
JUMP, so propagateEntryDepths (which flows depths along static edges from the
function entry) never assigns its depth; the dynamic-jump region uniform-entry
heuristic supplies one that under-counts the caller's frame. The shortfall
propagates into static successors, which can then hold
ResolvedEntryStackDepth + MinStackHeight < 0 -- popping below their own resolved
bottom. Lifting such a block sizes its logical entry state to the too-shallow
depth and validateLiftedBlockStackBounds emits a spurious EVMStackUnderflow,
reverting the call. This miscompiled four fork_Cancun stWalletTest cases under
ZEN_ENABLE_EVM_STACK_SSA_LIFT once the whole-module fallback guards were removed
(empty logs_hash, wrong state_root): dayLimitSetDailyLimit,
dayLimitSetDailyLimitNoData, multiOwnedChangeOwner,
walletChangeOwnerRemovePendingTransaction.

Add a finalizeLiftability clause that refuses to lift any block with
ResolvedEntryStackDepth + MinStackHeight < 0. The sum is a local proof the
resolved depth is under-resolved (or the block genuinely underflows); either way
the lifted entry state cannot cover the block's pops. The non-lifted path reads
the real runtime stack via its -MinStackHeight check, so unlifting is
behaviorally neutral -- a genuine underflow still traps.

Update DynamicJumpForcesReachableJumpDestsToFallback: its dynamic-jump source
block used CALLDATALOAD at entry depth 0 (a genuine underflow) that the new
clause correctly unlifts; give it the CALLDATALOAD offset so the test still
exercises reachable-JUMPDEST fallback. Add UnderResolvedEntryDepthBlockIsNot-
Lifted guarding the new clause.

Gates (lift-ON and lift-OFF): full EEST fork_Cancun 2723/2723,
evmDifferentialTests 46/46, evmJitFrontendTests 28/28, evmone-unittests
223/223, evm_asm 200/200; corpus interp-vs-multipass differential (28 contracts
x2 calldata incl 0x9649BF5d and 0x6a877e5A, 8 benchmark kernels) zero
divergence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwhdGjJ1rKgZXz9sUQJZys
Main-suite geomean -65.1% (lift-OFF, byte-identical flags, 12 reps,
median/IQR); 6/7 recovered kernels improve, weierstrudel regresses +19-22%
(real, flagged for profiling follow-up); micro suite null; control clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwhdGjJ1rKgZXz9sUQJZys
Close three review findings on the EVM multipass lift-boundary path.

1. Under-resolved-depth revocation had an `== 0` hole. A block whose
   dynamic-jump region heuristic depth was under-counted such that
   ResolvedEntryStackDepth + MinStackHeight == 0 still lifted with a
   too-shallow absolute depth; its materializing exit spill sets StackSize
   absolutely and truncates the caller's deeper frame slots, diverging from
   the interpreter. Root fix: taint-track heuristic-derived depths.
   EntryDepthFromRegionHeuristic is set where the region uniform-entry
   machinery seeds a depth and inherited along every static propagation edge;
   finalizeLiftability never lifts a tainted block. Only depths propagated
   purely from the function entry (seed depth 0) stay trusted. The prior
   `< 0` rule remains as a subsumed sanity net.

2. assertDynamicJumpConsistency used ZEN_ASSERT (abort() in release), so an
   analyzer/visitor constant-tracking mismatch would kill the loading
   process. It now throws a Compilation-phase Error
   (EVMDynamicJumpConsistencyFailed); the EVM compile path already catches
   std::exception and degrades to interpreter fallback with a logged error.
   The check stays active in every build.

3. The hidden-prefix fixpoint keyed its materializing-exit test on
   HasDynamicJump, missing a JUMPI whose constant destination is not a valid
   JUMPDEST (HasDynamicJump stays unset, yet codegen forces materialization).
   The fixpoint now uses blockExitMaterializesRuntimeStack, true for
   HasDynamicJump || (HasConditionalJump && !HasConstantJump), matching
   codegen's materialization truth independent of successor liftability.

Also simplify the JUMP exit materialization predicate to !HasKnownLiftedSucc.

Add an analyzer-level test asserting a region-heuristic-resolved continuation
and its static successor are both tainted and unlifted (red-verified: the
successor lifts without the taint rule). Update the change-doc Findings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwhdGjJ1rKgZXz9sUQJZys
Copilot AI review requested due to automatic review settings July 3, 2026 03:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Removes structural multipass whole-module interpreter fallbacks by making SSA stack-lift “dynamic boundary” handling sound (spill depth correctness, always-materialize rules, and stricter liftability gating), so real-world Solidity contracts route to the multipass JIT instead of the interpreter.

Changes:

  • Fix lifted-block materializing-exit spilling to spill from stack bottom (avoids hidden-prefix depth inflation) and always materialize the runtime stack for dynamic jump exits.
  • Add liftability safety rules (region-heuristic depth tainting, JUMPDEST/dynamic-target exclusions, unresolved-predecessor depth gating) plus debug invariants to assert analyzer/codegen consistency.
  • Remove the two structural ShouldFallbackToInterp guards so only size/complexity thresholds remain, and add regression tests + change doc.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/tests/evm_jit_frontend_tests.cpp Adds analyzer unit tests for new liftability rules (under-resolved entry depth, region-heuristic taint propagation).
src/tests/evm_differential_tests.cpp Adds differential regressions for hidden-prefix spill bug and deep-entry “internal-call continuation” JIT/no-fallback behavior.
src/runtime/evm_module.cpp Drops structural whole-module fallback guards; keeps suitability size/complexity threshold only.
src/compiler/evm_frontend/evm_analyzer.h Introduces heuristic-depth taint tracking, new lift-gating rules, hidden-prefix fixpoint boundary logic, and debug structural invariants.
src/common/errors.def Adds a compilation error code for analyzer/codegen dynamic-jump dispatch inconsistency.
src/action/evm_bytecode_visitor.h Fixes spill prefix handling, forces runtime materialization at dynamic exits, and adds a hard consistency guard that throws into existing compile-failure fallback.
docs/changes/2026-07-02-evm-lift-dynamic-boundary-soundness/README.md Full design/evidence/change proposal documenting the hazard and staged fixes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

⚡ Performance Regression Check Results

✅ Performance Check Passed (interpreter)

Performance Benchmark Results (threshold: 25%)

Benchmark Baseline (us) Current (us) Change Status
total/main/blake2b_huff/8415nulls 2.54 2.42 -4.6% PASS
total/main/blake2b_huff/empty 0.04 0.04 -0.3% PASS
total/main/blake2b_shifts/8415nulls 13.89 13.47 -3.0% PASS
total/main/sha1_divs/5311 8.20 8.33 +1.6% PASS
total/main/sha1_divs/empty 0.10 0.10 -3.4% PASS
total/main/sha1_shifts/5311 5.36 5.32 -0.6% PASS
total/main/sha1_shifts/empty 0.04 0.04 -0.4% PASS
total/main/snailtracer/benchmark 92.06 86.94 -5.5% PASS
total/main/structarray_alloc/nfts_rank 1.19 1.11 -6.3% PASS
total/main/swap_math/insufficient_liquidity 0.00 0.00 +0.4% PASS
total/main/swap_math/received 0.01 0.01 +0.8% PASS
total/main/swap_math/spent 0.01 0.01 -7.2% PASS
total/main/weierstrudel/1 0.30 0.32 +9.1% PASS
total/main/weierstrudel/15 3.57 3.49 -2.1% PASS
total/micro/JUMPDEST_n0/empty 2.83 2.81 -0.5% PASS
total/micro/jump_around/empty 0.11 0.11 +1.7% PASS
total/micro/loop_with_many_jumpdests/empty 69.77 69.90 +0.2% PASS
total/micro/memory_grow_mload/by1 0.17 0.17 +3.6% PASS
total/micro/memory_grow_mload/by16 0.20 0.19 -3.5% PASS
total/micro/memory_grow_mload/by32 0.13 0.12 -11.5% PASS
total/micro/memory_grow_mload/nogrow 0.09 0.08 -8.1% PASS
total/micro/memory_grow_mstore/by1 0.19 0.19 +0.0% PASS
total/micro/memory_grow_mstore/by16 0.12 0.11 -10.4% PASS
total/micro/memory_grow_mstore/by32 0.24 0.23 -2.1% PASS
total/micro/memory_grow_mstore/nogrow 0.19 0.18 -5.1% PASS
total/micro/signextend/one 0.26 0.26 +0.6% PASS
total/micro/signextend/zero 0.47 0.50 +4.9% PASS
total/synth/ADD/b0 2.02 2.03 +0.5% PASS
total/synth/ADD/b1 2.79 2.73 -2.0% PASS
total/synth/ADDRESS/a0 6.13 6.28 +2.5% PASS
total/synth/ADDRESS/a1 5.18 5.10 -1.5% PASS
total/synth/AND/b0 2.09 2.11 +1.0% PASS
total/synth/AND/b1 2.46 2.43 -1.3% PASS
total/synth/BYTE/b0 6.22 6.23 +0.1% PASS
total/synth/BYTE/b1 8.29 8.67 +4.7% PASS
total/synth/CALLDATASIZE/a0 5.32 5.33 +0.2% PASS
total/synth/CALLDATASIZE/a1 3.38 3.33 -1.5% PASS
total/synth/CALLER/a0 5.86 5.66 -3.4% PASS
total/synth/CALLER/a1 6.41 6.18 -3.7% PASS
total/synth/CALLVALUE/a0 3.51 3.53 +0.6% PASS
total/synth/CALLVALUE/a1 6.07 5.84 -3.7% PASS
total/synth/CODESIZE/a0 5.91 5.75 -2.6% PASS
total/synth/CODESIZE/a1 6.09 5.83 -4.2% PASS
total/synth/DUP1/d0 1.54 1.53 -0.9% PASS
total/synth/DUP1/d1 1.84 1.81 -1.6% PASS
total/synth/DUP10/d0 1.77 1.53 -13.5% PASS
total/synth/DUP10/d1 1.74 1.85 +6.5% PASS
total/synth/DUP11/d0 1.14 0.93 -18.5% PASS
total/synth/DUP11/d1 1.77 1.77 -0.1% PASS
total/synth/DUP12/d0 1.56 1.52 -2.3% PASS
total/synth/DUP12/d1 1.18 1.09 -7.8% PASS
total/synth/DUP13/d0 1.61 1.61 -0.4% PASS
total/synth/DUP13/d1 1.75 1.74 -0.6% PASS
total/synth/DUP14/d0 1.17 0.93 -20.5% PASS
total/synth/DUP14/d1 1.77 1.92 +8.4% PASS
total/synth/DUP15/d0 1.55 1.53 -0.8% PASS
total/synth/DUP15/d1 1.18 1.15 -2.9% PASS
total/synth/DUP16/d0 1.52 1.57 +3.2% PASS
total/synth/DUP16/d1 1.84 1.86 +1.2% PASS
total/synth/DUP2/d0 1.15 1.15 +0.0% PASS
total/synth/DUP2/d1 1.79 1.78 -0.4% PASS
total/synth/DUP3/d0 1.65 1.51 -8.8% PASS
total/synth/DUP3/d1 1.18 1.09 -7.1% PASS
total/synth/DUP4/d0 1.64 1.57 -4.0% PASS
total/synth/DUP4/d1 1.78 1.95 +9.6% PASS
total/synth/DUP5/d0 1.15 1.14 -0.5% PASS
total/synth/DUP5/d1 1.83 1.75 -4.4% PASS
total/synth/DUP6/d0 1.71 1.57 -8.1% PASS
total/synth/DUP6/d1 1.18 1.09 -7.5% PASS
total/synth/DUP7/d0 1.52 1.68 +10.7% PASS
total/synth/DUP7/d1 1.76 1.75 -0.5% PASS
total/synth/DUP8/d0 1.16 1.14 -1.2% PASS
total/synth/DUP8/d1 1.74 1.78 +2.2% PASS
total/synth/DUP9/d0 1.69 1.60 -5.5% PASS
total/synth/DUP9/d1 1.11 1.15 +3.5% PASS
total/synth/EQ/b0 4.54 4.55 +0.2% PASS
total/synth/EQ/b1 4.97 4.94 -0.4% PASS
total/synth/GAS/a0 3.76 3.76 +0.1% PASS
total/synth/GAS/a1 6.77 6.84 +1.0% PASS
total/synth/GT/b0 4.81 4.84 +0.6% PASS
total/synth/GT/b1 5.00 4.99 -0.4% PASS
total/synth/ISZERO/u0 7.14 7.04 -1.3% PASS
total/synth/JUMPDEST/n0 2.81 2.81 +0.1% PASS
total/synth/LT/b0 4.84 4.79 -0.8% PASS
total/synth/LT/b1 4.36 4.28 -1.8% PASS
total/synth/MSIZE/a0 5.49 5.50 +0.3% PASS
total/synth/MSIZE/a1 5.59 6.15 +9.9% PASS
total/synth/MUL/b0 5.55 5.54 -0.2% PASS
total/synth/MUL/b1 5.35 5.25 -1.8% PASS
total/synth/NOT/u0 2.11 2.11 -0.0% PASS
total/synth/OR/b0 2.31 2.30 -0.7% PASS
total/synth/OR/b1 1.73 1.69 -2.6% PASS
total/synth/PC/a0 5.50 5.37 -2.3% PASS
total/synth/PC/a1 3.40 3.38 -0.7% PASS
total/synth/PUSH1/p0 1.86 1.90 +1.9% PASS
total/synth/PUSH1/p1 1.65 1.63 -1.1% PASS
total/synth/PUSH10/p0 1.96 1.83 -6.2% PASS
total/synth/PUSH10/p1 1.65 1.64 -0.8% PASS
total/synth/PUSH11/p0 1.80 1.79 -0.3% PASS
total/synth/PUSH11/p1 2.03 2.01 -1.0% PASS
total/synth/PUSH12/p0 1.23 1.20 -2.3% PASS
total/synth/PUSH12/p1 2.03 2.03 +0.0% PASS
total/synth/PUSH13/p0 1.82 1.82 +0.0% PASS
total/synth/PUSH13/p1 1.65 1.64 -1.0% PASS
total/synth/PUSH14/p0 2.03 2.06 +1.8% PASS
total/synth/PUSH14/p1 1.99 2.01 +1.3% PASS
total/synth/PUSH15/p0 1.21 1.20 -1.3% PASS
total/synth/PUSH15/p1 2.24 2.26 +0.9% PASS
total/synth/PUSH16/p0 1.80 1.87 +4.0% PASS
total/synth/PUSH16/p1 1.65 1.65 -0.0% PASS
total/synth/PUSH17/p0 1.80 1.80 -0.0% PASS
total/synth/PUSH17/p1 2.05 2.11 +2.8% PASS
total/synth/PUSH18/p0 1.21 1.20 -0.8% PASS
total/synth/PUSH18/p1 2.18 2.13 -2.3% PASS
total/synth/PUSH19/p0 1.98 1.98 -0.2% PASS
total/synth/PUSH19/p1 1.65 1.65 +0.2% PASS
total/synth/PUSH2/p0 1.79 2.00 +11.4% PASS
total/synth/PUSH2/p1 1.94 1.91 -1.5% PASS
total/synth/PUSH20/p0 1.87 1.79 -4.2% PASS
total/synth/PUSH20/p1 1.97 2.00 +2.6% PASS
total/synth/PUSH21/p0 1.21 1.20 -0.4% PASS
total/synth/PUSH21/p1 2.06 1.94 -5.8% PASS
total/synth/PUSH22/p0 1.79 1.79 +0.1% PASS
total/synth/PUSH22/p1 1.67 1.64 -1.5% PASS
total/synth/PUSH23/p0 2.07 1.80 -12.9% PASS
total/synth/PUSH23/p1 2.22 2.25 +1.1% PASS
total/synth/PUSH24/p0 1.22 1.20 -1.3% PASS
total/synth/PUSH24/p1 2.05 2.03 -0.8% PASS
total/synth/PUSH25/p0 1.85 1.87 +0.6% PASS
total/synth/PUSH25/p1 1.65 1.65 -0.1% PASS
total/synth/PUSH26/p0 1.80 1.95 +8.4% PASS
total/synth/PUSH26/p1 1.99 2.07 +3.8% PASS
total/synth/PUSH27/p0 1.22 1.20 -1.1% PASS
total/synth/PUSH27/p1 2.03 2.10 +3.2% PASS
total/synth/PUSH28/p0 1.78 1.75 -2.0% PASS
total/synth/PUSH28/p1 1.67 1.67 -0.2% PASS
total/synth/PUSH29/p0 2.00 1.84 -8.1% PASS
total/synth/PUSH29/p1 2.08 2.06 -0.8% PASS
total/synth/PUSH3/p0 1.20 1.20 -0.5% PASS
total/synth/PUSH3/p1 1.91 2.33 +21.7% PASS
total/synth/PUSH30/p0 1.41 1.34 -4.4% PASS
total/synth/PUSH30/p1 2.11 2.05 -2.7% PASS
total/synth/PUSH31/p0 1.85 1.80 -2.6% PASS
total/synth/PUSH31/p1 1.69 1.69 -0.1% PASS
total/synth/PUSH32/p0 1.82 1.79 -1.6% PASS
total/synth/PUSH32/p1 2.11 2.02 -4.4% PASS
total/synth/PUSH4/p0 1.92 1.85 -3.5% PASS
total/synth/PUSH4/p1 1.65 1.64 -0.6% PASS
total/synth/PUSH5/p0 2.03 1.85 -9.2% PASS
total/synth/PUSH5/p1 1.98 1.99 +0.5% PASS
total/synth/PUSH6/p0 1.23 1.20 -2.2% PASS
total/synth/PUSH6/p1 2.03 1.97 -3.0% PASS
total/synth/PUSH7/p0 2.01 1.85 -8.3% PASS
total/synth/PUSH7/p1 1.66 1.65 -0.9% PASS
total/synth/PUSH8/p0 1.88 1.80 -4.2% PASS
total/synth/PUSH8/p1 1.97 1.97 -0.1% PASS
total/synth/PUSH9/p0 1.21 1.20 -0.8% PASS
total/synth/PUSH9/p1 2.00 2.06 +2.9% PASS
total/synth/RETURNDATASIZE/a0 3.32 3.34 +0.6% PASS
total/synth/RETURNDATASIZE/a1 5.98 6.41 +7.3% PASS
total/synth/SAR/b0 3.95 3.94 -0.2% PASS
total/synth/SAR/b1 7.09 7.22 +1.8% PASS
total/synth/SGT/b0 3.62 3.66 +1.0% PASS
total/synth/SGT/b1 3.36 3.29 -1.9% PASS
total/synth/SHL/b0 3.65 3.71 +1.7% PASS
total/synth/SHL/b1 1.86 1.79 -3.7% PASS
total/synth/SHR/b0 3.76 3.77 +0.3% PASS
total/synth/SHR/b1 3.15 3.13 -0.3% PASS
total/synth/SIGNEXTEND/b0 3.31 3.22 -2.6% PASS
total/synth/SIGNEXTEND/b1 6.46 6.75 +4.5% PASS
total/synth/SLT/b0 3.42 3.42 +0.2% PASS
total/synth/SLT/b1 3.88 3.88 +0.0% PASS
total/synth/SUB/b0 2.63 2.64 +0.4% PASS
total/synth/SUB/b1 2.75 2.80 +1.8% PASS
total/synth/SWAP1/s0 1.81 1.52 -15.7% PASS
total/synth/SWAP10/s0 1.82 1.53 -15.9% PASS
total/synth/SWAP11/s0 3.11 3.11 +0.5% PASS
total/synth/SWAP12/s0 3.10 3.04 -2.1% PASS
total/synth/SWAP13/s0 1.82 1.82 -0.1% PASS
total/synth/SWAP14/s0 3.15 3.05 -3.1% PASS
total/synth/SWAP15/s0 3.09 3.08 -0.4% PASS
total/synth/SWAP16/s0 1.84 1.82 -0.9% PASS
total/synth/SWAP2/s0 3.05 3.11 +2.3% PASS
total/synth/SWAP3/s0 2.99 3.01 +0.6% PASS
total/synth/SWAP4/s0 1.64 1.81 +9.8% PASS
total/synth/SWAP5/s0 3.10 3.06 -1.3% PASS
total/synth/SWAP6/s0 3.09 2.98 -3.6% PASS
total/synth/SWAP7/s0 1.81 1.82 +0.2% PASS
total/synth/SWAP8/s0 3.11 3.09 -0.3% PASS
total/synth/SWAP9/s0 3.07 3.12 +1.6% PASS
total/synth/XOR/b0 2.00 2.02 +1.1% PASS
total/synth/XOR/b1 2.22 2.15 -3.5% PASS
total/synth/loop_v1 9.23 8.69 -5.9% PASS
total/synth/loop_v2 9.39 9.42 +0.3% PASS

Summary: 194 benchmarks, 0 regressions


✅ Performance Check Passed (multipass)

Performance Benchmark Results (threshold: 25%)

Benchmark Baseline (us) Current (us) Change Status
total/main/blake2b_huff/8415nulls 1.10 1.08 -1.3% PASS
total/main/blake2b_huff/empty 0.02 0.02 +0.0% PASS
total/main/blake2b_shifts/8415nulls 11.56 11.55 -0.0% PASS
total/main/sha1_divs/5311 6.24 6.23 -0.2% PASS
total/main/sha1_divs/empty 0.08 0.08 +2.7% PASS
total/main/sha1_shifts/5311 4.13 4.00 -3.1% PASS
total/main/sha1_shifts/empty 0.04 0.04 -0.2% PASS
total/main/snailtracer/benchmark 64.00 63.10 -1.3% PASS
total/main/structarray_alloc/nfts_rank 0.94 0.94 +0.0% PASS
total/main/swap_math/insufficient_liquidity 0.00 0.00 +0.4% PASS
total/main/swap_math/received 0.01 0.01 -3.5% PASS
total/main/swap_math/spent 0.01 0.01 +2.2% PASS
total/main/weierstrudel/1 0.26 0.25 -3.8% PASS
total/main/weierstrudel/15 2.76 2.72 -1.7% PASS
total/micro/JUMPDEST_n0/empty 0.00 0.00 -2.4% PASS
total/micro/jump_around/empty 0.03 0.03 +0.1% PASS
total/micro/loop_with_many_jumpdests/empty 0.01 0.01 -1.0% PASS
total/micro/memory_grow_mload/by1 0.01 0.01 +1.9% PASS
total/micro/memory_grow_mload/by16 0.01 0.01 -0.6% PASS
total/micro/memory_grow_mload/by32 0.01 0.01 -0.3% PASS
total/micro/memory_grow_mload/nogrow 0.01 0.01 -0.1% PASS
total/micro/memory_grow_mstore/by1 0.07 0.07 +0.7% PASS
total/micro/memory_grow_mstore/by16 0.07 0.07 +0.1% PASS
total/micro/memory_grow_mstore/by32 0.14 0.14 -0.1% PASS
total/micro/memory_grow_mstore/nogrow 0.07 0.07 -0.6% PASS
total/micro/signextend/one 0.07 0.07 +0.3% PASS
total/micro/signextend/zero 0.13 0.14 +1.5% PASS
total/synth/ADD/b0 0.00 0.00 -2.5% PASS
total/synth/ADD/b1 0.00 0.00 +2.8% PASS
total/synth/ADDRESS/a0 0.15 0.16 +6.3% PASS
total/synth/ADDRESS/a1 0.11 0.11 -0.1% PASS
total/synth/AND/b0 0.00 0.00 -2.3% PASS
total/synth/AND/b1 0.00 0.00 +1.2% PASS
total/synth/BYTE/b0 0.00 0.00 -2.3% PASS
total/synth/BYTE/b1 0.00 0.00 +1.1% PASS
total/synth/CALLDATASIZE/a0 0.08 0.08 +0.6% PASS
total/synth/CALLDATASIZE/a1 0.06 0.06 -0.4% PASS
total/synth/CALLER/a0 0.21 0.16 -22.5% PASS
total/synth/CALLER/a1 0.17 0.17 +0.4% PASS
total/synth/CALLVALUE/a0 0.14 0.14 +0.1% PASS
total/synth/CALLVALUE/a1 0.19 0.21 +6.8% PASS
total/synth/CODESIZE/a0 0.08 0.08 -6.2% PASS
total/synth/CODESIZE/a1 0.09 0.08 -8.3% PASS
total/synth/DUP1/d0 0.00 0.00 +2.0% PASS
total/synth/DUP1/d1 0.00 0.00 +1.5% PASS
total/synth/DUP10/d0 0.00 0.00 +0.7% PASS
total/synth/DUP10/d1 0.00 0.00 +0.2% PASS
total/synth/DUP11/d0 0.00 0.00 -2.4% PASS
total/synth/DUP11/d1 0.00 0.00 +2.1% PASS
total/synth/DUP12/d0 0.00 0.00 +1.7% PASS
total/synth/DUP12/d1 0.00 0.00 -2.0% PASS
total/synth/DUP13/d0 0.00 0.00 +1.2% PASS
total/synth/DUP13/d1 0.00 0.00 +0.9% PASS
total/synth/DUP14/d0 0.00 0.00 -1.5% PASS
total/synth/DUP14/d1 0.00 0.00 +1.4% PASS
total/synth/DUP15/d0 0.00 0.00 +1.9% PASS
total/synth/DUP15/d1 0.00 0.00 -1.9% PASS
total/synth/DUP16/d0 0.00 0.00 +0.8% PASS
total/synth/DUP16/d1 0.00 0.00 +3.9% PASS
total/synth/DUP2/d0 0.00 0.00 -1.2% PASS
total/synth/DUP2/d1 0.00 0.00 +2.9% PASS
total/synth/DUP3/d0 0.00 0.00 +3.1% PASS
total/synth/DUP3/d1 0.00 0.00 -1.5% PASS
total/synth/DUP4/d0 0.00 0.00 +0.6% PASS
total/synth/DUP4/d1 0.00 0.00 +0.3% PASS
total/synth/DUP5/d0 0.00 0.00 -1.6% PASS
total/synth/DUP5/d1 0.00 0.00 +2.5% PASS
total/synth/DUP6/d0 0.00 0.00 +0.6% PASS
total/synth/DUP6/d1 0.00 0.00 -2.0% PASS
total/synth/DUP7/d0 0.00 0.00 +0.1% PASS
total/synth/DUP7/d1 0.00 0.00 +2.6% PASS
total/synth/DUP8/d0 0.00 0.00 -2.2% PASS
total/synth/DUP8/d1 0.00 0.00 +1.3% PASS
total/synth/DUP9/d0 0.00 0.00 +1.7% PASS
total/synth/DUP9/d1 0.00 0.00 -1.9% PASS
total/synth/EQ/b0 0.00 0.00 +0.6% PASS
total/synth/EQ/b1 0.00 0.00 +3.2% PASS
total/synth/GAS/a0 0.67 0.67 +0.2% PASS
total/synth/GAS/a1 0.79 0.80 +0.4% PASS
total/synth/GT/b0 0.00 0.00 +1.9% PASS
total/synth/GT/b1 0.00 0.00 +2.2% PASS
total/synth/ISZERO/u0 0.00 0.00 +1.5% PASS
total/synth/JUMPDEST/n0 0.00 0.00 -0.8% PASS
total/synth/LT/b0 0.00 0.00 +0.7% PASS
total/synth/LT/b1 0.00 0.00 -2.5% PASS
total/synth/MSIZE/a0 0.00 0.00 +0.7% PASS
total/synth/MSIZE/a1 0.00 0.00 +4.8% PASS
total/synth/MUL/b0 0.00 0.00 +1.4% PASS
total/synth/MUL/b1 0.00 0.00 -1.7% PASS
total/synth/NOT/u0 0.00 0.00 +1.0% PASS
total/synth/OR/b0 0.00 0.00 +2.8% PASS
total/synth/OR/b1 0.00 0.00 -3.1% PASS
total/synth/PC/a0 0.00 0.00 +2.2% PASS
total/synth/PC/a1 0.00 0.00 -1.2% PASS
total/synth/PUSH1/p0 0.00 0.00 +2.4% PASS
total/synth/PUSH1/p1 0.00 0.00 -1.2% PASS
total/synth/PUSH10/p0 0.00 0.00 -1.0% PASS
total/synth/PUSH10/p1 0.00 0.00 -1.4% PASS
total/synth/PUSH11/p0 0.00 0.00 +5.8% PASS
total/synth/PUSH11/p1 0.00 0.00 +1.3% PASS
total/synth/PUSH12/p0 0.00 0.00 -0.3% PASS
total/synth/PUSH12/p1 0.00 0.00 +0.4% PASS
total/synth/PUSH13/p0 0.00 0.00 +1.5% PASS
total/synth/PUSH13/p1 0.00 0.00 -2.3% PASS
total/synth/PUSH14/p0 0.00 0.00 +0.7% PASS
total/synth/PUSH14/p1 0.00 0.00 -0.4% PASS
total/synth/PUSH15/p0 0.00 0.00 +0.7% PASS
total/synth/PUSH15/p1 0.00 0.00 -0.1% PASS
total/synth/PUSH16/p0 0.00 0.00 -1.0% PASS
total/synth/PUSH16/p1 0.00 0.00 -1.8% PASS
total/synth/PUSH17/p0 0.00 0.00 +2.2% PASS
total/synth/PUSH17/p1 0.00 0.00 +2.5% PASS
total/synth/PUSH18/p0 0.00 0.00 -0.8% PASS
total/synth/PUSH18/p1 0.00 0.00 +7.5% PASS
total/synth/PUSH19/p0 0.00 0.00 -3.0% PASS
total/synth/PUSH19/p1 0.00 0.00 -1.9% PASS
total/synth/PUSH2/p0 0.00 0.00 +1.7% PASS
total/synth/PUSH2/p1 0.00 0.00 +1.5% PASS
total/synth/PUSH20/p0 0.00 0.00 +4.3% PASS
total/synth/PUSH20/p1 0.00 0.00 -1.7% PASS
total/synth/PUSH21/p0 0.00 0.00 -1.0% PASS
total/synth/PUSH21/p1 0.00 0.00 +6.2% PASS
total/synth/PUSH22/p0 1.42 1.34 -6.1% PASS
total/synth/PUSH22/p1 1.24 1.23 -0.2% PASS
total/synth/PUSH23/p0 1.52 1.52 +0.3% PASS
total/synth/PUSH23/p1 1.60 1.52 -5.3% PASS
total/synth/PUSH24/p0 1.13 1.13 -0.3% PASS
total/synth/PUSH24/p1 1.45 1.53 +5.1% PASS
total/synth/PUSH25/p0 1.40 1.57 +12.1% PASS
total/synth/PUSH25/p1 1.25 1.45 +16.3% PASS
total/synth/PUSH26/p0 1.40 1.40 +0.4% PASS
total/synth/PUSH26/p1 1.56 1.55 -0.7% PASS
total/synth/PUSH27/p0 1.13 1.13 -0.0% PASS
total/synth/PUSH27/p1 1.55 1.47 -5.0% PASS
total/synth/PUSH28/p0 1.43 1.42 -0.7% PASS
total/synth/PUSH28/p1 1.25 1.24 -0.2% PASS
total/synth/PUSH29/p0 1.45 1.53 +5.6% PASS
total/synth/PUSH29/p1 1.58 1.57 -0.6% PASS
total/synth/PUSH3/p0 0.00 0.00 -1.3% PASS
total/synth/PUSH3/p1 0.00 0.00 +8.9% PASS
total/synth/PUSH30/p0 1.18 1.18 +0.4% PASS
total/synth/PUSH30/p1 1.47 1.59 +8.3% PASS
total/synth/PUSH31/p0 1.41 1.45 +2.6% PASS
total/synth/PUSH31/p1 1.25 1.25 -0.4% PASS
total/synth/PUSH32/p0 1.40 1.45 +3.4% PASS
total/synth/PUSH32/p1 1.54 1.57 +2.0% PASS
total/synth/PUSH4/p0 0.00 0.00 +0.3% PASS
total/synth/PUSH4/p1 0.00 0.00 -2.1% PASS
total/synth/PUSH5/p0 0.00 0.00 -1.4% PASS
total/synth/PUSH5/p1 0.00 0.00 +0.4% PASS
total/synth/PUSH6/p0 0.00 0.00 -2.4% PASS
total/synth/PUSH6/p1 0.00 0.00 -0.6% PASS
total/synth/PUSH7/p0 0.00 0.00 +3.3% PASS
total/synth/PUSH7/p1 0.00 0.00 -3.9% PASS
total/synth/PUSH8/p0 0.00 0.00 -3.2% PASS
total/synth/PUSH8/p1 0.00 0.00 +0.2% PASS
total/synth/PUSH9/p0 0.00 0.00 -1.0% PASS
total/synth/PUSH9/p1 0.00 0.00 +4.2% PASS
total/synth/RETURNDATASIZE/a0 0.03 0.03 -0.1% PASS
total/synth/RETURNDATASIZE/a1 0.04 0.04 +2.9% PASS
total/synth/SAR/b0 0.00 0.00 -2.5% PASS
total/synth/SAR/b1 0.00 0.00 +0.4% PASS
total/synth/SGT/b0 0.00 0.00 +1.1% PASS
total/synth/SGT/b1 0.00 0.00 -1.6% PASS
total/synth/SHL/b0 0.00 0.00 +0.2% PASS
total/synth/SHL/b1 0.00 0.00 -1.9% PASS
total/synth/SHR/b0 0.00 0.00 +3.0% PASS
total/synth/SHR/b1 0.00 0.00 +2.3% PASS
total/synth/SIGNEXTEND/b0 0.00 0.00 -1.9% PASS
total/synth/SIGNEXTEND/b1 0.00 0.00 +2.0% PASS
total/synth/SLT/b0 0.00 0.00 -1.7% PASS
total/synth/SLT/b1 0.00 0.00 +2.2% PASS
total/synth/SUB/b0 0.00 0.00 +2.1% PASS
total/synth/SUB/b1 0.00 0.00 +2.0% PASS
total/synth/SWAP1/s0 0.00 0.00 -2.1% PASS
total/synth/SWAP10/s0 0.00 0.00 -3.8% PASS
total/synth/SWAP11/s0 0.00 0.00 +3.1% PASS
total/synth/SWAP12/s0 0.00 0.00 +1.2% PASS
total/synth/SWAP13/s0 0.00 0.00 -1.6% PASS
total/synth/SWAP14/s0 0.00 0.00 +0.5% PASS
total/synth/SWAP15/s0 0.00 0.00 +2.3% PASS
total/synth/SWAP16/s0 0.00 0.00 -1.0% PASS
total/synth/SWAP2/s0 0.00 0.00 +1.5% PASS
total/synth/SWAP3/s0 0.00 0.00 +2.1% PASS
total/synth/SWAP4/s0 0.00 0.00 -2.0% PASS
total/synth/SWAP5/s0 0.00 0.00 +2.6% PASS
total/synth/SWAP6/s0 0.00 0.00 +0.3% PASS
total/synth/SWAP7/s0 0.00 0.00 -1.2% PASS
total/synth/SWAP8/s0 0.00 0.00 +0.7% PASS
total/synth/SWAP9/s0 0.00 0.00 +0.5% PASS
total/synth/XOR/b0 0.00 0.00 +0.2% PASS
total/synth/XOR/b1 0.00 0.00 +1.1% PASS
total/synth/loop_v1 6.61 6.55 -0.9% PASS
total/synth/loop_v2 6.85 6.71 -2.1% PASS

Summary: 194 benchmarks, 0 regressions


abmcar and others added 8 commits July 7, 2026 22:50
Add a static, theme-aware SVG (per-kernel steady-state change; recovered kernels
and the weierstrudel regression shown) embedded by relative path with a caption.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QrsLkrG8HezyC5bxmjMUeA
GitHub PR descriptions do not reliably render committed SVG via camo; add a
PNG raster alongside the theme-aware SVG so the figure shows in the PR body.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QrsLkrG8HezyC5bxmjMUeA
Invalidate stack depths that cannot satisfy a block's own pops before lift and range analysis, and conservatively disable range-derived transforms if shape propagation still disagrees.

Let frontend consistency errors reach the eager compiler fallback instead of aborting in Release builds.
Restore the analyzer predicate and module routing guard for unresolved non-lifted blocks that consume deeper stack entries. Lifted boundary fixes do not cover this path when stack SSA lifting is disabled.

Update the nested internal-call differential regression to assert both the predicate and interpreter fallback.
@abmcar abmcar changed the title fix(compiler): remove the EVM multipass whole-module interpreter fallback via sound lift boundaries fix(compiler): harden EVM stack-shape analysis before JIT admission Jul 16, 2026
Abmcar added 4 commits July 16, 2026 15:54
Pass fallthrough liveness explicitly into MIR JUMPDEST construction so dead post-RETURN and post-REVERT continuation blocks cannot become fake phi predecessors. Harden lifted entry-state identity and dynamic-depth handling, add gas-aware differential regressions, and record the final lift-OFF/lift-ON mainnet correctness gate.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants