lakefile: drop Blake3 from the native-decide dynlib - #607

Draft
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile
Draft

lakefile: drop Blake3 from the native-decide dynlib#607
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile

Conversation

@samuelburnham

Copy link
Copy Markdown
Member

Blake3 now precompiles its libraries, so Lake loads their shared objects -- which bundle the C and Rust FFI objects -- into any process elaborating a module that imports them. The Blake3 half of ix_native_decide_dynlib was assembling that by hand from a blake3_rs_shared cdylib, and that target no longer exists upstream.

The target keeps Ix's own externs, which nothing else supplies. Precompiling Ix.Unsigned instead would work, but only as its own library declared after Ix: both would claim the module, Package.findModule? resolves with findSomeRev?, and losing that race silently stops precompiling it -- with the symptom appearing as a missing native implementation inside a proof file rather than as a configuration error. A local dynlib naming its modules outright is worth more than the lines it costs.

The Blake3 pin moves to the revision that turned precompilation on, and moves in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The lakefile drops the cdylib that the older revision still provides, so a pin left behind in any one of them pairs the new lakefile with a Blake3 that does not precompile -- and that mismatch surfaces as a missing native implementation inside a proof file rather than as a build error.

arthurpaulinoand others added 5 commits September 1, 2026 12:56
Update the multi-stark dependency and Rust toolchain for Plonky3 0.6, along with the Rust 1.98 lint migrations required to keep the workspace warning-free. Refresh the Rust-compatible Blake3.lean pin in both root and compile-package manifests.
Adapt recursive Aiur verification to Plonky3's pruned FRI multiproofs. Native proofs retain their compact serialized representation and native verification path; the FFI expands authenticated Merkle frontiers into per-query advice only when entering the existing recursive verifier circuit.
Preserve the packed claim-digest convention in the recursion diagnostic and exercise the advice boundary in the end-to-end test and benchmark paths. CPU and CUDA recursive q1 runs produce identical 823,485-byte inner proofs and 331,273-byte outer proofs.
The q50 Vector.extract_append workload retains identical CPU/CUDA proof sizes. Inner plus outer STARK proving measures 65.87s on CPU and 8.81s with CUDA on the RTX PRO 6000, a 7.48x speedup.
PR benchmark runs execute trusted workflow YAML from the default branch while loading composite actions from the PR checkout. When Bencher data and binary caches are unavailable, the workflow checks out main under base/ and asks Lake to rebuild it without first installing the Rust channel pinned by that checkout.
Teach the existing CPU provenance action to install the base checkout's validated Rust channel and profile immediately before an uncached base build. The step is a no-op when the toolchain is already available and leaves cached benchmark comparisons unchanged.
Consume the Plonky3 0.6 batch-opening layout directly in Aiur instead of expanding every pruned Merkle frontier into one authentication path per FRI query. Sample all query indices from the unchanged transcript, sort and deduplicate them with an O(q log q) merge sort, authenticate each input and commit-phase commitment once, then retain the existing per-query reduced-opening and FRI arithmetic.
Bind every frontier to transcript-derived indices, consume boundary digests in Plonky3's level/parent/child order, reject trailing frontier elements and inconsistent duplicate leaves, and assert all native opening dimensions and sibling counts. Explicitly constrain the digest-bound protocol specialization to cap height 0, binary FRI, and a constant final polynomial. Move memo_u32_less_than into IxVM Core so both substitution and multiproof sorting share its constrained rows.
Strengthen the recursive negative test to mutate a structurally valid stage-1 commitment. Regenerate both checked-in Aiur Rust executors and retain interpreter/codegen query-count parity.
On Vector.extract_append q50, recursive-verifier FFT cost falls from 204.073B to 201.166B. CPU outer proving improves from 50.09s to 45.03s and the full CPU pipeline from 90.64s to 82.90s. GPU outer proving improves from 15.85s to 13.72s and the full GPU pipeline from 28.86s to 26.69s. The outer proof grows from 3.92 MB to 4.17 MB.
Validated with the MultiStark primitive suite, recursive honest/tamper/parity tests, codegen --check, release workspace clippy, release CUDA clippy, rustfmt, and diff checks.
The Warp x64 runner pool mixes Intel Granite Rapids and AMD Zen 5, and a
build job may land on one vendor while the job that runs its binaries
lands on the other. Neither vendor's feature set contains the other's, so
`-Ctarget-cpu=native` does not produce a portable binary: Zen 5 enables
SSE4A, and LLVM emits it. Disassembling the workspace built for znver5
finds 31 SSE4A instructions, all INSERTQ, in `ix-ffi` and in
`aiur_ixvm_witness::add_entries_parallel`. Granite Rapids has no SSE4A,
so the first one executed raises #UD, killing the process with SIGILL
during witness generation. That is what turned every row of #605's
benchmark into a crash.
Pin the measured intersection of the two CPUs instead. x86-64-v4 covers
every AVX-512 subset Plonky3 uses; +avx512vbmi2 preserves its VPSHRDQ
interleave and +gfni preserves LLVM's byte-shift lowering. A workspace
built with these flags contains no instruction absent from either vendor
and has an instruction vocabulary identical to a graniterapids build.
blake3 dispatches on CPUID at runtime and is unaffected either way.
`.cargo/config.toml` keeps `-Ctarget-cpu=native`: a developer builds and
runs on one machine, and x86-64-v4 would exclude every host without
AVX-512. Only CI has the split, so only CI pins the ISA. The new guard
fails the job when a runner lacks a required feature, so the assumption
is enforced rather than assumed, and the shared `warp-x64` cargo cache
key becomes sound now that codegen no longer varies by host. RUSTFLAGS
is hashed into that key, so the flag change rotates it on its own.
Pinning also removes a benchmarking hazard that never crashed: LLVM sets
prefer-256-bit for Granite Rapids but not for Zen 5, so the same source
vectorized 3.2x more widely depending on the build host, and main-vs-PR
timings were not comparable across a vendor split.
Blake3 now precompiles its libraries, so Lake loads their shared objects --
which bundle the C and Rust FFI objects -- into any process elaborating a
module that imports them. The Blake3 half of `ix_native_decide_dynlib` was
assembling that by hand from a `blake3_rs_shared` cdylib, and that target no
longer exists upstream.
The target keeps Ix's own externs, which nothing else supplies. Precompiling
`Ix.Unsigned` instead would work, but only as its own library declared after
`Ix`: both would claim the module, `Package.findModule?` resolves with
`findSomeRev?`, and losing that race silently stops precompiling it -- with
the symptom appearing as a missing native implementation inside a proof file
rather than as a configuration error. A local dynlib naming its modules
outright is worth more than the lines it costs.
The Blake3 pin moves to the revision that turned precompilation on, and moves
in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The
lakefile drops the cdylib that the older revision still provides, so a pin
left behind in any one of them pairs the new lakefile with a Blake3 that does
not precompile -- and that mismatch surfaces as a missing native
implementation inside a proof file rather than as a build error.
Sign up for freeto 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

@samuelburnham@arthurpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

