Skip to content

perf(evm): implement EVM lazy compile (segment-based compilation)#416

Open
ys8888john wants to merge 5 commits intoDTVMStack:mainfrom
ys8888john:evm-lazy-compile
Open

perf(evm): implement EVM lazy compile (segment-based compilation)#416
ys8888john wants to merge 5 commits intoDTVMStack:mainfrom
ys8888john:evm-lazy-compile

Conversation

@ys8888john
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

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

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

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

  • N
  • Y

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

6. Release note

None

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 18, 2026

⚡ 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 1.54 1.32 -14.7% PASS
total/main/blake2b_huff/empty 0.02 0.02 -11.8% PASS
total/main/blake2b_shifts/8415nulls 12.24 9.48 -22.5% PASS
total/main/sha1_divs/5311 5.29 4.41 -16.6% PASS
total/main/sha1_divs/empty 0.07 0.05 -21.5% PASS
total/main/sha1_shifts/5311 3.04 2.24 -26.3% PASS
total/main/sha1_shifts/empty 0.04 0.03 -28.2% PASS
total/main/snailtracer/benchmark 55.92 46.78 -16.3% PASS
total/main/structarray_alloc/nfts_rank 1.04 0.93 -10.6% PASS
total/main/swap_math/insufficient_liquidity 0.00 0.00 -20.5% PASS
total/main/swap_math/received 0.01 0.00 -25.0% PASS
total/main/swap_math/spent 0.00 0.00 -21.1% PASS
total/main/weierstrudel/1 0.29 0.23 -19.7% PASS
total/main/weierstrudel/15 3.15 2.51 -20.2% PASS
total/micro/JUMPDEST_n0/empty 1.79 1.51 -15.7% PASS
total/micro/jump_around/empty 0.09 0.04 -57.9% PASS
total/micro/loop_with_many_jumpdests/empty 26.96 23.01 -14.7% PASS
total/micro/memory_grow_mload/by1 0.09 0.08 -14.5% PASS
total/micro/memory_grow_mload/by16 0.11 0.09 -12.0% PASS
total/micro/memory_grow_mload/by32 0.11 0.10 -7.9% PASS
total/micro/memory_grow_mload/nogrow 0.09 0.08 -11.5% PASS
total/micro/memory_grow_mstore/by1 0.10 0.09 -11.6% PASS
total/micro/memory_grow_mstore/by16 0.11 0.10 -9.3% PASS
total/micro/memory_grow_mstore/by32 0.12 0.11 -9.3% PASS
total/micro/memory_grow_mstore/nogrow 0.09 0.09 -5.3% PASS
total/micro/signextend/one 0.24 0.25 +4.2% PASS
total/micro/signextend/zero 0.24 0.24 +2.2% PASS
total/synth/ADD/b0 1.96 1.34 -31.5% PASS
total/synth/ADD/b1 1.98 1.19 -39.5% PASS
total/synth/ADDRESS/a0 4.83 5.97 +23.6% PASS
total/synth/ADDRESS/a1 5.17 6.11 +18.1% PASS
total/synth/AND/b0 1.64 1.26 -23.4% PASS
total/synth/AND/b1 1.71 1.05 -38.8% PASS
total/synth/BYTE/b0 6.27 4.65 -25.9% PASS
total/synth/BYTE/b1 4.81 3.78 -21.3% PASS
total/synth/CALLDATASIZE/a0 3.18 2.95 -7.4% PASS
total/synth/CALLDATASIZE/a1 3.55 3.06 -13.9% PASS
total/synth/CALLER/a0 4.85 5.96 +23.0% PASS
total/synth/CALLER/a1 5.44 6.13 +12.6% PASS
total/synth/CALLVALUE/a0 3.26 3.00 -8.1% PASS
total/synth/CALLVALUE/a1 3.60 3.02 -16.1% PASS
total/synth/CODESIZE/a0 3.50 3.49 -0.4% PASS
total/synth/CODESIZE/a1 3.61 3.55 -1.9% PASS
total/synth/DUP1/d0 1.15 0.61 -46.8% PASS
total/synth/DUP1/d1 1.15 0.66 -43.2% PASS
total/synth/DUP10/d0 1.15 0.61 -46.7% PASS
total/synth/DUP10/d1 1.15 0.61 -46.8% PASS
total/synth/DUP11/d0 5.20 0.61 -88.3% PASS
total/synth/DUP11/d1 1.15 0.61 -46.8% PASS
total/synth/DUP12/d0 5.20 0.63 -87.8% PASS
total/synth/DUP12/d1 1.15 0.61 -46.8% PASS
total/synth/DUP13/d0 1.15 1.07 -7.5% PASS
total/synth/DUP13/d1 1.15 0.61 -46.8% PASS
total/synth/DUP14/d0 1.39 0.88 -36.7% PASS
total/synth/DUP14/d1 1.16 0.61 -46.9% PASS
total/synth/DUP15/d0 1.15 0.61 -46.8% PASS
total/synth/DUP15/d1 1.15 0.61 -46.8% PASS
total/synth/DUP16/d0 5.21 0.61 -88.3% PASS
total/synth/DUP16/d1 1.16 0.61 -46.9% PASS
total/synth/DUP2/d0 3.02 0.64 -78.9% PASS
total/synth/DUP2/d1 1.15 0.61 -46.9% PASS
total/synth/DUP3/d0 1.15 0.61 -46.9% PASS
total/synth/DUP3/d1 1.15 0.61 -46.8% PASS
total/synth/DUP4/d0 3.69 0.61 -83.5% PASS
total/synth/DUP4/d1 1.15 0.61 -46.9% PASS
total/synth/DUP5/d0 5.22 0.61 -88.3% PASS
total/synth/DUP5/d1 1.15 0.61 -46.8% PASS
total/synth/DUP6/d0 1.15 0.61 -46.8% PASS
total/synth/DUP6/d1 1.15 0.61 -46.7% PASS
total/synth/DUP7/d0 5.21 0.61 -88.3% PASS
total/synth/DUP7/d1 1.15 0.61 -46.8% PASS
total/synth/DUP8/d0 1.39 0.61 -56.3% PASS
total/synth/DUP8/d1 1.15 0.61 -46.8% PASS
total/synth/DUP9/d0 5.23 0.61 -88.3% PASS
total/synth/DUP9/d1 1.15 0.62 -46.7% PASS
total/synth/EQ/b0 2.69 2.28 -15.4% PASS
total/synth/EQ/b1 1.31 1.25 -4.5% PASS
total/synth/GAS/a0 3.76 3.24 -13.8% PASS
total/synth/GAS/a1 3.77 3.26 -13.4% PASS
total/synth/GT/b0 2.61 2.21 -15.1% PASS
total/synth/GT/b1 1.56 0.96 -38.5% PASS
total/synth/ISZERO/u0 1.14 0.77 -33.0% PASS
total/synth/JUMPDEST/n0 1.64 1.51 -8.0% PASS
total/synth/LT/b0 2.61 2.16 -17.1% PASS
total/synth/LT/b1 1.56 0.96 -38.5% PASS
total/synth/MSIZE/a0 4.24 4.14 -2.4% PASS
total/synth/MSIZE/a1 4.60 4.31 -6.2% PASS
total/synth/MUL/b0 5.30 4.41 -16.8% PASS
total/synth/MUL/b1 5.38 4.66 -13.3% PASS
total/synth/NOT/u0 1.66 1.10 -33.8% PASS
total/synth/OR/b0 1.64 1.25 -23.9% PASS
total/synth/OR/b1 1.71 1.05 -38.8% PASS
total/synth/PC/a0 3.11 2.94 -5.4% PASS
total/synth/PC/a1 4.15 2.95 -29.1% PASS
total/synth/PUSH1/p0 1.24 0.87 -30.0% PASS
total/synth/PUSH1/p1 1.35 0.87 -35.2% PASS
total/synth/PUSH10/p0 1.31 0.87 -33.2% PASS
total/synth/PUSH10/p1 1.11 0.90 -19.4% PASS
total/synth/PUSH11/p0 1.31 0.88 -32.9% PASS
total/synth/PUSH11/p1 1.11 0.89 -19.5% PASS
total/synth/PUSH12/p0 1.31 0.88 -33.2% PASS
total/synth/PUSH12/p1 1.36 0.90 -34.0% PASS
total/synth/PUSH13/p0 1.31 0.88 -32.9% PASS
total/synth/PUSH13/p1 1.11 0.90 -19.1% PASS
total/synth/PUSH14/p0 1.31 0.87 -33.5% PASS
total/synth/PUSH14/p1 1.35 0.90 -33.6% PASS
total/synth/PUSH15/p0 1.31 0.88 -32.8% PASS
total/synth/PUSH15/p1 1.21 0.89 -25.9% PASS
total/synth/PUSH16/p0 1.23 0.88 -28.3% PASS
total/synth/PUSH16/p1 1.13 0.90 -20.3% PASS
total/synth/PUSH17/p0 1.23 0.88 -28.3% PASS
total/synth/PUSH17/p1 1.12 0.90 -19.0% PASS
total/synth/PUSH18/p0 1.31 0.88 -32.8% PASS
total/synth/PUSH18/p1 1.13 0.91 -19.5% PASS
total/synth/PUSH19/p0 1.31 0.89 -32.6% PASS
total/synth/PUSH19/p1 1.36 0.91 -33.2% PASS
total/synth/PUSH2/p0 1.31 0.87 -34.1% PASS
total/synth/PUSH2/p1 1.11 0.88 -20.2% PASS
total/synth/PUSH20/p0 1.23 0.89 -28.0% PASS
total/synth/PUSH20/p1 1.36 0.91 -33.0% PASS
total/synth/PUSH21/p0 1.31 0.89 -32.2% PASS
total/synth/PUSH21/p1 1.36 0.91 -33.0% PASS
total/synth/PUSH22/p0 1.31 0.88 -32.7% PASS
total/synth/PUSH22/p1 1.14 0.91 -19.5% PASS
total/synth/PUSH23/p0 1.31 0.89 -32.2% PASS
total/synth/PUSH23/p1 1.19 0.92 -22.6% PASS
total/synth/PUSH24/p0 1.31 0.89 -31.8% PASS
total/synth/PUSH24/p1 1.14 0.92 -19.4% PASS
total/synth/PUSH25/p0 1.23 0.89 -27.6% PASS
total/synth/PUSH25/p1 1.13 0.92 -17.8% PASS
total/synth/PUSH26/p0 1.23 0.89 -27.6% PASS
total/synth/PUSH26/p1 1.37 0.93 -32.3% PASS
total/synth/PUSH27/p0 1.07 0.89 -16.7% PASS
total/synth/PUSH27/p1 1.14 0.93 -18.3% PASS
total/synth/PUSH28/p0 1.31 0.89 -31.7% PASS
total/synth/PUSH28/p1 1.14 0.93 -18.2% PASS
total/synth/PUSH29/p0 1.23 0.89 -27.4% PASS
total/synth/PUSH29/p1 1.15 0.93 -19.1% PASS
total/synth/PUSH3/p0 1.32 0.87 -33.8% PASS
total/synth/PUSH3/p1 1.11 0.89 -20.1% PASS
total/synth/PUSH30/p0 1.32 0.90 -32.2% PASS
total/synth/PUSH30/p1 1.14 0.94 -17.9% PASS
total/synth/PUSH31/p0 1.31 0.90 -31.6% PASS
total/synth/PUSH31/p1 1.48 0.94 -36.7% PASS
total/synth/PUSH32/p0 1.23 0.90 -27.3% PASS
total/synth/PUSH32/p1 1.37 0.94 -31.6% PASS
total/synth/PUSH4/p0 1.31 0.87 -33.5% PASS
total/synth/PUSH4/p1 1.11 0.89 -20.3% PASS
total/synth/PUSH5/p0 1.31 0.87 -33.6% PASS
total/synth/PUSH5/p1 1.35 0.88 -34.4% PASS
total/synth/PUSH6/p0 1.31 0.87 -33.7% PASS
total/synth/PUSH6/p1 1.11 0.90 -19.6% PASS
total/synth/PUSH7/p0 1.31 0.88 -33.2% PASS
total/synth/PUSH7/p1 1.35 0.88 -35.3% PASS
total/synth/PUSH8/p0 1.31 0.87 -33.5% PASS
total/synth/PUSH8/p1 1.11 0.89 -19.5% PASS
total/synth/PUSH9/p0 1.31 0.88 -32.8% PASS
total/synth/PUSH9/p1 1.11 0.89 -19.5% PASS
total/synth/RETURNDATASIZE/a0 3.78 3.33 -12.0% PASS
total/synth/RETURNDATASIZE/a1 3.68 3.31 -10.0% PASS
total/synth/SAR/b0 3.77 3.81 +1.1% PASS
total/synth/SAR/b1 4.26 3.99 -6.4% PASS
total/synth/SGT/b0 2.60 2.19 -15.8% PASS
total/synth/SGT/b1 1.72 1.06 -38.5% PASS
total/synth/SHL/b0 3.06 2.83 -7.3% PASS
total/synth/SHL/b1 1.75 1.26 -28.1% PASS
total/synth/SHR/b0 3.09 2.75 -11.0% PASS
total/synth/SHR/b1 1.67 1.22 -26.8% PASS
total/synth/SIGNEXTEND/b0 3.19 2.86 -10.5% PASS
total/synth/SIGNEXTEND/b1 3.44 2.94 -14.6% PASS
total/synth/SLT/b0 2.60 2.22 -14.6% PASS
total/synth/SLT/b1 1.65 1.03 -37.4% PASS
total/synth/SUB/b0 1.97 1.34 -31.9% PASS
total/synth/SUB/b1 1.98 1.20 -39.6% PASS
total/synth/SWAP1/s0 1.49 1.08 -27.6% PASS
total/synth/SWAP10/s0 1.81 1.08 -40.6% PASS
total/synth/SWAP11/s0 1.81 1.08 -40.3% PASS
total/synth/SWAP12/s0 1.82 1.08 -40.4% PASS
total/synth/SWAP13/s0 1.82 1.84 +1.4% PASS
total/synth/SWAP14/s0 1.82 1.39 -23.9% PASS
total/synth/SWAP15/s0 1.82 1.07 -41.0% PASS
total/synth/SWAP16/s0 1.82 1.09 -40.3% PASS
total/synth/SWAP2/s0 1.80 1.08 -40.2% PASS
total/synth/SWAP3/s0 1.80 1.08 -40.1% PASS
total/synth/SWAP4/s0 1.80 1.08 -40.2% PASS
total/synth/SWAP5/s0 1.81 1.08 -40.3% PASS
total/synth/SWAP6/s0 1.81 1.08 -40.4% PASS
total/synth/SWAP7/s0 1.67 1.08 -35.3% PASS
total/synth/SWAP8/s0 1.81 1.08 -40.4% PASS
total/synth/SWAP9/s0 1.81 1.08 -40.3% PASS
total/synth/XOR/b0 1.55 0.99 -35.9% PASS
total/synth/XOR/b1 1.55 0.98 -37.0% PASS
total/synth/loop_v1 4.81 3.60 -25.2% PASS
total/synth/loop_v2 4.80 3.71 -22.8% 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.55 1.37 -12.0% PASS
total/main/blake2b_huff/empty 0.07 0.06 -13.0% PASS
total/main/blake2b_shifts/8415nulls 6.43 5.98 -7.1% PASS
total/main/sha1_divs/5311 3.53 2.40 -32.1% PASS
total/main/sha1_divs/empty 0.05 0.04 -25.3% PASS
total/main/sha1_shifts/5311 3.71 3.46 -6.7% PASS
total/main/sha1_shifts/empty 0.05 0.05 -7.5% PASS
total/main/snailtracer/benchmark 58.39 47.93 -17.9% PASS
total/main/structarray_alloc/nfts_rank 0.30 0.32 +5.6% PASS
total/main/swap_math/insufficient_liquidity 0.02 0.02 -15.3% PASS
total/main/swap_math/received 0.02 0.02 -18.0% PASS
total/main/swap_math/spent 0.02 0.02 -17.5% PASS
total/main/weierstrudel/1 0.36 0.30 -17.2% PASS
total/main/weierstrudel/15 3.24 2.59 -20.2% PASS
total/micro/JUMPDEST_n0/empty 0.13 0.13 -5.8% PASS
total/micro/jump_around/empty 0.63 0.54 -14.3% PASS
total/micro/loop_with_many_jumpdests/empty 1.97 1.98 +0.8% PASS
total/micro/memory_grow_mload/by1 0.19 0.18 -3.0% PASS
total/micro/memory_grow_mload/by16 0.20 0.19 -2.4% PASS
total/micro/memory_grow_mload/by32 0.22 0.21 -3.6% PASS
total/micro/memory_grow_mload/nogrow 0.19 0.18 -4.5% PASS
total/micro/memory_grow_mstore/by1 0.19 0.18 -3.9% PASS
total/micro/memory_grow_mstore/by16 0.20 0.19 -4.7% PASS
total/micro/memory_grow_mstore/by32 0.25 0.20 -17.1% PASS
total/micro/memory_grow_mstore/nogrow 0.19 0.18 -6.2% PASS
total/micro/signextend/one 0.36 0.36 +0.6% PASS
total/micro/signextend/zero 0.35 0.36 +1.2% PASS
total/synth/ADD/b0 0.01 0.01 -2.6% PASS
total/synth/ADD/b1 0.01 0.01 -7.3% PASS
total/synth/ADDRESS/a0 0.16 0.16 +0.3% PASS
total/synth/ADDRESS/a1 0.16 0.16 +0.2% PASS
total/synth/AND/b0 0.01 0.01 -2.5% PASS
total/synth/AND/b1 0.01 0.01 -7.3% PASS
total/synth/BYTE/b0 1.95 2.72 +39.4% PASS
total/synth/BYTE/b1 2.29 2.72 +18.8% PASS
total/synth/CALLDATASIZE/a0 0.08 0.09 +3.9% PASS
total/synth/CALLDATASIZE/a1 0.08 0.09 +4.0% PASS
total/synth/CALLER/a0 0.16 0.16 +0.2% PASS
total/synth/CALLER/a1 0.16 0.16 +0.2% PASS
total/synth/CALLVALUE/a0 0.28 0.26 -6.8% PASS
total/synth/CALLVALUE/a1 0.28 0.31 +13.5% PASS
total/synth/CODESIZE/a0 0.08 0.09 +6.3% PASS
total/synth/CODESIZE/a1 0.08 0.09 +6.7% PASS
total/synth/DUP1/d0 0.01 0.01 -1.6% PASS
total/synth/DUP1/d1 0.01 0.01 -7.4% PASS
total/synth/DUP10/d0 0.01 0.01 -3.4% PASS
total/synth/DUP10/d1 0.01 0.01 -7.2% PASS
total/synth/DUP11/d0 0.01 0.01 +0.5% PASS
total/synth/DUP11/d1 0.01 0.01 -7.3% PASS
total/synth/DUP12/d0 0.01 0.01 -0.6% PASS
total/synth/DUP12/d1 0.01 0.01 -7.3% PASS
total/synth/DUP13/d0 0.01 0.01 -1.8% PASS
total/synth/DUP13/d1 0.01 0.01 -7.3% PASS
total/synth/DUP14/d0 0.01 0.01 -0.7% PASS
total/synth/DUP14/d1 0.01 0.01 -7.4% PASS
total/synth/DUP15/d0 0.01 0.01 -3.6% PASS
total/synth/DUP15/d1 0.01 0.01 -7.3% PASS
total/synth/DUP16/d0 0.01 0.01 -2.6% PASS
total/synth/DUP16/d1 0.01 0.01 -7.7% PASS
total/synth/DUP2/d0 0.01 0.01 -2.7% PASS
total/synth/DUP2/d1 0.01 0.01 -7.4% PASS
total/synth/DUP3/d0 0.01 0.01 -1.8% PASS
total/synth/DUP3/d1 0.01 0.01 -7.2% PASS
total/synth/DUP4/d0 0.01 0.01 -0.4% PASS
total/synth/DUP4/d1 0.01 0.01 -7.3% PASS
total/synth/DUP5/d0 0.01 0.01 +4.0% PASS
total/synth/DUP5/d1 0.01 0.01 -7.3% PASS
total/synth/DUP6/d0 0.01 0.01 -0.1% PASS
total/synth/DUP6/d1 0.01 0.01 -7.4% PASS
total/synth/DUP7/d0 0.01 0.01 -3.2% PASS
total/synth/DUP7/d1 0.01 0.01 -7.3% PASS
total/synth/DUP8/d0 0.01 0.01 -3.5% PASS
total/synth/DUP8/d1 0.01 0.01 -7.3% PASS
total/synth/DUP9/d0 0.01 0.01 -1.0% PASS
total/synth/DUP9/d1 0.01 0.01 -7.4% PASS
total/synth/EQ/b0 0.01 0.01 -2.5% PASS
total/synth/EQ/b1 0.01 0.01 -7.4% PASS
total/synth/GAS/a0 0.79 0.53 -33.2% PASS
total/synth/GAS/a1 0.79 0.53 -33.1% PASS
total/synth/GT/b0 0.01 0.01 -2.6% PASS
total/synth/GT/b1 0.01 0.01 -7.3% PASS
total/synth/ISZERO/u0 0.01 0.01 -8.6% PASS
total/synth/JUMPDEST/n0 0.13 0.13 -5.5% PASS
total/synth/LT/b0 0.01 0.01 -2.5% PASS
total/synth/LT/b1 0.01 0.01 -7.4% PASS
total/synth/MSIZE/a0 0.01 0.01 -10.2% PASS
total/synth/MSIZE/a1 0.01 0.01 -10.7% PASS
total/synth/MUL/b0 0.01 0.01 -2.5% PASS
total/synth/MUL/b1 0.01 0.01 -7.3% PASS
total/synth/NOT/u0 0.01 0.01 -8.3% PASS
total/synth/OR/b0 0.01 0.01 -2.5% PASS
total/synth/OR/b1 0.01 0.01 -7.4% PASS
total/synth/PC/a0 0.01 0.01 -10.6% PASS
total/synth/PC/a1 0.01 0.01 -10.5% PASS
total/synth/PUSH1/p0 0.01 0.01 -8.5% PASS
total/synth/PUSH1/p1 0.01 0.01 -8.7% PASS
total/synth/PUSH10/p0 0.01 0.01 -9.5% PASS
total/synth/PUSH10/p1 0.01 0.01 -8.5% PASS
total/synth/PUSH11/p0 0.01 0.01 -8.3% PASS
total/synth/PUSH11/p1 0.01 0.01 -8.8% PASS
total/synth/PUSH12/p0 0.01 0.01 -8.4% PASS
total/synth/PUSH12/p1 0.01 0.01 -9.6% PASS
total/synth/PUSH13/p0 0.01 0.01 -8.5% PASS
total/synth/PUSH13/p1 0.01 0.01 -8.6% PASS
total/synth/PUSH14/p0 0.01 0.01 -8.5% PASS
total/synth/PUSH14/p1 0.01 0.01 -8.6% PASS
total/synth/PUSH15/p0 0.01 0.01 -8.4% PASS
total/synth/PUSH15/p1 0.01 0.01 -8.5% PASS
total/synth/PUSH16/p0 0.01 0.01 -8.5% PASS
total/synth/PUSH16/p1 0.01 0.01 -8.8% PASS
total/synth/PUSH17/p0 0.01 0.01 -8.5% PASS
total/synth/PUSH17/p1 0.01 0.01 -8.7% PASS
total/synth/PUSH18/p0 0.01 0.01 -8.5% PASS
total/synth/PUSH18/p1 0.01 0.01 -8.6% PASS
total/synth/PUSH19/p0 0.01 0.01 -9.3% PASS
total/synth/PUSH19/p1 0.01 0.01 -8.8% PASS
total/synth/PUSH2/p0 0.01 0.01 -8.4% PASS
total/synth/PUSH2/p1 0.01 0.01 -8.6% PASS
total/synth/PUSH20/p0 0.01 0.01 -8.6% PASS
total/synth/PUSH20/p1 0.01 0.01 -8.6% PASS
total/synth/PUSH21/p0 0.01 0.01 -8.5% PASS
total/synth/PUSH21/p1 0.01 0.01 -8.5% PASS
total/synth/PUSH22/p0 1.32 0.90 -32.1% PASS
total/synth/PUSH22/p1 1.13 0.94 -17.5% PASS
total/synth/PUSH23/p0 1.24 0.90 -27.1% PASS
total/synth/PUSH23/p1 1.14 0.98 -14.1% PASS
total/synth/PUSH24/p0 1.32 0.91 -31.5% PASS
total/synth/PUSH24/p1 1.18 0.95 -19.9% PASS
total/synth/PUSH25/p0 1.32 0.91 -31.4% PASS
total/synth/PUSH25/p1 1.12 0.94 -16.5% PASS
total/synth/PUSH26/p0 1.17 0.91 -22.7% PASS
total/synth/PUSH26/p1 1.15 0.95 -16.9% PASS
total/synth/PUSH27/p0 1.32 0.91 -31.5% PASS
total/synth/PUSH27/p1 1.16 0.94 -18.9% PASS
total/synth/PUSH28/p0 1.32 0.91 -31.3% PASS
total/synth/PUSH28/p1 1.15 0.94 -18.6% PASS
total/synth/PUSH29/p0 1.30 0.91 -29.9% PASS
total/synth/PUSH29/p1 1.13 0.95 -16.1% PASS
total/synth/PUSH3/p0 0.01 0.01 -8.4% PASS
total/synth/PUSH3/p1 0.01 0.01 -9.0% PASS
total/synth/PUSH30/p0 1.35 0.92 -31.5% PASS
total/synth/PUSH30/p1 1.17 0.96 -17.9% PASS
total/synth/PUSH31/p0 1.32 0.91 -31.2% PASS
total/synth/PUSH31/p1 1.27 0.95 -25.2% PASS
total/synth/PUSH32/p0 1.32 0.91 -31.1% PASS
total/synth/PUSH32/p1 1.13 0.95 -15.6% PASS
total/synth/PUSH4/p0 0.01 0.01 -8.6% PASS
total/synth/PUSH4/p1 0.01 0.01 -8.5% PASS
total/synth/PUSH5/p0 0.01 0.01 -8.5% PASS
total/synth/PUSH5/p1 0.01 0.01 -8.7% PASS
total/synth/PUSH6/p0 0.01 0.01 -8.5% PASS
total/synth/PUSH6/p1 0.01 0.01 -8.7% PASS
total/synth/PUSH7/p0 0.01 0.01 -8.5% PASS
total/synth/PUSH7/p1 0.01 0.01 -8.6% PASS
total/synth/PUSH8/p0 0.01 0.01 -8.5% PASS
total/synth/PUSH8/p1 0.01 0.01 -8.7% PASS
total/synth/PUSH9/p0 0.01 0.01 -8.4% PASS
total/synth/PUSH9/p1 0.01 0.01 -8.3% PASS
total/synth/RETURNDATASIZE/a0 0.53 1.34 +155.3% PASS
total/synth/RETURNDATASIZE/a1 0.49 1.32 +169.2% PASS
total/synth/SAR/b0 3.79 3.84 +1.3% PASS
total/synth/SAR/b1 4.29 4.12 -4.0% PASS
total/synth/SGT/b0 0.01 0.01 -2.5% PASS
total/synth/SGT/b1 0.01 0.01 -7.4% PASS
total/synth/SHL/b0 3.06 2.85 -6.9% PASS
total/synth/SHL/b1 1.69 1.28 -24.5% PASS
total/synth/SHR/b0 3.10 2.76 -10.8% PASS
total/synth/SHR/b1 1.61 1.24 -23.0% PASS
total/synth/SIGNEXTEND/b0 3.15 2.98 -5.3% PASS
total/synth/SIGNEXTEND/b1 3.29 3.23 -1.7% PASS
total/synth/SLT/b0 0.01 0.01 -2.5% PASS
total/synth/SLT/b1 0.01 0.01 -7.5% PASS
total/synth/SUB/b0 0.01 0.01 -2.4% PASS
total/synth/SUB/b1 0.01 0.01 -7.4% PASS
total/synth/SWAP1/s0 0.01 0.01 -10.4% PASS
total/synth/SWAP10/s0 0.01 0.01 -10.4% PASS
total/synth/SWAP11/s0 0.01 0.01 -10.2% PASS
total/synth/SWAP12/s0 0.01 0.01 -10.4% PASS
total/synth/SWAP13/s0 0.01 0.01 -10.3% PASS
total/synth/SWAP14/s0 0.01 0.01 -10.3% PASS
total/synth/SWAP15/s0 0.01 0.01 -10.4% PASS
total/synth/SWAP16/s0 0.01 0.01 -10.4% PASS
total/synth/SWAP2/s0 0.01 0.01 -10.1% PASS
total/synth/SWAP3/s0 0.01 0.01 -10.2% PASS
total/synth/SWAP4/s0 0.01 0.01 -10.3% PASS
total/synth/SWAP5/s0 0.01 0.01 -10.2% PASS
total/synth/SWAP6/s0 0.01 0.01 -10.3% PASS
total/synth/SWAP7/s0 0.01 0.01 -10.3% PASS
total/synth/SWAP8/s0 0.01 0.01 -10.3% PASS
total/synth/SWAP9/s0 0.01 0.01 -10.3% PASS
total/synth/XOR/b0 0.01 0.01 -2.5% PASS
total/synth/XOR/b1 0.01 0.01 -7.4% PASS
total/synth/loop_v1 1.41 1.35 -4.2% PASS
total/synth/loop_v2 1.41 1.27 -10.2% PASS

