Skip to content

fix(gpu): recover device-only declines at the remaining cliff sites (R4 DEEP, comp-tree, R3 barycentric) - #935

Merged
MauroToscano merged 7 commits into
mainfrom
gpu-cliff-recovery
Aug 18, 2026
Merged

fix(gpu): recover device-only declines at the remaining cliff sites (R4 DEEP, comp-tree, R3 barycentric)#935
MauroToscano merged 7 commits into
mainfrom
gpu-cliff-recovery

Conversation

@ColoCarletti

Copy link
Copy Markdown
Collaborator

Closes #927.

Extends the #914 device-decline recovery to the downstream cliff sites, which today hard-abort under the same transient VRAM pressure (census on rented 5090s: R4 DEEP ×97, comp-tree ×42, R3 ×3):

  • R2 comp-poly commit: when both tree-build arms decline, the CPU commit now downloads the H part evals from the resident R2 handle (materialize_composition_parts_host) instead of asserting. The parts handle is folded into the trace session before the commit so the recovery and every downstream consumer read it from one place.
  • R3 parts OOD: same parts download when the host arm finds them empty.
  • R3 barycentric (main/aux): per-buffer recovery via materialize_lde_trace_host, threaded &mut through get_trace_evaluations_from_lde.
  • R4 DEEP: the host loop reads the trace and the part evals — recovers both. round_3/round_4/compute_deep now take &mut Round1/&mut Round2 (single caller).

The asserts survive only for the case where a resident handle cannot serve the data, with the device-only contract's message. New counter GPU_COMPOSITION_PARTS_DOWNLOADS mirrors the downgrade counters (parts side).

Testing: sticky fault hooks in math-cuda (test-faults) on the barycentric/DEEP/comp-tree entries — sticky because the drain-and-retry absorbs one-shot faults — plus three end-to-end tests in cuda_fallback_tests.rs that arm them, require the device-only path on warm-up, and assert the recovery counters moved and the proof verifies.

Validation (RTX 5090): fault tests 5/5 (3 new + 2 existing), cuda_path_integration 7/7 (happy path, zero downgrades), stark cuda lib 220/220; CPU-only stark suite 217/217 and clippy clean (cuda / no-cuda / test-faults).

ColoCarletti and others added 3 commits August 12, 2026 15:55
…R2 commit, R3 OOD, R4 DEEP)

Under VRAM pressure a device dispatch can decline after the device-only
gate already skipped the host drain, and the host fallbacks at the R2
comp-poly commit, the R3 parts/trace OOD and the R4 DEEP loop hard-abort
on the empty host buffers. Download the resident data instead: the trace
LDEs via materialize_lde_trace_host, the H part evaluations via a new
download off the resident R2 parts handle. The asserts remain only for
handles that cannot serve the data. The R4 DEEP host loop reads both the
trace and the part evals, so it recovers both sides.

Also adds sticky fault-injection hooks (test-faults) to the cuda
barycentric, DEEP and comp-tree entries: the drain-and-retry absorbs
one-shot faults, so the cliff paths need a fault that keeps firing.
Three prove+verify runs under sticky faults (comp-tree, barycentric,
DEEP), each requiring the device-only path to fire on the warm-up and
the recovery counters to move.
@ColoCarletti

Copy link
Copy Markdown
Collaborator Author

/ai-review

@github-actions

Copy link
Copy Markdown

Codex Code Review

  • Lowfaults.rs:29: load followed by fetch_sub is not atomic as a countdown. Concurrent GPU table dispatches can decrement 1 below zero, which disarms the supposedly sticky fault and lets later calls succeed. Use a compare-exchange/fetch-update loop that saturates at 0.

Comment thread prover/tests/cuda_fallback_tests.rs
@claude

claude Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Review

Traced the whole recovery chain — R2 commit → R3 parts OOD / barycentric → R4 DEEP — plus the slab layout, stream ordering and feature wiring. No correctness or safety issues found. Details of what I checked and three minor notes below.