lakefile: drop Blake3 from the native-decide dynlib - #607

Draft
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile
Draft

lakefile: drop Blake3 from the native-decide dynlib#607
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile

Conversation

@samuelburnham

Copy link
Copy Markdown
Member

Blake3 now precompiles its libraries, so Lake loads their shared objects -- which bundle the C and Rust FFI objects -- into any process elaborating a module that imports them. The Blake3 half of ix_native_decide_dynlib was assembling that by hand from a blake3_rs_shared cdylib, and that target no longer exists upstream.

The target keeps Ix's own externs, which nothing else supplies. Precompiling Ix.Unsigned instead would work, but only as its own library declared after Ix: both would claim the module, Package.findModule? resolves with findSomeRev?, and losing that race silently stops precompiling it -- with the symptom appearing as a missing native implementation inside a proof file rather than as a configuration error. A local dynlib naming its modules outright is worth more than the lines it costs.

The Blake3 pin moves to the revision that turned precompilation on, and moves in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The lakefile drops the cdylib that the older revision still provides, so a pin left behind in any one of them pairs the new lakefile with a Blake3 that does not precompile -- and that mismatch surfaces as a missing native implementation inside a proof file rather than as a build error.

arthurpaulinoand others added 5 commits September 1, 2026 12:56
Update the multi-stark dependency and Rust toolchain for Plonky3 0.6, along with the Rust 1.98 lint migrations required to keep the workspace warning-free. Refresh the Rust-compatible Blake3.lean pin in both root and compile-package manifests.
Adapt recursive Aiur verification to Plonky3's pruned FRI multiproofs. Native proofs retain their compact serialized representation and native verification path; the FFI expands authenticated Merkle frontiers into per-query advice only when entering the existing recursive verifier circuit.
Preserve the packed claim-digest convention in the recursion diagnostic and exercise the advice boundary in the end-to-end test and benchmark paths. CPU and CUDA recursive q1 runs produce identical 823,485-byte inner proofs and 331,273-byte outer proofs.
The q50 Vector.extract_append workload retains identical CPU/CUDA proof sizes. Inner plus outer STARK proving measures 65.87s on CPU and 8.81s with CUDA on the RTX PRO 6000, a 7.48x speedup.
PR benchmark runs execute trusted workflow YAML from the default branch while loading composite actions from the PR checkout. When Bencher data and binary caches are unavailable, the workflow checks out main under base/ and asks Lake to rebuild it without first installing the Rust channel pinned by that checkout.
Teach the existing CPU provenance action to install the base checkout's validated Rust channel and profile immediately before an uncached base build. The step is a no-op when the toolchain is already available and leaves cached benchmark comparisons unchanged.
Consume the Plonky3 0.6 batch-opening layout directly in Aiur instead of expanding every pruned Merkle frontier into one authentication path per FRI query. Sample all query indices from the unchanged transcript, sort and deduplicate them with an O(q log q) merge sort, authenticate each input and commit-phase commitment once, then retain the existing per-query reduced-opening and FRI arithmetic.
Bind every frontier to transcript-derived indices, consume boundary digests in Plonky3's level/parent/child order, reject trailing frontier elements and inconsistent duplicate leaves, and assert all native opening dimensions and sibling counts. Explicitly constrain the digest-bound protocol specialization to cap height 0, binary FRI, and a constant final polynomial. Move memo_u32_less_than into IxVM Core so both substitution and multiproof sorting share its constrained rows.
Strengthen the recursive negative test to mutate a structurally valid stage-1 commitment. Regenerate both checked-in Aiur Rust executors and retain interpreter/codegen query-count parity.
On Vector.extract_append q50, recursive-verifier FFT cost falls from 204.073B to 201.166B. CPU outer proving improves from 50.09s to 45.03s and the full CPU pipeline from 90.64s to 82.90s. GPU outer proving improves from 15.85s to 13.72s and the full GPU pipeline from 28.86s to 26.69s. The outer proof grows from 3.92 MB to 4.17 MB.
Validated with the MultiStark primitive suite, recursive honest/tamper/parity tests, codegen --check, release workspace clippy, release CUDA clippy, rustfmt, and diff checks.
The Warp x64 runner pool mixes Intel Granite Rapids and AMD Zen 5, and a
build job may land on one vendor while the job that runs its binaries
lands on the other. Neither vendor's feature set contains the other's, so
`-Ctarget-cpu=native` does not produce a portable binary: Zen 5 enables
SSE4A, and LLVM emits it. Disassembling the workspace built for znver5
finds 31 SSE4A instructions, all INSERTQ, in `ix-ffi` and in
`aiur_ixvm_witness::add_entries_parallel`. Granite Rapids has no SSE4A,
so the first one executed raises #UD, killing the process with SIGILL
during witness generation. That is what turned every row of #605's
benchmark into a crash.
Pin the measured intersection of the two CPUs instead. x86-64-v4 covers
every AVX-512 subset Plonky3 uses; +avx512vbmi2 preserves its VPSHRDQ
interleave and +gfni preserves LLVM's byte-shift lowering. A workspace
built with these flags contains no instruction absent from either vendor
and has an instruction vocabulary identical to a graniterapids build.
blake3 dispatches on CPUID at runtime and is unaffected either way.
`.cargo/config.toml` keeps `-Ctarget-cpu=native`: a developer builds and
runs on one machine, and x86-64-v4 would exclude every host without
AVX-512. Only CI has the split, so only CI pins the ISA. The new guard
fails the job when a runner lacks a required feature, so the assumption
is enforced rather than assumed, and the shared `warp-x64` cargo cache
key becomes sound now that codegen no longer varies by host. RUSTFLAGS
is hashed into that key, so the flag change rotates it on its own.
Pinning also removes a benchmarking hazard that never crashed: LLVM sets
prefer-256-bit for Granite Rapids but not for Zen 5, so the same source
vectorized 3.2x more widely depending on the build host, and main-vs-PR
timings were not comparable across a vendor split.
Blake3 now precompiles its libraries, so Lake loads their shared objects --
which bundle the C and Rust FFI objects -- into any process elaborating a
module that imports them. The Blake3 half of `ix_native_decide_dynlib` was
assembling that by hand from a `blake3_rs_shared` cdylib, and that target no
longer exists upstream.
The target keeps Ix's own externs, which nothing else supplies. Precompiling
`Ix.Unsigned` instead would work, but only as its own library declared after
`Ix`: both would claim the module, `Package.findModule?` resolves with
`findSomeRev?`, and losing that race silently stops precompiling it -- with
the symptom appearing as a missing native implementation inside a proof file
rather than as a configuration error. A local dynlib naming its modules
outright is worth more than the lines it costs.
The Blake3 pin moves to the revision that turned precompilation on, and moves
in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The
lakefile drops the cdylib that the older revision still provides, so a pin
left behind in any one of them pairs the new lakefile with a Blake3 that does
not precompile -- and that mismatch surfaces as a missing native
implementation inside a proof file rather than as a build error.
Sign up for freeto 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

@samuelburnham@arthurpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

lakefile: drop Blake3 from the native-decide dynlib - #607

