Skip to content

perf(evm): u256 arithmetic optimizations (shift/addmod/barrier/value-range/div-mod) - #458

Merged
zoowii merged 9 commits into
DTVMStack:mainfrom
abmcar:perf/u256-batch1
Apr 28, 2026
Merged

perf(evm): u256 arithmetic optimizations (shift/addmod/barrier/value-range/div-mod)#458
zoowii merged 9 commits into
DTVMStack:mainfrom
abmcar:perf/u256-batch1

Conversation

@abmcar

@abmcar abmcar commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Five u256 arithmetic optimizations plus one follow-up register-allocator fix, all contained in src/compiler/evm_frontend/evm_mir_compiler.{cpp,h}.

  • Shift strength reduction (1eb0df3, +14/-6) — In SHL/SHR/SAR dynamic-shift lowering, replace OP_udiv/OP_urem by 64 with OP_ushr 6 / OP_and 63. Avoids ~30-cycle x86 DIV.

  • ADDMOD inline fast path (ab69050, +227/-4) — For non-constant ADDMOD with mod[3] != 0 (modulus ≥ 2^192), inline intx::addmod at MIR level: normalize augend/addend against mod, add with overflow detection, subtract mod with borrow detection, select. Small moduli (mod[3] == 0) and mod == 0 still call the runtime.

  • Dead-carry barrier elimination (df7d5c5, +16/-9) — Remove protectUnsafeValue barriers where x86 CF is provably dead:

    • handleAddU64Const: drop barriers on RHS immediates (MOV imm never clobbers flags) and on the terminal ADC (no consumer). Intermediate ADC barriers are preserved to keep lowering ordered while CF is live.
    • handleSubU64Const: drop all barriers. This function uses explicit SUB + ICMP_ULT + zeroExtend borrow, never SBB, so CF is never live.
    • Generic u256 ADD/SUB chains in the header are intentionally unchanged.
  • Value-range narrowing (718dab0, +155/-7) — Add ValueRange{U64,U128,U256} on Operand. Range is auto-derived from constants and propagated through AND masks (narrow to smaller operand), BYTE (always U64), and comparisons (always U64). When both operands of ADD/MUL/DIV/MOD are provably U64, emit a single-instruction fast path instead of the 4-limb chain.

  • Inline u256 DIV/MOD (0c5afd8 + f4fc7ce, net +157/-42) — Replace the runtime fallback in handleDiv/handleMod with a runtime divisor-size check. Single-limb divisors (upper three limbs zero) use the existing cascading 128/64 division pattern inline; multi-limb divisors still call the runtime.

    The initial 5-block CFG (with a dedicated ZeroDivisorBB) tripped a live-range assertion (TheVNI != nullptr) during register allocation. f4fc7ce reworks it into a 3-block CFG matching handleDiv: a branchless guard SafeB0 = select(B[0] == 0, 1, B[0]) feeds the hardware DIV, then select(B[0] == 0, 0, result) zeros the output. This path is distinct from PR fix(compiler): fix live range calc assertion when no reaching def found #456's CgLiveRangeCalc::findReachingDefs fix.

Dropped

A Barrett-reduction MULMOD variant was prototyped but abandoned — both the inline and C-helper forms crashed the register coalescer on weierstrudel (1408 MULMOD call sites in one function) and showed no measurable win.

Benchmark

evmone-bench, multipass mode, current upstream/main baseline (HEAD ec3c9f9), 27 external/total/{main,micro} benches, 10 reps each, CPU-pinned cores 2-3, single session.

Suite-level geomean speedup: 1.0069 (+0.69%), 95% bootstrap CI [-1.80%, +3.44%] — within per-bench noise; not statistically distinguishable from parity at suite level.

Targeted wins on u256-arithmetic-heavy benches:

Benchmark Speedup
weierstrudel/15 +18.3%
weierstrudel/1 +13.5%
swap_math/received +9.7%
snailtracer/benchmark +7.9%
swap_math/spent +6.7%
swap_math/insufficient_liquidity +6.6%
structarray_alloc/nfts_rank +3.9%