Verified

  • Slab layout. download_composition_parts_host de-interleaves slabs[(p*3+k)*lde + r] into interleaved[r*3+k], exactly the inverse of unpack_pinned_slabs_to_ext3 (math-cuda/src/lde.rs:96), which is what fills the host part evals on the non-device-only path. Natural evaluation order is preserved, and nothing between R2 and R4 mutates the parts buffer in place (build_comp_poly_tree_from_slabs_dev bit-reverses inside the leaf kernel, not in buf), so the downloaded evals are byte-identical to what the host consumers would otherwise have had. Both consumers index correctly: lde_evals[i*blowup] in R3, [j][i] in the R4 DEEP loop.
  • Host/device sync. Both producers of the parts handle are safe to download cross-stream: coset_lde_batch_ext3_slabs_keep records a ready event on the device-only (outputs: None) branch, and evaluate_poly_coset_batch_ext3_into_keep host-blocks in wait_and_read before returning (ready: None is correct there). wait_ready_on + synchronize on the bound stream covers both. No buffers are dropped on the recovery path, so there is no use-after-free window like the one materialize_aux_trace_host has to drain for.
  • Shape guards. h.m != evals.len() and slabs.len() != m*lde*3 both degrade to false → the caller abort, rather than slicing out of bounds. The only producer that leaves empty host part evals is try_decompose_extend_d2_dev(want_host=false), which returns two empty vecs with a matching h.m == 2, so the length check cannot spuriously reject a real recovery.
  • Borrows / ordering. Moving set_gpu_composition_parts above the commit is safe (the tree builds only borrow the handle), and the &mut Round1 / &mut Round2 threading is disjoint at every new call site. get_trace_evaluations_from_lde has no callers outside prover.rs and the updated test.
  • Sticky hook semantics. check_sticky parks at 0 and keeps returning Err (N=1 fires on the first call), which is what the drain-and-retry arms require; *_fault_fired() reading == 0 is consistent with that. Feature wiring prover/test-cuda-faults → stark/test-cuda-faults → math-cuda/test-faults is correct and the module is fully cfg-d out of production builds. No dead code — every new hook, counter and accessor has a consumer.
  • Downstream effect of clearing host_trace_empty. After an R3/R4 recovery the later opening guards (prover.rs:2622/2664/2674) turn from hard aborts into valid host fall-throughs, and the query-0 device/host cross-check at prover.rs:2686 re-enables against data downloaded from the same buffer. Both correct, just slower — consistent with the intent.

Notes (all minor, inline)

  1. download_composition_parts_host is serial and copies twice, where the sibling materialize_lde_trace_host parallelizes and reinterprets in place — likely dominates the D2H it follows, on a path the census says fires often.
  2. The GPU_DEVICE_ONLY_DOWNGRADES doc still says "every count is a gate miss", which now contradicts both the new call sites and materialize_lde_trace_host own doc.
  3. The new tests leave the sticky fault armed if an assert fails, cascading into the rest of the single-threaded run.

One thing to be aware of rather than change: in the R3 arms the recovery is gated on the individual buffer, but materialize_lde_trace_host early-returns true on the table-wide host_trace_empty() flag. A state with the flag clear and one buffer still empty would no-op the recovery and fire the assert with a message about handles that were never consulted. Working through the flag formula in into_round1 that state does not look reachable (any empty side sets the flag; set_host_data only clears it after filling), and the pre-PR code asserted identically there — so not a regression, just a place where the comment promises slightly more than the code delivers.

@github-actions

Copy link
Copy Markdown

AI Review

PR #935 · 10 changed files

Findings

Status Sev Location Finding Found by
uncertain medium crypto/math-cuda/src/faults.rs:25 Race condition in sticky fault check: non-atomic load-then-decrement nemotron
openrouter/nvidia/nemotron-3-ultra-550b-a55b
kimi
openrouter/moonshotai/kimi-k2.7-code
glm
openrouter/z-ai/glm-5.2
confirmed low crypto/stark/src/trace.rs:708 Doc comment does not mention new mutability contract kimi
openrouter/moonshotai/kimi-k2.7-code

Status column reflects the verdict from the verifier: deepseek-verifier (openrouter/deepseek/deepseek-v4-pro).