Draft
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile
Draft

lakefile: drop Blake3 from the native-decide dynlib#607
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile

Conversation

@samuelburnham

Copy link
Copy Markdown
Member

Blake3 now precompiles its libraries, so Lake loads their shared objects -- which bundle the C and Rust FFI objects -- into any process elaborating a module that imports them. The Blake3 half of ix_native_decide_dynlib was assembling that by hand from a blake3_rs_shared cdylib, and that target no longer exists upstream.

The target keeps Ix's own externs, which nothing else supplies. Precompiling Ix.Unsigned instead would work, but only as its own library declared after Ix: both would claim the module, Package.findModule? resolves with findSomeRev?, and losing that race silently stops precompiling it -- with the symptom appearing as a missing native implementation inside a proof file rather than as a configuration error. A local dynlib naming its modules outright is worth more than the lines it costs.

The Blake3 pin moves to the revision that turned precompilation on, and moves in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The lakefile drops the cdylib that the older revision still provides, so a pin left behind in any one of them pairs the new lakefile with a Blake3 that does not precompile -- and that mismatch surfaces as a missing native implementation inside a proof file rather than as a build error.

arthurpaulinoand others added 5 commits September 1, 2026 12:56
Update the multi-stark dependency and Rust toolchain for Plonky3 0.6, along with the Rust 1.98 lint migrations required to keep the workspace warning-free. Refresh the Rust-compatible Blake3.lean pin in both root and compile-package manifests.
Adapt recursive Aiur verification to Plonky3's pruned FRI multiproofs. Native proofs retain their compact serialized representation and native verification path; the FFI expands authenticated Merkle frontiers into per-query advice only when entering the existing recursive verifier circuit.
Preserve the packed claim-digest convention in the recursion diagnostic and exercise the advice boundary in the end-to-end test and benchmark paths. CPU and CUDA recursive q1 runs produce identical 823,485-byte inner proofs and 331,273-byte outer proofs.
The q50 Vector.extract_append workload retains identical CPU/CUDA proof sizes. Inner plus outer STARK proving measures 65.87s on CPU and 8.81s with CUDA on the RTX PRO 6000, a 7.48x speedup.
PR benchmark runs execute trusted workflow YAML from the default branch while loading composite actions from the PR checkout. When Bencher data and binary caches are unavailable, the workflow checks out main under base/ and asks Lake to rebuild it without first installing the Rust channel pinned by that checkout.
Teach the existing CPU provenance action to install the base checkout's validated Rust channel and profile immediately before an uncached base build. The step is a no-op when the toolchain is already available and leaves cached benchmark comparisons unchanged.
Consume the Plonky3 0.6 batch-opening layout directly in Aiur instead of expanding every pruned Merkle frontier into one authentication path per FRI query. Sample all query indices from the unchanged transcript, sort and deduplicate them with an O(q log q) merge sort, authenticate each input and commit-phase commitment once, then retain the existing per-query reduced-opening and FRI arithmetic.
Bind every frontier to transcript-derived indices, consume boundary digests in Plonky3's level/parent/child order, reject trailing frontier elements and inconsistent duplicate leaves, and assert all native opening dimensions and sibling counts. Explicitly constrain the digest-bound protocol specialization to cap height 0, binary FRI, and a constant final polynomial. Move memo_u32_less_than into IxVM Core so both substitution and multiproof sorting share its constrained rows.
Strengthen the recursive negative test to mutate a structurally valid stage-1 commitment. Regenerate both checked-in Aiur Rust executors and retain interpreter/codegen query-count parity.
On Vector.extract_append q50, recursive-verifier FFT cost falls from 204.073B to 201.166B. CPU outer proving improves from 50.09s to 45.03s and the full CPU pipeline from 90.64s to 82.90s. GPU outer proving improves from 15.85s to 13.72s and the full GPU pipeline from 28.86s to 26.69s. The outer proof grows from 3.92 MB to 4.17 MB.
Validated with the MultiStark primitive suite, recursive honest/tamper/parity tests, codegen --check, release workspace clippy, release CUDA clippy, rustfmt, and diff checks.
The Warp x64 runner pool mixes Intel Granite Rapids and AMD Zen 5, and a
build job may land on one vendor while the job that runs its binaries
lands on the other. Neither vendor's feature set contains the other's, so
`-Ctarget-cpu=native` does not produce a portable binary: Zen 5 enables
SSE4A, and LLVM emits it. Disassembling the workspace built for znver5
finds 31 SSE4A instructions, all INSERTQ, in `ix-ffi` and in
`aiur_ixvm_witness::add_entries_parallel`. Granite Rapids has no SSE4A,
so the first one executed raises #UD, killing the process with SIGILL
during witness generation. That is what turned every row of #605's
benchmark into a crash.
Pin the measured intersection of the two CPUs instead. x86-64-v4 covers
every AVX-512 subset Plonky3 uses; +avx512vbmi2 preserves its VPSHRDQ
interleave and +gfni preserves LLVM's byte-shift lowering. A workspace
built with these flags contains no instruction absent from either vendor
and has an instruction vocabulary identical to a graniterapids build.
blake3 dispatches on CPUID at runtime and is unaffected either way.
`.cargo/config.toml` keeps `-Ctarget-cpu=native`: a developer builds and
runs on one machine, and x86-64-v4 would exclude every host without
AVX-512. Only CI has the split, so only CI pins the ISA. The new guard
fails the job when a runner lacks a required feature, so the assumption
is enforced rather than assumed, and the shared `warp-x64` cargo cache
key becomes sound now that codegen no longer varies by host. RUSTFLAGS
is hashed into that key, so the flag change rotates it on its own.
Pinning also removes a benchmarking hazard that never crashed: LLVM sets
prefer-256-bit for Granite Rapids but not for Zen 5, so the same source
vectorized 3.2x more widely depending on the build host, and main-vs-PR
timings were not comparable across a vendor split.
Blake3 now precompiles its libraries, so Lake loads their shared objects --
which bundle the C and Rust FFI objects -- into any process elaborating a
module that imports them. The Blake3 half of `ix_native_decide_dynlib` was
assembling that by hand from a `blake3_rs_shared` cdylib, and that target no
longer exists upstream.
The target keeps Ix's own externs, which nothing else supplies. Precompiling
`Ix.Unsigned` instead would work, but only as its own library declared after
`Ix`: both would claim the module, `Package.findModule?` resolves with
`findSomeRev?`, and losing that race silently stops precompiling it -- with
the symptom appearing as a missing native implementation inside a proof file
rather than as a configuration error. A local dynlib naming its modules
outright is worth more than the lines it costs.
The Blake3 pin moves to the revision that turned precompilation on, and moves
in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The
lakefile drops the cdylib that the older revision still provides, so a pin
left behind in any one of them pairs the new lakefile with a Blake3 that does
not precompile -- and that mismatch surfaces as a missing native
implementation inside a proof file rather than as a build error.
Sign up for freeto 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

@samuelburnham@arthurpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

lakefile: drop Blake3 from the native-decide dynlib - #607

