Skip to content

perf(pv): walk the call graph once in the read and write sets of a procedure - #1218

Merged
strub merged 2 commits into
mainfrom
perf/pv-call-graph
Oct 26, 2026
Merged

strub merged 2 commits into
mainfrom
perf/pv-call-graph

Conversation

@strub

@strub strub commented Oct 26, 2026 •

Copy link
Copy Markdown
Member

Last of a stack of three performance fixes (after #1216 and #1217) found while checking safety proofs of Jasmin AVX2 implementations, whose procedures share many helpers.

The variables read or written by a procedure (EcPV.f_write, f_read) were collected by a walk of the call graph as a tree: a procedure reached through several callees was visited once per path, so the walk was exponential in the depth of the sharing. On the ML-DSA safety model (about 300 procedures, 900 call sites), the frame computed by call took longer than the rest of the proof. The walk now threads the set of procedures already visited and skips them; the public functions keep their signature.

With the three fixes, the ML-DSA-65 safety proof checks in two minutes; lemmas did not finish in 20 minutes before. The unit, prelude and stdlib scenarios pass on the stack.

@strub
strub force-pushed the perf/pv-call-graph branch from a99024a to 4781a6d Compare October 26, 2026 14:25
@strub
strub enabled stacked auto-merge October 26, 2026 14:38
@strub
strub added this pull request to the merge queue Oct 26, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue because a pull request earlier in the stack was removed Oct 26, 2026
@strub
strub force-pushed the perf/pv-call-graph branch from 4781a6d to 31cf80b Compare October 26, 2026 16:02
Base automatically changed from fix/dead-let-cbv to main October 26, 2026 16:06
strub added 2 commits October 26, 2026 17:06
The call-by-value normaliser evaluated the value of every let binding
before its body. A binding introduced by wp for a dead assignment (the
flags of an x86 instruction, a register accumulated across an unrolled
loop) had its value fully evaluated before being discarded; on 256-bit
vector code this took minutes where the rest of the goal takes seconds.
A binding none of whose variables occurs in the body is now dropped at
once, and a preliminary pass drops the dead bindings of the goal from
the inside out, so that a chain of bindings only feeding a dead one
disappears as well.
…ocedure

The variables read or written by a procedure were collected by a walk of
its call graph as a tree: a procedure reached through several callees
was visited once per path, so the walk was exponential in the depth of
the sharing. On a program whose procedures share many helpers (an
implementation extracted from Jasmin), the computation of these sets by
`call`, `seq`, `while` or `conseq` took longer than the proof itself.
The walk now skips the procedures already visited.
@strub
strub force-pushed the perf/pv-call-graph branch from 31cf80b to 616a21b Compare October 26, 2026 16:06
@strub
strub merged commit e704d26 into main Oct 26, 2026
4 of 5 checks passed
@strub
strub deleted the perf/pv-call-graph branch October 26, 2026 16:07
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