Summary: 194 benchmarks, 0 regressions


@zoowii zoowii requested a review from Copilot March 19, 2026 10:43
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces an EVM “lazy compile” mode (implemented as Multipass JIT + a lazy flag) and adds initial infrastructure for segment-based compilation (bytecode segmentation at JUMPDEST boundaries), along with basic unit tests.

Changes:

  • Add lazy mode handling and a new DTVM_EVM_LAZY_COMPILE env var / enable_lazy_compile EVMC option to toggle Multipass lazy compilation.
  • Implement LazyEVMJITCompiler + EVMSegmentAnalyzer scaffolding and wire lazy compilation into performEVMJITCompile().
  • Add a new evmLazyCompileTests test target with segment analyzer coverage.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/vm/dt_evmc_vm.cpp Adds lazy mode parsing and env/option toggles for lazy compilation.
src/action/compiler.cpp Switches Multipass+lazy EVM compilation path to use LazyEVMJITCompiler.
src/runtime/evm_module.h Adds storage/accessors for a per-module LazyEVMJITCompiler.
src/runtime/evm_module.cpp Implements EVMModule::newLazyEVMJITCompiler().
src/compiler/evm_lazy_compiler.h Declares EVMSegmentAnalyzer and LazyEVMJITCompiler APIs.
src/compiler/evm_lazy_compiler.cpp Implements segment analysis and the initial “lazy” compiler behavior.
src/compiler/CMakeLists.txt Adds evm_lazy_compiler.cpp to the compiler library sources.
src/tests/evm_lazy_compile_tests.cpp Adds unit tests for the segment analyzer.
src/tests/CMakeLists.txt Builds/runs the new evmLazyCompileTests when Multipass JIT is enabled.

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

