Skip to content

fix(evm): reload the cached memory base whenever the cached size is reloaded - #607

Open
abmcar wants to merge 1 commit into
DTVMStack:mainfrom
abmcar:fix/evm-jit-null-membase-upstream
Open

fix(evm): reload the cached memory base whenever the cached size is reloaded#607
abmcar wants to merge 1 commit into
DTVMStack:mainfrom
abmcar:fix/evm-jit-null-membase-upstream

Conversation

@abmcar

@abmcar abmcar commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

1. Does this PR affect any open issues?(Y/N) and add issue references (e.g. "fix #123", "re #123".):

  • N
  • Y

Related, but independent and separately mergeable: #604 reclassifies the fault
this defect produces, so that a null dereference in JIT'd code is no longer
reported as a consensus EVMC_INVALID_MEMORY_ACCESS halt. This PR removes the
dereference itself. Neither depends on the other.

2. What is the scope of this PR (e.g. component or file name):

Multipass JIT memory-cache invalidation in
src/compiler/evm_frontend/evm_mir_compiler.{cpp,h}, plus a differential
regression test in src/tests/evm_differential_tests.cpp.

3. Provide a description of the PR(e.g. more details, effects, motivations or doc link):

  • Affects user behaviors
  • Contains CI/CD configuration changes
  • Contains documentation changes
  • Contains experimental features
  • Performance regression: Consumes more CPU
  • Performance regression: Consumes more Memory
  • Other

The multipass JIT caches the EVM memory base and size in function-entry locals.
It refreshes both on the memory-expansion branch, but after a runtime helper
that can grow memory it refreshed only the size — the CALL family, CREATE,
CREATE2, LOG, KECCAK256, CALLDATACOPY, CODECOPY, EXTCODECOPY, RETURNDATACOPY.
The base was never refreshed there, and those helpers can move it.

The reason that is not merely theoretical is lazy allocation. A frame starts
with MemoryBase == nullptr; the buffer is created by ensureMemoryBuffer() on
that frame's first real growth. When the first growth happens inside a runtime
helper rather than through expandMemoryIR, the helper allocates the buffer and
sets the instance's base, the JIT reloads only the size, and the cached base
keeps the entry-time null.

A later access then combines the two halves: the reloaded size is large enough
that the expansion branch — the only other place the base is refreshed — is not
taken, so a precheck-covered access reads the cached base and stores through
null.

Two consequences worth stating plainly:

  • No nested call is required. Depth 0 on a fresh instance qualifies. The
    trigger is "first growth happened in a helper", not call depth.
  • The write lands on page zero. Before fix(evm): do not report a null-pointer dereference as an EVM memory fault #604 that was laundered into an
    EVMC_INVALID_MEMORY_ACCESS halt — a consensus status — and execution
    continued to a wrong gas number rather than failing.

The fix makes the pairing structural rather than incidental:
reloadMemoryBaseFromInstance() becomes one helper used by both the expansion
branch and the post-helper reload, and reloadMemorySizeFromInstance() becomes
reloadMemoryCachesFromInstance(), refreshing both halves of the snapshot. All
15 post-helper reload sites go through it. The cost is one 8-byte load after a
call that has already gone out to a runtime helper.

4. Are there any breaking changes?(Y/N) and describe the breaking changes(e.g. more details, motivations or doc link):

  • N
  • Y

No API, ABI or interface change. Code that previously faulted on a null store —
surfacing as an invalid memory access status, and before #604 as a consensus
halt with a wrong gas number — now executes correctly.

5. Are there test cases for these changes?(Y/N) select and add more details, references or doc links:

  • Unit test
  • Integration test
  • Benchmark (add benchmark stats below)
  • Manual test (add detailed scripts or steps below)
  • Other

Regression testEVMMemoryBaseCacheDifferential.HelperGrownMemoryIsAddressableFromALaterBlock,
a 22-byte contract with no nested call. A CALLDATACOPY with a dynamic copy
length stays on the generic memory-growing helper and performs the lazy
allocation; two constant-offset MSTOREs in the next block share one block
precheck whose expansion is already satisfied by the reloaded size, so they
address memory through the cached base without taking the expansion branch.

The two stores are load-bearing — please keep them. A single-store version
passes even against unfixed code. The block precheck that makes the stores skip
the expansion branch is only formed when it covers at least two ops
(evm_memory_grouping.h, if (CoveredOps < 2) { RejectReason = TooFewOps; });
with one store there is no shared precheck, the expansion branch is taken, and
the base gets refreshed by the very path the defect avoids. Anyone simplifying
this test will get a false green.

The test is a real negative control — verified, not assumed. I built a third
tree carrying only the test file from this commit on top of pristine 338d123,
with the compiler left unfixed, and ran it:

Expected equality of these values:
  Multi.Status    Which is: invalid memory access
  Interp.Status   Which is: success
status diverged: memory_base_cache_after_helper_growth
  Multi.OutputHex   Which is: ""
  Interp.OutputHex  Which is: "...0001...0002"

So against unfixed code the multipass JIT reports invalid memory access where
the interpreter succeeds — which is exactly the laundering #604 addresses,
reproduced in a unit test. With this commit applied the same test passes.