Draft
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile
Draft

lakefile: drop Blake3 from the native-decide dynlib#607
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile

Conversation

@samuelburnham

Copy link
Copy Markdown
Member

Blake3 now precompiles its libraries, so Lake loads their shared objects -- which bundle the C and Rust FFI objects -- into any process elaborating a module that imports them. The Blake3 half of ix_native_decide_dynlib was assembling that by hand from a blake3_rs_shared cdylib, and that target no longer exists upstream.

The target keeps Ix's own externs, which nothing else supplies. Precompiling Ix.Unsigned instead would work, but only as its own library declared after Ix: both would claim the module, Package.findModule? resolves with findSomeRev?, and losing that race silently stops precompiling it -- with the symptom appearing as a missing native implementation inside a proof file rather than as a configuration error. A local dynlib naming its modules outright is worth more than the lines it costs.

The Blake3 pin moves to the revision that turned precompilation on, and moves in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The lakefile drops the cdylib that the older revision still provides, so a pin left behind in any one of them pairs the new lakefile with a Blake3 that does not precompile -- and that mismatch surfaces as a missing native implementation inside a proof file rather than as a build error.

arthurpaulinoand others added 5 commits September 1, 2026 12:56
Update the multi-stark dependency and Rust toolchain for Plonky3 0.6, along with the Rust 1.98 lint migrations required to keep the workspace warning-free. Refresh the Rust-compatible Blake3.lean pin in both root and compile-package manifests.
Adapt recursive Aiur verification to Plonky3's pruned FRI multiproofs. Native proofs retain their compact serialized representation and native verification path; the FFI expands authenticated Merkle frontiers into per-query advice only when entering the existing recursive verifier circuit.
Preserve the packed claim-digest convention in the recursion diagnostic and exercise the advice boundary in the end-to-end test and benchmark paths. CPU and CUDA recursive q1 runs produce identical 823,485-byte inner proofs and 331,273-byte outer proofs.
The q50 Vector.extract_append workload retains identical CPU/CUDA proof sizes. Inner plus outer STARK proving measures 65.87s on CPU and 8.81s with CUDA on the RTX PRO 6000, a 7.48x speedup.
PR benchmark runs execute trusted workflow YAML from the default branch while loading composite actions from the PR checkout. When Bencher data and binary caches are unavailable, the workflow checks out main under base/ and asks Lake to rebuild it without first installing the Rust channel pinned by that checkout.
Teach the existing CPU provenance action to install the base checkout's validated Rust channel and profile immediately before an uncached base build. The step is a no-op when the toolchain is already available and leaves cached benchmark comparisons unchanged.
Consume the Plonky3 0.6 batch-opening layout directly in Aiur instead of expanding every pruned Merkle frontier into one authentication path per FRI query. Sample all query indices from the unchanged transcript, sort and deduplicate them with an O(q log q) merge sort, authenticate each input and commit-phase commitment once, then retain the existing per-query reduced-opening and FRI arithmetic.
Bind every frontier to transcript-derived indices, consume boundary digests in Plonky3's level/parent/child order, reject trailing frontier elements and inconsistent duplicate leaves, and assert all native opening dimensions and sibling counts. Explicitly constrain the digest-bound protocol specialization to cap height 0, binary FRI, and a constant final polynomial. Move memo_u32_less_than into IxVM Core so both substitution and multiproof sorting share its constrained rows.
Strengthen the recursive negative test to mutate a structurally valid stage-1 commitment. Regenerate both checked-in Aiur Rust executors and retain interpreter/codegen query-count parity.
On Vector.extract_append q50, recursive-verifier FFT cost falls from 204.073B to 201.166B. CPU outer proving improves from 50.09s to 45.03s and the full CPU pipeline from 90.64s to 82.90s. GPU outer proving improves from 15.85s to 13.72s and the full GPU pipeline from 28.86s to 26.69s. The outer proof grows from 3.92 MB to 4.17 MB.
Validated with the MultiStark primitive suite, recursive honest/tamper/parity tests, codegen --check, release workspace clippy, release CUDA clippy, rustfmt, and diff checks.
The Warp x64 runner pool mixes Intel Granite Rapids and AMD Zen 5, and a
build job may land on one vendor while the job that runs its binaries
lands on the other. Neither vendor's feature set contains the other's, so
`-Ctarget-cpu=native` does not produce a portable binary: Zen 5 enables
SSE4A, and LLVM emits it. Disassembling the workspace built for znver5
finds 31 SSE4A instructions, all INSERTQ, in `ix-ffi` and in
`aiur_ixvm_witness::add_entries_parallel`. Granite Rapids has no SSE4A,
so the first one executed raises #UD, killing the process with SIGILL
during witness generation. That is what turned every row of #605's
benchmark into a crash.
Pin the measured intersection of the two CPUs instead. x86-64-v4 covers
every AVX-512 subset Plonky3 uses; +avx512vbmi2 preserves its VPSHRDQ
interleave and +gfni preserves LLVM's byte-shift lowering. A workspace
built with these flags contains no instruction absent from either vendor
and has an instruction vocabulary identical to a graniterapids build.
blake3 dispatches on CPUID at runtime and is unaffected either way.
`.cargo/config.toml` keeps `-Ctarget-cpu=native`: a developer builds and
runs on one machine, and x86-64-v4 would exclude every host without
AVX-512. Only CI has the split, so only CI pins the ISA. The new guard
fails the job when a runner lacks a required feature, so the assumption
is enforced rather than assumed, and the shared `warp-x64` cargo cache
key becomes sound now that codegen no longer varies by host. RUSTFLAGS
is hashed into that key, so the flag change rotates it on its own.
Pinning also removes a benchmarking hazard that never crashed: LLVM sets
prefer-256-bit for Granite Rapids but not for Zen 5, so the same source
vectorized 3.2x more widely depending on the build host, and main-vs-PR
timings were not comparable across a vendor split.
Blake3 now precompiles its libraries, so Lake loads their shared objects --
which bundle the C and Rust FFI objects -- into any process elaborating a
module that imports them. The Blake3 half of `ix_native_decide_dynlib` was
assembling that by hand from a `blake3_rs_shared` cdylib, and that target no
longer exists upstream.
The target keeps Ix's own externs, which nothing else supplies. Precompiling
`Ix.Unsigned` instead would work, but only as its own library declared after
`Ix`: both would claim the module, `Package.findModule?` resolves with
`findSomeRev?`, and losing that race silently stops precompiling it -- with
the symptom appearing as a missing native implementation inside a proof file
rather than as a configuration error. A local dynlib naming its modules
outright is worth more than the lines it costs.
The Blake3 pin moves to the revision that turned precompilation on, and moves
in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The
lakefile drops the cdylib that the older revision still provides, so a pin
left behind in any one of them pairs the new lakefile with a Blake3 that does
not precompile -- and that mismatch surfaces as a missing native
implementation inside a proof file rather than as a build error.
Sign up for freeto 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

@samuelburnham@arthurpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

lakefile: drop Blake3 from the native-decide dynlib - #607