AI-003: Race condition in sticky fault check: non-atomic load-then-decrement
  • Status: uncertain
  • Severity: medium
  • Location: crypto/math-cuda/src/faults.rs:25
  • Found by: nemotron:openrouter/nvidia/nemotron-3-ultra-550b-a55b, kimi:openrouter/moonshotai/kimi-k2.7-code, glm:openrouter/z-ai/glm-5.2
  • Verified by: -
  • Rejected by: -

Claim

The check_sticky function loads the counter, then conditionally decrements, but uses the original loaded value for the fire check. Concurrent calls can both see v=1, both decrement to 0, and both return error (double-fire), or other interleavings can cause the counter to not park at 0 correctly.

Evidence

faults.rs lines 25-38: let v = counter.load(Ordering::Relaxed); ... if v > 0 { counter.fetch_sub(1, Ordering::Relaxed); } if v <= 1 { return Err(...) }. The loaded value v is used for the fire decision, not the return value of fetch_sub. gpu_lde.rs lines 1870, 1883, 1896 define barycentric_fault_fired(), deep_fault_fired(), and comp_tree_fault_fired() as load(...) == 0. Under the race described the counter can become negative, making the fired predicates falsely report not-fired.

Suggested fix

Make the decrement saturate at 0 with a compare-exchange loop, e.g. loop { let v = counter.load(Relaxed); if v < 0 { return Ok; } if v <= 1 { counter.store(0, Relaxed); return Err(...); } if counter.compare_exchange(v, v-1, Relaxed, Relaxed).is_ok() { return Ok; } } (or fetch_update with a closure that maps v>1 to v-1 and v in 0..=1 to 0).

AI-008: Doc comment does not mention new mutability contract
  • Status: confirmed
  • Severity: low
  • Location: crypto/stark/src/trace.rs:708
  • Found by: kimi:openrouter/moonshotai/kimi-k2.7-code
  • Verified by: deepseek-verifier:openrouter/deepseek/deepseek-v4-pro
  • Rejected by: -

Claim

get_trace_evaluations_from_lde now takes &amp;mut LDETraceTable because it may call materialize_lde_trace_host to download resident LDEs into host buffers, but its doc comment still describes the function as if it only reads the table. This makes the new mutability contract easy to miss for maintainers.

Evidence

trace.rs lines 708-715 show the signature changed to lde_trace: &amp;mut LDETraceTable&lt;F, E&gt;, and lines 824-825 and 889-890 call crate::gpu_lde::materialize_lde_trace_host(lde_trace), which mutates the table. The doc block above (lines 693-707) does not mention that the function can mutate the table or download resident data.

Suggested fix

Update the doc comment for get_trace_evaluations_from_lde to state that it may mutate lde_trace by downloading resident device LDEs when a GPU fallback reaches a device-only table.

Reviewer Lanes

Lane Model Prompt Status Findings
glm openrouter/z-ai/glm-5.2 general success 1
kimi openrouter/moonshotai/kimi-k2.7-code general success 2
minimax minimax/MiniMax-M3 general error: opencode failed (provider/auth/runtime error) and no findings were submitted 0
moonmath zro/minimax-m3 general error: opencode failed (provider/auth/runtime error) and no findings were submitted 0
nemotron openrouter/nvidia/nemotron-3-ultra-550b-a55b general success 5

Verification Lanes

Lane Model Status Confirmed Rejected Uncertain
deepseek-verifier openrouter/deepseek/deepseek-v4-pro success 1 3 1

Native Codex and Claude reviews run separately and post their own comments. They are not included in this structured provenance report.