Full suite, GCC 12 / LLVM 15 Release multipass build (ZEN_ENABLE_EVM,
ZEN_ENABLE_MULTIPASS_JIT, ZEN_ENABLE_VIRTUAL_STACK,
ZEN_ENABLE_CPU_EXCEPTION ON):

  • clang-format check over src/: clean
  • Release build on top of 338d123: pass
  • EVM fixture generation: 209/209
  • ctest: 11 of 12 binaries pass

The one failure, solidityContractTests, is solc not found in this
environment. It fails identically on a pristine 338d123 build that I built and
ran side by side for exactly this comparison, so it is environmental rather than
a regression.

Mainnet blocks. The two blocks in a 1000-block window (25817835–25818834)
that failed under DTVM in JIT mode only. Both now verify pre- and post-state
roots, on an independent re-run:

block gasUsed txs subject db accesses reference
25818502 27644811 337 3296 3296
25818530 59842647 374 5153 5153

27644811 is the figure the crash previously corrupted to 27730760, so the gas
commitment is what most directly closes the defect. The subject's database access
count matches the reference execution exactly on both blocks. (Access counts are
our replay harness's internal gate, offered as corroboration rather than as
upstream-relevant evidence.)

6. Release note

Fix a null memory-base dereference in multipass JIT when a frame's first memory growth happens inside a runtime helper.

…eloaded

The multipass JIT caches EVM memory base and size in function-entry locals and
refreshes them on the memory-expansion branch. It also refreshes the size alone
after every runtime helper that can grow memory - the CALL family, CREATE,
CREATE2, LOG, KECCAK256, CALLDATACOPY, CODECOPY, EXTCODECOPY, RETURNDATACOPY.
The base was never refreshed there, and that is unsound, because those helpers
can move the base.

EVM memory is allocated lazily: a frame starts with MemoryBase == nullptr and
the 16 MB buffer is created by ensureMemoryBuffer() on the frame's first real
growth. When that first growth happens inside a runtime helper rather than
through expandMemoryIR, the helper allocates the buffer and sets MemoryBase,
the JIT reloads the size, and the cached base keeps the entry-time null.

A later memory access then combines the two: the reloaded size is large enough
that the expansion branch - the only other place the base is refreshed - is not
taken, and a precheck-covered access reads the cached base and stores through
null. The write lands on page zero, which the trap handler reports as an
internal error and which, before it was reclassified, was laundered into an
EVMC_INVALID_MEMORY_ACCESS halt and a wrong gas number.

Make the pairing structural instead of incidental: reloadMemoryBaseFromInstance()
is now one helper used by both the expansion branch and the post-helper reload,
and reloadMemorySizeFromInstance() becomes reloadMemoryCachesFromInstance(),
which refreshes both halves of the snapshot. The cost is one 8-byte load after
a call that has already gone out to a runtime helper.

The added differential test is a 22-byte contract with no nested call: a
CALLDATACOPY with a dynamic length grows memory inside the generic helper, and
two constant-offset stores in the next block share a block precheck whose
expansion is already satisfied, so they address memory through the cached base.
It segfaults under multipass without this change and matches the interpreter
with it.

Verified on mainnet blocks 25818502 and 25818530, the two blocks out of a
1000-block window that failed under DTVM only in JIT mode. Both now complete
with post-state-root verification.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hg7u14XK36yqjEidE3o2rY
@abmcar

abmcar commented Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

For anyone reviewing this alongside the other two open EVM PRs — they are separable, which is why they are three, but a reviewer seeing only this one may reasonably ask why a null store through a stale base was ever silent. That is the missing half:

Merging this one alone is sufficient to fix the defect. Merging #604 alone is sufficient to stop the class from being silent. Neither depends on the other.

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.

🟢 Approval recommended

The change is well-scoped, consistently applied across affected call sites, and includes a targeted regression test that exercises the previously failing helper-growth scenario.

Pull request overview

This PR fixes a multipass JIT correctness bug where the EVM memory base pointer cache could remain stale (including null) after runtime helpers that grow/allocate memory, by ensuring the cached memory base is reloaded whenever the cached memory size is reloaded. This aligns multipass JIT behavior with the interpreter and prevents null dereferences / incorrect memory addressing after helper-driven lazy memory allocation.

Changes:

  • Introduce reloadMemoryBaseFromInstance() and replace size-only refreshes with reloadMemoryCachesFromInstance() to reload both base + size as a single snapshot.
  • Update all post-runtime-helper “memory cache reload” sites to use the new paired reload helper.
  • Add a differential regression test that reproduces helper-driven first memory growth followed by later-block memory stores.