Draft
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile
Draft

lakefile: drop Blake3 from the native-decide dynlib#607
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile

Conversation

@samuelburnham

Copy link
Copy Markdown
Member

Blake3 now precompiles its libraries, so Lake loads their shared objects -- which bundle the C and Rust FFI objects -- into any process elaborating a module that imports them. The Blake3 half of ix_native_decide_dynlib was assembling that by hand from a blake3_rs_shared cdylib, and that target no longer exists upstream.

The target keeps Ix's own externs, which nothing else supplies. Precompiling Ix.Unsigned instead would work, but only as its own library declared after Ix: both would claim the module, Package.findModule? resolves with findSomeRev?, and losing that race silently stops precompiling it -- with the symptom appearing as a missing native implementation inside a proof file rather than as a configuration error. A local dynlib naming its modules outright is worth more than the lines it costs.

The Blake3 pin moves to the revision that turned precompilation on, and moves in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The lakefile drops the cdylib that the older revision still provides, so a pin left behind in any one of them pairs the new lakefile with a Blake3 that does not precompile -- and that mismatch surfaces as a missing native implementation inside a proof file rather than as a build error.

arthurpaulinoand others added 5 commits September 1, 2026 12:56
Update the multi-stark dependency and Rust toolchain for Plonky3 0.6, along with the Rust 1.98 lint migrations required to keep the workspace warning-free. Refresh the Rust-compatible Blake3.lean pin in both root and compile-package manifests.
Adapt recursive Aiur verification to Plonky3's pruned FRI multiproofs. Native proofs retain their compact serialized representation and native verification path; the FFI expands authenticated Merkle frontiers into per-query advice only when entering the existing recursive verifier circuit.
Preserve the packed claim-digest convention in the recursion diagnostic and exercise the advice boundary in the end-to-end test and benchmark paths. CPU and CUDA recursive q1 runs produce identical 823,485-byte inner proofs and 331,273-byte outer proofs.
The q50 Vector.extract_append workload retains identical CPU/CUDA proof sizes. Inner plus outer STARK proving measures 65.87s on CPU and 8.81s with CUDA on the RTX PRO 6000, a 7.48x speedup.
PR benchmark runs execute trusted workflow YAML from the default branch while loading composite actions from the PR checkout. When Bencher data and binary caches are unavailable, the workflow checks out main under base/ and asks Lake to rebuild it without first installing the Rust channel pinned by that checkout.
Teach the existing CPU provenance action to install the base checkout's validated Rust channel and profile immediately before an uncached base build. The step is a no-op when the toolchain is already available and leaves cached benchmark comparisons unchanged.
Consume the Plonky3 0.6 batch-opening layout directly in Aiur instead of expanding every pruned Merkle frontier into one authentication path per FRI query. Sample all query indices from the unchanged transcript, sort and deduplicate them with an O(q log q) merge sort, authenticate each input and commit-phase commitment once, then retain the existing per-query reduced-opening and FRI arithmetic.
Bind every frontier to transcript-derived indices, consume boundary digests in Plonky3's level/parent/child order, reject trailing frontier elements and inconsistent duplicate leaves, and assert all native opening dimensions and sibling counts. Explicitly constrain the digest-bound protocol specialization to cap height 0, binary FRI, and a constant final polynomial. Move memo_u32_less_than into IxVM Core so both substitution and multiproof sorting share its constrained rows.
Strengthen the recursive negative test to mutate a structurally valid stage-1 commitment. Regenerate both checked-in Aiur Rust executors and retain interpreter/codegen query-count parity.
On Vector.extract_append q50, recursive-verifier FFT cost falls from 204.073B to 201.166B. CPU outer proving improves from 50.09s to 45.03s and the full CPU pipeline from 90.64s to 82.90s. GPU outer proving improves from 15.85s to 13.72s and the full GPU pipeline from 28.86s to 26.69s. The outer proof grows from 3.92 MB to 4.17 MB.
Validated with the MultiStark primitive suite, recursive honest/tamper/parity tests, codegen --check, release workspace clippy, release CUDA clippy, rustfmt, and diff checks.
The Warp x64 runner pool mixes Intel Granite Rapids and AMD Zen 5, and a
build job may land on one vendor while the job that runs its binaries
lands on the other. Neither vendor's feature set contains the other's, so
`-Ctarget-cpu=native` does not produce a portable binary: Zen 5 enables
SSE4A, and LLVM emits it. Disassembling the workspace built for znver5
finds 31 SSE4A instructions, all INSERTQ, in `ix-ffi` and in
`aiur_ixvm_witness::add_entries_parallel`. Granite Rapids has no SSE4A,
so the first one executed raises #UD, killing the process with SIGILL
during witness generation. That is what turned every row of #605's
benchmark into a crash.
Pin the measured intersection of the two CPUs instead. x86-64-v4 covers
every AVX-512 subset Plonky3 uses; +avx512vbmi2 preserves its VPSHRDQ
interleave and +gfni preserves LLVM's byte-shift lowering. A workspace
built with these flags contains no instruction absent from either vendor
and has an instruction vocabulary identical to a graniterapids build.
blake3 dispatches on CPUID at runtime and is unaffected either way.
`.cargo/config.toml` keeps `-Ctarget-cpu=native`: a developer builds and
runs on one machine, and x86-64-v4 would exclude every host without
AVX-512. Only CI has the split, so only CI pins the ISA. The new guard
fails the job when a runner lacks a required feature, so the assumption
is enforced rather than assumed, and the shared `warp-x64` cargo cache
key becomes sound now that codegen no longer varies by host. RUSTFLAGS
is hashed into that key, so the flag change rotates it on its own.
Pinning also removes a benchmarking hazard that never crashed: LLVM sets
prefer-256-bit for Granite Rapids but not for Zen 5, so the same source
vectorized 3.2x more widely depending on the build host, and main-vs-PR
timings were not comparable across a vendor split.
Blake3 now precompiles its libraries, so Lake loads their shared objects --
which bundle the C and Rust FFI objects -- into any process elaborating a
module that imports them. The Blake3 half of `ix_native_decide_dynlib` was
assembling that by hand from a `blake3_rs_shared` cdylib, and that target no
longer exists upstream.
The target keeps Ix's own externs, which nothing else supplies. Precompiling
`Ix.Unsigned` instead would work, but only as its own library declared after
`Ix`: both would claim the module, `Package.findModule?` resolves with
`findSomeRev?`, and losing that race silently stops precompiling it -- with
the symptom appearing as a missing native implementation inside a proof file
rather than as a configuration error. A local dynlib naming its modules
outright is worth more than the lines it costs.
The Blake3 pin moves to the revision that turned precompilation on, and moves
in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The
lakefile drops the cdylib that the older revision still provides, so a pin
left behind in any one of them pairs the new lakefile with a Blake3 that does
not precompile -- and that mismatch surfaces as a missing native
implementation inside a proof file rather than as a build error.
Sign up for freeto 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

@samuelburnham@arthurpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

lakefile: drop Blake3 from the native-decide dynlib - #607

Draft
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile
Draft