Discarded candidates (3) — rejected by the verifier
  • Materialization skipped in mixed device-only/host state causing host evaluator to read empty buffers (crypto/stark/src/prover.rs:1674, found by nemotron:openrouter/nvidia/nemotron-3-ultra-550b-a55b) — The claim states that host_trace_empty() returns false in a mixed state. But the code at trace.rs lines 330-344 shows host_trace_empty is a single boolean flag set to true when the device-only path is taken (line 357), meaning at least one of main_data/aux_data is empty. In a mixed state, the flag is true, not false. The condition at prover.rs line 1674 calls materialize_lde_trace_host when host_trace_empty() is true, which is correct. materialize_lde_trace_host (gpu_lde.rs lines 1550-1570) correctly handles mixed states by downloading only the missing sides — it checks !lde_trace.main_data.is_empty() and !lde_trace.aux_data.is_empty() separately. The claim's premise about the boolean behavior is factually wrong.
  • Redundant synchronization in materialize_lde_trace_host aux download (crypto/stark/src/gpu_lde.rs:1585, found by nemotron:openrouter/nvidia/nemotron-3-ultra-550b-a55b) — The claim asserts that clone_dtoh in cudarc already synchronizes internally. However, across the entire codebase, clone_dtoh is consistently followed by an explicit stream.synchronize() call (34 occurrences found). This pattern is used in every single GPU kernel dispatch — barycentric.rs, deep.rs, merkle.rs, ntt.rs, lib.rs, fri.rs, gpu_lde.rs, etc. The universal consistency of this pattern is strong evidence that cudarc's clone_dtoh does NOT synchronize the stream, and the explicit synchronize() call at gpu_lde.rs line 1585 is necessary, not redundant.
  • Inconsistent error handling in materialize_aux_trace_host vs materialize_lde_trace_host (crypto/stark/src/gpu_lde.rs:1719, found by nemotron:openrouter/nvidia/nemotron-3-ultra-550b-a55b) — Both functions perform the same two checks: (1) whether clone_dtoh + synchronize succeeded, and (2) whether the buffer length matches the expected dimensions. materialize_aux_trace_host (line 1722) combines them: if stream.synchronize().is_err() \|\| raw.len() != rows * cols * 3. materialize_lde_trace_host (lines 1585, 1592-1593) separates them in two if blocks. Both check length AFTER synchronize and BEFORE de-interleaving — the logical order is identical. The only difference is whether the two conditions are combined in one if or separated. This is a trivial stylistic difference with no behavioral impact, not an inconsistency in error handling.

Raw lane outputs, candidates, final issues, and model metrics are uploaded as workflow artifacts.

Comment thread crypto/stark/src/gpu_lde.rs
Comment thread crypto/stark/src/gpu_lde.rs Outdated
…llel parts download

Review follow-ups on the device-only cliff recovery:

- check_sticky: collapse the load-then-decrement into one fetch_update that
  saturates at 0, so concurrent per-table dispatches can't underflow the
  counter — which would break both the sticky guarantee and the `== 0`
  fired check.
- cuda_fallback_tests: disarm the sticky faults with a Drop guard, so a
  panic in prove or a failing assert can't leave one armed and cascade into
  the next test in the single-threaded binary.
- download_composition_parts_host: de-interleave under rayon and reinterpret
  the u64 buffer in place, matching materialize_lde_trace_host instead of
  copying again through u64_to_ext3_vec — this path fires often under VRAM
  pressure.
- Docs: the device-only downgrade counter now also covers transient device
  declines, not only gate misses; note the new &mut contract on
  get_trace_evaluations_from_lde.
cargo fmt collapses the aligned match-arm comments (the CI lint failure);
also drop a stale doc sentence describing an earlier post-load variant that
the fetch_update version does not use.
#938)

The device-only cliff recoveries replace hard aborts with a silent
download-and-continue, so the counters are now the only thing that
surfaces a gate/dispatch lockstep break. GPU_DEVICE_ONLY_DOWNGRADES
(trace side) already has its == 0 guard here; its parts-side counterpart
did not, and its only readers were the > 0 assertions in
cuda_fallback_tests, which run with a fault deliberately armed.

Without this, a decline in the R2 comp-poly tree build on a device-only
table recovers, verifies and passes green, while every such table pays a
full parts D2H plus a CPU commit_bit_reversed and loses the resident
composition tree. The R4 DEEP site is already covered transitively (it
needs the trace to be device-only too, which moves the trace counter),
so this closes the R2 commit and R3 parts-OOD sites.

Zero is the right expectation: materialize_composition_parts_host
early-returns without bumping when the part evals are already populated,
so the counter only moves for a device-only table that had to pull its
parts back.