Comment on lines +163 to +168
// Bytecode: JUMPDEST at PC=3
std::vector<uint8_t> Bytecode = {
0x60, 0x01, // PUSH1 0x01 (PC 0-1)
0x60, 0x02, // PUSH1 0x02 (PC 2-3)
0x5B, // JUMPDEST (PC 4)
0x00 // STOP (PC 5)
Comment on lines +212 to +226
if (SegmentIdx < SegmentAnalyzer.getNumSegments() &&
CompileStatuses[SegmentIdx] == CompileStatus::Done) {
return SegmentCodePtrs[SegmentIdx];
}

// Fallback: compile the full bytecode if not yet done
std::lock_guard<std::mutex> Lock(ForegroundMutex);

auto Timer =
Stats.startRecord(zen::utils::StatisticPhase::JITLazyFgCompilation);

// Re-check after acquiring lock
if (CompileStatuses[SegmentIdx] == CompileStatus::Done) {
Stats.stopRecord(Timer);
return SegmentCodePtrs[SegmentIdx];
Comment on lines +61 to +78
/// Instead of compiling the entire EVM bytecode at once, this compiler:
/// 1. Analyzes the bytecode to identify segments (JUMPDEST boundaries)
/// 2. Compiles only the entry segment initially
/// 3. Creates stubs for other segments that trigger on-demand compilation
/// 4. Optionally dispatches background compilation tasks for remaining segments
///
/// This is analogous to the WASM LazyJITCompiler but adapted for EVM's
/// single-function, segment-based structure.
class LazyEVMJITCompiler final : public EVMJITCompiler {
public:
LazyEVMJITCompiler(runtime::EVMModule *EVMMod);
~LazyEVMJITCompiler() override;

/// Perform initial precompilation:
/// - Analyze bytecode into segments
/// - Compile the entry segment eagerly
/// - Create stubs for remaining segments
/// - Optionally dispatch background compilation tasks
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces an initial implementation of an EVM “lazy compile” mode intended to support segment-based (JUMPDEST-delimited) compilation in the multipass JIT pipeline.

Changes:

  • Add lazy mode / EnableMultipassLazy toggles for the EVMC VM (options + env var).
  • Introduce EVMSegmentAnalyzer and LazyEVMJITCompiler, and wire it into EVM JIT compilation selection.
  • Add a new evmLazyCompileTests target with unit tests for the segment analyzer.

Reviewed changes

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

Show a summary per file
File Description
src/vm/dt_evmc_vm.cpp Adds EVMC options/env var to enable lazy compilation mode.
src/action/compiler.cpp Switches EVM multipass compilation to use LazyEVMJITCompiler when enabled.
src/runtime/evm_module.h Stores/exports a lazy EVM JIT compiler instance on EVMModule.
src/runtime/evm_module.cpp Implements EVMModule::newLazyEVMJITCompiler().
src/compiler/evm_lazy_compiler.h Declares segment analyzer + lazy EVM JIT compiler APIs.
src/compiler/evm_lazy_compiler.cpp Implements segment analysis and the lazy compiler (currently eager-full compile).
src/compiler/CMakeLists.txt Adds evm_lazy_compiler.cpp to compiler sources when EVM is enabled.
src/tests/evm_lazy_compile_tests.cpp Adds segment analyzer unit tests.
src/tests/CMakeLists.txt Builds/runs evmLazyCompileTests when ZEN_ENABLE_MULTIPASS_JIT is enabled.

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

Comment on lines 83 to 88
#ifdef ZEN_ENABLE_JIT
common::CodeMemPool JITCodeMemPool;
void *JITCode = nullptr;
size_t JITCodeSize = 0;
std::unique_ptr<COMPILER::LazyEVMJITCompiler> LazyJITCompiler;
#endif // ZEN_ENABLE_JIT
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

LazyJITCompiler is declared as std::unique_ptr<COMPILER::LazyEVMJITCompiler> under ZEN_ENABLE_JIT, but ZEN_ENABLE_JIT is defined for both singlepass and multipass builds (see src/CMakeLists.txt). In a singlepass-only build, LazyEVMJITCompiler is only forward-declared here, so EVMModule’s destructor will need a complete type and this will not compile. Guard the lazy-compiler member (and related APIs) with ZEN_ENABLE_MULTIPASS_JIT (or provide a custom deleter / include the full definition whenever ZEN_ENABLE_JIT is set).

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +69
/// Lazy JIT compiler for EVM bytecode.
///
/// Instead of compiling the entire EVM bytecode at once, this compiler:
/// 1. Analyzes the bytecode to identify segments (JUMPDEST boundaries)
/// 2. Compiles only the entry segment initially
/// 3. Creates stubs for other segments that trigger on-demand compilation
/// 4. Optionally dispatches background compilation tasks for remaining segments
///
/// This is analogous to the WASM LazyJITCompiler but adapted for EVM's
/// single-function, segment-based structure.
class LazyEVMJITCompiler final : public EVMJITCompiler {
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

The header comment describes stub-based, segment-level lazy compilation (entry-only compile + on-demand compilation for other segments), but precompile() currently compiles the full bytecode eagerly and marks all segments Done. Either update the documentation to match the current behavior or implement the described stub/on-demand compilation so callers aren’t misled about what “lazy compile” does.

Copilot uses AI. Check for mistakes.
Comment on lines +42 to +45
EVMSegmentAnalyzer Analyzer;
Analyzer.analyze(reinterpret_cast<const std::byte *>(Bytecode.data()),
Bytecode.size(), EVMC_OSAKA);

Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

These tests pass reinterpret_cast<const std::byte*> into APIs expecting zen::common::Byte*. This will break when ZEN_DISABLE_CXX17_STL is enabled (where zen::common::Byte is libcxx::byte, not std::byte). Prefer using reinterpret_cast<const zen::common::Byte*> (or a helper) so the tests compile in both configurations.

Copilot uses AI. Check for mistakes.
Comment on lines +237 to +241
EVMMod->setJITCodeAndSize(CodePtr, MainContext->CodeSize);

size_t CodeSize = CodeMPool.getMemEnd() - JITCode;
zen::platform::mprotect(JITCode, TO_MPROTECT_CODE_SIZE_LAZY(CodeSize),
PROT_READ | PROT_EXEC);
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

In the fallback path of compileSegmentOnRequest(), after mprotect() you never call EVMMod->setJITCodeAndSize(JITCode, CodeSize) (unlike precompile() and EagerEVMJITCompiler). This leaves JITCode/JITCodeSize potentially inconsistent (e.g., trap handler code-range checks) depending on what CodePtr/MainContext->CodeSize are. Consider mirroring the precompile()/eager sequence: set entry pointer/size if needed, then set module JIT code to the pool start + protected size.

Suggested change
EVMMod->setJITCodeAndSize(CodePtr, MainContext->CodeSize);
size_t CodeSize = CodeMPool.getMemEnd() - JITCode;
zen::platform::mprotect(JITCode, TO_MPROTECT_CODE_SIZE_LAZY(CodeSize),
PROT_READ | PROT_EXEC);
size_t CodeSize = CodeMPool.getMemEnd() - JITCode;
zen::platform::mprotect(JITCode, TO_MPROTECT_CODE_SIZE_LAZY(CodeSize),
PROT_READ | PROT_EXEC);
EVMMod->setJITCodeAndSize(JITCode, CodeSize);

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +67
COMPILER::LazyEVMJITCompiler *newLazyEVMJITCompiler();

COMPILER::LazyEVMJITCompiler *getLazyEVMJITCompiler() const {
return LazyJITCompiler.get();
}

bool hasLazyJITCompiler() const { return LazyJITCompiler != nullptr; }
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

newLazyEVMJITCompiler() is declared under ZEN_ENABLE_JIT but defined in evm_module.cpp only under ZEN_ENABLE_MULTIPASS_JIT. To avoid mismatched feature-guards (and the incomplete-type issue from the lazy compiler member), put this API under the same ZEN_ENABLE_MULTIPASS_JIT guard as its implementation (or provide a definition for all ZEN_ENABLE_JIT builds).

Copilot uses AI. Check for mistakes.
Comment on lines 232 to +238
} else if (std::strcmp(Value, "multipass") == 0) {
VM->Config.Mode = RunMode::MultipassMode;
return EVMC_SET_OPTION_SUCCESS;
} else if (std::strcmp(Value, "lazy") == 0) {
VM->Config.Mode = RunMode::MultipassMode;
VM->Config.EnableMultipassLazy = true;
return EVMC_SET_OPTION_SUCCESS;
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

Selecting mode=multipass does not clear EnableMultipassLazy. If the VM was previously configured with mode=lazy (or enable_lazy_compile=true), switching back to multipass will still run in lazy mode. Consider explicitly setting Config.EnableMultipassLazy = false in the multipass (and interpreter) branches so the mode option is self-contained and deterministic.

Copilot uses AI. Check for mistakes.
Comment on lines +209 to +227
uint8_t *LazyEVMJITCompiler::compileSegmentOnRequest(uint32_t SegmentIdx) {
// In the current implementation, all segments are compiled during precompile.
// This method is a placeholder for future segment-level lazy compilation.
if (SegmentIdx < SegmentAnalyzer.getNumSegments() &&
CompileStatuses[SegmentIdx] == CompileStatus::Done) {
return SegmentCodePtrs[SegmentIdx];
}

// Fallback: compile the full bytecode if not yet done
std::lock_guard<std::mutex> Lock(ForegroundMutex);

auto Timer =
Stats.startRecord(zen::utils::StatisticPhase::JITLazyFgCompilation);

// Re-check after acquiring lock
if (CompileStatuses[SegmentIdx] == CompileStatus::Done) {
Stats.stopRecord(Timer);
return SegmentCodePtrs[SegmentIdx];
}
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

compileSegmentOnRequest() can read out of bounds / dereference null: after the initial bounds check, the code unconditionally evaluates CompileStatuses[SegmentIdx] again (and later writes it) even when SegmentIdx >= getNumSegments() or when precompile() was never called (leaving CompileStatuses null). Add an early guard that validates SegmentIdx < getNumSegments() and that per-segment state has been initialized (or make this function unreachable before precompile()).

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +45
// Get instruction metrics for opcode length calculation
const auto *Metrics = evmc_get_instruction_metrics_table(Rev);
if (!Metrics) {
Metrics = evmc_get_instruction_metrics_table(zen::evm::DEFAULT_REVISION);
}

Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

Metrics is computed but never used. If opcode-length calculation only needs PUSH handling, remove the unused metrics lookup; otherwise, use Metrics to advance PC safely/consistently for non-PUSH opcodes.

Suggested change
// Get instruction metrics for opcode length calculation
const auto *Metrics = evmc_get_instruction_metrics_table(Rev);
if (!Metrics) {
Metrics = evmc_get_instruction_metrics_table(zen::evm::DEFAULT_REVISION);
}

Copilot uses AI. Check for mistakes.
Comment on lines +163 to +169
// Bytecode: JUMPDEST at PC=3
std::vector<uint8_t> Bytecode = {
0x60, 0x01, // PUSH1 0x01 (PC 0-1)
0x60, 0x02, // PUSH1 0x02 (PC 2-3)
0x5B, // JUMPDEST (PC 4)
0x00 // STOP (PC 5)
};
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

The comment says “JUMPDEST at PC=3” but with two PUSH1 instructions (2 bytes each) the JUMPDEST is actually at PC=4. Please fix the comment to match the bytecode layout to avoid confusion when maintaining these tests.

Copilot uses AI. Check for mistakes.
@ys8888john ys8888john force-pushed the evm-lazy-compile branch 2 times, most recently from 2c6c17f to ece08ab Compare March 23, 2026 03:51
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 5 comments.


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

Comment on lines +243 to +244
EVMMod->setJITCodeAndSize(EntryCodePtr, 0);

Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

precompile() sets EVMModule JIT code size to 0. The runtime/trap handlers use getJITCodeSize() to compute the JIT address range (e.g. for backtraces), so a zero size breaks that logic even though the entry stub is callable. Consider setting JITCode to the code pool start (or entry stub, if it is the pool start) and JITCodeSize to CodeMemPool.getMemEnd() - getMemStart() after stub/resolver allocation, and updating it again after the first compilation when the pool grows.

Suggested change
EVMMod->setJITCodeAndSize(EntryCodePtr, 0);
// Initialize JIT code size based on the code memory pool so that
// runtime/trap handlers see a valid JIT address range (for backtraces, etc.)
size_t JITSize = 0;
if (MainContext && MainContext->CodeMPool) {
void *MemStart = MainContext->CodeMPool->getMemStart();
void *MemEnd = MainContext->CodeMPool->getMemEnd();
if (MemStart && MemEnd && MemEnd > MemStart) {
auto *Start = static_cast<uint8_t *>(MemStart);
auto *End = static_cast<uint8_t *>(MemEnd);
JITSize = static_cast<size_t>(End - Start);
}
}
EVMMod->setJITCodeAndSize(EntryCodePtr, JITSize);

Copilot uses AI. Check for mistakes.
Comment on lines +248 to +266
uint8_t *LazyEVMJITCompiler::compileSegmentOnRequest(uint32_t SegmentIdx) {
uint32_t NumSegments = SegmentAnalyzer.getNumSegments();

// Fast path: already compiled
if (SegmentIdx < NumSegments &&
CompileStatuses[SegmentIdx] == CompileStatus::Done) {
return SegmentCodePtrs[SegmentIdx];
}

// Slow path: compile on demand with thread safety
std::lock_guard<std::mutex> Lock(ForegroundMutex);

auto Timer =
Stats.startRecord(zen::utils::StatisticPhase::JITLazyFgCompilation);

// Double-check after acquiring lock
if (CompileStatuses[SegmentIdx] == CompileStatus::Done) {
Stats.stopRecord(Timer);
return SegmentCodePtrs[SegmentIdx];
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

compileSegmentOnRequest() uses CompileStatuses[SegmentIdx] without validating SegmentIdx < NumSegments after taking the lock. If an invalid segment index is ever passed (e.g. corrupted stub address), this is out-of-bounds UB. Add a bounds check early in the slow path and return/abort with an error if SegmentIdx is invalid.

Copilot uses AI. Check for mistakes.
EnableLazy != nullptr) {
bool ParsedEnableLazy = false;
if (parseBoolEnvValue(EnableLazy, ParsedEnableLazy)) {
Config.EnableMultipassLazy = ParsedEnableLazy;
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

DTVM_EVM_LAZY_COMPILE can enable EnableMultipassLazy without switching Config.Mode to MultipassMode, even though the option comment says lazy compile requires multipass. To avoid confusing/partial configurations, consider forcing Config.Mode = RunMode::MultipassMode when the env var parses to true (mirroring set_option(enable_lazy_compile)).

Suggested change
Config.EnableMultipassLazy = ParsedEnableLazy;
Config.EnableMultipassLazy = ParsedEnableLazy;
if (ParsedEnableLazy) {
Config.Mode = RunMode::MultipassMode;
}

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +72
static uint64_t
compileSegmentOnRequestTrampoline([[maybe_unused]] zen::runtime::Instance *Inst,
uint8_t *NextStubCodePtr) {
// Validate instance pointer
if (!Inst) {
ZEN_LOG_ERROR("EVM lazy compile: Instance pointer is null in trampoline");
return 0;
}
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

In the trampoline error paths you return 0, but the stub resolver uses the return value as the re-entry address (it overwrites the saved RIP). Returning 0 will jump to address 0 and crash. If these conditions are truly impossible, prefer ZEN_ASSERT/ZEN_ABORT; otherwise return a valid failure stub/throw an exception so control flow can’t continue with a null target.

Copilot uses AI. Check for mistakes.
Comment on lines +60 to +70
uint32_t SegmentStartPC = 0;
uint32_t SegmentEndPC = static_cast<uint32_t>(BytecodeSize);
const EVMFrontendContext *EvmCtx =
dynamic_cast<const EVMFrontendContext *>(Ctx);
if (EvmCtx) {
SegmentStartPC = EvmCtx->getSegmentStartPC();
SegmentEndPC = EvmCtx->getSegmentEndPC();
if (SegmentEndPC == UINT32_MAX ||
SegmentEndPC > static_cast<uint32_t>(BytecodeSize)) {
SegmentEndPC = static_cast<uint32_t>(BytecodeSize);
}
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

dynamic_cast requires RTTI, but the project is built with -fno-rtti (see top-level CMakeLists). This will fail to compile. Since Ctx in the EVM JIT pipeline is an EVMFrontendContext, use a static_cast (or add an explicit kind/tag on CompileContext) instead of dynamic_cast.

Suggested change
uint32_t SegmentStartPC = 0;
uint32_t SegmentEndPC = static_cast<uint32_t>(BytecodeSize);
const EVMFrontendContext *EvmCtx =
dynamic_cast<const EVMFrontendContext *>(Ctx);
if (EvmCtx) {
SegmentStartPC = EvmCtx->getSegmentStartPC();
SegmentEndPC = EvmCtx->getSegmentEndPC();
if (SegmentEndPC == UINT32_MAX ||
SegmentEndPC > static_cast<uint32_t>(BytecodeSize)) {
SegmentEndPC = static_cast<uint32_t>(BytecodeSize);
}
const EVMFrontendContext *EvmCtx =
static_cast<const EVMFrontendContext *>(Ctx);
uint32_t SegmentStartPC = EvmCtx->getSegmentStartPC();
uint32_t SegmentEndPC = EvmCtx->getSegmentEndPC();
if (SegmentEndPC == UINT32_MAX ||
SegmentEndPC > static_cast<uint32_t>(BytecodeSize)) {
SegmentEndPC = static_cast<uint32_t>(BytecodeSize);

Copilot uses AI. Check for mistakes.
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