lakefile: drop Blake3 from the native-decide dynlib#607
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile

Conversation

@samuelburnham

Copy link
Copy Markdown
Member

Blake3 now precompiles its libraries, so Lake loads their shared objects -- which bundle the C and Rust FFI objects -- into any process elaborating a module that imports them. The Blake3 half of ix_native_decide_dynlib was assembling that by hand from a blake3_rs_shared cdylib, and that target no longer exists upstream.

The target keeps Ix's own externs, which nothing else supplies. Precompiling Ix.Unsigned instead would work, but only as its own library declared after Ix: both would claim the module, Package.findModule? resolves with findSomeRev?, and losing that race silently stops precompiling it -- with the symptom appearing as a missing native implementation inside a proof file rather than as a configuration error. A local dynlib naming its modules outright is worth more than the lines it costs.

The Blake3 pin moves to the revision that turned precompilation on, and moves in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The lakefile drops the cdylib that the older revision still provides, so a pin left behind in any one of them pairs the new lakefile with a Blake3 that does not precompile -- and that mismatch surfaces as a missing native implementation inside a proof file rather than as a build error.

arthurpaulinoand others added 5 commits September 1, 2026 12:56
Update the multi-stark dependency and Rust toolchain for Plonky3 0.6, along with the Rust 1.98 lint migrations required to keep the workspace warning-free. Refresh the Rust-compatible Blake3.lean pin in both root and compile-package manifests.
Adapt recursive Aiur verification to Plonky3's pruned FRI multiproofs. Native proofs retain their compact serialized representation and native verification path; the FFI expands authenticated Merkle frontiers into per-query advice only when entering the existing recursive verifier circuit.
Preserve the packed claim-digest convention in the recursion diagnostic and exercise the advice boundary in the end-to-end test and benchmark paths. CPU and CUDA recursive q1 runs produce identical 823,485-byte inner proofs and 331,273-byte outer proofs.
The q50 Vector.extract_append workload retains identical CPU/CUDA proof sizes. Inner plus outer STARK proving measures 65.87s on CPU and 8.81s with CUDA on the RTX PRO 6000, a 7.48x speedup.
PR benchmark runs execute trusted workflow YAML from the default branch while loading composite actions from the PR checkout. When Bencher data and binary caches are unavailable, the workflow checks out main under base/ and asks Lake to rebuild it without first installing the Rust channel pinned by that checkout.
Teach the existing CPU provenance action to install the base checkout's validated Rust channel and profile immediately before an uncached base build. The step is a no-op when the toolchain is already available and leaves cached benchmark comparisons unchanged.
Consume the Plonky3 0.6 batch-opening layout directly in Aiur instead of expanding every pruned Merkle frontier into one authentication path per FRI query. Sample all query indices from the unchanged transcript, sort and deduplicate them with an O(q log q) merge sort, authenticate each input and commit-phase commitment once, then retain the existing per-query reduced-opening and FRI arithmetic.
Bind every frontier to transcript-derived indices, consume boundary digests in Plonky3's level/parent/child order, reject trailing frontier elements and inconsistent duplicate leaves, and assert all native opening dimensions and sibling counts. Explicitly constrain the digest-bound protocol specialization to cap height 0, binary FRI, and a constant final polynomial. Move memo_u32_less_than into IxVM Core so both substitution and multiproof sorting share its constrained rows.
Strengthen the recursive negative test to mutate a structurally valid stage-1 commitment. Regenerate both checked-in Aiur Rust executors and retain interpreter/codegen query-count parity.
On Vector.extract_append q50, recursive-verifier FFT cost falls from 204.073B to 201.166B. CPU outer proving improves from 50.09s to 45.03s and the full CPU pipeline from 90.64s to 82.90s. GPU outer proving improves from 15.85s to 13.72s and the full GPU pipeline from 28.86s to 26.69s. The outer proof grows from 3.92 MB to 4.17 MB.
Validated with the MultiStark primitive suite, recursive honest/tamper/parity tests, codegen --check, release workspace clippy, release CUDA clippy, rustfmt, and diff checks.
The Warp x64 runner pool mixes Intel Granite Rapids and AMD Zen 5, and a
build job may land on one vendor while the job that runs its binaries
lands on the other. Neither vendor's feature set contains the other's, so
`-Ctarget-cpu=native` does not produce a portable binary: Zen 5 enables
SSE4A, and LLVM emits it. Disassembling the workspace built for znver5
finds 31 SSE4A instructions, all INSERTQ, in `ix-ffi` and in
`aiur_ixvm_witness::add_entries_parallel`. Granite Rapids has no SSE4A,
so the first one executed raises #UD, killing the process with SIGILL
during witness generation. That is what turned every row of #605's
benchmark into a crash.
Pin the measured intersection of the two CPUs instead. x86-64-v4 covers
every AVX-512 subset Plonky3 uses; +avx512vbmi2 preserves its VPSHRDQ
interleave and +gfni preserves LLVM's byte-shift lowering. A workspace
built with these flags contains no instruction absent from either vendor
and has an instruction vocabulary identical to a graniterapids build.
blake3 dispatches on CPUID at runtime and is unaffected either way.
`.cargo/config.toml` keeps `-Ctarget-cpu=native`: a developer builds and
runs on one machine, and x86-64-v4 would exclude every host without
AVX-512. Only CI has the split, so only CI pins the ISA. The new guard
fails the job when a runner lacks a required feature, so the assumption
is enforced rather than assumed, and the shared `warp-x64` cargo cache
key becomes sound now that codegen no longer varies by host. RUSTFLAGS
is hashed into that key, so the flag change rotates it on its own.
Pinning also removes a benchmarking hazard that never crashed: LLVM sets
prefer-256-bit for Granite Rapids but not for Zen 5, so the same source
vectorized 3.2x more widely depending on the build host, and main-vs-PR
timings were not comparable across a vendor split.
Blake3 now precompiles its libraries, so Lake loads their shared objects --
which bundle the C and Rust FFI objects -- into any process elaborating a
module that imports them. The Blake3 half of `ix_native_decide_dynlib` was
assembling that by hand from a `blake3_rs_shared` cdylib, and that target no
longer exists upstream.
The target keeps Ix's own externs, which nothing else supplies. Precompiling
`Ix.Unsigned` instead would work, but only as its own library declared after
`Ix`: both would claim the module, `Package.findModule?` resolves with
`findSomeRev?`, and losing that race silently stops precompiling it -- with
the symptom appearing as a missing native implementation inside a proof file
rather than as a configuration error. A local dynlib naming its modules
outright is worth more than the lines it costs.
The Blake3 pin moves to the revision that turned precompilation on, and moves
in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The
lakefile drops the cdylib that the older revision still provides, so a pin
left behind in any one of them pairs the new lakefile with a Blake3 that does
not precompile -- and that mismatch surfaces as a missing native
implementation inside a proof file rather than as a build error.
Sign up for freeto 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

@samuelburnham@arthurpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

lakefile: drop Blake3 from the native-decide dynlib - #607

Draft
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile
Draft