The message names both causes rather than blaming the gate, matching the
counter's own doc, which now allows a transient VRAM decline as well as
a gate miss.
@MauroToscano
MauroToscano enabled auto-merge August 18, 2026 20:11
@MauroToscano
MauroToscano added this pull request to the merge queue Aug 18, 2026
@MauroToscano

Copy link
Copy Markdown
Contributor

/bench-gpu

@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown

GPU Benchmark (ABBA) — 7863a6740d vs main (14 pairs)

RTX 5090 · AMD Ryzen 9 7950X 16-Core Processor (32 threads) · Vast.ai datacenter @ $0.5480555555555555/hr · prover/cuda · ethrex real block, continuations · drift-free A/B/B/A

=== ABBA paired result  (improvement: - = PR faster) ===
  pairs: 14   mean A (PR): 67.782s   mean B (base): 67.933s

  [parametric] paired-t   mean -0.22%   sd 1.22%   se 0.32%
               95% CI: [-0.92%, +0.49%]   (t df=13 = 2.16)
  [robust]     median -0.39%   Wilcoxon W+=39 W-=66  p(exact)=0.4263  (z=-0.82)

  --- server stability (this run; compare across servers) ---
  run-to-run jitter:    A CV 0.75%   B CV 0.80%        (lower = steadier)
  within-session drift: +0.60% over the run, 1st->2nd half +0.12%
    (jitter -> Tier-1 cached gate floor; drift -> whether the cached baseline can be trusted)

  VERDICT: INCONCLUSIVE - effect not separable from 0 at n=14.
           Point estimate ~-0.39% (median). Need more pairs to resolve.

  raw pairs: /tmp/abba_run/pairs.csv

- = PR faster. Trust the verdict when paired-t and Wilcoxon agree.

@ColoCarletti

Copy link
Copy Markdown
Collaborator Author

/bench-gpu