File summaries
File Description
src/compiler/evm_frontend/evm_mir_compiler.h Declares new paired memory-cache reload helpers and documents the base+size snapshot invariant.
src/compiler/evm_frontend/evm_mir_compiler.cpp Implements base reload and updates all relevant helper-return paths to reload both base and size; reuses the base reload in expandMemoryIR.
src/tests/evm_differential_tests.cpp Adds a differential regression test that fails on unfixed multipass JIT but matches the interpreter after the fix.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions

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.55 2.49 -2.7% PASS
total/main/blake2b_huff/empty 0.04 0.04 -2.6% PASS
total/main/blake2b_shifts/8415nulls 13.61 13.81 +1.4% PASS
total/main/sha1_divs/5311 8.06 8.11 +0.7% PASS
total/main/sha1_divs/empty 0.10 0.10 +2.0% PASS
total/main/sha1_shifts/5311 5.35 5.34 -0.2% PASS
total/main/sha1_shifts/empty 0.04 0.04 +0.6% PASS
total/main/snailtracer/benchmark 87.66 85.43 -2.5% PASS
total/main/structarray_alloc/nfts_rank 1.11 1.13 +1.4% PASS
total/main/swap_math/insufficient_liquidity 0.00 0.00 -1.1% PASS
total/main/swap_math/received 0.01 0.01 +6.2% PASS
total/main/swap_math/spent 0.01 0.01 -1.5% PASS
total/main/weierstrudel/1 0.32 0.33 +3.2% PASS
total/main/weierstrudel/15 3.48 3.47 -0.2% PASS
total/micro/JUMPDEST_n0/empty 2.81 2.81 -0.1% PASS
total/micro/jump_around/empty 0.11 0.11 +2.2% PASS
total/micro/loop_with_many_jumpdests/empty 69.98 69.09 -1.3% PASS
total/micro/memory_grow_mload/by1 0.17 0.17 -1.2% PASS
total/micro/memory_grow_mload/by16 0.19 0.19 +4.3% PASS
total/micro/memory_grow_mload/by32 0.11 0.12 +6.3% PASS
total/micro/memory_grow_mload/nogrow 0.09 0.08 -4.5% PASS
total/micro/memory_grow_mstore/by1 0.17 0.19 +8.2% PASS
total/micro/memory_grow_mstore/by16 0.11 0.11 +2.9% PASS
total/micro/memory_grow_mstore/by32 0.23 0.23 +0.4% PASS
total/micro/memory_grow_mstore/nogrow 0.18 0.18 -1.9% PASS
total/micro/signextend/one 0.26 0.26 -0.1% PASS
total/micro/signextend/zero 0.48 0.49 +2.2% PASS
total/synth/ADD/b0 2.02 2.02 -0.2% PASS
total/synth/ADD/b1 2.80 2.84 +1.3% PASS
total/synth/ADDRESS/a0 6.06 5.85 -3.5% PASS
total/synth/ADDRESS/a1 5.11 5.10 -0.2% PASS
total/synth/AND/b0 2.12 2.07 -2.2% PASS
total/synth/AND/b1 2.39 2.37 -1.2% PASS
total/synth/BYTE/b0 6.23 6.22 -0.2% PASS
total/synth/BYTE/b1 8.47 8.12 -4.0% PASS
total/synth/CALLDATASIZE/a0 5.01 4.93 -1.8% PASS
total/synth/CALLDATASIZE/a1 3.33 3.33 -0.1% PASS
total/synth/CALLER/a0 5.96 5.85 -1.8% PASS
total/synth/CALLER/a1 6.39 6.02 -5.7% PASS
total/synth/CALLVALUE/a0 3.52 3.52 +0.1% PASS
total/synth/CALLVALUE/a1 6.10 6.05 -0.9% PASS
total/synth/CODESIZE/a0 5.87 5.82 -0.8% PASS
total/synth/CODESIZE/a1 6.53 5.88 -10.0% PASS
total/synth/DUP1/d0 1.50 1.56 +3.5% PASS
total/synth/DUP1/d1 1.75 1.92 +10.1% PASS
total/synth/DUP10/d0 1.67 1.82 +8.9% PASS
total/synth/DUP10/d1 1.76 1.72 -2.2% PASS
total/synth/DUP11/d0 0.91 1.15 +26.2% PASS
total/synth/DUP11/d1 1.77 1.73 -2.3% PASS
total/synth/DUP12/d0 1.69 1.54 -8.4% PASS
total/synth/DUP12/d1 1.15 1.09 -5.9% PASS
total/synth/DUP13/d0 1.65 1.54 -6.1% PASS
total/synth/DUP13/d1 1.79 1.75 -2.2% PASS
total/synth/DUP14/d0 1.15 1.16 +1.1% PASS
total/synth/DUP14/d1 1.77 1.79 +1.5% PASS
total/synth/DUP15/d0 1.55 1.55 -0.1% PASS
total/synth/DUP15/d1 1.16 1.15 -0.2% PASS
total/synth/DUP16/d0 1.53 1.64 +7.1% PASS
total/synth/DUP16/d1 1.85 1.89 +1.9% PASS
total/synth/DUP2/d0 1.15 1.15 +0.4% PASS
total/synth/DUP2/d1 1.76 1.73 -1.4% PASS
total/synth/DUP3/d0 1.58 1.62 +2.8% PASS
total/synth/DUP3/d1 1.15 1.16 +0.0% PASS
total/synth/DUP4/d0 1.62 1.61 -0.6% PASS
total/synth/DUP4/d1 1.76 1.85 +5.3% PASS
total/synth/DUP5/d0 1.15 1.15 -0.8% PASS
total/synth/DUP5/d1 1.75 1.73 -1.1% PASS
total/synth/DUP6/d0 1.54 1.69 +9.6% PASS
total/synth/DUP6/d1 1.16 1.08 -6.6% PASS
total/synth/DUP7/d0 1.64 1.58 -3.3% PASS
total/synth/DUP7/d1 1.66 1.99 +19.9% PASS
total/synth/DUP8/d0 1.16 1.15 -1.1% PASS
total/synth/DUP8/d1 1.78 1.74 -1.9% PASS
total/synth/DUP9/d0 1.86 1.52 -18.0% PASS
total/synth/DUP9/d1 1.16 1.16 -0.0% PASS
total/synth/EQ/b0 4.55 4.53 -0.4% PASS
total/synth/EQ/b1 4.93 4.95 +0.4% PASS
total/synth/GAS/a0 3.76 3.76 +0.0% PASS
total/synth/GAS/a1 6.92 6.86 -0.9% PASS
total/synth/GT/b0 4.83 4.83 -0.1% PASS
total/synth/GT/b1 4.94 4.98 +0.9% PASS
total/synth/ISZERO/u0 7.09 7.08 -0.1% PASS
total/synth/JUMPDEST/n0 2.81 2.81 -0.1% PASS
total/synth/LT/b0 4.81 4.81 -0.1% PASS
total/synth/LT/b1 4.29 4.30 +0.3% PASS
total/synth/MSIZE/a0 5.14 5.43 +5.6% PASS
total/synth/MSIZE/a1 5.76 5.57 -3.2% PASS
total/synth/MUL/b0 5.60 5.52 -1.3% PASS
total/synth/MUL/b1 5.26 5.27 +0.3% PASS
total/synth/NOT/u0 2.12 2.10 -1.1% PASS
total/synth/OR/b0 2.38 2.30 -3.3% PASS
total/synth/OR/b1 1.69 1.70 +0.6% PASS
total/synth/PC/a0 5.34 5.28 -1.2% PASS
total/synth/PC/a1 3.37 3.37 +0.2% PASS
total/synth/PUSH1/p0 1.96 1.82 -7.2% PASS
total/synth/PUSH1/p1 1.64 1.65 +1.0% PASS
total/synth/PUSH10/p0 1.74 1.85 +6.4% PASS
total/synth/PUSH10/p1 1.64 1.64 -0.1% PASS
total/synth/PUSH11/p0 1.93 1.78 -7.9% PASS
total/synth/PUSH11/p1 2.11 2.04 -3.6% PASS
total/synth/PUSH12/p0 1.20 1.21 +1.4% PASS
total/synth/PUSH12/p1 2.01 2.08 +3.5% PASS
total/synth/PUSH13/p0 1.82 1.82 -0.4% PASS
total/synth/PUSH13/p1 1.64 1.64 +0.1% PASS
total/synth/PUSH14/p0 1.90 1.87 -1.2% PASS
total/synth/PUSH14/p1 2.07 1.95 -5.7% PASS
total/synth/PUSH15/p0 1.20 1.21 +1.3% PASS
total/synth/PUSH15/p1 2.34 2.08 -11.2% PASS
total/synth/PUSH16/p0 1.82 1.84 +1.2% PASS
total/synth/PUSH16/p1 1.64 1.66 +0.9% PASS
total/synth/PUSH17/p0 2.04 1.91 -5.9% PASS
total/synth/PUSH17/p1 2.08 2.26 +9.0% PASS
total/synth/PUSH18/p0 1.20 1.21 +0.9% PASS
total/synth/PUSH18/p1 2.12 1.95 -7.8% PASS
total/synth/PUSH19/p0 1.89 1.82 -3.8% PASS
total/synth/PUSH19/p1 1.65 1.64 -0.5% PASS
total/synth/PUSH2/p0 1.90 1.76 -7.6% PASS
total/synth/PUSH2/p1 1.94 2.08 +7.2% PASS
total/synth/PUSH20/p0 1.81 1.79 -1.0% PASS
total/synth/PUSH20/p1 2.39 2.25 -6.0% PASS
total/synth/PUSH21/p0 1.20 1.21 +0.9% PASS
total/synth/PUSH21/p1 2.10 2.22 +5.4% PASS
total/synth/PUSH22/p0 2.01 1.96 -2.3% PASS
total/synth/PUSH22/p1 1.64 1.65 +0.6% PASS
total/synth/PUSH23/p0 1.95 1.79 -8.1% PASS
total/synth/PUSH23/p1 2.24 2.34 +4.5% PASS
total/synth/PUSH24/p0 1.20 1.20 +0.3% PASS
total/synth/PUSH24/p1 2.21 2.02 -8.8% PASS
total/synth/PUSH25/p0 1.80 1.81 +0.6% PASS
total/synth/PUSH25/p1 1.67 1.66 -0.3% PASS
total/synth/PUSH26/p0 1.95 1.85 -5.4% PASS
total/synth/PUSH26/p1 2.07 1.95 -5.7% PASS
total/synth/PUSH27/p0 1.20 1.21 +1.4% PASS
total/synth/PUSH27/p1 1.96 2.49 +26.7% PASS
total/synth/PUSH28/p0 1.81 1.97 +8.7% PASS
total/synth/PUSH28/p1 1.64 1.64 +0.1% PASS
total/synth/PUSH29/p0 1.83 1.84 +0.5% PASS
total/synth/PUSH29/p1 1.94 2.14 +10.3% PASS
total/synth/PUSH3/p0 1.19 1.21 +1.0% PASS
total/synth/PUSH3/p1 2.09 2.07 -1.0% PASS
total/synth/PUSH30/p0 1.28 1.28 +0.3% PASS
total/synth/PUSH30/p1 2.30 2.09 -9.2% PASS
total/synth/PUSH31/p0 1.84 1.95 +5.9% PASS
total/synth/PUSH31/p1 1.68 1.69 +0.7% PASS
total/synth/PUSH32/p0 1.92 1.84 -4.1% PASS
total/synth/PUSH32/p1 2.24 2.29 +2.3% PASS
total/synth/PUSH4/p0 1.84 1.79 -3.0% PASS
total/synth/PUSH4/p1 1.64 1.64 -0.4% PASS
total/synth/PUSH5/p0 1.92 2.01 +4.7% PASS
total/synth/PUSH5/p1 2.08 2.07 -0.4% PASS
total/synth/PUSH6/p0 1.19 1.23 +2.9% PASS
total/synth/PUSH6/p1 1.98 2.04 +3.4% PASS
total/synth/PUSH7/p0 1.92 1.73 -9.7% PASS
total/synth/PUSH7/p1 1.65 1.64 -0.6% PASS
total/synth/PUSH8/p0 1.83 2.04 +11.5% PASS
total/synth/PUSH8/p1 2.10 2.13 +1.3% PASS
total/synth/PUSH9/p0 1.20 1.21 +0.9% PASS
total/synth/PUSH9/p1 1.97 2.44 +23.8% PASS
total/synth/RETURNDATASIZE/a0 3.34 3.32 -0.8% PASS
total/synth/RETURNDATASIZE/a1 6.19 5.97 -3.5% PASS
total/synth/SAR/b0 3.95 3.95 -0.0% PASS
total/synth/SAR/b1 7.23 7.43 +2.9% PASS
total/synth/SGT/b0 3.63 3.63 -0.2% PASS
total/synth/SGT/b1 3.30 3.31 +0.5% PASS
total/synth/SHL/b0 3.63 3.73 +2.6% PASS
total/synth/SHL/b1 1.81 1.82 +0.8% PASS
total/synth/SHR/b0 3.80 3.76 -1.1% PASS
total/synth/SHR/b1 3.14 3.15 +0.6% PASS
total/synth/SIGNEXTEND/b0 3.24 3.21 -0.9% PASS
total/synth/SIGNEXTEND/b1 6.81 6.45 -5.2% PASS
total/synth/SLT/b0 3.42 3.42 -0.1% PASS
total/synth/SLT/b1 3.84 3.82 -0.6% PASS
total/synth/SUB/b0 2.75 2.70 -1.7% PASS
total/synth/SUB/b1 2.66 2.61 -1.8% PASS
total/synth/SWAP1/s0 1.80 1.80 +0.0% PASS
total/synth/SWAP10/s0 1.81 1.82 +0.5% PASS
total/synth/SWAP11/s0 3.13 3.12 -0.3% PASS
total/synth/SWAP12/s0 3.09 3.16 +2.3% PASS
total/synth/SWAP13/s0 1.85 1.82 -1.3% PASS
total/synth/SWAP14/s0 3.14 3.08 -1.6% PASS
total/synth/SWAP15/s0 3.13 3.16 +1.1% PASS
total/synth/SWAP16/s0 1.82 1.82 -0.0% PASS
total/synth/SWAP2/s0 3.03 3.24 +6.8% PASS
total/synth/SWAP3/s0 3.09 3.08 -0.2% PASS
total/synth/SWAP4/s0 1.81 1.81 -0.1% PASS
total/synth/SWAP5/s0 3.09 3.14 +1.5% PASS
total/synth/SWAP6/s0 3.11 3.09 -0.9% PASS
total/synth/SWAP7/s0 1.82 1.82 -0.0% PASS
total/synth/SWAP8/s0 3.19 3.14 -1.4% PASS
total/synth/SWAP9/s0 3.04 3.76 +23.5% PASS
total/synth/XOR/b0 2.01 2.04 +1.7% PASS
total/synth/XOR/b1 2.13 2.16 +1.2% PASS
total/synth/loop_v1 8.82 8.49 -3.7% PASS
total/synth/loop_v2 8.98 8.75 -2.5% 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 0.88 0.87 -1.6% PASS
total/main/blake2b_huff/empty 0.01 0.01 -1.5% PASS
total/main/blake2b_shifts/8415nulls 2.84 2.84 -0.2% PASS
total/main/sha1_divs/5311 0.55 0.55 +0.2% PASS
total/main/sha1_divs/empty 0.01 0.01 +6.4% PASS
total/main/sha1_shifts/5311 0.48 0.48 -0.4% PASS
total/main/sha1_shifts/empty 0.00 0.00 -0.2% PASS
total/main/snailtracer/benchmark 27.20 27.59 +1.4% PASS
total/main/structarray_alloc/nfts_rank 0.19 0.19 +0.1% PASS
total/main/swap_math/insufficient_liquidity 0.00 0.00 +0.9% PASS
total/main/swap_math/received 0.00 0.00 +4.9% PASS
total/main/swap_math/spent 0.00 0.00 +0.9% PASS
total/main/weierstrudel/1 0.21 0.19 -8.3% PASS
total/main/weierstrudel/15 2.18 2.21 +1.7% PASS
total/micro/JUMPDEST_n0/empty 0.00 0.00 +0.0% PASS
total/micro/jump_around/empty 0.01 0.01 +0.6% PASS
total/micro/loop_with_many_jumpdests/empty 0.00 0.00 -0.9% PASS
total/micro/memory_grow_mload/by1 0.01 0.01 -1.0% PASS
total/micro/memory_grow_mload/by16 0.01 0.01 +0.7% PASS
total/micro/memory_grow_mload/by32 0.01 0.01 +0.8% PASS
total/micro/memory_grow_mload/nogrow 0.01 0.01 +0.8% PASS
total/micro/memory_grow_mstore/by1 0.01 0.01 +2.0% PASS
total/micro/memory_grow_mstore/by16 0.01 0.01 -0.4% PASS
total/micro/memory_grow_mstore/by32 0.01 0.01 +0.4% PASS
total/micro/memory_grow_mstore/nogrow 0.01 0.01 +0.7% PASS
total/micro/signextend/one 0.21 0.22 +0.2% PASS
total/micro/signextend/zero 0.30 0.30 +0.1% PASS
total/synth/ADD/b0 0.00 0.00 +0.0% PASS
total/synth/ADD/b1 0.00 0.00 -3.4% PASS
total/synth/ADDRESS/a0 0.15 0.13 -11.4% PASS
total/synth/ADDRESS/a1 0.10 0.10 +0.1% PASS
total/synth/AND/b0 0.00 0.00 +0.5% PASS
total/synth/AND/b1 0.00 0.00 -3.0% PASS
total/synth/BYTE/b0 0.00 0.00 -0.2% PASS
total/synth/BYTE/b1 0.00 0.00 -4.6% PASS
total/synth/CALLDATASIZE/a0 0.07 0.07 +4.5% PASS
total/synth/CALLDATASIZE/a1 0.05 0.05 -0.0% PASS
total/synth/CALLER/a0 0.14 0.14 -5.2% PASS
total/synth/CALLER/a1 0.14 0.13 -4.9% PASS
total/synth/CALLVALUE/a0 0.13 0.13 -0.7% PASS
total/synth/CALLVALUE/a1 0.17 0.16 -5.4% PASS
total/synth/CODESIZE/a0 0.07 0.07 +0.3% PASS
total/synth/CODESIZE/a1 0.07 0.07 +0.9% PASS
total/synth/DUP1/d0 0.00 0.00 -8.4% PASS
total/synth/DUP1/d1 0.00 0.00 -3.7% PASS
total/synth/DUP10/d0 0.00 0.00 -1.0% PASS
total/synth/DUP10/d1 0.00 0.00 +0.6% PASS
total/synth/DUP11/d0 0.00 0.00 -0.1% PASS
total/synth/DUP11/d1 0.00 0.00 -3.8% PASS
total/synth/DUP12/d0 0.00 0.00 -3.7% PASS
total/synth/DUP12/d1 0.00 0.00 +0.4% PASS
total/synth/DUP13/d0 0.00 0.00 -2.1% PASS
total/synth/DUP13/d1 0.00 0.00 -0.7% PASS
total/synth/DUP14/d0 0.00 0.00 -0.1% PASS
total/synth/DUP14/d1 0.00 0.00 +2.4% PASS
total/synth/DUP15/d0 0.00 0.00 -4.2% PASS
total/synth/DUP15/d1 0.00 0.00 +0.4% PASS
total/synth/DUP16/d0 0.00 0.00 -2.6% PASS
total/synth/DUP16/d1 0.00 0.00 +0.9% PASS
total/synth/DUP2/d0 0.00 0.00 +0.7% PASS
total/synth/DUP2/d1 0.00 0.00 -2.2% PASS
total/synth/DUP3/d0 0.00 0.00 -3.4% PASS
total/synth/DUP3/d1 0.00 0.00 +0.2% PASS
total/synth/DUP4/d0 0.00 0.00 -4.3% PASS
total/synth/DUP4/d1 0.00 0.00 -4.1% PASS
total/synth/DUP5/d0 0.00 0.00 +0.3% PASS
total/synth/DUP5/d1 0.00 0.00 -0.7% PASS
total/synth/DUP6/d0 0.00 0.00 +2.7% PASS
total/synth/DUP6/d1 0.00 0.00 +0.1% PASS
total/synth/DUP7/d0 0.00 0.00 -3.1% PASS
total/synth/DUP7/d1 0.00 0.00 -0.9% PASS
total/synth/DUP8/d0 0.00 0.00 +0.3% PASS
total/synth/DUP8/d1 0.00 0.00 -1.6% PASS
total/synth/DUP9/d0 0.00 0.00 -2.1% PASS
total/synth/DUP9/d1 0.00 0.00 +0.5% PASS
total/synth/EQ/b0 0.00 0.00 -0.8% PASS
total/synth/EQ/b1 0.00 0.00 +11.2% PASS
total/synth/GAS/a0 0.37 0.37 +0.3% PASS
total/synth/GAS/a1 0.52 0.51 -1.2% PASS
total/synth/GT/b0 0.00 0.00 -2.6% PASS
total/synth/GT/b1 0.00 0.00 +5.8% PASS
total/synth/ISZERO/u0 0.00 0.00 -0.8% PASS
total/synth/JUMPDEST/n0 0.00 0.00 +0.7% PASS
total/synth/LT/b0 0.00 0.00 -1.1% PASS
total/synth/LT/b1 0.00 0.00 +0.0% PASS
total/synth/MSIZE/a0 0.00 0.00 +6.8% PASS
total/synth/MSIZE/a1 0.00 0.00 +3.7% PASS
total/synth/MUL/b0 0.00 0.00 +0.8% PASS
total/synth/MUL/b1 0.00 0.00 +0.4% PASS
total/synth/NOT/u0 0.00 0.00 +0.2% PASS
total/synth/OR/b0 0.00 0.00 +0.8% PASS
total/synth/OR/b1 0.00 0.00 -0.0% PASS
total/synth/PC/a0 0.00 0.00 +2.4% PASS
total/synth/PC/a1 0.00 0.00 +0.0% PASS
total/synth/PUSH1/p0 0.00 0.00 -1.1% PASS
total/synth/PUSH1/p1 0.00 0.00 +0.2% PASS
total/synth/PUSH10/p0 0.00 0.00 -0.7% PASS
total/synth/PUSH10/p1 0.00 0.00 +1.9% PASS
total/synth/PUSH11/p0 0.00 0.00 -1.9% PASS
total/synth/PUSH11/p1 0.00 0.00 +3.1% PASS
total/synth/PUSH12/p0 0.00 0.00 -0.2% PASS
total/synth/PUSH12/p1 0.00 0.00 +0.0% PASS
total/synth/PUSH13/p0 0.00 0.00 -3.1% PASS
total/synth/PUSH13/p1 0.00 0.00 -2.9% PASS
total/synth/PUSH14/p0 0.00 0.00 -5.8% PASS
total/synth/PUSH14/p1 0.00 0.00 +0.3% PASS
total/synth/PUSH15/p0 0.00 0.00 -1.8% PASS
total/synth/PUSH15/p1 0.00 0.00 -0.8% PASS
total/synth/PUSH16/p0 0.00 0.00 +0.1% PASS
total/synth/PUSH16/p1 0.00 0.00 +5.1% PASS
total/synth/PUSH17/p0 0.00 0.00 -6.0% PASS
total/synth/PUSH17/p1 0.00 0.00 -5.5% PASS
total/synth/PUSH18/p0 0.00 0.00 -7.9% PASS
total/synth/PUSH18/p1 0.00 0.00 -1.2% PASS
total/synth/PUSH19/p0 0.00 0.00 +1.5% PASS
total/synth/PUSH19/p1 0.00 0.00 -7.4% PASS
total/synth/PUSH2/p0 0.00 0.00 -7.0% PASS
total/synth/PUSH2/p1 0.00 0.00 -1.8% PASS
total/synth/PUSH20/p0 0.00 0.00 +7.5% PASS
total/synth/PUSH20/p1 0.00 0.00 +3.4% PASS
total/synth/PUSH21/p0 0.00 0.00 +1.9% PASS
total/synth/PUSH21/p1 0.00 0.00 -4.1% PASS
total/synth/PUSH22/p0 1.15 1.12 -3.1% PASS
total/synth/PUSH22/p1 0.83 0.83 +0.4% PASS
total/synth/PUSH23/p0 1.12 1.14 +1.7% PASS
total/synth/PUSH23/p1 1.35 1.20 -11.2% PASS
total/synth/PUSH24/p0 0.72 0.72 -0.2% PASS
total/synth/PUSH24/p1 1.21 1.19 -1.4% PASS
total/synth/PUSH25/p0 1.15 1.15 +0.3% PASS
total/synth/PUSH25/p1 0.84 0.83 -1.5% PASS
total/synth/PUSH26/p0 1.16 1.18 +1.7% PASS
total/synth/PUSH26/p1 1.29 1.20 -7.0% PASS
total/synth/PUSH27/p0 0.73 0.73 +0.1% PASS
total/synth/PUSH27/p1 1.27 1.25 -1.6% PASS
total/synth/PUSH28/p0 1.14 1.15 +1.5% PASS
total/synth/PUSH28/p1 0.84 0.84 -0.1% PASS
total/synth/PUSH29/p0 1.18 1.12 -4.6% PASS
total/synth/PUSH29/p1 1.16 1.23 +5.7% PASS
total/synth/PUSH3/p0 0.00 0.00 +0.0% PASS
total/synth/PUSH3/p1 0.00 0.00 -1.4% PASS
total/synth/PUSH30/p0 0.73 0.73 +0.6% PASS
total/synth/PUSH30/p1 1.21 1.20 -0.5% PASS
total/synth/PUSH31/p0 1.18 1.13 -4.6% PASS
total/synth/PUSH31/p1 0.84 0.83 -0.6% PASS
total/synth/PUSH32/p0 1.20 1.25 +4.1% PASS
total/synth/PUSH32/p1 1.21 1.17 -3.1% PASS
total/synth/PUSH4/p0 0.00 0.00 -6.6% PASS
total/synth/PUSH4/p1 0.00 0.00 +0.6% PASS
total/synth/PUSH5/p0 0.00 0.00 -1.1% PASS
total/synth/PUSH5/p1 0.00 0.00 -3.9% PASS
total/synth/PUSH6/p0 0.00 0.00 +0.1% PASS
total/synth/PUSH6/p1 0.00 0.00 +0.7% PASS
total/synth/PUSH7/p0 0.00 0.00 -0.2% PASS
total/synth/PUSH7/p1 0.00 0.00 -0.6% PASS
total/synth/PUSH8/p0 0.00 0.00 -4.5% PASS
total/synth/PUSH8/p1 0.00 0.00 +3.0% PASS
total/synth/PUSH9/p0 0.00 0.00 +1.8% PASS
total/synth/PUSH9/p1 0.00 0.00 -1.8% PASS
total/synth/RETURNDATASIZE/a0 0.03 0.03 -0.1% PASS
total/synth/RETURNDATASIZE/a1 0.04 0.04 -1.1% PASS
total/synth/SAR/b0 0.00 0.00 +0.1% PASS
total/synth/SAR/b1 0.00 0.00 +1.3% PASS
total/synth/SGT/b0 0.00 0.00 -0.3% PASS
total/synth/SGT/b1 0.00 0.00 +0.7% PASS
total/synth/SHL/b0 0.00 0.00 -8.5% PASS
total/synth/SHL/b1 0.00 0.00 -0.7% PASS
total/synth/SHR/b0 0.00 0.00 -7.7% PASS
total/synth/SHR/b1 0.00 0.00 +0.0% PASS
total/synth/SIGNEXTEND/b0 0.00 0.00 -7.4% PASS
total/synth/SIGNEXTEND/b1 0.00 0.00 +5.1% PASS
total/synth/SLT/b0 0.00 0.00 -0.1% PASS
total/synth/SLT/b1 0.00 0.00 -2.9% PASS
total/synth/SUB/b0 0.00 0.00 -5.7% PASS
total/synth/SUB/b1 0.00 0.00 -1.8% PASS
total/synth/SWAP1/s0 0.00 0.00 -0.0% PASS
total/synth/SWAP10/s0 0.00 0.00 -0.0% PASS
total/synth/SWAP11/s0 0.00 0.00 +6.4% PASS
total/synth/SWAP12/s0 0.00 0.00 -6.8% PASS
total/synth/SWAP13/s0 0.00 0.00 +0.3% PASS
total/synth/SWAP14/s0 0.00 0.00 -12.3% PASS
total/synth/SWAP15/s0 0.00 0.00 -5.4% PASS
total/synth/SWAP16/s0 0.00 0.00 -0.2% PASS
total/synth/SWAP2/s0 0.00 0.00 +0.4% PASS
total/synth/SWAP3/s0 0.00 0.00 +3.7% PASS
total/synth/SWAP4/s0 0.00 0.00 -0.1% PASS
total/synth/SWAP5/s0 0.00 0.00 +3.4% PASS
total/synth/SWAP6/s0 0.00 0.00 -3.9% PASS
total/synth/SWAP7/s0 0.00 0.00 -0.1% PASS
total/synth/SWAP8/s0 0.00 0.00 -2.6% PASS
total/synth/SWAP9/s0 0.00 0.00 -5.1% PASS
total/synth/XOR/b0 0.00 0.00 -2.7% PASS
total/synth/XOR/b1 0.00 0.00 +0.7% PASS
total/synth/loop_v1 4.82 5.29 +9.9% PASS
total/synth/loop_v2 5.21 4.80 -7.8% PASS