lakefile: drop Blake3 from the native-decide dynlib#607
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile

Conversation

@samuelburnham

Copy link
Copy Markdown
Member

Blake3 now precompiles its libraries, so Lake loads their shared objects -- which bundle the C and Rust FFI objects -- into any process elaborating a module that imports them. The Blake3 half of ix_native_decide_dynlib was assembling that by hand from a blake3_rs_shared cdylib, and that target no longer exists upstream.

The target keeps Ix's own externs, which nothing else supplies. Precompiling Ix.Unsigned instead would work, but only as its own library declared after Ix: both would claim the module, Package.findModule? resolves with findSomeRev?, and losing that race silently stops precompiling it -- with the symptom appearing as a missing native implementation inside a proof file rather than as a configuration error. A local dynlib naming its modules outright is worth more than the lines it costs.

The Blake3 pin moves to the revision that turned precompilation on, and moves in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The lakefile drops the cdylib that the older revision still provides, so a pin left behind in any one of them pairs the new lakefile with a Blake3 that does not precompile -- and that mismatch surfaces as a missing native implementation inside a proof file rather than as a build error.

arthurpaulinoand others added 5 commits September 1, 2026 12:56
Update the multi-stark dependency and Rust toolchain for Plonky3 0.6, along with the Rust 1.98 lint migrations required to keep the workspace warning-free. Refresh the Rust-compatible Blake3.lean pin in both root and compile-package manifests.
Adapt recursive Aiur verification to Plonky3's pruned FRI multiproofs. Native proofs retain their compact serialized representation and native verification path; the FFI expands authenticated Merkle frontiers into per-query advice only when entering the existing recursive verifier circuit.
Preserve the packed claim-digest convention in the recursion diagnostic and exercise the advice boundary in the end-to-end test and benchmark paths. CPU and CUDA recursive q1 runs produce identical 823,485-byte inner proofs and 331,273-byte outer proofs.
The q50 Vector.extract_append workload retains identical CPU/CUDA proof sizes. Inner plus outer STARK proving measures 65.87s on CPU and 8.81s with CUDA on the RTX PRO 6000, a 7.48x speedup.
PR benchmark runs execute trusted workflow YAML from the default branch while loading composite actions from the PR checkout. When Bencher data and binary caches are unavailable, the workflow checks out main under base/ and asks Lake to rebuild it without first installing the Rust channel pinned by that checkout.
Teach the existing CPU provenance action to install the base checkout's validated Rust channel and profile immediately before an uncached base build. The step is a no-op when the toolchain is already available and leaves cached benchmark comparisons unchanged.
Consume the Plonky3 0.6 batch-opening layout directly in Aiur instead of expanding every pruned Merkle frontier into one authentication path per FRI query. Sample all query indices from the unchanged transcript, sort and deduplicate them with an O(q log q) merge sort, authenticate each input and commit-phase commitment once, then retain the existing per-query reduced-opening and FRI arithmetic.
Bind every frontier to transcript-derived indices, consume boundary digests in Plonky3's level/parent/child order, reject trailing frontier elements and inconsistent duplicate leaves, and assert all native opening dimensions and sibling counts. Explicitly constrain the digest-bound protocol specialization to cap height 0, binary FRI, and a constant final polynomial. Move memo_u32_less_than into IxVM Core so both substitution and multiproof sorting share its constrained rows.
Strengthen the recursive negative test to mutate a structurally valid stage-1 commitment. Regenerate both checked-in Aiur Rust executors and retain interpreter/codegen query-count parity.
On Vector.extract_append q50, recursive-verifier FFT cost falls from 204.073B to 201.166B. CPU outer proving improves from 50.09s to 45.03s and the full CPU pipeline from 90.64s to 82.90s. GPU outer proving improves from 15.85s to 13.72s and the full GPU pipeline from 28.86s to 26.69s. The outer proof grows from 3.92 MB to 4.17 MB.
Validated with the MultiStark primitive suite, recursive honest/tamper/parity tests, codegen --check, release workspace clippy, release CUDA clippy, rustfmt, and diff checks.
The Warp x64 runner pool mixes Intel Granite Rapids and AMD Zen 5, and a
build job may land on one vendor while the job that runs its binaries
lands on the other. Neither vendor's feature set contains the other's, so
`-Ctarget-cpu=native` does not produce a portable binary: Zen 5 enables
SSE4A, and LLVM emits it. Disassembling the workspace built for znver5
finds 31 SSE4A instructions, all INSERTQ, in `ix-ffi` and in
`aiur_ixvm_witness::add_entries_parallel`. Granite Rapids has no SSE4A,
so the first one executed raises #UD, killing the process with SIGILL
during witness generation. That is what turned every row of #605's
benchmark into a crash.
Pin the measured intersection of the two CPUs instead. x86-64-v4 covers
every AVX-512 subset Plonky3 uses; +avx512vbmi2 preserves its VPSHRDQ
interleave and +gfni preserves LLVM's byte-shift lowering. A workspace
built with these flags contains no instruction absent from either vendor
and has an instruction vocabulary identical to a graniterapids build.
blake3 dispatches on CPUID at runtime and is unaffected either way.
`.cargo/config.toml` keeps `-Ctarget-cpu=native`: a developer builds and
runs on one machine, and x86-64-v4 would exclude every host without
AVX-512. Only CI has the split, so only CI pins the ISA. The new guard
fails the job when a runner lacks a required feature, so the assumption
is enforced rather than assumed, and the shared `warp-x64` cargo cache
key becomes sound now that codegen no longer varies by host. RUSTFLAGS
is hashed into that key, so the flag change rotates it on its own.
Pinning also removes a benchmarking hazard that never crashed: LLVM sets
prefer-256-bit for Granite Rapids but not for Zen 5, so the same source
vectorized 3.2x more widely depending on the build host, and main-vs-PR
timings were not comparable across a vendor split.
Blake3 now precompiles its libraries, so Lake loads their shared objects --
which bundle the C and Rust FFI objects -- into any process elaborating a
module that imports them. The Blake3 half of `ix_native_decide_dynlib` was
assembling that by hand from a `blake3_rs_shared` cdylib, and that target no
longer exists upstream.
The target keeps Ix's own externs, which nothing else supplies. Precompiling
`Ix.Unsigned` instead would work, but only as its own library declared after
`Ix`: both would claim the module, `Package.findModule?` resolves with
`findSomeRev?`, and losing that race silently stops precompiling it -- with
the symptom appearing as a missing native implementation inside a proof file
rather than as a configuration error. A local dynlib naming its modules
outright is worth more than the lines it costs.
The Blake3 pin moves to the revision that turned precompilation on, and moves
in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The
lakefile drops the cdylib that the older revision still provides, so a pin
left behind in any one of them pairs the new lakefile with a Blake3 that does
not precompile -- and that mismatch surfaces as a missing native
implementation inside a proof file rather than as a build error.
Sign up for freeto 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

@samuelburnham@arthurpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

lakefile: drop Blake3 from the native-decide dynlib - #607

Draft
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile
Draft

