Skip to content

Align DMA memcpy with the EF's Accelerated Memory Operations standard - #922

Merged
jotabulacios merged 8 commits into
feat/dma-memcpyfrom
fix/dma-memcpy-symbol-resolution
Aug 27, 2026
Merged

Align DMA memcpy with the EF's Accelerated Memory Operations standard#922
jotabulacios merged 8 commits into
feat/dma-memcpyfrom
fix/dma-memcpy-symbol-resolution

Conversation

@nicole-graus

@nicole-graus nicole-graus commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Motivation

#874 makes the guest's out-of-line memcpy a DMA ecall. Which memcpy the guest
actually links was left to chance: the symbol lived in syscalls.rs, and it won
resolution because _start calls sys_halt from that same module, so the linker
extracts the object anyway. Nothing wrote that down, and nothing tests it. Inline the
halt into _start and the strong definition stops being extracted — the guest silently
falls back to the weak compiler-builtins memcpy, keeps producing correct output, and
just gets slower.

The EF's "Accelerated Memory Operations" standard (eth-act/zkevm-standards#32, merged)
names this exact failure and requires a vendor to pick a mechanism that makes resolution
deterministic, and to document which one.

Description

  • Defines memcpy in the always-linked entrypoint object, next to _start, so the
    strong definition is in the link graph from the start — mechanism (1) of the standard,
    documented in docs/general_flow.md.
  • Adds a guest whose copies are only compiler-emitted (nothing names memcpy), so a
    silent fallback to the weak definition fails a test instead of quietly costing
    performance. The two existing DMA guests declare extern "C" { fn memcpy }, which
    forces the symbol undefined — that is the easy case, not the one that degrades.
  • Reports Dma bytes and Dma rows alongside Dma calls in execute --cycles. One
    memcpy becomes as many ecalls as the stub chunks it into, so the call count follows
    the chunking rather than the work. The row formula is shared by trace generation, the
    sizing pass and the CLI, so the three cannot drift.
  • Documents the aligned/misaligned cost difference: chunk width comes from the bytes
    remaining, so the DMA table's row count is alignment-independent, but each chunk's two
    memory operations are routed by address — an 8-aligned window sharing one old timestamp
    reaches MEMW_A (29 columns, one LT), anything else falls to MEMW (49 columns, eight
    LT). A misaligned copy therefore commits strictly more cells.

Conformance

Satisfies the clauses an accelerator can satisfy on its own:

  • Semantics / Alignmentmemcpy is behaviourally identical to the C function for
    every input, including n == 0 and any alignment of dest, src or n. Covered by
    dma_memcpy_cases (lengths 0–256, a multi-chunk 777-byte copy, both overlap
    directions, page-crossing operands) and a 256-case differential fuzz.
  • Linking and symbol resolution — mechanism (1), "always-linked runtime": memcpy is
    defined in the object that defines _start, which every guest links unconditionally.
    Documented in docs/general_flow.md, as the standard requires. Verified on the shipped
    ELFs: ethrex.elf carries a single memcpy definition, the accelerated one, with the
    compiler-builtins member never extracted.
  • Observability (recommended) — execute --cycles reports Dma calls, Dma bytes
    and Dma rows. The aligned/misaligned split the standard suggests is not reported: the
    statistics come from Log, whose two operand slots already carry src and n. The
    reason is written down rather than claimed as done.

It does not satisfy the scope clause, and no memcpy change can. That clause says
the symbols "are exported from the vendor static library defined by the Static Library
and Linker Script standard". Lambda VM has no such library: the guest interface is a Rust
rlib, with no .a, no linker script, no _heap_start/_heap_end and no int main(void)
ABI. That standard is unimplemented repo-wide — the IO interface and the cryptographic
accelerators are in the same position — so adopting it is a repo-level decision rather
than one this PR can make, and belongs in its own issue.

Validation

Three guests execute and assert their DMA ecall counts. The guard is not vacuous: renaming
the symbol by hand and rebuilding drops Dma calls 4 → 0 and raises cycles 12,670 →
13,316 — correct output, more expensive — and the test catches it. Plus cargo fmt, the
four make lint clippy passes, cargo test -p executor, -p cli, the prover DMA suite
including the disk-spill length-drift test.

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

Benchmark Results for modified programs 🚀

Command Mean [ms] Min [ms] Max [ms] Relative
head hashmap 111.0 ± 1.7 108.4 113.9 1.00

@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown

Benchmark Results for unmodified programs 🚀

Command Mean [ms] Min [ms] Max [ms] Relative
base binary_search 59.5 ± 0.9 58.3 60.8 1.00
head binary_search 59.7 ± 1.8 58.0 64.3 1.00 ± 0.03
Command Mean [ms] Min [ms] Max [ms] Relative
base bitwise_ops 62.5 ± 3.6 58.4 68.6 1.08 ± 0.06
head bitwise_ops 58.0 ± 0.3 57.8 58.6 1.00
Command Mean [ms] Min [ms] Max [ms] Relative
base ecsm 3.3 ± 0.1 3.2 3.5 1.03 ± 0.05
head ecsm 3.2 ± 0.1 3.1 3.5 1.00
Command Mean [ms] Min [ms] Max [ms] Relative
base fibonacci_26 63.3 ± 1.0 62.4 64.8 1.00 ± 0.02
head fibonacci_26 63.0 ± 1.0 62.1 65.1 1.00
Command Mean [ms] Min [ms] Max [ms] Relative
base keccak 131.3 ± 3.7 127.3 139.0 1.03 ± 0.04
head keccak 127.3 ± 3.6 120.9 133.2 1.00
Command Mean [ms] Min [ms] Max [ms] Relative
base matrix_multiply 64.0 ± 1.0 63.0 66.5 1.00
head matrix_multiply 65.0 ± 0.8 63.7 66.3 1.02 ± 0.02
Command Mean [ms] Min [ms] Max [ms] Relative
base modular_exp 58.7 ± 0.6 58.0 60.0 1.00
head modular_exp 62.1 ± 4.2 58.4 70.3 1.06 ± 0.07
Command Mean [ms] Min [ms] Max [ms] Relative
base quicksort 64.1 ± 1.8 62.0 68.7 1.01 ± 0.03
head quicksort 63.5 ± 0.9 62.2 64.8 1.00
Command Mean [ms] Min [ms] Max [ms] Relative
base sieve 64.8 ± 0.6 64.0 65.7 1.00
head sieve 64.8 ± 0.6 64.0 65.6 1.00 ± 0.01
Command Mean [ms] Min [ms] Max [ms] Relative
base sum_array 73.5 ± 0.6 72.9 74.2 1.00
head sum_array 74.7 ± 2.4 72.9 80.3 1.02 ± 0.03
Command Mean [ms] Min [ms] Max [ms] Relative
base syscall_commit 89.0 ± 0.4 88.3 89.5 1.00
head syscall_commit 89.5 ± 0.7 88.7 90.9 1.01 ± 0.01

@jotabulacios jotabulacios changed the title Align DMA memcpy with the EF's Accelerated Memory Operations standard Pin the DMA memcpy symbol and report what copies cost Aug 27, 2026
@jotabulacios jotabulacios changed the title Pin the DMA memcpy symbol and report what copies cost Align DMA memcpy with the EF's Accelerated Memory Operations standard Aug 27, 2026
@jotabulacios
jotabulacios merged commit 4d4d758 into feat/dma-memcpy Aug 27, 2026
20 checks passed
@jotabulacios
jotabulacios deleted the fix/dma-memcpy-symbol-resolution branch August 27, 2026 18:05
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