Suite-level geomean is in the noise band because integer-arithmetic wins on JIT-bound micro benches have been largely absorbed by recent upstream work (#428 BMI2 lowering, #435 peephole, #395 SSA). No regressions outside per-bench noise (CV > 7% on bottom 3 benches).

Test plan

  • evmone-unittests multipass: 223/223
  • evmone-unittests interpreter: 215/215
  • evmone-statetest fork_Cancun: 2723/2723
  • tests/evm_asm/addmod_fastpath_*: 3 new boundary fixtures (multipass + interpreter both 6/6)
  • CI green (16/16, post-rebase HEAD 66235af)

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings April 10, 2026 08:14
@abmcar
abmcar marked this pull request as draft April 10, 2026 08:18

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

This PR improves EVM u256 arithmetic codegen in the MIR compiler by applying several strength reductions and inlining a large-modulus ADDMOD fast path to reduce runtime calls and expensive arithmetic sequences.

Changes:

  • Strength-reduce dynamic u256 shift lowering by replacing / 64 and % 64 with >> 6 and & 63 in SHL/SHR/SAR paths.
  • Inline an ADDMOD fast path at MIR level for large moduli (based on the high-limb guard), with a runtime-call slow path for other cases (including mod == 0).
  • Remove unnecessary protectUnsafeValue barriers in select u64-const ADD/SUB fast paths where carry/borrow flags are proven dead.

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

Comment thread src/compiler/evm_frontend/evm_mir_compiler.cpp Outdated
Comment thread src/compiler/evm_frontend/evm_mir_compiler.cpp Outdated
@github-actions

github-actions Bot commented Apr 10, 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 1.39 1.55 +11.4% PASS
total/main/blake2b_huff/empty 0.02 0.02 +11.2% PASS
total/main/blake2b_shifts/8415nulls 9.89 11.95 +20.8% PASS
total/main/sha1_divs/5311 4.43 5.19 +17.1% PASS
total/main/sha1_divs/empty 0.05 0.06 +22.2% PASS
total/main/sha1_shifts/5311 2.34 3.11 +33.2% PASS
total/main/sha1_shifts/empty 0.03 0.04 +31.7% PASS
total/main/snailtracer/benchmark 48.73 54.90 +12.7% PASS
total/main/structarray_alloc/nfts_rank 0.89 1.06 +19.0% PASS
total/main/swap_math/insufficient_liquidity 0.00 0.00 +29.5% PASS
total/main/swap_math/received 0.00 0.00 +32.8% PASS
total/main/swap_math/spent 0.00 0.00 +30.8% PASS
total/main/weierstrudel/1 0.23 0.28 +21.4% PASS
total/main/weierstrudel/15 2.51 3.10 +23.4% PASS
total/micro/JUMPDEST_n0/empty 1.36 1.47 +8.0% PASS
total/micro/jump_around/empty 0.04 0.09 +134.1% PASS
total/micro/loop_with_many_jumpdests/empty 20.73 22.44 +8.2% PASS
total/micro/memory_grow_mload/by1 0.09 0.10 +19.6% PASS
total/micro/memory_grow_mload/by16 0.09 0.11 +18.1% PASS
total/micro/memory_grow_mload/by32 0.10 0.12 +15.9% PASS
total/micro/memory_grow_mload/nogrow 0.09 0.10 +18.1% PASS
total/micro/memory_grow_mstore/by1 0.09 0.11 +22.8% PASS
total/micro/memory_grow_mstore/by16 0.10 0.11 +17.4% PASS
total/micro/memory_grow_mstore/by32 0.10 0.12 +17.1% PASS
total/micro/memory_grow_mstore/nogrow 0.08 0.10 +22.6% PASS
total/micro/signextend/one 0.24 0.24 +1.0% PASS
total/micro/signextend/zero 0.23 0.24 +3.8% PASS
total/synth/ADD/b0 1.42 1.95 +37.4% PASS
total/synth/ADD/b1 1.17 1.98 +68.9% PASS
total/synth/ADDRESS/a0 6.06 4.85 -19.9% PASS
total/synth/ADDRESS/a1 6.28 5.43 -13.5% PASS
total/synth/AND/b0 1.30 1.71 +31.6% PASS
total/synth/AND/b1 1.15 1.71 +48.8% PASS
total/synth/BYTE/b0 4.61 6.18 +34.0% PASS
total/synth/BYTE/b1 3.76 4.96 +32.1% PASS
total/synth/CALLDATASIZE/a0 3.22 3.51 +8.8% PASS
total/synth/CALLDATASIZE/a1 3.18 3.88 +21.9% PASS
total/synth/CALLER/a0 6.00 4.83 -19.5% PASS
total/synth/CALLER/a1 6.28 5.39 -14.2% PASS
total/synth/CALLVALUE/a0 3.45 3.75 +8.8% PASS
total/synth/CALLVALUE/a1 3.32 3.76 +13.5% PASS
total/synth/CODESIZE/a0 3.76 4.08 +8.3% PASS
total/synth/CODESIZE/a1 3.77 4.71 +24.9% PASS
total/synth/DUP1/d0 0.86 1.39 +62.6% PASS
total/synth/DUP1/d1 0.84 1.39 +66.2% PASS
total/synth/DUP10/d0 0.85 1.39 +63.3% PASS
total/synth/DUP10/d1 0.86 1.39 +62.1% PASS
total/synth/DUP11/d0 0.85 1.39 +63.9% PASS
total/synth/DUP11/d1 0.84 1.39 +66.3% PASS
total/synth/DUP12/d0 0.86 1.39 +62.6% PASS
total/synth/DUP12/d1 0.84 1.39 +66.1% PASS
total/synth/DUP13/d0 0.85 1.39 +64.2% PASS
total/synth/DUP13/d1 0.84 1.39 +66.4% PASS
total/synth/DUP14/d0 0.85 1.39 +64.2% PASS
total/synth/DUP14/d1 0.84 1.39 +66.3% PASS
total/synth/DUP15/d0 0.84 1.39 +65.8% PASS
total/synth/DUP15/d1 0.84 1.39 +66.0% PASS
total/synth/DUP16/d0 0.84 1.39 +66.4% PASS
total/synth/DUP16/d1 0.84 1.39 +66.3% PASS
total/synth/DUP2/d0 0.86 1.39 +62.7% PASS
total/synth/DUP2/d1 0.84 1.39 +66.3% PASS
total/synth/DUP3/d0 0.84 1.39 +65.2% PASS
total/synth/DUP3/d1 0.85 1.39 +64.2% PASS
total/synth/DUP4/d0 0.84 1.39 +64.5% PASS
total/synth/DUP4/d1 0.84 1.39 +66.4% PASS
total/synth/DUP5/d0 0.85 1.39 +64.0% PASS
total/synth/DUP5/d1 0.86 1.39 +62.3% PASS
total/synth/DUP6/d0 0.85 1.39 +63.2% PASS
total/synth/DUP6/d1 0.84 1.39 +66.4% PASS
total/synth/DUP7/d0 0.85 1.39 +64.5% PASS
total/synth/DUP7/d1 0.84 1.39 +66.1% PASS
total/synth/DUP8/d0 0.85 1.39 +64.2% PASS
total/synth/DUP8/d1 0.84 1.39 +65.5% PASS
total/synth/DUP9/d0 0.84 1.39 +64.9% PASS
total/synth/DUP9/d1 0.84 1.39 +65.2% PASS
total/synth/EQ/b0 2.30 2.73 +18.7% PASS
total/synth/EQ/b1 1.29 1.40 +8.2% PASS
total/synth/GAS/a0 3.61 3.83 +6.1% PASS
total/synth/GAS/a1 3.47 3.85 +11.0% PASS
total/synth/GT/b0 2.17 2.61 +20.3% PASS
total/synth/GT/b1 1.06 1.64 +54.1% PASS
total/synth/ISZERO/u0 1.33 1.47 +10.4% PASS
total/synth/JUMPDEST/n0 1.36 1.47 +7.8% PASS
total/synth/LT/b0 2.22 2.58 +16.3% PASS
total/synth/LT/b1 1.06 1.64 +53.9% PASS
total/synth/MSIZE/a0 4.15 4.23 +1.9% PASS
total/synth/MSIZE/a1 4.49 4.84 +7.8% PASS
total/synth/MUL/b0 4.43 5.31 +19.9% PASS
total/synth/MUL/b1 4.69 5.31 +13.1% PASS
total/synth/NOT/u0 1.11 1.84 +66.6% PASS
total/synth/OR/b0 1.31 1.65 +25.4% PASS
total/synth/OR/b1 1.14 1.71 +50.5% PASS
total/synth/PC/a0 3.14 3.67 +16.8% PASS
total/synth/PC/a1 3.17 3.68 +16.1% PASS
total/synth/PUSH1/p0 0.94 1.23 +31.2% PASS
total/synth/PUSH1/p1 1.04 1.41 +36.5% PASS
total/synth/PUSH10/p0 0.94 1.23 +30.3% PASS
total/synth/PUSH10/p1 1.06 1.42 +33.8% PASS
total/synth/PUSH11/p0 0.94 1.07 +13.3% PASS
total/synth/PUSH11/p1 1.06 1.47 +38.7% PASS
total/synth/PUSH12/p0 0.95 1.23 +29.3% PASS
total/synth/PUSH12/p1 1.06 1.44 +35.3% PASS
total/synth/PUSH13/p0 0.95 1.23 +30.0% PASS
total/synth/PUSH13/p1 1.06 1.42 +33.4% PASS
total/synth/PUSH14/p0 0.95 1.32 +39.3% PASS
total/synth/PUSH14/p1 1.06 1.42 +33.4% PASS
total/synth/PUSH15/p0 0.96 1.23 +28.7% PASS
total/synth/PUSH15/p1 1.06 1.51 +42.9% PASS
total/synth/PUSH16/p0 0.96 1.31 +36.4% PASS
total/synth/PUSH16/p1 1.07 1.42 +32.8% PASS
total/synth/PUSH17/p0 0.95 1.31 +37.5% PASS
total/synth/PUSH17/p1 1.07 1.42 +32.5% PASS
total/synth/PUSH18/p0 0.94 1.24 +32.2% PASS
total/synth/PUSH18/p1 1.07 1.42 +32.5% PASS
total/synth/PUSH19/p0 0.96 1.31 +37.3% PASS
total/synth/PUSH19/p1 1.07 1.42 +33.0% PASS
total/synth/PUSH2/p0 0.94 1.23 +31.4% PASS
total/synth/PUSH2/p1 1.04 1.42 +35.8% PASS
total/synth/PUSH20/p0 0.95 1.31 +38.1% PASS
total/synth/PUSH20/p1 1.08 1.42 +32.1% PASS
total/synth/PUSH21/p0 0.95 1.23 +29.0% PASS
total/synth/PUSH21/p1 1.07 1.42 +32.1% PASS
total/synth/PUSH22/p0 0.95 1.23 +29.7% PASS
total/synth/PUSH22/p1 1.08 1.43 +31.9% PASS
total/synth/PUSH23/p0 0.96 1.31 +36.6% PASS
total/synth/PUSH23/p1 1.08 1.49 +38.1% PASS
total/synth/PUSH24/p0 0.97 1.31 +35.8% PASS
total/synth/PUSH24/p1 1.08 1.45 +33.7% PASS
total/synth/PUSH25/p0 0.97 1.31 +35.4% PASS
total/synth/PUSH25/p1 1.09 1.42 +31.0% PASS
total/synth/PUSH26/p0 0.96 1.23 +27.7% PASS
total/synth/PUSH26/p1 1.08 1.44 +33.5% PASS
total/synth/PUSH27/p0 0.97 1.23 +27.2% PASS
total/synth/PUSH27/p1 1.09 1.46 +34.0% PASS
total/synth/PUSH28/p0 0.97 1.23 +26.9% PASS
total/synth/PUSH28/p1 1.10 1.45 +31.7% PASS
total/synth/PUSH29/p0 0.97 1.31 +35.1% PASS
total/synth/PUSH29/p1 1.10 1.42 +29.4% PASS
total/synth/PUSH3/p0 0.93 1.26 +35.9% PASS
total/synth/PUSH3/p1 1.04 1.42 +36.3% PASS
total/synth/PUSH30/p0 0.90 1.32 +45.4% PASS
total/synth/PUSH30/p1 1.10 1.43 +29.8% PASS
total/synth/PUSH31/p0 0.98 1.23 +25.6% PASS
total/synth/PUSH31/p1 1.09 1.61 +48.1% PASS
total/synth/PUSH32/p0 0.97 1.07 +10.6% PASS
total/synth/PUSH32/p1 1.09 1.42 +30.2% PASS
total/synth/PUSH4/p0 0.93 1.23 +31.7% PASS
total/synth/PUSH4/p1 1.05 1.42 +35.1% PASS
total/synth/PUSH5/p0 0.93 1.23 +31.9% PASS
total/synth/PUSH5/p1 1.05 1.42 +35.4% PASS
total/synth/PUSH6/p0 0.94 1.23 +31.1% PASS
total/synth/PUSH6/p1 1.05 1.42 +35.0% PASS
total/synth/PUSH7/p0 0.94 1.23 +30.5% PASS
total/synth/PUSH7/p1 1.04 1.42 +36.5% PASS
total/synth/PUSH8/p0 0.94 1.23 +30.9% PASS
total/synth/PUSH8/p1 1.06 1.45 +37.0% PASS
total/synth/PUSH9/p0 0.94 1.23 +30.8% PASS
total/synth/PUSH9/p1 1.05 1.43 +35.2% PASS
total/synth/RETURNDATASIZE/a0 3.84 4.16 +8.2% PASS
total/synth/RETURNDATASIZE/a1 3.84 4.18 +8.8% PASS
total/synth/SAR/b0 3.71 3.96 +6.8% PASS
total/synth/SAR/b1 3.94 4.37 +10.8% PASS
total/synth/SGT/b0 2.19 2.59 +18.4% PASS
total/synth/SGT/b1 1.08 1.64 +51.2% PASS
total/synth/SHL/b0 2.82 3.03 +7.6% PASS
total/synth/SHL/b1 1.31 1.67 +27.9% PASS
total/synth/SHR/b0 2.82 2.95 +4.6% PASS
total/synth/SHR/b1 1.27 1.68 +32.4% PASS
total/synth/SIGNEXTEND/b0 3.27 3.75 +14.9% PASS
total/synth/SIGNEXTEND/b1 3.26 3.81 +16.8% PASS
total/synth/SLT/b0 2.20 2.59 +17.7% PASS
total/synth/SLT/b1 1.06 1.64 +53.9% PASS
total/synth/SUB/b0 1.42 1.97 +38.4% PASS
total/synth/SUB/b1 1.17 2.00 +71.2% PASS
total/synth/SWAP1/s0 1.10 1.49 +35.9% PASS
total/synth/SWAP10/s0 1.10 1.50 +36.7% PASS
total/synth/SWAP11/s0 1.10 1.51 +37.1% PASS
total/synth/SWAP12/s0 1.10 1.51 +37.8% PASS
total/synth/SWAP13/s0 1.10 1.52 +37.9% PASS
total/synth/SWAP14/s0 1.10 1.52 +37.8% PASS
total/synth/SWAP15/s0 1.09 1.52 +39.2% PASS
total/synth/SWAP16/s0 1.09 1.52 +39.6% PASS
total/synth/SWAP2/s0 1.10 1.49 +36.0% PASS
total/synth/SWAP3/s0 1.10 1.49 +35.6% PASS
total/synth/SWAP4/s0 1.10 1.50 +36.2% PASS
total/synth/SWAP5/s0 1.10 1.54 +39.4% PASS
total/synth/SWAP6/s0 1.10 1.51 +36.6% PASS
total/synth/SWAP7/s0 1.10 1.51 +37.5% PASS
total/synth/SWAP8/s0 1.11 1.50 +35.6% PASS
total/synth/SWAP9/s0 1.11 1.51 +36.7% PASS
total/synth/XOR/b0 1.09 1.55 +42.2% PASS
total/synth/XOR/b1 1.03 1.55 +50.9% PASS
total/synth/loop_v1 3.49 4.39 +25.9% PASS
total/synth/loop_v2 3.49 4.36 +24.9% 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.08 0.84 -22.8% PASS
total/main/blake2b_huff/empty 0.02 0.01 -19.7% PASS
total/main/blake2b_shifts/8415nulls 5.09 4.59 -9.9% PASS
total/main/sha1_divs/5311 0.63 0.59 -6.4% PASS
total/main/sha1_divs/empty 0.01 0.01 -7.3% PASS
total/main/sha1_shifts/5311 0.58 0.55 -4.8% PASS
total/main/sha1_shifts/empty 0.01 0.01 -6.2% PASS
total/main/snailtracer/benchmark 36.26 31.32 -13.6% PASS
total/main/structarray_alloc/nfts_rank 0.31 0.31 -2.9% PASS
total/main/swap_math/insufficient_liquidity 0.00 0.00 -6.9% PASS
total/main/swap_math/received 0.00 0.00 -7.5% PASS
total/main/swap_math/spent 0.00 0.00 -8.0% PASS
total/main/weierstrudel/1 0.26 0.24 -6.2% PASS
total/main/weierstrudel/15 2.95 2.59 -12.3% PASS
total/micro/JUMPDEST_n0/empty 0.00 0.00 -5.8% PASS
total/micro/jump_around/empty 0.03 0.05 +48.1% PASS
total/micro/loop_with_many_jumpdests/empty 0.00 0.00 +0.2% PASS
total/micro/memory_grow_mload/by1 0.01 0.01 -8.8% PASS
total/micro/memory_grow_mload/by16 0.01 0.01 -7.7% PASS
total/micro/memory_grow_mload/by32 0.01 0.01 -5.2% PASS
total/micro/memory_grow_mload/nogrow 0.01 0.01 -8.6% PASS
total/micro/memory_grow_mstore/by1 0.02 0.01 -9.2% PASS
total/micro/memory_grow_mstore/by16 0.02 0.02 -8.1% PASS
total/micro/memory_grow_mstore/by32 0.02 0.02 -5.4% PASS
total/micro/memory_grow_mstore/nogrow 0.02 0.01 -8.7% PASS
total/micro/signextend/one 0.08 0.07 -13.0% PASS
total/micro/signextend/zero 0.08 0.07 -12.4% PASS
total/synth/ADD/b0 0.00 0.00 -7.8% PASS
total/synth/ADD/b1 0.00 0.00 -7.5% PASS
total/synth/ADDRESS/a0 0.14 0.15 +5.5% PASS
total/synth/ADDRESS/a1 0.14 0.15 +5.6% PASS
total/synth/AND/b0 0.00 0.00 -7.5% PASS
total/synth/AND/b1 0.00 0.00 -7.9% PASS
total/synth/BYTE/b0 0.00 0.00 -7.6% PASS
total/synth/BYTE/b1 0.00 0.00 -7.9% PASS
total/synth/CALLDATASIZE/a0 0.07 0.07 -1.6% PASS
total/synth/CALLDATASIZE/a1 0.07 0.07 -1.6% PASS
total/synth/CALLER/a0 0.19 0.18 -6.8% PASS
total/synth/CALLER/a1 0.19 0.18 -6.8% PASS
total/synth/CALLVALUE/a0 0.30 0.26 -11.3% PASS
total/synth/CALLVALUE/a1 0.30 0.26 -11.3% PASS
total/synth/CODESIZE/a0 0.07 0.07 -1.7% PASS
total/synth/CODESIZE/a1 0.07 0.07 -1.6% PASS
total/synth/DUP1/d0 0.00 0.00 -7.4% PASS
total/synth/DUP1/d1 0.00 0.00 -7.8% PASS
total/synth/DUP10/d0 0.00 0.00 -7.2% PASS
total/synth/DUP10/d1 0.00 0.00 -7.6% PASS
total/synth/DUP11/d0 0.00 0.00 -9.0% PASS
total/synth/DUP11/d1 0.00 0.00 -7.7% PASS
total/synth/DUP12/d0 0.00 0.00 -7.6% PASS
total/synth/DUP12/d1 0.00 0.00 -7.3% PASS
total/synth/DUP13/d0 0.00 0.00 -8.0% PASS
total/synth/DUP13/d1 0.00 0.00 -7.4% PASS
total/synth/DUP14/d0 0.00 0.00 -7.5% PASS
total/synth/DUP14/d1 0.00 0.00 -7.2% PASS
total/synth/DUP15/d0 0.00 0.00 -7.1% PASS
total/synth/DUP15/d1 0.00 0.00 -7.8% PASS
total/synth/DUP16/d0 0.00 0.00 -7.7% PASS
total/synth/DUP16/d1 0.00 0.00 -7.4% PASS
total/synth/DUP2/d0 0.00 0.00 -7.4% PASS
total/synth/DUP2/d1 0.00 0.00 -7.5% PASS
total/synth/DUP3/d0 0.00 0.00 -7.7% PASS
total/synth/DUP3/d1 0.00 0.00 -7.6% PASS
total/synth/DUP4/d0 0.00 0.00 -7.5% PASS
total/synth/DUP4/d1 0.00 0.00 -7.7% PASS
total/synth/DUP5/d0 0.00 0.00 -7.8% PASS
total/synth/DUP5/d1 0.00 0.00 -7.9% PASS
total/synth/DUP6/d0 0.00 0.00 -7.5% PASS
total/synth/DUP6/d1 0.00 0.00 -7.5% PASS
total/synth/DUP7/d0 0.00 0.00 -7.8% PASS
total/synth/DUP7/d1 0.00 0.00 -7.8% PASS
total/synth/DUP8/d0 0.00 0.00 -7.5% PASS
total/synth/DUP8/d1 0.00 0.00 -7.4% PASS
total/synth/DUP9/d0 0.00 0.00 -7.9% PASS
total/synth/DUP9/d1 0.00 0.00 -7.7% PASS
total/synth/EQ/b0 0.00 0.00 -7.7% PASS
total/synth/EQ/b1 0.00 0.00 -7.3% PASS
total/synth/GAS/a0 0.86 0.76 -11.5% PASS
total/synth/GAS/a1 0.86 0.76 -11.5% PASS
total/synth/GT/b0 0.00 0.00 -8.0% PASS
total/synth/GT/b1 0.00 0.00 -7.4% PASS
total/synth/ISZERO/u0 0.00 0.00 -7.3% PASS
total/synth/JUMPDEST/n0 0.00 0.00 -5.5% PASS
total/synth/LT/b0 0.00 0.00 -7.6% PASS
total/synth/LT/b1 0.00 0.00 -7.8% PASS
total/synth/MSIZE/a0 0.00 0.00 -7.5% PASS
total/synth/MSIZE/a1 0.00 0.00 -7.8% PASS
total/synth/MUL/b0 0.00 0.00 -7.7% PASS
total/synth/MUL/b1 0.00 0.00 -7.4% PASS
total/synth/NOT/u0 0.00 0.00 -7.7% PASS
total/synth/OR/b0 0.00 0.00 -7.5% PASS
total/synth/OR/b1 0.00 0.00 -7.6% PASS
total/synth/PC/a0 0.00 0.00 -7.8% PASS
total/synth/PC/a1 0.00 0.00 -7.3% PASS
total/synth/PUSH1/p0 0.00 0.00 -8.0% PASS
total/synth/PUSH1/p1 0.00 0.00 -7.9% PASS
total/synth/PUSH10/p0 0.00 0.00 -7.4% PASS
total/synth/PUSH10/p1 0.00 0.00 -7.5% PASS
total/synth/PUSH11/p0 0.00 0.00 -7.9% PASS
total/synth/PUSH11/p1 0.00 0.00 -7.8% PASS
total/synth/PUSH12/p0 0.00 0.00 -7.6% PASS
total/synth/PUSH12/p1 0.00 0.00 -7.7% PASS
total/synth/PUSH13/p0 0.00 0.00 -8.0% PASS
total/synth/PUSH13/p1 0.00 0.00 -7.7% PASS
total/synth/PUSH14/p0 0.00 0.00 -7.8% PASS
total/synth/PUSH14/p1 0.00 0.00 -7.6% PASS
total/synth/PUSH15/p0 0.00 0.00 -7.7% PASS
total/synth/PUSH15/p1 0.00 0.00 -8.0% PASS
total/synth/PUSH16/p0 0.00 0.00 -7.4% PASS
total/synth/PUSH16/p1 0.00 0.00 -7.7% PASS
total/synth/PUSH17/p0 0.00 0.00 -8.1% PASS
total/synth/PUSH17/p1 0.00 0.00 -7.8% PASS
total/synth/PUSH18/p0 0.00 0.00 -7.8% PASS
total/synth/PUSH18/p1 0.00 0.00 -7.8% PASS
total/synth/PUSH19/p0 0.00 0.00 -8.1% PASS
total/synth/PUSH19/p1 0.00 0.00 -8.1% PASS
total/synth/PUSH2/p0 0.00 0.00 -8.4% PASS
total/synth/PUSH2/p1 0.00 0.00 -7.6% PASS
total/synth/PUSH20/p0 0.00 0.00 -8.1% PASS
total/synth/PUSH20/p1 0.00 0.00 -7.3% PASS
total/synth/PUSH21/p0 0.00 0.00 -8.0% PASS
total/synth/PUSH21/p1 0.00 0.00 -7.6% PASS
total/synth/PUSH22/p0 0.93 1.31 +41.1% PASS
total/synth/PUSH22/p1 1.49 1.42 -4.2% PASS
total/synth/PUSH23/p0 0.93 1.31 +41.3% PASS
total/synth/PUSH23/p1 1.48 1.43 -3.6% PASS
total/synth/PUSH24/p0 0.93 1.31 +40.7% PASS
total/synth/PUSH24/p1 1.49 1.43 -4.0% PASS
total/synth/PUSH25/p0 0.94 1.31 +40.4% PASS
total/synth/PUSH25/p1 1.49 1.44 -3.2% PASS
total/synth/PUSH26/p0 1.12 1.07 -3.6% PASS
total/synth/PUSH26/p1 1.49 1.43 -4.0% PASS
total/synth/PUSH27/p0 0.93 1.31 +40.9% PASS
total/synth/PUSH27/p1 1.51 1.46 -3.0% PASS
total/synth/PUSH28/p0 0.93 1.31 +40.8% PASS
total/synth/PUSH28/p1 1.49 1.42 -4.5% PASS
total/synth/PUSH29/p0 0.93 1.31 +41.2% PASS
total/synth/PUSH29/p1 1.49 1.46 -2.3% PASS
total/synth/PUSH3/p0 0.00 0.00 -7.5% PASS
total/synth/PUSH3/p1 0.00 0.00 -8.0% PASS
total/synth/PUSH30/p0 0.96 1.33 +38.4% PASS
total/synth/PUSH30/p1 1.51 1.45 -4.1% PASS
total/synth/PUSH31/p0 0.93 1.31 +40.6% PASS
total/synth/PUSH31/p1 1.56 1.54 -1.0% PASS
total/synth/PUSH32/p0 0.95 1.31 +38.1% PASS
total/synth/PUSH32/p1 1.52 1.44 -5.6% PASS
total/synth/PUSH4/p0 0.00 0.00 -7.8% PASS
total/synth/PUSH4/p1 0.00 0.00 -7.4% PASS
total/synth/PUSH5/p0 0.00 0.00 -7.5% PASS
total/synth/PUSH5/p1 0.00 0.00 -7.7% PASS
total/synth/PUSH6/p0 0.00 0.00 -7.6% PASS
total/synth/PUSH6/p1 0.00 0.00 -7.7% PASS
total/synth/PUSH7/p0 0.00 0.00 -7.9% PASS
total/synth/PUSH7/p1 0.00 0.00 -7.8% PASS
total/synth/PUSH8/p0 0.00 0.00 -7.4% PASS
total/synth/PUSH8/p1 0.00 0.00 -7.8% PASS
total/synth/PUSH9/p0 0.00 0.00 -7.6% PASS
total/synth/PUSH9/p1 0.00 0.00 -7.7% PASS
total/synth/RETURNDATASIZE/a0 0.03 0.03 -10.6% PASS
total/synth/RETURNDATASIZE/a1 0.03 0.03 -10.5% PASS
total/synth/SAR/b0 6.53 0.00 -100.0% PASS
total/synth/SAR/b1 7.76 0.00 -100.0% PASS
total/synth/SGT/b0 0.00 0.00 -7.4% PASS
total/synth/SGT/b1 0.00 0.00 -7.8% PASS
total/synth/SHL/b0 14.56 0.00 -100.0% PASS
total/synth/SHL/b1 17.97 0.00 -100.0% PASS
total/synth/SHR/b0 12.90 0.00 -100.0% PASS
total/synth/SHR/b1 21.56 0.00 -100.0% PASS
total/synth/SIGNEXTEND/b0 0.00 0.00 -7.8% PASS
total/synth/SIGNEXTEND/b1 0.00 0.00 -7.5% PASS
total/synth/SLT/b0 0.00 0.00 -7.7% PASS
total/synth/SLT/b1 0.00 0.00 -7.9% PASS
total/synth/SUB/b0 0.00 0.00 -7.7% PASS
total/synth/SUB/b1 0.00 0.00 -7.7% PASS
total/synth/SWAP1/s0 0.00 0.00 -7.7% PASS
total/synth/SWAP10/s0 0.00 0.00 -7.7% PASS
total/synth/SWAP11/s0 0.00 0.00 -7.8% PASS
total/synth/SWAP12/s0 0.00 0.00 -7.8% PASS
total/synth/SWAP13/s0 0.00 0.00 -7.8% PASS
total/synth/SWAP14/s0 0.00 0.00 -6.8% PASS
total/synth/SWAP15/s0 0.00 0.00 -7.9% PASS
total/synth/SWAP16/s0 0.00 0.00 -7.7% PASS
total/synth/SWAP2/s0 0.00 0.00 -7.8% PASS
total/synth/SWAP3/s0 0.00 0.00 -7.2% PASS
total/synth/SWAP4/s0 0.00 0.00 -7.5% PASS
total/synth/SWAP5/s0 0.00 0.00 -7.8% PASS
total/synth/SWAP6/s0 0.00 0.00 -7.5% PASS
total/synth/SWAP7/s0 0.00 0.00 -8.1% PASS
total/synth/SWAP8/s0 0.00 0.00 -7.6% PASS
total/synth/SWAP9/s0 0.00 0.00 -7.4% PASS
total/synth/XOR/b0 0.00 0.00 -7.2% PASS
total/synth/XOR/b1 0.00 0.00 -7.3% PASS
total/synth/loop_v1 1.25 1.18 -5.7% PASS
total/synth/loop_v2 1.16 1.06 -8.6% PASS

Summary: 194 benchmarks, 0 regressions


@abmcar abmcar changed the title perf(evm): u256 strength reduction batch 1 (shift/addmod/barrier) perf(evm): u256 strength reduction batch 1+2 (shift/addmod/barrier/value-range/div-mod-inline) Apr 11, 2026
@abmcar
abmcar force-pushed the perf/u256-batch1 branch from 36acc06 to 062fa17 Compare April 13, 2026 10:10
@abmcar abmcar changed the title perf(evm): u256 strength reduction batch 1+2 (shift/addmod/barrier/value-range/div-mod-inline) perf(evm): u256 arithmetic optimizations (shift/addmod/barrier/value-range/div-mod) Apr 15, 2026
abmcar added a commit to abmcar/DTVM that referenced this pull request Apr 15, 2026
…ariant

Address Copilot review feedback on PR DTVMStack#458:

1. Extract the cascaded u256 unsigned-LT comparison (previously inlined
   four times in handleAddMod for AugLtMod, AddLtMod, Overflow, and
   SumLtMod) into a single u256UnsignedLT(A, B) lambda returning i64
   {0,1}. No behavior change; reduces risk of future divergent edits
   across the four call sites.

2. Strengthen the fast-path eligibility comment to document the
   invariant the guard establishes: mod[3] != 0 && x[3] <= mod[3]
   implies x < 2*mod (and symmetrically for y), which is exactly what
   the single-subtraction normalization step requires. Includes the
   proof sketch so future refactors don't weaken the check.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
abmcar and others added 8 commits April 25, 2026 10:40
Replace OP_udiv(ShiftAmount, 64) with OP_ushr(ShiftAmount, 6) and
OP_urem(ShiftAmount, 64) with OP_and(ShiftAmount, 63) in SHL/SHR/SAR
dynamic shift paths. Since the divisor is always 64 (2^6), these
strength reductions avoid expensive x86 DIV instructions (~30 cycles).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Inline the intx::addmod fast-path algorithm at the MIR level for
non-constant ADDMOD operations. When mod[3] != 0 (modulus >= 2^192)
and both operands' high limbs are <= mod's high limb, the result is
computed entirely in JIT-generated code using conditional normalize,
add, and subtract-and-select — avoiding the runtime function call to
evmGetAddMod/intx::addmod.

The fast path performs:
1. Normalize augend: if augend >= mod, use augend - mod
2. Normalize addend: if addend >= mod, use addend - mod
3. Add normalized values, detect overflow via sum < augend
4. Subtract mod from sum, detect borrow via sum < mod
5. Select result: if (overflow || !borrow) use difference, else sum

Falls back to the existing runtime call for small moduli (mod[3] == 0)
and the mod == 0 edge case.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…st paths

Remove protectUnsafeValue barriers in handleAddU64Const and
handleSubU64Const where the x86 carry flag is provably dead:

- handleAddU64Const: Remove barriers on RHS constants (MOV imm never
  clobbers flags) and on the last ADC result (no subsequent ADC
  consumes the carry). Intermediate ADC barriers are preserved to
  force immediate execution while CF is live between ADD→ADC→ADC.

- handleSubU64Const: Remove all barriers. This function uses explicit
  borrow computation (SUB + ICMP_ULT + zeroExtend) rather than SBB
  instructions, so the carry flag is never live.

Generic u256 ADD/SUB chains (evm_mir_compiler.h) are intentionally
left unchanged: their last ADC/SBB barriers ensure the instruction
executes while CF from the previous ADC/SBB is still valid, and
removing them causes deferred lowering to execute after CF is clobbered.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add lightweight ValueRange tracking (U64/U128/U256) to Operand class to
detect when u256 operands provably fit in narrower types, then emit
single-instruction fast paths instead of expensive multi-limb arithmetic.

Range sources:
- Constants: auto-derived from limb values
- AND masks: result narrows to the smaller operand range
- BYTE: always U64 (single byte value 0..255)
- Comparisons: always U64 (result is 0 or 1)

Range-based fast paths:
- ADD(U64,U64): single add + carry → U128 result
- MUL(U64,U64): single 64×64→128 multiply → U128 result
- DIV(U64,U64): single udiv with div-by-zero guard → U64 result
- MOD(U64,U64): single urem with div-by-zero guard → U64 result

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the runtime function call fallback in handleDiv and handleMod with
inline runtime divisor-size branching. At runtime, check if the divisor fits
in a single 64-bit limb (upper 3 limbs all zero). If so, use the existing
cascading 128/64 division pattern inline, avoiding the expensive indirect
call, caller-save register spills, and stack frame overhead. Multi-limb
divisors still fall back to the runtime call.

Also handles the divisor==0 case inline (EVM spec: returns 0) to avoid
entering the runtime for a common edge case.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The 5-block CFG pattern in handleDivModGeneral caused a live range
calculation assertion failure (TheVNI != nullptr) during register
allocation. The root cause was cross-block value references spanning
two intermediate blocks (EntryBB -> SingleLimbCheckBB -> ZeroDivisorBB),
which the live range calculator could not resolve.

Replace the ZeroDivisorBB branch with a select-based zero guard:
SafeB0 = select(IsB0Zero, 1, B[0]) prevents hardware DIV-by-zero,
then select(IsB0Zero, 0, result) zeros the output when divisor was 0.
This reduces the CFG from 5 blocks to 3, matching the proven handleDiv
pattern (EntryBB -> SingleLimbBB/MultiLimbBB -> AfterBB).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ariant

Address Copilot review feedback on PR DTVMStack#458:

1. Extract the cascaded u256 unsigned-LT comparison (previously inlined
   four times in handleAddMod for AugLtMod, AddLtMod, Overflow, and
   SumLtMod) into a single u256UnsignedLT(A, B) lambda returning i64
   {0,1}. No behavior change; reduces risk of future divergent edits
   across the four call sites.

2. Strengthen the fast-path eligibility comment to document the
   invariant the guard establishes: mod[3] != 0 && x[3] <= mod[3]
   implies x < 2*mod (and symmetrically for y), which is exactly what
   the single-subtraction normalization step requires. Includes the
   proof sketch so future refactors don't weaken the check.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add three .easm/.expected pairs to exercise the inline ADDMOD fast path
introduced in earlier u256 batch commits. The existing addmod fixtures
cover only small modulus / 0xFF carry / mod==0 cases; none of them set
mod[3] != 0, so the fast path's eligibility branch and normalize-subtract
chain were untested.

New cases:
- addmod_fastpath_boundary: mod[3]=x[3]=y[3]=1 with x>mod and y>mod,
  forces the conditional subtract on both operands before sum.
- addmod_fastpath_x_eq_mod: x == mod, exercising the AugLtMod=false /
  NormAugend=0 path while y < mod stays in place.
- addmod_fastpath_overflow_into_high_limb: x[3]=y[3]=mod[3]=2^63 with
  low limbs maxed, sum carries out of limb 3 -> Overflow detection
  forces selecting SumSubMod regardless of borrow flag.

Verified with run_evm_tests.py in both multipass JIT and interpreter
modes (6/6 pass each), and via evmone-unittests multipass (223/223).
@abmcar
abmcar force-pushed the perf/u256-batch1 branch from ac7cc13 to 66235af Compare April 25, 2026 05:58
@abmcar
abmcar marked this pull request as ready for review April 25, 2026 16:07
Light-tier change doc covering the five MIR-level u256 lowering
optimizations on this branch (shift strength reduction, ADDMOD inline
fast path, dead-carry barrier elimination, value-range narrowing,
inline DIV/MOD) plus the 3-block CFG workaround for the register
coalescer assertion. Records the suite-level geomean +0.69%
[-1.80%, +3.44%] and targeted +6%-18% wins on u256-arithmetic-heavy
benches, and the dropped Barrett MULMOD variant.
@zoowii
zoowii merged commit fca0b1a into DTVMStack:main Apr 28, 2026
16 checks passed
@abmcar
abmcar deleted the perf/u256-batch1 branch May 7, 2026 10:43
abmcar added a commit to abmcar/DTVM that referenced this pull request May 12, 2026
After rebasing onto current upstream/main (which now includes DTVMStack#458 / DTVMStack#460
/ DTVMStack#482 / DTVMStack#483 perf work) and running a 10-rep evmone-bench on the 27 paper
benches, the cumulative PR delta has collapsed to noise (raw geomean
+1.15%, +0.46% after correcting a single-iteration outlier on
main/blake2b_shifts/8415nulls via a focused 20-rep re-measurement).
0 benches above the +/-25% CI gate.

The A-vs-PR-base -2.73% from this commit's own optimization is unchanged;
the framing shift is that the absolute runtime delta of the whole PR vs
unmodified main has been absorbed by the intervening upstream perf
optimizations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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