lakefile: drop Blake3 from the native-decide dynlib#607
samuelburnham wants to merge 5 commits into
mainfrom
sb/blake3-precompile

Conversation

@samuelburnham

Copy link
Copy Markdown
Member

Blake3 now precompiles its libraries, so Lake loads their shared objects -- which bundle the C and Rust FFI objects -- into any process elaborating a module that imports them. The Blake3 half of ix_native_decide_dynlib was assembling that by hand from a blake3_rs_shared cdylib, and that target no longer exists upstream.

The target keeps Ix's own externs, which nothing else supplies. Precompiling Ix.Unsigned instead would work, but only as its own library declared after Ix: both would claim the module, Package.findModule? resolves with findSomeRev?, and losing that race silently stops precompiling it -- with the symptom appearing as a missing native implementation inside a proof file rather than as a configuration error. A local dynlib naming its modules outright is worth more than the lines it costs.

The Blake3 pin moves to the revision that turned precompilation on, and moves in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The lakefile drops the cdylib that the older revision still provides, so a pin left behind in any one of them pairs the new lakefile with a Blake3 that does not precompile -- and that mismatch surfaces as a missing native implementation inside a proof file rather than as a build error.

arthurpaulinoand others added 5 commits September 1, 2026 12:56
Update the multi-stark dependency and Rust toolchain for Plonky3 0.6, along with the Rust 1.98 lint migrations required to keep the workspace warning-free. Refresh the Rust-compatible Blake3.lean pin in both root and compile-package manifests.
Adapt recursive Aiur verification to Plonky3's pruned FRI multiproofs. Native proofs retain their compact serialized representation and native verification path; the FFI expands authenticated Merkle frontiers into per-query advice only when entering the existing recursive verifier circuit.
Preserve the packed claim-digest convention in the recursion diagnostic and exercise the advice boundary in the end-to-end test and benchmark paths. CPU and CUDA recursive q1 runs produce identical 823,485-byte inner proofs and 331,273-byte outer proofs.
The q50 Vector.extract_append workload retains identical CPU/CUDA proof sizes. Inner plus outer STARK proving measures 65.87s on CPU and 8.81s with CUDA on the RTX PRO 6000, a 7.48x speedup.
PR benchmark runs execute trusted workflow YAML from the default branch while loading composite actions from the PR checkout. When Bencher data and binary caches are unavailable, the workflow checks out main under base/ and asks Lake to rebuild it without first installing the Rust channel pinned by that checkout.
Teach the existing CPU provenance action to install the base checkout's validated Rust channel and profile immediately before an uncached base build. The step is a no-op when the toolchain is already available and leaves cached benchmark comparisons unchanged.
Consume the Plonky3 0.6 batch-opening layout directly in Aiur instead of expanding every pruned Merkle frontier into one authentication path per FRI query. Sample all query indices from the unchanged transcript, sort and deduplicate them with an O(q log q) merge sort, authenticate each input and commit-phase commitment once, then retain the existing per-query reduced-opening and FRI arithmetic.
Bind every frontier to transcript-derived indices, consume boundary digests in Plonky3's level/parent/child order, reject trailing frontier elements and inconsistent duplicate leaves, and assert all native opening dimensions and sibling counts. Explicitly constrain the digest-bound protocol specialization to cap height 0, binary FRI, and a constant final polynomial. Move memo_u32_less_than into IxVM Core so both substitution and multiproof sorting share its constrained rows.
Strengthen the recursive negative test to mutate a structurally valid stage-1 commitment. Regenerate both checked-in Aiur Rust executors and retain interpreter/codegen query-count parity.
On Vector.extract_append q50, recursive-verifier FFT cost falls from 204.073B to 201.166B. CPU outer proving improves from 50.09s to 45.03s and the full CPU pipeline from 90.64s to 82.90s. GPU outer proving improves from 15.85s to 13.72s and the full GPU pipeline from 28.86s to 26.69s. The outer proof grows from 3.92 MB to 4.17 MB.
Validated with the MultiStark primitive suite, recursive honest/tamper/parity tests, codegen --check, release workspace clippy, release CUDA clippy, rustfmt, and diff checks.
The Warp x64 runner pool mixes Intel Granite Rapids and AMD Zen 5, and a
build job may land on one vendor while the job that runs its binaries
lands on the other. Neither vendor's feature set contains the other's, so
`-Ctarget-cpu=native` does not produce a portable binary: Zen 5 enables
SSE4A, and LLVM emits it. Disassembling the workspace built for znver5
finds 31 SSE4A instructions, all INSERTQ, in `ix-ffi` and in
`aiur_ixvm_witness::add_entries_parallel`. Granite Rapids has no SSE4A,
so the first one executed raises #UD, killing the process with SIGILL
during witness generation. That is what turned every row of #605's
benchmark into a crash.
Pin the measured intersection of the two CPUs instead. x86-64-v4 covers
every AVX-512 subset Plonky3 uses; +avx512vbmi2 preserves its VPSHRDQ
interleave and +gfni preserves LLVM's byte-shift lowering. A workspace
built with these flags contains no instruction absent from either vendor
and has an instruction vocabulary identical to a graniterapids build.
blake3 dispatches on CPUID at runtime and is unaffected either way.
`.cargo/config.toml` keeps `-Ctarget-cpu=native`: a developer builds and
runs on one machine, and x86-64-v4 would exclude every host without
AVX-512. Only CI has the split, so only CI pins the ISA. The new guard
fails the job when a runner lacks a required feature, so the assumption
is enforced rather than assumed, and the shared `warp-x64` cargo cache
key becomes sound now that codegen no longer varies by host. RUSTFLAGS
is hashed into that key, so the flag change rotates it on its own.
Pinning also removes a benchmarking hazard that never crashed: LLVM sets
prefer-256-bit for Granite Rapids but not for Zen 5, so the same source
vectorized 3.2x more widely depending on the build host, and main-vs-PR
timings were not comparable across a vendor split.
Blake3 now precompiles its libraries, so Lake loads their shared objects --
which bundle the C and Rust FFI objects -- into any process elaborating a
module that imports them. The Blake3 half of `ix_native_decide_dynlib` was
assembling that by hand from a `blake3_rs_shared` cdylib, and that target no
longer exists upstream.
The target keeps Ix's own externs, which nothing else supplies. Precompiling
`Ix.Unsigned` instead would work, but only as its own library declared after
`Ix`: both would claim the module, `Package.findModule?` resolves with
`findSomeRev?`, and losing that race silently stops precompiling it -- with
the symptom appearing as a missing native implementation inside a proof file
rather than as a configuration error. A local dynlib naming its modules
outright is worth more than the lines it costs.
The Blake3 pin moves to the revision that turned precompilation on, and moves
in lakefile.lean, lake-manifest.json, flake.nix and flake.lock together. The
lakefile drops the cdylib that the older revision still provides, so a pin
left behind in any one of them pairs the new lakefile with a Blake3 that does
not precompile -- and that mismatch surfaces as a missing native
implementation inside a proof file rather than as a build error.
Sign up for freeto 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

@samuelburnham@arthurpaulino