Summary: 194 benchmarks, 0 regressions


abmcar pushed a commit to abmcar/reth that referenced this pull request Aug 29, 2026
Two corrections to §10 now that the campaign has run.

The defect table listed the JIT null dereference as open and told the reader to
run on a 998-block subset. It is fixed upstream (DTVMStack/DTVM#607) and a full
rescan under the fixed libraries returns 1000 OK, 0 failures. All 46 originally
failing blocks are closed and the four-engine intersection is the whole corpus.
The table's description of that defect was also wrong in a way worth fixing: it
is not about nested calls. EVM memory is allocated lazily, and the failure needs
a frame's first growth to happen inside a runtime helper rather than through
expandMemoryIR; depth 0 on a fresh instance qualifies.

New §10.5 says what the measured number actually contains, because the obvious
reading of it is wrong in two ways. First, the tight boundary is available after
all — replay-batch fills rethSubjectRunExecLoop, replay-block does not, which is
why it reads as null if you check the wrong tool — and moving to it makes DTVM
look worse rather than better, since the fixed overhead is a larger fraction of
REVM's smaller figure. Second, neither boundary isolates code generation: a
metrics build counts 2,248 module-cache lookups per block against 186 top-level
executions, each running a full bytecode memcmp under the mandatory strict
validation, and nested frames never reopen the timing window. The figure is
DTVM-through-the-bridge against native REVM, and the document now says so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018osheVEXr1SywFtbYFjHAc
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.

3 participants