Merged via the queue into main with commit cf3b1e9 Aug 18, 2026
15 checks passed
@MauroToscano
MauroToscano deleted the gpu-cliff-recovery branch August 18, 2026 20:45
MauroToscano added a commit that referenced this pull request Aug 18, 2026
Brings in the four commits that landed since the campaign base 58160b6:
the bump guest allocator default (#869), the VRAM-pressure/R2-race fix
(#914), the cuda table scheduler K = num_airs default (#911), and the
device-only cliff recovery at R4 DEEP / comp-tree / R3 barycentric (#935).

Conflicts resolved (one file, three hunks, all the same collision):

- crypto/stark/src/prover.rs — the Stage-2 H-threading parameterized
  Round1/Round2 over the hasher, while #935 changed the same parameters
  from shared to mutable so the cliff recovery can download resident
  device data back into the host buffers. Rule: keep both — main's `&mut`
  mutability and this branch's `H` parameter. Applied at
  round_3_evaluate_polynomials_in_out_of_domain_element,
  round_4_compute_and_run_fri_on_the_deep_composition_polynomial, and
  compute_deep_composition_poly_evaluations. The recovery paths therefore
  run through the H-generic signatures; nothing is un-genericized.

Everything else merged without conflict. Checked by hand rather than
trusted to the textual merge:

- crypto/stark/src/gpu_lde.rs — the two sides are disjoint. #935 appends
  the host-download helpers and the sticky fault hooks; the H-threading
  edits sit in the tree-building and FRI-commit entries. main's one hunk
  inside threaded territory is comment-only.
- crypto/math-cuda/src/lib.rs — both sides add one `pub mod` to the same
  alphabetized list (`blake3` here, `faults` on main); both survive.
- crypto/math-cuda/src/device.rs — touched only by this branch, so #935's
  math-cuda edits (barycentric, deep, faults, merkle) do not collide.
- prover/tests/calibration.rs and prover/src/auto_storage.rs — #911 splits
  the scheduler's `k` from the storage estimate's, so both call sites move
  to `storage_estimate_parallelism()`. This branch never touched either
  file, so main's version lands whole and the RAM-vs-Disk decision is
  unmoved.
- The `table_parallelism()` call site takes main's `table_parallelism(num_airs)`,
  which clamps internally to the same range this branch clamped by hand.
MauroToscano added a commit that referenced this pull request Aug 18, 2026
…t MainLdeSlot

Belongs to the origin/main merge in the previous commit; it is separate only
because the collision is invisible off the `cuda` feature, so it surfaced in
`make lint`'s cuda pass rather than in the merge itself.

#935's resident-aux downgrade added a consumer of `main_lde_cells` that
destructures the slot as a plain `(data, _)` tuple and fills it from the device
handle. This branch had already replaced that tuple with the `MainLdeSlot` enum,
whose entire purpose is stated in its doc comment: a consumer added between
Round 1 and the fused task must not be able to read an empty buffer as if it
were an LDE. The guard worked — #935 is exactly such a consumer, and it failed
to compile rather than silently reading a dropped buffer.

Resolution rule: match the slot exhaustively, and split the two states by what
each one actually owns.

- `Retained`: unchanged #935 behaviour. This buffer is the one the fused task
  reads, so under device-only it is empty and has to be downloaded off the
  resident main handle, with the download's failure still failing the table.
- `Dropped` (`ResidencyMode::RecomputeLde`): nothing to download and nothing to
  fail. The buffer was dropped deliberately and the fused task rebuilds the main
  LDE from the host trace via `expand_main_lde_row_major`, a path the device
  decline never touched. Only the aux recovery above is needed, so `recovered`
  stands.

Matched without a `_` arm so a future `MainLdeSlot` variant has to state its own
answer here, which is the property the enum was introduced for.

NOTE: this interleaving is `cuda`-gated and device-resident, so no host test
reaches it. #935's own `cuda_fallback_tests` cover the `Retained` arm on a GPU
box; the `Dropped` arm — a resident-aux decline on a table under
`ResidencyMode::RecomputeLde` — is not covered by any existing test and wants
one added to that suite on the next box session.
MauroToscano added a commit that referenced this pull request Aug 18, 2026
Carries origin/main (cf3b1e9) onto the flip branch: the bump guest allocator
default (#869), the VRAM-pressure/R2-race fix (#914), the cuda table scheduler
K = num_airs default (#911), and the device-only cliff recovery at R4 DEEP /
comp-tree / R3 barycentric (#935).

No conflicts. Both of the resolutions made when main met this campaign's tree
were already settled one branch down and arrive whole:

- `crypto/stark/src/prover.rs` — Round1/Round2 carry both main's `&mut` and the
  campaign's `H` parameter, so #935's cliff recovery runs through the H-generic
  signatures.
- `crypto/stark/src/prover.rs` — the device-only main-LDE recovery matches
  `MainLdeSlot` exhaustively: `Retained` downloads off the resident handle,
  `Dropped` (RecomputeLde) needs nothing because the fused task rebuilds from the
  host trace.

The flip's own collision surface stayed clear: the renamed alias layer
(`DefaultStarkHash`, `DefaultStarkTranscript`) and the `assert_keccak_backend`
guard over the cuda fork are untouched by main's gpu_lde edits, and the cuda
clippy pass — where this branch resolves to keccak — compiles clean.

Gates: stark release 287/0; crypto 72/0 on both round arms; `lfm::` 354 passed /
1 failed / 9 ignored, the same single pre-existing `fibonacci.elf` drift
exonerated in RESUME-PA-STAGE6.md §5.7, so zero delta; BLAKE3 host KAT green on
both round arms; second-source green; `make lint` clean across all five combos;
fmt clean.

The cross-version king gate against pre-merge refs stays failing by design — that
is the flip's inverted polarity (PA-PLAN §6), not a merge regression.
MauroToscano added a commit that referenced this pull request Aug 18, 2026
Carries origin/main (cf3b1e9) onto the MMCS integration branch: the bump guest
allocator default (#869), the VRAM-pressure/R2-race fix (#914), the cuda table
scheduler K = num_airs default (#911), and the device-only cliff recovery at
R4 DEEP / comp-tree / R3 barycentric (#935).

This is the branch where the two sides genuinely interleave. M-4p2 extracted the
round bodies so they take the data they use — `lde_trace`, `composition_parts`,
`rap_challenges` — instead of the whole `Round1`/`Round2`, and `multi_prove_batched`
reuses those same extractions. #935 works the other way: its recoveries write the
resident device data back into those very buffers, which is why upstream widened
the round signatures to `&mut Round1`/`&mut Round2`. Neither shape can simply win.

Resolution rule, applied to all nine hunks: **keep the extraction, move the
mutability onto the extracted parameter.** Each recovery then writes to exactly
the buffer its caller owns, and the batched path keeps sharing one implementation
with the monolithic one.

- `crypto/stark/src/prover.rs` `compute_composition_parts` — `lde_trace` becomes
  `&mut`; the R2 host-evaluator arm takes #935's recover-then-assert (replacing the
  old hard abort) against that parameter rather than `round_1_result.lde_trace`.
- `crypto/stark/src/prover.rs` `compute_composition_parts` — the `evaluate_dev`
  arm keeps the extracted `rap_challenges` and the extracted `lde_trace` in the
  `host_trace_empty` retain flag.
- `crypto/stark/src/prover.rs` `round_2_compute_composition_polynomial` — keeps the
  `CompositionParts` return, and #935's fold of the R2 device parts handle into the
  session (`set_gpu_composition_parts`) is added after the call, where
  `round_1_result` is in scope.
- `crypto/stark/src/prover.rs` `round_3_evaluate_polynomials_in_out_of_domain_element`
  — extracted `lde_trace` and `composition_parts` both become `&mut`; the R3 parts
  OOD arm takes #935's recovery against them.
- `crypto/stark/src/prover.rs` `compute_deep_composition_poly_evaluations` — same
  two parameters become `&mut`; the host DEEP loop's recovery writes through
  `composition_parts` instead of `round_2_result.lde_composition_poly_evaluations`.
- `crypto/stark/src/batched/prover.rs` — the three call sites and `deep_codeword`
  follow the widened signatures; the FRI combine closure captures `retained_parts`
  mutably. That closure is `FnOnce` and runs serially, so the capture adds no
  concurrency requirement.
- `crypto/stark/src/prover.rs` — the two `mut` bindings the split moved: the
  parts the R2 commit recovery writes now live in
  `round_2_compute_composition_polynomial` (so `computed.parts` is bound `mut`
  there), and `compute_composition_parts`'s own local is no longer mutated by
  anything, so it loses the `mut` and the `unused_mut` cfg_attr that went with it.
  Only the cuda lint pass sees either.

Both semantics are live afterwards: nothing is un-genericized, no recovery is
dropped, and the parameter each recovery writes to is the one the caller reads
next.

Gates: stark release 350/0 (RESUME-MMCS-INT.md's 349/0 plus main's new
`table_parallelism_stays_within_one_and_num_airs`); debug batched/mmcs 87/0,
exactly the recorded baseline; crypto 71+1/0 on both round arms; `make lint`
clean across all five combos; fmt clean.

`lfm::` reads 345 passed / 19 failed / 9 ignored against a recorded baseline of
349/15/9, and the merge is NOT the cause. Checked out 46798a5 — this branch's
own pre-merge tip — and ran the same suite there: 345/19/9, and the 19 failing
test names diff byte-identical against the merged tree's. The merge delta is
exactly zero; the recorded baseline is stale, drifted by the fixture/toolchain
trap already documented in the lfm fixture-drift notes. Independently, every
resolution in this merge is inside `#[cfg(feature = "cuda")]` or is a signature
mutability change, and that suite runs without cuda, so it could not have moved
those tests either way.

SEMANTIC-CONFLICT NOTE. The batched path consumes its parts on the host
immediately (`parts_builder.absorb`) and never reads the device parts handle, so
the recoveries are inert there today — `materialize_composition_parts_host`
returns true without touching anything when the evals are already populated, so
the widened signatures cost the batched path nothing and cannot trip its asserts.
The recovery is only reachable on the monolithic path. Flagged because that is a
judgement about reachability, not something a test currently pins.
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.

GPU prover: extend the device-decline recovery to the remaining cliff sites (R4 DEEP, composition-tree fallback, R3 barycentric)

2 participants